From b0444d593efbb0ac7f2cce0083d1a92e6fec2fa3 Mon Sep 17 00:00:00 2001 From: Gabriel Jordan Date: Sun, 25 Oct 2026 15:16:58 -0300 Subject: [PATCH] Keep the glass background a fixed size while the panel animates The glass background took the animated panel shape, so every frame of the open and close animation asked for a new backdrop surface of a new size. Opening the panel briefly took about 300 MB of graphics memory. The glass is now a rectangle sized for the state being shown, set without animation and clipped by the animated notch shape, which keeps the peak under 30 MB with no visible difference. --- CHANGELOG.md | 3 +++ .../Features/Notch/NotchBackground.swift | 17 ++++++++++++++++- .../Pullo/Features/Notch/NotchRootView.swift | 19 ++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17be0f0..86dcb57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to Pullo are listed here, newest first. The format follows [ ## [Unreleased] +### Fixed +- Opening the panel with the glass style no longer briefly takes about 300 MB of memory. The glass background stays the same size while the panel animates, so it peaks under 30 MB. + ## [0.11.3] - 2026-10-25 ### Fixed diff --git a/Sources/Pullo/Features/Notch/NotchBackground.swift b/Sources/Pullo/Features/Notch/NotchBackground.swift index 2bbc583..cc730f7 100644 --- a/Sources/Pullo/Features/Notch/NotchBackground.swift +++ b/Sources/Pullo/Features/Notch/NotchBackground.swift @@ -10,13 +10,14 @@ struct NotchBackground: View { let height: CGFloat var style: PanelStyle? var scale: CGFloat = 1 + var glassSize: CGSize? private var currentStyle: PanelStyle { style ?? settings.panelStyle } var body: some View { if usesGlass, #available(macOS 26, *) { ZStack { - Color.clear.glassEffect(.regular, in: shape) + glass if currentStyle == .glassDarkTop { darkTop } @@ -27,6 +28,20 @@ struct NotchBackground: View { } } + @available(macOS 26, *) + @ViewBuilder + private var glass: some View { + if let glassSize { + Color.clear + .glassEffect(.regular, in: Rectangle()) + .frame(width: glassSize.width, height: glassSize.height) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) + .transaction { $0.animation = nil } + } else { + Color.clear.glassEffect(.regular, in: shape) + } + } + private var darkTop: some View { RadialGradient( stops: [ diff --git a/Sources/Pullo/Features/Notch/NotchRootView.swift b/Sources/Pullo/Features/Notch/NotchRootView.swift index 12d6f36..c2f00d5 100644 --- a/Sources/Pullo/Features/Notch/NotchRootView.swift +++ b/Sources/Pullo/Features/Notch/NotchRootView.swift @@ -5,6 +5,7 @@ struct NotchRootView: View { @Environment(AccountStore.self) private var accounts @Environment(AppSettings.self) private var settings @Environment(\.accessibilityReduceTransparency) private var reduceTransparency + @State private var lastShown: NotchState = .open var body: some View { let state = notch.state @@ -23,6 +24,22 @@ struct NotchRootView: View { .hintLayer(within: CGSize(width: size.width + radii.top * 2, height: size.height)) .animation(Motion.peek, value: notch.liveCI) .environment(\.panelGlass, settings.panelStyle.usesGlass(reduceTransparency: reduceTransparency)) + .onChange(of: state) { _, new in + if new != .closed { lastShown = new } + } + } + + private func background(state: NotchState, shape: NotchShape, height: CGFloat) -> some View { + let glassState = state == .closed ? lastShown : state + let glassSize = notch.layout.size(for: glassState) + let glassRadii = notch.layout.radii(for: glassState) + return NotchBackground( + state: state, + shape: shape, + notchWidth: notch.geometry.notchSize.width, + height: height, + glassSize: CGSize(width: glassSize.width + glassRadii.top * 2, height: glassSize.height) + ) } private func panel(state: NotchState, size: CGSize, radii: (top: CGFloat, bottom: CGFloat), shape: NotchShape) -> some View { @@ -40,7 +57,7 @@ struct NotchRootView: View { } .frame(width: size.width, height: size.height, alignment: .top) .padding(.horizontal, radii.top) - .background { NotchBackground(state: state, shape: shape, notchWidth: notch.geometry.notchSize.width, height: size.height) } + .background { background(state: state, shape: shape, height: size.height) } .clipShape(shape) .compositingGroup() .shadow(color: .black.opacity(state == .closed ? 0 : 0.55), radius: state == .open ? 26 : 14, y: 8)