From 04448350160d232a34e19bc41ffd8ab276ca65e5 Mon Sep 17 00:00:00 2001 From: Marlon Kranz Date: Sat, 10 Oct 2026 06:04:17 +0200 Subject: [PATCH] fix(frame): make connected mode honor the settings it overrides (#3665) * fix(frame): make connected mode honor the settings it overrides Several frame-mode behaviors contradicted what the settings UI shows: - Frame -> Blur gated blur for every shell surface in connected mode, including the Settings window, polkit, context menus, OSDs and toasts. WindowBlur now defaults to Background blur; only surfaces painted with the frame color (connected popouts, slideouts, Control Center) follow Frame -> Blur, and frame-owned surfaces are blurred by the frame as before. - The notepad slideout painted its own fill at the notepad transparency. On screens where the frame owns connected chrome it now claims a frame surface (new connected_arc slot 5), so the frame paints it flush with the cutout, with fillets, and follows the slide. - Notification popups painted a card surface on top of the frame body, stacking fills. That layer is transparent when the frame paints it. - Connected mode stripped shadow, border and corner styling from every bar, including bars on displays without a frame, and hid the rows. Styling is now stripped and restored per bar based on whether it covers a frame display, re-checked on screen, frame display and display-name-mode changes. - Auto popup gaps and Gap size were hidden in separate mode although they still apply there; they now hide only in connected mode. - The launcher style picker was hidden in connected mode while Island still overrode the connected launcher. Connected mode now always uses the connected launcher; the picker shows Full, disabled, with a note linking to the Bar layout picker. - The frame shadow ignored Shadows -> Apply to -> Bars. * fix(frame): address review feedback on connected mode - Skip bar style reconciliation while the screen list is empty, so a hotplug doesn't restore and re-strip every bar. - Replace the 16 copies of the connector blur anchor and cutout in FrameWindow with a ConnectorBlurRegion inline component. - Let connected mode use the island launcher again now that the island can sit in any bar. The picker offers Full and Island there and hides Spotlight, which opens the full launcher in that mode anyway; the note and the SettingsControlledBy bar target are dropped. * frame: address review findings on connected mode --------- Co-authored-by: purian23 --- .../Common/ConnectedSurfaceDescriptor.js | 3 +- quickshell/Common/SettingsData.qml | 170 ++---- quickshell/DMSShell.qml | 1 + quickshell/Modals/Common/DModalHost.qml | 3 +- .../DLauncherV2/DLauncherV2ModalHost.qml | 3 +- quickshell/Modules/Dock/Dock.qml | 2 +- quickshell/Modules/Dock/DockBody.qml | 1 - quickshell/Modules/Frame/FrameWindow.qml | 553 ++++++------------ quickshell/Modules/Notepad/Notepad.qml | 1 + .../Modules/Notepad/NotepadSettings.qml | 5 +- quickshell/Modules/Settings/DBarTab.qml | 5 +- quickshell/Modules/Settings/LauncherTab.qml | 14 +- .../Settings/Widgets/BarSelectionState.qml | 2 +- quickshell/Shaders/frag/connected_arc.frag | 8 +- quickshell/Shaders/qsb/connected_arc.frag.qsb | Bin 8221 -> 8716 bytes quickshell/Widgets/DContextMenu.qml | 2 +- quickshell/Widgets/DFloatingWindow.qml | 2 +- quickshell/Widgets/DPopoutHost.qml | 2 +- quickshell/Widgets/DSlideout.qml | 93 ++- quickshell/Widgets/WindowBlur.qml | 2 +- quickshell/tests/frame-bar-styles.test.mjs | 111 ++++ quickshell/tests/island-routing.test.mjs | 4 +- 22 files changed, 467 insertions(+), 520 deletions(-) create mode 100644 quickshell/tests/frame-bar-styles.test.mjs diff --git a/quickshell/Common/ConnectedSurfaceDescriptor.js b/quickshell/Common/ConnectedSurfaceDescriptor.js index 0eda4a109..f5af5b2d7 100644 --- a/quickshell/Common/ConnectedSurfaceDescriptor.js +++ b/quickshell/Common/ConnectedSurfaceDescriptor.js @@ -6,7 +6,8 @@ var VALID_KINDS = { "launcher": true, "dock": true, "notification": true, - "island": true + "island": true, + "slideout": true }; var VALID_PHASES = { diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index 846e4b594..080a1249d 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -388,7 +388,11 @@ Singleton { property real frameRounding: Spec.SPEC.frameRounding.def onFrameRoundingChanged: saveSettings() property var frameScreenPreferences: Spec.SPEC.frameScreenPreferences.def - onFrameScreenPreferencesChanged: saveSettings() + onFrameScreenPreferencesChanged: { + saveSettings(); + if (!_loading) + Qt.callLater(_reconcileConnectedFrameBarStyles); + } property real frameBarSize: Spec.SPEC.frameBarSize.def onFrameBarSizeChanged: saveSettings() property bool frameShowOnOverview: Spec.SPEC.frameShowOnOverview.def @@ -1138,6 +1142,10 @@ Singleton { property var updaterIgnoredPackages: Spec.SPEC.updaterIgnoredPackages.def property string displayNameMode: Spec.SPEC.displayNameMode.def + onDisplayNameModeChanged: { + if (!_loading) + _reconcileConnectedFrameBarStyles(); + } property var screenPreferences: Spec.SPEC.screenPreferences.def property var showOnLastDisplay: Spec.SPEC.showOnLastDisplay.def property var displayProfiles: Spec.SPEC.displayProfiles.def @@ -1515,6 +1523,14 @@ Singleton { }); } + Connections { + target: Quickshell + function onScreensChanged() { + if (!root._loading) + Qt.callLater(root._reconcileConnectedFrameBarStyles); + } + } + Connections { target: typeof SessionData !== "undefined" ? SessionData : null function onIsLightModeChanged() { @@ -2085,116 +2101,48 @@ Singleton { }; } - function _hasConnectedFrameBarStyleBackups() { - return connectedFrameBarStyleBackups && Object.keys(connectedFrameBarStyleBackups).length > 0; + function barUsesConnectedFrameStyle(config) { + if (!config || !connectedFrameModeActive) + return false; + return Quickshell.screens.some(screen => barConfigCoversScreen(config, screen) && isScreenInPreferences(screen, frameScreenPreferences)); } - function _captureConnectedFrameBarStyleBackups(configs, overwriteExisting) { + function _applyConnectedFrameBarStyles(configs) { if (!Array.isArray(configs)) - return; - - const nextBackups = JSON.parse(JSON.stringify(connectedFrameBarStyleBackups || {})); - const validIds = {}; - let changed = false; - - for (let i = 0; i < configs.length; i++) { - const config = configs[i]; - if (!config?.id) - continue; - validIds[config.id] = true; - - if (!overwriteExisting && nextBackups[config.id] !== undefined) - continue; - - const snapshot = _connectedFrameBarStyleSnapshot(config); - if (JSON.stringify(nextBackups[config.id]) !== JSON.stringify(snapshot)) { - nextBackups[config.id] = snapshot; - changed = true; - } - } - - if (overwriteExisting) { - for (const barId in nextBackups) { - if (validIds[barId]) - continue; - delete nextBackups[barId]; - changed = true; - } - } - - if (changed) - connectedFrameBarStyleBackups = nextBackups; - } - - function _restoreConnectedFrameBarStyleBackups() { - if (!_hasConnectedFrameBarStyleBackups()) - return; - - const backups = connectedFrameBarStyleBackups || {}; - const configs = JSON.parse(JSON.stringify(barConfigs)); - let changed = false; - - for (let i = 0; i < configs.length; i++) { - const backup = backups[configs[i].id]; - if (!backup) - continue; - for (const key in backup) { - if (configs[i][key] === backup[key]) - continue; - configs[i][key] = backup[key]; - changed = true; - } - } - - if (changed) - barConfigs = configs; - connectedFrameBarStyleBackups = ({}); - if (changed) - updateBarConfigs(); - } - - // Zeroes out connected-mode-hostile fields (shadow, square/goth corners, edge attach, border). - // Returns { configs, changed } — `configs` is the same ref when no change. - function _sanitizeBarConfigsForConnectedFrame(configs) { - if (!connectedFrameModeActive || !Array.isArray(configs)) return { "configs": configs, - "changed": false + "changed": false, + "backups": null }; + const backups = connectedFrameBarStyleBackups || {}; + const nextBackups = {}; + let backupsChanged = false; let anyChanged = false; + const zeroed = _connectedFrameBarStyleSnapshot(null); const out = configs.map(cfg => { - if (!cfg) + if (!cfg?.id) return cfg; - let dirty = false; - const s = Object.assign({}, cfg); - if ((s.shadowIntensity ?? 0) !== 0) { - s.shadowIntensity = 0; - dirty = true; + const covered = barUsesConnectedFrameStyle(cfg); + if (covered) { + nextBackups[cfg.id] = backups[cfg.id] ?? _connectedFrameBarStyleSnapshot(cfg); + backupsChanged = backupsChanged || backups[cfg.id] === undefined; } - if (s.squareCorners ?? false) { - s.squareCorners = false; - dirty = true; - } - if (s.attachToScreenEdge ?? false) { - s.attachToScreenEdge = false; - dirty = true; - } - if (s.gothCornersEnabled ?? false) { - s.gothCornersEnabled = false; - dirty = true; - } - if (s.borderEnabled ?? false) { - s.borderEnabled = false; - dirty = true; - } - if (dirty) - anyChanged = true; - return dirty ? s : cfg; + const target = covered ? zeroed : backups[cfg.id]; + if (!target) + return cfg; + const current = _connectedFrameBarStyleSnapshot(cfg); + if (Object.keys(target).every(key => current[key] === target[key])) + return cfg; + anyChanged = true; + return Object.assign({}, cfg, target); }); + + const backupsDirty = backupsChanged || Object.keys(nextBackups).length !== Object.keys(backups).length; return { "configs": anyChanged ? out : configs, - "changed": anyChanged + "changed": anyChanged, + "backups": backupsDirty ? nextBackups : null }; } @@ -2207,20 +2155,16 @@ Singleton { return Object.assign({}, config, backup); } - // Single entry point for connected-mode settings state. - // !active → restore backups function _reconcileConnectedFrameBarStyles() { - if (!connectedFrameModeActive) { - _restoreConnectedFrameBarStyleBackups(); + if (Quickshell.screens.length === 0) return; - } - if (!_hasConnectedFrameBarStyleBackups()) - _captureConnectedFrameBarStyleBackups(barConfigs, true); - const result = _sanitizeBarConfigsForConnectedFrame(barConfigs); - if (result.changed) { + const result = _applyConnectedFrameBarStyles(barConfigs); + if (result.changed) barConfigs = result.configs; + if (result.backups) + connectedFrameBarStyleBackups = result.backups; + if (result.changed) updateBarConfigs(); - } } function detectAvailableIconThemes() { @@ -2678,9 +2622,10 @@ Singleton { function addBarConfig(config) { const configs = JSON.parse(JSON.stringify(barConfigs)); configs.push(config); - if (connectedFrameModeActive) - _captureConnectedFrameBarStyleBackups(configs, false); - barConfigs = _sanitizeBarConfigsForConnectedFrame(configs).configs; + const result = _applyConnectedFrameBarStyles(configs); + barConfigs = result.configs; + if (result.backups) + connectedFrameBarStyleBackups = result.backups; updateBarConfigs(); } @@ -2870,7 +2815,10 @@ Singleton { function _commitBarConfigs(configs) { for (const cfg of configs) _restoreIslandWidget(cfg, configs); - barConfigs = _sanitizeBarConfigsForConnectedFrame(configs).configs; + const result = _applyConnectedFrameBarStyles(configs); + barConfigs = result.configs; + if (result.backups) + connectedFrameBarStyleBackups = result.backups; updateBarConfigs(); } diff --git a/quickshell/DMSShell.qml b/quickshell/DMSShell.qml index e3d120b27..19a3f2c25 100644 --- a/quickshell/DMSShell.qml +++ b/quickshell/DMSShell.qml @@ -1068,6 +1068,7 @@ Item { expandable: true expandedWidthValue: 960 edgeGap: SettingsData.notepadEffectiveEdgeGap + frameSurfaceEnabled: true slideEdge: SettingsData.notepadSlideoutSide customTransparency: Theme.notepadTransparency diff --git a/quickshell/Modals/Common/DModalHost.qml b/quickshell/Modals/Common/DModalHost.qml index 796e5a031..8761b34f0 100644 --- a/quickshell/Modals/Common/DModalHost.qml +++ b/quickshell/Modals/Common/DModalHost.qml @@ -68,7 +68,6 @@ Item { readonly property color effectiveBorderColor: frameOwnsConnectedChrome ? Theme.withAlpha(borderColor, 0) : borderColor readonly property real effectiveBorderWidth: frameOwnsConnectedChrome ? 0 : borderWidth readonly property real effectiveCornerRadius: frameOwnsConnectedChrome ? Theme.connectedSurfaceRadius : cornerRadius - readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled property bool enableShadow: modalHandle.enableShadow property alias modalFocusScope: focusScope property Item handleFocusScope: null @@ -547,7 +546,7 @@ Item { WindowBlur { targetWindow: contentWindow - blurEnabled: root.effectiveBlurEnabled && !root.frameOwnsConnectedChrome + blurEnabled: !root.frameOwnsConnectedChrome surfaceColor: root.effectiveBackgroundColor readonly property real s: Math.min(1, modalContainer.scaleValue) readonly property real op: Math.max(0, Math.min(1, (morph.value - 0.06) * 2)) diff --git a/quickshell/Modals/DLauncherV2/DLauncherV2ModalHost.qml b/quickshell/Modals/DLauncherV2/DLauncherV2ModalHost.qml index ce2c9545f..e97ee4be2 100644 --- a/quickshell/Modals/DLauncherV2/DLauncherV2ModalHost.qml +++ b/quickshell/Modals/DLauncherV2/DLauncherV2ModalHost.qml @@ -208,7 +208,6 @@ Item { readonly property color effectiveBorderColor: connectedSurfaceOverride ? Theme.withAlpha(borderColor, 0) : borderColor readonly property int effectiveBorderWidth: connectedSurfaceOverride ? 0 : borderWidth readonly property int paintedBorderWidth: connected ? (frameOwnsConnectedChrome ? 0 : effectiveBorderWidth) : Math.max(borderWidth, BlurService.borderWidth) - readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled readonly property var shadowLevel: Theme.elevationLevel3 readonly property real shadowFallbackOffset: Theme.spacingS @@ -919,7 +918,7 @@ Item { WindowBlur { targetWindow: contentWindow - blurEnabled: root.effectiveBlurEnabled && !root.frameOwnsConnectedChrome + blurEnabled: !root.frameOwnsConnectedChrome surfaceColor: root.backgroundColor readonly property real s: Math.min(1, contentContainer.scaleValue) readonly property bool clipDriven: root._fluidMotionActive && !root.connectedFluidMotion diff --git a/quickshell/Modules/Dock/Dock.qml b/quickshell/Modules/Dock/Dock.qml index b2dd0c3d9..669cdd5ac 100644 --- a/quickshell/Modules/Dock/Dock.qml +++ b/quickshell/Modules/Dock/Dock.qml @@ -67,7 +67,7 @@ Variants { WindowBlur { targetWindow: dock - blurEnabled: body.effectiveBlurEnabled && !body.usesConnectedFrameChrome + blurEnabled: !body.usesConnectedFrameChrome surfaceColor: body.surfaceColor blurX: body.blurX blurY: body.blurY diff --git a/quickshell/Modules/Dock/DockBody.qml b/quickshell/Modules/Dock/DockBody.qml index cbb1b7c1b..874b34626 100644 --- a/quickshell/Modules/Dock/DockBody.qml +++ b/quickshell/Modules/Dock/DockBody.qml @@ -216,7 +216,6 @@ FocusScope { readonly property real adjacentLeftBarWidth: !isVertical && !autoHide ? ShellLayout.dockAdjacentThickness(screen, "left") : 0 readonly property real dockMargin: dock.config.margin - readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled readonly property real effectiveDockMargin: dockGeometry.effectiveMargin readonly property real joinedEdgeMargin: dockGeometry.joinedEdgeMargin readonly property real _dpr: (dock.screen && dock.screen.devicePixelRatio) ? dock.screen.devicePixelRatio : 1 diff --git a/quickshell/Modules/Frame/FrameWindow.qml b/quickshell/Modules/Frame/FrameWindow.qml index e9f5476ee..c4548c624 100644 --- a/quickshell/Modules/Frame/FrameWindow.qml +++ b/quickshell/Modules/Frame/FrameWindow.qml @@ -127,6 +127,35 @@ PanelWindow { win._surfaceRevision; return ConnectedModeState.surfaceDescriptor(win._screenName, "popout"); } + component ConnectorBlurRegion: Region { + id: connectorBlur + required property string barSide + required property var body + required property string placement + required property real connectorRadius + property bool far: false + property bool bodyActive: true + + readonly property bool _active: bodyActive && connectorRadius > 0 + readonly property var _rect: far ? SurfaceGeometry.farConnectorRect(barSide, body, placement, connectorRadius, win._dpr) : SurfaceGeometry.connectorRect(barSide, body, placement, 0, connectorRadius, win._dpr) + readonly property string _arcCorner: far ? ConnectorGeometry.arcCorner(win._farConnectorBarSide(barSide, placement), win._farConnectorPlacement(barSide, placement)) : ConnectorGeometry.arcCorner(barSide, placement) + + x: _active ? Math.round(_rect.x) : 0 + y: _active ? Math.round(_rect.y) : 0 + width: _active ? Math.round(_rect.width) : 0 + height: _active ? Math.round(_rect.height) : 0 + + Region { + readonly property bool _active: connectorBlur.width > 0 && connectorBlur.height > 0 + + intersection: Intersection.Subtract + radius: connectorBlur.connectorRadius + x: _active ? Math.round(win._connectorCutoutX(connectorBlur.x, connectorBlur.width, connectorBlur._arcCorner, connectorBlur.connectorRadius)) : 0 + y: _active ? Math.round(win._connectorCutoutY(connectorBlur.y, connectorBlur.height, connectorBlur._arcCorner, connectorBlur.connectorRadius)) : 0 + width: _active ? Math.round(connectorBlur.connectorRadius * 2) : 0 + height: _active ? Math.round(connectorBlur.connectorRadius * 2) : 0 + } + } component DockBlurRegion: Region { id: dockBlur required property var dockSurface @@ -162,55 +191,19 @@ PanelWindow { width: _active ? _capWidth : 0 height: _active ? _capHeight : 0 } - Region { - id: _dockLeftConnectorBlurAnchor - - readonly property bool _active: _dockBodyBlurAnchor._active && dockBlur.connectorRadius > 0 - readonly property var _rect: SurfaceGeometry.connectorRect(dockBlur.descriptor.barSide, dockBlur.geometry, "left", 0, dockBlur.connectorRadius, win._dpr) - - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _dockLeftConnectorCutout - - readonly property bool _active: _dockLeftConnectorBlurAnchor.width > 0 && _dockLeftConnectorBlurAnchor.height > 0 - readonly property string _arcCorner: ConnectorGeometry.arcCorner(dockBlur.descriptor.barSide, "left") - - intersection: Intersection.Subtract - radius: dockBlur.connectorRadius - x: _active ? Math.round(win._connectorCutoutX(_dockLeftConnectorBlurAnchor.x, _dockLeftConnectorBlurAnchor.width, _arcCorner, dockBlur.connectorRadius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_dockLeftConnectorBlurAnchor.y, _dockLeftConnectorBlurAnchor.height, _arcCorner, dockBlur.connectorRadius)) : 0 - width: _active ? Math.round(dockBlur.connectorRadius * 2) : 0 - height: _active ? Math.round(dockBlur.connectorRadius * 2) : 0 - } + ConnectorBlurRegion { + barSide: dockBlur.descriptor.barSide + body: dockBlur.geometry + placement: "left" + bodyActive: _dockBodyBlurAnchor._active + connectorRadius: dockBlur.connectorRadius } - Region { - id: _dockRightConnectorBlurAnchor - - readonly property bool _active: _dockBodyBlurAnchor._active && dockBlur.connectorRadius > 0 - readonly property var _rect: SurfaceGeometry.connectorRect(dockBlur.descriptor.barSide, dockBlur.geometry, "right", 0, dockBlur.connectorRadius, win._dpr) - - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _dockRightConnectorCutout - - readonly property bool _active: _dockRightConnectorBlurAnchor.width > 0 && _dockRightConnectorBlurAnchor.height > 0 - readonly property string _arcCorner: ConnectorGeometry.arcCorner(dockBlur.descriptor.barSide, "right") - - intersection: Intersection.Subtract - radius: dockBlur.connectorRadius - x: _active ? Math.round(win._connectorCutoutX(_dockRightConnectorBlurAnchor.x, _dockRightConnectorBlurAnchor.width, _arcCorner, dockBlur.connectorRadius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_dockRightConnectorBlurAnchor.y, _dockRightConnectorBlurAnchor.height, _arcCorner, dockBlur.connectorRadius)) : 0 - width: _active ? Math.round(dockBlur.connectorRadius * 2) : 0 - height: _active ? Math.round(dockBlur.connectorRadius * 2) : 0 - } + ConnectorBlurRegion { + barSide: dockBlur.descriptor.barSide + body: dockBlur.geometry + placement: "right" + bodyActive: _dockBodyBlurAnchor._active + connectorRadius: dockBlur.connectorRadius } } @@ -253,6 +246,23 @@ PanelWindow { connector: SurfaceGeometry.connectorRadii(descriptor, body, win._ccr, radius, win._dpr, true).near }; } + readonly property var _slideoutDescriptor: { + win._surfaceRevision; + return ConnectedModeState.surfaceDescriptor(win._screenName, "slideout"); + } + readonly property var _slideoutSurface: { + const descriptor = win._slideoutDescriptor; + if (!win._connectedActive || !descriptor.visible) + return null; + const body = win._clampNear(descriptor.barSide, SurfaceGeometry.animatedBodyRect(descriptor, win._dpr)); + if (body.width < 1 || body.height < 1) + return null; + return { + descriptor: descriptor, + body: body, + radii: SurfaceGeometry.connectorRadii(descriptor, body, win._ccr, win._surfaceRadius, win._dpr, true) + }; + } readonly property var _modalDescriptor: { win._surfaceRevision; return ConnectedModeState.surfaceDescriptor(win._screenName, "modal"); @@ -324,7 +334,7 @@ PanelWindow { readonly property real _surfaceRadius: Theme.connectedSurfaceRadius readonly property real _seamOverlap: Theme.hairline(win._dpr) readonly property bool _disableLayer: Quickshell.env("DMS_DISABLE_LAYER") === "true" || Quickshell.env("DMS_DISABLE_LAYER") === "1" - readonly property bool _elevationShadow: win._connectedActive && Theme.elevationEnabled && !win._disableLayer + readonly property bool _elevationShadow: win._connectedActive && Theme.elevationEnabled && (SettingsData.barElevationEnabled || SettingsData.popoutElevationEnabled || SettingsData.modalElevationEnabled) && !win._disableLayer function _clampNear(side, b) { const r = { "x": b.x, @@ -387,7 +397,7 @@ PanelWindow { "param": Qt.vector4d(0, 0, 0, 0) }) - // Slots 0-3 hold popout, modal, notification and docks; the island's spring steps every frame, so it owns slot 4 alone. + // Slots 0-3 hold popout, modal, notification and docks; the island and slideout spring every frame, so each owns a slot alone (4, 5). readonly property var _sdfSlots: { win._surfaceRevision; const src = win._unifiedSurfaces(); @@ -415,6 +425,24 @@ PanelWindow { } }); } + + readonly property var _slideoutSdfSlot: { + const slideout = win._slideoutSurface; + if (!slideout) + return win._emptySdfSlot; + return win._sdfSlot({ + "side": slideout.descriptor.barSide, + "body": slideout.body, + "radii": { + "farCr": slideout.radii.far, + "startCr": slideout.radii.start, + "endCr": slideout.radii.end, + "farStartCr": slideout.radii.farStart, + "farEndCr": slideout.radii.farEnd, + "surfaceRadius": win._surfaceRadius + } + }); + } function _regionInt(value) { return Math.max(0, Math.round(Theme.px(value, win._dpr))); } @@ -534,59 +562,19 @@ PanelWindow { width: _active ? _capWidth : 0 height: _active ? _capHeight : 0 } - Region { - id: _popoutLeftConnectorBlurAnchor - - readonly property real _radius: win._popoutConnectorRadiusLeft - readonly property bool _active: _popoutBodyBlurAnchor._active && _radius > 0 - readonly property var _rect: SurfaceGeometry.connectorRect(win._popoutDescriptor.barSide, win._popoutBodyGeometry, "left", 0, _radius, win._dpr) - - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _popoutLeftConnectorCutout - - readonly property bool _active: _popoutLeftConnectorBlurAnchor.width > 0 && _popoutLeftConnectorBlurAnchor.height > 0 - readonly property string _arcCorner: ConnectorGeometry.arcCorner(win._popoutDescriptor.barSide, "left") - readonly property real _radius: win._popoutConnectorRadiusLeft - - intersection: Intersection.Subtract - radius: win._popoutConnectorRadiusLeft - x: _active ? Math.round(win._connectorCutoutX(_popoutLeftConnectorBlurAnchor.x, _popoutLeftConnectorBlurAnchor.width, _arcCorner, _radius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_popoutLeftConnectorBlurAnchor.y, _popoutLeftConnectorBlurAnchor.height, _arcCorner, _radius)) : 0 - width: _active ? Math.round(_radius * 2) : 0 - height: _active ? Math.round(_radius * 2) : 0 - } + ConnectorBlurRegion { + barSide: win._popoutDescriptor.barSide + body: win._popoutBodyGeometry + placement: "left" + bodyActive: _popoutBodyBlurAnchor._active + connectorRadius: win._popoutConnectorRadiusLeft } - Region { - id: _popoutRightConnectorBlurAnchor - - readonly property real _radius: win._popoutConnectorRadiusRight - readonly property bool _active: _popoutBodyBlurAnchor._active && _radius > 0 - readonly property var _rect: SurfaceGeometry.connectorRect(win._popoutDescriptor.barSide, win._popoutBodyGeometry, "right", 0, _radius, win._dpr) - - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _popoutRightConnectorCutout - - readonly property bool _active: _popoutRightConnectorBlurAnchor.width > 0 && _popoutRightConnectorBlurAnchor.height > 0 - readonly property string _arcCorner: ConnectorGeometry.arcCorner(win._popoutDescriptor.barSide, "right") - readonly property real _radius: win._popoutConnectorRadiusRight - - intersection: Intersection.Subtract - radius: win._popoutConnectorRadiusRight - x: _active ? Math.round(win._connectorCutoutX(_popoutRightConnectorBlurAnchor.x, _popoutRightConnectorBlurAnchor.width, _arcCorner, _radius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_popoutRightConnectorBlurAnchor.y, _popoutRightConnectorBlurAnchor.height, _arcCorner, _radius)) : 0 - width: _active ? Math.round(_radius * 2) : 0 - height: _active ? Math.round(_radius * 2) : 0 - } + ConnectorBlurRegion { + barSide: win._popoutDescriptor.barSide + body: win._popoutBodyGeometry + placement: "right" + bodyActive: _popoutBodyBlurAnchor._active + connectorRadius: win._popoutConnectorRadiusRight } Region { id: _popoutFarStartBodyBlurCap @@ -612,63 +600,21 @@ PanelWindow { width: _active ? Math.round(_rect.width) : 0 height: _active ? Math.round(_rect.height) : 0 } - Region { - id: _popoutFarStartConnectorBlurAnchor - - readonly property real _radius: win._effectivePopoutFarStartCcr - readonly property bool _active: _popoutBodyBlurAnchor._active && _radius > 0 - readonly property var _rect: SurfaceGeometry.farConnectorRect(win._popoutDescriptor.barSide, win._popoutBodyGeometry, "left", _radius, win._dpr) - - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _popoutFarStartConnectorCutout - - readonly property bool _active: _popoutFarStartConnectorBlurAnchor.width > 0 && _popoutFarStartConnectorBlurAnchor.height > 0 - readonly property string _barSide: win._farConnectorBarSide(win._popoutDescriptor.barSide, "left") - readonly property string _placement: win._farConnectorPlacement(win._popoutDescriptor.barSide, "left") - readonly property string _arcCorner: ConnectorGeometry.arcCorner(_barSide, _placement) - readonly property real _radius: win._effectivePopoutFarStartCcr - - intersection: Intersection.Subtract - radius: win._effectivePopoutFarStartCcr - x: _active ? Math.round(win._connectorCutoutX(_popoutFarStartConnectorBlurAnchor.x, _popoutFarStartConnectorBlurAnchor.width, _arcCorner, _radius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_popoutFarStartConnectorBlurAnchor.y, _popoutFarStartConnectorBlurAnchor.height, _arcCorner, _radius)) : 0 - width: _active ? Math.round(_radius * 2) : 0 - height: _active ? Math.round(_radius * 2) : 0 - } + ConnectorBlurRegion { + barSide: win._popoutDescriptor.barSide + body: win._popoutBodyGeometry + placement: "left" + far: true + bodyActive: _popoutBodyBlurAnchor._active + connectorRadius: win._effectivePopoutFarStartCcr } - Region { - id: _popoutFarEndConnectorBlurAnchor - - readonly property real _radius: win._effectivePopoutFarEndCcr - readonly property bool _active: _popoutBodyBlurAnchor._active && _radius > 0 - readonly property var _rect: SurfaceGeometry.farConnectorRect(win._popoutDescriptor.barSide, win._popoutBodyGeometry, "right", _radius, win._dpr) - - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _popoutFarEndConnectorCutout - - readonly property bool _active: _popoutFarEndConnectorBlurAnchor.width > 0 && _popoutFarEndConnectorBlurAnchor.height > 0 - readonly property string _barSide: win._farConnectorBarSide(win._popoutDescriptor.barSide, "right") - readonly property string _placement: win._farConnectorPlacement(win._popoutDescriptor.barSide, "right") - readonly property string _arcCorner: ConnectorGeometry.arcCorner(_barSide, _placement) - readonly property real _radius: win._effectivePopoutFarEndCcr - - intersection: Intersection.Subtract - radius: win._effectivePopoutFarEndCcr - x: _active ? Math.round(win._connectorCutoutX(_popoutFarEndConnectorBlurAnchor.x, _popoutFarEndConnectorBlurAnchor.width, _arcCorner, _radius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_popoutFarEndConnectorBlurAnchor.y, _popoutFarEndConnectorBlurAnchor.height, _arcCorner, _radius)) : 0 - width: _active ? Math.round(_radius * 2) : 0 - height: _active ? Math.round(_radius * 2) : 0 - } + ConnectorBlurRegion { + barSide: win._popoutDescriptor.barSide + body: win._popoutBodyGeometry + placement: "right" + far: true + bodyActive: _popoutBodyBlurAnchor._active + connectorRadius: win._effectivePopoutFarEndCcr } DockBlurRegion { @@ -713,59 +659,19 @@ PanelWindow { width: _active ? _capWidth : 0 height: _active ? _capHeight : 0 } - Region { - id: _notifLeftConnectorBlurAnchor - - readonly property real _radius: win._notifConnectorRadiusLeft - readonly property bool _active: _notifBodySceneBlurAnchor._active && _radius > 0 - readonly property var _rect: SurfaceGeometry.connectorRect(win._notifDescriptor.barSide, _notifBodySceneBlurAnchor, "left", 0, _radius, win._dpr) - - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _notifLeftConnectorCutout - - readonly property bool _active: _notifLeftConnectorBlurAnchor.width > 0 && _notifLeftConnectorBlurAnchor.height > 0 - readonly property string _arcCorner: ConnectorGeometry.arcCorner(win._notifDescriptor.barSide, "left") - readonly property real _radius: win._notifConnectorRadiusLeft - - intersection: Intersection.Subtract - radius: win._notifConnectorRadiusLeft - x: _active ? Math.round(win._connectorCutoutX(_notifLeftConnectorBlurAnchor.x, _notifLeftConnectorBlurAnchor.width, _arcCorner, _radius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_notifLeftConnectorBlurAnchor.y, _notifLeftConnectorBlurAnchor.height, _arcCorner, _radius)) : 0 - width: _active ? Math.round(_radius * 2) : 0 - height: _active ? Math.round(_radius * 2) : 0 - } + ConnectorBlurRegion { + barSide: win._notifDescriptor.barSide + body: _notifBodySceneBlurAnchor + placement: "left" + bodyActive: _notifBodySceneBlurAnchor._active + connectorRadius: win._notifConnectorRadiusLeft } - Region { - id: _notifRightConnectorBlurAnchor - - readonly property real _radius: win._notifConnectorRadiusRight - readonly property bool _active: _notifBodySceneBlurAnchor._active && _radius > 0 - readonly property var _rect: SurfaceGeometry.connectorRect(win._notifDescriptor.barSide, _notifBodySceneBlurAnchor, "right", 0, _radius, win._dpr) - - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _notifRightConnectorCutout - - readonly property bool _active: _notifRightConnectorBlurAnchor.width > 0 && _notifRightConnectorBlurAnchor.height > 0 - readonly property string _arcCorner: ConnectorGeometry.arcCorner(win._notifDescriptor.barSide, "right") - readonly property real _radius: win._notifConnectorRadiusRight - - intersection: Intersection.Subtract - radius: win._notifConnectorRadiusRight - x: _active ? Math.round(win._connectorCutoutX(_notifRightConnectorBlurAnchor.x, _notifRightConnectorBlurAnchor.width, _arcCorner, _radius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_notifRightConnectorBlurAnchor.y, _notifRightConnectorBlurAnchor.height, _arcCorner, _radius)) : 0 - width: _active ? Math.round(_radius * 2) : 0 - height: _active ? Math.round(_radius * 2) : 0 - } + ConnectorBlurRegion { + barSide: win._notifDescriptor.barSide + body: _notifBodySceneBlurAnchor + placement: "right" + bodyActive: _notifBodySceneBlurAnchor._active + connectorRadius: win._notifConnectorRadiusRight } Region { id: _notifFarStartBodyBlurCap @@ -791,63 +697,46 @@ PanelWindow { width: _active ? Math.round(_rect.width) : 0 height: _active ? Math.round(_rect.height) : 0 } - Region { - id: _notifFarStartConnectorBlurAnchor - - readonly property real _radius: win._effectiveNotifFarStartCcr - readonly property bool _active: _notifBodySceneBlurAnchor._active && _radius > 0 - readonly property var _rect: SurfaceGeometry.farConnectorRect(win._notifDescriptor.barSide, _notifBodySceneBlurAnchor, "left", _radius, win._dpr) - - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _notifFarStartConnectorCutout - - readonly property bool _active: _notifFarStartConnectorBlurAnchor.width > 0 && _notifFarStartConnectorBlurAnchor.height > 0 - readonly property string _barSide: win._farConnectorBarSide(win._notifDescriptor.barSide, "left") - readonly property string _placement: win._farConnectorPlacement(win._notifDescriptor.barSide, "left") - readonly property string _arcCorner: ConnectorGeometry.arcCorner(_barSide, _placement) - readonly property real _radius: win._effectiveNotifFarStartCcr - - intersection: Intersection.Subtract - radius: win._effectiveNotifFarStartCcr - x: _active ? Math.round(win._connectorCutoutX(_notifFarStartConnectorBlurAnchor.x, _notifFarStartConnectorBlurAnchor.width, _arcCorner, _radius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_notifFarStartConnectorBlurAnchor.y, _notifFarStartConnectorBlurAnchor.height, _arcCorner, _radius)) : 0 - width: _active ? Math.round(_radius * 2) : 0 - height: _active ? Math.round(_radius * 2) : 0 - } + ConnectorBlurRegion { + barSide: win._notifDescriptor.barSide + body: _notifBodySceneBlurAnchor + placement: "left" + far: true + bodyActive: _notifBodySceneBlurAnchor._active + connectorRadius: win._effectiveNotifFarStartCcr } + ConnectorBlurRegion { + barSide: win._notifDescriptor.barSide + body: _notifBodySceneBlurAnchor + placement: "right" + far: true + bodyActive: _notifBodySceneBlurAnchor._active + connectorRadius: win._effectiveNotifFarEndCcr + } + Region { - id: _notifFarEndConnectorBlurAnchor + id: _slideoutBodyBlurAnchor - readonly property real _radius: win._effectiveNotifFarEndCcr - readonly property bool _active: _notifBodySceneBlurAnchor._active && _radius > 0 - readonly property var _rect: SurfaceGeometry.farConnectorRect(win._notifDescriptor.barSide, _notifBodySceneBlurAnchor, "right", _radius, win._dpr) + readonly property bool _active: win._blurSurfacesActive && win._slideoutSurface !== null - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _notifFarEndConnectorCutout - - readonly property bool _active: _notifFarEndConnectorBlurAnchor.width > 0 && _notifFarEndConnectorBlurAnchor.height > 0 - readonly property string _barSide: win._farConnectorBarSide(win._notifDescriptor.barSide, "right") - readonly property string _placement: win._farConnectorPlacement(win._notifDescriptor.barSide, "right") - readonly property string _arcCorner: ConnectorGeometry.arcCorner(_barSide, _placement) - readonly property real _radius: win._effectiveNotifFarEndCcr - - intersection: Intersection.Subtract - radius: win._effectiveNotifFarEndCcr - x: _active ? Math.round(win._connectorCutoutX(_notifFarEndConnectorBlurAnchor.x, _notifFarEndConnectorBlurAnchor.width, _arcCorner, _radius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_notifFarEndConnectorBlurAnchor.y, _notifFarEndConnectorBlurAnchor.height, _arcCorner, _radius)) : 0 - width: _active ? Math.round(_radius * 2) : 0 - height: _active ? Math.round(_radius * 2) : 0 - } + x: _active ? Math.round(win._slideoutSurface.body.x) : 0 + y: _active ? Math.round(win._slideoutSurface.body.y) : 0 + width: _active ? Math.round(win._slideoutSurface.body.width) : 0 + height: _active ? Math.round(win._slideoutSurface.body.height) : 0 + } + ConnectorBlurRegion { + barSide: win._slideoutDescriptor.barSide + body: _slideoutBodyBlurAnchor + placement: "left" + far: true + connectorRadius: _slideoutBodyBlurAnchor._active ? Math.min(win._slideoutSurface.radii.farStart, win._slideoutSurface.body.width) : 0 + } + ConnectorBlurRegion { + barSide: win._slideoutDescriptor.barSide + body: _slideoutBodyBlurAnchor + placement: "right" + far: true + connectorRadius: _slideoutBodyBlurAnchor._active ? Math.min(win._slideoutSurface.radii.farEnd, win._slideoutSurface.body.width) : 0 } Region { @@ -875,59 +764,19 @@ PanelWindow { width: _active ? _capWidth : 0 height: _active ? _capHeight : 0 } - Region { - id: _modalLeftConnectorBlurAnchor - - readonly property real _radius: win._modalConnectorRadiusLeft - readonly property bool _active: _modalBodyBlurAnchor._active && _radius > 0 - readonly property var _rect: SurfaceGeometry.connectorRect(win._modalDescriptor.barSide, win._modalBodyGeometry, "left", 0, _radius, win._dpr) - - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _modalLeftConnectorCutout - - readonly property bool _active: _modalLeftConnectorBlurAnchor.width > 0 && _modalLeftConnectorBlurAnchor.height > 0 - readonly property string _arcCorner: ConnectorGeometry.arcCorner(win._modalDescriptor.barSide, "left") - readonly property real _radius: win._modalConnectorRadiusLeft - - intersection: Intersection.Subtract - radius: win._modalConnectorRadiusLeft - x: _active ? Math.round(win._connectorCutoutX(_modalLeftConnectorBlurAnchor.x, _modalLeftConnectorBlurAnchor.width, _arcCorner, _radius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_modalLeftConnectorBlurAnchor.y, _modalLeftConnectorBlurAnchor.height, _arcCorner, _radius)) : 0 - width: _active ? Math.round(_radius * 2) : 0 - height: _active ? Math.round(_radius * 2) : 0 - } + ConnectorBlurRegion { + barSide: win._modalDescriptor.barSide + body: win._modalBodyGeometry + placement: "left" + bodyActive: _modalBodyBlurAnchor._active + connectorRadius: win._modalConnectorRadiusLeft } - Region { - id: _modalRightConnectorBlurAnchor - - readonly property real _radius: win._modalConnectorRadiusRight - readonly property bool _active: _modalBodyBlurAnchor._active && _radius > 0 - readonly property var _rect: SurfaceGeometry.connectorRect(win._modalDescriptor.barSide, win._modalBodyGeometry, "right", 0, _radius, win._dpr) - - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _modalRightConnectorCutout - - readonly property bool _active: _modalRightConnectorBlurAnchor.width > 0 && _modalRightConnectorBlurAnchor.height > 0 - readonly property string _arcCorner: ConnectorGeometry.arcCorner(win._modalDescriptor.barSide, "right") - readonly property real _radius: win._modalConnectorRadiusRight - - intersection: Intersection.Subtract - radius: win._modalConnectorRadiusRight - x: _active ? Math.round(win._connectorCutoutX(_modalRightConnectorBlurAnchor.x, _modalRightConnectorBlurAnchor.width, _arcCorner, _radius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_modalRightConnectorBlurAnchor.y, _modalRightConnectorBlurAnchor.height, _arcCorner, _radius)) : 0 - width: _active ? Math.round(_radius * 2) : 0 - height: _active ? Math.round(_radius * 2) : 0 - } + ConnectorBlurRegion { + barSide: win._modalDescriptor.barSide + body: win._modalBodyGeometry + placement: "right" + bodyActive: _modalBodyBlurAnchor._active + connectorRadius: win._modalConnectorRadiusRight } Region { id: _modalFarStartBodyBlurCap @@ -953,63 +802,21 @@ PanelWindow { width: _active ? Math.round(_rect.width) : 0 height: _active ? Math.round(_rect.height) : 0 } - Region { - id: _modalFarStartConnectorBlurAnchor - - readonly property real _radius: win._effectiveModalFarStartCcr - readonly property bool _active: _modalBodyBlurAnchor._active && _radius > 0 - readonly property var _rect: SurfaceGeometry.farConnectorRect(win._modalDescriptor.barSide, win._modalBodyGeometry, "left", _radius, win._dpr) - - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _modalFarStartConnectorCutout - - readonly property bool _active: _modalFarStartConnectorBlurAnchor.width > 0 && _modalFarStartConnectorBlurAnchor.height > 0 - readonly property string _barSide: win._farConnectorBarSide(win._modalDescriptor.barSide, "left") - readonly property string _placement: win._farConnectorPlacement(win._modalDescriptor.barSide, "left") - readonly property string _arcCorner: ConnectorGeometry.arcCorner(_barSide, _placement) - readonly property real _radius: win._effectiveModalFarStartCcr - - intersection: Intersection.Subtract - radius: win._effectiveModalFarStartCcr - x: _active ? Math.round(win._connectorCutoutX(_modalFarStartConnectorBlurAnchor.x, _modalFarStartConnectorBlurAnchor.width, _arcCorner, _radius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_modalFarStartConnectorBlurAnchor.y, _modalFarStartConnectorBlurAnchor.height, _arcCorner, _radius)) : 0 - width: _active ? Math.round(_radius * 2) : 0 - height: _active ? Math.round(_radius * 2) : 0 - } + ConnectorBlurRegion { + barSide: win._modalDescriptor.barSide + body: win._modalBodyGeometry + placement: "left" + far: true + bodyActive: _modalBodyBlurAnchor._active + connectorRadius: win._effectiveModalFarStartCcr } - Region { - id: _modalFarEndConnectorBlurAnchor - - readonly property real _radius: win._effectiveModalFarEndCcr - readonly property bool _active: _modalBodyBlurAnchor._active && _radius > 0 - readonly property var _rect: SurfaceGeometry.farConnectorRect(win._modalDescriptor.barSide, win._modalBodyGeometry, "right", _radius, win._dpr) - - x: _active ? Math.round(_rect.x) : 0 - y: _active ? Math.round(_rect.y) : 0 - width: _active ? Math.round(_rect.width) : 0 - height: _active ? Math.round(_rect.height) : 0 - - Region { - id: _modalFarEndConnectorCutout - - readonly property bool _active: _modalFarEndConnectorBlurAnchor.width > 0 && _modalFarEndConnectorBlurAnchor.height > 0 - readonly property string _barSide: win._farConnectorBarSide(win._modalDescriptor.barSide, "right") - readonly property string _placement: win._farConnectorPlacement(win._modalDescriptor.barSide, "right") - readonly property string _arcCorner: ConnectorGeometry.arcCorner(_barSide, _placement) - readonly property real _radius: win._effectiveModalFarEndCcr - - intersection: Intersection.Subtract - radius: win._effectiveModalFarEndCcr - x: _active ? Math.round(win._connectorCutoutX(_modalFarEndConnectorBlurAnchor.x, _modalFarEndConnectorBlurAnchor.width, _arcCorner, _radius)) : 0 - y: _active ? Math.round(win._connectorCutoutY(_modalFarEndConnectorBlurAnchor.y, _modalFarEndConnectorBlurAnchor.height, _arcCorner, _radius)) : 0 - width: _active ? Math.round(_radius * 2) : 0 - height: _active ? Math.round(_radius * 2) : 0 - } + ConnectorBlurRegion { + barSide: win._modalDescriptor.barSide + body: win._modalBodyGeometry + placement: "right" + far: true + bodyActive: _modalBodyBlurAnchor._active + connectorRadius: win._effectiveModalFarEndCcr } } @@ -1334,6 +1141,10 @@ PanelWindow { property vector4d chromeCorner4: win._islandSdfSlot.corner property vector4d chromeK4: win._islandSdfSlot.k property vector4d chromeParam4: win._islandSdfSlot.param + property vector4d chromeRect5: win._slideoutSdfSlot.rect + property vector4d chromeCorner5: win._slideoutSdfSlot.corner + property vector4d chromeK5: win._slideoutSdfSlot.k + property vector4d chromeParam5: win._slideoutSdfSlot.param } Loader { diff --git a/quickshell/Modules/Notepad/Notepad.qml b/quickshell/Modules/Notepad/Notepad.qml index 002f43bde..d73a18a58 100644 --- a/quickshell/Modules/Notepad/Notepad.qml +++ b/quickshell/Modules/Notepad/Notepad.qml @@ -544,6 +544,7 @@ Item { id: notepadSettings anchors.fill: parent isVisible: showSettingsMenu + frameManagesGap: root.slideout?.frameOwnsConnectedChrome ?? false onSettingsRequested: showSettingsMenu = !showSettingsMenu onFindRequested: { showSettingsMenu = false; diff --git a/quickshell/Modules/Notepad/NotepadSettings.qml b/quickshell/Modules/Notepad/NotepadSettings.qml index 0ee5eb3f4..01ea96e5e 100644 --- a/quickshell/Modules/Notepad/NotepadSettings.qml +++ b/quickshell/Modules/Notepad/NotepadSettings.qml @@ -12,6 +12,7 @@ Item { property var cachedMonoFamilies: [] property bool fontsEnumerated: false property bool shortcutsExpanded: false + property bool frameManagesGap: false signal settingsRequested signal findRequested @@ -449,7 +450,8 @@ Item { anchors.leftMargin: -Theme.spacingM width: parent.width + Theme.spacingM text: I18n.tr("Auto Compositor Gaps") - description: I18n.tr("Inset the Notepad from screen edges using the compositor's configured gaps") + description: root.frameManagesGap ? I18n.tr("Managed by Frame in Connected Mode") : I18n.tr("Inset the Notepad from screen edges using the compositor's configured gaps") + enabled: !root.frameManagesGap checked: SettingsData.notepadUseCompositorGap onToggled: checked => { SettingsData.notepadUseCompositorGap = checked; @@ -469,6 +471,7 @@ Item { anchors.leftMargin: Theme.spacingXS width: parent.width - Theme.spacingXS * 2 visible: !SettingsData.notepadUseCompositorGap + enabled: !root.frameManagesGap value: SettingsData.notepadEdgeGap minimum: 0 maximum: 64 diff --git a/quickshell/Modules/Settings/DBarTab.qml b/quickshell/Modules/Settings/DBarTab.qml index 5ad773a6c..09bc5db24 100644 --- a/quickshell/Modules/Settings/DBarTab.qml +++ b/quickshell/Modules/Settings/DBarTab.qml @@ -20,6 +20,7 @@ Item { readonly property string clickThroughHint: I18n.tr("Disabled by Click through", "bar hover popouts card, Click through is the Advanced toggle name") readonly property bool selectedIslandFree: bar.selectedBarIsIsland && SettingsData.islandFreePlacement(bar.selectedBarConfig) readonly property bool selectedIslandDocked: bar.selectedBarIsIsland && !selectedIslandFree + readonly property bool popupGapsZeroed: SettingsData.barUsesConnectedFrameStyle(bar.selectedBarConfig) && !(bar.selectedBarConfig?.useOverlayLayer ?? false) readonly property int placementIndex: !bar.islandSetting("islandFloating") ? 0 : (bar.islandSetting("islandPlacement") === "free" ? 2 : 1) function setBarScreenPreferences(barId, prefs) { @@ -656,7 +657,7 @@ Item { text: I18n.tr("Auto popup gaps") description: I18n.tr("Gap between the bar and its popouts follows edge spacing", "bar auto popup gaps toggle description") tags: ["popup", "gaps", "auto"] - visible: !bar.selectedBarFrameStyled + visible: !bar.popupGapsZeroed checked: bar.selectedBarConfig?.popupGapsAuto ?? true onToggled: checked => SettingsData.updateBarConfig(bar.selectedBarId, { popupGapsAuto: checked @@ -664,7 +665,7 @@ Item { } SettingsSliderRow { - visible: !bar.selectedBarFrameStyled && !(bar.selectedBarConfig?.popupGapsAuto ?? true) + visible: !bar.popupGapsZeroed && !(bar.selectedBarConfig?.popupGapsAuto ?? true) text: I18n.tr("Gap size") tags: ["popup", "gaps", "size"] resetStore: bar diff --git a/quickshell/Modules/Settings/LauncherTab.qml b/quickshell/Modules/Settings/LauncherTab.qml index 923d1efb8..0ff3733b7 100644 --- a/quickshell/Modules/Settings/LauncherTab.qml +++ b/quickshell/Modules/Settings/LauncherTab.qml @@ -72,24 +72,16 @@ Item { title: I18n.tr("Default action") settingKey: "launcherStyle" - SettingsControlledBy { - visible: SettingsData.connectedFrameModeActive - parentModal: root.parentModal - section: "frameConnectedOptions" - settingLabel: I18n.tr("Default action") - reason: I18n.tr("Connected Frame Mode uses the connected launcher for default launcher shortcuts.") - } - SettingsButtonGroupRow { readonly property bool islandOffered: SettingsData.dankIslandEnabled || SettingsData.launcherStyle === "island" + readonly property bool spotlightOffered: !SettingsData.connectedFrameModeActive - visible: !SettingsData.connectedFrameModeActive settingKey: "launcherStyleSelector" tags: ["launcher", "style", "default", "spotlight", "full", "minimal", "island", "dankisland"] resetKeys: ["launcherStyle"] text: I18n.tr("Opens", "verb, row label, which launcher style the shortcut opens") - model: [I18n.tr("Full", "adjective, full size launcher style option"), I18n.tr("Spotlight", "launcher style option, small centered search bar")].concat(islandOffered ? [I18n.tr("Island")] : []) - values: ["full", "spotlight"].concat(islandOffered ? ["island"] : []) + model: [I18n.tr("Full", "adjective, full size launcher style option")].concat(spotlightOffered ? [I18n.tr("Spotlight", "launcher style option, small centered search bar")] : []).concat(islandOffered ? [I18n.tr("Island")] : []) + values: ["full"].concat(spotlightOffered ? ["spotlight"] : []).concat(islandOffered ? ["island"] : []) value: SettingsData.launcherStyle fallbackValue: "full" onValueSelected: value => SettingsData.set("launcherStyle", value) diff --git a/quickshell/Modules/Settings/Widgets/BarSelectionState.qml b/quickshell/Modules/Settings/Widgets/BarSelectionState.qml index 77abe7722..022207b7c 100644 --- a/quickshell/Modules/Settings/Widgets/BarSelectionState.qml +++ b/quickshell/Modules/Settings/Widgets/BarSelectionState.qml @@ -53,7 +53,7 @@ Item { return root.selectedBarScreens.filter(screen => ShellLayout.forScreen(screen)?.frameConfigured).length; } readonly property bool selectedBarFrameStyled: SettingsData.frameEnabled && root.frameStyledScreenCount === root.selectedBarScreens.length - readonly property bool selectedBarFrameSanitized: SettingsData.connectedFrameModeActive || root.selectedBarFrameStyled + readonly property bool selectedBarFrameSanitized: SettingsData.barUsesConnectedFrameStyle(root.selectedBarConfig) || root.selectedBarFrameStyled readonly property var positionChoices: ShellLayout.positionChoices(selectedBarConfig) function positionLabel(pos) { diff --git a/quickshell/Shaders/frag/connected_arc.frag b/quickshell/Shaders/frag/connected_arc.frag index ce77ac7ad..22406394c 100644 --- a/quickshell/Shaders/frag/connected_arc.frag +++ b/quickshell/Shaders/frag/connected_arc.frag @@ -16,7 +16,7 @@ layout(std140, binding = 0) uniform buf { vec4 shadowColor; // straight rgba; a = 0 disables both shadow terms vec4 shadowParam; // key: x = blur px, y = spread px, z,w = offset px vec4 ambientParam; // ambient: x = blur px, y = spread px, z = alpha - // Five chrome slots: rect = x,y,w,h px; corner and k = per-corner radii and junction fillets (TL, TR, BR, BL), + // Six chrome slots: rect = x,y,w,h px; corner and k = per-corner radii and junction fillets (TL, TR, BR, BL), // a corner is sharp exactly where its k > 0; param.x = active. vec4 chromeRect0; vec4 chromeCorner0; @@ -38,6 +38,10 @@ layout(std140, binding = 0) uniform buf { vec4 chromeCorner4; vec4 chromeK4; vec4 chromeParam4; + vec4 chromeRect5; + vec4 chromeCorner5; + vec4 chromeK5; + vec4 chromeParam5; } ubuf; float sdRoundBox(vec2 p, vec2 c, vec2 hs, float r) { @@ -85,6 +89,8 @@ float sceneDist(vec2 px) { d = smin(d, chromeDist(px, ubuf.chromeRect3, ubuf.chromeCorner3), chromeK(px, ubuf.chromeRect3, ubuf.chromeK3)); if (ubuf.chromeParam4.x > 0.5) d = smin(d, chromeDist(px, ubuf.chromeRect4, ubuf.chromeCorner4), chromeK(px, ubuf.chromeRect4, ubuf.chromeK4)); + if (ubuf.chromeParam5.x > 0.5) + d = smin(d, chromeDist(px, ubuf.chromeRect5, ubuf.chromeCorner5), chromeK(px, ubuf.chromeRect5, ubuf.chromeK5)); return d; } diff --git a/quickshell/Shaders/qsb/connected_arc.frag.qsb b/quickshell/Shaders/qsb/connected_arc.frag.qsb index 7911c0492706e3b0eff3a097b3e6e955dc76c9aa..04f594454e2e38f178ba2bb60a2ae3e18bad9f04 100644 GIT binary patch literal 8716 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 literal 8221 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 UJ1*o#W!mEed5gysdavSt0pZ)(2LJ#7 diff --git a/quickshell/Widgets/DContextMenu.qml b/quickshell/Widgets/DContextMenu.qml index 0c7cfc721..283842c0c 100644 --- a/quickshell/Widgets/DContextMenu.qml +++ b/quickshell/Widgets/DContextMenu.qml @@ -31,7 +31,7 @@ Item { property int selectedMenuIndex: -1 property bool keyboardNavigation: false readonly property alias contextWindow: menuWindow - readonly property bool blurActive: renderActive && openState && BlurService.enabled && Theme.connectedSurfaceBlurEnabled + readonly property bool blurActive: renderActive && openState && BlurService.enabled readonly property real maxMenuWidth: Math.max(0, (targetScreen?.width ?? Theme.launcherWidthMicro) - menuMargin * 2) readonly property real maxMenuHeight: Math.max(0, (targetScreen?.height ?? Theme.launcherHeightDefault) - menuMargin * 2) diff --git a/quickshell/Widgets/DFloatingWindow.qml b/quickshell/Widgets/DFloatingWindow.qml index 6e4ce43f4..b0307258f 100644 --- a/quickshell/Widgets/DFloatingWindow.qml +++ b/quickshell/Widgets/DFloatingWindow.qml @@ -25,7 +25,7 @@ FloatingWindow { // No surfaceColor gate: compositor window rules can make this window translucent. WindowBlur { targetWindow: root - blurEnabled: root.contentVisible && Theme.connectedSurfaceBlurEnabled + blurEnabled: root.contentVisible blurX: 0 blurY: 0 blurWidth: root.visible ? root.width : 0 diff --git a/quickshell/Widgets/DPopoutHost.qml b/quickshell/Widgets/DPopoutHost.qml index c1d281559..97fb04615 100644 --- a/quickshell/Widgets/DPopoutHost.qml +++ b/quickshell/Widgets/DPopoutHost.qml @@ -1816,7 +1816,7 @@ Item { WindowBlur { id: popoutBlur targetWindow: contentWindow - blurEnabled: Theme.connectedSurfaceBlurEnabled && !root.frameOwnsConnectedChrome + blurEnabled: !root.frameOwnsConnectedChrome && (!root.usesConnectedSurfaceChrome || Theme.connectedSurfaceBlurEnabled) surfaceColor: chrome.surfaceColor readonly property real s: Math.min(1, contentContainer.scaleValue) diff --git a/quickshell/Widgets/DSlideout.qml b/quickshell/Widgets/DSlideout.qml index beeaa6626..9e9c0ec9c 100644 --- a/quickshell/Widgets/DSlideout.qml +++ b/quickshell/Widgets/DSlideout.qml @@ -28,7 +28,14 @@ PanelWindow { property real edgeGap: 0 property string slideEdge: "right" readonly property bool slideFromLeft: slideEdge === "left" - readonly property real surfaceOriginX: slideFromLeft ? 0 : Math.max(0, (modelData?.width ?? width) - width) + // Opt-in: the frame has a single slideout slot per screen. + property bool frameSurfaceEnabled: false + readonly property bool frameOwnsConnectedChrome: frameSurfaceEnabled && mappedVisible && CompositorService.canShareConnectedFrameChromeForScreen(modelData) + readonly property int frameInsetTop: _frameInset("top") + readonly property int frameInsetBottom: _frameInset("bottom") + readonly property int frameInsetEdge: _frameInset(slideEdge) + readonly property real surfaceOriginX: slideFromLeft ? frameInsetEdge : Math.max(0, (modelData?.width ?? width) - width - frameInsetEdge) + readonly property real surfaceOriginY: frameInsetTop property Component content: null property bool contentRequested: false property string title: "" @@ -71,9 +78,68 @@ PanelWindow { const padding = 24; const topLeft = slideContainer.mapToItem(null, 0, 0); const globalX = surfaceOriginX + topLeft.x; - return gx >= globalX - padding && gx < globalX + slideContainer.width + padding && gy >= topLeft.y - padding && gy < topLeft.y + slideContainer.height + padding; + const globalY = surfaceOriginY + topLeft.y; + return gx >= globalX - padding && gx < globalX + slideContainer.width + padding && gy >= globalY - padding && gy < globalY + slideContainer.height + padding; } + function _frameInset(edge) { + if (!frameOwnsConnectedChrome) + return 0; + return Math.max(0, Math.round(Theme.px(SettingsData.frameEdgeReservation(modelData, edge) + SettingsData.dockReservationForEdge(modelData, edge), dpr))); + } + + readonly property string _frameSlot: ConnectedModeState.surfaceSlot("slideout") + readonly property bool _frameClaimActive: frameOwnsConnectedChrome && mappedVisible && _slideoutScreenName !== "" + readonly property real _frameSurfaceOpacity: Theme.connectedSurfaceColor.a + readonly property var _frameMotion: ({ + "bodyX": surfaceOriginX + slideContainer.x, + "bodyY": surfaceOriginY + slideContainer.y, + "bodyW": slideContainer.width, + "bodyH": slideContainer.height, + "animX": slideoutSlideSnapX + }) + property string _claimedScreen: "" + property string _claimedOwner: "" + + function _releaseFrameClaim() { + if (!_claimedScreen) + return; + ConnectedModeState.releaseSurface(_claimedScreen, _frameSlot, _claimedOwner); + _claimedScreen = ""; + _claimedOwner = ""; + } + + function _syncFrameClaim() { + const owner = layerNamespace + ":" + _slideoutScreenName; + if (!_frameClaimActive || _claimedScreen !== _slideoutScreenName || _claimedOwner !== owner) + _releaseFrameClaim(); + if (!_frameClaimActive) + return; + if (ConnectedModeState.claimSurface(_slideoutScreenName, _frameSlot, Object.assign({ + "kind": "slideout", + "visible": true, + "presented": true, + "phase": "open", + "barSide": slideEdge, + "omitStartConnector": true, + "omitEndConnector": true, + "opacity": _frameSurfaceOpacity + }, _frameMotion), owner)) { + _claimedScreen = _slideoutScreenName; + _claimedOwner = owner; + } + } + + on_FrameClaimActiveChanged: _syncFrameClaim() + on_SlideoutScreenNameChanged: _syncFrameClaim() + onSlideEdgeChanged: _syncFrameClaim() + on_FrameSurfaceOpacityChanged: _syncFrameClaim() + on_FrameMotionChanged: { + if (_claimedScreen) + ConnectedModeState.setSurfaceMotion(_claimedScreen, _frameSlot, _claimedOwner, _frameMotion); + } + Component.onDestruction: _releaseFrameClaim() + function toggle() { if (isVisible) { hide(); @@ -103,22 +169,30 @@ PanelWindow { return !PopoutManager.cursorOverBar(PopoutManager.hoverCursorGlobalX, PopoutManager.hoverCursorGlobalY); } onDismissRequested: root.hideFromHoverDismiss() - onHoverMoved: (sceneX, sceneY) => PopoutManager.updateHoverCursor(root.surfaceOriginX + sceneX, sceneY) + onHoverMoved: (sceneX, sceneY) => PopoutManager.updateHoverCursor(root.surfaceOriginX + sceneX, root.surfaceOriginY + sceneY) } - readonly property bool slideoutBlurActive: root.visible && BlurService.enabled && Theme.connectedSurfaceBlurEnabled + readonly property bool slideoutBlurActive: root.visible && !root.frameOwnsConnectedChrome && BlurService.enabled && Theme.connectedSurfaceBlurEnabled readonly property string _slideoutScreenName: modelData?.name ?? "" WlrLayershell.layer: (!suppressOverlayLayer && (triggerUsesOverlayLayer || CompositorService.framePeerSurfacesUseOverlayForScreen(modelData))) ? WlrLayershell.Overlay : WlrLayershell.Top - WlrLayershell.exclusiveZone: 0 + // Frame-owned: sit exactly in the frame cutout so the claimed body matches the frame's own geometry. + WlrLayershell.exclusiveZone: frameOwnsConnectedChrome ? -1 : 0 + + margins { + top: root.frameInsetTop + bottom: root.frameInsetBottom + left: root.slideFromLeft ? root.frameInsetEdge : 0 + right: root.slideFromLeft ? 0 : root.frameInsetEdge + } WlrLayershell.keyboardFocus: isVisible && !ModalManager.currentModalsByScreen[_slideoutScreenName] ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None readonly property real dpr: CompositorService.getScreenScale(root.screen) readonly property real alignedWidth: Theme.px(expandable && expandedWidth ? expandedWidthValue : slideoutWidth, dpr) onAlignedWidthChanged: widthSpring.retarget(alignedWidth) readonly property real alignedHeight: Theme.px(modelData ? modelData.height : 800, dpr) - readonly property real alignedEdgeGap: Theme.px(edgeGap, dpr) + readonly property real alignedEdgeGap: frameOwnsConnectedChrome ? 0 : Theme.px(edgeGap, dpr) readonly property real slideoutSlideSnapX: Theme.snap(slideContainer.slideOffset, dpr) onIsVisibleChanged: { @@ -195,10 +269,10 @@ PanelWindow { clip: true readonly property color slideoutSurfaceColor: { - if (root.customTransparency >= 0) - return Theme.withAlpha(Theme.hostSurface, root.customTransparency); if (Theme.isConnectedEffect) return Theme.connectedSurfaceColor; + if (root.customTransparency >= 0) + return Theme.withAlpha(Theme.hostSurface, root.customTransparency); return Theme.readableSurface; } @@ -218,7 +292,7 @@ PanelWindow { Rectangle { anchors.fill: parent - color: Theme.isConnectedEffect ? contentRect.slideoutSurfaceColor : "transparent" + color: Theme.isConnectedEffect && !root.frameOwnsConnectedChrome ? contentRect.slideoutSurfaceColor : "transparent" radius: Theme.isConnectedEffect ? Theme.connectedSurfaceRadius : Theme.windowRadius border.color: Theme.isConnectedEffect ? Theme.withAlpha(BlurService.borderColor, 0) : BlurService.borderColor border.width: Theme.isConnectedEffect ? 0 : BlurService.borderWidth @@ -314,6 +388,7 @@ PanelWindow { WindowBlur { targetWindow: root + blurEnabled: root.slideoutBlurActive surfaceColor: contentRect.slideoutSurfaceColor blurX: root.slideoutBlurActive ? slideContainer.x + root.slideoutSlideSnapX : 0 blurY: root.slideoutBlurActive ? slideContainer.y : 0 diff --git a/quickshell/Widgets/WindowBlur.qml b/quickshell/Widgets/WindowBlur.qml index 9e2021664..4473f8ad7 100644 --- a/quickshell/Widgets/WindowBlur.qml +++ b/quickshell/Widgets/WindowBlur.qml @@ -10,7 +10,7 @@ Item { visible: false required property var targetWindow - property bool blurEnabled: Theme.connectedSurfaceBlurEnabled + property bool blurEnabled: true property color surfaceColor: "transparent" property real blurX: 0 property real blurY: 0 diff --git a/quickshell/tests/frame-bar-styles.test.mjs b/quickshell/tests/frame-bar-styles.test.mjs new file mode 100644 index 000000000..25e74e85d --- /dev/null +++ b/quickshell/tests/frame-bar-styles.test.mjs @@ -0,0 +1,111 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import test from "node:test"; +import vm from "node:vm"; + +function functions(source, indent, context) { + const pattern = new RegExp(`^${indent}function (\\w+)\\(([^)]*)\\)(?:: \\w+)? \\{([\\s\\S]*?)^${indent}\\}`, "gm"); + for (const [, name, parameters, body] of source.matchAll(pattern)) { + const args = parameters.replace(/:\s*\w+/g, ""); + vm.runInContext(`function ${name}(${args}) {${body}}`, context); + } + return context; +} + +const settingsSource = readFileSync(new URL("../Common/SettingsData.qml", import.meta.url), "utf8"); +const plain = value => JSON.parse(JSON.stringify(value)); +const styled = { shadowIntensity: 40, squareCorners: true, attachToScreenEdge: true, gothCornersEnabled: true, borderEnabled: true }; +const zeroed = { shadowIntensity: 0, squareCorners: false, attachToScreenEdge: false, gothCornersEnabled: false, borderEnabled: false }; +const style = cfg => Object.fromEntries(Object.keys(styled).map(key => [key, cfg[key]])); + +function frameSettings({ active = true, frameOn = ["A"], barConfigs, backups = {}, screens = [{ name: "A" }, { name: "B" }] } = {}) { + const settings = functions(settingsSource, " ", vm.createContext({ + Quickshell: { screens }, + connectedFrameModeActive: active, + frameScreenPreferences: frameOn, + connectedFrameBarStyleBackups: backups, + barConfigs: barConfigs ?? [{ id: "bar1", screenPreferences: ["A"], ...styled }, { id: "bar2", screenPreferences: ["B"], ...styled }] + })); + settings.barConfigCoversScreen = (cfg, screen) => cfg.screenPreferences.includes(screen.name); + settings.isScreenInPreferences = (screen, prefs) => prefs.includes(screen.name); + settings.updateBarConfigs = () => settings.updates++; + settings._restoreIslandWidget = () => {}; + settings.updates = 0; + return settings; +} + +const bar = (settings, id) => settings.barConfigs.find(cfg => cfg.id === id); + +test("only bars on a frame display are sanitized", () => { + const settings = frameSettings(); + settings._reconcileConnectedFrameBarStyles(); + assert.deepEqual(style(bar(settings, "bar1")), zeroed); + assert.deepEqual(style(bar(settings, "bar2")), styled); + assert.deepEqual(plain(settings.connectedFrameBarStyleBackups), { bar1: styled }); + assert.equal(settings.barUsesConnectedFrameStyle(bar(settings, "bar1")), true); + assert.equal(settings.barUsesConnectedFrameStyle(bar(settings, "bar2")), false); + assert.equal(settings.barUsesConnectedFrameStyle(null), false); +}); + +test("moving the frame display swaps which bar is sanitized", () => { + const settings = frameSettings(); + settings._reconcileConnectedFrameBarStyles(); + settings.frameScreenPreferences = ["B"]; + settings._reconcileConnectedFrameBarStyles(); + assert.deepEqual(style(bar(settings, "bar1")), styled); + assert.deepEqual(style(bar(settings, "bar2")), zeroed); + assert.deepEqual(plain(settings.connectedFrameBarStyleBackups), { bar2: styled }); +}); + +test("leaving connected mode restores all bars and clears backups", () => { + const settings = frameSettings({ frameOn: ["A", "B"] }); + settings._reconcileConnectedFrameBarStyles(); + assert.deepEqual(style(bar(settings, "bar2")), zeroed); + settings.connectedFrameModeActive = false; + settings._reconcileConnectedFrameBarStyles(); + assert.deepEqual(style(bar(settings, "bar1")), styled); + assert.deepEqual(style(bar(settings, "bar2")), styled); + assert.deepEqual(plain(settings.connectedFrameBarStyleBackups), {}); + assert.equal(settings.barUsesConnectedFrameStyle(bar(settings, "bar1")), false); +}); + +test("legacy all-sanitized state self-heals on reconcile", () => { + const settings = frameSettings({ + barConfigs: [{ id: "bar1", screenPreferences: ["A"], ...zeroed }, { id: "bar2", screenPreferences: ["B"], ...zeroed }], + backups: { bar1: styled, bar2: styled, gone: styled } + }); + settings._reconcileConnectedFrameBarStyles(); + assert.deepEqual(style(bar(settings, "bar1")), zeroed); + assert.deepEqual(style(bar(settings, "bar2")), styled); + assert.deepEqual(plain(settings.connectedFrameBarStyleBackups), { bar1: styled }); + assert.equal(settings.updates, 1); + settings._reconcileConnectedFrameBarStyles(); + assert.equal(settings.updates, 1, "a settled state does not write again"); +}); + +test("committing a bar onto a frame display captures its styling first", () => { + const settings = frameSettings({ barConfigs: [{ id: "bar2", screenPreferences: ["B"], ...styled }] }); + const configs = plain(settings.barConfigs); + configs[0].screenPreferences = ["A"]; + settings._commitBarConfigs(configs); + assert.deepEqual(style(bar(settings, "bar2")), zeroed); + assert.deepEqual(plain(settings.connectedFrameBarStyleBackups), { bar2: styled }); +}); + +test("adding a bar off the frame display keeps its styling", () => { + const settings = frameSettings(); + settings._reconcileConnectedFrameBarStyles(); + settings.addBarConfig({ id: "bar3", screenPreferences: ["B"], ...styled }); + assert.deepEqual(style(bar(settings, "bar3")), styled); + assert.equal(plain(settings.connectedFrameBarStyleBackups).bar3, undefined); +}); + +test("an empty screen list leaves bar styles and backups alone", () => { + const settings = frameSettings(); + settings._reconcileConnectedFrameBarStyles(); + settings.Quickshell.screens = []; + settings._reconcileConnectedFrameBarStyles(); + assert.deepEqual(style(bar(settings, "bar1")), zeroed); + assert.deepEqual(plain(settings.connectedFrameBarStyleBackups), { bar1: styled }); + assert.equal(settings.updates, 1); +}); diff --git a/quickshell/tests/island-routing.test.mjs b/quickshell/tests/island-routing.test.mjs index fb0b98f38..3af1dd9e6 100644 --- a/quickshell/tests/island-routing.test.mjs +++ b/quickshell/tests/island-routing.test.mjs @@ -121,7 +121,7 @@ test("always-here is exclusive among islands and dots sharing a screen", () => { barConfigs: [{ id: "dot", dot: true, islandSharedRouting: "always" }, { id: "island", island: true }, { id: "remote", island: true, islandSharedRouting: "always" }] })); settings.updateBarConfigs = () => {}; - settings._sanitizeBarConfigsForConnectedFrame = configs => ({ configs }); + settings._applyConnectedFrameBarStyles = configs => ({ configs }); settings.barConfigCoversScreen = (cfg, screen) => covers[cfg.id].includes(screen.name); settings.setIslandSharedRouting("island", "always"); assert.deepEqual([...settings.barConfigs].map(cfg => cfg.islandSharedRouting), [undefined, "always", "always"], "same-screen always is reset, other-screen always is kept"); @@ -148,7 +148,7 @@ function hostedSettings(barConfigs, extra = {}) { settings.updateBarConfigs = () => {}; settings.barConfigCoversScreen = () => true; settings.getBarConfig = id => settings.barConfigs.find(cfg => cfg.id === id); - settings._sanitizeBarConfigsForConnectedFrame = configs => ({ configs }); + settings._applyConnectedFrameBarStyles = configs => ({ configs }); return settings; }