mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-15 15:45:20 -04:00
Cleanup
This commit is contained in:
@@ -131,7 +131,6 @@ Singleton {
|
|||||||
"slideY": 0
|
"slideY": 0
|
||||||
})
|
})
|
||||||
|
|
||||||
// Popout state (updated by DankPopout when connectedFrameModeActive)
|
|
||||||
property string popoutOwnerId: ""
|
property string popoutOwnerId: ""
|
||||||
property bool popoutVisible: false
|
property bool popoutVisible: false
|
||||||
property string popoutBarSide: "top"
|
property string popoutBarSide: "top"
|
||||||
@@ -145,14 +144,10 @@ Singleton {
|
|||||||
property bool popoutOmitStartConnector: false
|
property bool popoutOmitStartConnector: false
|
||||||
property bool popoutOmitEndConnector: false
|
property bool popoutOmitEndConnector: false
|
||||||
|
|
||||||
// Dock state (updated by Dock when connectedFrameModeActive), keyed by screen.name
|
|
||||||
property var dockStates: ({})
|
property var dockStates: ({})
|
||||||
|
|
||||||
// Dock slide offsets — hot-path updates separated from full geometry state
|
|
||||||
property var dockSlides: ({})
|
property var dockSlides: ({})
|
||||||
|
|
||||||
// Surfaces are keyed by screen.name. FrameWindow watches to refresh connected chrome
|
|
||||||
// after claim/release boundaries without tracking each animation frame
|
|
||||||
property var surfaceRevisions: ({})
|
property var surfaceRevisions: ({})
|
||||||
|
|
||||||
function _cloneDict(src) {
|
function _cloneDict(src) {
|
||||||
@@ -353,7 +348,6 @@ Singleton {
|
|||||||
dockStates = next;
|
dockStates = next;
|
||||||
_clearSurfaceDescriptor(screenName, "dock");
|
_clearSurfaceDescriptor(screenName, "dock");
|
||||||
|
|
||||||
// Also clear corresponding slide
|
|
||||||
if (dockSlides[screenName]) {
|
if (dockSlides[screenName]) {
|
||||||
const nextSlides = _cloneDict(dockSlides);
|
const nextSlides = _cloneDict(dockSlides);
|
||||||
delete nextSlides[screenName];
|
delete nextSlides[screenName];
|
||||||
@@ -454,7 +448,6 @@ Singleton {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DankModal / DankLauncherV2Modal State
|
|
||||||
readonly property var emptyModalState: ({
|
readonly property var emptyModalState: ({
|
||||||
"visible": false,
|
"visible": false,
|
||||||
"barSide": "bottom",
|
"barSide": "bottom",
|
||||||
@@ -655,9 +648,6 @@ Singleton {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prune state for screens that are no longer connected. Stale entries
|
|
||||||
// accumulate across hotplug cycles otherwise — Frame's per-screen
|
|
||||||
// FrameInstance doesn't notice when its peer dicts go orphan.
|
|
||||||
function _pruneToLiveScreens() {
|
function _pruneToLiveScreens() {
|
||||||
const live = {};
|
const live = {};
|
||||||
const screens = Quickshell.screens || [];
|
const screens = Quickshell.screens || [];
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ Item {
|
|||||||
property color borderColor: "transparent"
|
property color borderColor: "transparent"
|
||||||
property real borderWidth: 0
|
property real borderWidth: 0
|
||||||
|
|
||||||
// Rounded-rect geometry within the item; defaults fill the item.
|
|
||||||
property real sourceX: 0
|
property real sourceX: 0
|
||||||
property real sourceY: 0
|
property real sourceY: 0
|
||||||
property real sourceWidth: width
|
property real sourceWidth: width
|
||||||
@@ -36,8 +35,6 @@ Item {
|
|||||||
readonly property var _ambient: Theme.elevationAmbient(level)
|
readonly property var _ambient: Theme.elevationAmbient(level)
|
||||||
readonly property real _pad: shadowEnabled ? Math.ceil(Math.max(shadowBlurPx + shadowSpreadPx + Math.max(Math.abs(shadowOffsetX), Math.abs(shadowOffsetY)), _ambient.blurPx + _ambient.spreadPx) + 2) : 0
|
readonly property real _pad: shadowEnabled ? Math.ceil(Math.max(shadowBlurPx + shadowSpreadPx + Math.max(Math.abs(shadowOffsetX), Math.abs(shadowOffsetY)), _ambient.blurPx + _ambient.spreadPx) + 2) : 0
|
||||||
|
|
||||||
// Fill + border + key/ambient shadows drawn analytically on one oversized
|
|
||||||
// quad — no FBO, no blur passes.
|
|
||||||
ShaderEffect {
|
ShaderEffect {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: -root._pad
|
anchors.margins: -root._pad
|
||||||
|
|||||||
@@ -911,9 +911,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
return Qt.rgba(r, g, b, alpha);
|
return Qt.rgba(r, g, b, alpha);
|
||||||
}
|
}
|
||||||
// Non-directional ambient layer of the M3 two-part shadow model (key +
|
|
||||||
// ambient). Derived from the key level so user intensity/opacity settings
|
|
||||||
// scale both layers together.
|
|
||||||
function elevationAmbient(level) {
|
function elevationAmbient(level) {
|
||||||
const blur = (level && level.blurPx !== undefined) ? Math.max(0, level.blurPx) : 0;
|
const blur = (level && level.blurPx !== undefined) ? Math.max(0, level.blurPx) : 0;
|
||||||
const alpha = ((level && level.alpha !== undefined) ? level.alpha : 0.3) * 0.5;
|
const alpha = ((level && level.alpha !== undefined) ? level.alpha : 0.3) * 0.5;
|
||||||
|
|||||||
@@ -54,10 +54,7 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
// One focus grab for every modal; on Hyprland this is what delivers
|
// Hyprland OnDemand grab delivers keyboard focus to the modal content surface.
|
||||||
// keyboard focus to the OnDemand surface, identically in both modes.
|
|
||||||
// The full-surface content window receives outside clicks itself, so an
|
|
||||||
// outside click closes the modal instead of being consumed by the grab.
|
|
||||||
HyprlandFocusGrab {
|
HyprlandFocusGrab {
|
||||||
windows: root.contentWindow ? [root.contentWindow] : []
|
windows: root.contentWindow ? [root.contentWindow] : []
|
||||||
active: KeyboardFocus.wantsGrab(root.shouldHaveFocus, root.customKeyboardFocus)
|
active: KeyboardFocus.wantsGrab(root.shouldHaveFocus, root.customKeyboardFocus)
|
||||||
@@ -105,8 +102,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defer Loader source-component swap until impl is fully closed; avoids
|
|
||||||
// tearing down a modal mid-animation when frame mode is toggled.
|
|
||||||
function _maybeResolveBackend() {
|
function _maybeResolveBackend() {
|
||||||
if (_resolvedBackend === _desiredBackend)
|
if (_resolvedBackend === _desiredBackend)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ Item {
|
|||||||
property bool closeOnBackgroundClick: true
|
property bool closeOnBackgroundClick: true
|
||||||
property string animationType: "scale"
|
property string animationType: "scale"
|
||||||
|
|
||||||
// Opposite side from the launcher by default; subclasses may override
|
|
||||||
property string preferredConnectedBarSide: SettingsData.frameModalEmergeSide
|
property string preferredConnectedBarSide: SettingsData.frameModalEmergeSide
|
||||||
|
|
||||||
readonly property bool frameConnectedMode: SettingsData.frameEnabled && Theme.isConnectedEffect && !!effectiveScreen && SettingsData.isScreenInPreferences(effectiveScreen, SettingsData.frameScreenPreferences)
|
readonly property bool frameConnectedMode: SettingsData.frameEnabled && Theme.isConnectedEffect && !!effectiveScreen && SettingsData.isScreenInPreferences(effectiveScreen, SettingsData.frameScreenPreferences)
|
||||||
@@ -94,7 +93,6 @@ Item {
|
|||||||
signal dialogClosed
|
signal dialogClosed
|
||||||
signal backgroundClicked
|
signal backgroundClicked
|
||||||
|
|
||||||
// Coalesce per-channel dirty bits; one ConnectedModeState write per tick.
|
|
||||||
Timer {
|
Timer {
|
||||||
id: _syncTimer
|
id: _syncTimer
|
||||||
interval: 0
|
interval: 0
|
||||||
@@ -342,7 +340,6 @@ Item {
|
|||||||
return SettingsData.frameEdgeInsetForSide(effectiveScreen, side);
|
return SettingsData.frameEdgeInsetForSide(effectiveScreen, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
// frameEdgeInsetForSide is the full inset; do not add frameBarSize
|
|
||||||
readonly property real _connectedAlignedX: {
|
readonly property real _connectedAlignedX: {
|
||||||
switch (resolvedConnectedBarSide) {
|
switch (resolvedConnectedBarSide) {
|
||||||
case "top":
|
case "top":
|
||||||
@@ -471,12 +468,9 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reveal frame: when the frame owns the connected chrome, the content
|
|
||||||
// must only become visible inside the modal's final footprint so it
|
|
||||||
// emerges in step with the chrome growing from the bar edge (the old
|
|
||||||
// two-window layout got this for free from the window boundary).
|
|
||||||
Item {
|
Item {
|
||||||
id: connectedReveal
|
id: connectedReveal
|
||||||
|
// Clip to final footprint while frame-owned chrome grows from the bar edge.
|
||||||
x: root.alignedX
|
x: root.alignedX
|
||||||
y: root.alignedY
|
y: root.alignedY
|
||||||
width: root.alignedWidth
|
width: root.alignedWidth
|
||||||
@@ -512,7 +506,6 @@ Item {
|
|||||||
readonly property real customDistRight: root.screenWidth - customAnchorX
|
readonly property real customDistRight: root.screenWidth - customAnchorX
|
||||||
readonly property real customDistTop: customAnchorY
|
readonly property real customDistTop: customAnchorY
|
||||||
readonly property real customDistBottom: root.screenHeight - customAnchorY
|
readonly property real customDistBottom: root.screenHeight - customAnchorY
|
||||||
// Connected emergence: travel from the resolved bar edge, matching DankPopout cadence.
|
|
||||||
readonly property real connectedEmergenceTravelX: Math.max(root.animationOffset, root.alignedWidth + Theme.spacingL)
|
readonly property real connectedEmergenceTravelX: Math.max(root.animationOffset, root.alignedWidth + Theme.spacingL)
|
||||||
readonly property real connectedEmergenceTravelY: Math.max(root.animationOffset, root.alignedHeight + Theme.spacingL)
|
readonly property real connectedEmergenceTravelY: Math.max(root.animationOffset, root.alignedHeight + Theme.spacingL)
|
||||||
readonly property real offsetX: {
|
readonly property real offsetX: {
|
||||||
@@ -580,7 +573,6 @@ Item {
|
|||||||
return directionalTravel;
|
return directionalTravel;
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
// Default to sliding down from top when centered
|
|
||||||
return -Math.max(directionalTravel, root.screenHeight * 0.24);
|
return -Math.max(directionalTravel, root.screenHeight * 0.24);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -603,7 +595,6 @@ Item {
|
|||||||
|
|
||||||
readonly property real computedScaleCollapsed: root.animationScaleCollapsed
|
readonly property real computedScaleCollapsed: root.animationScaleCollapsed
|
||||||
|
|
||||||
// openProgress: 0 = closed (at frozenMotionOffset, scaleCollapsed), 1 = open (at 0, scale 1).
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: morph
|
id: morph
|
||||||
property real openProgress: root.shouldBeVisible ? 1 : 0
|
property real openProgress: root.shouldBeVisible ? 1 : 0
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ Item {
|
|||||||
property string _pendingMode: ""
|
property string _pendingMode: ""
|
||||||
readonly property bool unloadContentOnClose: SettingsData.dankLauncherV2UnloadOnClose
|
readonly property bool unloadContentOnClose: SettingsData.dankLauncherV2UnloadOnClose
|
||||||
|
|
||||||
// Animation state — matches DankPopout/DankModal pattern
|
|
||||||
property bool animationsEnabled: true
|
property bool animationsEnabled: true
|
||||||
property bool _motionActive: false
|
property bool _motionActive: false
|
||||||
property real _frozenMotionX: 0
|
property real _frozenMotionX: 0
|
||||||
@@ -108,8 +107,6 @@ Item {
|
|||||||
return SettingsData.frameEdgeInsetForSide(effectiveScreen, side);
|
return SettingsData.frameEdgeInsetForSide(effectiveScreen, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
// frameEdgeInsetForSide is the full inset; do not add frameBarSize.
|
|
||||||
// Positions the modal flush to the emerge side, centered on the cross axis.
|
|
||||||
readonly property var _connectedModalPos: {
|
readonly property var _connectedModalPos: {
|
||||||
const fallback = {
|
const fallback = {
|
||||||
"x": (screenWidth - modalWidth) / 2,
|
"x": (screenWidth - modalWidth) / 2,
|
||||||
@@ -175,8 +172,6 @@ Item {
|
|||||||
readonly property int effectiveBorderWidth: connectedSurfaceOverride ? 0 : borderWidth
|
readonly property int effectiveBorderWidth: connectedSurfaceOverride ? 0 : borderWidth
|
||||||
readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled
|
readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled
|
||||||
|
|
||||||
// Shadow padding for the content window (render padding only, no motion padding).
|
|
||||||
// Zeroed when frame owns the chrome and Wayland clips past the bar edge
|
|
||||||
readonly property var shadowLevel: Theme.elevationLevel3
|
readonly property var shadowLevel: Theme.elevationLevel3
|
||||||
readonly property real shadowFallbackOffset: 6
|
readonly property real shadowFallbackOffset: 6
|
||||||
readonly property real shadowRenderPadding: (!frameOwnsConnectedChrome && Theme.elevationEnabled && SettingsData.modalElevationEnabled) ? Theme.elevationRenderPadding(shadowLevel, Theme.elevationLightDirection, shadowFallbackOffset, 8, 16) : 0
|
readonly property real shadowRenderPadding: (!frameOwnsConnectedChrome && Theme.elevationEnabled && SettingsData.modalElevationEnabled) ? Theme.elevationRenderPadding(shadowLevel, Theme.elevationLightDirection, shadowFallbackOffset, 8, 16) : 0
|
||||||
@@ -203,13 +198,11 @@ Item {
|
|||||||
}
|
}
|
||||||
readonly property real contentSurfaceHeight: launcherArcExtenderActive ? _connectedChromeHeight : alignedHeight
|
readonly property real contentSurfaceHeight: launcherArcExtenderActive ? _connectedChromeHeight : alignedHeight
|
||||||
|
|
||||||
// Where the content container sits inside the full-surface content window (screen coordinates)
|
|
||||||
readonly property real _ccX: _connectedChromeX
|
readonly property real _ccX: _connectedChromeX
|
||||||
readonly property real _ccY: _connectedChromeY
|
readonly property real _ccY: _connectedChromeY
|
||||||
|
|
||||||
signal dialogClosed
|
signal dialogClosed
|
||||||
|
|
||||||
// Coalesce per-channel dirty bits; one ConnectedModeState write per tick.
|
|
||||||
Timer {
|
Timer {
|
||||||
id: _syncTimer
|
id: _syncTimer
|
||||||
interval: 0
|
interval: 0
|
||||||
@@ -365,8 +358,6 @@ Item {
|
|||||||
return;
|
return;
|
||||||
contentVisible = true;
|
contentVisible = true;
|
||||||
spotlightContent.closeTransientUi?.();
|
spotlightContent.closeTransientUi?.();
|
||||||
// NOTE: forceActiveFocus() is deliberately NOT called here.
|
|
||||||
// It is deferred to after animation starts to avoid compositor IPC stalls.
|
|
||||||
|
|
||||||
if (spotlightContent.searchField) {
|
if (spotlightContent.searchField) {
|
||||||
spotlightContent.searchField.text = query;
|
spotlightContent.searchField.text = query;
|
||||||
@@ -404,10 +395,8 @@ Item {
|
|||||||
isClosing = false;
|
isClosing = false;
|
||||||
openedFromOverview = false;
|
openedFromOverview = false;
|
||||||
|
|
||||||
// Disable animations so the snap is instant
|
|
||||||
animationsEnabled = false;
|
animationsEnabled = false;
|
||||||
|
|
||||||
// Freeze the collapsed offsets (they depend on height which could change)
|
|
||||||
_frozenMotionX = contentContainer ? contentContainer.collapsedMotionX : 0;
|
_frozenMotionX = contentContainer ? contentContainer.collapsedMotionX : 0;
|
||||||
_frozenMotionY = contentContainer ? contentContainer.collapsedMotionY : (Theme.isDirectionalEffect ? Math.max(root.screenHeight - root._ccY + root.shadowPad, Theme.effectAnimOffset * 1.1) : -Theme.effectAnimOffset);
|
_frozenMotionY = contentContainer ? contentContainer.collapsedMotionY : (Theme.isDirectionalEffect ? Math.max(root.screenHeight - root._ccY + root.shadowPad, Theme.effectAnimOffset * 1.1) : -Theme.effectAnimOffset);
|
||||||
|
|
||||||
@@ -416,24 +405,19 @@ Item {
|
|||||||
contentWindow.screen = focusedScreen;
|
contentWindow.screen = focusedScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
// _motionActive = false ensures motionX/Y snap to frozen collapsed position
|
|
||||||
_motionActive = false;
|
_motionActive = false;
|
||||||
|
|
||||||
// Make windows visible but do NOT request keyboard focus yet
|
|
||||||
ModalManager.openModal(modalHandle);
|
ModalManager.openModal(modalHandle);
|
||||||
spotlightOpen = true;
|
spotlightOpen = true;
|
||||||
contentWindow.visible = true;
|
contentWindow.visible = true;
|
||||||
|
|
||||||
// Load content and initialize (but no forceActiveFocus — that's deferred)
|
|
||||||
_ensureContentLoadedAndInitialize(query || "", mode || "");
|
_ensureContentLoadedAndInitialize(query || "", mode || "");
|
||||||
|
|
||||||
// Frame 1: enable animations and trigger enter motion
|
// Defer focus until after enter motion starts (avoids compositor IPC stalls).
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
root.animationsEnabled = true;
|
root.animationsEnabled = true;
|
||||||
root._motionActive = true;
|
root._motionActive = true;
|
||||||
|
|
||||||
// Frame 2: request keyboard focus + activate search field
|
|
||||||
// Double-deferred to avoid compositor IPC competing with animation frames
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
root.keyboardActive = true;
|
root.keyboardActive = true;
|
||||||
if (root.spotlightContent && root.spotlightContent.searchField)
|
if (root.spotlightContent && root.spotlightContent.searchField)
|
||||||
@@ -456,11 +440,9 @@ Item {
|
|||||||
spotlightContent?.closeTransientUi?.();
|
spotlightContent?.closeTransientUi?.();
|
||||||
openedFromOverview = false;
|
openedFromOverview = false;
|
||||||
isClosing = true;
|
isClosing = true;
|
||||||
// For directional effects, defer contentVisible=false so content stays rendered during exit slide
|
|
||||||
if (!Theme.isDirectionalEffect)
|
if (!Theme.isDirectionalEffect)
|
||||||
contentVisible = false;
|
contentVisible = false;
|
||||||
|
|
||||||
// Trigger exit animation — Behaviors will animate motionX/Y to frozen collapsed position
|
|
||||||
_motionActive = false;
|
_motionActive = false;
|
||||||
|
|
||||||
keyboardActive = false;
|
keyboardActive = false;
|
||||||
@@ -612,8 +594,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dismissable area: everything except the bar/dock strips, so bar
|
|
||||||
// widgets stay clickable for widget-to-widget transfer.
|
|
||||||
Item {
|
Item {
|
||||||
id: dismissArea
|
id: dismissArea
|
||||||
visible: false
|
visible: false
|
||||||
@@ -712,7 +692,6 @@ Item {
|
|||||||
return -Math.max((root.shadowPad || 0) + Theme.effectAnimOffset, 40);
|
return -Math.max((root.shadowPad || 0) + Theme.effectAnimOffset, 40);
|
||||||
}
|
}
|
||||||
|
|
||||||
// openProgress: 0 = closed (at frozenMotion, scaleCollapsed), 1 = open (at 0, scale 1).
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: morph
|
id: morph
|
||||||
property real openProgress: root._motionActive ? 1 : 0
|
property real openProgress: root._motionActive ? 1 : 0
|
||||||
@@ -771,7 +750,6 @@ Item {
|
|||||||
width: contentContainer.width
|
width: contentContainer.width
|
||||||
height: contentContainer.height
|
height: contentContainer.height
|
||||||
|
|
||||||
// Shadow mirrors contentWrapper position/scale/opacity
|
|
||||||
ElevationShadow {
|
ElevationShadow {
|
||||||
id: launcherShadowLayer
|
id: launcherShadowLayer
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -789,7 +767,6 @@ Item {
|
|||||||
shadowEnabled: !root.frameOwnsConnectedChrome && Theme.elevationEnabled && SettingsData.modalElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
shadowEnabled: !root.frameOwnsConnectedChrome && Theme.elevationEnabled && SettingsData.modalElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
||||||
}
|
}
|
||||||
|
|
||||||
// contentWrapper moves inside static contentContainer — DankPopout pattern
|
|
||||||
Item {
|
Item {
|
||||||
id: contentWrapper
|
id: contentWrapper
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ pragma ComponentBehavior: Bound
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
|
||||||
// Frame perimeter ring: the full window rectangle with a rounded-rectangle
|
// Frame perimeter ring with rounded cutout (SDF).
|
||||||
// cutout, rendered as a signed-distance field with analytic antialiasing.
|
|
||||||
// One primitive: no full-output mask textures, no corner double-blend, crisp
|
|
||||||
// edges at any scale without an FBO.
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
|||||||
@@ -92,8 +92,6 @@ PanelWindow {
|
|||||||
readonly property real _notifStartUnderlapValue: win._notifDescriptor.omitStartConnector ? win._seamOverlap : 0
|
readonly property real _notifStartUnderlapValue: win._notifDescriptor.omitStartConnector ? win._seamOverlap : 0
|
||||||
readonly property real _notifEndUnderlapValue: win._notifDescriptor.omitEndConnector ? win._seamOverlap : 0
|
readonly property real _notifEndUnderlapValue: win._notifDescriptor.omitEndConnector ? win._seamOverlap : 0
|
||||||
|
|
||||||
// Theme.snap rounds to integer pixel: equal rounded values suppress
|
|
||||||
// downstream Changed during sub-pixel morph jitter.
|
|
||||||
readonly property var _popoutRadii: SurfaceGeometry.connectorRadii(win._popoutDescriptor, win._popoutBodyGeometry, win._ccr, win._surfaceRadius, win._dpr, false)
|
readonly property var _popoutRadii: SurfaceGeometry.connectorRadii(win._popoutDescriptor, win._popoutBodyGeometry, win._ccr, win._surfaceRadius, win._dpr, false)
|
||||||
readonly property real _effectivePopoutCcr: win._popoutRadii.near
|
readonly property real _effectivePopoutCcr: win._popoutRadii.near
|
||||||
readonly property real _effectivePopoutFarCcr: win._popoutRadii.far
|
readonly property real _effectivePopoutFarCcr: win._popoutRadii.far
|
||||||
@@ -125,12 +123,8 @@ PanelWindow {
|
|||||||
readonly property real _surfaceRadius: Theme.connectedSurfaceRadius
|
readonly property real _surfaceRadius: Theme.connectedSurfaceRadius
|
||||||
readonly property real _seamOverlap: Theme.hairline(win._dpr)
|
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 _disableLayer: Quickshell.env("DMS_DISABLE_LAYER") === "true" || Quickshell.env("DMS_DISABLE_LAYER") === "1"
|
||||||
// Both elevation states render through the SDF shader; this only toggles
|
|
||||||
// the shadow term inside it.
|
|
||||||
readonly property bool _elevationShadow: win._connectedActive && Theme.elevationEnabled && !win._disableLayer
|
readonly property bool _elevationShadow: win._connectedActive && Theme.elevationEnabled && !win._disableLayer
|
||||||
// Active surfaces packed into four fixed SDF-shader slots. Each near (bar)
|
// Pack active connected surfaces into four fixed SDF slots (near edges clamp to cutout).
|
||||||
// edge is clamped to the cutout edge so the smooth-min connector attaches
|
|
||||||
// there; the per-corner smin radius is that corner's junction fillet.
|
|
||||||
readonly property var _sdfSlots: {
|
readonly property var _sdfSlots: {
|
||||||
const T = win.cutoutTopInset;
|
const T = win.cutoutTopInset;
|
||||||
const L = win.cutoutLeftInset;
|
const L = win.cutoutLeftInset;
|
||||||
@@ -158,16 +152,7 @@ PanelWindow {
|
|||||||
const s = src[i];
|
const s = src[i];
|
||||||
const b = clampNear(s.side, s.body);
|
const b = clampNear(s.side, s.body);
|
||||||
const active = b.width > 0 && b.height > 0 ? 1 : 0;
|
const active = b.width > 0 && b.height > 0 ? 1 : 0;
|
||||||
// Map start/end (left/top, right/bottom) onto corners
|
|
||||||
// (tl,tr,br,bl): bar-side corners take their near connector
|
|
||||||
// fillet, far corners always take the far fillet so a body
|
|
||||||
// meeting a perpendicular border joins with an arc (smin is
|
|
||||||
// inert when nothing is within k). A bar-side corner is sharp
|
|
||||||
// where its connector is present; an omitted connector makes
|
|
||||||
// its far corner sharp instead (the far-cap join).
|
|
||||||
const sc = s.radii.startCr, ec = s.radii.endCr;
|
const sc = s.radii.startCr, ec = s.radii.endCr;
|
||||||
// Clamp the far fillet to the body extent so it cannot flare
|
|
||||||
// back across a shallow body into the bar mid-animation.
|
|
||||||
const extent = (s.side === "top" || s.side === "bottom") ? b.height : b.width;
|
const extent = (s.side === "top" || s.side === "bottom") ? b.height : b.width;
|
||||||
const fc = Math.min(s.radii.farCr, extent);
|
const fc = Math.min(s.radii.farCr, extent);
|
||||||
const omitS = s.radii.farStartCr > 0;
|
const omitS = s.radii.farStartCr > 0;
|
||||||
@@ -175,9 +160,6 @@ PanelWindow {
|
|||||||
const bodyR = s.radii.surfaceRadius;
|
const bodyR = s.radii.surfaceRadius;
|
||||||
const nearS = omitS ? bodyR : 0, nearE = omitE ? bodyR : 0;
|
const nearS = omitS ? bodyR : 0, nearE = omitE ? bodyR : 0;
|
||||||
const farS = omitS ? 0 : bodyR, farE = omitE ? 0 : bodyR;
|
const farS = omitS ? 0 : bodyR, farE = omitE ? 0 : bodyR;
|
||||||
// An omitted bar-side corner sits flush against the border, so
|
|
||||||
// it keeps a nonzero fillet (a zero k hard-joins the coincident
|
|
||||||
// edges and shows a half-coverage hairline along the seam).
|
|
||||||
const kS = omitS ? fc : sc, kE = omitE ? fc : ec;
|
const kS = omitS ? fc : sc, kE = omitE ? fc : ec;
|
||||||
let ks, cr;
|
let ks, cr;
|
||||||
if (s.side === "top") {
|
if (s.side === "top") {
|
||||||
@@ -221,8 +203,6 @@ PanelWindow {
|
|||||||
return Math.max(0, Math.min(requested, maxRadius));
|
return Math.max(0, Math.min(requested, maxRadius));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pins every surface blur region to zero while frame blur cannot be
|
|
||||||
// consumed, so animations stop dirtying the region tree.
|
|
||||||
readonly property bool _blurSurfacesActive: BlurService.enabled && SettingsData.frameBlurEnabled && win._frameActive
|
readonly property bool _blurSurfacesActive: BlurService.enabled && SettingsData.frameBlurEnabled && win._frameActive
|
||||||
readonly property int _blurCutoutCompensation: SettingsData.frameOpacity <= 0.2 ? 1 : 0
|
readonly property int _blurCutoutCompensation: SettingsData.frameOpacity <= 0.2 ? 1 : 0
|
||||||
readonly property int _blurCutoutLeft: Math.max(0, win.cutoutLeftInset - win._blurCutoutCompensation)
|
readonly property int _blurCutoutLeft: Math.max(0, win.cutoutLeftInset - win._blurCutoutCompensation)
|
||||||
@@ -235,9 +215,6 @@ PanelWindow {
|
|||||||
return Math.max(0, Math.min(requested, maxRadius));
|
return Math.max(0, Math.min(requested, maxRadius));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blur regions bind rounded integer pixels directly: equal rounded values
|
|
||||||
// suppress Changed during sub-pixel motion, and inactive children pin to
|
|
||||||
// all-zero so the region build skips them.
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: _notifBodyBlurAnchor
|
id: _notifBodyBlurAnchor
|
||||||
|
|
||||||
@@ -255,7 +232,6 @@ PanelWindow {
|
|||||||
width: win._windowRegionWidth
|
width: win._windowRegionWidth
|
||||||
height: win._windowRegionHeight
|
height: win._windowRegionHeight
|
||||||
|
|
||||||
// Frame cutout (always active when frame is on)
|
|
||||||
Region {
|
Region {
|
||||||
id: _blurCutout
|
id: _blurCutout
|
||||||
intersection: Intersection.Subtract
|
intersection: Intersection.Subtract
|
||||||
@@ -829,7 +805,6 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notif body scene rect, accounting for start/end/side underlaps per bar orientation.
|
|
||||||
function _notifBodyScene() {
|
function _notifBodyScene() {
|
||||||
const isHoriz = SurfaceGeometry.isHorizontal(win._notifDescriptor.barSide);
|
const isHoriz = SurfaceGeometry.isHorizontal(win._notifDescriptor.barSide);
|
||||||
const start = win._notifStartUnderlapValue;
|
const start = win._notifStartUnderlapValue;
|
||||||
@@ -861,9 +836,6 @@ PanelWindow {
|
|||||||
return Math.max(0, Math.min(win._effectivePopoutMaxCcr, extent - win._surfaceRadius));
|
return Math.max(0, Math.min(win._effectivePopoutMaxCcr, extent - win._surfaceRadius));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Active connected surfaces fed to the unified silhouette path. Raw animated
|
|
||||||
// body rects (no seam/fill overlap); the builder anchors each to the cutout
|
|
||||||
// edge. Connector radii use the same per-surface helpers as the blur regions.
|
|
||||||
function _unifiedSurfaces() {
|
function _unifiedSurfaces() {
|
||||||
const arr = [];
|
const arr = [];
|
||||||
const p = win._popoutBodyGeometry;
|
const p = win._popoutBodyGeometry;
|
||||||
@@ -971,8 +943,6 @@ PanelWindow {
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Coalesce bursts of settings-change signals into a single _buildBlur() call
|
|
||||||
// on the next event loop tick.
|
|
||||||
DeferredAction {
|
DeferredAction {
|
||||||
id: blurRebuildAction
|
id: blurRebuildAction
|
||||||
onTriggered: win._runBlurRebuild()
|
onTriggered: win._runBlurRebuild()
|
||||||
@@ -1096,10 +1066,6 @@ PanelWindow {
|
|||||||
cutoutRadius: win.cutoutRadius
|
cutoutRadius: win.cutoutRadius
|
||||||
}
|
}
|
||||||
|
|
||||||
// The entire connected silhouette (frame ring + every active chrome) as one
|
|
||||||
// SDF in a fragment shader. Analytic fwidth AA → crisp at any scale, no FBO;
|
|
||||||
// the smooth-min radius is the connector. The elevation shadow is derived
|
|
||||||
// from the same distance field, so elevation-on needs no grouping layer.
|
|
||||||
ShaderEffect {
|
ShaderEffect {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: win._connectedActive
|
visible: win._connectedActive
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
// Connected Frame Mode silhouette as a signed-distance field: the frame ring
|
// Connected frame silhouette: frame ring + chrome bodies as one SDF with elevation shadow.
|
||||||
// (an inverted rounded rectangle) smooth-unioned with each active chrome
|
|
||||||
// (popout/modal, dock, notification). The smooth-min radius IS the connector
|
|
||||||
// fillet. Antialiasing is analytic via fwidth -> crisp at any scale, no FBO.
|
|
||||||
// The elevation shadow samples the same field at the light offset, so both
|
|
||||||
// elevation states render in this one pass.
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 qt_TexCoord0;
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
layout(location = 0) out vec4 fragColor;
|
layout(location = 0) out vec4 fragColor;
|
||||||
@@ -53,7 +48,6 @@ float sdRoundBox(vec2 p, vec2 c, vec2 hs, float r) {
|
|||||||
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - r;
|
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Per-corner rounded box. r = (topLeft, topRight, bottomRight, bottomLeft).
|
|
||||||
float sdRoundBox4(vec2 p, vec2 c, vec2 hs, vec4 r) {
|
float sdRoundBox4(vec2 p, vec2 c, vec2 hs, vec4 r) {
|
||||||
p -= c;
|
p -= c;
|
||||||
float rr = (p.x >= 0.0) ? (p.y >= 0.0 ? r.z : r.y) : (p.y >= 0.0 ? r.w : r.x);
|
float rr = (p.x >= 0.0) ? (p.y >= 0.0 ? r.z : r.y) : (p.y >= 0.0 ? r.w : r.x);
|
||||||
@@ -62,7 +56,6 @@ float sdRoundBox4(vec2 p, vec2 c, vec2 hs, vec4 r) {
|
|||||||
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - rr;
|
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - rr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Circular smooth-min: blends two SDFs with a fillet of radius k.
|
|
||||||
float smin(float a, float b, float k) {
|
float smin(float a, float b, float k) {
|
||||||
if (k <= 0.0)
|
if (k <= 0.0)
|
||||||
return min(a, b);
|
return min(a, b);
|
||||||
@@ -74,14 +67,12 @@ float chromeDist(vec2 px, vec4 rect, vec4 corner) {
|
|||||||
return sdRoundBox4(px, c, rect.zw * 0.5, corner);
|
return sdRoundBox4(px, c, rect.zw * 0.5, corner);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Per-corner junction fillet radius, selected by chrome-rect quadrant.
|
|
||||||
float chromeK(vec2 px, vec4 rect, vec4 ks) {
|
float chromeK(vec2 px, vec4 rect, vec4 ks) {
|
||||||
vec2 p = px - (rect.xy + rect.zw * 0.5);
|
vec2 p = px - (rect.xy + rect.zw * 0.5);
|
||||||
return (p.x >= 0.0) ? (p.y >= 0.0 ? ks.z : ks.y) : (p.y >= 0.0 ? ks.w : ks.x);
|
return (p.x >= 0.0) ? (p.y >= 0.0 ? ks.z : ks.y) : (p.y >= 0.0 ? ks.w : ks.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
float sceneDist(vec2 px) {
|
float sceneDist(vec2 px) {
|
||||||
// Frame ring: inside the screen rect AND outside the rounded cutout (hole).
|
|
||||||
vec2 sc = vec2(ubuf.widthPx, ubuf.heightPx) * 0.5;
|
vec2 sc = vec2(ubuf.widthPx, ubuf.heightPx) * 0.5;
|
||||||
float dOuter = sdBox(px, sc, sc);
|
float dOuter = sdBox(px, sc, sc);
|
||||||
vec2 cutC = vec2((ubuf.cutout.x + ubuf.cutout.z) * 0.5, (ubuf.cutout.y + ubuf.cutout.w) * 0.5);
|
vec2 cutC = vec2((ubuf.cutout.x + ubuf.cutout.z) * 0.5, (ubuf.cutout.y + ubuf.cutout.w) * 0.5);
|
||||||
@@ -89,7 +80,6 @@ float sceneDist(vec2 px) {
|
|||||||
float dCut = sdRoundBox(px, cutC, cutH, ubuf.cutoutRadius);
|
float dCut = sdRoundBox(px, cutC, cutH, ubuf.cutoutRadius);
|
||||||
float d = max(dOuter, -dCut);
|
float d = max(dOuter, -dCut);
|
||||||
|
|
||||||
// Smooth-union the active chrome surfaces; smin radius = junction fillet.
|
|
||||||
if (ubuf.chromeParam0.x > 0.5)
|
if (ubuf.chromeParam0.x > 0.5)
|
||||||
d = smin(d, chromeDist(px, ubuf.chromeRect0, ubuf.chromeCorner0), chromeK(px, ubuf.chromeRect0, ubuf.chromeK0));
|
d = smin(d, chromeDist(px, ubuf.chromeRect0, ubuf.chromeCorner0), chromeK(px, ubuf.chromeRect0, ubuf.chromeK0));
|
||||||
if (ubuf.chromeParam1.x > 0.5)
|
if (ubuf.chromeParam1.x > 0.5)
|
||||||
@@ -106,14 +96,11 @@ void main() {
|
|||||||
float d = sceneDist(px);
|
float d = sceneDist(px);
|
||||||
float fw = max(fwidth(d), 1e-4);
|
float fw = max(fwidth(d), 1e-4);
|
||||||
float cov = 1.0 - smoothstep(-fw, fw, d);
|
float cov = 1.0 - smoothstep(-fw, fw, d);
|
||||||
// Opaque silhouette over shadow, then the surface alpha applied to the
|
|
||||||
// whole result — matches the old flattened-FBO + group-opacity look.
|
|
||||||
vec4 col = vec4(ubuf.surfaceColor.rgb, 1.0) * cov;
|
vec4 col = vec4(ubuf.surfaceColor.rgb, 1.0) * cov;
|
||||||
if (ubuf.shadowColor.a > 0.0) {
|
if (ubuf.shadowColor.a > 0.0) {
|
||||||
float dk = sceneDist(px - ubuf.shadowParam.zw) - ubuf.shadowParam.y;
|
float dk = sceneDist(px - ubuf.shadowParam.zw) - ubuf.shadowParam.y;
|
||||||
float bk = max(ubuf.shadowParam.x, fw);
|
float bk = max(ubuf.shadowParam.x, fw);
|
||||||
float covK = 1.0 - smoothstep(-bk, bk, dk);
|
float covK = 1.0 - smoothstep(-bk, bk, dk);
|
||||||
// Ambient wrap reuses the field already computed for the silhouette.
|
|
||||||
float ba = max(ubuf.ambientParam.x, fw);
|
float ba = max(ubuf.ambientParam.x, fw);
|
||||||
float covA = 1.0 - smoothstep(-ba, ba, d - ubuf.ambientParam.y);
|
float covA = 1.0 - smoothstep(-ba, ba, d - ubuf.ambientParam.y);
|
||||||
float sh = 1.0 - (1.0 - covK * ubuf.shadowColor.a) * (1.0 - covA * ubuf.ambientParam.z);
|
float sh = 1.0 - (1.0 - covK * ubuf.shadowColor.a) * (1.0 - covA * ubuf.ambientParam.z);
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
// Popout-local connected chrome as a signed-distance field: the body rounded
|
// Popout-local connected chrome body + bar-edge connector as one SDF.
|
||||||
// rect smooth-unioned against the bar-edge half-plane, so the connector
|
|
||||||
// fillets form analytically. Key + ambient shadows sample the same field;
|
|
||||||
// shadow is masked outside the silhouette.
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 qt_TexCoord0;
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
layout(location = 0) out vec4 fragColor;
|
layout(location = 0) out vec4 fragColor;
|
||||||
@@ -22,7 +19,6 @@ layout(std140, binding = 0) uniform buf {
|
|||||||
vec4 edgeParam; // x = bar side (0 top, 1 bottom, 2 left, 3 right), y = fillet k
|
vec4 edgeParam; // x = bar side (0 top, 1 bottom, 2 left, 3 right), y = fillet k
|
||||||
} ubuf;
|
} ubuf;
|
||||||
|
|
||||||
// Per-corner rounded box. r = (topLeft, topRight, bottomRight, bottomLeft).
|
|
||||||
float sdRoundBox4(vec2 p, vec2 c, vec2 hs, vec4 r) {
|
float sdRoundBox4(vec2 p, vec2 c, vec2 hs, vec4 r) {
|
||||||
p -= c;
|
p -= c;
|
||||||
float rr = (p.x >= 0.0) ? (p.y >= 0.0 ? r.z : r.y) : (p.y >= 0.0 ? r.w : r.x);
|
float rr = (p.x >= 0.0) ? (p.y >= 0.0 ? r.z : r.y) : (p.y >= 0.0 ? r.w : r.x);
|
||||||
@@ -31,7 +27,6 @@ float sdRoundBox4(vec2 p, vec2 c, vec2 hs, vec4 r) {
|
|||||||
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - rr;
|
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - rr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Circular smooth-min: blends two SDFs with a fillet of radius k.
|
|
||||||
float smin(float a, float b, float k) {
|
float smin(float a, float b, float k) {
|
||||||
if (k <= 0.0)
|
if (k <= 0.0)
|
||||||
return min(a, b);
|
return min(a, b);
|
||||||
@@ -39,8 +34,6 @@ float smin(float a, float b, float k) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float sceneDist(vec2 px) {
|
float sceneDist(vec2 px) {
|
||||||
// Bar edge as a half-plane whose interior lies off-item, on the bar's
|
|
||||||
// side; only its fillet contribution is visible.
|
|
||||||
float side = ubuf.edgeParam.x;
|
float side = ubuf.edgeParam.x;
|
||||||
float dEdge = side < 0.5 ? px.y
|
float dEdge = side < 0.5 ? px.y
|
||||||
: side < 1.5 ? (ubuf.heightPx - px.y)
|
: side < 1.5 ? (ubuf.heightPx - px.y)
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
// Standalone elevation surface as a signed-distance field: one quad draws the
|
// Standalone rounded rect with border and M3 elevation shadow as one SDF.
|
||||||
// rounded-rect fill, its border, and the M3 two-part shadow (directional key +
|
|
||||||
// non-directional ambient) analytically — no FBO, no blur passes. The shadow
|
|
||||||
// is masked to outside the silhouette, so translucent fills never get
|
|
||||||
// interior darkening.
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 qt_TexCoord0;
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
layout(location = 0) out vec4 fragColor;
|
layout(location = 0) out vec4 fragColor;
|
||||||
@@ -24,7 +20,6 @@ layout(std140, binding = 0) uniform buf {
|
|||||||
vec4 ambientParam; // ambient: x = blur px, y = spread px, z = alpha
|
vec4 ambientParam; // ambient: x = blur px, y = spread px, z = alpha
|
||||||
} ubuf;
|
} ubuf;
|
||||||
|
|
||||||
// Per-corner rounded box. r = (topLeft, topRight, bottomRight, bottomLeft).
|
|
||||||
float sdRoundBox4(vec2 p, vec2 c, vec2 hs, vec4 r) {
|
float sdRoundBox4(vec2 p, vec2 c, vec2 hs, vec4 r) {
|
||||||
p -= c;
|
p -= c;
|
||||||
float rr = (p.x >= 0.0) ? (p.y >= 0.0 ? r.z : r.y) : (p.y >= 0.0 ? r.w : r.x);
|
float rr = (p.x >= 0.0) ? (p.y >= 0.0 ? r.z : r.y) : (p.y >= 0.0 ? r.w : r.x);
|
||||||
@@ -43,7 +38,6 @@ void main() {
|
|||||||
float d = rectDist(px);
|
float d = rectDist(px);
|
||||||
float fw = max(fwidth(d), 1e-4);
|
float fw = max(fwidth(d), 1e-4);
|
||||||
float cov = 1.0 - smoothstep(-fw, fw, d);
|
float cov = 1.0 - smoothstep(-fw, fw, d);
|
||||||
// Qt Rectangle semantics: border band on the rim, fill inset inside it.
|
|
||||||
float covInner = 1.0 - smoothstep(-fw, fw, d + ubuf.borderWidth);
|
float covInner = 1.0 - smoothstep(-fw, fw, d + ubuf.borderWidth);
|
||||||
vec4 col = vec4(ubuf.fillColor.rgb, 1.0) * (ubuf.fillColor.a * covInner)
|
vec4 col = vec4(ubuf.fillColor.rgb, 1.0) * (ubuf.fillColor.a * covInner)
|
||||||
+ vec4(ubuf.borderColor.rgb, 1.0) * (ubuf.borderColor.a * max(0.0, cov - covInner));
|
+ vec4(ubuf.borderColor.rgb, 1.0) * (ubuf.borderColor.a * max(0.0, cov - covInner));
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
// Frame perimeter ring as a signed-distance field: the window rectangle minus
|
// Frame perimeter ring with rounded cutout as one SDF.
|
||||||
// a rounded-rectangle cutout. Antialiasing is analytic via fwidth -> crisp at
|
|
||||||
// any scale, no FBO and no mask textures.
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 qt_TexCoord0;
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
layout(location = 0) out vec4 fragColor;
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|||||||
@@ -53,11 +53,7 @@ Item {
|
|||||||
readonly property var backgroundWindow: impl.item ? impl.item.backgroundWindow : null
|
readonly property var backgroundWindow: impl.item ? impl.item.backgroundWindow : null
|
||||||
readonly property var contentWindow: impl.item ? impl.item.contentWindow : null
|
readonly property var contentWindow: impl.item ? impl.item.contentWindow : null
|
||||||
|
|
||||||
// On Hyprland the OnDemand content surface only receives keyboard focus
|
// Hyprland OnDemand grab: whitelist popout surfaces and bars so dismiss clicks still land.
|
||||||
// through a grab; everywhere else Exclusive focus covers this. Both
|
|
||||||
// popout windows plus every bar are whitelisted so clicks on them are
|
|
||||||
// delivered normally (dismiss MouseAreas, widget-to-widget transfer)
|
|
||||||
// instead of being consumed clearing the grab.
|
|
||||||
HyprlandFocusGrab {
|
HyprlandFocusGrab {
|
||||||
windows: {
|
windows: {
|
||||||
const list = [];
|
const list = [];
|
||||||
@@ -146,8 +142,6 @@ Item {
|
|||||||
return _usesConnectedBackendForScreen(targetScreen) ? connectedComp : standaloneComp;
|
return _usesConnectedBackendForScreen(targetScreen) ? connectedComp : standaloneComp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defer Loader source-component swap until impl is fully closed; avoids
|
|
||||||
// tearing down a popout mid-animation when frame mode is toggled.
|
|
||||||
function _maybeResolveBackend() {
|
function _maybeResolveBackend() {
|
||||||
_resolveBackendForScreen(screen);
|
_resolveBackendForScreen(screen);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ Item {
|
|||||||
"rightBar": 0
|
"rightBar": 0
|
||||||
})
|
})
|
||||||
property var screen: null
|
property var screen: null
|
||||||
// Connected resize uses one full-screen surface; body-sized regions are masks.
|
|
||||||
readonly property bool useBackgroundWindow: false
|
readonly property bool useBackgroundWindow: false
|
||||||
readonly property var effectivePopoutLayer: LayerShell.fromEnv("DMS_POPOUT_LAYER", root.triggerUsesOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top, {
|
readonly property var effectivePopoutLayer: LayerShell.fromEnv("DMS_POPOUT_LAYER", root.triggerUsesOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top, {
|
||||||
"allow": ["top", "overlay"],
|
"allow": ["top", "overlay"],
|
||||||
@@ -91,7 +90,6 @@ Item {
|
|||||||
signal popoutClosed
|
signal popoutClosed
|
||||||
signal backgroundClicked
|
signal backgroundClicked
|
||||||
|
|
||||||
// Coalesce per-channel dirty bits; one ConnectedModeState write per tick.
|
|
||||||
Timer {
|
Timer {
|
||||||
id: _syncTimer
|
id: _syncTimer
|
||||||
interval: 0
|
interval: 0
|
||||||
@@ -278,11 +276,9 @@ Item {
|
|||||||
chromeLease.release();
|
chromeLease.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Exposed animation state for ConnectedModeState ────────────────────
|
|
||||||
readonly property real contentAnimX: contentContainer.animX
|
readonly property real contentAnimX: contentContainer.animX
|
||||||
readonly property real contentAnimY: contentContainer.animY
|
readonly property real contentAnimY: contentContainer.animY
|
||||||
|
|
||||||
// ─── ConnectedModeState sync ────────────────────────────────────────────
|
|
||||||
function _syncPopoutChromeState() {
|
function _syncPopoutChromeState() {
|
||||||
if (!root.frameOwnsConnectedChrome) {
|
if (!root.frameOwnsConnectedChrome) {
|
||||||
_releaseConnectedChromeState();
|
_releaseConnectedChromeState();
|
||||||
@@ -422,16 +418,12 @@ Item {
|
|||||||
|
|
||||||
const screenChanged = _lastOpenedScreen !== null && _lastOpenedScreen !== screen;
|
const screenChanged = _lastOpenedScreen !== null && _lastOpenedScreen !== screen;
|
||||||
if (screenChanged) {
|
if (screenChanged) {
|
||||||
// Hide on this tick so Qt actually tears down the wl_surface; the show
|
|
||||||
// gets deferred below so the unmap is processed before the remap.
|
|
||||||
contentWindow.visible = false;
|
contentWindow.visible = false;
|
||||||
}
|
}
|
||||||
_lastOpenedScreen = screen;
|
_lastOpenedScreen = screen;
|
||||||
PopoutManager.showPopout(popoutHandle);
|
PopoutManager.showPopout(popoutHandle);
|
||||||
|
|
||||||
if (contentContainer) {
|
if (contentContainer) {
|
||||||
// Snap morph closed only on a fresh open; on screen-change re-open we stay at 1
|
|
||||||
// because shouldBeVisible doesn't change and won't drive morph back to 1.
|
|
||||||
if (!shouldBeVisible)
|
if (!shouldBeVisible)
|
||||||
morph.openProgress = 0;
|
morph.openProgress = 0;
|
||||||
_captureChromeAnimTravel();
|
_captureChromeAnimTravel();
|
||||||
@@ -445,11 +437,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (screenChanged) {
|
if (screenChanged) {
|
||||||
// Defer the show one event-loop tick. Qt coalesces a synchronous
|
// Unmap/remap wl_surface across ticks so blur republishes on the new screen.
|
||||||
// false→true visibility flip into a no-op, leaving WindowBlur committed
|
|
||||||
// to the previous screen's wl_surface. Splitting the flip across ticks
|
|
||||||
// forces a real surface destroy+create so BackgroundEffect.surfaceCreated
|
|
||||||
// fires and the blur region republishes on the new surface.
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (!root.shouldBeVisible)
|
if (!root.shouldBeVisible)
|
||||||
return;
|
return;
|
||||||
@@ -568,9 +556,7 @@ Item {
|
|||||||
return Math.abs(value - bound) <= Math.max(1, Theme.hairline(root.dpr) * 2);
|
return Math.abs(value - bound) <= Math.max(1, Theme.hairline(root.dpr) * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snap a frame-perpendicular position flush to the frame bound when it
|
// Snap positions within connector radius flush to the frame edge (avoids pinched arcs).
|
||||||
// lands within the connector radius: a gap smaller than the radius cannot
|
|
||||||
// form the close-gap arc and renders as a pinched wedge instead.
|
|
||||||
function _snapNearFrameBound(value, minBound, maxBound, minIsFrame, maxIsFrame) {
|
function _snapNearFrameBound(value, minBound, maxBound, minIsFrame, maxIsFrame) {
|
||||||
if (!root.usesConnectedSurfaceChrome || !root.closeFrameGapsActive)
|
if (!root.usesConnectedSurfaceChrome || !root.closeFrameGapsActive)
|
||||||
return value;
|
return value;
|
||||||
@@ -637,7 +623,6 @@ Item {
|
|||||||
property real renderedAlignedY: alignedY
|
property real renderedAlignedY: alignedY
|
||||||
property real renderedAlignedHeight: alignedHeight
|
property real renderedAlignedHeight: alignedHeight
|
||||||
readonly property bool renderedGeometryGrowing: alignedHeight >= renderedAlignedHeight
|
readonly property bool renderedGeometryGrowing: alignedHeight >= renderedAlignedHeight
|
||||||
// Snap rendered geometry while the entrance morph runs so it doesn't ride a second animation (side-bar ramp).
|
|
||||||
readonly property bool _settlingToOpen: fullHeightSurface && shouldBeVisible && morphAnim.running
|
readonly property bool _settlingToOpen: fullHeightSurface && shouldBeVisible && morphAnim.running
|
||||||
|
|
||||||
Behavior on renderedAlignedY {
|
Behavior on renderedAlignedY {
|
||||||
@@ -687,8 +672,6 @@ Item {
|
|||||||
return 0;
|
return 0;
|
||||||
if (!root.usesConnectedSurfaceChrome)
|
if (!root.usesConnectedSurfaceChrome)
|
||||||
return exclusion;
|
return exclusion;
|
||||||
// In a shared frame corner, the adjacent connected bar already occupies
|
|
||||||
// one rounded-corner radius before the popout's own connector begins.
|
|
||||||
return exclusion + Theme.connectedCornerRadius * 2;
|
return exclusion + Theme.connectedCornerRadius * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -721,10 +704,8 @@ Item {
|
|||||||
|
|
||||||
switch (effectiveBarPosition) {
|
switch (effectiveBarPosition) {
|
||||||
case SettingsData.Position.Left:
|
case SettingsData.Position.Left:
|
||||||
// bar on left: left side is bar-adjacent (popupGap), right side is frame-perpendicular (edgeGap)
|
|
||||||
return Math.max(popupGap, Math.min(screenWidth - popupWidth - edgeGapRight, anchorX));
|
return Math.max(popupGap, Math.min(screenWidth - popupWidth - edgeGapRight, anchorX));
|
||||||
case SettingsData.Position.Right:
|
case SettingsData.Position.Right:
|
||||||
// bar on right: right side is bar-adjacent (popupGap), left side is frame-perpendicular (edgeGap)
|
|
||||||
return Math.max(edgeGapLeft, Math.min(screenWidth - popupWidth - popupGap, anchorX - popupWidth));
|
return Math.max(edgeGapLeft, Math.min(screenWidth - popupWidth - popupGap, anchorX - popupWidth));
|
||||||
default:
|
default:
|
||||||
const rawX = triggerX + (triggerWidth / 2) - (popupWidth / 2);
|
const rawX = triggerX + (triggerWidth / 2) - (popupWidth / 2);
|
||||||
@@ -744,10 +725,8 @@ Item {
|
|||||||
|
|
||||||
switch (effectiveBarPosition) {
|
switch (effectiveBarPosition) {
|
||||||
case SettingsData.Position.Bottom:
|
case SettingsData.Position.Bottom:
|
||||||
// bar on bottom: bottom side is bar-adjacent (popupGap), top side is frame-perpendicular (edgeGap)
|
|
||||||
return Math.max(edgeGapTop, Math.min(screenHeight - popupHeight - popupGap, anchorY - popupHeight));
|
return Math.max(edgeGapTop, Math.min(screenHeight - popupHeight - popupGap, anchorY - popupHeight));
|
||||||
case SettingsData.Position.Top:
|
case SettingsData.Position.Top:
|
||||||
// bar on top: top side is bar-adjacent (popupGap), bottom side is frame-perpendicular (edgeGap)
|
|
||||||
return Math.max(popupGap, Math.min(screenHeight - popupHeight - edgeGapBottom, anchorY));
|
return Math.max(popupGap, Math.min(screenHeight - popupHeight - edgeGapBottom, anchorY));
|
||||||
default:
|
default:
|
||||||
const rawY = triggerY - (popupHeight / 2);
|
const rawY = triggerY - (popupHeight / 2);
|
||||||
@@ -790,8 +769,6 @@ Item {
|
|||||||
readonly property real s: Math.min(1, contentContainer.scaleValue)
|
readonly property real s: Math.min(1, contentContainer.scaleValue)
|
||||||
readonly property bool trackBlurFromBarEdge: root.usesConnectedSurfaceChrome
|
readonly property bool trackBlurFromBarEdge: root.usesConnectedSurfaceChrome
|
||||||
|
|
||||||
// Connected chrome clips to the bar edge, so its blur grows from
|
|
||||||
// that same edge instead of translating through the bar before settling.
|
|
||||||
readonly property real _dyClamp: (contentContainer.barTop || contentContainer.barBottom) ? Math.max(-contentContainer.height, Math.min(contentContainer.animY, contentContainer.height)) : 0
|
readonly property real _dyClamp: (contentContainer.barTop || contentContainer.barBottom) ? Math.max(-contentContainer.height, Math.min(contentContainer.animY, contentContainer.height)) : 0
|
||||||
readonly property real _dxClamp: (contentContainer.barLeft || contentContainer.barRight) ? Math.max(-contentContainer.width, Math.min(contentContainer.animX, contentContainer.width)) : 0
|
readonly property real _dxClamp: (contentContainer.barLeft || contentContainer.barRight) ? Math.max(-contentContainer.width, Math.min(contentContainer.animX, contentContainer.width)) : 0
|
||||||
|
|
||||||
@@ -827,7 +804,6 @@ Item {
|
|||||||
|
|
||||||
Region {
|
Region {
|
||||||
id: contentInputMask
|
id: contentInputMask
|
||||||
// Use bar-aware mask so bar widget clicks pass through when a popout is open.
|
|
||||||
item: (shouldBeVisible && backgroundInteractive) ? backgroundDismissalMask : contentMaskRect
|
item: (shouldBeVisible && backgroundInteractive) ? backgroundDismissalMask : contentMaskRect
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -938,7 +914,6 @@ Item {
|
|||||||
|
|
||||||
readonly property real computedScaleCollapsed: root.animationScaleCollapsed
|
readonly property real computedScaleCollapsed: root.animationScaleCollapsed
|
||||||
|
|
||||||
// openProgress: 0 = closed (at offset, scaleCollapsed), 1 = open (at 0, scale 1).
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: morph
|
id: morph
|
||||||
property real openProgress: 0
|
property real openProgress: 0
|
||||||
@@ -985,7 +960,6 @@ Item {
|
|||||||
|
|
||||||
clip: shouldClip
|
clip: shouldClip
|
||||||
|
|
||||||
// Bound the clipping strictly to the bar side, allowing massive overflow on the other 3 sides for shadows
|
|
||||||
x: shouldClip ? (contentContainer.barLeft ? -connectedClipAllowance : -clipOversize) : 0
|
x: shouldClip ? (contentContainer.barLeft ? -connectedClipAllowance : -clipOversize) : 0
|
||||||
y: shouldClip ? (contentContainer.barTop ? -connectedClipAllowance : -clipOversize) : 0
|
y: shouldClip ? (contentContainer.barTop ? -connectedClipAllowance : -clipOversize) : 0
|
||||||
|
|
||||||
@@ -1043,8 +1017,6 @@ Item {
|
|||||||
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive) && !root.frameOwnsConnectedChrome
|
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive) && !root.frameOwnsConnectedChrome
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local connected chrome (body + connector fillets joined to
|
|
||||||
// the bar edge) and its shadow as one SDF quad — no FBO.
|
|
||||||
Item {
|
Item {
|
||||||
id: localChrome
|
id: localChrome
|
||||||
visible: root.usesLocalConnectedSurfaceChrome
|
visible: root.usesLocalConnectedSurfaceChrome
|
||||||
@@ -1070,8 +1042,6 @@ Item {
|
|||||||
|
|
||||||
ShaderEffect {
|
ShaderEffect {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
// Shadow overflow pads every side except the bar
|
|
||||||
// edge, where the silhouette must end flush.
|
|
||||||
anchors.topMargin: contentContainer.barTop ? 0 : -localChrome.pad
|
anchors.topMargin: contentContainer.barTop ? 0 : -localChrome.pad
|
||||||
anchors.bottomMargin: contentContainer.barBottom ? 0 : -localChrome.pad
|
anchors.bottomMargin: contentContainer.barBottom ? 0 : -localChrome.pad
|
||||||
anchors.leftMargin: contentContainer.barLeft ? 0 : -localChrome.pad
|
anchors.leftMargin: contentContainer.barLeft ? 0 : -localChrome.pad
|
||||||
@@ -1144,8 +1114,6 @@ Item {
|
|||||||
Connections {
|
Connections {
|
||||||
target: contentWindow
|
target: contentWindow
|
||||||
function onVisibleChanged() {
|
function onVisibleChanged() {
|
||||||
// open() flips contentWindow.visible to rebind the layer surface to
|
|
||||||
// a new screen; don't deactivate the wrapper while still open.
|
|
||||||
if (!contentWindow.visible && !root.shouldBeVisible)
|
if (!contentWindow.visible && !root.shouldBeVisible)
|
||||||
contentWrapper._renderActive = false;
|
contentWrapper._renderActive = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
|
||||||
// Defines the screen area (excluding bars) that dismisses popouts
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
|||||||
@@ -52,10 +52,7 @@ Item {
|
|||||||
targetWindow.BackgroundEffect.blurRegion = null;
|
targetWindow.BackgroundEffect.blurRegion = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force BackgroundEffect to re-publish the blur region on the current wl_surface.
|
// Re-publish blur region after wl_surface remaps (e.g. screen change).
|
||||||
// Clearing first bypasses Quickshell's same-Region dedup in BackgroundEffect::setBlurRegion,
|
|
||||||
// setting pendingBlurRegion=true so the next polish actually ships the region — needed
|
|
||||||
// when the underlying surface has been remapped (e.g. PanelWindow.screen change).
|
|
||||||
function kick() {
|
function kick() {
|
||||||
if (!targetWindow)
|
if (!targetWindow)
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user