Keep browser omnibar visible across pane zoom
This commit is contained in:
parent
b99ec64ae3
commit
034f157feb
2 changed files with 114 additions and 3 deletions
|
|
@ -1720,7 +1720,13 @@ final class BrowserPanel: Panel, ObservableObject {
|
|||
let inWindow: Bool
|
||||
let area: CGFloat
|
||||
}
|
||||
private struct PortalHostLock {
|
||||
let hostId: ObjectIdentifier
|
||||
let paneId: UUID
|
||||
}
|
||||
private var activePortalHostLease: PortalHostLease?
|
||||
private var pendingDistinctPortalHostReplacementPaneId: UUID?
|
||||
private var lockedPortalHost: PortalHostLock?
|
||||
private var webViewCancellables = Set<AnyCancellable>()
|
||||
private var navigationDelegate: BrowserNavigationDelegate?
|
||||
private var uiDelegate: BrowserUIDelegate?
|
||||
|
|
@ -1773,6 +1779,22 @@ final class BrowserPanel: Panel, ObservableObject {
|
|||
lease.inWindow && lease.area > portalHostAreaThreshold
|
||||
}
|
||||
|
||||
func preparePortalHostReplacementForNextDistinctClaim(
|
||||
inPane paneId: PaneID,
|
||||
reason: String
|
||||
) {
|
||||
pendingDistinctPortalHostReplacementPaneId = paneId.id
|
||||
if lockedPortalHost?.paneId == paneId.id {
|
||||
lockedPortalHost = nil
|
||||
}
|
||||
#if DEBUG
|
||||
dlog(
|
||||
"browser.portal.host.rearm panel=\(id.uuidString.prefix(5)) " +
|
||||
"reason=\(reason) pane=\(paneId.id.uuidString.prefix(5))"
|
||||
)
|
||||
#endif
|
||||
}
|
||||
|
||||
func claimPortalHost(
|
||||
hostId: ObjectIdentifier,
|
||||
paneId: PaneID,
|
||||
|
|
@ -1788,6 +1810,11 @@ final class BrowserPanel: Panel, ObservableObject {
|
|||
)
|
||||
|
||||
if let current = activePortalHostLease {
|
||||
if let lock = lockedPortalHost,
|
||||
(lock.hostId != current.hostId || lock.paneId != current.paneId) {
|
||||
lockedPortalHost = nil
|
||||
}
|
||||
|
||||
if current.hostId == hostId {
|
||||
activePortalHostLease = next
|
||||
return true
|
||||
|
|
@ -1795,12 +1822,47 @@ final class BrowserPanel: Panel, ObservableObject {
|
|||
|
||||
let currentUsable = Self.portalHostIsUsable(current)
|
||||
let nextUsable = Self.portalHostIsUsable(next)
|
||||
let isSamePaneReplacement = current.paneId == paneId.id
|
||||
let shouldForceDistinctReplacement =
|
||||
isSamePaneReplacement &&
|
||||
pendingDistinctPortalHostReplacementPaneId == paneId.id &&
|
||||
inWindow
|
||||
if shouldForceDistinctReplacement {
|
||||
#if DEBUG
|
||||
dlog(
|
||||
"browser.portal.host.claim panel=\(id.uuidString.prefix(5)) " +
|
||||
"reason=\(reason) host=\(hostId) pane=\(paneId.id.uuidString.prefix(5)) " +
|
||||
"inWin=\(inWindow ? 1 : 0) size=\(String(format: "%.1fx%.1f", bounds.width, bounds.height)) " +
|
||||
"replacingHost=\(current.hostId) replacingPane=\(current.paneId.uuidString.prefix(5)) " +
|
||||
"replacingInWin=\(current.inWindow ? 1 : 0) replacingArea=\(String(format: "%.1f", current.area)) " +
|
||||
"forced=1"
|
||||
)
|
||||
#endif
|
||||
activePortalHostLease = next
|
||||
pendingDistinctPortalHostReplacementPaneId = nil
|
||||
lockedPortalHost = PortalHostLock(hostId: hostId, paneId: paneId.id)
|
||||
return true
|
||||
}
|
||||
|
||||
let lockBlocksSamePaneReplacement =
|
||||
isSamePaneReplacement &&
|
||||
currentUsable &&
|
||||
lockedPortalHost?.hostId == current.hostId &&
|
||||
lockedPortalHost?.paneId == current.paneId
|
||||
let shouldReplace =
|
||||
current.paneId != paneId.id ||
|
||||
!currentUsable ||
|
||||
(nextUsable && next.area > (current.area * Self.portalHostReplacementAreaGainRatio))
|
||||
(
|
||||
!lockBlocksSamePaneReplacement &&
|
||||
nextUsable &&
|
||||
next.area > (current.area * Self.portalHostReplacementAreaGainRatio)
|
||||
)
|
||||
|
||||
if shouldReplace {
|
||||
if lockedPortalHost?.hostId == current.hostId &&
|
||||
lockedPortalHost?.paneId == current.paneId {
|
||||
lockedPortalHost = nil
|
||||
}
|
||||
#if DEBUG
|
||||
dlog(
|
||||
"browser.portal.host.claim panel=\(id.uuidString.prefix(5)) " +
|
||||
|
|
@ -1820,7 +1882,8 @@ final class BrowserPanel: Panel, ObservableObject {
|
|||
"reason=\(reason) host=\(hostId) pane=\(paneId.id.uuidString.prefix(5)) " +
|
||||
"inWin=\(inWindow ? 1 : 0) size=\(String(format: "%.1fx%.1f", bounds.width, bounds.height)) " +
|
||||
"ownerHost=\(current.hostId) ownerPane=\(current.paneId.uuidString.prefix(5)) " +
|
||||
"ownerInWin=\(current.inWindow ? 1 : 0) ownerArea=\(String(format: "%.1f", current.area))"
|
||||
"ownerInWin=\(current.inWindow ? 1 : 0) ownerArea=\(String(format: "%.1f", current.area)) " +
|
||||
"locked=\(lockBlocksSamePaneReplacement ? 1 : 0)"
|
||||
)
|
||||
#endif
|
||||
return false
|
||||
|
|
@ -1842,6 +1905,9 @@ final class BrowserPanel: Panel, ObservableObject {
|
|||
func releasePortalHostIfOwned(hostId: ObjectIdentifier, reason: String) -> Bool {
|
||||
guard let current = activePortalHostLease, current.hostId == hostId else { return false }
|
||||
activePortalHostLease = nil
|
||||
if lockedPortalHost?.hostId == hostId {
|
||||
lockedPortalHost = nil
|
||||
}
|
||||
#if DEBUG
|
||||
dlog(
|
||||
"browser.portal.host.release panel=\(id.uuidString.prefix(5)) " +
|
||||
|
|
|
|||
|
|
@ -3217,11 +3217,19 @@ final class Workspace: Identifiable, ObservableObject {
|
|||
|
||||
@discardableResult
|
||||
func toggleSplitZoom(panelId: UUID) -> Bool {
|
||||
let wasSplitZoomed = bonsplitController.isSplitZoomed
|
||||
guard let paneId = paneId(forPanelId: panelId) else { return false }
|
||||
guard bonsplitController.togglePaneZoom(inPane: paneId) else { return false }
|
||||
focusPanel(panelId)
|
||||
if browserPanel(for: panelId) != nil {
|
||||
if let browserPanel = browserPanel(for: panelId) {
|
||||
browserPanel.preparePortalHostReplacementForNextDistinctClaim(
|
||||
inPane: paneId,
|
||||
reason: "workspace.toggleSplitZoom"
|
||||
)
|
||||
scheduleBrowserPortalReconcileAfterSplitZoom(panelId: panelId, remainingPasses: 4)
|
||||
if wasSplitZoomed && !bonsplitController.isSplitZoomed {
|
||||
scheduleBrowserSplitZoomExitFocusReassert(panelId: panelId, remainingPasses: 4)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
@ -3525,6 +3533,43 @@ final class Workspace: Identifiable, ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
// Browser panes can briefly keep the portal-hosted WKWebView visible while Bonsplit is
|
||||
// still rebuilding the unzoomed pane host. Reassert pane/tab selection after layout settles
|
||||
// so the SwiftUI chrome does not remain hidden until another browser focus command runs.
|
||||
private func scheduleBrowserSplitZoomExitFocusReassert(panelId: UUID, remainingPasses: Int) {
|
||||
guard remainingPasses > 0 else { return }
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self, self.browserPanel(for: panelId) != nil else { return }
|
||||
guard let paneId = self.paneId(forPanelId: panelId),
|
||||
let tabId = self.surfaceIdFromPanelId(panelId) else { return }
|
||||
|
||||
let selectionConverged =
|
||||
self.bonsplitController.focusedPaneId == paneId &&
|
||||
self.bonsplitController.selectedTab(inPane: paneId)?.id == tabId
|
||||
let anchorReady: Bool = {
|
||||
guard let browserPanel = self.browserPanel(for: panelId) else { return false }
|
||||
let anchorView = browserPanel.portalAnchorView
|
||||
return
|
||||
anchorView.window != nil &&
|
||||
anchorView.superview != nil &&
|
||||
anchorView.bounds.width > 1 &&
|
||||
anchorView.bounds.height > 1
|
||||
}()
|
||||
|
||||
if !selectionConverged {
|
||||
self.focusPanel(panelId)
|
||||
self.scheduleFocusReconcile()
|
||||
}
|
||||
|
||||
if !selectionConverged || !anchorReady {
|
||||
self.scheduleBrowserSplitZoomExitFocusReassert(
|
||||
panelId: panelId,
|
||||
remainingPasses: remainingPasses - 1
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func scheduleMovedTerminalRefresh(panelId: UUID) {
|
||||
guard terminalPanel(for: panelId) != nil else { return }
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue