fix(macos): prevent menubar menu width jump
This commit is contained in:
@@ -7,6 +7,7 @@ final class MenuContextCardInjector: NSObject, NSMenuDelegate {
|
|||||||
|
|
||||||
private let tag = 9_415_227
|
private let tag = 9_415_227
|
||||||
private let fallbackCardWidth: CGFloat = 320
|
private let fallbackCardWidth: CGFloat = 320
|
||||||
|
private var lastKnownMenuWidth: CGFloat?
|
||||||
private weak var originalDelegate: NSMenuDelegate?
|
private weak var originalDelegate: NSMenuDelegate?
|
||||||
private var loadTask: Task<Void, Never>?
|
private var loadTask: Task<Void, Never>?
|
||||||
private var warmTask: Task<Void, Never>?
|
private var warmTask: Task<Void, Never>?
|
||||||
@@ -55,9 +56,10 @@ final class MenuContextCardInjector: NSObject, NSMenuDelegate {
|
|||||||
|
|
||||||
let hosting = NSHostingView(rootView: initial)
|
let hosting = NSHostingView(rootView: initial)
|
||||||
let size = hosting.fittingSize
|
let size = hosting.fittingSize
|
||||||
|
let initialWidth = self.initialCardWidth(for: menu)
|
||||||
hosting.frame = NSRect(
|
hosting.frame = NSRect(
|
||||||
origin: .zero,
|
origin: .zero,
|
||||||
size: NSSize(width: self.initialCardWidth(for: menu), height: size.height))
|
size: NSSize(width: initialWidth, height: size.height))
|
||||||
|
|
||||||
let item = NSMenuItem()
|
let item = NSMenuItem()
|
||||||
item.tag = self.tag
|
item.tag = self.tag
|
||||||
@@ -66,10 +68,10 @@ final class MenuContextCardInjector: NSObject, NSMenuDelegate {
|
|||||||
|
|
||||||
menu.insertItem(item, at: insertIndex)
|
menu.insertItem(item, at: insertIndex)
|
||||||
|
|
||||||
// After the menu attaches the view to its window, adopt the menu's computed width.
|
// Capture the menu window width for next open, but do not mutate widths while the menu is visible.
|
||||||
DispatchQueue.main.async { [weak self, weak hosting] in
|
DispatchQueue.main.async { [weak self, weak hosting] in
|
||||||
guard let self, let hosting else { return }
|
guard let self, let hosting else { return }
|
||||||
self.adoptMenuWidthIfAvailable(for: menu, hosting: hosting)
|
self.captureMenuWidthIfAvailable(for: menu, hosting: hosting)
|
||||||
}
|
}
|
||||||
|
|
||||||
if initialIsLoading {
|
if initialIsLoading {
|
||||||
@@ -80,7 +82,7 @@ final class MenuContextCardInjector: NSObject, NSMenuDelegate {
|
|||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
hosting.rootView = view
|
hosting.rootView = view
|
||||||
hosting.invalidateIntrinsicContentSize()
|
hosting.invalidateIntrinsicContentSize()
|
||||||
self.adoptMenuWidthIfAvailable(for: menu, hosting: hosting)
|
self.captureMenuWidthIfAvailable(for: menu, hosting: hosting)
|
||||||
let size = hosting.fittingSize
|
let size = hosting.fittingSize
|
||||||
hosting.frame.size.height = size.height
|
hosting.frame.size.height = size.height
|
||||||
}
|
}
|
||||||
@@ -165,12 +167,16 @@ final class MenuContextCardInjector: NSObject, NSMenuDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func initialCardWidth(for menu: NSMenu) -> CGFloat {
|
private func initialCardWidth(for menu: NSMenu) -> CGFloat {
|
||||||
let width = menu.minimumWidth
|
let widthCandidates: [CGFloat] = [
|
||||||
if width > 0 { return max(300, width) }
|
menu.minimumWidth,
|
||||||
return 300
|
self.lastKnownMenuWidth ?? 0,
|
||||||
|
self.fallbackCardWidth,
|
||||||
|
]
|
||||||
|
let resolved = widthCandidates.max() ?? self.fallbackCardWidth
|
||||||
|
return max(300, resolved)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func adoptMenuWidthIfAvailable(for menu: NSMenu, hosting: NSHostingView<AnyView>) {
|
private func captureMenuWidthIfAvailable(for menu: NSMenu, hosting: NSHostingView<AnyView>) {
|
||||||
let targetWidth: CGFloat? = {
|
let targetWidth: CGFloat? = {
|
||||||
if let contentWidth = hosting.window?.contentView?.bounds.width, contentWidth > 0 { return contentWidth }
|
if let contentWidth = hosting.window?.contentView?.bounds.width, contentWidth > 0 { return contentWidth }
|
||||||
if let superWidth = hosting.superview?.bounds.width, superWidth > 0 { return superWidth }
|
if let superWidth = hosting.superview?.bounds.width, superWidth > 0 { return superWidth }
|
||||||
@@ -179,15 +185,7 @@ final class MenuContextCardInjector: NSObject, NSMenuDelegate {
|
|||||||
return nil
|
return nil
|
||||||
}()
|
}()
|
||||||
|
|
||||||
guard let targetWidth else {
|
guard let targetWidth else { return }
|
||||||
if hosting.frame.width <= 0 {
|
self.lastKnownMenuWidth = max(300, targetWidth)
|
||||||
hosting.frame.size.width = self.fallbackCardWidth
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let clamped = max(300, targetWidth)
|
|
||||||
if abs(hosting.frame.width - clamped) < 1 { return }
|
|
||||||
hosting.frame.size.width = clamped
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user