mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-13 01:02:18 -04:00
(frame): Update connected mode animation & motion logic
This commit is contained in:
@@ -3,6 +3,10 @@ import QtQuick.Shapes
|
||||
import qs.Common
|
||||
|
||||
// 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
|
||||
|
||||
286
quickshell/Widgets/ConnectedShape.qml
Normal file
286
quickshell/Widgets/ConnectedShape.qml
Normal file
@@ -0,0 +1,286 @@
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
import qs.Common
|
||||
|
||||
// Unified connected silhouette: body + concave arcs as one ShapePath.
|
||||
// PathArc pattern — 4 arcs + 4 lines, no sibling alignment.
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string barSide: "top"
|
||||
|
||||
property real bodyWidth: 0
|
||||
property real bodyHeight: 0
|
||||
|
||||
property real connectorRadius: 12
|
||||
|
||||
property real surfaceRadius: 12
|
||||
|
||||
property color fillColor: "transparent"
|
||||
|
||||
// ── Derived layout ──
|
||||
readonly property bool _horiz: barSide === "top" || barSide === "bottom"
|
||||
readonly property real _cr: Math.max(0, connectorRadius)
|
||||
readonly property real _sr: Math.max(0, Math.min(surfaceRadius, (_horiz ? bodyWidth : bodyHeight) / 2, (_horiz ? bodyHeight : bodyWidth) / 2))
|
||||
|
||||
// Root-level aliases — PathArc/PathLine elements can't use `parent`.
|
||||
readonly property real _bw: bodyWidth
|
||||
readonly property real _bh: bodyHeight
|
||||
readonly property real _totalW: _horiz ? _bw + _cr * 2 : _bw
|
||||
readonly property real _totalH: _horiz ? _bh : _bh + _cr * 2
|
||||
|
||||
width: _totalW
|
||||
height: _totalH
|
||||
|
||||
readonly property real bodyX: _horiz ? _cr : 0
|
||||
readonly property real bodyY: _horiz ? 0 : _cr
|
||||
|
||||
Shape {
|
||||
anchors.fill: parent
|
||||
asynchronous: false
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
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._cr;
|
||||
case "right":
|
||||
return -root._cr;
|
||||
default:
|
||||
return -root._cr;
|
||||
}
|
||||
}
|
||||
relativeY: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return -root._cr;
|
||||
case "left":
|
||||
return root._cr;
|
||||
case "right":
|
||||
return -root._cr;
|
||||
default:
|
||||
return root._cr;
|
||||
}
|
||||
}
|
||||
radiusX: root._cr
|
||||
radiusY: root._cr
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
|
||||
// Body edge to first convex corner
|
||||
PathLine {
|
||||
x: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return root._bw - root._sr;
|
||||
case "right":
|
||||
return root._sr;
|
||||
default:
|
||||
return root._totalW - root._cr;
|
||||
}
|
||||
}
|
||||
y: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._sr;
|
||||
case "left":
|
||||
return root._cr;
|
||||
case "right":
|
||||
return root._cr + root._bh;
|
||||
default:
|
||||
return root._totalH - root._sr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convex arc 1
|
||||
PathArc {
|
||||
relativeX: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return root._sr;
|
||||
case "right":
|
||||
return -root._sr;
|
||||
default:
|
||||
return -root._sr;
|
||||
}
|
||||
}
|
||||
relativeY: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return -root._sr;
|
||||
case "left":
|
||||
return root._sr;
|
||||
case "right":
|
||||
return -root._sr;
|
||||
default:
|
||||
return root._sr;
|
||||
}
|
||||
}
|
||||
radiusX: root._sr
|
||||
radiusY: root._sr
|
||||
direction: PathArc.Clockwise
|
||||
}
|
||||
|
||||
// Far edge
|
||||
PathLine {
|
||||
x: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return root._bw;
|
||||
case "right":
|
||||
return 0;
|
||||
default:
|
||||
return root._cr + root._sr;
|
||||
}
|
||||
}
|
||||
y: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return 0;
|
||||
case "left":
|
||||
return root._cr + root._bh - root._sr;
|
||||
case "right":
|
||||
return root._cr + root._sr;
|
||||
default:
|
||||
return root._totalH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convex arc 2
|
||||
PathArc {
|
||||
relativeX: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return -root._sr;
|
||||
case "right":
|
||||
return root._sr;
|
||||
default:
|
||||
return -root._sr;
|
||||
}
|
||||
}
|
||||
relativeY: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._sr;
|
||||
case "left":
|
||||
return root._sr;
|
||||
case "right":
|
||||
return -root._sr;
|
||||
default:
|
||||
return -root._sr;
|
||||
}
|
||||
}
|
||||
radiusX: root._sr
|
||||
radiusY: root._sr
|
||||
direction: PathArc.Clockwise
|
||||
}
|
||||
|
||||
// Body edge to second concave arc
|
||||
PathLine {
|
||||
x: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return root._cr;
|
||||
case "right":
|
||||
return root._bw - root._cr;
|
||||
default:
|
||||
return root._cr;
|
||||
}
|
||||
}
|
||||
y: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._totalH - root._cr;
|
||||
case "left":
|
||||
return root._cr + root._bh;
|
||||
case "right":
|
||||
return root._cr;
|
||||
default:
|
||||
return root._cr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Concave arc 2
|
||||
PathArc {
|
||||
relativeX: {
|
||||
switch (root.barSide) {
|
||||
case "left":
|
||||
return -root._cr;
|
||||
case "right":
|
||||
return root._cr;
|
||||
default:
|
||||
return -root._cr;
|
||||
}
|
||||
}
|
||||
relativeY: {
|
||||
switch (root.barSide) {
|
||||
case "bottom":
|
||||
return root._cr;
|
||||
case "left":
|
||||
return root._cr;
|
||||
case "right":
|
||||
return -root._cr;
|
||||
default:
|
||||
return -root._cr;
|
||||
}
|
||||
}
|
||||
radiusX: root._cr
|
||||
radiusY: root._cr
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ Item {
|
||||
property bool _resizeActive: false
|
||||
property real _surfaceMarginLeft: 0
|
||||
property real _surfaceW: 0
|
||||
property string _connectedChromeToken: ""
|
||||
property string _chromeClaimId: ""
|
||||
property int _connectedChromeSerial: 0
|
||||
|
||||
property real storedBarThickness: Theme.barHeight - 4
|
||||
@@ -153,7 +153,7 @@ Item {
|
||||
setBarContext(pos, bottomGap);
|
||||
}
|
||||
|
||||
function _nextConnectedChromeToken() {
|
||||
function _nextChromeClaimId() {
|
||||
_connectedChromeSerial += 1;
|
||||
return layerNamespace + ":" + _connectedChromeSerial + ":" + (new Date()).getTime();
|
||||
}
|
||||
@@ -174,21 +174,21 @@ Item {
|
||||
}
|
||||
|
||||
function _publishConnectedChromeState(forceClaim, visibleOverride) {
|
||||
if (!SettingsData.connectedFrameModeActive || !root.screen || !_connectedChromeToken)
|
||||
if (!root.frameOwnsConnectedChrome || !root.screen || !_chromeClaimId)
|
||||
return;
|
||||
|
||||
const state = _connectedChromeState(visibleOverride);
|
||||
if (forceClaim || !ConnectedModeState.hasPopoutOwner(_connectedChromeToken)) {
|
||||
ConnectedModeState.claimPopout(_connectedChromeToken, state);
|
||||
if (forceClaim || !ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
|
||||
ConnectedModeState.claimPopout(_chromeClaimId, state);
|
||||
} else {
|
||||
ConnectedModeState.updatePopout(_connectedChromeToken, state);
|
||||
ConnectedModeState.updatePopout(_chromeClaimId, state);
|
||||
}
|
||||
}
|
||||
|
||||
function _releaseConnectedChromeState() {
|
||||
if (_connectedChromeToken)
|
||||
ConnectedModeState.releasePopout(_connectedChromeToken);
|
||||
_connectedChromeToken = "";
|
||||
if (_chromeClaimId)
|
||||
ConnectedModeState.releasePopout(_chromeClaimId);
|
||||
_chromeClaimId = "";
|
||||
}
|
||||
|
||||
// ─── Exposed animation state for ConnectedModeState ────────────────────
|
||||
@@ -197,7 +197,7 @@ Item {
|
||||
|
||||
// ─── ConnectedModeState sync ────────────────────────────────────────────
|
||||
function _syncPopoutChromeState() {
|
||||
if (!SettingsData.connectedFrameModeActive) {
|
||||
if (!root.frameOwnsConnectedChrome) {
|
||||
_releaseConnectedChromeState();
|
||||
return;
|
||||
}
|
||||
@@ -207,9 +207,9 @@ Item {
|
||||
}
|
||||
if (!contentWindow.visible && !shouldBeVisible)
|
||||
return;
|
||||
if (!_connectedChromeToken)
|
||||
_connectedChromeToken = _nextConnectedChromeToken();
|
||||
_publishConnectedChromeState(contentWindow.visible && !ConnectedModeState.hasPopoutOwner(_connectedChromeToken));
|
||||
if (!_chromeClaimId)
|
||||
_chromeClaimId = _nextChromeClaimId();
|
||||
_publishConnectedChromeState(contentWindow.visible && !ConnectedModeState.hasPopoutOwner(_chromeClaimId));
|
||||
}
|
||||
|
||||
onAlignedXChanged: _syncPopoutChromeState()
|
||||
@@ -233,10 +233,10 @@ Item {
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onConnectedFrameModeActiveChanged() {
|
||||
if (SettingsData.connectedFrameModeActive) {
|
||||
if (root.frameOwnsConnectedChrome) {
|
||||
if (contentWindow.visible || root.shouldBeVisible) {
|
||||
if (!root._connectedChromeToken)
|
||||
root._connectedChromeToken = root._nextConnectedChromeToken();
|
||||
if (!root._chromeClaimId)
|
||||
root._chromeClaimId = root._nextChromeClaimId();
|
||||
root._publishConnectedChromeState(true);
|
||||
}
|
||||
} else {
|
||||
@@ -246,6 +246,9 @@ Item {
|
||||
}
|
||||
|
||||
readonly property bool useBackgroundWindow: !CompositorService.isHyprland || CompositorService.useHyprlandFocusGrab
|
||||
readonly property bool frameOwnsConnectedChrome: SettingsData.connectedFrameModeActive
|
||||
&& !!root.screen
|
||||
&& SettingsData.isScreenInPreferences(root.screen, SettingsData.frameScreenPreferences)
|
||||
|
||||
function updateSurfacePosition() {
|
||||
if (useBackgroundWindow && shouldBeVisible) {
|
||||
@@ -282,11 +285,11 @@ Item {
|
||||
contentContainer.scaleValue = root.animationScaleCollapsed;
|
||||
}
|
||||
|
||||
if (SettingsData.connectedFrameModeActive) {
|
||||
_connectedChromeToken = _nextConnectedChromeToken();
|
||||
if (root.frameOwnsConnectedChrome) {
|
||||
_chromeClaimId = _nextChromeClaimId();
|
||||
_publishConnectedChromeState(true, true);
|
||||
} else {
|
||||
_connectedChromeToken = "";
|
||||
_chromeClaimId = "";
|
||||
}
|
||||
|
||||
if (useBackgroundWindow) {
|
||||
@@ -641,7 +644,7 @@ Item {
|
||||
WindowBlur {
|
||||
id: popoutBlur
|
||||
targetWindow: contentWindow
|
||||
blurEnabled: root.effectiveSurfaceBlurEnabled && !SettingsData.connectedFrameModeActive
|
||||
blurEnabled: root.effectiveSurfaceBlurEnabled && !root.frameOwnsConnectedChrome
|
||||
|
||||
readonly property real s: Math.min(1, contentContainer.scaleValue)
|
||||
readonly property bool trackBlurFromBarEdge: Theme.isConnectedEffect || (typeof SettingsData !== "undefined" && Theme.isDirectionalEffect && SettingsData.directionalAnimationMode !== 2)
|
||||
@@ -984,7 +987,7 @@ Item {
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
visible: Theme.isConnectedEffect && !SettingsData.connectedFrameModeActive
|
||||
visible: Theme.isConnectedEffect && !root.frameOwnsConnectedChrome
|
||||
clip: false
|
||||
|
||||
Rectangle {
|
||||
|
||||
Reference in New Issue
Block a user