mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-15 07:35:20 -04:00
refactor(framemode): unify connected surface chrome via SDF pipeline
- Shadow system rewrite with SDF quads - Replace ConnectedShape/layer FBOs w/frame & chrome SDF shaders - Improve frame blur performance - Plugin performance gate
This commit is contained in:
@@ -1,151 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
import "../Common/ConnectorGeometry.js" as ConnectorGeometry
|
||||
|
||||
// Concave arc connector filling the gap between a bar corner and an adjacent surface.
|
||||
//
|
||||
// NOTE: FrameWindow now uses ConnectedShape.qml for frame-owned connected chrome
|
||||
// (unified single-path rendering). This component is still used by DankPopout's
|
||||
// own shadow source for non-frame-owned chrome (popouts on non-frame screens).
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string barSide: "top"
|
||||
property string placement: "left"
|
||||
property real spacing: 4
|
||||
property real connectorRadius: 12
|
||||
property color color: "transparent"
|
||||
property real edgeStrokeWidth: 0
|
||||
property color edgeStrokeColor: color
|
||||
property real dpr: 1
|
||||
|
||||
readonly property bool isHorizontalBar: barSide === "top" || barSide === "bottom"
|
||||
readonly property bool isPlacementLeft: placement === "left"
|
||||
readonly property real _edgeStrokeWidth: Math.max(0, edgeStrokeWidth)
|
||||
readonly property string arcCorner: ConnectorGeometry.arcCorner(barSide, placement)
|
||||
readonly property real pathStartX: {
|
||||
switch (arcCorner) {
|
||||
case "topLeft":
|
||||
return width;
|
||||
case "topRight":
|
||||
case "bottomLeft":
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
readonly property real pathStartY: {
|
||||
switch (arcCorner) {
|
||||
case "bottomRight":
|
||||
return height;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
readonly property real firstLineX: {
|
||||
switch (arcCorner) {
|
||||
case "topLeft":
|
||||
case "bottomLeft":
|
||||
return width;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
readonly property real firstLineY: {
|
||||
switch (arcCorner) {
|
||||
case "topLeft":
|
||||
case "topRight":
|
||||
return height;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
readonly property real secondLineX: {
|
||||
switch (arcCorner) {
|
||||
case "topRight":
|
||||
case "bottomLeft":
|
||||
case "bottomRight":
|
||||
return width;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
readonly property real secondLineY: {
|
||||
switch (arcCorner) {
|
||||
case "topLeft":
|
||||
case "topRight":
|
||||
case "bottomLeft":
|
||||
return height;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
readonly property real arcCenterX: arcCorner === "topRight" || arcCorner === "bottomRight" ? width : 0
|
||||
readonly property real arcCenterY: arcCorner === "bottomLeft" || arcCorner === "bottomRight" ? height : 0
|
||||
readonly property real arcStartAngle: {
|
||||
switch (arcCorner) {
|
||||
case "topLeft":
|
||||
case "topRight":
|
||||
return 90;
|
||||
case "bottomLeft":
|
||||
return 0;
|
||||
default:
|
||||
return -90;
|
||||
}
|
||||
}
|
||||
readonly property real arcSweepAngle: {
|
||||
switch (arcCorner) {
|
||||
case "topRight":
|
||||
return 90;
|
||||
default:
|
||||
return -90;
|
||||
}
|
||||
}
|
||||
|
||||
width: isHorizontalBar ? connectorRadius : (spacing + connectorRadius)
|
||||
height: isHorizontalBar ? (spacing + connectorRadius) : connectorRadius
|
||||
|
||||
Shape {
|
||||
x: -root._edgeStrokeWidth
|
||||
y: -root._edgeStrokeWidth
|
||||
width: root.width + root._edgeStrokeWidth * 2
|
||||
height: root.height + root._edgeStrokeWidth * 2
|
||||
asynchronous: false
|
||||
antialiasing: true
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
layer.textureSize: root.dpr > 1 ? Qt.size(Math.ceil(width * root.dpr), Math.ceil(height * root.dpr)) : Qt.size(0, 0)
|
||||
|
||||
ShapePath {
|
||||
fillColor: root.color
|
||||
strokeColor: root._edgeStrokeWidth > 0 ? root.edgeStrokeColor : "transparent"
|
||||
strokeWidth: root._edgeStrokeWidth * 2
|
||||
joinStyle: ShapePath.RoundJoin
|
||||
capStyle: ShapePath.RoundCap
|
||||
fillRule: ShapePath.WindingFill
|
||||
startX: root.pathStartX + root._edgeStrokeWidth
|
||||
startY: root.pathStartY + root._edgeStrokeWidth
|
||||
|
||||
PathLine {
|
||||
x: root.firstLineX + root._edgeStrokeWidth
|
||||
y: root.firstLineY + root._edgeStrokeWidth
|
||||
}
|
||||
|
||||
PathLine {
|
||||
x: root.secondLineX + root._edgeStrokeWidth
|
||||
y: root.secondLineY + root._edgeStrokeWidth
|
||||
}
|
||||
|
||||
PathAngleArc {
|
||||
centerX: root.arcCenterX + root._edgeStrokeWidth
|
||||
centerY: root.arcCenterY + root._edgeStrokeWidth
|
||||
radiusX: root.connectorRadius
|
||||
radiusY: root.connectorRadius
|
||||
startAngle: root.arcStartAngle
|
||||
sweepAngle: root.arcSweepAngle
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,414 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
import qs.Common
|
||||
|
||||
// Unified connected silhouette: body + near/far concave arcs as one ShapePath.
|
||||
// Keeping the connected chrome in one path avoids sibling alignment seams.
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string barSide: "top"
|
||||
|
||||
property real bodyWidth: 0
|
||||
property real bodyHeight: 0
|
||||
|
||||
property real connectorRadius: 12
|
||||
property real startConnectorRadius: connectorRadius
|
||||
property real endConnectorRadius: connectorRadius
|
||||
property real farStartConnectorRadius: 0
|
||||
property real farEndConnectorRadius: 0
|
||||
|
||||
property real surfaceRadius: 12
|
||||
|
||||
property color fillColor: "transparent"
|
||||
|
||||
readonly property bool _horiz: barSide === "top" || barSide === "bottom"
|
||||
readonly property real _sc: Math.max(0, startConnectorRadius)
|
||||
readonly property real _ec: Math.max(0, endConnectorRadius)
|
||||
readonly property real _fsc: Math.max(0, farStartConnectorRadius)
|
||||
readonly property real _fec: Math.max(0, farEndConnectorRadius)
|
||||
readonly property real _firstCr: barSide === "left" ? _sc : _ec
|
||||
readonly property real _secondCr: barSide === "left" ? _ec : _sc
|
||||
readonly property real _firstFarCr: barSide === "left" ? _fsc : _fec
|
||||
readonly property real _secondFarCr: barSide === "left" ? _fec : _fsc
|
||||
readonly property real _farExtent: Math.max(_fsc, _fec)
|
||||
readonly property real _sr: Math.max(0, Math.min(surfaceRadius, (_horiz ? bodyWidth : bodyHeight) / 2, (_horiz ? bodyHeight : bodyWidth) / 2))
|
||||
readonly property real _firstSr: _firstFarCr > 0 ? 0 : _sr
|
||||
readonly property real _secondSr: _secondFarCr > 0 ? 0 : _sr
|
||||
readonly property real _firstFarInset: _firstFarCr > 0 ? _firstFarCr : _firstSr
|
||||
readonly property real _secondFarInset: _secondFarCr > 0 ? _secondFarCr : _secondSr
|
||||
|
||||
// Root-level aliases — PathArc/PathLine elements can't use `parent`.
|
||||
readonly property real _bw: bodyWidth
|
||||
readonly property real _bh: bodyHeight
|
||||
readonly property real _bodyLeft: _horiz ? _sc : (barSide === "right" ? _farExtent : 0)
|
||||
readonly property real _bodyRight: _bodyLeft + _bw
|
||||
readonly property real _bodyTop: _horiz ? (barSide === "bottom" ? _farExtent : 0) : _sc
|
||||
readonly property real _bodyBottom: _bodyTop + _bh
|
||||
readonly property real _totalW: _horiz ? _bw + _sc + _ec : _bw + _farExtent
|
||||
readonly property real _totalH: _horiz ? _bh + _farExtent : _bh + _sc + _ec
|
||||
|
||||
width: _totalW
|
||||
height: _totalH
|
||||
|
||||
readonly property real bodyX: root._bodyLeft
|
||||
readonly property real bodyY: root._bodyTop
|
||||
|
||||
Shape {
|
||||
anchors.fill: parent
|
||||
asynchronous: false
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
antialiasing: true
|
||||
|
||||
ShapePath {
|
||||
fillColor: root.fillColor
|
||||
strokeWidth: -1
|
||||
fillRule: ShapePath.WindingFill
|
||||
|
||||
// CW path: bar edge → concave arc → body → convex arc → far edge → convex arc → body → concave arc
|
||||
|
||||
startX: root.barSide === "right" ? root._totalW : 0
|
||||
startY: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._totalH;
|
||||
case "left":
|
||||
return root._totalH;
|
||||
case "right":
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Bar edge
|
||||
PathLine {
|
||||
x: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return 0;
|
||||
case "right":
|
||||
return root._totalW;
|
||||
default:
|
||||
return root._totalW;
|
||||
}
|
||||
}
|
||||
y: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._totalH;
|
||||
case "left":
|
||||
return 0;
|
||||
case "right":
|
||||
return root._totalH;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Concave arc 1
|
||||
PathArc {
|
||||
relativeX: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return root._firstCr;
|
||||
case "right":
|
||||
return -root._firstCr;
|
||||
default:
|
||||
return -root._firstCr;
|
||||
}
|
||||
}
|
||||
relativeY: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return -root._firstCr;
|
||||
case "left":
|
||||
return root._firstCr;
|
||||
case "right":
|
||||
return -root._firstCr;
|
||||
default:
|
||||
return root._firstCr;
|
||||
}
|
||||
}
|
||||
radiusX: root._firstCr
|
||||
radiusY: root._firstCr
|
||||
direction: root.barSide === "bottom" ? PathArc.Clockwise : PathArc.Counterclockwise
|
||||
}
|
||||
|
||||
// Body edge to first convex corner
|
||||
PathLine {
|
||||
x: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return root._bodyRight - root._firstSr;
|
||||
case "right":
|
||||
return root._bodyLeft + root._firstSr;
|
||||
default:
|
||||
return root._bodyRight;
|
||||
}
|
||||
}
|
||||
y: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._bodyTop + root._firstSr;
|
||||
case "left":
|
||||
return root._bodyTop;
|
||||
case "right":
|
||||
return root._bodyBottom;
|
||||
default:
|
||||
return root._bodyBottom - root._firstSr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convex arc 1
|
||||
PathArc {
|
||||
relativeX: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return root._firstSr;
|
||||
case "right":
|
||||
return -root._firstSr;
|
||||
default:
|
||||
return -root._firstSr;
|
||||
}
|
||||
}
|
||||
relativeY: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return -root._firstSr;
|
||||
case "left":
|
||||
return root._firstSr;
|
||||
case "right":
|
||||
return -root._firstSr;
|
||||
default:
|
||||
return root._firstSr;
|
||||
}
|
||||
}
|
||||
radiusX: root._firstSr
|
||||
radiusY: root._firstSr
|
||||
direction: root.barSide === "bottom" ? PathArc.Counterclockwise : PathArc.Clockwise
|
||||
}
|
||||
|
||||
// Opposite-side connector 1
|
||||
PathLine {
|
||||
x: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return root._firstFarCr > 0 ? root._bodyRight + root._firstFarCr : root._bodyRight;
|
||||
case "right":
|
||||
return root._firstFarCr > 0 ? root._bodyLeft - root._firstFarCr : root._bodyLeft;
|
||||
default:
|
||||
return root._firstFarCr > 0 ? root._bodyRight : root._bodyRight - root._firstSr;
|
||||
}
|
||||
}
|
||||
y: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._firstFarCr > 0 ? root._bodyTop - root._firstFarCr : root._bodyTop;
|
||||
case "left":
|
||||
return root._firstFarCr > 0 ? root._bodyTop : root._bodyTop + root._firstSr;
|
||||
case "right":
|
||||
return root._firstFarCr > 0 ? root._bodyBottom : root._bodyBottom - root._firstSr;
|
||||
default:
|
||||
return root._firstFarCr > 0 ? root._bodyBottom + root._firstFarCr : root._bodyBottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PathArc {
|
||||
relativeX: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return -root._firstFarCr;
|
||||
case "right":
|
||||
return root._firstFarCr;
|
||||
default:
|
||||
return -root._firstFarCr;
|
||||
}
|
||||
}
|
||||
relativeY: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._firstFarCr;
|
||||
case "left":
|
||||
return root._firstFarCr;
|
||||
case "right":
|
||||
return -root._firstFarCr;
|
||||
default:
|
||||
return -root._firstFarCr;
|
||||
}
|
||||
}
|
||||
radiusX: root._firstFarCr
|
||||
radiusY: root._firstFarCr
|
||||
direction: root.barSide === "bottom" ? PathArc.Clockwise : PathArc.Counterclockwise
|
||||
}
|
||||
|
||||
// Far edge
|
||||
PathLine {
|
||||
x: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return root._bodyRight;
|
||||
case "right":
|
||||
return root._bodyLeft;
|
||||
default:
|
||||
return root._bodyLeft + root._secondFarInset;
|
||||
}
|
||||
}
|
||||
y: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._bodyTop;
|
||||
case "left":
|
||||
return root._bodyBottom - root._secondFarInset;
|
||||
case "right":
|
||||
return root._bodyTop + root._secondFarInset;
|
||||
default:
|
||||
return root._bodyBottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Opposite-side connector 2
|
||||
PathArc {
|
||||
relativeX: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return root._secondFarCr;
|
||||
case "right":
|
||||
return -root._secondFarCr;
|
||||
default:
|
||||
return -root._secondFarCr;
|
||||
}
|
||||
}
|
||||
relativeY: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return -root._secondFarCr;
|
||||
case "left":
|
||||
return root._secondFarCr;
|
||||
case "right":
|
||||
return -root._secondFarCr;
|
||||
default:
|
||||
return root._secondFarCr;
|
||||
}
|
||||
}
|
||||
radiusX: root._secondFarCr
|
||||
radiusY: root._secondFarCr
|
||||
direction: root.barSide === "bottom" ? PathArc.Clockwise : PathArc.Counterclockwise
|
||||
}
|
||||
|
||||
PathLine {
|
||||
x: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return root._secondFarCr > 0 ? root._bodyRight : root._bodyRight;
|
||||
case "right":
|
||||
return root._secondFarCr > 0 ? root._bodyLeft : root._bodyLeft;
|
||||
default:
|
||||
return root._secondFarCr > 0 ? root._bodyLeft : root._bodyLeft + root._secondSr;
|
||||
}
|
||||
}
|
||||
y: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._secondFarCr > 0 ? root._bodyTop : root._bodyTop;
|
||||
case "left":
|
||||
return root._secondFarCr > 0 ? root._bodyBottom : root._bodyBottom - root._secondSr;
|
||||
case "right":
|
||||
return root._secondFarCr > 0 ? root._bodyTop : root._bodyTop + root._secondSr;
|
||||
default:
|
||||
return root._secondFarCr > 0 ? root._bodyBottom : root._bodyBottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convex arc 2
|
||||
PathArc {
|
||||
relativeX: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return -root._secondSr;
|
||||
case "right":
|
||||
return root._secondSr;
|
||||
default:
|
||||
return -root._secondSr;
|
||||
}
|
||||
}
|
||||
relativeY: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._secondSr;
|
||||
case "left":
|
||||
return root._secondSr;
|
||||
case "right":
|
||||
return -root._secondSr;
|
||||
default:
|
||||
return -root._secondSr;
|
||||
}
|
||||
}
|
||||
radiusX: root._secondSr
|
||||
radiusY: root._secondSr
|
||||
direction: root.barSide === "bottom" ? PathArc.Counterclockwise : PathArc.Clockwise
|
||||
}
|
||||
|
||||
// Body edge to second concave arc
|
||||
PathLine {
|
||||
x: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return root._bodyLeft + root._ec;
|
||||
case "right":
|
||||
return root._bodyRight - root._sc;
|
||||
default:
|
||||
return root._bodyLeft;
|
||||
}
|
||||
}
|
||||
y: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._bodyBottom - root._sc;
|
||||
case "left":
|
||||
return root._bodyBottom;
|
||||
case "right":
|
||||
return root._bodyTop;
|
||||
default:
|
||||
return root._bodyTop + root._sc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Concave arc 2
|
||||
PathArc {
|
||||
relativeX: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return -root._secondCr;
|
||||
case "right":
|
||||
return root._secondCr;
|
||||
default:
|
||||
return -root._secondCr;
|
||||
}
|
||||
}
|
||||
relativeY: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._secondCr;
|
||||
case "left":
|
||||
return root._secondCr;
|
||||
case "right":
|
||||
return -root._secondCr;
|
||||
default:
|
||||
return -root._secondCr;
|
||||
}
|
||||
}
|
||||
radiusX: root._secondCr
|
||||
radiusY: root._secondCr
|
||||
direction: root.barSide === "bottom" ? PathArc.Clockwise : PathArc.Counterclockwise
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,8 +289,6 @@ PanelWindow {
|
||||
borderColor: Theme.outlineMedium
|
||||
borderWidth: 1
|
||||
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
||||
layer.textureSize: Qt.size(Math.round(width * root.dpr), Math.round(height * root.dpr))
|
||||
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import Quickshell.Hyprland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
|
||||
@@ -50,6 +51,20 @@ Item {
|
||||
readonly property var contentLoader: impl.item ? impl.item.contentLoader : _fallbackContentLoader
|
||||
readonly property var overlayLoader: impl.item ? impl.item.overlayLoader : _fallbackOverlayLoader
|
||||
readonly property var backgroundWindow: impl.item ? impl.item.backgroundWindow : null
|
||||
readonly property var contentWindow: impl.item ? impl.item.contentWindow : null
|
||||
|
||||
// Hyprland OnDemand grab: whitelist popout surfaces and bars so dismiss clicks still land.
|
||||
HyprlandFocusGrab {
|
||||
windows: {
|
||||
const list = [];
|
||||
if (root.contentWindow)
|
||||
list.push(root.contentWindow);
|
||||
if (root.backgroundWindow && root.backgroundWindow !== root.contentWindow)
|
||||
list.push(root.backgroundWindow);
|
||||
return list.concat(KeyboardFocus.barWindows);
|
||||
}
|
||||
active: KeyboardFocus.wantsGrab(root.shouldBeVisible, root.customKeyboardFocus)
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: _fallbackContentLoader
|
||||
@@ -127,8 +142,6 @@ Item {
|
||||
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() {
|
||||
_resolveBackendForScreen(screen);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import "../Common/ConnectorGeometry.js" as ConnectorGeometry
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -18,6 +17,7 @@ Item {
|
||||
property Component overlayContent: null
|
||||
property alias overlayLoader: overlayLoader
|
||||
readonly property alias backgroundWindow: contentWindow
|
||||
readonly property alias contentWindow: contentWindow
|
||||
property real popupWidth: 400
|
||||
property real popupHeight: 300
|
||||
property real triggerX: 0
|
||||
@@ -38,8 +38,6 @@ Item {
|
||||
property bool fullHeightSurface: false
|
||||
property bool _primeContent: false
|
||||
property bool _resizeActive: false
|
||||
property string _chromeClaimId: ""
|
||||
property int _connectedChromeSerial: 0
|
||||
property real _chromeAnimTravelX: 1
|
||||
property real _chromeAnimTravelY: 1
|
||||
property bool _fullSyncQueued: false
|
||||
@@ -55,7 +53,6 @@ Item {
|
||||
"rightBar": 0
|
||||
})
|
||||
property var screen: null
|
||||
// Connected resize uses one full-screen surface; body-sized regions are masks.
|
||||
readonly property bool useBackgroundWindow: false
|
||||
readonly property var effectivePopoutLayer: LayerShell.fromEnv("DMS_POPOUT_LAYER", root.triggerUsesOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top, {
|
||||
"allow": ["top", "overlay"],
|
||||
@@ -93,13 +90,49 @@ Item {
|
||||
signal popoutClosed
|
||||
signal backgroundClicked
|
||||
|
||||
// Coalesce per-channel dirty bits; one ConnectedModeState write per tick.
|
||||
Timer {
|
||||
id: _syncTimer
|
||||
interval: 0
|
||||
onTriggered: root._flushSync()
|
||||
}
|
||||
|
||||
ConnectedSurfaceLease {
|
||||
id: chromeLease
|
||||
claimPrefix: root.layerNamespace
|
||||
screenName: root.screen ? root.screen.name : ""
|
||||
enabled: root.frameOwnsConnectedChrome
|
||||
active: contentWindow.visible || root.shouldBeVisible
|
||||
presented: contentWindow.visible || root.shouldBeVisible
|
||||
renewTokenOnRecovery: false
|
||||
isCurrentOwner: function(name) {
|
||||
return PopoutManager.isCurrentPopout(root.popoutHandle, name);
|
||||
}
|
||||
hasOwner: function(_name, ownerId) {
|
||||
return ConnectedModeState.hasPopoutOwner(ownerId);
|
||||
}
|
||||
statePresent: function(name, ownerId) {
|
||||
return ConnectedModeState.hasPopoutOwner(ownerId) && ConnectedModeState.hasSurfaceDescriptor(name, "popout", ownerId);
|
||||
}
|
||||
claimState: function(_name, state, ownerId) {
|
||||
return ConnectedModeState.claimPopout(ownerId, state);
|
||||
}
|
||||
ensureState: function(_name, state, ownerId) {
|
||||
if (!ConnectedModeState.hasPopoutOwner(ownerId))
|
||||
return false;
|
||||
return ConnectedModeState.updatePopout(ownerId, state);
|
||||
}
|
||||
releaseState: function(_name, ownerId) {
|
||||
return ConnectedModeState.releasePopout(ownerId);
|
||||
}
|
||||
updateAnimationState: function(_name, ownerId, animX, animY) {
|
||||
return ConnectedModeState.setPopoutAnim(ownerId, animX, animY);
|
||||
}
|
||||
updateBodyState: function(_name, ownerId, bodyX, bodyY, bodyW, bodyH) {
|
||||
return ConnectedModeState.setPopoutBody(ownerId, bodyX, bodyY, bodyW, bodyH);
|
||||
}
|
||||
onRecoveryRequested: root._queueFullSync()
|
||||
}
|
||||
|
||||
property var _lastOpenedScreen: null
|
||||
property bool isClosing: false
|
||||
|
||||
@@ -169,11 +202,6 @@ Item {
|
||||
setBarContext(pos, bottomGap);
|
||||
}
|
||||
|
||||
function _nextChromeClaimId() {
|
||||
_connectedChromeSerial += 1;
|
||||
return layerNamespace + ":" + _connectedChromeSerial + ":" + (new Date()).getTime();
|
||||
}
|
||||
|
||||
function _captureChromeAnimTravel() {
|
||||
_chromeAnimTravelX = Math.max(1, Math.abs(contentContainer.offsetX));
|
||||
_chromeAnimTravelY = Math.max(1, Math.abs(contentContainer.offsetY));
|
||||
@@ -203,15 +231,35 @@ Item {
|
||||
|
||||
function _connectedChromeState(visibleOverride) {
|
||||
const visible = visibleOverride !== undefined ? !!visibleOverride : contentWindow.visible;
|
||||
const presented = contentWindow.visible || root.shouldBeVisible;
|
||||
const phase = root.isClosing ? "closing" : (!presented ? "hidden" : (!contentWindow.visible && root.shouldBeVisible ? "opening" : "open"));
|
||||
const bodyRect = {
|
||||
"x": root.alignedX,
|
||||
"y": root.renderedAlignedY,
|
||||
"width": root.alignedWidth,
|
||||
"height": root.renderedAlignedHeight
|
||||
};
|
||||
const animationOffset = {
|
||||
"x": _connectedChromeAnimX(),
|
||||
"y": _connectedChromeAnimY()
|
||||
};
|
||||
return {
|
||||
"kind": "popout",
|
||||
"screenName": root.screen ? root.screen.name : "",
|
||||
"phase": phase,
|
||||
"visible": visible,
|
||||
"presented": presented,
|
||||
"barSide": contentContainer.connectedBarSide,
|
||||
"bodyRect": bodyRect,
|
||||
"animationOffset": animationOffset,
|
||||
"scale": 1,
|
||||
"opacity": Theme.connectedSurfaceColor.a,
|
||||
"bodyX": root.alignedX,
|
||||
"bodyY": root.renderedAlignedY,
|
||||
"bodyW": root.alignedWidth,
|
||||
"bodyH": root.renderedAlignedHeight,
|
||||
"animX": _connectedChromeAnimX(),
|
||||
"animY": _connectedChromeAnimY(),
|
||||
"animX": animationOffset.x,
|
||||
"animY": animationOffset.y,
|
||||
"screen": root.screen ? root.screen.name : "",
|
||||
"omitStartConnector": root._closeGapOmitStartConnector(),
|
||||
"omitEndConnector": root._closeGapOmitEndConnector()
|
||||
@@ -219,34 +267,18 @@ Item {
|
||||
}
|
||||
|
||||
function _publishConnectedChromeState(forceClaim, visibleOverride) {
|
||||
if (!root.frameOwnsConnectedChrome || !root.screen || !_chromeClaimId)
|
||||
if (!root.frameOwnsConnectedChrome || !root.screen)
|
||||
return false;
|
||||
|
||||
const screenName = root.screen.name;
|
||||
const isCurrent = PopoutManager.isCurrentPopout(popoutHandle, screenName);
|
||||
if (!ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
|
||||
if (!isCurrent)
|
||||
return false;
|
||||
forceClaim = true;
|
||||
} else if (forceClaim && !isCurrent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const state = _connectedChromeState(visibleOverride);
|
||||
return forceClaim ? ConnectedModeState.claimPopout(_chromeClaimId, state) : ConnectedModeState.updatePopout(_chromeClaimId, state);
|
||||
return chromeLease.publish(_connectedChromeState(visibleOverride), !!forceClaim);
|
||||
}
|
||||
|
||||
function _releaseConnectedChromeState() {
|
||||
if (_chromeClaimId)
|
||||
ConnectedModeState.releasePopout(_chromeClaimId);
|
||||
_chromeClaimId = "";
|
||||
chromeLease.release();
|
||||
}
|
||||
|
||||
// ─── Exposed animation state for ConnectedModeState ────────────────────
|
||||
readonly property real contentAnimX: contentContainer.animX
|
||||
readonly property real contentAnimY: contentContainer.animY
|
||||
|
||||
// ─── ConnectedModeState sync ────────────────────────────────────────────
|
||||
function _syncPopoutChromeState() {
|
||||
if (!root.frameOwnsConnectedChrome) {
|
||||
_releaseConnectedChromeState();
|
||||
@@ -258,16 +290,11 @@ Item {
|
||||
}
|
||||
if (!contentWindow.visible && !shouldBeVisible)
|
||||
return;
|
||||
if (!_chromeClaimId) {
|
||||
if (!PopoutManager.isCurrentPopout(popoutHandle, root.screen.name))
|
||||
return;
|
||||
_chromeClaimId = _nextChromeClaimId();
|
||||
}
|
||||
_publishConnectedChromeState(!ConnectedModeState.hasPopoutOwner(_chromeClaimId));
|
||||
_publishConnectedChromeState(false);
|
||||
}
|
||||
|
||||
function _syncPopoutAnim(axis) {
|
||||
if (!root.frameOwnsConnectedChrome || !_chromeClaimId)
|
||||
if (!root.frameOwnsConnectedChrome || !chromeLease.claimId)
|
||||
return;
|
||||
if (!contentWindow.visible && !shouldBeVisible)
|
||||
return;
|
||||
@@ -276,25 +303,15 @@ Item {
|
||||
const syncY = axis === "y" && (barSide === "top" || barSide === "bottom");
|
||||
if (!syncX && !syncY)
|
||||
return;
|
||||
if (!ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
|
||||
if (root.screen && PopoutManager.isCurrentPopout(popoutHandle, root.screen.name))
|
||||
_queueFullSync();
|
||||
return;
|
||||
}
|
||||
ConnectedModeState.setPopoutAnim(_chromeClaimId, syncX ? _connectedChromeAnimX() : undefined, syncY ? _connectedChromeAnimY() : undefined);
|
||||
chromeLease.updateAnim(syncX ? _connectedChromeAnimX() : undefined, syncY ? _connectedChromeAnimY() : undefined);
|
||||
}
|
||||
|
||||
function _syncPopoutBody() {
|
||||
if (!root.frameOwnsConnectedChrome || !_chromeClaimId)
|
||||
if (!root.frameOwnsConnectedChrome || !chromeLease.claimId)
|
||||
return;
|
||||
if (!contentWindow.visible && !shouldBeVisible)
|
||||
return;
|
||||
if (!ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
|
||||
if (root.screen && PopoutManager.isCurrentPopout(popoutHandle, root.screen.name))
|
||||
_queueFullSync();
|
||||
return;
|
||||
}
|
||||
ConnectedModeState.setPopoutBody(_chromeClaimId, root.alignedX, root.renderedAlignedY, root.alignedWidth, root.renderedAlignedHeight);
|
||||
chromeLease.updateBody(root.alignedX, root.renderedAlignedY, root.alignedWidth, root.renderedAlignedHeight);
|
||||
}
|
||||
|
||||
property bool _animSyncQueued: false
|
||||
@@ -345,13 +362,10 @@ Item {
|
||||
Connections {
|
||||
target: contentWindow
|
||||
function onVisibleChanged() {
|
||||
if (contentWindow.visible) {
|
||||
if (!root._chromeClaimId)
|
||||
root._chromeClaimId = root._nextChromeClaimId();
|
||||
if (contentWindow.visible)
|
||||
root._publishConnectedChromeState(true);
|
||||
} else {
|
||||
else
|
||||
root._releaseConnectedChromeState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,11 +373,8 @@ Item {
|
||||
target: SettingsData
|
||||
function onConnectedFrameModeActiveChanged() {
|
||||
if (root.frameOwnsConnectedChrome) {
|
||||
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name)) {
|
||||
if (!root._chromeClaimId)
|
||||
root._chromeClaimId = root._nextChromeClaimId();
|
||||
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name))
|
||||
root._publishConnectedChromeState(true);
|
||||
}
|
||||
} else {
|
||||
root._releaseConnectedChromeState();
|
||||
}
|
||||
@@ -376,16 +387,17 @@ Item {
|
||||
Connections {
|
||||
target: ConnectedModeState
|
||||
function onPopoutOwnerIdChanged() {
|
||||
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name) && !ConnectedModeState.hasPopoutOwner(root._chromeClaimId))
|
||||
root._queueFullSync();
|
||||
chromeLease.checkOwnershipRecovery();
|
||||
}
|
||||
function onSurfaceDescriptorsChanged() {
|
||||
chromeLease.checkStateRecovery();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: PopoutManager
|
||||
function onPopoutChanged() {
|
||||
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name))
|
||||
root._queueFullSync();
|
||||
chromeLease.requestRecovery();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,34 +418,26 @@ Item {
|
||||
|
||||
const screenChanged = _lastOpenedScreen !== null && _lastOpenedScreen !== screen;
|
||||
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;
|
||||
}
|
||||
_lastOpenedScreen = screen;
|
||||
PopoutManager.showPopout(popoutHandle);
|
||||
|
||||
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)
|
||||
morph.openProgress = 0;
|
||||
_captureChromeAnimTravel();
|
||||
}
|
||||
|
||||
if (root.frameOwnsConnectedChrome) {
|
||||
_chromeClaimId = _nextChromeClaimId();
|
||||
chromeLease.beginClaim();
|
||||
_publishConnectedChromeState(true, true);
|
||||
} else {
|
||||
_chromeClaimId = "";
|
||||
chromeLease.release();
|
||||
}
|
||||
|
||||
if (screenChanged) {
|
||||
// Defer the show one event-loop tick. Qt coalesces a synchronous
|
||||
// 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.
|
||||
// Unmap/remap wl_surface across ticks so blur republishes on the new screen.
|
||||
Qt.callLater(() => {
|
||||
if (!root.shouldBeVisible)
|
||||
return;
|
||||
@@ -552,6 +556,18 @@ Item {
|
||||
return Math.abs(value - bound) <= Math.max(1, Theme.hairline(root.dpr) * 2);
|
||||
}
|
||||
|
||||
// Snap positions within connector radius flush to the frame edge (avoids pinched arcs).
|
||||
function _snapNearFrameBound(value, minBound, maxBound, minIsFrame, maxIsFrame) {
|
||||
if (!root.usesConnectedSurfaceChrome || !root.closeFrameGapsActive)
|
||||
return value;
|
||||
const snapDist = Theme.connectedCornerRadius;
|
||||
if (maxIsFrame && value < maxBound && maxBound - value < snapDist && maxBound - value <= value - minBound)
|
||||
return maxBound;
|
||||
if (minIsFrame && value > minBound && value - minBound < snapDist)
|
||||
return minBound;
|
||||
return value;
|
||||
}
|
||||
|
||||
function _closeGapClampedToFrameSide(side) {
|
||||
if (!root.closeFrameGapsActive)
|
||||
return false;
|
||||
@@ -607,7 +623,6 @@ Item {
|
||||
property real renderedAlignedY: alignedY
|
||||
property real renderedAlignedHeight: alignedHeight
|
||||
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
|
||||
|
||||
Behavior on renderedAlignedY {
|
||||
@@ -657,8 +672,6 @@ Item {
|
||||
return 0;
|
||||
if (!root.usesConnectedSurfaceChrome)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -691,16 +704,16 @@ Item {
|
||||
|
||||
switch (effectiveBarPosition) {
|
||||
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));
|
||||
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));
|
||||
default:
|
||||
const rawX = triggerX + (triggerWidth / 2) - (popupWidth / 2);
|
||||
const minX = Math.max(edgeGapLeft, adjacentBarClearance(adjacentBarInfo.leftBar));
|
||||
const maxX = screenWidth - popupWidth - Math.max(edgeGapRight, adjacentBarClearance(adjacentBarInfo.rightBar));
|
||||
return Math.max(minX, Math.min(maxX, rawX));
|
||||
const clearLeft = adjacentBarClearance(adjacentBarInfo.leftBar);
|
||||
const clearRight = adjacentBarClearance(adjacentBarInfo.rightBar);
|
||||
const minX = Math.max(edgeGapLeft, clearLeft);
|
||||
const maxX = screenWidth - popupWidth - Math.max(edgeGapRight, clearRight);
|
||||
return _snapNearFrameBound(Math.max(minX, Math.min(maxX, rawX)), minX, maxX, edgeGapLeft >= clearLeft, edgeGapRight >= clearRight);
|
||||
}
|
||||
})(), dpr)
|
||||
|
||||
@@ -712,44 +725,34 @@ Item {
|
||||
|
||||
switch (effectiveBarPosition) {
|
||||
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));
|
||||
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));
|
||||
default:
|
||||
const rawY = triggerY - (popupHeight / 2);
|
||||
const minY = Math.max(edgeGapTop, adjacentBarClearance(adjacentBarInfo.topBar));
|
||||
const maxY = screenHeight - popupHeight - Math.max(edgeGapBottom, adjacentBarClearance(adjacentBarInfo.bottomBar));
|
||||
return Math.max(minY, Math.min(maxY, rawY));
|
||||
const clearTop = adjacentBarClearance(adjacentBarInfo.topBar);
|
||||
const clearBottom = adjacentBarClearance(adjacentBarInfo.bottomBar);
|
||||
const minY = Math.max(edgeGapTop, clearTop);
|
||||
const maxY = screenHeight - popupHeight - Math.max(edgeGapBottom, clearBottom);
|
||||
return _snapNearFrameBound(Math.max(minY, Math.min(maxY, rawY)), minY, maxY, edgeGapTop >= clearTop, edgeGapBottom >= clearBottom);
|
||||
}
|
||||
})(), dpr)
|
||||
|
||||
readonly property real triggeringBarLeftExclusion: (effectiveBarPosition === SettingsData.Position.Left && barWidth > 0) ? Math.max(0, barX + barWidth) : 0
|
||||
readonly property real triggeringBarTopExclusion: (effectiveBarPosition === SettingsData.Position.Top && barHeight > 0) ? Math.max(0, barY + barHeight) : 0
|
||||
readonly property real triggeringBarRightExclusion: (effectiveBarPosition === SettingsData.Position.Right && barWidth > 0) ? Math.max(0, screenWidth - barX) : 0
|
||||
readonly property real triggeringBarBottomExclusion: (effectiveBarPosition === SettingsData.Position.Bottom && barHeight > 0) ? Math.max(0, screenHeight - barY) : 0
|
||||
readonly property real maskX: _dismissZone.x
|
||||
readonly property real maskY: _dismissZone.y
|
||||
readonly property real maskWidth: _dismissZone.width
|
||||
readonly property real maskHeight: _dismissZone.height
|
||||
|
||||
readonly property real maskX: {
|
||||
const adjacentLeftBar = adjacentBarInfo?.leftBar ?? 0;
|
||||
return Math.max(triggeringBarLeftExclusion, adjacentLeftBar);
|
||||
}
|
||||
|
||||
readonly property real maskY: {
|
||||
const adjacentTopBar = adjacentBarInfo?.topBar ?? 0;
|
||||
return Math.max(triggeringBarTopExclusion, adjacentTopBar);
|
||||
}
|
||||
|
||||
readonly property real maskWidth: {
|
||||
const adjacentRightBar = adjacentBarInfo?.rightBar ?? 0;
|
||||
const rightExclusion = Math.max(triggeringBarRightExclusion, adjacentRightBar);
|
||||
return Math.max(100, screenWidth - maskX - rightExclusion);
|
||||
}
|
||||
|
||||
readonly property real maskHeight: {
|
||||
const adjacentBottomBar = adjacentBarInfo?.bottomBar ?? 0;
|
||||
const bottomExclusion = Math.max(triggeringBarBottomExclusion, adjacentBottomBar);
|
||||
return Math.max(100, screenHeight - maskY - bottomExclusion);
|
||||
DismissZone {
|
||||
id: _dismissZone
|
||||
barPosition: root.effectiveBarPosition
|
||||
barX: root.barX
|
||||
barY: root.barY
|
||||
barWidth: root.barWidth
|
||||
barHeight: root.barHeight
|
||||
screenWidth: root.screenWidth
|
||||
screenHeight: root.screenHeight
|
||||
adjacentBarInfo: root.adjacentBarInfo
|
||||
}
|
||||
|
||||
PanelWindow {
|
||||
@@ -764,10 +767,8 @@ Item {
|
||||
blurEnabled: root.effectiveSurfaceBlurEnabled && !root.frameOwnsConnectedChrome
|
||||
|
||||
readonly property real s: Math.min(1, contentContainer.scaleValue)
|
||||
readonly property bool trackBlurFromBarEdge: root.usesConnectedSurfaceChrome || Theme.isDirectionalEffect
|
||||
readonly property bool trackBlurFromBarEdge: root.usesConnectedSurfaceChrome
|
||||
|
||||
// Directional popouts clip to the bar edge, so the blur needs to grow 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 _dxClamp: (contentContainer.barLeft || contentContainer.barRight) ? Math.max(-contentContainer.width, Math.min(contentContainer.animX, contentContainer.width)) : 0
|
||||
|
||||
@@ -781,17 +782,7 @@ Item {
|
||||
WlrLayershell.namespace: root.layerNamespace
|
||||
WlrLayershell.layer: root.effectivePopoutLayer
|
||||
WlrLayershell.exclusiveZone: -1
|
||||
WlrLayershell.keyboardFocus: {
|
||||
if (PopoutManager.screenshotActive)
|
||||
return WlrKeyboardFocus.None;
|
||||
if (customKeyboardFocus !== null)
|
||||
return customKeyboardFocus;
|
||||
if (!shouldBeVisible)
|
||||
return WlrKeyboardFocus.None;
|
||||
if (CompositorService.useHyprlandFocusGrab)
|
||||
return WlrKeyboardFocus.OnDemand;
|
||||
return WlrKeyboardFocus.Exclusive;
|
||||
}
|
||||
WlrLayershell.keyboardFocus: KeyboardFocus.keyboardFocus(shouldBeVisible, customKeyboardFocus)
|
||||
|
||||
readonly property bool _fullHeight: root.fullHeightSurface
|
||||
anchors {
|
||||
@@ -813,7 +804,6 @@ Item {
|
||||
|
||||
Region {
|
||||
id: contentInputMask
|
||||
// Use bar-aware mask so bar widget clicks pass through when a popout is open.
|
||||
item: (shouldBeVisible && backgroundInteractive) ? backgroundDismissalMask : contentMaskRect
|
||||
}
|
||||
|
||||
@@ -924,7 +914,6 @@ Item {
|
||||
|
||||
readonly property real computedScaleCollapsed: root.animationScaleCollapsed
|
||||
|
||||
// openProgress: 0 = closed (at offset, scaleCollapsed), 1 = open (at 0, scale 1).
|
||||
QtObject {
|
||||
id: morph
|
||||
property real openProgress: 0
|
||||
@@ -971,7 +960,6 @@ Item {
|
||||
|
||||
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
|
||||
y: shouldClip ? (contentContainer.barTop ? -connectedClipAllowance : -clipOversize) : 0
|
||||
|
||||
@@ -1008,22 +996,13 @@ Item {
|
||||
|
||||
ElevationShadow {
|
||||
id: shadowSource
|
||||
readonly property real connectorExtent: root.usesConnectedSurfaceChrome ? Theme.connectedCornerRadius : 0
|
||||
readonly property real extraLeft: root.usesConnectedSurfaceChrome && (contentContainer.barTop || contentContainer.barBottom) ? connectorExtent : 0
|
||||
readonly property real extraRight: root.usesConnectedSurfaceChrome && (contentContainer.barTop || contentContainer.barBottom) ? connectorExtent : 0
|
||||
readonly property real extraTop: root.usesConnectedSurfaceChrome && (contentContainer.barLeft || contentContainer.barRight) ? connectorExtent : 0
|
||||
readonly property real extraBottom: root.usesConnectedSurfaceChrome && (contentContainer.barLeft || contentContainer.barRight) ? connectorExtent : 0
|
||||
readonly property real bodyX: extraLeft
|
||||
readonly property real bodyY: extraTop
|
||||
readonly property real bodyWidth: rollOutAdjuster.baseWidth
|
||||
readonly property real bodyHeight: rollOutAdjuster.baseHeight
|
||||
|
||||
width: rollOutAdjuster.baseWidth + extraLeft + extraRight
|
||||
height: rollOutAdjuster.baseHeight + extraTop + extraBottom
|
||||
visible: !root.usesConnectedSurfaceChrome
|
||||
width: rollOutAdjuster.baseWidth
|
||||
height: rollOutAdjuster.baseHeight
|
||||
opacity: contentWrapper.publishedOpacity
|
||||
scale: contentWrapper.scale
|
||||
x: contentWrapper.x - extraLeft
|
||||
y: contentWrapper.y - extraTop
|
||||
x: contentWrapper.x
|
||||
y: contentWrapper.y
|
||||
level: root.shadowLevel
|
||||
direction: root.effectiveShadowDirection
|
||||
fallbackOffset: root.shadowFallbackOffset
|
||||
@@ -1035,49 +1014,49 @@ Item {
|
||||
targetColor: contentContainer.surfaceColor
|
||||
borderColor: contentContainer.surfaceBorderColor
|
||||
borderWidth: contentContainer.surfaceBorderWidth
|
||||
useCustomSource: root.usesConnectedSurfaceChrome
|
||||
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive) && !root.frameOwnsConnectedChrome
|
||||
}
|
||||
|
||||
Item {
|
||||
Item {
|
||||
id: localChrome
|
||||
visible: root.usesLocalConnectedSurfaceChrome
|
||||
|
||||
readonly property real extraLeft: (contentContainer.barTop || contentContainer.barBottom) ? Theme.connectedCornerRadius : 0
|
||||
readonly property real extraTop: (contentContainer.barLeft || contentContainer.barRight) ? Theme.connectedCornerRadius : 0
|
||||
|
||||
readonly property bool shadowsOn: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive)
|
||||
readonly property real shadowBlurPx: root.shadowLevel && root.shadowLevel.blurPx !== undefined ? root.shadowLevel.blurPx : 0
|
||||
readonly property real shadowSpreadPx: root.shadowLevel && root.shadowLevel.spreadPx !== undefined ? root.shadowLevel.spreadPx : 0
|
||||
readonly property real shadowOffsetX: Theme.elevationOffsetXFor(root.shadowLevel, root.effectiveShadowDirection, root.shadowFallbackOffset)
|
||||
readonly property real shadowOffsetY: Theme.elevationOffsetYFor(root.shadowLevel, root.effectiveShadowDirection, root.shadowFallbackOffset)
|
||||
readonly property color shadowTint: Theme.elevationShadowColor(root.shadowLevel)
|
||||
readonly property var ambient: Theme.elevationAmbient(root.shadowLevel)
|
||||
readonly property real pad: shadowsOn ? Math.ceil(Math.max(shadowBlurPx + shadowSpreadPx + Math.max(Math.abs(shadowOffsetX), Math.abs(shadowOffsetY)), ambient.blurPx + ambient.spreadPx) + 2) : 0
|
||||
|
||||
width: rollOutAdjuster.baseWidth + extraLeft * 2
|
||||
height: rollOutAdjuster.baseHeight + extraTop * 2
|
||||
opacity: contentWrapper.publishedOpacity
|
||||
scale: contentWrapper.scale
|
||||
x: contentWrapper.x - extraLeft
|
||||
y: contentWrapper.y - extraTop
|
||||
|
||||
ShaderEffect {
|
||||
anchors.fill: parent
|
||||
visible: root.usesLocalConnectedSurfaceChrome
|
||||
clip: false
|
||||
anchors.topMargin: contentContainer.barTop ? 0 : -localChrome.pad
|
||||
anchors.bottomMargin: contentContainer.barBottom ? 0 : -localChrome.pad
|
||||
anchors.leftMargin: contentContainer.barLeft ? 0 : -localChrome.pad
|
||||
anchors.rightMargin: contentContainer.barRight ? 0 : -localChrome.pad
|
||||
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/connected_chrome.frag.qsb")
|
||||
|
||||
Rectangle {
|
||||
x: shadowSource.bodyX
|
||||
y: shadowSource.bodyY
|
||||
width: shadowSource.bodyWidth
|
||||
height: shadowSource.bodyHeight
|
||||
topLeftRadius: contentContainer.surfaceTopLeftRadius
|
||||
topRightRadius: contentContainer.surfaceTopRightRadius
|
||||
bottomLeftRadius: contentContainer.surfaceBottomLeftRadius
|
||||
bottomRightRadius: contentContainer.surfaceBottomRightRadius
|
||||
color: contentContainer.surfaceColor
|
||||
}
|
||||
|
||||
ConnectedCorner {
|
||||
visible: root.usesConnectedSurfaceChrome
|
||||
barSide: contentContainer.connectedBarSide
|
||||
placement: "left"
|
||||
spacing: 0
|
||||
connectorRadius: Theme.connectedCornerRadius
|
||||
color: contentContainer.surfaceColor
|
||||
dpr: root.dpr
|
||||
x: Theme.snap(ConnectorGeometry.connectorX(contentContainer.connectedBarSide, shadowSource.bodyX, shadowSource.bodyWidth, placement, spacing, Theme.connectedCornerRadius), root.dpr)
|
||||
y: Theme.snap(ConnectorGeometry.connectorY(contentContainer.connectedBarSide, shadowSource.bodyY, shadowSource.bodyHeight, placement, spacing, Theme.connectedCornerRadius), root.dpr)
|
||||
}
|
||||
|
||||
ConnectedCorner {
|
||||
visible: root.usesConnectedSurfaceChrome
|
||||
barSide: contentContainer.connectedBarSide
|
||||
placement: "right"
|
||||
spacing: 0
|
||||
connectorRadius: Theme.connectedCornerRadius
|
||||
color: contentContainer.surfaceColor
|
||||
dpr: root.dpr
|
||||
x: Theme.snap(ConnectorGeometry.connectorX(contentContainer.connectedBarSide, shadowSource.bodyX, shadowSource.bodyWidth, placement, spacing, Theme.connectedCornerRadius), root.dpr)
|
||||
y: Theme.snap(ConnectorGeometry.connectorY(contentContainer.connectedBarSide, shadowSource.bodyY, shadowSource.bodyHeight, placement, spacing, Theme.connectedCornerRadius), root.dpr)
|
||||
}
|
||||
property real widthPx: width
|
||||
property real heightPx: height
|
||||
property vector4d surfaceColor: Qt.vector4d(contentContainer.surfaceColor.r, contentContainer.surfaceColor.g, contentContainer.surfaceColor.b, contentContainer.surfaceColor.a)
|
||||
property vector4d shadowColor: Qt.vector4d(localChrome.shadowTint.r, localChrome.shadowTint.g, localChrome.shadowTint.b, localChrome.shadowsOn ? localChrome.shadowTint.a : 0)
|
||||
property vector4d shadowParam: Qt.vector4d(Math.max(0, localChrome.shadowBlurPx), localChrome.shadowSpreadPx, localChrome.shadowOffsetX, localChrome.shadowOffsetY)
|
||||
property vector4d ambientParam: Qt.vector4d(localChrome.ambient.blurPx, localChrome.ambient.spreadPx, localChrome.shadowsOn ? localChrome.ambient.alpha : 0, 0)
|
||||
property vector4d bodyRect: Qt.vector4d((contentContainer.barLeft ? 0 : localChrome.pad) + localChrome.extraLeft, (contentContainer.barTop ? 0 : localChrome.pad) + localChrome.extraTop, rollOutAdjuster.baseWidth, rollOutAdjuster.baseHeight)
|
||||
property vector4d cornerRadius: Qt.vector4d(contentContainer.surfaceTopLeftRadius, contentContainer.surfaceTopRightRadius, contentContainer.surfaceBottomRightRadius, contentContainer.surfaceBottomLeftRadius)
|
||||
property vector4d edgeParam: Qt.vector4d(contentContainer.barTop ? 0 : (contentContainer.barBottom ? 1 : (contentContainer.barLeft ? 2 : 3)), Theme.connectedCornerRadius, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1135,8 +1114,6 @@ Item {
|
||||
Connections {
|
||||
target: contentWindow
|
||||
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)
|
||||
contentWrapper._renderActive = false;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ Item {
|
||||
property Component overlayContent: null
|
||||
property alias overlayLoader: overlayLoader
|
||||
readonly property alias backgroundWindow: backgroundWindow
|
||||
readonly property alias contentWindow: contentWindow
|
||||
property real popupWidth: 400
|
||||
property real popupHeight: 300
|
||||
property real triggerX: 0
|
||||
@@ -494,31 +495,21 @@ Item {
|
||||
}
|
||||
})(), dpr)
|
||||
|
||||
readonly property real triggeringBarLeftExclusion: (effectiveBarPosition === SettingsData.Position.Left && barWidth > 0) ? Math.max(0, barX + barWidth) : 0
|
||||
readonly property real triggeringBarTopExclusion: (effectiveBarPosition === SettingsData.Position.Top && barHeight > 0) ? Math.max(0, barY + barHeight) : 0
|
||||
readonly property real triggeringBarRightExclusion: (effectiveBarPosition === SettingsData.Position.Right && barWidth > 0) ? Math.max(0, screenWidth - barX) : 0
|
||||
readonly property real triggeringBarBottomExclusion: (effectiveBarPosition === SettingsData.Position.Bottom && barHeight > 0) ? Math.max(0, screenHeight - barY) : 0
|
||||
readonly property real maskX: _dismissZone.x
|
||||
readonly property real maskY: _dismissZone.y
|
||||
readonly property real maskWidth: _dismissZone.width
|
||||
readonly property real maskHeight: _dismissZone.height
|
||||
|
||||
readonly property real maskX: {
|
||||
const adjacentLeftBar = adjacentBarInfo?.leftBar ?? 0;
|
||||
return Math.max(triggeringBarLeftExclusion, adjacentLeftBar);
|
||||
}
|
||||
|
||||
readonly property real maskY: {
|
||||
const adjacentTopBar = adjacentBarInfo?.topBar ?? 0;
|
||||
return Math.max(triggeringBarTopExclusion, adjacentTopBar);
|
||||
}
|
||||
|
||||
readonly property real maskWidth: {
|
||||
const adjacentRightBar = adjacentBarInfo?.rightBar ?? 0;
|
||||
const rightExclusion = Math.max(triggeringBarRightExclusion, adjacentRightBar);
|
||||
return Math.max(100, screenWidth - maskX - rightExclusion);
|
||||
}
|
||||
|
||||
readonly property real maskHeight: {
|
||||
const adjacentBottomBar = adjacentBarInfo?.bottomBar ?? 0;
|
||||
const bottomExclusion = Math.max(triggeringBarBottomExclusion, adjacentBottomBar);
|
||||
return Math.max(100, screenHeight - maskY - bottomExclusion);
|
||||
DismissZone {
|
||||
id: _dismissZone
|
||||
barPosition: root.effectiveBarPosition
|
||||
barX: root.barX
|
||||
barY: root.barY
|
||||
barWidth: root.barWidth
|
||||
barHeight: root.barHeight
|
||||
screenWidth: root.screenWidth
|
||||
screenHeight: root.screenHeight
|
||||
adjacentBarInfo: root.adjacentBarInfo
|
||||
}
|
||||
|
||||
PanelWindow {
|
||||
@@ -598,31 +589,25 @@ Item {
|
||||
id: popoutBlur
|
||||
targetWindow: contentWindow
|
||||
readonly property real s: Math.min(1, contentContainer.scaleValue)
|
||||
readonly property bool trackBlurFromBarEdge: root.fluidStandaloneActive
|
||||
readonly property real op: Math.max(0, Math.min(1, (morph.openProgress - 0.08) * 1.6))
|
||||
readonly property bool blurAlive: trackBlurFromBarEdge ? (contentContainer.revealWidth > 0 && contentContainer.revealHeight > 0) : root.shouldBeVisible
|
||||
readonly property bool revealClipActive: root.fluidStandaloneActive
|
||||
|
||||
blurX: trackBlurFromBarEdge ? contentContainer.x + contentContainer.revealX : contentContainer.x + contentContainer.width * (1 - s * op) * 0.5 + Theme.snap(contentContainer.animX, root.dpr)
|
||||
blurY: trackBlurFromBarEdge ? contentContainer.y + contentContainer.revealY : contentContainer.y + contentContainer.height * (1 - s * op) * 0.5 + Theme.snap(contentContainer.animY, root.dpr)
|
||||
blurWidth: blurAlive ? (trackBlurFromBarEdge ? contentContainer.revealWidth : contentContainer.width * s * op) : 0
|
||||
blurHeight: blurAlive ? (trackBlurFromBarEdge ? contentContainer.revealHeight : contentContainer.height * s * op) : 0
|
||||
blurX: revealClipActive ? contentContainer.x : contentContainer.x + contentContainer.width * (1 - s * op) * 0.5 + Theme.snap(contentContainer.animX, root.dpr)
|
||||
blurY: revealClipActive ? contentContainer.y : contentContainer.y + contentContainer.height * (1 - s * op) * 0.5 + Theme.snap(contentContainer.animY, root.dpr)
|
||||
blurWidth: root.shouldBeVisible ? (revealClipActive ? contentContainer.width : contentContainer.width * s * op) : 0
|
||||
blurHeight: root.shouldBeVisible ? (revealClipActive ? contentContainer.height : contentContainer.height * s * op) : 0
|
||||
blurRadius: Theme.cornerRadius
|
||||
clipEnabled: revealClipActive
|
||||
clipX: contentContainer.x + contentContainer.revealX
|
||||
clipY: contentContainer.y + contentContainer.revealY
|
||||
clipWidth: root.shouldBeVisible ? contentContainer.revealWidth : 0
|
||||
clipHeight: root.shouldBeVisible ? contentContainer.revealHeight : 0
|
||||
}
|
||||
|
||||
WlrLayershell.namespace: root.layerNamespace
|
||||
WlrLayershell.layer: root.effectivePopoutLayer
|
||||
WlrLayershell.exclusiveZone: -1
|
||||
WlrLayershell.keyboardFocus: {
|
||||
if (PopoutManager.screenshotActive)
|
||||
return WlrKeyboardFocus.None;
|
||||
if (customKeyboardFocus !== null)
|
||||
return customKeyboardFocus;
|
||||
if (!shouldBeVisible)
|
||||
return WlrKeyboardFocus.None;
|
||||
if (CompositorService.useHyprlandFocusGrab)
|
||||
return WlrKeyboardFocus.OnDemand;
|
||||
return WlrKeyboardFocus.Exclusive;
|
||||
}
|
||||
WlrLayershell.keyboardFocus: KeyboardFocus.keyboardFocus(shouldBeVisible, customKeyboardFocus)
|
||||
|
||||
anchors {
|
||||
left: true
|
||||
@@ -721,6 +706,8 @@ Item {
|
||||
QtObject {
|
||||
id: morph
|
||||
property real openProgress: 0
|
||||
onOpenProgressChanged: if (root.fluidStandaloneActive)
|
||||
root._kickBlurCommit()
|
||||
Behavior on openProgress {
|
||||
enabled: root.animationsEnabled
|
||||
NumberAnimation {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
property int barPosition: 0
|
||||
property real barX: 0
|
||||
property real barY: 0
|
||||
property real barWidth: 0
|
||||
property real barHeight: 0
|
||||
property real screenWidth: 0
|
||||
property real screenHeight: 0
|
||||
property var adjacentBarInfo: null
|
||||
|
||||
readonly property real _leftExclusion: (barPosition === SettingsData.Position.Left && barWidth > 0) ? Math.max(0, barX + barWidth) : 0
|
||||
readonly property real _topExclusion: (barPosition === SettingsData.Position.Top && barHeight > 0) ? Math.max(0, barY + barHeight) : 0
|
||||
readonly property real _rightExclusion: (barPosition === SettingsData.Position.Right && barWidth > 0) ? Math.max(0, screenWidth - barX) : 0
|
||||
readonly property real _bottomExclusion: (barPosition === SettingsData.Position.Bottom && barHeight > 0) ? Math.max(0, screenHeight - barY) : 0
|
||||
|
||||
readonly property real x: Math.max(_leftExclusion, adjacentBarInfo?.leftBar ?? 0)
|
||||
readonly property real y: Math.max(_topExclusion, adjacentBarInfo?.topBar ?? 0)
|
||||
readonly property real width: Math.max(100, screenWidth - x - Math.max(_rightExclusion, adjacentBarInfo?.rightBar ?? 0))
|
||||
readonly property real height: Math.max(100, screenHeight - y - Math.max(_bottomExclusion, adjacentBarInfo?.bottomBar ?? 0))
|
||||
}
|
||||
@@ -16,6 +16,11 @@ Item {
|
||||
property real blurWidth: 0
|
||||
property real blurHeight: 0
|
||||
property real blurRadius: 0
|
||||
property bool clipEnabled: false
|
||||
property real clipX: blurX
|
||||
property real clipY: blurY
|
||||
property real clipWidth: blurWidth
|
||||
property real clipHeight: blurHeight
|
||||
|
||||
readonly property bool _active: blurEnabled && BlurService.enabled && !!targetWindow
|
||||
|
||||
@@ -26,6 +31,14 @@ Item {
|
||||
width: root.blurWidth
|
||||
height: root.blurHeight
|
||||
radius: root.blurRadius
|
||||
|
||||
Region {
|
||||
intersection: Intersection.Intersect
|
||||
x: root.clipEnabled ? root.clipX : root.blurX
|
||||
y: root.clipEnabled ? root.clipY : root.blurY
|
||||
width: root.clipEnabled ? root.clipWidth : root.blurWidth
|
||||
height: root.clipEnabled ? root.clipHeight : root.blurHeight
|
||||
}
|
||||
}
|
||||
|
||||
function _apply() {
|
||||
@@ -39,10 +52,7 @@ Item {
|
||||
targetWindow.BackgroundEffect.blurRegion = null;
|
||||
}
|
||||
|
||||
// Force BackgroundEffect to re-publish the blur region on the current wl_surface.
|
||||
// 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).
|
||||
// Re-publish blur region after wl_surface remaps (e.g. screen change).
|
||||
function kick() {
|
||||
if (!targetWindow)
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user