mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-02 02:22:06 -04:00
Compare commits
20 Commits
master
...
c11069d502
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c11069d502 | ||
|
|
59160ce5ac | ||
|
|
995995034b | ||
|
|
7568d5a4e1 | ||
|
|
cca0b7957d | ||
|
|
52c3ecd4f2 | ||
|
|
2a2817d443 | ||
|
|
2b3d227ad8 | ||
|
|
3b894bf72b | ||
|
|
7bef86533a | ||
|
|
dc61b1cc44 | ||
|
|
7ffdfc265c | ||
|
|
643468171c | ||
|
|
3d9af9557f | ||
|
|
20c90419d5 | ||
|
|
248ee8c485 | ||
|
|
06a3d28586 | ||
|
|
1672bde9e1 | ||
|
|
75112f0b7a | ||
|
|
dd43cce8fe |
@@ -1,26 +1,13 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: local
|
- repo: https://github.com/golangci/golangci-lint
|
||||||
|
rev: v2.10.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: golangci-lint-fmt
|
- id: golangci-lint-fmt
|
||||||
name: golangci-lint-fmt
|
|
||||||
entry: go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.3 fmt
|
|
||||||
language: system
|
|
||||||
require_serial: true
|
require_serial: true
|
||||||
types: [go]
|
|
||||||
pass_filenames: false
|
|
||||||
- id: golangci-lint-full
|
- id: golangci-lint-full
|
||||||
name: golangci-lint-full
|
|
||||||
entry: go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.3 run --fix
|
|
||||||
language: system
|
|
||||||
require_serial: true
|
|
||||||
types: [go]
|
|
||||||
pass_filenames: false
|
|
||||||
- id: golangci-lint-config-verify
|
- id: golangci-lint-config-verify
|
||||||
name: golangci-lint-config-verify
|
- repo: local
|
||||||
entry: go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.3 config verify
|
hooks:
|
||||||
language: system
|
|
||||||
files: \.golangci\.(?:yml|yaml|toml|json)
|
|
||||||
pass_filenames: false
|
|
||||||
- id: go-test
|
- id: go-test
|
||||||
name: go test
|
name: go test
|
||||||
entry: go test ./...
|
entry: go test ./...
|
||||||
|
|||||||
179
quickshell/Common/AnimVariants.qml
Normal file
179
quickshell/Common/AnimVariants.qml
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
|
// AnimVariants — Central tuning for animation and Motion Effects variants
|
||||||
|
// (Material/Fluent/Dynamic) (Standard/Directional/Depth)
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property list<real> variantEnterCurve: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return Anims.expressiveDefaultSpatial;
|
||||||
|
switch (SettingsData.animationVariant) {
|
||||||
|
case 1:
|
||||||
|
return Anims.standardDecel;
|
||||||
|
case 2:
|
||||||
|
return Anims.expressiveFastSpatial;
|
||||||
|
default:
|
||||||
|
return Anims.expressiveDefaultSpatial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property list<real> variantExitCurve: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return Anims.emphasized;
|
||||||
|
switch (SettingsData.animationVariant) {
|
||||||
|
case 1:
|
||||||
|
return Anims.standard;
|
||||||
|
case 2:
|
||||||
|
return Anims.emphasized;
|
||||||
|
default:
|
||||||
|
return Anims.emphasized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal-specific entry curve
|
||||||
|
readonly property list<real> variantModalEnterCurve: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return Anims.expressiveDefaultSpatial;
|
||||||
|
if (isDirectionalEffect) {
|
||||||
|
if (SettingsData.animationVariant === 1)
|
||||||
|
return Anims.standardDecel;
|
||||||
|
if (SettingsData.animationVariant === 2)
|
||||||
|
return Anims.expressiveFastSpatial;
|
||||||
|
}
|
||||||
|
return variantEnterCurve;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property list<real> variantModalExitCurve: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return Anims.emphasized;
|
||||||
|
if (isDirectionalEffect) {
|
||||||
|
if (SettingsData.animationVariant === 1)
|
||||||
|
return Anims.emphasizedAccel;
|
||||||
|
if (SettingsData.animationVariant === 2)
|
||||||
|
return Anims.emphasizedAccel;
|
||||||
|
}
|
||||||
|
return variantExitCurve;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Popout-specific entry curve
|
||||||
|
readonly property list<real> variantPopoutEnterCurve: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return Anims.expressiveDefaultSpatial;
|
||||||
|
if (isDirectionalEffect) {
|
||||||
|
if (SettingsData.animationVariant === 1)
|
||||||
|
return Anims.standardDecel;
|
||||||
|
if (SettingsData.animationVariant === 2)
|
||||||
|
return Anims.expressiveFastSpatial;
|
||||||
|
return Anims.standardDecel;
|
||||||
|
}
|
||||||
|
return variantEnterCurve;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property list<real> variantPopoutExitCurve: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return Anims.emphasized;
|
||||||
|
if (isDirectionalEffect) {
|
||||||
|
if (SettingsData.animationVariant === 1)
|
||||||
|
return Anims.emphasizedAccel;
|
||||||
|
if (SettingsData.animationVariant === 2)
|
||||||
|
return Anims.emphasizedAccel;
|
||||||
|
}
|
||||||
|
return variantExitCurve;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property real variantEnterDurationFactor: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return 1.0;
|
||||||
|
switch (SettingsData.animationVariant) {
|
||||||
|
case 1:
|
||||||
|
return 0.9;
|
||||||
|
case 2:
|
||||||
|
return 1.08;
|
||||||
|
default:
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property real variantExitDurationFactor: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return 1.0;
|
||||||
|
switch (SettingsData.animationVariant) {
|
||||||
|
case 1:
|
||||||
|
return 0.85;
|
||||||
|
case 2:
|
||||||
|
return 0.92;
|
||||||
|
default:
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fluent: opacity at ~55% of duration; Material/Dynamic: 1:1 with position
|
||||||
|
readonly property real variantOpacityDurationScale: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return 1.0;
|
||||||
|
return SettingsData.animationVariant === 1 ? 0.55 : 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function variantDuration(baseDuration, entering) {
|
||||||
|
const factor = entering ? variantEnterDurationFactor : variantExitDurationFactor;
|
||||||
|
return Math.max(0, Math.round(baseDuration * factor));
|
||||||
|
}
|
||||||
|
|
||||||
|
function variantExitCleanupPadding() {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return 50;
|
||||||
|
switch (SettingsData.motionEffect) {
|
||||||
|
case 1:
|
||||||
|
return 8;
|
||||||
|
case 2:
|
||||||
|
return 24;
|
||||||
|
default:
|
||||||
|
return 50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function variantCloseInterval(baseDuration) {
|
||||||
|
return variantDuration(baseDuration, false) + variantExitCleanupPadding();
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property bool isDirectionalEffect: isConnectedEffect
|
||||||
|
|| (typeof SettingsData !== "undefined" && SettingsData.motionEffect === 1)
|
||||||
|
readonly property bool isDepthEffect: typeof SettingsData !== "undefined" && SettingsData.motionEffect === 2
|
||||||
|
readonly property bool isConnectedEffect: typeof SettingsData !== "undefined"
|
||||||
|
&& SettingsData.frameEnabled
|
||||||
|
&& SettingsData.motionEffect === 1
|
||||||
|
&& SettingsData.directionalAnimationMode === 3
|
||||||
|
|
||||||
|
readonly property real effectScaleCollapsed: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return 0.96;
|
||||||
|
switch (SettingsData.motionEffect) {
|
||||||
|
case 1:
|
||||||
|
return 1.0;
|
||||||
|
case 2:
|
||||||
|
return 0.88;
|
||||||
|
default:
|
||||||
|
return 0.96;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property real effectAnimOffset: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return 16;
|
||||||
|
switch (SettingsData.motionEffect) {
|
||||||
|
case 1:
|
||||||
|
return 144;
|
||||||
|
case 2:
|
||||||
|
return 56;
|
||||||
|
default:
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,4 +22,9 @@ Singleton {
|
|||||||
readonly property var standard: [0.20, 0.00, 0.00, 1.00, 1.00, 1.00]
|
readonly property var standard: [0.20, 0.00, 0.00, 1.00, 1.00, 1.00]
|
||||||
readonly property var standardDecel: [0.00, 0.00, 0.00, 1.00, 1.00, 1.00]
|
readonly property var standardDecel: [0.00, 0.00, 0.00, 1.00, 1.00, 1.00]
|
||||||
readonly property var standardAccel: [0.30, 0.00, 1.00, 1.00, 1.00, 1.00]
|
readonly property var standardAccel: [0.30, 0.00, 1.00, 1.00, 1.00, 1.00]
|
||||||
|
|
||||||
|
// Used by AnimVariants for variant/effect logic
|
||||||
|
readonly property var expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1]
|
||||||
|
readonly property var expressiveFastSpatial: [0.34, 1.5, 0.2, 1.0, 1.0, 1.0]
|
||||||
|
readonly property var expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1]
|
||||||
}
|
}
|
||||||
|
|||||||
258
quickshell/Common/ConnectedModeState.qml
Normal file
258
quickshell/Common/ConnectedModeState.qml
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property var emptyDockState: ({
|
||||||
|
"reveal": false,
|
||||||
|
"barSide": "bottom",
|
||||||
|
"bodyX": 0,
|
||||||
|
"bodyY": 0,
|
||||||
|
"bodyW": 0,
|
||||||
|
"bodyH": 0,
|
||||||
|
"slideX": 0,
|
||||||
|
"slideY": 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// Popout state (updated by DankPopout when connectedFrameModeActive)
|
||||||
|
property string popoutOwnerId: ""
|
||||||
|
property bool popoutVisible: false
|
||||||
|
property string popoutBarSide: "top"
|
||||||
|
property real popoutBodyX: 0
|
||||||
|
property real popoutBodyY: 0
|
||||||
|
property real popoutBodyW: 0
|
||||||
|
property real popoutBodyH: 0
|
||||||
|
property real popoutAnimX: 0
|
||||||
|
property real popoutAnimY: 0
|
||||||
|
property string popoutScreen: ""
|
||||||
|
property bool popoutOmitStartConnector: false
|
||||||
|
property bool popoutOmitEndConnector: false
|
||||||
|
|
||||||
|
// Dock state (updated by Dock when connectedFrameModeActive), keyed by screen.name
|
||||||
|
property var dockStates: ({})
|
||||||
|
|
||||||
|
// Dock slide offsets — hot-path updates separated from full geometry state
|
||||||
|
property var dockSlides: ({})
|
||||||
|
|
||||||
|
function hasPopoutOwner(claimId) {
|
||||||
|
return !!claimId && popoutOwnerId === claimId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function claimPopout(claimId, state) {
|
||||||
|
if (!claimId)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
popoutOwnerId = claimId;
|
||||||
|
return updatePopout(claimId, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePopout(claimId, state) {
|
||||||
|
if (!hasPopoutOwner(claimId) || !state)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (state.visible !== undefined)
|
||||||
|
popoutVisible = !!state.visible;
|
||||||
|
if (state.barSide !== undefined)
|
||||||
|
popoutBarSide = state.barSide || "top";
|
||||||
|
if (state.bodyX !== undefined)
|
||||||
|
popoutBodyX = Number(state.bodyX);
|
||||||
|
if (state.bodyY !== undefined)
|
||||||
|
popoutBodyY = Number(state.bodyY);
|
||||||
|
if (state.bodyW !== undefined)
|
||||||
|
popoutBodyW = Number(state.bodyW);
|
||||||
|
if (state.bodyH !== undefined)
|
||||||
|
popoutBodyH = Number(state.bodyH);
|
||||||
|
if (state.animX !== undefined)
|
||||||
|
popoutAnimX = Number(state.animX);
|
||||||
|
if (state.animY !== undefined)
|
||||||
|
popoutAnimY = Number(state.animY);
|
||||||
|
if (state.screen !== undefined)
|
||||||
|
popoutScreen = state.screen || "";
|
||||||
|
if (state.omitStartConnector !== undefined)
|
||||||
|
popoutOmitStartConnector = !!state.omitStartConnector;
|
||||||
|
if (state.omitEndConnector !== undefined)
|
||||||
|
popoutOmitEndConnector = !!state.omitEndConnector;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function releasePopout(claimId) {
|
||||||
|
if (!hasPopoutOwner(claimId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
popoutOwnerId = "";
|
||||||
|
popoutVisible = false;
|
||||||
|
popoutBarSide = "top";
|
||||||
|
popoutBodyX = 0;
|
||||||
|
popoutBodyY = 0;
|
||||||
|
popoutBodyW = 0;
|
||||||
|
popoutBodyH = 0;
|
||||||
|
popoutAnimX = 0;
|
||||||
|
popoutAnimY = 0;
|
||||||
|
popoutScreen = "";
|
||||||
|
popoutOmitStartConnector = false;
|
||||||
|
popoutOmitEndConnector = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPopoutAnim(claimId, animX, animY) {
|
||||||
|
if (!hasPopoutOwner(claimId))
|
||||||
|
return false;
|
||||||
|
if (animX !== undefined) {
|
||||||
|
const nextX = Number(animX);
|
||||||
|
if (!isNaN(nextX) && popoutAnimX !== nextX)
|
||||||
|
popoutAnimX = nextX;
|
||||||
|
}
|
||||||
|
if (animY !== undefined) {
|
||||||
|
const nextY = Number(animY);
|
||||||
|
if (!isNaN(nextY) && popoutAnimY !== nextY)
|
||||||
|
popoutAnimY = nextY;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPopoutBody(claimId, bodyX, bodyY, bodyW, bodyH) {
|
||||||
|
if (!hasPopoutOwner(claimId))
|
||||||
|
return false;
|
||||||
|
if (bodyX !== undefined) {
|
||||||
|
const nextX = Number(bodyX);
|
||||||
|
if (!isNaN(nextX) && popoutBodyX !== nextX)
|
||||||
|
popoutBodyX = nextX;
|
||||||
|
}
|
||||||
|
if (bodyY !== undefined) {
|
||||||
|
const nextY = Number(bodyY);
|
||||||
|
if (!isNaN(nextY) && popoutBodyY !== nextY)
|
||||||
|
popoutBodyY = nextY;
|
||||||
|
}
|
||||||
|
if (bodyW !== undefined) {
|
||||||
|
const nextW = Number(bodyW);
|
||||||
|
if (!isNaN(nextW) && popoutBodyW !== nextW)
|
||||||
|
popoutBodyW = nextW;
|
||||||
|
}
|
||||||
|
if (bodyH !== undefined) {
|
||||||
|
const nextH = Number(bodyH);
|
||||||
|
if (!isNaN(nextH) && popoutBodyH !== nextH)
|
||||||
|
popoutBodyH = nextH;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _cloneDockStates() {
|
||||||
|
const next = {};
|
||||||
|
for (const screenName in dockStates)
|
||||||
|
next[screenName] = dockStates[screenName];
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _normalizeDockState(state) {
|
||||||
|
return {
|
||||||
|
"reveal": !!(state && state.reveal),
|
||||||
|
"barSide": state && state.barSide ? state.barSide : "bottom",
|
||||||
|
"bodyX": Number(state && state.bodyX !== undefined ? state.bodyX : 0),
|
||||||
|
"bodyY": Number(state && state.bodyY !== undefined ? state.bodyY : 0),
|
||||||
|
"bodyW": Number(state && state.bodyW !== undefined ? state.bodyW : 0),
|
||||||
|
"bodyH": Number(state && state.bodyH !== undefined ? state.bodyH : 0),
|
||||||
|
"slideX": Number(state && state.slideX !== undefined ? state.slideX : 0),
|
||||||
|
"slideY": Number(state && state.slideY !== undefined ? state.slideY : 0)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDockState(screenName, state) {
|
||||||
|
if (!screenName || !state)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const next = _cloneDockStates();
|
||||||
|
next[screenName] = _normalizeDockState(state);
|
||||||
|
dockStates = next;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearDockState(screenName) {
|
||||||
|
if (!screenName || !dockStates[screenName])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const next = _cloneDockStates();
|
||||||
|
delete next[screenName];
|
||||||
|
dockStates = next;
|
||||||
|
|
||||||
|
// Also clear corresponding slide
|
||||||
|
if (dockSlides[screenName]) {
|
||||||
|
const nextSlides = {};
|
||||||
|
for (const k in dockSlides)
|
||||||
|
nextSlides[k] = dockSlides[k];
|
||||||
|
delete nextSlides[screenName];
|
||||||
|
dockSlides = nextSlides;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDockSlide(screenName, x, y) {
|
||||||
|
if (!screenName)
|
||||||
|
return false;
|
||||||
|
const next = {};
|
||||||
|
for (const k in dockSlides)
|
||||||
|
next[k] = dockSlides[k];
|
||||||
|
next[screenName] = { "x": Number(x), "y": Number(y) };
|
||||||
|
dockSlides = next;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Notification state (per screen, updated by NotificationSurface) ──────
|
||||||
|
|
||||||
|
readonly property var emptyNotificationState: ({
|
||||||
|
"visible": false,
|
||||||
|
"barSide": "top",
|
||||||
|
"bodyX": 0,
|
||||||
|
"bodyY": 0,
|
||||||
|
"bodyW": 0,
|
||||||
|
"bodyH": 0,
|
||||||
|
"omitStartConnector": false,
|
||||||
|
"omitEndConnector": false
|
||||||
|
})
|
||||||
|
|
||||||
|
property var notificationStates: ({})
|
||||||
|
|
||||||
|
function _cloneNotificationStates() {
|
||||||
|
const next = {};
|
||||||
|
for (const screenName in notificationStates)
|
||||||
|
next[screenName] = notificationStates[screenName];
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _normalizeNotificationState(state) {
|
||||||
|
return {
|
||||||
|
"visible": !!(state && state.visible),
|
||||||
|
"barSide": state && state.barSide ? state.barSide : "top",
|
||||||
|
"bodyX": Number(state && state.bodyX !== undefined ? state.bodyX : 0),
|
||||||
|
"bodyY": Number(state && state.bodyY !== undefined ? state.bodyY : 0),
|
||||||
|
"bodyW": Number(state && state.bodyW !== undefined ? state.bodyW : 0),
|
||||||
|
"bodyH": Number(state && state.bodyH !== undefined ? state.bodyH : 0),
|
||||||
|
"omitStartConnector": !!(state && state.omitStartConnector),
|
||||||
|
"omitEndConnector": !!(state && state.omitEndConnector)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNotificationState(screenName, state) {
|
||||||
|
if (!screenName || !state)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const next = _cloneNotificationStates();
|
||||||
|
next[screenName] = _normalizeNotificationState(state);
|
||||||
|
notificationStates = next;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearNotificationState(screenName) {
|
||||||
|
if (!screenName || !notificationStates[screenName])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const next = _cloneNotificationStates();
|
||||||
|
delete next[screenName];
|
||||||
|
notificationStates = next;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,8 +13,13 @@ Item {
|
|||||||
|
|
||||||
property color targetColor: "white"
|
property color targetColor: "white"
|
||||||
property real targetRadius: Theme.cornerRadius
|
property real targetRadius: Theme.cornerRadius
|
||||||
|
property real topLeftRadius: targetRadius
|
||||||
|
property real topRightRadius: targetRadius
|
||||||
|
property real bottomLeftRadius: targetRadius
|
||||||
|
property real bottomRightRadius: targetRadius
|
||||||
property color borderColor: "transparent"
|
property color borderColor: "transparent"
|
||||||
property real borderWidth: 0
|
property real borderWidth: 0
|
||||||
|
property bool useCustomSource: false
|
||||||
|
|
||||||
property bool shadowEnabled: Theme.elevationEnabled
|
property bool shadowEnabled: Theme.elevationEnabled
|
||||||
property real shadowBlurPx: level && level.blurPx !== undefined ? level.blurPx : 0
|
property real shadowBlurPx: level && level.blurPx !== undefined ? level.blurPx : 0
|
||||||
@@ -46,7 +51,11 @@ Item {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: sourceRect
|
id: sourceRect
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: root.targetRadius
|
visible: !root.useCustomSource
|
||||||
|
topLeftRadius: root.topLeftRadius
|
||||||
|
topRightRadius: root.topRightRadius
|
||||||
|
bottomLeftRadius: root.bottomLeftRadius
|
||||||
|
bottomRightRadius: root.bottomRightRadius
|
||||||
color: root.targetColor
|
color: root.targetColor
|
||||||
border.color: root.borderColor
|
border.color: root.borderColor
|
||||||
border.width: root.borderWidth
|
border.width: root.borderWidth
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import "settings/SettingsStore.js" as Store
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property int settingsConfigVersion: 5
|
readonly property int settingsConfigVersion: 11
|
||||||
|
|
||||||
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
|
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
|
||||||
|
|
||||||
@@ -37,6 +37,18 @@ Singleton {
|
|||||||
Custom
|
Custom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum AnimationVariant {
|
||||||
|
Material,
|
||||||
|
Fluent,
|
||||||
|
Dynamic
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AnimationEffect {
|
||||||
|
Standard, // 0 — M3: scale-in, rises from below
|
||||||
|
Directional, // 1 — pure large slide, no scale
|
||||||
|
Depth // 2 — medium slide with deep depth scale pop
|
||||||
|
}
|
||||||
|
|
||||||
enum SuspendBehavior {
|
enum SuspendBehavior {
|
||||||
Suspend,
|
Suspend,
|
||||||
Hibernate,
|
Hibernate,
|
||||||
@@ -168,6 +180,12 @@ Singleton {
|
|||||||
property int modalCustomAnimationDuration: 150
|
property int modalCustomAnimationDuration: 150
|
||||||
property bool enableRippleEffects: true
|
property bool enableRippleEffects: true
|
||||||
onEnableRippleEffectsChanged: saveSettings()
|
onEnableRippleEffectsChanged: saveSettings()
|
||||||
|
property int animationVariant: SettingsData.AnimationVariant.Material
|
||||||
|
onAnimationVariantChanged: saveSettings()
|
||||||
|
property int motionEffect: SettingsData.AnimationEffect.Standard
|
||||||
|
onMotionEffectChanged: saveSettings()
|
||||||
|
property int directionalAnimationMode: 0
|
||||||
|
onDirectionalAnimationModeChanged: saveSettings()
|
||||||
property bool m3ElevationEnabled: true
|
property bool m3ElevationEnabled: true
|
||||||
onM3ElevationEnabledChanged: saveSettings()
|
onM3ElevationEnabledChanged: saveSettings()
|
||||||
property int m3ElevationIntensity: 12
|
property int m3ElevationIntensity: 12
|
||||||
@@ -186,6 +204,7 @@ Singleton {
|
|||||||
onPopoutElevationEnabledChanged: saveSettings()
|
onPopoutElevationEnabledChanged: saveSettings()
|
||||||
property bool barElevationEnabled: true
|
property bool barElevationEnabled: true
|
||||||
onBarElevationEnabledChanged: saveSettings()
|
onBarElevationEnabledChanged: saveSettings()
|
||||||
|
|
||||||
property bool blurEnabled: false
|
property bool blurEnabled: false
|
||||||
onBlurEnabledChanged: saveSettings()
|
onBlurEnabledChanged: saveSettings()
|
||||||
property string blurBorderColor: "outline"
|
property string blurBorderColor: "outline"
|
||||||
@@ -198,6 +217,52 @@ Singleton {
|
|||||||
property bool blurredWallpaperLayer: false
|
property bool blurredWallpaperLayer: false
|
||||||
property bool blurWallpaperOnOverview: false
|
property bool blurWallpaperOnOverview: false
|
||||||
|
|
||||||
|
property bool frameEnabled: false
|
||||||
|
onFrameEnabledChanged: saveSettings()
|
||||||
|
property real frameThickness: 16
|
||||||
|
onFrameThicknessChanged: saveSettings()
|
||||||
|
property real frameRounding: 23
|
||||||
|
onFrameRoundingChanged: saveSettings()
|
||||||
|
property string frameColor: ""
|
||||||
|
onFrameColorChanged: saveSettings()
|
||||||
|
property real frameOpacity: 1.0
|
||||||
|
onFrameOpacityChanged: saveSettings()
|
||||||
|
property var frameScreenPreferences: ["all"]
|
||||||
|
onFrameScreenPreferencesChanged: saveSettings()
|
||||||
|
property real frameBarSize: 40
|
||||||
|
onFrameBarSizeChanged: saveSettings()
|
||||||
|
property bool frameShowOnOverview: false
|
||||||
|
onFrameShowOnOverviewChanged: saveSettings()
|
||||||
|
property bool frameBlurEnabled: true
|
||||||
|
onFrameBlurEnabledChanged: saveSettings()
|
||||||
|
property bool frameCloseGaps: false
|
||||||
|
onFrameCloseGapsChanged: saveSettings()
|
||||||
|
property int previousDirectionalMode: 1
|
||||||
|
onPreviousDirectionalModeChanged: saveSettings()
|
||||||
|
property var connectedFrameBarStyleBackups: ({})
|
||||||
|
onConnectedFrameBarStyleBackupsChanged: saveSettings()
|
||||||
|
readonly property bool connectedFrameModeActive: frameEnabled
|
||||||
|
&& motionEffect === SettingsData.AnimationEffect.Directional
|
||||||
|
&& directionalAnimationMode === 3
|
||||||
|
onConnectedFrameModeActiveChanged: {
|
||||||
|
if (_loading)
|
||||||
|
return;
|
||||||
|
if (connectedFrameModeActive) {
|
||||||
|
_captureConnectedFrameBarStyleBackups(barConfigs, true);
|
||||||
|
_enforceConnectedModeBarStyleReset();
|
||||||
|
} else {
|
||||||
|
_restoreConnectedFrameBarStyleBackups();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property color effectiveFrameColor: {
|
||||||
|
const fc = frameColor;
|
||||||
|
if (!fc || fc === "default") return Theme.surfaceContainer;
|
||||||
|
if (fc === "primary") return Theme.primary;
|
||||||
|
if (fc === "surface") return Theme.surface;
|
||||||
|
return fc;
|
||||||
|
}
|
||||||
|
|
||||||
property bool showLauncherButton: true
|
property bool showLauncherButton: true
|
||||||
property bool showWorkspaceSwitcher: true
|
property bool showWorkspaceSwitcher: true
|
||||||
property bool showFocusedWindow: true
|
property bool showFocusedWindow: true
|
||||||
@@ -1288,6 +1353,7 @@ Singleton {
|
|||||||
_loading = false;
|
_loading = false;
|
||||||
}
|
}
|
||||||
loadPluginSettings();
|
loadPluginSettings();
|
||||||
|
Qt.callLater(() => _reconcileConnectedFrameBarStyles());
|
||||||
}
|
}
|
||||||
|
|
||||||
property var _pendingMigration: null
|
property var _pendingMigration: null
|
||||||
@@ -1401,6 +1467,149 @@ Singleton {
|
|||||||
pluginSettingsFile.setText(JSON.stringify(pluginSettings, null, 2));
|
pluginSettingsFile.setText(JSON.stringify(pluginSettings, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _connectedFrameBarStyleSnapshot(config) {
|
||||||
|
return {
|
||||||
|
"shadowIntensity": config?.shadowIntensity ?? 0,
|
||||||
|
"squareCorners": config?.squareCorners ?? false,
|
||||||
|
"gothCornersEnabled": config?.gothCornersEnabled ?? false,
|
||||||
|
"borderEnabled": config?.borderEnabled ?? false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function _hasConnectedFrameBarStyleBackups() {
|
||||||
|
return connectedFrameBarStyleBackups && Object.keys(connectedFrameBarStyleBackups).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _captureConnectedFrameBarStyleBackups(configs, overwriteExisting) {
|
||||||
|
if (!Array.isArray(configs))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const nextBackups = JSON.parse(JSON.stringify(connectedFrameBarStyleBackups || {}));
|
||||||
|
const validIds = {};
|
||||||
|
let changed = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < configs.length; i++) {
|
||||||
|
const config = configs[i];
|
||||||
|
if (!config?.id)
|
||||||
|
continue;
|
||||||
|
validIds[config.id] = true;
|
||||||
|
|
||||||
|
if (!overwriteExisting && nextBackups[config.id] !== undefined)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const snapshot = _connectedFrameBarStyleSnapshot(config);
|
||||||
|
if (JSON.stringify(nextBackups[config.id]) !== JSON.stringify(snapshot)) {
|
||||||
|
nextBackups[config.id] = snapshot;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overwriteExisting) {
|
||||||
|
for (const barId in nextBackups) {
|
||||||
|
if (validIds[barId])
|
||||||
|
continue;
|
||||||
|
delete nextBackups[barId];
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
connectedFrameBarStyleBackups = nextBackups;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _restoreConnectedFrameBarStyleBackups() {
|
||||||
|
if (!_hasConnectedFrameBarStyleBackups())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const backups = connectedFrameBarStyleBackups || {};
|
||||||
|
const configs = JSON.parse(JSON.stringify(barConfigs));
|
||||||
|
let changed = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < configs.length; i++) {
|
||||||
|
const backup = backups[configs[i].id];
|
||||||
|
if (!backup)
|
||||||
|
continue;
|
||||||
|
for (const key in backup) {
|
||||||
|
if (configs[i][key] === backup[key])
|
||||||
|
continue;
|
||||||
|
configs[i][key] = backup[key];
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
barConfigs = configs;
|
||||||
|
connectedFrameBarStyleBackups = ({});
|
||||||
|
if (changed)
|
||||||
|
updateBarConfigs();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _reconcileConnectedFrameBarStyles() {
|
||||||
|
if (connectedFrameModeActive) {
|
||||||
|
if (!_hasConnectedFrameBarStyleBackups())
|
||||||
|
_captureConnectedFrameBarStyleBackups(barConfigs, true);
|
||||||
|
_enforceConnectedModeBarStyleReset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_restoreConnectedFrameBarStyleBackups();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _sanitizeBarConfigForConnectedFrame(config) {
|
||||||
|
if (!connectedFrameModeActive || !config)
|
||||||
|
return config;
|
||||||
|
|
||||||
|
let changed = false;
|
||||||
|
const sanitized = Object.assign({}, config);
|
||||||
|
|
||||||
|
if ((sanitized.shadowIntensity ?? 0) !== 0) {
|
||||||
|
sanitized.shadowIntensity = 0;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (sanitized.squareCorners ?? false) {
|
||||||
|
sanitized.squareCorners = false;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (sanitized.gothCornersEnabled ?? false) {
|
||||||
|
sanitized.gothCornersEnabled = false;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (sanitized.borderEnabled ?? false) {
|
||||||
|
sanitized.borderEnabled = false;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed ? sanitized : config;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _sanitizeBarConfigsForConnectedFrame(configs) {
|
||||||
|
if (!connectedFrameModeActive || !Array.isArray(configs))
|
||||||
|
return {
|
||||||
|
"configs": configs,
|
||||||
|
"changed": false
|
||||||
|
};
|
||||||
|
|
||||||
|
let changed = false;
|
||||||
|
const sanitizedConfigs = configs.map(config => {
|
||||||
|
const sanitized = _sanitizeBarConfigForConnectedFrame(config);
|
||||||
|
if (sanitized !== config)
|
||||||
|
changed = true;
|
||||||
|
return sanitized;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
"configs": changed ? sanitizedConfigs : configs,
|
||||||
|
"changed": changed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function _enforceConnectedModeBarStyleReset() {
|
||||||
|
const result = _sanitizeBarConfigsForConnectedFrame(barConfigs);
|
||||||
|
if (!result.changed)
|
||||||
|
return;
|
||||||
|
barConfigs = result.configs;
|
||||||
|
updateBarConfigs();
|
||||||
|
}
|
||||||
|
|
||||||
function detectAvailableIconThemes() {
|
function detectAvailableIconThemes() {
|
||||||
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS") || "";
|
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS") || "";
|
||||||
const localData = Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation));
|
const localData = Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation));
|
||||||
@@ -1548,35 +1757,37 @@ Singleton {
|
|||||||
const spacing = barSpacing !== undefined ? barSpacing : (defaultBar?.spacing ?? 4);
|
const spacing = barSpacing !== undefined ? barSpacing : (defaultBar?.spacing ?? 4);
|
||||||
const position = barPosition !== undefined ? barPosition : (defaultBar?.position ?? SettingsData.Position.Top);
|
const position = barPosition !== undefined ? barPosition : (defaultBar?.position ?? SettingsData.Position.Top);
|
||||||
const rawBottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : (defaultBar?.bottomGap ?? 0)) : (defaultBar?.bottomGap ?? 0);
|
const rawBottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : (defaultBar?.bottomGap ?? 0)) : (defaultBar?.bottomGap ?? 0);
|
||||||
const bottomGap = Math.max(0, rawBottomGap);
|
const isConnected = connectedFrameModeActive;
|
||||||
|
const bottomGap = isConnected ? 0 : Math.max(0, rawBottomGap);
|
||||||
|
|
||||||
const useAutoGaps = (barConfig && barConfig.popupGapsAuto !== undefined) ? barConfig.popupGapsAuto : (defaultBar?.popupGapsAuto ?? true);
|
const useAutoGaps = (barConfig && barConfig.popupGapsAuto !== undefined) ? barConfig.popupGapsAuto : (defaultBar?.popupGapsAuto ?? true);
|
||||||
const manualGapValue = (barConfig && barConfig.popupGapsManual !== undefined) ? barConfig.popupGapsManual : (defaultBar?.popupGapsManual ?? 4);
|
const manualGapValue = (barConfig && barConfig.popupGapsManual !== undefined) ? barConfig.popupGapsManual : (defaultBar?.popupGapsManual ?? 4);
|
||||||
const popupGap = useAutoGaps ? Math.max(4, spacing) : manualGapValue;
|
const popupGap = isConnected ? 0 : (useAutoGaps ? Math.max(4, spacing) : manualGapValue);
|
||||||
|
const edgeSpacing = isConnected ? 0 : spacing;
|
||||||
|
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case SettingsData.Position.Left:
|
case SettingsData.Position.Left:
|
||||||
return {
|
return {
|
||||||
"x": barThickness + spacing + popupGap,
|
"x": barThickness + edgeSpacing + popupGap,
|
||||||
"y": relativeY,
|
"y": relativeY,
|
||||||
"width": widgetWidth
|
"width": widgetWidth
|
||||||
};
|
};
|
||||||
case SettingsData.Position.Right:
|
case SettingsData.Position.Right:
|
||||||
return {
|
return {
|
||||||
"x": (screen?.width || 0) - (barThickness + spacing + popupGap),
|
"x": (screen?.width || 0) - (barThickness + edgeSpacing + popupGap),
|
||||||
"y": relativeY,
|
"y": relativeY,
|
||||||
"width": widgetWidth
|
"width": widgetWidth
|
||||||
};
|
};
|
||||||
case SettingsData.Position.Bottom:
|
case SettingsData.Position.Bottom:
|
||||||
return {
|
return {
|
||||||
"x": relativeX,
|
"x": relativeX,
|
||||||
"y": (screen?.height || 0) - (barThickness + spacing + bottomGap + popupGap),
|
"y": (screen?.height || 0) - (barThickness + edgeSpacing + bottomGap + popupGap),
|
||||||
"width": widgetWidth
|
"width": widgetWidth
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
"x": relativeX,
|
"x": relativeX,
|
||||||
"y": barThickness + spacing + bottomGap + popupGap,
|
"y": barThickness + edgeSpacing + bottomGap + popupGap,
|
||||||
"width": widgetWidth
|
"width": widgetWidth
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1670,7 +1881,9 @@ Singleton {
|
|||||||
const screenWidth = screen.width;
|
const screenWidth = screen.width;
|
||||||
const screenHeight = screen.height;
|
const screenHeight = screen.height;
|
||||||
const position = barPosition !== undefined ? barPosition : (defaultBar?.position ?? SettingsData.Position.Top);
|
const position = barPosition !== undefined ? barPosition : (defaultBar?.position ?? SettingsData.Position.Top);
|
||||||
const bottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : (defaultBar?.bottomGap ?? 0)) : (defaultBar?.bottomGap ?? 0);
|
const isConnected = connectedFrameModeActive;
|
||||||
|
const rawBottomGap = barConfig ? (barConfig.bottomGap !== undefined ? barConfig.bottomGap : (defaultBar?.bottomGap ?? 0)) : (defaultBar?.bottomGap ?? 0);
|
||||||
|
const bottomGap = isConnected ? 0 : rawBottomGap;
|
||||||
|
|
||||||
let topOffset = 0;
|
let topOffset = 0;
|
||||||
let bottomOffset = 0;
|
let bottomOffset = 0;
|
||||||
@@ -1692,7 +1905,7 @@ Singleton {
|
|||||||
const otherSpacing = other.spacing !== undefined ? other.spacing : (defaultBar?.spacing ?? 4);
|
const otherSpacing = other.spacing !== undefined ? other.spacing : (defaultBar?.spacing ?? 4);
|
||||||
const otherPadding = other.innerPadding !== undefined ? other.innerPadding : (defaultBar?.innerPadding ?? 4);
|
const otherPadding = other.innerPadding !== undefined ? other.innerPadding : (defaultBar?.innerPadding ?? 4);
|
||||||
const otherThickness = Math.max(26 + otherPadding * 0.6, Theme.barHeight - 4 - (8 - otherPadding)) + otherSpacing + wingSize;
|
const otherThickness = Math.max(26 + otherPadding * 0.6, Theme.barHeight - 4 - (8 - otherPadding)) + otherSpacing + wingSize;
|
||||||
const otherBottomGap = other.bottomGap !== undefined ? other.bottomGap : (defaultBar?.bottomGap ?? 0);
|
const otherBottomGap = isConnected ? 0 : (other.bottomGap !== undefined ? other.bottomGap : (defaultBar?.bottomGap ?? 0));
|
||||||
|
|
||||||
switch (other.position) {
|
switch (other.position) {
|
||||||
case SettingsData.Position.Top:
|
case SettingsData.Position.Top:
|
||||||
@@ -1783,7 +1996,9 @@ Singleton {
|
|||||||
function addBarConfig(config) {
|
function addBarConfig(config) {
|
||||||
const configs = JSON.parse(JSON.stringify(barConfigs));
|
const configs = JSON.parse(JSON.stringify(barConfigs));
|
||||||
configs.push(config);
|
configs.push(config);
|
||||||
barConfigs = configs;
|
if (connectedFrameModeActive)
|
||||||
|
_captureConnectedFrameBarStyleBackups(configs, false);
|
||||||
|
barConfigs = _sanitizeBarConfigsForConnectedFrame(configs).configs;
|
||||||
updateBarConfigs();
|
updateBarConfigs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1795,7 +2010,7 @@ Singleton {
|
|||||||
const positionChanged = updates.position !== undefined && configs[index].position !== updates.position;
|
const positionChanged = updates.position !== undefined && configs[index].position !== updates.position;
|
||||||
|
|
||||||
Object.assign(configs[index], updates);
|
Object.assign(configs[index], updates);
|
||||||
barConfigs = configs;
|
barConfigs = _sanitizeBarConfigsForConnectedFrame(configs).configs;
|
||||||
updateBarConfigs();
|
updateBarConfigs();
|
||||||
|
|
||||||
if (positionChanged) {
|
if (positionChanged) {
|
||||||
@@ -1849,6 +2064,11 @@ Singleton {
|
|||||||
return;
|
return;
|
||||||
const configs = barConfigs.filter(cfg => cfg.id !== barId);
|
const configs = barConfigs.filter(cfg => cfg.id !== barId);
|
||||||
barConfigs = configs;
|
barConfigs = configs;
|
||||||
|
if (connectedFrameBarStyleBackups?.[barId] !== undefined) {
|
||||||
|
const nextBackups = JSON.parse(JSON.stringify(connectedFrameBarStyleBackups || {}));
|
||||||
|
delete nextBackups[barId];
|
||||||
|
connectedFrameBarStyleBackups = nextBackups;
|
||||||
|
}
|
||||||
updateBarConfigs();
|
updateBarConfigs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1943,6 +2163,66 @@ Singleton {
|
|||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFrameFilteredScreens() {
|
||||||
|
var prefs = frameScreenPreferences || ["all"];
|
||||||
|
if (!prefs || prefs.length === 0 || prefs.includes("all")) {
|
||||||
|
return Quickshell.screens;
|
||||||
|
}
|
||||||
|
return Quickshell.screens.filter(screen => isScreenInPreferences(screen, prefs));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActiveBarEdgeForScreen(screen) {
|
||||||
|
if (!screen) return "";
|
||||||
|
for (var i = 0; i < barConfigs.length; i++) {
|
||||||
|
var bc = barConfigs[i];
|
||||||
|
if (!bc.enabled) continue;
|
||||||
|
var prefs = bc.screenPreferences || ["all"];
|
||||||
|
if (!prefs.includes("all") && !isScreenInPreferences(screen, prefs)) continue;
|
||||||
|
switch (bc.position ?? 0) {
|
||||||
|
case SettingsData.Position.Top: return "top";
|
||||||
|
case SettingsData.Position.Bottom: return "bottom";
|
||||||
|
case SettingsData.Position.Left: return "left";
|
||||||
|
case SettingsData.Position.Right: return "right";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActiveBarEdgesForScreen(screen) {
|
||||||
|
if (!screen) return [];
|
||||||
|
var edges = [];
|
||||||
|
for (var i = 0; i < barConfigs.length; i++) {
|
||||||
|
var bc = barConfigs[i];
|
||||||
|
if (!bc.enabled) continue;
|
||||||
|
var prefs = bc.screenPreferences || ["all"];
|
||||||
|
if (!prefs.includes("all") && !isScreenInPreferences(screen, prefs)) continue;
|
||||||
|
switch (bc.position ?? 0) {
|
||||||
|
case SettingsData.Position.Top: edges.push("top"); break;
|
||||||
|
case SettingsData.Position.Bottom: edges.push("bottom"); break;
|
||||||
|
case SettingsData.Position.Left: edges.push("left"); break;
|
||||||
|
case SettingsData.Position.Right: edges.push("right"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActiveBarThicknessForScreen(screen) {
|
||||||
|
if (frameEnabled) return frameBarSize;
|
||||||
|
if (!screen) return frameThickness;
|
||||||
|
for (var i = 0; i < barConfigs.length; i++) {
|
||||||
|
var bc = barConfigs[i];
|
||||||
|
if (!bc.enabled) continue;
|
||||||
|
var prefs = bc.screenPreferences || ["all"];
|
||||||
|
if (!prefs.includes("all") && !isScreenInPreferences(screen, prefs)) continue;
|
||||||
|
const innerPadding = bc.innerPadding ?? 4;
|
||||||
|
const barT = Math.max(26 + innerPadding * 0.6, Theme.barHeight - 4 - (8 - innerPadding));
|
||||||
|
const spacing = bc.spacing ?? 4;
|
||||||
|
const bottomGap = bc.bottomGap ?? 0;
|
||||||
|
return barT + spacing + bottomGap;
|
||||||
|
}
|
||||||
|
return frameThickness;
|
||||||
|
}
|
||||||
|
|
||||||
function sendTestNotifications() {
|
function sendTestNotifications() {
|
||||||
NotificationService.dismissAllPopups();
|
NotificationService.dismissAllPopups();
|
||||||
sendTestNotification(0);
|
sendTestNotification(0);
|
||||||
|
|||||||
@@ -960,6 +960,40 @@ Singleton {
|
|||||||
"expressiveEffects": [0.34, 0.8, 0.34, 1, 1, 1]
|
"expressiveEffects": [0.34, 0.8, 0.34, 1, 1, 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delegates to AnimVariants.qml for curves, timing, scale, and offsets.
|
||||||
|
readonly property list<real> variantEnterCurve: AnimVariants.variantEnterCurve
|
||||||
|
readonly property list<real> variantExitCurve: AnimVariants.variantExitCurve
|
||||||
|
readonly property list<real> variantModalEnterCurve: AnimVariants.variantModalEnterCurve
|
||||||
|
readonly property list<real> variantModalExitCurve: AnimVariants.variantModalExitCurve
|
||||||
|
readonly property list<real> variantPopoutEnterCurve: AnimVariants.variantPopoutEnterCurve
|
||||||
|
readonly property list<real> variantPopoutExitCurve: AnimVariants.variantPopoutExitCurve
|
||||||
|
readonly property real variantEnterDurationFactor: AnimVariants.variantEnterDurationFactor
|
||||||
|
readonly property real variantExitDurationFactor: AnimVariants.variantExitDurationFactor
|
||||||
|
readonly property real variantOpacityDurationScale: AnimVariants.variantOpacityDurationScale
|
||||||
|
readonly property bool isDirectionalEffect: AnimVariants.isDirectionalEffect
|
||||||
|
readonly property bool isDepthEffect: AnimVariants.isDepthEffect
|
||||||
|
readonly property bool isConnectedEffect: AnimVariants.isConnectedEffect
|
||||||
|
readonly property real connectedCornerRadius: {
|
||||||
|
if (typeof SettingsData === "undefined") return 12;
|
||||||
|
return SettingsData.connectedFrameModeActive ? SettingsData.frameRounding : cornerRadius;
|
||||||
|
}
|
||||||
|
readonly property color connectedSurfaceColor: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return withAlpha(surfaceContainer, popupTransparency);
|
||||||
|
return isConnectedEffect
|
||||||
|
? Qt.rgba(SettingsData.effectiveFrameColor.r, SettingsData.effectiveFrameColor.g, SettingsData.effectiveFrameColor.b, SettingsData.frameOpacity)
|
||||||
|
: withAlpha(surfaceContainer, popupTransparency);
|
||||||
|
}
|
||||||
|
readonly property real connectedSurfaceRadius: isConnectedEffect ? connectedCornerRadius : cornerRadius
|
||||||
|
readonly property bool connectedSurfaceBlurEnabled: (typeof SettingsData === "undefined")
|
||||||
|
? true
|
||||||
|
: (!isConnectedEffect || SettingsData.frameBlurEnabled)
|
||||||
|
readonly property real effectScaleCollapsed: AnimVariants.effectScaleCollapsed
|
||||||
|
readonly property real effectAnimOffset: AnimVariants.effectAnimOffset
|
||||||
|
function variantDuration(baseDuration, entering) { return AnimVariants.variantDuration(baseDuration, entering); }
|
||||||
|
function variantExitCleanupPadding() { return AnimVariants.variantExitCleanupPadding(); }
|
||||||
|
function variantCloseInterval(baseDuration) { return AnimVariants.variantCloseInterval(baseDuration); }
|
||||||
|
|
||||||
readonly property var animationPresetDurations: {
|
readonly property var animationPresetDurations: {
|
||||||
"none": 0,
|
"none": 0,
|
||||||
"short": 250,
|
"short": 250,
|
||||||
@@ -1125,7 +1159,13 @@ Singleton {
|
|||||||
property real iconSizeLarge: 32
|
property real iconSizeLarge: 32
|
||||||
|
|
||||||
property real panelTransparency: 0.85
|
property real panelTransparency: 0.85
|
||||||
property real popupTransparency: typeof SettingsData !== "undefined" && SettingsData.popupTransparency !== undefined ? SettingsData.popupTransparency : 1.0
|
property real popupTransparency: {
|
||||||
|
if (typeof SettingsData === "undefined")
|
||||||
|
return 1.0;
|
||||||
|
if (isConnectedEffect)
|
||||||
|
return SettingsData.frameOpacity !== undefined ? SettingsData.frameOpacity : 1.0;
|
||||||
|
return SettingsData.popupTransparency !== undefined ? SettingsData.popupTransparency : 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
function screenTransition() {
|
function screenTransition() {
|
||||||
if (CompositorService.isNiri) {
|
if (CompositorService.isNiri) {
|
||||||
@@ -1824,6 +1864,12 @@ Singleton {
|
|||||||
return Qt.rgba(c.r, c.g, c.b, a);
|
return Qt.rgba(c.r, c.g, c.b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function popupLayerColor(baseColor) {
|
||||||
|
if (isConnectedEffect)
|
||||||
|
return connectedSurfaceColor;
|
||||||
|
return withAlpha(baseColor, popupTransparency);
|
||||||
|
}
|
||||||
|
|
||||||
function blendAlpha(c, a) {
|
function blendAlpha(c, a) {
|
||||||
return Qt.rgba(c.r, c.g, c.b, c.a * a);
|
return Qt.rgba(c.r, c.g, c.b, c.a * a);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,10 @@ var SPEC = {
|
|||||||
modalAnimationSpeed: { def: 1 },
|
modalAnimationSpeed: { def: 1 },
|
||||||
modalCustomAnimationDuration: { def: 150 },
|
modalCustomAnimationDuration: { def: 150 },
|
||||||
enableRippleEffects: { def: true },
|
enableRippleEffects: { def: true },
|
||||||
|
animationVariant: { def: 0 },
|
||||||
|
motionEffect: { def: 0 },
|
||||||
|
directionalAnimationMode: { def: 0 },
|
||||||
|
previousDirectionalMode: { def: 1 },
|
||||||
m3ElevationEnabled: { def: true },
|
m3ElevationEnabled: { def: true },
|
||||||
m3ElevationIntensity: { def: 12 },
|
m3ElevationIntensity: { def: 12 },
|
||||||
m3ElevationOpacity: { def: 30 },
|
m3ElevationOpacity: { def: 30 },
|
||||||
@@ -431,6 +435,7 @@ var SPEC = {
|
|||||||
displayProfileAutoSelect: { def: false },
|
displayProfileAutoSelect: { def: false },
|
||||||
displayShowDisconnected: { def: false },
|
displayShowDisconnected: { def: false },
|
||||||
displaySnapToEdge: { def: true },
|
displaySnapToEdge: { def: true },
|
||||||
|
connectedFrameBarStyleBackups: { def: {} },
|
||||||
|
|
||||||
barConfigs: {
|
barConfigs: {
|
||||||
def: [{
|
def: [{
|
||||||
@@ -537,7 +542,18 @@ var SPEC = {
|
|||||||
clipboardEnterToPaste: { def: false },
|
clipboardEnterToPaste: { def: false },
|
||||||
|
|
||||||
launcherPluginVisibility: { def: {} },
|
launcherPluginVisibility: { def: {} },
|
||||||
launcherPluginOrder: { def: [] }
|
launcherPluginOrder: { def: [] },
|
||||||
|
|
||||||
|
frameEnabled: { def: false },
|
||||||
|
frameThickness: { def: 16 },
|
||||||
|
frameRounding: { def: 23 },
|
||||||
|
frameColor: { def: "" },
|
||||||
|
frameOpacity: { def: 1.0 },
|
||||||
|
frameScreenPreferences: { def: ["all"] },
|
||||||
|
frameBarSize: { def: 40 },
|
||||||
|
frameShowOnOverview: { def: false },
|
||||||
|
frameBlurEnabled: { def: true },
|
||||||
|
frameCloseGaps: { def: false }
|
||||||
};
|
};
|
||||||
|
|
||||||
function getValidKeys() {
|
function getValidKeys() {
|
||||||
|
|||||||
@@ -248,6 +248,10 @@ function migrateToVersion(obj, targetVersion) {
|
|||||||
settings.configVersion = 6;
|
settings.configVersion = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentVersion < 11) {
|
||||||
|
settings.configVersion = 11;
|
||||||
|
}
|
||||||
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import qs.Modules.OSD
|
|||||||
import qs.Modules.ProcessList
|
import qs.Modules.ProcessList
|
||||||
import qs.Modules.DankBar
|
import qs.Modules.DankBar
|
||||||
import qs.Modules.DankBar.Popouts
|
import qs.Modules.DankBar.Popouts
|
||||||
|
import qs.Modules.Frame
|
||||||
import qs.Modules.WorkspaceOverlays
|
import qs.Modules.WorkspaceOverlays
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
@@ -176,6 +177,8 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Frame {}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: dankBarRepeater
|
id: dankBarRepeater
|
||||||
model: ScriptModel {
|
model: ScriptModel {
|
||||||
|
|||||||
@@ -64,11 +64,19 @@ DankModal {
|
|||||||
activeImageLoads = 0;
|
activeImageLoads = 0;
|
||||||
shouldHaveFocus = true;
|
shouldHaveFocus = true;
|
||||||
ClipboardService.reset();
|
ClipboardService.reset();
|
||||||
if (clipboardAvailable)
|
|
||||||
ClipboardService.refresh();
|
|
||||||
keyboardController.reset();
|
keyboardController.reset();
|
||||||
|
|
||||||
Qt.callLater(function () {
|
Qt.callLater(function () {
|
||||||
|
if (clipboardAvailable) {
|
||||||
|
if (Theme.isConnectedEffect) {
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (clipboardHistoryModal.shouldBeVisible)
|
||||||
|
ClipboardService.refresh();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ClipboardService.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (contentLoader.item?.searchField) {
|
if (contentLoader.item?.searchField) {
|
||||||
contentLoader.item.searchField.text = "";
|
contentLoader.item.searchField.text = "";
|
||||||
contentLoader.item.searchField.forceActiveFocus();
|
contentLoader.item.searchField.forceActiveFocus();
|
||||||
|
|||||||
@@ -53,8 +53,6 @@ DankPopout {
|
|||||||
open();
|
open();
|
||||||
activeImageLoads = 0;
|
activeImageLoads = 0;
|
||||||
ClipboardService.reset();
|
ClipboardService.reset();
|
||||||
if (clipboardAvailable)
|
|
||||||
ClipboardService.refresh();
|
|
||||||
keyboardController.reset();
|
keyboardController.reset();
|
||||||
|
|
||||||
Qt.callLater(function () {
|
Qt.callLater(function () {
|
||||||
@@ -121,8 +119,16 @@ DankPopout {
|
|||||||
onShouldBeVisibleChanged: {
|
onShouldBeVisibleChanged: {
|
||||||
if (!shouldBeVisible)
|
if (!shouldBeVisible)
|
||||||
return;
|
return;
|
||||||
if (clipboardAvailable)
|
if (clipboardAvailable) {
|
||||||
ClipboardService.refresh();
|
if (Theme.isConnectedEffect) {
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (root.shouldBeVisible)
|
||||||
|
ClipboardService.refresh();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ClipboardService.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
keyboardController.reset();
|
keyboardController.reset();
|
||||||
Qt.callLater(function () {
|
Qt.callLater(function () {
|
||||||
if (contentLoader.item?.searchField) {
|
if (contentLoader.item?.searchField) {
|
||||||
|
|||||||
@@ -26,15 +26,22 @@ Item {
|
|||||||
property bool closeOnEscapeKey: true
|
property bool closeOnEscapeKey: true
|
||||||
property bool closeOnBackgroundClick: true
|
property bool closeOnBackgroundClick: true
|
||||||
property string animationType: "scale"
|
property string animationType: "scale"
|
||||||
property int animationDuration: Theme.modalAnimationDuration
|
readonly property bool connectedMotionParity: Theme.isConnectedEffect
|
||||||
property real animationScaleCollapsed: 0.96
|
property int animationDuration: connectedMotionParity ? Theme.popoutAnimationDuration : Theme.modalAnimationDuration
|
||||||
property real animationOffset: Theme.spacingL
|
property real animationScaleCollapsed: Theme.effectScaleCollapsed
|
||||||
property list<real> animationEnterCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
property real animationOffset: Theme.effectAnimOffset
|
||||||
property list<real> animationExitCurve: Theme.expressiveCurves.emphasized
|
property list<real> animationEnterCurve: connectedMotionParity ? Theme.variantPopoutEnterCurve : Theme.variantModalEnterCurve
|
||||||
property color backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
property list<real> animationExitCurve: connectedMotionParity ? Theme.variantPopoutExitCurve : Theme.variantModalExitCurve
|
||||||
|
property color backgroundColor: Theme.surfaceContainer
|
||||||
property color borderColor: Theme.outlineMedium
|
property color borderColor: Theme.outlineMedium
|
||||||
property real borderWidth: 0
|
property real borderWidth: 0
|
||||||
property real cornerRadius: Theme.cornerRadius
|
property real cornerRadius: Theme.cornerRadius
|
||||||
|
readonly property bool connectedSurfaceOverride: Theme.isConnectedEffect
|
||||||
|
readonly property color effectiveBackgroundColor: connectedSurfaceOverride ? Theme.connectedSurfaceColor : backgroundColor
|
||||||
|
readonly property color effectiveBorderColor: connectedSurfaceOverride ? "transparent" : borderColor
|
||||||
|
readonly property real effectiveBorderWidth: connectedSurfaceOverride ? 0 : borderWidth
|
||||||
|
readonly property real effectiveCornerRadius: connectedSurfaceOverride ? Theme.connectedSurfaceRadius : cornerRadius
|
||||||
|
readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled
|
||||||
property bool enableShadow: true
|
property bool enableShadow: true
|
||||||
property alias modalFocusScope: focusScope
|
property alias modalFocusScope: focusScope
|
||||||
property bool shouldBeVisible: false
|
property bool shouldBeVisible: false
|
||||||
@@ -45,11 +52,13 @@ Item {
|
|||||||
property bool keepPopoutsOpen: false
|
property bool keepPopoutsOpen: false
|
||||||
property var customKeyboardFocus: null
|
property var customKeyboardFocus: null
|
||||||
property bool useOverlayLayer: false
|
property bool useOverlayLayer: false
|
||||||
|
property real frozenMotionOffsetX: 0
|
||||||
|
property real frozenMotionOffsetY: 0
|
||||||
readonly property alias contentWindow: contentWindow
|
readonly property alias contentWindow: contentWindow
|
||||||
readonly property alias clickCatcher: clickCatcher
|
readonly property alias clickCatcher: clickCatcher
|
||||||
readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
|
readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
|
||||||
readonly property bool useBackground: showBackground && SettingsData.modalDarkenBackground
|
readonly property bool useBackground: showBackground && SettingsData.modalDarkenBackground
|
||||||
readonly property bool useSingleWindow: CompositorService.isHyprland || useBackground
|
readonly property bool useSingleWindow: CompositorService.isHyprland
|
||||||
|
|
||||||
signal opened
|
signal opened
|
||||||
signal dialogClosed
|
signal dialogClosed
|
||||||
@@ -59,33 +68,34 @@ Item {
|
|||||||
|
|
||||||
function open() {
|
function open() {
|
||||||
closeTimer.stop();
|
closeTimer.stop();
|
||||||
const focusedScreen = CompositorService.getFocusedScreen();
|
animationsEnabled = false;
|
||||||
const screenChanged = focusedScreen && contentWindow.screen !== focusedScreen;
|
frozenMotionOffsetX = modalContainer ? modalContainer.offsetX : 0;
|
||||||
if (focusedScreen) {
|
frozenMotionOffsetY = modalContainer ? modalContainer.offsetY : animationOffset;
|
||||||
if (screenChanged)
|
|
||||||
contentWindow.visible = false;
|
|
||||||
contentWindow.screen = focusedScreen;
|
|
||||||
if (!useSingleWindow) {
|
|
||||||
if (screenChanged)
|
|
||||||
clickCatcher.visible = false;
|
|
||||||
clickCatcher.screen = focusedScreen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (screenChanged) {
|
|
||||||
Qt.callLater(() => root._finishOpen());
|
|
||||||
} else {
|
|
||||||
_finishOpen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _finishOpen() {
|
const focusedScreen = CompositorService.getFocusedScreen();
|
||||||
|
if (focusedScreen) {
|
||||||
|
contentWindow.screen = focusedScreen;
|
||||||
|
if (!useSingleWindow)
|
||||||
|
clickCatcher.screen = focusedScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Theme.isDirectionalEffect || root.useBackground) {
|
||||||
|
if (!useSingleWindow)
|
||||||
|
clickCatcher.visible = true;
|
||||||
|
contentWindow.visible = true;
|
||||||
|
}
|
||||||
ModalManager.openModal(root);
|
ModalManager.openModal(root);
|
||||||
shouldBeVisible = true;
|
|
||||||
if (!useSingleWindow)
|
Qt.callLater(() => {
|
||||||
clickCatcher.visible = true;
|
animationsEnabled = true;
|
||||||
contentWindow.visible = true;
|
shouldBeVisible = true;
|
||||||
shouldHaveFocus = false;
|
if (!useSingleWindow && !clickCatcher.visible)
|
||||||
Qt.callLater(() => shouldHaveFocus = Qt.binding(() => shouldBeVisible));
|
clickCatcher.visible = true;
|
||||||
|
if (!contentWindow.visible)
|
||||||
|
contentWindow.visible = true;
|
||||||
|
shouldHaveFocus = false;
|
||||||
|
Qt.callLater(() => shouldHaveFocus = Qt.binding(() => shouldBeVisible));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
@@ -146,7 +156,7 @@ Item {
|
|||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: closeTimer
|
id: closeTimer
|
||||||
interval: animationDuration + 50
|
interval: Theme.variantCloseInterval(animationDuration)
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (shouldBeVisible)
|
if (shouldBeVisible)
|
||||||
return;
|
return;
|
||||||
@@ -160,7 +170,19 @@ Item {
|
|||||||
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: (root.enableShadow && Theme.elevationEnabled && SettingsData.modalElevationEnabled) ? Theme.elevationRenderPadding(shadowLevel, Theme.elevationLightDirection, shadowFallbackOffset, 8, 16) : 0
|
readonly property real shadowRenderPadding: (root.enableShadow && Theme.elevationEnabled && SettingsData.modalElevationEnabled) ? Theme.elevationRenderPadding(shadowLevel, Theme.elevationLightDirection, shadowFallbackOffset, 8, 16) : 0
|
||||||
readonly property real shadowMotionPadding: animationType === "slide" ? 30 : Math.max(0, animationOffset)
|
readonly property real shadowMotionPadding: {
|
||||||
|
if (Theme.isConnectedEffect)
|
||||||
|
return 0;
|
||||||
|
if (typeof SettingsData !== "undefined" && SettingsData.directionalAnimationMode > 0 && Theme.isDirectionalEffect)
|
||||||
|
return 0; // Wayland native overlap mask
|
||||||
|
if (animationType === "slide")
|
||||||
|
return 30;
|
||||||
|
if (Theme.isDirectionalEffect)
|
||||||
|
return Math.max(Math.max(0, animationOffset), Math.max(alignedWidth, alignedHeight) * 0.9);
|
||||||
|
if (Theme.isDepthEffect)
|
||||||
|
return Math.max(Math.max(0, animationOffset), Math.max(alignedWidth, alignedHeight) * 0.35);
|
||||||
|
return Math.max(0, animationOffset);
|
||||||
|
}
|
||||||
readonly property real shadowBuffer: Theme.snap(shadowRenderPadding + shadowMotionPadding, dpr)
|
readonly property real shadowBuffer: Theme.snap(shadowRenderPadding + shadowMotionPadding, dpr)
|
||||||
readonly property real alignedWidth: Theme.px(modalWidth, dpr)
|
readonly property real alignedWidth: Theme.px(modalWidth, dpr)
|
||||||
readonly property real alignedHeight: Theme.px(modalHeight, dpr)
|
readonly property real alignedHeight: Theme.px(modalHeight, dpr)
|
||||||
@@ -220,9 +242,26 @@ Item {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: root.closeOnBackgroundClick && root.shouldBeVisible
|
enabled: !root.useSingleWindow && root.closeOnBackgroundClick && root.shouldBeVisible
|
||||||
onClicked: root.backgroundClicked()
|
onClicked: root.backgroundClicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
z: -1
|
||||||
|
color: "black"
|
||||||
|
opacity: (!root.useSingleWindow && root.useBackground) ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
|
||||||
|
visible: opacity > 0
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || Theme.isConnectedEffect)
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Math.round(Theme.variantDuration(root.animationDuration, root.shouldBeVisible) * Theme.variantOpacityDurationScale)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
@@ -232,12 +271,13 @@ Item {
|
|||||||
|
|
||||||
WindowBlur {
|
WindowBlur {
|
||||||
targetWindow: contentWindow
|
targetWindow: contentWindow
|
||||||
|
blurEnabled: root.effectiveBlurEnabled
|
||||||
readonly property real s: Math.min(1, modalContainer.scaleValue)
|
readonly property real s: Math.min(1, modalContainer.scaleValue)
|
||||||
blurX: modalContainer.x + modalContainer.width * (1 - s) * 0.5 + Theme.snap(modalContainer.animX, root.dpr)
|
blurX: modalContainer.x + modalContainer.width * (1 - s) * 0.5 + Theme.snap(modalContainer.animX, root.dpr)
|
||||||
blurY: modalContainer.y + modalContainer.height * (1 - s) * 0.5 + Theme.snap(modalContainer.animY, root.dpr)
|
blurY: modalContainer.y + modalContainer.height * (1 - s) * 0.5 + Theme.snap(modalContainer.animY, root.dpr)
|
||||||
blurWidth: (shouldBeVisible && animatedContent.opacity > 0) ? modalContainer.width * s : 0
|
blurWidth: (root.shouldBeVisible && animatedContent.opacity > 0) ? modalContainer.width * s : 0
|
||||||
blurHeight: (shouldBeVisible && animatedContent.opacity > 0) ? modalContainer.height * s : 0
|
blurHeight: (root.shouldBeVisible && animatedContent.opacity > 0) ? modalContainer.height * s : 0
|
||||||
blurRadius: root.cornerRadius
|
blurRadius: root.effectiveCornerRadius
|
||||||
}
|
}
|
||||||
|
|
||||||
WlrLayershell.namespace: root.layerNamespace
|
WlrLayershell.namespace: root.layerNamespace
|
||||||
@@ -275,9 +315,12 @@ Item {
|
|||||||
bottom: root.useSingleWindow
|
bottom: root.useSingleWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly property real actualMarginLeft: root.useSingleWindow ? 0 : Math.max(0, Theme.snap(root.alignedX - shadowBuffer, dpr))
|
||||||
|
readonly property real actualMarginTop: root.useSingleWindow ? 0 : Math.max(0, Theme.snap(root.alignedY - shadowBuffer, dpr))
|
||||||
|
|
||||||
WlrLayershell.margins {
|
WlrLayershell.margins {
|
||||||
left: root.useSingleWindow ? 0 : Math.max(0, Theme.snap(root.alignedX - shadowBuffer, dpr))
|
left: actualMarginLeft
|
||||||
top: root.useSingleWindow ? 0 : Math.max(0, Theme.snap(root.alignedY - shadowBuffer, dpr))
|
top: actualMarginTop
|
||||||
right: 0
|
right: 0
|
||||||
bottom: 0
|
bottom: 0
|
||||||
}
|
}
|
||||||
@@ -307,13 +350,14 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
z: -1
|
z: -1
|
||||||
color: "black"
|
color: "black"
|
||||||
opacity: root.useBackground ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
|
opacity: (root.useSingleWindow && root.useBackground) ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
|
||||||
visible: root.useBackground
|
visible: opacity > 0
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
enabled: root.animationsEnabled
|
enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || Theme.isConnectedEffect)
|
||||||
DankAnim {
|
NumberAnimation {
|
||||||
duration: root.animationDuration
|
duration: Math.round(Theme.variantDuration(root.animationDuration, root.shouldBeVisible) * Theme.variantOpacityDurationScale)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,8 +365,8 @@ Item {
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: modalContainer
|
id: modalContainer
|
||||||
x: root.useSingleWindow ? root.alignedX : shadowBuffer
|
x: (root.useSingleWindow ? root.alignedX : (root.alignedX - contentWindow.actualMarginLeft)) + Theme.snap(animX, root.dpr)
|
||||||
y: root.useSingleWindow ? root.alignedY : shadowBuffer
|
y: (root.useSingleWindow ? root.alignedY : (root.alignedY - contentWindow.actualMarginTop)) + Theme.snap(animY, root.dpr)
|
||||||
|
|
||||||
width: root.alignedWidth
|
width: root.alignedWidth
|
||||||
height: root.alignedHeight
|
height: root.alignedHeight
|
||||||
@@ -338,45 +382,117 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readonly property bool slide: root.animationType === "slide"
|
readonly property bool slide: root.animationType === "slide"
|
||||||
readonly property real offsetX: slide ? 15 : 0
|
readonly property bool directionalEffect: Theme.isDirectionalEffect
|
||||||
readonly property real offsetY: slide ? -30 : root.animationOffset
|
readonly property bool depthEffect: Theme.isDepthEffect
|
||||||
|
readonly property real directionalTravel: Math.max(root.animationOffset, Math.max(root.alignedWidth, root.alignedHeight) * 0.8)
|
||||||
property real animX: 0
|
readonly property real depthTravel: Math.max(root.animationOffset * 0.8, 36)
|
||||||
property real animY: 0
|
readonly property real customAnchorX: root.alignedX + root.alignedWidth * 0.5
|
||||||
property real scaleValue: root.animationScaleCollapsed
|
readonly property real customAnchorY: root.alignedY + root.alignedHeight * 0.5
|
||||||
|
readonly property real customDistLeft: customAnchorX
|
||||||
onOffsetXChanged: animX = Theme.snap(root.shouldBeVisible ? 0 : offsetX, root.dpr)
|
readonly property real customDistRight: root.screenWidth - customAnchorX
|
||||||
onOffsetYChanged: animY = Theme.snap(root.shouldBeVisible ? 0 : offsetY, root.dpr)
|
readonly property real customDistTop: customAnchorY
|
||||||
|
readonly property real customDistBottom: root.screenHeight - customAnchorY
|
||||||
Connections {
|
readonly property real offsetX: {
|
||||||
target: root
|
if (typeof SettingsData !== "undefined" && SettingsData.directionalAnimationMode === 2 && Theme.isDirectionalEffect)
|
||||||
function onShouldBeVisibleChanged() {
|
return 0;
|
||||||
modalContainer.animX = Theme.snap(root.shouldBeVisible ? 0 : modalContainer.offsetX, root.dpr);
|
if (slide && !directionalEffect && !depthEffect)
|
||||||
modalContainer.animY = Theme.snap(root.shouldBeVisible ? 0 : modalContainer.offsetY, root.dpr);
|
return 15;
|
||||||
modalContainer.scaleValue = root.shouldBeVisible ? 1.0 : root.animationScaleCollapsed;
|
if (directionalEffect) {
|
||||||
|
switch (root.positioning) {
|
||||||
|
case "top-right":
|
||||||
|
return 0;
|
||||||
|
case "custom":
|
||||||
|
if (customDistLeft <= customDistRight && customDistLeft <= customDistTop && customDistLeft <= customDistBottom)
|
||||||
|
return -directionalTravel;
|
||||||
|
if (customDistRight <= customDistTop && customDistRight <= customDistBottom)
|
||||||
|
return directionalTravel;
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (depthEffect) {
|
||||||
|
switch (root.positioning) {
|
||||||
|
case "top-right":
|
||||||
|
return 0;
|
||||||
|
case "custom":
|
||||||
|
if (customDistLeft <= customDistRight && customDistLeft <= customDistTop && customDistLeft <= customDistBottom)
|
||||||
|
return -depthTravel;
|
||||||
|
if (customDistRight <= customDistTop && customDistRight <= customDistBottom)
|
||||||
|
return depthTravel;
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
readonly property real offsetY: {
|
||||||
|
if (typeof SettingsData !== "undefined" && SettingsData.directionalAnimationMode === 2 && Theme.isDirectionalEffect)
|
||||||
|
return 0;
|
||||||
|
if (slide && !directionalEffect && !depthEffect)
|
||||||
|
return -30;
|
||||||
|
if (directionalEffect) {
|
||||||
|
switch (root.positioning) {
|
||||||
|
case "top-right":
|
||||||
|
return -Math.max(directionalTravel * 0.65, 96);
|
||||||
|
case "custom":
|
||||||
|
if (customDistTop <= customDistBottom && customDistTop <= customDistLeft && customDistTop <= customDistRight)
|
||||||
|
return -directionalTravel;
|
||||||
|
if (customDistBottom <= customDistLeft && customDistBottom <= customDistRight)
|
||||||
|
return directionalTravel;
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
// Default to sliding down from top when centered
|
||||||
|
return -Math.max(directionalTravel, root.screenHeight * 0.24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (depthEffect) {
|
||||||
|
switch (root.positioning) {
|
||||||
|
case "top-right":
|
||||||
|
return -depthTravel * 0.75;
|
||||||
|
case "custom":
|
||||||
|
if (customDistTop <= customDistBottom && customDistTop <= customDistLeft && customDistTop <= customDistRight)
|
||||||
|
return -depthTravel;
|
||||||
|
if (customDistBottom <= customDistLeft && customDistBottom <= customDistRight)
|
||||||
|
return depthTravel;
|
||||||
|
return depthTravel * 0.45;
|
||||||
|
default:
|
||||||
|
return -depthTravel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root.animationOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
property real animX: root.shouldBeVisible ? 0 : root.frozenMotionOffsetX
|
||||||
|
property real animY: root.shouldBeVisible ? 0 : root.frozenMotionOffsetY
|
||||||
|
|
||||||
|
readonly property real computedScaleCollapsed: (typeof SettingsData !== "undefined" && SettingsData.directionalAnimationMode === 2 && Theme.isDirectionalEffect) ? 0.0 : root.animationScaleCollapsed
|
||||||
|
property real scaleValue: root.shouldBeVisible ? 1.0 : computedScaleCollapsed
|
||||||
|
|
||||||
Behavior on animX {
|
Behavior on animX {
|
||||||
enabled: root.animationsEnabled
|
enabled: root.animationsEnabled
|
||||||
DankAnim {
|
NumberAnimation {
|
||||||
duration: root.animationDuration
|
duration: Theme.variantDuration(root.animationDuration, root.shouldBeVisible)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on animY {
|
Behavior on animY {
|
||||||
enabled: root.animationsEnabled
|
enabled: root.animationsEnabled
|
||||||
DankAnim {
|
NumberAnimation {
|
||||||
duration: root.animationDuration
|
duration: Theme.variantDuration(root.animationDuration, root.shouldBeVisible)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on scaleValue {
|
Behavior on scaleValue {
|
||||||
enabled: root.animationsEnabled
|
enabled: root.animationsEnabled
|
||||||
DankAnim {
|
NumberAnimation {
|
||||||
duration: root.animationDuration
|
duration: Theme.variantDuration(root.animationDuration, root.shouldBeVisible)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,15 +508,14 @@ Item {
|
|||||||
id: animatedContent
|
id: animatedContent
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
clip: false
|
clip: false
|
||||||
opacity: root.shouldBeVisible ? 1 : 0
|
opacity: (Theme.isDirectionalEffect && !Theme.isConnectedEffect) ? 1 : (root.shouldBeVisible ? 1 : 0)
|
||||||
scale: modalContainer.scaleValue
|
scale: modalContainer.scaleValue
|
||||||
x: Theme.snap(modalContainer.animX, root.dpr) + (parent.width - width) * (1 - modalContainer.scaleValue) * 0.5
|
transformOrigin: Item.Center
|
||||||
y: Theme.snap(modalContainer.animY, root.dpr) + (parent.height - height) * (1 - modalContainer.scaleValue) * 0.5
|
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
enabled: root.animationsEnabled
|
enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || Theme.isConnectedEffect)
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: animationDuration
|
duration: Math.round(Theme.variantDuration(animationDuration, root.shouldBeVisible) * Theme.variantOpacityDurationScale)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
easing.bezierCurve: root.shouldBeVisible ? root.animationEnterCurve : root.animationExitCurve
|
||||||
}
|
}
|
||||||
@@ -411,19 +526,19 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
level: root.shadowLevel
|
level: root.shadowLevel
|
||||||
fallbackOffset: root.shadowFallbackOffset
|
fallbackOffset: root.shadowFallbackOffset
|
||||||
targetRadius: root.cornerRadius
|
targetRadius: root.effectiveCornerRadius
|
||||||
targetColor: root.backgroundColor
|
targetColor: root.effectiveBackgroundColor
|
||||||
borderColor: root.borderColor
|
borderColor: root.effectiveBorderColor
|
||||||
borderWidth: root.borderWidth
|
borderWidth: root.effectiveBorderWidth
|
||||||
shadowEnabled: root.enableShadow && Theme.elevationEnabled && SettingsData.modalElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
shadowEnabled: root.enableShadow && Theme.elevationEnabled && SettingsData.modalElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: root.cornerRadius
|
radius: root.effectiveCornerRadius
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
border.color: BlurService.borderColor
|
border.color: root.connectedSurfaceOverride ? "transparent" : BlurService.borderColor
|
||||||
border.width: BlurService.borderWidth
|
border.width: root.connectedSurfaceOverride ? 0 : BlurService.borderWidth
|
||||||
z: 100
|
z: 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
@@ -14,16 +15,24 @@ Item {
|
|||||||
property bool spotlightOpen: false
|
property bool spotlightOpen: false
|
||||||
property bool keyboardActive: false
|
property bool keyboardActive: false
|
||||||
property bool contentVisible: false
|
property bool contentVisible: false
|
||||||
|
readonly property bool launcherMotionVisible: Theme.isConnectedEffect ? _motionActive : (Theme.isDirectionalEffect ? spotlightOpen : _motionActive)
|
||||||
property var spotlightContent: launcherContentLoader.item
|
property var spotlightContent: launcherContentLoader.item
|
||||||
property bool openedFromOverview: false
|
property bool openedFromOverview: false
|
||||||
property bool isClosing: false
|
property bool isClosing: false
|
||||||
|
property bool _windowEnabled: true
|
||||||
property bool _pendingInitialize: false
|
property bool _pendingInitialize: false
|
||||||
property string _pendingQuery: ""
|
property string _pendingQuery: ""
|
||||||
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 _motionActive: false
|
||||||
|
property real _frozenMotionX: 0
|
||||||
|
property real _frozenMotionY: 0
|
||||||
|
|
||||||
readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
|
readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
|
||||||
readonly property var effectiveScreen: launcherWindow.screen
|
readonly property var effectiveScreen: contentWindow.screen
|
||||||
readonly property real screenWidth: effectiveScreen?.width ?? 1920
|
readonly property real screenWidth: effectiveScreen?.width ?? 1920
|
||||||
readonly property real screenHeight: effectiveScreen?.height ?? 1080
|
readonly property real screenHeight: effectiveScreen?.height ?? 1080
|
||||||
readonly property real dpr: effectiveScreen ? CompositorService.getScreenScale(effectiveScreen) : 1
|
readonly property real dpr: effectiveScreen ? CompositorService.getScreenScale(effectiveScreen) : 1
|
||||||
@@ -57,8 +66,12 @@ Item {
|
|||||||
readonly property real modalX: (screenWidth - modalWidth) / 2
|
readonly property real modalX: (screenWidth - modalWidth) / 2
|
||||||
readonly property real modalY: (screenHeight - modalHeight) / 2
|
readonly property real modalY: (screenHeight - modalHeight) / 2
|
||||||
|
|
||||||
readonly property color backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
readonly property bool connectedSurfaceOverride: Theme.isConnectedEffect
|
||||||
readonly property real cornerRadius: Theme.cornerRadius
|
readonly property int launcherAnimationDuration: Theme.isConnectedEffect ? Theme.popoutAnimationDuration : Theme.modalAnimationDuration
|
||||||
|
readonly property list<real> launcherEnterCurve: Theme.isConnectedEffect ? Theme.variantPopoutEnterCurve : Theme.variantModalEnterCurve
|
||||||
|
readonly property list<real> launcherExitCurve: Theme.isConnectedEffect ? Theme.variantPopoutExitCurve : Theme.variantModalExitCurve
|
||||||
|
readonly property color backgroundColor: connectedSurfaceOverride ? Theme.connectedSurfaceColor : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
|
readonly property real cornerRadius: connectedSurfaceOverride ? Theme.connectedSurfaceRadius : Theme.cornerRadius
|
||||||
readonly property color borderColor: {
|
readonly property color borderColor: {
|
||||||
if (!SettingsData.dankLauncherV2BorderEnabled)
|
if (!SettingsData.dankLauncherV2BorderEnabled)
|
||||||
return Theme.outlineMedium;
|
return Theme.outlineMedium;
|
||||||
@@ -76,6 +89,37 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
readonly property int borderWidth: SettingsData.dankLauncherV2BorderEnabled ? SettingsData.dankLauncherV2BorderThickness : 0
|
readonly property int borderWidth: SettingsData.dankLauncherV2BorderEnabled ? SettingsData.dankLauncherV2BorderThickness : 0
|
||||||
|
readonly property color effectiveBorderColor: connectedSurfaceOverride ? "transparent" : borderColor
|
||||||
|
readonly property int effectiveBorderWidth: connectedSurfaceOverride ? 0 : borderWidth
|
||||||
|
readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled
|
||||||
|
|
||||||
|
// Shadow padding for the content window (render padding only, no motion padding)
|
||||||
|
readonly property var shadowLevel: Theme.elevationLevel3
|
||||||
|
readonly property real shadowFallbackOffset: 6
|
||||||
|
readonly property real shadowRenderPadding: (Theme.elevationEnabled && SettingsData.modalElevationEnabled) ? Theme.elevationRenderPadding(shadowLevel, Theme.elevationLightDirection, shadowFallbackOffset, 8, 16) : 0
|
||||||
|
readonly property real shadowPad: Theme.snap(shadowRenderPadding, dpr)
|
||||||
|
readonly property real alignedWidth: Theme.px(modalWidth, dpr)
|
||||||
|
readonly property real alignedHeight: Theme.px(modalHeight, dpr)
|
||||||
|
readonly property real alignedX: Theme.snap(modalX, dpr)
|
||||||
|
readonly property real alignedY: Theme.snap(modalY, dpr)
|
||||||
|
|
||||||
|
// For directional/depth: window extends from screen top (content slides within)
|
||||||
|
// For standard: small window tightly around the modal + shadow padding
|
||||||
|
readonly property bool _needsExtendedWindow: (Theme.isDirectionalEffect && !Theme.isConnectedEffect) || Theme.isDepthEffect
|
||||||
|
// Content window geometry
|
||||||
|
readonly property real _cwMarginLeft: Theme.snap(alignedX - shadowPad, dpr)
|
||||||
|
readonly property real _cwMarginTop: _needsExtendedWindow ? 0 : Theme.snap(alignedY - shadowPad, dpr)
|
||||||
|
readonly property real _cwWidth: alignedWidth + shadowPad * 2
|
||||||
|
readonly property real _cwHeight: {
|
||||||
|
if (Theme.isDirectionalEffect && !Theme.isConnectedEffect)
|
||||||
|
return screenHeight + shadowPad;
|
||||||
|
if (Theme.isDepthEffect)
|
||||||
|
return alignedY + alignedHeight + shadowPad;
|
||||||
|
return alignedHeight + shadowPad * 2;
|
||||||
|
}
|
||||||
|
// Where the content container sits inside the content window
|
||||||
|
readonly property real _ccX: shadowPad
|
||||||
|
readonly property real _ccY: _needsExtendedWindow ? alignedY : shadowPad
|
||||||
|
|
||||||
signal dialogClosed
|
signal dialogClosed
|
||||||
|
|
||||||
@@ -96,18 +140,11 @@ Item {
|
|||||||
if (!spotlightContent)
|
if (!spotlightContent)
|
||||||
return;
|
return;
|
||||||
contentVisible = true;
|
contentVisible = true;
|
||||||
spotlightContent.searchField.forceActiveFocus();
|
// NOTE: forceActiveFocus() is deliberately NOT called here.
|
||||||
|
// It is deferred to after animation starts to avoid compositor IPC stalls.
|
||||||
var targetQuery = "";
|
|
||||||
|
|
||||||
if (query) {
|
|
||||||
targetQuery = query;
|
|
||||||
} else if (SettingsData.rememberLastQuery) {
|
|
||||||
targetQuery = SessionData.launcherLastQuery || "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spotlightContent.searchField) {
|
if (spotlightContent.searchField) {
|
||||||
spotlightContent.searchField.text = targetQuery;
|
spotlightContent.searchField.text = query;
|
||||||
}
|
}
|
||||||
if (spotlightContent.controller) {
|
if (spotlightContent.controller) {
|
||||||
var targetMode = mode || SessionData.launcherLastMode || "all";
|
var targetMode = mode || SessionData.launcherLastMode || "all";
|
||||||
@@ -122,10 +159,12 @@ Item {
|
|||||||
spotlightContent.controller.collapsedSections = {};
|
spotlightContent.controller.collapsedSections = {};
|
||||||
spotlightContent.controller.selectedFlatIndex = 0;
|
spotlightContent.controller.selectedFlatIndex = 0;
|
||||||
spotlightContent.controller.selectedItem = null;
|
spotlightContent.controller.selectedItem = null;
|
||||||
spotlightContent.controller.historyIndex = -1;
|
if (query) {
|
||||||
spotlightContent.controller.searchQuery = targetQuery;
|
spotlightContent.controller.setSearchQuery(query);
|
||||||
|
} else {
|
||||||
spotlightContent.controller.performSearch();
|
spotlightContent.controller.searchQuery = "";
|
||||||
|
spotlightContent.controller.performSearch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (spotlightContent.resetScroll) {
|
if (spotlightContent.resetScroll) {
|
||||||
spotlightContent.resetScroll();
|
spotlightContent.resetScroll();
|
||||||
@@ -135,47 +174,59 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _finishShow(query, mode) {
|
function _openCommon(query, mode) {
|
||||||
spotlightOpen = true;
|
closeCleanupTimer.stop();
|
||||||
isClosing = false;
|
isClosing = false;
|
||||||
openedFromOverview = false;
|
openedFromOverview = false;
|
||||||
|
|
||||||
keyboardActive = true;
|
// Disable animations so the snap is instant
|
||||||
|
animationsEnabled = false;
|
||||||
|
|
||||||
|
// Freeze the collapsed offsets (they depend on height which could change)
|
||||||
|
_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);
|
||||||
|
|
||||||
|
var focusedScreen = CompositorService.getFocusedScreen();
|
||||||
|
if (focusedScreen) {
|
||||||
|
backgroundWindow.screen = focusedScreen;
|
||||||
|
contentWindow.screen = focusedScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _motionActive = false ensures motionX/Y snap to frozen collapsed position
|
||||||
|
_motionActive = false;
|
||||||
|
|
||||||
|
// Make windows visible but do NOT request keyboard focus yet
|
||||||
ModalManager.openModal(root);
|
ModalManager.openModal(root);
|
||||||
|
spotlightOpen = true;
|
||||||
|
backgroundWindow.visible = true;
|
||||||
|
contentWindow.visible = true;
|
||||||
if (useHyprlandFocusGrab)
|
if (useHyprlandFocusGrab)
|
||||||
focusGrab.active = true;
|
focusGrab.active = 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
|
||||||
|
Qt.callLater(() => {
|
||||||
|
root.animationsEnabled = true;
|
||||||
|
root._motionActive = true;
|
||||||
|
|
||||||
|
// Frame 2: request keyboard focus + activate search field
|
||||||
|
// Double-deferred to avoid compositor IPC competing with animation frames
|
||||||
|
Qt.callLater(() => {
|
||||||
|
root.keyboardActive = true;
|
||||||
|
if (root.spotlightContent && root.spotlightContent.searchField)
|
||||||
|
root.spotlightContent.searchField.forceActiveFocus();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
closeCleanupTimer.stop();
|
_openCommon("", "");
|
||||||
|
|
||||||
var focusedScreen = CompositorService.getFocusedScreen();
|
|
||||||
if (focusedScreen && launcherWindow.screen !== focusedScreen) {
|
|
||||||
spotlightOpen = false;
|
|
||||||
isClosing = false;
|
|
||||||
launcherWindow.screen = focusedScreen;
|
|
||||||
Qt.callLater(() => root._finishShow("", ""));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_finishShow("", "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showWithQuery(query) {
|
function showWithQuery(query) {
|
||||||
closeCleanupTimer.stop();
|
_openCommon(query, "");
|
||||||
|
|
||||||
var focusedScreen = CompositorService.getFocusedScreen();
|
|
||||||
if (focusedScreen && launcherWindow.screen !== focusedScreen) {
|
|
||||||
spotlightOpen = false;
|
|
||||||
isClosing = false;
|
|
||||||
launcherWindow.screen = focusedScreen;
|
|
||||||
Qt.callLater(() => root._finishShow(query, ""));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_finishShow(query, "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
@@ -183,13 +234,17 @@ Item {
|
|||||||
return;
|
return;
|
||||||
openedFromOverview = false;
|
openedFromOverview = false;
|
||||||
isClosing = true;
|
isClosing = true;
|
||||||
contentVisible = false;
|
// For directional effects, defer contentVisible=false so content stays rendered during exit slide
|
||||||
|
if (!Theme.isDirectionalEffect)
|
||||||
|
contentVisible = false;
|
||||||
|
|
||||||
|
// Trigger exit animation — Behaviors will animate motionX/Y to frozen collapsed position
|
||||||
|
_motionActive = false;
|
||||||
|
|
||||||
keyboardActive = false;
|
keyboardActive = false;
|
||||||
spotlightOpen = false;
|
spotlightOpen = false;
|
||||||
focusGrab.active = false;
|
focusGrab.active = false;
|
||||||
ModalManager.closeModal(root);
|
ModalManager.closeModal(root);
|
||||||
|
|
||||||
closeCleanupTimer.start();
|
closeCleanupTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,27 +253,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showWithMode(mode) {
|
function showWithMode(mode) {
|
||||||
closeCleanupTimer.stop();
|
_openCommon("", mode);
|
||||||
|
|
||||||
var focusedScreen = CompositorService.getFocusedScreen();
|
|
||||||
if (focusedScreen && launcherWindow.screen !== focusedScreen) {
|
|
||||||
spotlightOpen = false;
|
|
||||||
isClosing = false;
|
|
||||||
launcherWindow.screen = focusedScreen;
|
|
||||||
Qt.callLater(() => root._finishShow("", mode));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
spotlightOpen = true;
|
|
||||||
isClosing = false;
|
|
||||||
openedFromOverview = false;
|
|
||||||
|
|
||||||
keyboardActive = true;
|
|
||||||
ModalManager.openModal(root);
|
|
||||||
if (useHyprlandFocusGrab)
|
|
||||||
focusGrab.active = true;
|
|
||||||
|
|
||||||
_ensureContentLoadedAndInitialize("", mode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleWithMode(mode) {
|
function toggleWithMode(mode) {
|
||||||
@@ -239,10 +274,13 @@ Item {
|
|||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: closeCleanupTimer
|
id: closeCleanupTimer
|
||||||
interval: Theme.modalAnimationDuration + 50
|
interval: Theme.variantCloseInterval(root.launcherAnimationDuration)
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
isClosing = false;
|
isClosing = false;
|
||||||
|
contentVisible = false;
|
||||||
|
contentWindow.visible = false;
|
||||||
|
backgroundWindow.visible = false;
|
||||||
if (root.unloadContentOnClose)
|
if (root.unloadContentOnClose)
|
||||||
launcherContentLoader.active = false;
|
launcherContentLoader.active = false;
|
||||||
dialogClosed();
|
dialogClosed();
|
||||||
@@ -251,7 +289,6 @@ Item {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: spotlightContent?.controller ?? null
|
target: spotlightContent?.controller ?? null
|
||||||
|
|
||||||
function onModeChanged(mode) {
|
function onModeChanged(mode) {
|
||||||
if (spotlightContent.controller.autoSwitchedToFiles)
|
if (spotlightContent.controller.autoSwitchedToFiles)
|
||||||
return;
|
return;
|
||||||
@@ -261,7 +298,7 @@ Item {
|
|||||||
|
|
||||||
HyprlandFocusGrab {
|
HyprlandFocusGrab {
|
||||||
id: focusGrab
|
id: focusGrab
|
||||||
windows: [launcherWindow]
|
windows: [contentWindow]
|
||||||
active: false
|
active: false
|
||||||
|
|
||||||
onCleared: {
|
onCleared: {
|
||||||
@@ -286,55 +323,53 @@ Item {
|
|||||||
if (Quickshell.screens.length === 0)
|
if (Quickshell.screens.length === 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const screenName = launcherWindow.screen?.name;
|
const screen = contentWindow.screen;
|
||||||
if (screenName) {
|
const screenName = screen?.name;
|
||||||
|
|
||||||
|
let needsReset = !screen || !screenName;
|
||||||
|
if (!needsReset) {
|
||||||
|
needsReset = true;
|
||||||
for (let i = 0; i < Quickshell.screens.length; i++) {
|
for (let i = 0; i < Quickshell.screens.length; i++) {
|
||||||
if (Quickshell.screens[i].name === screenName)
|
if (Quickshell.screens[i].name === screenName) {
|
||||||
return;
|
needsReset = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spotlightOpen)
|
if (!needsReset)
|
||||||
hide();
|
return;
|
||||||
|
|
||||||
const newScreen = CompositorService.getFocusedScreen() ?? Quickshell.screens[0];
|
const newScreen = CompositorService.getFocusedScreen() ?? Quickshell.screens[0];
|
||||||
if (newScreen)
|
if (!newScreen)
|
||||||
launcherWindow.screen = newScreen;
|
return;
|
||||||
|
|
||||||
|
root._windowEnabled = false;
|
||||||
|
backgroundWindow.screen = newScreen;
|
||||||
|
contentWindow.screen = newScreen;
|
||||||
|
Qt.callLater(() => {
|
||||||
|
root._windowEnabled = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Background window: fullscreen, handles darkening + click-to-dismiss ──
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: launcherWindow
|
id: backgroundWindow
|
||||||
visible: spotlightOpen || isClosing
|
visible: false
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
exclusionMode: ExclusionMode.Ignore
|
|
||||||
|
|
||||||
WindowBlur {
|
WlrLayershell.namespace: "dms:spotlight:bg"
|
||||||
targetWindow: launcherWindow
|
WlrLayershell.layer: WlrLayershell.Top
|
||||||
readonly property real s: Math.min(1, modalContainer.scale)
|
WlrLayershell.exclusiveZone: -1
|
||||||
blurX: root.modalX + root.modalWidth * (1 - s) * 0.5
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
blurY: root.modalY + root.modalHeight * (1 - s) * 0.5
|
|
||||||
blurWidth: (contentVisible && modalContainer.opacity > 0) ? root.modalWidth * s : 0
|
|
||||||
blurHeight: (contentVisible && modalContainer.opacity > 0) ? root.modalHeight * s : 0
|
|
||||||
blurRadius: root.cornerRadius
|
|
||||||
}
|
|
||||||
|
|
||||||
WlrLayershell.namespace: "dms:spotlight"
|
WlrLayershell.margins {
|
||||||
WlrLayershell.layer: {
|
top: contentContainer.dockTop ? contentContainer.dockThickness : (typeof SettingsData !== "undefined" && SettingsData.barPosition === 0 ? Theme.px(42, root.dpr) : 0)
|
||||||
switch (Quickshell.env("DMS_MODAL_LAYER")) {
|
bottom: contentContainer.dockBottom ? contentContainer.dockThickness : (typeof SettingsData !== "undefined" && SettingsData.barPosition === 1 ? Theme.px(42, root.dpr) : 0)
|
||||||
case "bottom":
|
left: contentContainer.dockLeft ? contentContainer.dockThickness : (typeof SettingsData !== "undefined" && SettingsData.barPosition === 2 ? Theme.px(42, root.dpr) : 0)
|
||||||
console.error("DankModal: 'bottom' layer is not valid for modals. Defaulting to 'top' layer.");
|
right: contentContainer.dockRight ? contentContainer.dockThickness : (typeof SettingsData !== "undefined" && SettingsData.barPosition === 3 ? Theme.px(42, root.dpr) : 0)
|
||||||
return WlrLayershell.Top;
|
|
||||||
case "background":
|
|
||||||
console.error("DankModal: 'background' layer is not valid for modals. Defaulting to 'top' layer.");
|
|
||||||
return WlrLayershell.Top;
|
|
||||||
case "overlay":
|
|
||||||
return WlrLayershell.Overlay;
|
|
||||||
default:
|
|
||||||
return WlrLayershell.Top;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
WlrLayershell.keyboardFocus: keyboardActive ? (root.useHyprlandFocusGrab ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.Exclusive) : WlrKeyboardFocus.None
|
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: true
|
top: true
|
||||||
@@ -344,11 +379,11 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mask: Region {
|
mask: Region {
|
||||||
item: spotlightOpen ? fullScreenMask : null
|
item: (spotlightOpen || isClosing) ? bgFullScreenMask : null
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: fullScreenMask
|
id: bgFullScreenMask
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,13 +391,14 @@ Item {
|
|||||||
id: backgroundDarken
|
id: backgroundDarken
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "black"
|
color: "black"
|
||||||
opacity: contentVisible && SettingsData.modalDarkenBackground ? 0.5 : 0
|
opacity: launcherMotionVisible && SettingsData.modalDarkenBackground ? 0.5 : 0
|
||||||
visible: contentVisible || opacity > 0
|
visible: launcherMotionVisible || opacity > 0
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
|
enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || Theme.isConnectedEffect)
|
||||||
DankAnim {
|
DankAnim {
|
||||||
duration: Theme.modalAnimationDuration
|
duration: Math.round(Theme.variantDuration(root.launcherAnimationDuration, launcherMotionVisible) * Theme.variantOpacityDurationScale)
|
||||||
easing.bezierCurve: contentVisible ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
|
easing.bezierCurve: launcherMotionVisible ? root.launcherEnterCurve : root.launcherExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,96 +406,236 @@ Item {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: spotlightOpen
|
enabled: spotlightOpen
|
||||||
onClicked: mouse => {
|
onClicked: root.hide()
|
||||||
var contentX = modalContainer.x;
|
}
|
||||||
var contentY = modalContainer.y;
|
}
|
||||||
var contentW = modalContainer.width;
|
|
||||||
var contentH = modalContainer.height;
|
|
||||||
|
|
||||||
if (mouse.x < contentX || mouse.x > contentX + contentW || mouse.y < contentY || mouse.y > contentY + contentH) {
|
// ── Content window: SMALL, positioned with margins — only renders the modal area ──
|
||||||
root.hide();
|
PanelWindow {
|
||||||
}
|
id: contentWindow
|
||||||
|
visible: false
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
WindowBlur {
|
||||||
|
targetWindow: contentWindow
|
||||||
|
blurEnabled: root.effectiveBlurEnabled
|
||||||
|
readonly property real s: Math.min(1, contentContainer.scaleValue)
|
||||||
|
blurX: root._ccX + root.alignedWidth * (1 - s) * 0.5 + Theme.snap(contentContainer.animX, root.dpr)
|
||||||
|
blurY: root._ccY + root.alignedHeight * (1 - s) * 0.5 + Theme.snap(contentContainer.animY, root.dpr)
|
||||||
|
blurWidth: (root.spotlightOpen || root.isClosing) && contentWrapper.opacity > 0 ? root.alignedWidth * s : 0
|
||||||
|
blurHeight: (root.spotlightOpen || root.isClosing) && contentWrapper.opacity > 0 ? root.alignedHeight * s : 0
|
||||||
|
blurRadius: root.cornerRadius
|
||||||
|
}
|
||||||
|
|
||||||
|
WlrLayershell.namespace: "dms:spotlight"
|
||||||
|
WlrLayershell.layer: {
|
||||||
|
switch (Quickshell.env("DMS_MODAL_LAYER")) {
|
||||||
|
case "bottom":
|
||||||
|
console.error("DankLauncherV2Modal: 'bottom' layer is not valid for modals. Defaulting to 'top' layer.");
|
||||||
|
return WlrLayershell.Top;
|
||||||
|
case "background":
|
||||||
|
console.error("DankLauncherV2Modal: 'background' layer is not valid for modals. Defaulting to 'top' layer.");
|
||||||
|
return WlrLayershell.Top;
|
||||||
|
case "overlay":
|
||||||
|
return WlrLayershell.Overlay;
|
||||||
|
default:
|
||||||
|
return WlrLayershell.Top;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
WlrLayershell.exclusiveZone: -1
|
||||||
|
WlrLayershell.keyboardFocus: keyboardActive ? (root.useHyprlandFocusGrab ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.Exclusive) : WlrKeyboardFocus.None
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: true
|
||||||
|
top: true
|
||||||
|
}
|
||||||
|
|
||||||
|
WlrLayershell.margins {
|
||||||
|
left: root._cwMarginLeft
|
||||||
|
top: root._cwMarginTop
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitWidth: root._cwWidth
|
||||||
|
implicitHeight: root._cwHeight
|
||||||
|
|
||||||
|
mask: Region {
|
||||||
|
item: contentInputMask
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: modalContainer
|
id: contentInputMask
|
||||||
x: root.modalX
|
visible: false
|
||||||
y: root.modalY
|
x: contentContainer.x + contentWrapper.x
|
||||||
width: root.modalWidth
|
y: contentContainer.y + contentWrapper.y
|
||||||
height: root.modalHeight
|
width: root.alignedWidth
|
||||||
visible: contentVisible || opacity > 0
|
height: root.alignedHeight
|
||||||
|
|
||||||
opacity: contentVisible ? 1 : 0
|
|
||||||
scale: contentVisible ? 1 : 0.96
|
|
||||||
transformOrigin: Item.Center
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
DankAnim {
|
|
||||||
duration: Theme.modalAnimationDuration
|
|
||||||
easing.bezierCurve: contentVisible ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on scale {
|
|
||||||
DankAnim {
|
|
||||||
duration: Theme.modalAnimationDuration
|
|
||||||
easing.bezierCurve: contentVisible ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ElevationShadow {
|
|
||||||
id: launcherShadowLayer
|
|
||||||
anchors.fill: parent
|
|
||||||
level: Theme.elevationLevel3
|
|
||||||
fallbackOffset: 6
|
|
||||||
targetColor: root.backgroundColor
|
|
||||||
borderColor: root.borderColor
|
|
||||||
borderWidth: root.borderWidth
|
|
||||||
targetRadius: root.cornerRadius
|
|
||||||
shadowEnabled: Theme.elevationEnabled && SettingsData.modalElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onPressed: mouse => mouse.accepted = true
|
|
||||||
}
|
|
||||||
|
|
||||||
FocusScope {
|
|
||||||
anchors.fill: parent
|
|
||||||
focus: keyboardActive
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: launcherContentLoader
|
|
||||||
anchors.fill: parent
|
|
||||||
active: !root.unloadContentOnClose || root.spotlightOpen || root.isClosing || root.contentVisible || root._pendingInitialize
|
|
||||||
asynchronous: false
|
|
||||||
sourceComponent: LauncherContent {
|
|
||||||
focus: true
|
|
||||||
parentModal: root
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoaded: {
|
|
||||||
if (root._pendingInitialize) {
|
|
||||||
root._initializeAndShow(root._pendingQuery, root._pendingMode);
|
|
||||||
root._pendingInitialize = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEscapePressed: event => {
|
|
||||||
root.hide();
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: root.cornerRadius
|
|
||||||
color: "transparent"
|
|
||||||
border.color: BlurService.borderColor
|
|
||||||
border.width: BlurService.borderWidth
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
Item {
|
||||||
|
id: contentContainer
|
||||||
|
|
||||||
|
// For directional/depth: contentContainer is at alignedY from window top (window starts at screen top)
|
||||||
|
// For standard: contentContainer is at shadowPad from window top (window starts near modal)
|
||||||
|
x: root._ccX
|
||||||
|
y: root._ccY
|
||||||
|
width: root.alignedWidth
|
||||||
|
height: root.alignedHeight
|
||||||
|
|
||||||
|
readonly property int dockEdge: typeof SettingsData !== "undefined" ? SettingsData.dockPosition : 1
|
||||||
|
readonly property bool dockTop: dockEdge === 0
|
||||||
|
readonly property bool dockBottom: dockEdge === 1
|
||||||
|
readonly property bool dockLeft: dockEdge === 2
|
||||||
|
readonly property bool dockRight: dockEdge === 3
|
||||||
|
|
||||||
|
readonly property real dockThickness: typeof SettingsData !== "undefined" && SettingsData.showDock ? Theme.px(SettingsData.dockIconSize + (SettingsData.dockMargin * 2) + SettingsData.dockSpacing + 8, root.dpr) : Theme.px(60, root.dpr)
|
||||||
|
|
||||||
|
readonly property bool directionalEffect: Theme.isDirectionalEffect
|
||||||
|
readonly property bool depthEffect: Theme.isDepthEffect
|
||||||
|
readonly property real collapsedMotionX: {
|
||||||
|
if (directionalEffect) {
|
||||||
|
if (dockLeft)
|
||||||
|
return -(root._ccX + root.alignedWidth + Theme.effectAnimOffset);
|
||||||
|
if (dockRight)
|
||||||
|
return root.screenWidth - root._ccX + Theme.effectAnimOffset;
|
||||||
|
}
|
||||||
|
if (depthEffect)
|
||||||
|
return Theme.effectAnimOffset * 0.25;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
readonly property real collapsedMotionY: {
|
||||||
|
if (directionalEffect) {
|
||||||
|
if (dockTop)
|
||||||
|
return -(root._ccY + root.alignedHeight + Theme.effectAnimOffset);
|
||||||
|
if (dockBottom)
|
||||||
|
return root.screenHeight - root._ccY + root.shadowPad + Theme.effectAnimOffset;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (depthEffect)
|
||||||
|
return -Math.max(Theme.effectAnimOffset * 0.85, 34);
|
||||||
|
return -Math.max((root.shadowPad || 0) + Theme.effectAnimOffset, 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declarative bindings — snap applied at render layer (contentWrapper x/y)
|
||||||
|
property real animX: root._motionActive ? 0 : root._frozenMotionX
|
||||||
|
property real animY: root._motionActive ? 0 : root._frozenMotionY
|
||||||
|
property real scaleValue: root._motionActive ? 1.0 : (Theme.isDirectionalEffect && typeof SettingsData !== "undefined" && SettingsData.directionalAnimationMode === 2 ? Theme.effectScaleCollapsed : (Theme.isDirectionalEffect ? 1 : Theme.effectScaleCollapsed))
|
||||||
|
|
||||||
|
Behavior on animX {
|
||||||
|
enabled: root.animationsEnabled
|
||||||
|
DankAnim {
|
||||||
|
duration: Theme.variantDuration(root.launcherAnimationDuration, root._motionActive)
|
||||||
|
easing.bezierCurve: root._motionActive ? root.launcherEnterCurve : root.launcherExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on animY {
|
||||||
|
enabled: root.animationsEnabled
|
||||||
|
DankAnim {
|
||||||
|
duration: Theme.variantDuration(root.launcherAnimationDuration, root._motionActive)
|
||||||
|
easing.bezierCurve: root._motionActive ? root.launcherEnterCurve : root.launcherExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scaleValue {
|
||||||
|
enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || (typeof SettingsData !== "undefined" && SettingsData.directionalAnimationMode === 2))
|
||||||
|
DankAnim {
|
||||||
|
duration: Theme.variantDuration(root.launcherAnimationDuration, root._motionActive)
|
||||||
|
easing.bezierCurve: root._motionActive ? root.launcherEnterCurve : root.launcherExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: directionalClipMask
|
||||||
|
readonly property bool shouldClip: Theme.isDirectionalEffect && typeof SettingsData !== "undefined" && SettingsData.directionalAnimationMode > 0
|
||||||
|
readonly property real clipOversize: 2000
|
||||||
|
|
||||||
|
clip: shouldClip
|
||||||
|
|
||||||
|
x: shouldClip ? (contentContainer.dockRight ? -clipOversize : (contentContainer.dockLeft ? contentContainer.dockThickness - root._ccX : -clipOversize)) : 0
|
||||||
|
y: shouldClip ? (contentContainer.dockBottom ? -clipOversize : (contentContainer.dockTop ? contentContainer.dockThickness - root._ccY : -clipOversize)) : 0
|
||||||
|
|
||||||
|
width: shouldClip ? parent.width + clipOversize + (contentContainer.dockRight ? (root.screenWidth - contentContainer.dockThickness - root._ccX - parent.width) : (contentContainer.dockLeft ? clipOversize : clipOversize)) : parent.width
|
||||||
|
height: shouldClip ? parent.height + clipOversize + (contentContainer.dockBottom ? (root.screenHeight - contentContainer.dockThickness - root._ccY - parent.height) : (contentContainer.dockTop ? clipOversize : clipOversize)) : parent.height
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: aligner
|
||||||
|
x: directionalClipMask.x !== 0 ? -directionalClipMask.x : 0
|
||||||
|
y: directionalClipMask.y !== 0 ? -directionalClipMask.y : 0
|
||||||
|
width: contentContainer.width
|
||||||
|
height: contentContainer.height
|
||||||
|
|
||||||
|
// Shadow mirrors contentWrapper position/scale/opacity
|
||||||
|
ElevationShadow {
|
||||||
|
id: launcherShadowLayer
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
opacity: contentWrapper.opacity
|
||||||
|
scale: contentWrapper.scale
|
||||||
|
x: contentWrapper.x
|
||||||
|
y: contentWrapper.y
|
||||||
|
level: root.shadowLevel
|
||||||
|
fallbackOffset: root.shadowFallbackOffset
|
||||||
|
targetColor: root.backgroundColor
|
||||||
|
borderColor: root.effectiveBorderColor
|
||||||
|
borderWidth: root.effectiveBorderWidth
|
||||||
|
targetRadius: root.cornerRadius
|
||||||
|
shadowEnabled: Theme.elevationEnabled && SettingsData.modalElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
// contentWrapper moves inside static contentContainer — DankPopout pattern
|
||||||
|
Item {
|
||||||
|
id: contentWrapper
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
opacity: (Theme.isDirectionalEffect && !Theme.isConnectedEffect) ? 1 : (launcherMotionVisible ? 1 : 0)
|
||||||
|
visible: opacity > 0
|
||||||
|
scale: contentContainer.scaleValue
|
||||||
|
x: Theme.snap(contentContainer.animX + (parent.width - width) * (1 - contentContainer.scaleValue) * 0.5, root.dpr)
|
||||||
|
y: Theme.snap(contentContainer.animY + (parent.height - height) * (1 - contentContainer.scaleValue) * 0.5, root.dpr)
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
enabled: root.animationsEnabled && (!Theme.isDirectionalEffect || Theme.isConnectedEffect)
|
||||||
|
DankAnim {
|
||||||
|
duration: Math.round(Theme.variantDuration(root.launcherAnimationDuration, launcherMotionVisible) * Theme.variantOpacityDurationScale)
|
||||||
|
easing.bezierCurve: launcherMotionVisible ? root.launcherEnterCurve : root.launcherExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: mouse => mouse.accepted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusScope {
|
||||||
|
anchors.fill: parent
|
||||||
|
focus: keyboardActive
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: launcherContentLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
active: !root.unloadContentOnClose || root.spotlightOpen || root.isClosing || root.contentVisible || root._pendingInitialize
|
||||||
|
asynchronous: false
|
||||||
|
sourceComponent: LauncherContent {
|
||||||
|
focus: true
|
||||||
|
parentModal: root
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoaded: {
|
||||||
|
if (root._pendingInitialize) {
|
||||||
|
root._initializeAndShow(root._pendingQuery, root._pendingMode);
|
||||||
|
root._pendingInitialize = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEscapePressed: event => {
|
||||||
|
root.hide();
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // contentWrapper
|
||||||
|
} // aligner
|
||||||
|
} // directionalClipMask
|
||||||
|
} // contentContainer
|
||||||
|
} // PanelWindow
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ FocusScope {
|
|||||||
editCommentField.text = existing?.comment || "";
|
editCommentField.text = existing?.comment || "";
|
||||||
editEnvVarsField.text = existing?.envVars || "";
|
editEnvVarsField.text = existing?.envVars || "";
|
||||||
editExtraFlagsField.text = existing?.extraFlags || "";
|
editExtraFlagsField.text = existing?.extraFlags || "";
|
||||||
editDgpuToggle.checked = existing?.launchOnDgpu || false;
|
|
||||||
editMode = true;
|
editMode = true;
|
||||||
Qt.callLater(() => editNameField.forceActiveFocus());
|
Qt.callLater(() => editNameField.forceActiveFocus());
|
||||||
}
|
}
|
||||||
@@ -65,8 +64,6 @@ FocusScope {
|
|||||||
override.envVars = editEnvVarsField.text.trim();
|
override.envVars = editEnvVarsField.text.trim();
|
||||||
if (editExtraFlagsField.text.trim())
|
if (editExtraFlagsField.text.trim())
|
||||||
override.extraFlags = editExtraFlagsField.text.trim();
|
override.extraFlags = editExtraFlagsField.text.trim();
|
||||||
if (editDgpuToggle.checked)
|
|
||||||
override.launchOnDgpu = true;
|
|
||||||
SessionData.setAppOverride(editAppId, override);
|
SessionData.setAppOverride(editAppId, override);
|
||||||
closeEditMode();
|
closeEditMode();
|
||||||
}
|
}
|
||||||
@@ -89,7 +86,7 @@ FocusScope {
|
|||||||
|
|
||||||
Controller {
|
Controller {
|
||||||
id: controller
|
id: controller
|
||||||
active: root.parentModal?.spotlightOpen ?? true
|
active: root.parentModal ? (root.parentModal.spotlightOpen || root.parentModal.isClosing) : true
|
||||||
viewModeContext: root.viewModeContext
|
viewModeContext: root.viewModeContext
|
||||||
|
|
||||||
onItemExecuted: {
|
onItemExecuted: {
|
||||||
@@ -149,18 +146,10 @@ FocusScope {
|
|||||||
event.accepted = false;
|
event.accepted = false;
|
||||||
return;
|
return;
|
||||||
case Qt.Key_Down:
|
case Qt.Key_Down:
|
||||||
if (hasCtrl) {
|
controller.selectNext();
|
||||||
controller.navigateHistory("down");
|
|
||||||
} else {
|
|
||||||
controller.selectNext();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
case Qt.Key_Up:
|
case Qt.Key_Up:
|
||||||
if (hasCtrl) {
|
controller.selectPrevious();
|
||||||
controller.navigateHistory("up");
|
|
||||||
} else {
|
|
||||||
controller.selectPrevious();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
case Qt.Key_PageDown:
|
case Qt.Key_PageDown:
|
||||||
controller.selectPageDown(8);
|
controller.selectPageDown(8);
|
||||||
@@ -169,10 +158,6 @@ FocusScope {
|
|||||||
controller.selectPageUp(8);
|
controller.selectPageUp(8);
|
||||||
return;
|
return;
|
||||||
case Qt.Key_Right:
|
case Qt.Key_Right:
|
||||||
if (hasCtrl) {
|
|
||||||
controller.cycleMode();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (controller.getCurrentSectionViewMode() !== "list") {
|
if (controller.getCurrentSectionViewMode() !== "list") {
|
||||||
controller.selectRight();
|
controller.selectRight();
|
||||||
return;
|
return;
|
||||||
@@ -180,25 +165,12 @@ FocusScope {
|
|||||||
event.accepted = false;
|
event.accepted = false;
|
||||||
return;
|
return;
|
||||||
case Qt.Key_Left:
|
case Qt.Key_Left:
|
||||||
if (hasCtrl) {
|
|
||||||
const reverse = true;
|
|
||||||
controller.cycleMode(reverse);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (controller.getCurrentSectionViewMode() !== "list") {
|
if (controller.getCurrentSectionViewMode() !== "list") {
|
||||||
controller.selectLeft();
|
controller.selectLeft();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
event.accepted = false;
|
event.accepted = false;
|
||||||
return;
|
return;
|
||||||
case Qt.Key_H:
|
|
||||||
if (hasCtrl) {
|
|
||||||
const reverse = true;
|
|
||||||
controller.cycleMode(reverse);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.accepted = false;
|
|
||||||
return;
|
|
||||||
case Qt.Key_J:
|
case Qt.Key_J:
|
||||||
if (hasCtrl) {
|
if (hasCtrl) {
|
||||||
controller.selectNext();
|
controller.selectNext();
|
||||||
@@ -213,13 +185,6 @@ FocusScope {
|
|||||||
}
|
}
|
||||||
event.accepted = false;
|
event.accepted = false;
|
||||||
return;
|
return;
|
||||||
case Qt.Key_L:
|
|
||||||
if (hasCtrl) {
|
|
||||||
controller.cycleMode();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.accepted = false;
|
|
||||||
return;
|
|
||||||
case Qt.Key_N:
|
case Qt.Key_N:
|
||||||
if (hasCtrl) {
|
if (hasCtrl) {
|
||||||
controller.selectNextSection();
|
controller.selectNextSection();
|
||||||
@@ -235,19 +200,13 @@ FocusScope {
|
|||||||
event.accepted = false;
|
event.accepted = false;
|
||||||
return;
|
return;
|
||||||
case Qt.Key_Tab:
|
case Qt.Key_Tab:
|
||||||
if (hasCtrl && actionPanel.hasActions) {
|
if (actionPanel.hasActions) {
|
||||||
actionPanel.expanded ? actionPanel.cycleAction() : actionPanel.show();
|
actionPanel.expanded ? actionPanel.cycleAction() : actionPanel.show();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
controller.selectNext();
|
|
||||||
return;
|
return;
|
||||||
case Qt.Key_Backtab:
|
case Qt.Key_Backtab:
|
||||||
if (hasCtrl && actionPanel.expanded) {
|
if (actionPanel.expanded)
|
||||||
const reverse = true;
|
actionPanel.hide();
|
||||||
actionPanel.expanded ? actionPanel.cycleAction(reverse) : actionPanel.show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
controller.selectPrevious();
|
|
||||||
return;
|
return;
|
||||||
case Qt.Key_Return:
|
case Qt.Key_Return:
|
||||||
case Qt.Key_Enter:
|
case Qt.Key_Enter:
|
||||||
@@ -311,7 +270,7 @@ FocusScope {
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: !editMode && !(root.parentModal?.isClosing ?? false)
|
visible: !editMode
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: footerBar
|
id: footerBar
|
||||||
@@ -429,7 +388,7 @@ FocusScope {
|
|||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: "Ctrl-Tab " + I18n.tr("actions")
|
text: "Tab " + I18n.tr("actions")
|
||||||
font.pixelSize: Theme.fontSizeSmall - 1
|
font.pixelSize: Theme.fontSizeSmall - 1
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
visible: actionPanel.hasActions
|
visible: actionPanel.hasActions
|
||||||
@@ -503,7 +462,7 @@ FocusScope {
|
|||||||
showClearButton: true
|
showClearButton: true
|
||||||
textColor: Theme.surfaceText
|
textColor: Theme.surfaceText
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
enabled: root.parentModal ? root.parentModal.spotlightOpen : true
|
enabled: root.parentModal ? (root.parentModal.spotlightOpen || root.parentModal.isClosing) : true
|
||||||
placeholderText: ""
|
placeholderText: ""
|
||||||
ignoreUpDownKeys: true
|
ignoreUpDownKeys: true
|
||||||
ignoreTabKeys: true
|
ignoreTabKeys: true
|
||||||
@@ -737,6 +696,14 @@ FocusScope {
|
|||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - searchField.height - categoryRow.height - fileFilterRow.height - actionPanel.height - Theme.spacingXS * ((categoryRow.visible ? 1 : 0) + (fileFilterRow.visible ? 1 : 0) + 2)
|
height: parent.height - searchField.height - categoryRow.height - fileFilterRow.height - actionPanel.height - Theme.spacingXS * ((categoryRow.visible ? 1 : 0) + (fileFilterRow.visible ? 1 : 0) + 2)
|
||||||
|
opacity: {
|
||||||
|
if (!root.parentModal)
|
||||||
|
return 1;
|
||||||
|
if (Theme.isDirectionalEffect && root.parentModal.isClosing)
|
||||||
|
return 1;
|
||||||
|
return root.parentModal.isClosing ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
ResultsList {
|
ResultsList {
|
||||||
id: resultsList
|
id: resultsList
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -769,7 +736,6 @@ FocusScope {
|
|||||||
}
|
}
|
||||||
function onSearchQueryRequested(query) {
|
function onSearchQueryRequested(query) {
|
||||||
searchField.text = query;
|
searchField.text = query;
|
||||||
searchField.cursorPosition = query.length;
|
|
||||||
}
|
}
|
||||||
function onModeChanged() {
|
function onModeChanged() {
|
||||||
extFilterField.text = "";
|
extFilterField.text = "";
|
||||||
@@ -980,15 +946,6 @@ FocusScope {
|
|||||||
keyNavigationBacktab: editEnvVarsField
|
keyNavigationBacktab: editEnvVarsField
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DankToggle {
|
|
||||||
id: editDgpuToggle
|
|
||||||
width: parent.width
|
|
||||||
text: I18n.tr("Launch on dGPU by default")
|
|
||||||
visible: SessionService.nvidiaCommand.length > 0
|
|
||||||
checked: false
|
|
||||||
onToggled: checked => editDgpuToggle.checked = checked
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -518,5 +518,20 @@ FocusScope {
|
|||||||
Qt.callLater(() => item.forceActiveFocus());
|
Qt.callLater(() => item.forceActiveFocus());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: frameLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
active: root.currentIndex === 33
|
||||||
|
visible: active
|
||||||
|
focus: active
|
||||||
|
|
||||||
|
sourceComponent: FrameTab {}
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (active && item)
|
||||||
|
Qt.callLater(() => item.forceActiveFocus());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,6 +120,12 @@ Rectangle {
|
|||||||
"text": I18n.tr("Widgets"),
|
"text": I18n.tr("Widgets"),
|
||||||
"icon": "widgets",
|
"icon": "widgets",
|
||||||
"tabIndex": 22
|
"tabIndex": 22
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "frame",
|
||||||
|
"text": I18n.tr("Frame"),
|
||||||
|
"icon": "frame_source",
|
||||||
|
"tabIndex": 33
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ DankPopout {
|
|||||||
|
|
||||||
layerNamespace: "dms:app-launcher"
|
layerNamespace: "dms:app-launcher"
|
||||||
|
|
||||||
readonly property real screenWidth: screen?.width ?? 1920
|
|
||||||
readonly property real screenHeight: screen?.height ?? 1080
|
|
||||||
|
|
||||||
property string _pendingMode: ""
|
property string _pendingMode: ""
|
||||||
property string _pendingQuery: ""
|
property string _pendingQuery: ""
|
||||||
|
|
||||||
@@ -44,35 +41,8 @@ DankPopout {
|
|||||||
openWithQuery(query);
|
openWithQuery(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property int _baseWidth: {
|
popupWidth: 560
|
||||||
switch (SettingsData.dankLauncherV2Size) {
|
popupHeight: 640
|
||||||
case "micro":
|
|
||||||
return 500;
|
|
||||||
case "medium":
|
|
||||||
return 720;
|
|
||||||
case "large":
|
|
||||||
return 860;
|
|
||||||
default:
|
|
||||||
return 620;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property int _baseHeight: {
|
|
||||||
switch (SettingsData.dankLauncherV2Size) {
|
|
||||||
case "micro":
|
|
||||||
return 480;
|
|
||||||
case "medium":
|
|
||||||
return 720;
|
|
||||||
case "large":
|
|
||||||
return 860;
|
|
||||||
default:
|
|
||||||
return 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
popupWidth: Math.min(_baseWidth, screenWidth - 100)
|
|
||||||
popupHeight: Math.min(_baseHeight, screenHeight - 100)
|
|
||||||
|
|
||||||
triggerWidth: 40
|
triggerWidth: 40
|
||||||
positioning: ""
|
positioning: ""
|
||||||
contentHandlesKeys: contentLoader.item?.launcherContent?.editMode ?? false
|
contentHandlesKeys: contentLoader.item?.launcherContent?.editMode ?? false
|
||||||
@@ -90,7 +60,7 @@ DankPopout {
|
|||||||
if (!lc)
|
if (!lc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const query = _pendingQuery || (SettingsData.rememberLastQuery ? SessionData.launcherLastQuery : "") || "";
|
const query = _pendingQuery;
|
||||||
const mode = _pendingMode || SessionData.appDrawerLastMode || "apps";
|
const mode = _pendingMode || SessionData.appDrawerLastMode || "apps";
|
||||||
_pendingMode = "";
|
_pendingMode = "";
|
||||||
_pendingQuery = "";
|
_pendingQuery = "";
|
||||||
@@ -102,9 +72,12 @@ DankPopout {
|
|||||||
if (lc.controller) {
|
if (lc.controller) {
|
||||||
lc.controller.searchMode = mode;
|
lc.controller.searchMode = mode;
|
||||||
lc.controller.pluginFilter = "";
|
lc.controller.pluginFilter = "";
|
||||||
lc.controller.searchQuery = query;
|
lc.controller.searchQuery = "";
|
||||||
|
if (query) {
|
||||||
lc.controller.performSearch();
|
lc.controller.setSearchQuery(query);
|
||||||
|
} else {
|
||||||
|
lc.controller.performSearch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lc.resetScroll?.();
|
lc.resetScroll?.();
|
||||||
lc.actionPanel?.hide();
|
lc.actionPanel?.hide();
|
||||||
@@ -133,7 +106,7 @@ DankPopout {
|
|||||||
QtObject {
|
QtObject {
|
||||||
id: modalAdapter
|
id: modalAdapter
|
||||||
property bool spotlightOpen: appDrawerPopout.shouldBeVisible
|
property bool spotlightOpen: appDrawerPopout.shouldBeVisible
|
||||||
readonly property bool isClosing: !appDrawerPopout.shouldBeVisible
|
property bool isClosing: appDrawerPopout.isClosing
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
appDrawerPopout.close();
|
appDrawerPopout.close();
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ Item {
|
|||||||
Loader {
|
Loader {
|
||||||
id: pluginDetailLoader
|
id: pluginDetailLoader
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - Theme.spacingS
|
height: Math.max(0, parent.height - Theme.spacingS)
|
||||||
y: Theme.spacingS
|
y: Theme.spacingS
|
||||||
active: false
|
active: false
|
||||||
sourceComponent: null
|
sourceComponent: null
|
||||||
@@ -46,7 +46,7 @@ Item {
|
|||||||
Loader {
|
Loader {
|
||||||
id: coreDetailLoader
|
id: coreDetailLoader
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - Theme.spacingS
|
height: Math.max(0, parent.height - Theme.spacingS)
|
||||||
y: Theme.spacingS
|
y: Theme.spacingS
|
||||||
active: false
|
active: false
|
||||||
sourceComponent: null
|
sourceComponent: null
|
||||||
@@ -134,7 +134,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pluginDetailLoader.sourceComponent = builtinInstance.ccDetailContent;
|
pluginDetailLoader.sourceComponent = builtinInstance.ccDetailContent;
|
||||||
pluginDetailLoader.active = parent.height > 0;
|
pluginDetailLoader.active = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,19 +155,19 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pluginDetailLoader.sourceComponent = pluginDetailInstance.ccDetailContent;
|
pluginDetailLoader.sourceComponent = pluginDetailInstance.ccDetailContent;
|
||||||
pluginDetailLoader.active = parent.height > 0;
|
pluginDetailLoader.active = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.expandedSection.startsWith("diskUsage_")) {
|
if (root.expandedSection.startsWith("diskUsage_")) {
|
||||||
coreDetailLoader.sourceComponent = diskUsageDetailComponent;
|
coreDetailLoader.sourceComponent = diskUsageDetailComponent;
|
||||||
coreDetailLoader.active = parent.height > 0;
|
coreDetailLoader.active = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.expandedSection.startsWith("brightnessSlider_")) {
|
if (root.expandedSection.startsWith("brightnessSlider_")) {
|
||||||
coreDetailLoader.sourceComponent = brightnessDetailComponent;
|
coreDetailLoader.sourceComponent = brightnessDetailComponent;
|
||||||
coreDetailLoader.active = parent.height > 0;
|
coreDetailLoader.active = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ Item {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
coreDetailLoader.active = parent.height > 0;
|
coreDetailLoader.active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
|
|||||||
@@ -51,6 +51,35 @@ Column {
|
|||||||
return Math.max(100, maxPopoutHeight - totalRowHeight - rowSpacing);
|
return Math.max(100, maxPopoutHeight - totalRowHeight - rowSpacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly property real targetImplicitHeight: {
|
||||||
|
const rows = layoutResult.rows;
|
||||||
|
let totalHeight = 0;
|
||||||
|
for (let i = 0; i < rows.length; i++) {
|
||||||
|
const widgets = rows[i] || [];
|
||||||
|
const sliderOnly = widgets.length > 0 && widgets.every(w => {
|
||||||
|
const id = w.id || "";
|
||||||
|
return id === "volumeSlider" || id === "brightnessSlider" || id === "inputVolumeSlider";
|
||||||
|
});
|
||||||
|
totalHeight += sliderOnly ? (editMode ? 56 : 36) : 60;
|
||||||
|
if (expandedSection !== "" && i === expandedRowIndex)
|
||||||
|
totalHeight += detailHeightForSection(expandedSection) + Theme.spacingS;
|
||||||
|
}
|
||||||
|
totalHeight += Math.max(0, rows.length - 1) * spacing;
|
||||||
|
return totalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function detailHeightForSection(section) {
|
||||||
|
if (!section)
|
||||||
|
return 0;
|
||||||
|
if (section === "wifi" || section === "bluetooth" || section === "builtin_vpn")
|
||||||
|
return Math.min(350, _maxDetailHeight);
|
||||||
|
if (section.startsWith("brightnessSlider_"))
|
||||||
|
return Math.min(400, _maxDetailHeight);
|
||||||
|
if (section.startsWith("plugin_"))
|
||||||
|
return Math.min(250, _maxDetailHeight);
|
||||||
|
return Math.min(250, _maxDetailHeight);
|
||||||
|
}
|
||||||
|
|
||||||
function calculateRowsAndWidgets() {
|
function calculateRowsAndWidgets() {
|
||||||
return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex);
|
return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex);
|
||||||
}
|
}
|
||||||
@@ -179,7 +208,10 @@ Column {
|
|||||||
id: detailHost
|
id: detailHost
|
||||||
width: parent.width
|
width: parent.width
|
||||||
maxAvailableHeight: root._maxDetailHeight
|
maxAvailableHeight: root._maxDetailHeight
|
||||||
height: active ? (getDetailHeight(root.expandedSection) + Theme.spacingS) : 0
|
height: active ? (root.detailHeightForSection(root.expandedSection) + Theme.spacingS) : 0
|
||||||
|
clip: true
|
||||||
|
property string retainedSection: ""
|
||||||
|
property var retainedWidgetData: null
|
||||||
property bool active: {
|
property bool active: {
|
||||||
if (root.expandedSection === "")
|
if (root.expandedSection === "")
|
||||||
return false;
|
return false;
|
||||||
@@ -196,14 +228,47 @@ Column {
|
|||||||
|
|
||||||
return rowIndex === root.expandedRowIndex;
|
return rowIndex === root.expandedRowIndex;
|
||||||
}
|
}
|
||||||
visible: active
|
visible: active || height > 0.5
|
||||||
expandedSection: root.expandedSection
|
expandedSection: active ? root.expandedSection : retainedSection
|
||||||
expandedWidgetData: root.expandedWidgetData
|
expandedWidgetData: active ? root.expandedWidgetData : retainedWidgetData
|
||||||
bluetoothCodecSelector: root.bluetoothCodecSelector
|
bluetoothCodecSelector: root.bluetoothCodecSelector
|
||||||
widgetModel: root.model
|
widgetModel: root.model
|
||||||
collapseCallback: root.requestCollapse
|
collapseCallback: root.requestCollapse
|
||||||
screenName: root.screenName
|
screenName: root.screenName
|
||||||
screenModel: root.screenModel
|
screenModel: root.screenModel
|
||||||
|
|
||||||
|
function retainActiveDetail() {
|
||||||
|
if (!active || !root.expandedSection)
|
||||||
|
return;
|
||||||
|
retainedSection = root.expandedSection;
|
||||||
|
retainedWidgetData = root.expandedWidgetData;
|
||||||
|
}
|
||||||
|
|
||||||
|
onActiveChanged: retainActiveDetail()
|
||||||
|
onHeightChanged: {
|
||||||
|
if (!active && height <= 0.5) {
|
||||||
|
retainedSection = "";
|
||||||
|
retainedWidgetData = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onExpandedSectionChanged() {
|
||||||
|
detailHost.retainActiveDetail();
|
||||||
|
}
|
||||||
|
function onExpandedWidgetDataChanged() {
|
||||||
|
detailHost.retainActiveDetail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.variantDuration(Theme.popoutAnimationDuration, detailHost.active)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: detailHost.active ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,19 +20,53 @@ DankPopout {
|
|||||||
property int expandedWidgetIndex: -1
|
property int expandedWidgetIndex: -1
|
||||||
property var expandedWidgetData: null
|
property var expandedWidgetData: null
|
||||||
property bool powerMenuOpen: powerMenuModalLoader?.item?.shouldBeVisible ?? false
|
property bool powerMenuOpen: powerMenuModalLoader?.item?.shouldBeVisible ?? false
|
||||||
|
property real targetPopupHeight: 400
|
||||||
|
property bool _heightUpdatePending: false
|
||||||
|
|
||||||
signal lockRequested
|
signal lockRequested
|
||||||
|
|
||||||
|
function _maxPopupHeight() {
|
||||||
|
const screenHeight = (triggerScreen?.height ?? 1080);
|
||||||
|
return screenHeight - 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _contentTargetHeight() {
|
||||||
|
const item = contentLoader.item;
|
||||||
|
if (!item)
|
||||||
|
return 400;
|
||||||
|
const naturalHeight = item.targetImplicitHeight !== undefined ? item.targetImplicitHeight : item.implicitHeight;
|
||||||
|
return Math.max(300, naturalHeight + 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTargetPopupHeight() {
|
||||||
|
const target = Math.min(_maxPopupHeight(), _contentTargetHeight());
|
||||||
|
if (Math.abs(targetPopupHeight - target) < 0.5)
|
||||||
|
return;
|
||||||
|
targetPopupHeight = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueTargetPopupHeightUpdate() {
|
||||||
|
if (_heightUpdatePending)
|
||||||
|
return;
|
||||||
|
_heightUpdatePending = true;
|
||||||
|
Qt.callLater(() => {
|
||||||
|
_heightUpdatePending = false;
|
||||||
|
updateTargetPopupHeight();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function collapseAll() {
|
function collapseAll() {
|
||||||
expandedSection = "";
|
expandedSection = "";
|
||||||
expandedWidgetIndex = -1;
|
expandedWidgetIndex = -1;
|
||||||
expandedWidgetData = null;
|
expandedWidgetData = null;
|
||||||
|
queueTargetPopupHeightUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
onEditModeChanged: {
|
onEditModeChanged: {
|
||||||
if (editMode) {
|
if (editMode) {
|
||||||
collapseAll();
|
collapseAll();
|
||||||
}
|
}
|
||||||
|
queueTargetPopupHeightUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
@@ -52,12 +86,7 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
popupWidth: 550
|
popupWidth: 550
|
||||||
popupHeight: {
|
popupHeight: targetPopupHeight
|
||||||
const screenHeight = (triggerScreen?.height ?? 1080);
|
|
||||||
const maxHeight = screenHeight - 100;
|
|
||||||
const contentHeight = contentLoader.item && contentLoader.item.implicitHeight > 0 ? contentLoader.item.implicitHeight + 20 : 400;
|
|
||||||
return Math.min(maxHeight, contentHeight);
|
|
||||||
}
|
|
||||||
triggerWidth: 80
|
triggerWidth: 80
|
||||||
positioning: ""
|
positioning: ""
|
||||||
screen: triggerScreen
|
screen: triggerScreen
|
||||||
@@ -95,6 +124,7 @@ DankPopout {
|
|||||||
onShouldBeVisibleChanged: {
|
onShouldBeVisibleChanged: {
|
||||||
if (shouldBeVisible) {
|
if (shouldBeVisible) {
|
||||||
collapseAll();
|
collapseAll();
|
||||||
|
queueTargetPopupHeightUpdate();
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (NetworkService.activeService)
|
if (NetworkService.activeService)
|
||||||
NetworkService.activeService.autoRefreshEnabled = NetworkService.wifiEnabled;
|
NetworkService.activeService.autoRefreshEnabled = NetworkService.wifiEnabled;
|
||||||
@@ -111,6 +141,28 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onExpandedSectionChanged: queueTargetPopupHeightUpdate()
|
||||||
|
onExpandedWidgetIndexChanged: queueTargetPopupHeightUpdate()
|
||||||
|
onTriggerScreenChanged: queueTargetPopupHeightUpdate()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: contentLoader
|
||||||
|
function onLoaded() {
|
||||||
|
root.queueTargetPopupHeightUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: contentLoader.item
|
||||||
|
ignoreUnknownSignals: true
|
||||||
|
function onTargetImplicitHeightChanged() {
|
||||||
|
root.queueTargetPopupHeightUpdate();
|
||||||
|
}
|
||||||
|
function onImplicitHeightChanged() {
|
||||||
|
root.queueTargetPopupHeightUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WidgetModel {
|
WidgetModel {
|
||||||
id: widgetModel
|
id: widgetModel
|
||||||
}
|
}
|
||||||
@@ -122,7 +174,13 @@ DankPopout {
|
|||||||
LayoutMirroring.enabled: I18n.isRtl
|
LayoutMirroring.enabled: I18n.isRtl
|
||||||
LayoutMirroring.childrenInherit: true
|
LayoutMirroring.childrenInherit: true
|
||||||
|
|
||||||
implicitHeight: mainColumn.implicitHeight + Theme.spacingM
|
readonly property real targetImplicitHeight: {
|
||||||
|
let total = headerPane.implicitHeight + Theme.spacingS + widgetGrid.targetImplicitHeight;
|
||||||
|
if (editControls.visible)
|
||||||
|
total += Theme.spacingS + editControls.height;
|
||||||
|
return total + Theme.spacingM;
|
||||||
|
}
|
||||||
|
implicitHeight: targetImplicitHeight
|
||||||
property alias bluetoothCodecSelector: bluetoothCodecSelector
|
property alias bluetoothCodecSelector: bluetoothCodecSelector
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
@@ -136,91 +194,103 @@ DankPopout {
|
|||||||
z: 5000
|
z: 5000
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
|
enabled: !Theme.isDirectionalEffect
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: 200
|
duration: Theme.shortDuration
|
||||||
easing.type: Easing.OutCubic
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.shouldBeVisible ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
DankFlickable {
|
||||||
id: mainColumn
|
id: contentFlickable
|
||||||
width: parent.width - Theme.spacingL * 2
|
anchors.fill: parent
|
||||||
x: Theme.spacingL
|
clip: true
|
||||||
y: Theme.spacingL
|
contentWidth: width
|
||||||
spacing: Theme.spacingS
|
contentHeight: Math.max(height, mainColumn.implicitHeight + Theme.spacingM)
|
||||||
|
interactive: contentHeight > height
|
||||||
|
|
||||||
HeaderPane {
|
Column {
|
||||||
id: headerPane
|
id: mainColumn
|
||||||
width: parent.width
|
width: contentFlickable.width - Theme.spacingL * 2
|
||||||
editMode: root.editMode
|
x: Theme.spacingL
|
||||||
onEditModeToggled: root.editMode = !root.editMode
|
y: Theme.spacingL
|
||||||
onPowerButtonClicked: {
|
spacing: Theme.spacingS
|
||||||
if (powerMenuModalLoader) {
|
|
||||||
powerMenuModalLoader.active = true;
|
HeaderPane {
|
||||||
if (powerMenuModalLoader.item) {
|
id: headerPane
|
||||||
const bounds = Qt.rect(root.alignedX, root.alignedY, root.popupWidth, root.popupHeight);
|
width: parent.width
|
||||||
powerMenuModalLoader.item.openFromControlCenter(bounds, root.screen);
|
editMode: root.editMode
|
||||||
|
onEditModeToggled: root.editMode = !root.editMode
|
||||||
|
onPowerButtonClicked: {
|
||||||
|
if (powerMenuModalLoader) {
|
||||||
|
powerMenuModalLoader.active = true;
|
||||||
|
if (powerMenuModalLoader.item) {
|
||||||
|
const bounds = Qt.rect(root.alignedX, root.alignedY, root.popupWidth, root.popupHeight);
|
||||||
|
powerMenuModalLoader.item.openFromControlCenter(bounds, root.screen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
onLockRequested: {
|
||||||
onLockRequested: {
|
root.close();
|
||||||
root.close();
|
root.lockRequested();
|
||||||
root.lockRequested();
|
}
|
||||||
}
|
onSettingsButtonClicked: {
|
||||||
onSettingsButtonClicked: {
|
root.close();
|
||||||
root.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DragDropGrid {
|
|
||||||
id: widgetGrid
|
|
||||||
width: parent.width
|
|
||||||
editMode: root.editMode
|
|
||||||
maxPopoutHeight: {
|
|
||||||
const screenHeight = (root.triggerScreen?.height ?? 1080);
|
|
||||||
return screenHeight - 100 - Theme.spacingL - headerPane.height - Theme.spacingS;
|
|
||||||
}
|
|
||||||
expandedSection: root.expandedSection
|
|
||||||
expandedWidgetIndex: root.expandedWidgetIndex
|
|
||||||
expandedWidgetData: root.expandedWidgetData
|
|
||||||
model: widgetModel
|
|
||||||
bluetoothCodecSelector: bluetoothCodecSelector
|
|
||||||
colorPickerModal: root.colorPickerModal
|
|
||||||
screenName: root.triggerScreen?.name || ""
|
|
||||||
screenModel: root.triggerScreen?.model || ""
|
|
||||||
parentScreen: root.triggerScreen
|
|
||||||
onExpandClicked: (widgetData, globalIndex) => {
|
|
||||||
root.expandedWidgetIndex = globalIndex;
|
|
||||||
root.expandedWidgetData = widgetData;
|
|
||||||
if (widgetData.id === "diskUsage") {
|
|
||||||
root.toggleSection("diskUsage_" + (widgetData.instanceId || "default"));
|
|
||||||
} else if (widgetData.id === "brightnessSlider") {
|
|
||||||
root.toggleSection("brightnessSlider_" + (widgetData.instanceId || "default"));
|
|
||||||
} else {
|
|
||||||
root.toggleSection(widgetData.id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onRemoveWidget: index => widgetModel.removeWidget(index)
|
|
||||||
onMoveWidget: (fromIndex, toIndex) => widgetModel.moveWidget(fromIndex, toIndex)
|
|
||||||
onToggleWidgetSize: index => widgetModel.toggleWidgetSize(index)
|
|
||||||
onCollapseRequested: root.collapseAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
EditControls {
|
DragDropGrid {
|
||||||
width: parent.width
|
id: widgetGrid
|
||||||
visible: editMode
|
width: parent.width
|
||||||
popoutContent: controlContent
|
editMode: root.editMode
|
||||||
availableWidgets: {
|
maxPopoutHeight: {
|
||||||
if (!editMode)
|
const screenHeight = (root.triggerScreen?.height ?? 1080);
|
||||||
return [];
|
return screenHeight - 100 - Theme.spacingL - headerPane.implicitHeight - Theme.spacingS;
|
||||||
const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id);
|
}
|
||||||
const allWidgets = widgetModel.baseWidgetDefinitions.concat(widgetModel.getPluginWidgets());
|
expandedSection: root.expandedSection
|
||||||
return allWidgets.filter(w => w.allowMultiple || !existingIds.includes(w.id));
|
expandedWidgetIndex: root.expandedWidgetIndex
|
||||||
|
expandedWidgetData: root.expandedWidgetData
|
||||||
|
model: widgetModel
|
||||||
|
bluetoothCodecSelector: bluetoothCodecSelector
|
||||||
|
colorPickerModal: root.colorPickerModal
|
||||||
|
screenName: root.triggerScreen?.name || ""
|
||||||
|
screenModel: root.triggerScreen?.model || ""
|
||||||
|
parentScreen: root.triggerScreen
|
||||||
|
onExpandClicked: (widgetData, globalIndex) => {
|
||||||
|
root.expandedWidgetIndex = globalIndex;
|
||||||
|
root.expandedWidgetData = widgetData;
|
||||||
|
if (widgetData.id === "diskUsage") {
|
||||||
|
root.toggleSection("diskUsage_" + (widgetData.instanceId || "default"));
|
||||||
|
} else if (widgetData.id === "brightnessSlider") {
|
||||||
|
root.toggleSection("brightnessSlider_" + (widgetData.instanceId || "default"));
|
||||||
|
} else {
|
||||||
|
root.toggleSection(widgetData.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onRemoveWidget: index => widgetModel.removeWidget(index)
|
||||||
|
onMoveWidget: (fromIndex, toIndex) => widgetModel.moveWidget(fromIndex, toIndex)
|
||||||
|
onToggleWidgetSize: index => widgetModel.toggleWidgetSize(index)
|
||||||
|
onCollapseRequested: root.collapseAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
EditControls {
|
||||||
|
id: editControls
|
||||||
|
width: parent.width
|
||||||
|
visible: editMode
|
||||||
|
popoutContent: controlContent
|
||||||
|
availableWidgets: {
|
||||||
|
if (!editMode)
|
||||||
|
return [];
|
||||||
|
const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id);
|
||||||
|
const allWidgets = widgetModel.baseWidgetDefinitions.concat(widgetModel.getPluginWidgets());
|
||||||
|
return allWidgets.filter(w => w.allowMultiple || !existingIds.includes(w.id));
|
||||||
|
}
|
||||||
|
onAddWidget: widgetId => widgetModel.addWidget(widgetId)
|
||||||
|
onResetToDefault: () => widgetModel.resetToDefault()
|
||||||
|
onClearAll: () => widgetModel.clearAll()
|
||||||
}
|
}
|
||||||
onAddWidget: widgetId => widgetModel.addWidget(widgetId)
|
|
||||||
onResetToDefault: () => widgetModel.resetToDefault()
|
|
||||||
onClearAll: () => widgetModel.clearAll()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ Item {
|
|||||||
required property var axis
|
required property var axis
|
||||||
required property var barConfig
|
required property var barConfig
|
||||||
|
|
||||||
|
visible: !SettingsData.frameEnabled
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -37,6 +39,8 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property real rt: {
|
property real rt: {
|
||||||
|
if (SettingsData.frameEnabled)
|
||||||
|
return SettingsData.frameRounding;
|
||||||
if (barConfig?.squareCorners ?? false)
|
if (barConfig?.squareCorners ?? false)
|
||||||
return 0;
|
return 0;
|
||||||
if (barWindow.hasMaximizedToplevel)
|
if (barWindow.hasMaximizedToplevel)
|
||||||
@@ -255,11 +259,12 @@ Item {
|
|||||||
h = h - wing;
|
h = h - wing;
|
||||||
const r = wing;
|
const r = wing;
|
||||||
const cr = rt;
|
const cr = rt;
|
||||||
|
const crE = SettingsData.frameEnabled ? 0 : cr;
|
||||||
|
|
||||||
let d = `M ${cr} 0`;
|
let d = `M ${crE} 0`;
|
||||||
d += ` L ${w - cr} 0`;
|
d += ` L ${w - crE} 0`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${w} ${cr}`;
|
d += ` A ${crE} ${crE} 0 0 1 ${w} ${crE}`;
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
d += ` L ${w} ${h + r}`;
|
d += ` L ${w} ${h + r}`;
|
||||||
d += ` A ${r} ${r} 0 0 0 ${w - r} ${h}`;
|
d += ` A ${r} ${r} 0 0 0 ${w - r} ${h}`;
|
||||||
@@ -273,9 +278,9 @@ Item {
|
|||||||
if (cr > 0)
|
if (cr > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 1 0 ${h - cr}`;
|
d += ` A ${cr} ${cr} 0 0 1 0 ${h - cr}`;
|
||||||
}
|
}
|
||||||
d += ` L 0 ${cr}`;
|
d += ` L 0 ${crE}`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${cr} 0`;
|
d += ` A ${crE} ${crE} 0 0 1 ${crE} 0`;
|
||||||
d += " Z";
|
d += " Z";
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
@@ -285,11 +290,12 @@ Item {
|
|||||||
h = h - wing;
|
h = h - wing;
|
||||||
const r = wing;
|
const r = wing;
|
||||||
const cr = rt;
|
const cr = rt;
|
||||||
|
const crE = SettingsData.frameEnabled ? 0 : cr;
|
||||||
|
|
||||||
let d = `M ${cr} ${fullH}`;
|
let d = `M ${crE} ${fullH}`;
|
||||||
d += ` L ${w - cr} ${fullH}`;
|
d += ` L ${w - crE} ${fullH}`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 0 ${w} ${fullH - cr}`;
|
d += ` A ${crE} ${crE} 0 0 0 ${w} ${fullH - crE}`;
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
d += ` L ${w} 0`;
|
d += ` L ${w} 0`;
|
||||||
d += ` A ${r} ${r} 0 0 1 ${w - r} ${r}`;
|
d += ` A ${r} ${r} 0 0 1 ${w - r} ${r}`;
|
||||||
@@ -303,9 +309,9 @@ Item {
|
|||||||
if (cr > 0)
|
if (cr > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 0 0 ${cr}`;
|
d += ` A ${cr} ${cr} 0 0 0 0 ${cr}`;
|
||||||
}
|
}
|
||||||
d += ` L 0 ${fullH - cr}`;
|
d += ` L 0 ${fullH - crE}`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 0 ${cr} ${fullH}`;
|
d += ` A ${crE} ${crE} 0 0 0 ${crE} ${fullH}`;
|
||||||
d += " Z";
|
d += " Z";
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
@@ -314,11 +320,12 @@ Item {
|
|||||||
w = w - wing;
|
w = w - wing;
|
||||||
const r = wing;
|
const r = wing;
|
||||||
const cr = rt;
|
const cr = rt;
|
||||||
|
const crE = SettingsData.frameEnabled ? 0 : cr;
|
||||||
|
|
||||||
let d = `M 0 ${cr}`;
|
let d = `M 0 ${crE}`;
|
||||||
d += ` L 0 ${h - cr}`;
|
d += ` L 0 ${h - crE}`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 0 ${cr} ${h}`;
|
d += ` A ${crE} ${crE} 0 0 0 ${crE} ${h}`;
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
d += ` L ${w + r} ${h}`;
|
d += ` L ${w + r} ${h}`;
|
||||||
d += ` A ${r} ${r} 0 0 1 ${w} ${h - r}`;
|
d += ` A ${r} ${r} 0 0 1 ${w} ${h - r}`;
|
||||||
@@ -332,9 +339,9 @@ Item {
|
|||||||
if (cr > 0)
|
if (cr > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 0 ${w - cr} 0`;
|
d += ` A ${cr} ${cr} 0 0 0 ${w - cr} 0`;
|
||||||
}
|
}
|
||||||
d += ` L ${cr} 0`;
|
d += ` L ${crE} 0`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 0 0 ${cr}`;
|
d += ` A ${crE} ${crE} 0 0 0 0 ${crE}`;
|
||||||
d += " Z";
|
d += " Z";
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
@@ -344,11 +351,12 @@ Item {
|
|||||||
w = w - wing;
|
w = w - wing;
|
||||||
const r = wing;
|
const r = wing;
|
||||||
const cr = rt;
|
const cr = rt;
|
||||||
|
const crE = SettingsData.frameEnabled ? 0 : cr;
|
||||||
|
|
||||||
let d = `M ${fullW} ${cr}`;
|
let d = `M ${fullW} ${crE}`;
|
||||||
d += ` L ${fullW} ${h - cr}`;
|
d += ` L ${fullW} ${h - crE}`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${fullW - cr} ${h}`;
|
d += ` A ${crE} ${crE} 0 0 1 ${fullW - crE} ${h}`;
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
d += ` L 0 ${h}`;
|
d += ` L 0 ${h}`;
|
||||||
d += ` A ${r} ${r} 0 0 0 ${r} ${h - r}`;
|
d += ` A ${r} ${r} 0 0 0 ${r} ${h - r}`;
|
||||||
@@ -362,9 +370,9 @@ Item {
|
|||||||
if (cr > 0)
|
if (cr > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${cr} 0`;
|
d += ` A ${cr} ${cr} 0 0 1 ${cr} 0`;
|
||||||
}
|
}
|
||||||
d += ` L ${fullW - cr} 0`;
|
d += ` L ${fullW - crE} 0`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${fullW} ${cr}`;
|
d += ` A ${crE} ${crE} 0 0 1 ${fullW} ${crE}`;
|
||||||
d += " Z";
|
d += " Z";
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,31 @@ Item {
|
|||||||
readonly property real innerPadding: barConfig?.innerPadding ?? 4
|
readonly property real innerPadding: barConfig?.innerPadding ?? 4
|
||||||
readonly property real outlineThickness: (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0
|
readonly property real outlineThickness: (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0
|
||||||
|
|
||||||
|
readonly property real _frameLeftInset: {
|
||||||
|
if (!SettingsData.frameEnabled || barWindow.isVertical) return 0
|
||||||
|
return barWindow.hasAdjacentLeftBar
|
||||||
|
? SettingsData.frameBarSize
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
readonly property real _frameRightInset: {
|
||||||
|
if (!SettingsData.frameEnabled || barWindow.isVertical) return 0
|
||||||
|
return barWindow.hasAdjacentRightBar
|
||||||
|
? SettingsData.frameBarSize
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
readonly property real _frameTopInset: {
|
||||||
|
if (!SettingsData.frameEnabled || !barWindow.isVertical) return 0
|
||||||
|
return barWindow.hasAdjacentTopBar
|
||||||
|
? SettingsData.frameThickness
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
readonly property real _frameBottomInset: {
|
||||||
|
if (!SettingsData.frameEnabled || !barWindow.isVertical) return 0
|
||||||
|
return barWindow.hasAdjacentBottomBar
|
||||||
|
? SettingsData.frameThickness
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
|
||||||
property alias hLeftSection: hLeftSection
|
property alias hLeftSection: hLeftSection
|
||||||
property alias hCenterSection: hCenterSection
|
property alias hCenterSection: hCenterSection
|
||||||
property alias hRightSection: hRightSection
|
property alias hRightSection: hRightSection
|
||||||
@@ -31,10 +56,14 @@ Item {
|
|||||||
property alias vRightSection: vRightSection
|
property alias vRightSection: vRightSection
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: Math.max(Theme.spacingXS, innerPadding * 0.8)
|
anchors.leftMargin: Math.max(Theme.spacingXS, innerPadding * 0.8) + _frameLeftInset
|
||||||
anchors.rightMargin: Math.max(Theme.spacingXS, innerPadding * 0.8)
|
anchors.rightMargin: Math.max(Theme.spacingXS, innerPadding * 0.8) + _frameRightInset
|
||||||
anchors.topMargin: barWindow.isVertical ? (barWindow.hasAdjacentTopBar ? outlineThickness : Theme.spacingXS) : 0
|
anchors.topMargin: (barWindow.isVertical
|
||||||
anchors.bottomMargin: barWindow.isVertical ? (barWindow.hasAdjacentBottomBar ? outlineThickness : Theme.spacingXS) : 0
|
? (barWindow.hasAdjacentTopBar ? outlineThickness : Theme.spacingXS)
|
||||||
|
: 0) + _frameTopInset
|
||||||
|
anchors.bottomMargin: (barWindow.isVertical
|
||||||
|
? (barWindow.hasAdjacentBottomBar ? outlineThickness : Theme.spacingXS)
|
||||||
|
: 0) + _frameBottomInset
|
||||||
clip: false
|
clip: false
|
||||||
|
|
||||||
property int componentMapRevision: 0
|
property int componentMapRevision: 0
|
||||||
@@ -1156,6 +1185,7 @@ Item {
|
|||||||
if (!notificationCenterLoader.item) {
|
if (!notificationCenterLoader.item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
notificationCenterLoader.item.triggerScreen = barWindow.screen;
|
||||||
const effectiveBarConfig = topBarContent.barConfig;
|
const effectiveBarConfig = topBarContent.barConfig;
|
||||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||||
if (notificationCenterLoader.item.setBarContext) {
|
if (notificationCenterLoader.item.setBarContext) {
|
||||||
|
|||||||
@@ -133,6 +133,11 @@ PanelWindow {
|
|||||||
teardown();
|
teardown();
|
||||||
if (!BlurService.enabled || !BlurService.available)
|
if (!BlurService.enabled || !BlurService.available)
|
||||||
return;
|
return;
|
||||||
|
// In frame mode, FrameWindow owns the blur region for the entire screen edge
|
||||||
|
// (including the bar area). The bar must not set its own competing blur region
|
||||||
|
// so that frameBlurEnabled acts as the single control for all blur in frame mode.
|
||||||
|
if (SettingsData.frameEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
const widgets = barWindow._blurWidgetItems.filter(w => w && w.visible && w.width > 0 && w.height > 0);
|
const widgets = barWindow._blurWidgetItems.filter(w => w && w.visible && w.width > 0 && w.height > 0);
|
||||||
const hasBar = barHasTransparency;
|
const hasBar = barHasTransparency;
|
||||||
@@ -187,6 +192,11 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onFrameEnabledChanged() { barBlur.rebuild(); }
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: topBarSlide
|
target: topBarSlide
|
||||||
function onXChanged() {
|
function onXChanged() {
|
||||||
@@ -238,7 +248,9 @@ PanelWindow {
|
|||||||
readonly property color _surfaceContainer: Theme.surfaceContainer
|
readonly property color _surfaceContainer: Theme.surfaceContainer
|
||||||
readonly property string _barId: barConfig?.id ?? "default"
|
readonly property string _barId: barConfig?.id ?? "default"
|
||||||
property real _backgroundAlpha: barConfig?.transparency ?? 1.0
|
property real _backgroundAlpha: barConfig?.transparency ?? 1.0
|
||||||
readonly property color _bgColor: Theme.withAlpha(_surfaceContainer, _backgroundAlpha)
|
readonly property color _bgColor: SettingsData.frameEnabled
|
||||||
|
? Qt.rgba(SettingsData.effectiveFrameColor.r, SettingsData.effectiveFrameColor.g, SettingsData.effectiveFrameColor.b, SettingsData.frameOpacity)
|
||||||
|
: Theme.withAlpha(_surfaceContainer, _backgroundAlpha)
|
||||||
|
|
||||||
function _updateBackgroundAlpha() {
|
function _updateBackgroundAlpha() {
|
||||||
const live = SettingsData.barConfigs.find(c => c.id === _barId);
|
const live = SettingsData.barConfigs.find(c => c.id === _barId);
|
||||||
@@ -384,7 +396,7 @@ PanelWindow {
|
|||||||
shouldHideForWindows = filtered.length > 0;
|
shouldHideForWindows = filtered.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
property real effectiveSpacing: hasMaximizedToplevel ? 0 : (barConfig?.spacing ?? 4)
|
property real effectiveSpacing: SettingsData.frameEnabled ? 0 : (hasMaximizedToplevel ? 0 : (barConfig?.spacing ?? 4))
|
||||||
|
|
||||||
Behavior on effectiveSpacing {
|
Behavior on effectiveSpacing {
|
||||||
enabled: barWindow.visible
|
enabled: barWindow.visible
|
||||||
@@ -395,7 +407,12 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readonly property int notificationCount: NotificationService.notifications.length
|
readonly property int notificationCount: NotificationService.notifications.length
|
||||||
readonly property real effectiveBarThickness: Theme.snap(Math.max(barWindow.widgetThickness + (barConfig?.innerPadding ?? 4) + 4, Theme.barHeight - 4 - (8 - (barConfig?.innerPadding ?? 4))), _dpr)
|
readonly property real effectiveBarThickness: SettingsData.frameEnabled
|
||||||
|
? SettingsData.frameBarSize
|
||||||
|
: Theme.snap(Math.max(barWindow.widgetThickness + (barConfig?.innerPadding ?? 4) + 4, Theme.barHeight - 4 - (8 - (barConfig?.innerPadding ?? 4))), _dpr)
|
||||||
|
readonly property bool effectiveOpenOnOverview: SettingsData.frameEnabled
|
||||||
|
? SettingsData.frameShowOnOverview
|
||||||
|
: (barConfig?.openOnOverview ?? false)
|
||||||
readonly property real widgetThickness: Theme.snap(Math.max(20, 26 + (barConfig?.innerPadding ?? 4) * 0.6), _dpr)
|
readonly property real widgetThickness: Theme.snap(Math.max(20, 26 + (barConfig?.innerPadding ?? 4) * 0.6), _dpr)
|
||||||
|
|
||||||
readonly property bool hasAdjacentTopBar: {
|
readonly property bool hasAdjacentTopBar: {
|
||||||
@@ -644,14 +661,14 @@ PanelWindow {
|
|||||||
anchors.left: !isVertical ? true : (barPos === SettingsData.Position.Left)
|
anchors.left: !isVertical ? true : (barPos === SettingsData.Position.Left)
|
||||||
anchors.right: !isVertical ? true : (barPos === SettingsData.Position.Right)
|
anchors.right: !isVertical ? true : (barPos === SettingsData.Position.Right)
|
||||||
|
|
||||||
exclusiveZone: (!(barConfig?.visible ?? true) || topBarCore.autoHide) ? -1 : (barWindow.effectiveBarThickness + effectiveSpacing + (barConfig?.bottomGap ?? 0))
|
exclusiveZone: (!(barConfig?.visible ?? true) || topBarCore.autoHide) ? -1 : (barWindow.effectiveBarThickness + effectiveSpacing + (Theme.isConnectedEffect ? 0 : (barConfig?.bottomGap ?? 0)))
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: inputMask
|
id: inputMask
|
||||||
|
|
||||||
readonly property int barThickness: Theme.px(barWindow.effectiveBarThickness + barWindow.effectiveSpacing, barWindow._dpr)
|
readonly property int barThickness: Theme.px(barWindow.effectiveBarThickness + barWindow.effectiveSpacing, barWindow._dpr)
|
||||||
|
|
||||||
readonly property bool inOverviewWithShow: CompositorService.isNiri && NiriService.inOverview && (barConfig?.openOnOverview ?? false)
|
readonly property bool inOverviewWithShow: CompositorService.isNiri && NiriService.inOverview && barWindow.effectiveOpenOnOverview
|
||||||
readonly property bool effectiveVisible: (barConfig?.visible ?? true) || inOverviewWithShow
|
readonly property bool effectiveVisible: (barConfig?.visible ?? true) || inOverviewWithShow
|
||||||
readonly property bool showing: effectiveVisible && (topBarCore.reveal || inOverviewWithShow || !topBarCore.autoHide)
|
readonly property bool showing: effectiveVisible && (topBarCore.reveal || inOverviewWithShow || !topBarCore.autoHide)
|
||||||
|
|
||||||
@@ -792,7 +809,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property bool reveal: {
|
property bool reveal: {
|
||||||
const inOverviewWithShow = CompositorService.isNiri && NiriService.inOverview && (barConfig?.openOnOverview ?? false);
|
const inOverviewWithShow = CompositorService.isNiri && NiriService.inOverview && barWindow.effectiveOpenOnOverview;
|
||||||
if (inOverviewWithShow)
|
if (inOverviewWithShow)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -889,7 +906,7 @@ PanelWindow {
|
|||||||
top: barWindow.isVertical ? parent.top : undefined
|
top: barWindow.isVertical ? parent.top : undefined
|
||||||
bottom: barWindow.isVertical ? parent.bottom : undefined
|
bottom: barWindow.isVertical ? parent.bottom : undefined
|
||||||
}
|
}
|
||||||
readonly property bool inOverview: CompositorService.isNiri && NiriService.inOverview && (barConfig?.openOnOverview ?? false)
|
readonly property bool inOverview: CompositorService.isNiri && NiriService.inOverview && barWindow.effectiveOpenOnOverview
|
||||||
hoverEnabled: (barConfig?.autoHide ?? false) && !inOverview && !topBarCore.hasActivePopout
|
hoverEnabled: (barConfig?.autoHide ?? false) && !inOverview && !topBarCore.hasActivePopout
|
||||||
acceptedButtons: Qt.NoButton
|
acceptedButtons: Qt.NoButton
|
||||||
enabled: (barConfig?.autoHide ?? false) && !inOverview
|
enabled: (barConfig?.autoHide ?? false) && !inOverview
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ DankPopout {
|
|||||||
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 500
|
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 500
|
||||||
triggerWidth: 80
|
triggerWidth: 80
|
||||||
screen: triggerScreen
|
screen: triggerScreen
|
||||||
shouldBeVisible: dashVisible
|
|
||||||
|
|
||||||
property bool __focusArmed: false
|
property bool __focusArmed: false
|
||||||
property bool __contentReady: false
|
property bool __contentReady: false
|
||||||
|
|||||||
@@ -43,6 +43,43 @@ Item {
|
|||||||
|
|
||||||
property int __volumeHoverCount: 0
|
property int __volumeHoverCount: 0
|
||||||
|
|
||||||
|
readonly property bool directionalEffect: Theme.isDirectionalEffect
|
||||||
|
readonly property bool depthEffect: Theme.isDepthEffect
|
||||||
|
|
||||||
|
function panelMotionX(panelWidth, active) {
|
||||||
|
if (active)
|
||||||
|
return 0;
|
||||||
|
if (directionalEffect) {
|
||||||
|
const travel = Math.max(Theme.effectAnimOffset, panelWidth * 0.85);
|
||||||
|
return isRightEdge ? -travel : travel;
|
||||||
|
}
|
||||||
|
if (depthEffect) {
|
||||||
|
const travel = Math.max(Theme.effectAnimOffset * 0.7, panelWidth * 0.32);
|
||||||
|
return isRightEdge ? -travel : travel;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function panelMotionY(panelType, panelHeight, active) {
|
||||||
|
if (active)
|
||||||
|
return 0;
|
||||||
|
if (directionalEffect) {
|
||||||
|
if (panelType === 2)
|
||||||
|
return panelHeight * 0.08;
|
||||||
|
if (panelType === 3)
|
||||||
|
return -panelHeight * 0.08;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (depthEffect) {
|
||||||
|
if (panelType === 2)
|
||||||
|
return panelHeight * 0.04;
|
||||||
|
if (panelType === 3)
|
||||||
|
return -panelHeight * 0.04;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
function volumeAreaEntered() {
|
function volumeAreaEntered() {
|
||||||
__volumeHoverCount++;
|
__volumeHoverCount++;
|
||||||
panelEntered();
|
panelEntered();
|
||||||
@@ -61,30 +98,47 @@ Item {
|
|||||||
visible: dropdownType === 1 && volumeAvailable
|
visible: dropdownType === 1 && volumeAvailable
|
||||||
width: 60
|
width: 60
|
||||||
height: 180
|
height: 180
|
||||||
x: isRightEdge ? anchorPos.x : anchorPos.x - width
|
x: (isRightEdge ? anchorPos.x : anchorPos.x - width) + panelMotionX(width, dropdownType === 1)
|
||||||
y: anchorPos.y - height / 2
|
y: anchorPos.y - height / 2 + panelMotionY(1, height, dropdownType === 1)
|
||||||
radius: Theme.cornerRadius * 2
|
radius: Theme.cornerRadius * 2
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95)
|
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95)
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
opacity: dropdownType === 1 ? 1 : 0
|
opacity: Theme.isDirectionalEffect ? 1 : (dropdownType === 1 ? 1 : 0)
|
||||||
scale: dropdownType === 1 ? 1 : 0.96
|
scale: Theme.isDirectionalEffect ? 1 : (dropdownType === 1 ? 1 : Theme.effectScaleCollapsed)
|
||||||
transformOrigin: isRightEdge ? Item.Left : Item.Right
|
transformOrigin: isRightEdge ? Item.Left : Item.Right
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
enabled: !Theme.isDirectionalEffect
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
DankAnim {
|
||||||
easing.type: Easing.BezierSpline
|
duration: Math.round(Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 1) * Theme.variantOpacityDurationScale)
|
||||||
easing.bezierCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
easing.bezierCurve: dropdownType === 1 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on scale {
|
Behavior on scale {
|
||||||
|
enabled: !Theme.isDirectionalEffect
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 1)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
easing.bezierCurve: dropdownType === 1 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 1)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: dropdownType === 1 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on y {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 1)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: dropdownType === 1 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,33 +250,50 @@ Item {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: audioDevicesPanel
|
id: audioDevicesPanel
|
||||||
visible: dropdownType === 2
|
visible: dropdownType === 2 && activePlayer !== null
|
||||||
width: 280
|
width: 280
|
||||||
height: Math.max(200, Math.min(280, availableDevices.length * 50 + 100))
|
height: Math.max(200, Math.min(280, availableDevices.length * 50 + 100))
|
||||||
x: isRightEdge ? anchorPos.x : anchorPos.x - width
|
x: (isRightEdge ? anchorPos.x : anchorPos.x - width) + panelMotionX(width, dropdownType === 2)
|
||||||
y: anchorPos.y - height / 2
|
y: anchorPos.y - height / 2 + panelMotionY(2, height, dropdownType === 2)
|
||||||
radius: Theme.cornerRadius * 2
|
radius: Theme.cornerRadius * 2
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.98)
|
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.98)
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.6)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.6)
|
||||||
border.width: 2
|
border.width: 2
|
||||||
|
|
||||||
opacity: dropdownType === 2 ? 1 : 0
|
opacity: Theme.isDirectionalEffect ? 1 : (dropdownType === 2 ? 1 : 0)
|
||||||
scale: dropdownType === 2 ? 1 : 0.96
|
scale: Theme.isDirectionalEffect ? 1 : (dropdownType === 2 ? 1 : Theme.effectScaleCollapsed)
|
||||||
transformOrigin: isRightEdge ? Item.Left : Item.Right
|
transformOrigin: isRightEdge ? Item.Left : Item.Right
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
enabled: !Theme.isDirectionalEffect
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
DankAnim {
|
||||||
easing.type: Easing.BezierSpline
|
duration: Math.round(Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 2) * Theme.variantOpacityDurationScale)
|
||||||
easing.bezierCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
easing.bezierCurve: dropdownType === 2 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on scale {
|
Behavior on scale {
|
||||||
|
enabled: !Theme.isDirectionalEffect
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 2)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
easing.bezierCurve: dropdownType === 2 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 2)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: dropdownType === 2 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on y {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 2)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: dropdownType === 2 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,30 +424,47 @@ Item {
|
|||||||
visible: dropdownType === 3
|
visible: dropdownType === 3
|
||||||
width: 240
|
width: 240
|
||||||
height: Math.max(180, Math.min(240, (allPlayers?.length || 0) * 50 + 80))
|
height: Math.max(180, Math.min(240, (allPlayers?.length || 0) * 50 + 80))
|
||||||
x: isRightEdge ? anchorPos.x : anchorPos.x - width
|
x: (isRightEdge ? anchorPos.x : anchorPos.x - width) + panelMotionX(width, dropdownType === 3)
|
||||||
y: anchorPos.y - height / 2
|
y: anchorPos.y - height / 2 + panelMotionY(3, height, dropdownType === 3)
|
||||||
radius: Theme.cornerRadius * 2
|
radius: Theme.cornerRadius * 2
|
||||||
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.98)
|
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.98)
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.6)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.6)
|
||||||
border.width: 2
|
border.width: 2
|
||||||
|
|
||||||
opacity: dropdownType === 3 ? 1 : 0
|
opacity: Theme.isDirectionalEffect ? 1 : (dropdownType === 3 ? 1 : 0)
|
||||||
scale: dropdownType === 3 ? 1 : 0.96
|
scale: Theme.isDirectionalEffect ? 1 : (dropdownType === 3 ? 1 : Theme.effectScaleCollapsed)
|
||||||
transformOrigin: isRightEdge ? Item.Left : Item.Right
|
transformOrigin: isRightEdge ? Item.Left : Item.Right
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
enabled: !Theme.isDirectionalEffect
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
DankAnim {
|
||||||
easing.type: Easing.BezierSpline
|
duration: Math.round(Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 3) * Theme.variantOpacityDurationScale)
|
||||||
easing.bezierCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
easing.bezierCurve: dropdownType === 3 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on scale {
|
Behavior on scale {
|
||||||
|
enabled: !Theme.isDirectionalEffect
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 3)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
easing.bezierCurve: dropdownType === 3 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 3)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: dropdownType === 3 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on y {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 3)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: dropdownType === 3 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,11 +19,12 @@ Variants {
|
|||||||
|
|
||||||
WindowBlur {
|
WindowBlur {
|
||||||
targetWindow: dock
|
targetWindow: dock
|
||||||
|
blurEnabled: dock.effectiveBlurEnabled && !SettingsData.connectedFrameModeActive
|
||||||
blurX: dockBackground.x + dockContainer.x + dockMouseArea.x + dockCore.x + dockSlide.x
|
blurX: dockBackground.x + dockContainer.x + dockMouseArea.x + dockCore.x + dockSlide.x
|
||||||
blurY: dockBackground.y + dockContainer.y + dockMouseArea.y + dockCore.y + dockSlide.y
|
blurY: dockBackground.y + dockContainer.y + dockMouseArea.y + dockCore.y + dockSlide.y
|
||||||
blurWidth: dock.hasApps && dock.reveal ? dockBackground.width : 0
|
blurWidth: dock.hasApps && dock.reveal ? dockBackground.width : 0
|
||||||
blurHeight: dock.hasApps && dock.reveal ? dockBackground.height : 0
|
blurHeight: dock.hasApps && dock.reveal ? dockBackground.height : 0
|
||||||
blurRadius: Theme.cornerRadius
|
blurRadius: Theme.isConnectedEffect ? Theme.connectedCornerRadius : dock.surfaceRadius
|
||||||
}
|
}
|
||||||
|
|
||||||
WlrLayershell.namespace: "dms:dock"
|
WlrLayershell.namespace: "dms:dock"
|
||||||
@@ -42,6 +43,23 @@ Variants {
|
|||||||
property real backgroundTransparency: SettingsData.dockTransparency
|
property real backgroundTransparency: SettingsData.dockTransparency
|
||||||
property bool groupByApp: SettingsData.dockGroupByApp
|
property bool groupByApp: SettingsData.dockGroupByApp
|
||||||
readonly property int borderThickness: SettingsData.dockBorderEnabled ? SettingsData.dockBorderThickness : 0
|
readonly property int borderThickness: SettingsData.dockBorderEnabled ? SettingsData.dockBorderThickness : 0
|
||||||
|
readonly property string connectedBarSide: SettingsData.dockPosition === SettingsData.Position.Top ? "top" : SettingsData.dockPosition === SettingsData.Position.Bottom ? "bottom" : SettingsData.dockPosition === SettingsData.Position.Left ? "left" : "right"
|
||||||
|
readonly property bool connectedBarActiveOnEdge: Theme.isConnectedEffect && !!(dock.screen || modelData) && SettingsData.getActiveBarEdgesForScreen(dock.screen || modelData).includes(connectedBarSide)
|
||||||
|
readonly property real connectedJoinInset: {
|
||||||
|
if (!Theme.isConnectedEffect)
|
||||||
|
return 0;
|
||||||
|
return connectedBarActiveOnEdge ? SettingsData.frameBarSize : SettingsData.frameThickness;
|
||||||
|
}
|
||||||
|
readonly property real surfaceRadius: Theme.connectedSurfaceRadius
|
||||||
|
readonly property color surfaceColor: Theme.isConnectedEffect ? Theme.connectedSurfaceColor : Theme.withAlpha(Theme.surfaceContainer, backgroundTransparency)
|
||||||
|
readonly property color surfaceBorderColor: Theme.isConnectedEffect ? "transparent" : BlurService.borderColor
|
||||||
|
readonly property real surfaceBorderWidth: Theme.isConnectedEffect ? 0 : BlurService.borderWidth
|
||||||
|
readonly property real surfaceTopLeftRadius: Theme.isConnectedEffect && (SettingsData.dockPosition === SettingsData.Position.Top || SettingsData.dockPosition === SettingsData.Position.Left) ? 0 : surfaceRadius
|
||||||
|
readonly property real surfaceTopRightRadius: Theme.isConnectedEffect && (SettingsData.dockPosition === SettingsData.Position.Top || SettingsData.dockPosition === SettingsData.Position.Right) ? 0 : surfaceRadius
|
||||||
|
readonly property real surfaceBottomLeftRadius: Theme.isConnectedEffect && (SettingsData.dockPosition === SettingsData.Position.Bottom || SettingsData.dockPosition === SettingsData.Position.Left) ? 0 : surfaceRadius
|
||||||
|
readonly property real surfaceBottomRightRadius: Theme.isConnectedEffect && (SettingsData.dockPosition === SettingsData.Position.Bottom || SettingsData.dockPosition === SettingsData.Position.Right) ? 0 : surfaceRadius
|
||||||
|
readonly property real horizontalConnectorExtent: Theme.isConnectedEffect && !isVertical ? Theme.connectedCornerRadius : 0
|
||||||
|
readonly property real verticalConnectorExtent: Theme.isConnectedEffect && isVertical ? Theme.connectedCornerRadius : 0
|
||||||
|
|
||||||
readonly property int hasApps: dockApps.implicitWidth > 0 || dockApps.implicitHeight > 0
|
readonly property int hasApps: dockApps.implicitWidth > 0 || dockApps.implicitHeight > 0
|
||||||
|
|
||||||
@@ -113,13 +131,116 @@ Variants {
|
|||||||
return getBarHeight(leftBar);
|
return getBarHeight(leftBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property real dockMargin: SettingsData.dockSpacing
|
readonly property real dockMargin: SettingsData.dockMargin
|
||||||
readonly property real positionSpacing: barSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin
|
readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled
|
||||||
|
readonly property real effectiveDockBottomGap: Theme.isConnectedEffect ? 0 : SettingsData.dockBottomGap
|
||||||
|
readonly property real effectiveDockMargin: Theme.isConnectedEffect ? 0 : SettingsData.dockMargin
|
||||||
|
readonly property real positionSpacing: barSpacing + effectiveDockBottomGap + effectiveDockMargin
|
||||||
|
readonly property real joinedEdgeMargin: Theme.isConnectedEffect ? 0 : (barSpacing + effectiveDockMargin + 1 + dock.borderThickness)
|
||||||
readonly property real _dpr: (dock.screen && dock.screen.devicePixelRatio) ? dock.screen.devicePixelRatio : 1
|
readonly property real _dpr: (dock.screen && dock.screen.devicePixelRatio) ? dock.screen.devicePixelRatio : 1
|
||||||
function px(v) {
|
function px(v) {
|
||||||
return Math.round(v * _dpr) / _dpr;
|
return Math.round(v * _dpr) / _dpr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function connectorWidth(spacing) {
|
||||||
|
return dock.isVertical ? (spacing + Theme.connectedCornerRadius) : Theme.connectedCornerRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectorHeight(spacing) {
|
||||||
|
return dock.isVertical ? Theme.connectedCornerRadius : (spacing + Theme.connectedCornerRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectorSeamX(baseX, bodyWidth, placement) {
|
||||||
|
if (!dock.isVertical)
|
||||||
|
return placement === "left" ? baseX : baseX + bodyWidth;
|
||||||
|
return SettingsData.dockPosition === SettingsData.Position.Left ? baseX : baseX + bodyWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectorSeamY(baseY, bodyHeight, placement) {
|
||||||
|
if (SettingsData.dockPosition === SettingsData.Position.Top)
|
||||||
|
return baseY;
|
||||||
|
if (SettingsData.dockPosition === SettingsData.Position.Bottom)
|
||||||
|
return baseY + bodyHeight;
|
||||||
|
return placement === "left" ? baseY : baseY + bodyHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectorX(baseX, bodyWidth, placement, spacing) {
|
||||||
|
const seamX = connectorSeamX(baseX, bodyWidth, placement);
|
||||||
|
const width = connectorWidth(spacing);
|
||||||
|
if (!dock.isVertical)
|
||||||
|
return placement === "left" ? seamX - width : seamX;
|
||||||
|
return SettingsData.dockPosition === SettingsData.Position.Left ? seamX : seamX - width;
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectorY(baseY, bodyHeight, placement, spacing) {
|
||||||
|
const seamY = connectorSeamY(baseY, bodyHeight, placement);
|
||||||
|
const height = connectorHeight(spacing);
|
||||||
|
if (SettingsData.dockPosition === SettingsData.Position.Top)
|
||||||
|
return seamY;
|
||||||
|
if (SettingsData.dockPosition === SettingsData.Position.Bottom)
|
||||||
|
return seamY - height;
|
||||||
|
return placement === "left" ? seamY - height : seamY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── ConnectedModeState sync ────────────────────────────────────────
|
||||||
|
// Dock window origin in screen-relative coordinates (FrameWindow space).
|
||||||
|
function _dockWindowOriginX() {
|
||||||
|
if (!dock.isVertical)
|
||||||
|
return 0;
|
||||||
|
if (SettingsData.dockPosition === SettingsData.Position.Right)
|
||||||
|
return (dock.screen ? dock.screen.width : 0) - dock.width;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
function _dockWindowOriginY() {
|
||||||
|
if (dock.isVertical)
|
||||||
|
return 0;
|
||||||
|
if (SettingsData.dockPosition === SettingsData.Position.Bottom)
|
||||||
|
return (dock.screen ? dock.screen.height : 0) - dock.height;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property string _dockScreenName: dock.modelData ? dock.modelData.name : (dock.screen ? dock.screen.name : "")
|
||||||
|
|
||||||
|
function _syncDockChromeState() {
|
||||||
|
if (!dock._dockScreenName)
|
||||||
|
return;
|
||||||
|
if (!SettingsData.connectedFrameModeActive) {
|
||||||
|
ConnectedModeState.clearDockState(dock._dockScreenName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectedModeState.setDockState(dock._dockScreenName, {
|
||||||
|
"reveal": dock.visible && (dock.reveal || slideXAnimation.running || slideYAnimation.running) && dock.hasApps,
|
||||||
|
"barSide": dock.connectedBarSide,
|
||||||
|
"bodyX": dock._dockWindowOriginX() + dockBackground.x + dockContainer.x + dockMouseArea.x + dockCore.x,
|
||||||
|
"bodyY": dock._dockWindowOriginY() + dockBackground.y + dockContainer.y + dockMouseArea.y + dockCore.y,
|
||||||
|
"bodyW": dock.hasApps ? dockBackground.width : 0,
|
||||||
|
"bodyH": dock.hasApps ? dockBackground.height : 0,
|
||||||
|
"slideX": dockSlide.x,
|
||||||
|
"slideY": dockSlide.y
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _syncDockSlide() {
|
||||||
|
if (!dock._dockScreenName || !SettingsData.connectedFrameModeActive)
|
||||||
|
return;
|
||||||
|
ConnectedModeState.setDockSlide(dock._dockScreenName, dockSlide.x, dockSlide.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool _slideSyncPending: false
|
||||||
|
function _queueSlideSync() {
|
||||||
|
if (!SettingsData.connectedFrameModeActive)
|
||||||
|
return;
|
||||||
|
if (_slideSyncPending)
|
||||||
|
return;
|
||||||
|
_slideSyncPending = true;
|
||||||
|
Qt.callLater(dock._flushSlideSync);
|
||||||
|
}
|
||||||
|
function _flushSlideSync() {
|
||||||
|
_slideSyncPending = false;
|
||||||
|
dock._syncDockSlide();
|
||||||
|
}
|
||||||
|
|
||||||
property bool contextMenuOpen: (dockVariants.contextMenu && dockVariants.contextMenu.visible && dockVariants.contextMenu.screen === modelData)
|
property bool contextMenuOpen: (dockVariants.contextMenu && dockVariants.contextMenu.visible && dockVariants.contextMenu.screen === modelData)
|
||||||
property bool revealSticky: false
|
property bool revealSticky: false
|
||||||
|
|
||||||
@@ -130,7 +251,7 @@ Variants {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
const screenName = dock.modelData?.name ?? "";
|
const screenName = dock.modelData?.name ?? "";
|
||||||
const dockThickness = effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin;
|
const dockThickness = dock.connectedJoinInset + effectiveBarHeight + SettingsData.dockSpacing + dock.effectiveDockBottomGap + dock.effectiveDockMargin;
|
||||||
const screenWidth = dock.screen?.width ?? 0;
|
const screenWidth = dock.screen?.width ?? 0;
|
||||||
const screenHeight = dock.screen?.height ?? 0;
|
const screenHeight = dock.screen?.height ?? 0;
|
||||||
|
|
||||||
@@ -281,6 +402,23 @@ Variants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: Qt.callLater(() => dock._syncDockChromeState())
|
||||||
|
Component.onDestruction: ConnectedModeState.clearDockState(dock._dockScreenName)
|
||||||
|
|
||||||
|
onRevealChanged: dock._syncDockChromeState()
|
||||||
|
onWidthChanged: dock._syncDockChromeState()
|
||||||
|
onHeightChanged: dock._syncDockChromeState()
|
||||||
|
onVisibleChanged: dock._syncDockChromeState()
|
||||||
|
onHasAppsChanged: dock._syncDockChromeState()
|
||||||
|
onConnectedBarSideChanged: dock._syncDockChromeState()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onConnectedFrameModeActiveChanged() {
|
||||||
|
dock._syncDockChromeState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: SettingsData
|
target: SettingsData
|
||||||
function onDockTransparencyChanged() {
|
function onDockTransparencyChanged() {
|
||||||
@@ -302,13 +440,13 @@ Variants {
|
|||||||
return -1;
|
return -1;
|
||||||
if (barSpacing > 0)
|
if (barSpacing > 0)
|
||||||
return -1;
|
return -1;
|
||||||
return px(effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin);
|
return px(connectedJoinInset + effectiveBarHeight + SettingsData.dockSpacing + effectiveDockBottomGap + effectiveDockMargin);
|
||||||
}
|
}
|
||||||
|
|
||||||
property real animationHeadroom: Math.ceil(SettingsData.dockIconSize * 0.35)
|
property real animationHeadroom: Math.ceil(SettingsData.dockIconSize * 0.35)
|
||||||
|
|
||||||
implicitWidth: isVertical ? (px(effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockMargin + SettingsData.dockIconSize * 0.3) + animationHeadroom) : 0
|
implicitWidth: isVertical ? (px(connectedJoinInset + effectiveBarHeight + SettingsData.dockSpacing + effectiveDockMargin + SettingsData.dockIconSize * 0.3) + animationHeadroom) : 0
|
||||||
implicitHeight: !isVertical ? (px(effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockMargin + SettingsData.dockIconSize * 0.3) + animationHeadroom) : 0
|
implicitHeight: !isVertical ? (px(connectedJoinInset + effectiveBarHeight + SettingsData.dockSpacing + effectiveDockMargin + SettingsData.dockIconSize * 0.3) + animationHeadroom) : 0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: maskItem
|
id: maskItem
|
||||||
@@ -318,17 +456,17 @@ Variants {
|
|||||||
x: {
|
x: {
|
||||||
const baseX = dockCore.x + dockMouseArea.x;
|
const baseX = dockCore.x + dockMouseArea.x;
|
||||||
if (isVertical && SettingsData.dockPosition === SettingsData.Position.Right)
|
if (isVertical && SettingsData.dockPosition === SettingsData.Position.Right)
|
||||||
return baseX - (expanded ? animationHeadroom + borderThickness : 0);
|
return baseX - (expanded ? animationHeadroom + borderThickness + dock.horizontalConnectorExtent : 0);
|
||||||
return baseX - (expanded ? borderThickness : 0);
|
return baseX - (expanded ? borderThickness + dock.horizontalConnectorExtent : 0);
|
||||||
}
|
}
|
||||||
y: {
|
y: {
|
||||||
const baseY = dockCore.y + dockMouseArea.y;
|
const baseY = dockCore.y + dockMouseArea.y;
|
||||||
if (!isVertical && SettingsData.dockPosition === SettingsData.Position.Bottom)
|
if (!isVertical && SettingsData.dockPosition === SettingsData.Position.Bottom)
|
||||||
return baseY - (expanded ? animationHeadroom + borderThickness : 0);
|
return baseY - (expanded ? animationHeadroom + borderThickness + dock.verticalConnectorExtent : 0);
|
||||||
return baseY - (expanded ? borderThickness : 0);
|
return baseY - (expanded ? borderThickness + dock.verticalConnectorExtent : 0);
|
||||||
}
|
}
|
||||||
width: dockMouseArea.width + (isVertical && expanded ? animationHeadroom : 0) + (expanded ? borderThickness * 2 : 0)
|
width: dockMouseArea.width + (isVertical && expanded ? animationHeadroom : 0) + (expanded ? borderThickness * 2 + dock.horizontalConnectorExtent * 2 : 0)
|
||||||
height: dockMouseArea.height + (!isVertical && expanded ? animationHeadroom : 0) + (expanded ? borderThickness * 2 : 0)
|
height: dockMouseArea.height + (!isVertical && expanded ? animationHeadroom : 0) + (expanded ? borderThickness * 2 + dock.verticalConnectorExtent * 2 : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
mask: Region {
|
mask: Region {
|
||||||
@@ -388,7 +526,7 @@ Variants {
|
|||||||
const screenHeight = dock.screen ? dock.screen.height : 0;
|
const screenHeight = dock.screen ? dock.screen.height : 0;
|
||||||
|
|
||||||
const gap = Theme.spacingS;
|
const gap = Theme.spacingS;
|
||||||
const bgMargin = barSpacing + SettingsData.dockMargin + 1 + dock.borderThickness;
|
const bgMargin = dock.joinedEdgeMargin + dock.connectedJoinInset;
|
||||||
const btnW = dock.hoveredButton.width;
|
const btnW = dock.hoveredButton.width;
|
||||||
const btnH = dock.hoveredButton.height;
|
const btnH = dock.hoveredButton.height;
|
||||||
|
|
||||||
@@ -459,11 +597,11 @@ Variants {
|
|||||||
// Keep the taller hit area regardless of the reveal state to prevent shrinking loop
|
// Keep the taller hit area regardless of the reveal state to prevent shrinking loop
|
||||||
return Math.min(Math.max(dockBackground.height + 64, 200), maxDockHeight);
|
return Math.min(Math.max(dockBackground.height + 64, 200), maxDockHeight);
|
||||||
}
|
}
|
||||||
return dock.reveal ? px(dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin) : 1;
|
return dock.reveal ? px(dock.connectedJoinInset + dock.effectiveBarHeight + SettingsData.dockSpacing + dock.effectiveDockBottomGap + dock.effectiveDockMargin) : 1;
|
||||||
}
|
}
|
||||||
width: {
|
width: {
|
||||||
if (dock.isVertical) {
|
if (dock.isVertical) {
|
||||||
return dock.reveal ? px(dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin) : 1;
|
return dock.reveal ? px(dock.connectedJoinInset + dock.effectiveBarHeight + SettingsData.dockSpacing + dock.effectiveDockBottomGap + dock.effectiveDockMargin) : 1;
|
||||||
}
|
}
|
||||||
// Keep the wider hit area regardless of the reveal state to prevent shrinking loop
|
// Keep the wider hit area regardless of the reveal state to prevent shrinking loop
|
||||||
return Math.min(dockBackground.width + 8 + dock.borderThickness, maxDockWidth);
|
return Math.min(dockBackground.width + 8 + dock.borderThickness, maxDockWidth);
|
||||||
@@ -505,7 +643,11 @@ Variants {
|
|||||||
return 0;
|
return 0;
|
||||||
if (dock.reveal)
|
if (dock.reveal)
|
||||||
return 0;
|
return 0;
|
||||||
const hideDistance = dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin + 10;
|
if (Theme.isConnectedEffect) {
|
||||||
|
const retractDist = dockBackground.width + SettingsData.dockSpacing + 10;
|
||||||
|
return SettingsData.dockPosition === SettingsData.Position.Right ? retractDist : -retractDist;
|
||||||
|
}
|
||||||
|
const hideDistance = dock.connectedJoinInset + dock.effectiveBarHeight + SettingsData.dockSpacing + dock.effectiveDockBottomGap + dock.effectiveDockMargin + 10;
|
||||||
if (SettingsData.dockPosition === SettingsData.Position.Right) {
|
if (SettingsData.dockPosition === SettingsData.Position.Right) {
|
||||||
return hideDistance;
|
return hideDistance;
|
||||||
} else {
|
} else {
|
||||||
@@ -517,7 +659,11 @@ Variants {
|
|||||||
return 0;
|
return 0;
|
||||||
if (dock.reveal)
|
if (dock.reveal)
|
||||||
return 0;
|
return 0;
|
||||||
const hideDistance = dock.effectiveBarHeight + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin + 10;
|
if (Theme.isConnectedEffect) {
|
||||||
|
const retractDist = dockBackground.height + SettingsData.dockSpacing + 10;
|
||||||
|
return SettingsData.dockPosition === SettingsData.Position.Bottom ? retractDist : -retractDist;
|
||||||
|
}
|
||||||
|
const hideDistance = dock.connectedJoinInset + dock.effectiveBarHeight + SettingsData.dockSpacing + dock.effectiveDockBottomGap + dock.effectiveDockMargin + 10;
|
||||||
if (SettingsData.dockPosition === SettingsData.Position.Bottom) {
|
if (SettingsData.dockPosition === SettingsData.Position.Bottom) {
|
||||||
return hideDistance;
|
return hideDistance;
|
||||||
} else {
|
} else {
|
||||||
@@ -528,18 +674,25 @@ Variants {
|
|||||||
Behavior on x {
|
Behavior on x {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
id: slideXAnimation
|
id: slideXAnimation
|
||||||
duration: Theme.shortDuration
|
duration: Theme.isConnectedEffect ? Theme.variantDuration(Theme.popoutAnimationDuration, dock.reveal) : Theme.shortDuration
|
||||||
easing.type: Easing.OutCubic
|
easing.type: Theme.isConnectedEffect ? Easing.BezierSpline : Easing.OutCubic
|
||||||
|
easing.bezierCurve: Theme.isConnectedEffect ? (dock.reveal ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve) : []
|
||||||
|
onRunningChanged: if (!running) dock._syncDockChromeState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on y {
|
Behavior on y {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
id: slideYAnimation
|
id: slideYAnimation
|
||||||
duration: Theme.shortDuration
|
duration: Theme.isConnectedEffect ? Theme.variantDuration(Theme.popoutAnimationDuration, dock.reveal) : Theme.shortDuration
|
||||||
easing.type: Easing.OutCubic
|
easing.type: Theme.isConnectedEffect ? Easing.BezierSpline : Easing.OutCubic
|
||||||
|
easing.bezierCurve: Theme.isConnectedEffect ? (dock.reveal ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve) : []
|
||||||
|
onRunningChanged: if (!running) dock._syncDockChromeState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onXChanged: dock._queueSlideSync()
|
||||||
|
onYChanged: dock._queueSlideSync()
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -553,33 +706,60 @@ Variants {
|
|||||||
right: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined) : undefined
|
right: dock.isVertical ? (SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined) : undefined
|
||||||
verticalCenter: dock.isVertical ? parent.verticalCenter : undefined
|
verticalCenter: dock.isVertical ? parent.verticalCenter : undefined
|
||||||
}
|
}
|
||||||
anchors.topMargin: !dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Top ? barSpacing + SettingsData.dockMargin + 1 + dock.borderThickness : 0
|
anchors.topMargin: !dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Top ? (dock.connectedJoinInset + dock.joinedEdgeMargin) : 0
|
||||||
anchors.bottomMargin: !dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Bottom ? barSpacing + SettingsData.dockMargin + 1 + dock.borderThickness : 0
|
anchors.bottomMargin: !dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Bottom ? (dock.connectedJoinInset + dock.joinedEdgeMargin) : 0
|
||||||
anchors.leftMargin: dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Left ? barSpacing + SettingsData.dockMargin + 1 + dock.borderThickness : 0
|
anchors.leftMargin: dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Left ? (dock.connectedJoinInset + dock.joinedEdgeMargin) : 0
|
||||||
anchors.rightMargin: dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Right ? barSpacing + SettingsData.dockMargin + 1 + dock.borderThickness : 0
|
anchors.rightMargin: dock.isVertical && SettingsData.dockPosition === SettingsData.Position.Right ? (dock.connectedJoinInset + dock.joinedEdgeMargin) : 0
|
||||||
|
|
||||||
implicitWidth: dock.isVertical ? (dockApps.implicitHeight + SettingsData.dockSpacing * 2) : (dockApps.implicitWidth + SettingsData.dockSpacing * 2)
|
implicitWidth: dock.isVertical ? (dockApps.implicitHeight + SettingsData.dockSpacing * 2) : (dockApps.implicitWidth + SettingsData.dockSpacing * 2)
|
||||||
implicitHeight: dock.isVertical ? (dockApps.implicitWidth + SettingsData.dockSpacing * 2) : (dockApps.implicitHeight + SettingsData.dockSpacing * 2)
|
implicitHeight: dock.isVertical ? (dockApps.implicitWidth + SettingsData.dockSpacing * 2) : (dockApps.implicitHeight + SettingsData.dockSpacing * 2)
|
||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
height: implicitHeight
|
height: implicitHeight
|
||||||
|
|
||||||
layer.enabled: true
|
// Avoid an offscreen texture seam where the connected dock meets the frame.
|
||||||
|
layer.enabled: !Theme.isConnectedEffect
|
||||||
clip: false
|
clip: false
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Theme.withAlpha(Theme.surfaceContainer, backgroundTransparency)
|
visible: !SettingsData.connectedFrameModeActive && !(Theme.isConnectedEffect && dock.reveal)
|
||||||
radius: Theme.cornerRadius
|
color: dock.surfaceColor
|
||||||
|
topLeftRadius: dock.surfaceTopLeftRadius
|
||||||
|
topRightRadius: dock.surfaceTopRightRadius
|
||||||
|
bottomLeftRadius: dock.surfaceBottomLeftRadius
|
||||||
|
bottomRightRadius: dock.surfaceBottomRightRadius
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
visible: !SettingsData.connectedFrameModeActive && !(Theme.isConnectedEffect && dock.reveal)
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
radius: Theme.cornerRadius
|
topLeftRadius: dock.surfaceTopLeftRadius
|
||||||
border.color: BlurService.borderColor
|
topRightRadius: dock.surfaceTopRightRadius
|
||||||
border.width: BlurService.borderWidth
|
bottomLeftRadius: dock.surfaceBottomLeftRadius
|
||||||
|
bottomRightRadius: dock.surfaceBottomRightRadius
|
||||||
|
border.color: dock.surfaceBorderColor
|
||||||
|
border.width: dock.surfaceBorderWidth
|
||||||
z: 100
|
z: 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync dockBackground geometry to ConnectedModeState
|
||||||
|
onXChanged: dock._syncDockChromeState()
|
||||||
|
onYChanged: dock._syncDockChromeState()
|
||||||
|
onWidthChanged: dock._syncDockChromeState()
|
||||||
|
onHeightChanged: dock._syncDockChromeState()
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectedShape {
|
||||||
|
visible: Theme.isConnectedEffect && dock.reveal && !SettingsData.connectedFrameModeActive
|
||||||
|
barSide: dock.connectedBarSide
|
||||||
|
bodyWidth: dockBackground.width
|
||||||
|
bodyHeight: dockBackground.height
|
||||||
|
connectorRadius: Theme.connectedCornerRadius
|
||||||
|
surfaceRadius: dock.surfaceRadius
|
||||||
|
fillColor: dock.surfaceColor
|
||||||
|
x: dockBackground.x - bodyX
|
||||||
|
y: dockBackground.y - bodyY
|
||||||
}
|
}
|
||||||
|
|
||||||
Shape {
|
Shape {
|
||||||
@@ -588,12 +768,12 @@ Variants {
|
|||||||
y: dockBackground.y - borderThickness
|
y: dockBackground.y - borderThickness
|
||||||
width: dockBackground.width + borderThickness * 2
|
width: dockBackground.width + borderThickness * 2
|
||||||
height: dockBackground.height + borderThickness * 2
|
height: dockBackground.height + borderThickness * 2
|
||||||
visible: SettingsData.dockBorderEnabled && dock.hasApps
|
visible: SettingsData.dockBorderEnabled && dock.hasApps && !Theme.isConnectedEffect
|
||||||
preferredRendererType: Shape.CurveRenderer
|
preferredRendererType: Shape.CurveRenderer
|
||||||
|
|
||||||
readonly property real borderThickness: Math.max(1, dock.borderThickness)
|
readonly property real borderThickness: Math.max(1, dock.borderThickness)
|
||||||
readonly property real i: borderThickness / 2
|
readonly property real i: borderThickness / 2
|
||||||
readonly property real cr: Theme.cornerRadius
|
readonly property real cr: dock.surfaceRadius
|
||||||
readonly property real w: dockBackground.width
|
readonly property real w: dockBackground.width
|
||||||
readonly property real h: dockBackground.height
|
readonly property real h: dockBackground.height
|
||||||
|
|
||||||
|
|||||||
17
quickshell/Modules/Frame/Frame.qml
Normal file
17
quickshell/Modules/Frame/Frame.qml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
model: Quickshell.screens
|
||||||
|
|
||||||
|
FrameInstance {
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
screen: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
54
quickshell/Modules/Frame/FrameBorder.qml
Normal file
54
quickshell/Modules/Frame/FrameBorder.qml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
required property real cutoutTopInset
|
||||||
|
required property real cutoutBottomInset
|
||||||
|
required property real cutoutLeftInset
|
||||||
|
required property real cutoutRightInset
|
||||||
|
required property real cutoutRadius
|
||||||
|
property color borderColor: Qt.rgba(SettingsData.effectiveFrameColor.r, SettingsData.effectiveFrameColor.g, SettingsData.effectiveFrameColor.b, SettingsData.frameOpacity)
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: borderRect
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
// Bake frameOpacity into the color alpha rather than using the `opacity` property
|
||||||
|
color: root.borderColor
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
maskSource: cutoutMask
|
||||||
|
maskEnabled: true
|
||||||
|
maskInverted: true
|
||||||
|
maskThresholdMin: 0.5
|
||||||
|
maskSpreadAtMin: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: cutoutMask
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
layer.enabled: true
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
topMargin: root.cutoutTopInset
|
||||||
|
bottomMargin: root.cutoutBottomInset
|
||||||
|
leftMargin: root.cutoutLeftInset
|
||||||
|
rightMargin: root.cutoutRightInset
|
||||||
|
}
|
||||||
|
radius: root.cutoutRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
87
quickshell/Modules/Frame/FrameExclusions.qml
Normal file
87
quickshell/Modules/Frame/FrameExclusions.qml
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var screen
|
||||||
|
|
||||||
|
readonly property var barEdges: {
|
||||||
|
SettingsData.barConfigs; // force re-eval when bar configs change
|
||||||
|
return SettingsData.getActiveBarEdgesForScreen(screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// One thin invisible PanelWindow per edge.
|
||||||
|
// Skips any edge where a bar already provides its own exclusiveZone.
|
||||||
|
|
||||||
|
readonly property bool screenEnabled: SettingsData.frameEnabled && SettingsData.isScreenInPreferences(root.screen, SettingsData.frameScreenPreferences)
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: root.screenEnabled && !root.barEdges.includes("top")
|
||||||
|
sourceComponent: EdgeExclusion {
|
||||||
|
targetScreen: root.screen
|
||||||
|
anchorTop: true
|
||||||
|
anchorLeft: true
|
||||||
|
anchorRight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: root.screenEnabled && !root.barEdges.includes("bottom")
|
||||||
|
sourceComponent: EdgeExclusion {
|
||||||
|
targetScreen: root.screen
|
||||||
|
anchorBottom: true
|
||||||
|
anchorLeft: true
|
||||||
|
anchorRight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: root.screenEnabled && !root.barEdges.includes("left")
|
||||||
|
sourceComponent: EdgeExclusion {
|
||||||
|
targetScreen: root.screen
|
||||||
|
anchorLeft: true
|
||||||
|
anchorTop: true
|
||||||
|
anchorBottom: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: root.screenEnabled && !root.barEdges.includes("right")
|
||||||
|
sourceComponent: EdgeExclusion {
|
||||||
|
targetScreen: root.screen
|
||||||
|
anchorRight: true
|
||||||
|
anchorTop: true
|
||||||
|
anchorBottom: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component EdgeExclusion: PanelWindow {
|
||||||
|
required property var targetScreen
|
||||||
|
|
||||||
|
screen: targetScreen
|
||||||
|
property bool anchorTop: false
|
||||||
|
property bool anchorBottom: false
|
||||||
|
property bool anchorLeft: false
|
||||||
|
property bool anchorRight: false
|
||||||
|
|
||||||
|
WlrLayershell.namespace: "dms:frame-exclusion"
|
||||||
|
WlrLayershell.layer: WlrLayer.Top
|
||||||
|
exclusiveZone: SettingsData.frameThickness
|
||||||
|
color: "transparent"
|
||||||
|
mask: Region {}
|
||||||
|
implicitWidth: 1
|
||||||
|
implicitHeight: 1
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: anchorTop
|
||||||
|
bottom: anchorBottom
|
||||||
|
left: anchorLeft
|
||||||
|
right: anchorRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
quickshell/Modules/Frame/FrameInstance.qml
Normal file
18
quickshell/Modules/Frame/FrameInstance.qml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var screen
|
||||||
|
|
||||||
|
FrameWindow {
|
||||||
|
targetScreen: root.screen
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameExclusions {
|
||||||
|
screen: root.screen
|
||||||
|
}
|
||||||
|
}
|
||||||
1347
quickshell/Modules/Frame/FrameWindow.qml
Normal file
1347
quickshell/Modules/Frame/FrameWindow.qml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -16,8 +16,7 @@ DankListView {
|
|||||||
property bool listInitialized: false
|
property bool listInitialized: false
|
||||||
property int swipingCardIndex: -1
|
property int swipingCardIndex: -1
|
||||||
property real swipingCardOffset: 0
|
property real swipingCardOffset: 0
|
||||||
property real __pendingStableHeight: 0
|
property bool _stableHeightUpdatePending: false
|
||||||
property real __heightUpdateThreshold: 20
|
|
||||||
readonly property real shadowBlurPx: Theme.elevationEnabled ? ((Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined) ? Theme.elevationLevel1.blurPx : 4) : 0
|
readonly property real shadowBlurPx: Theme.elevationEnabled ? ((Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined) ? Theme.elevationLevel1.blurPx : 4) : 0
|
||||||
readonly property real shadowHorizontalGutter: Theme.snap(Math.max(Theme.spacingS, Math.min(32, shadowBlurPx * 1.5 + 6)), 1)
|
readonly property real shadowHorizontalGutter: Theme.snap(Math.max(Theme.spacingS, Math.min(32, shadowBlurPx * 1.5 + 6)), 1)
|
||||||
readonly property real shadowVerticalGutter: Theme.snap(Math.max(Theme.spacingXS, 6), 1)
|
readonly property real shadowVerticalGutter: Theme.snap(Math.max(Theme.spacingXS, 6), 1)
|
||||||
@@ -27,51 +26,52 @@ DankListView {
|
|||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (listView) {
|
if (listView) {
|
||||||
listView.listInitialized = true;
|
listView.listInitialized = true;
|
||||||
listView.stableContentHeight = listView.contentHeight;
|
listView.syncStableContentHeight(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
function targetContentHeight() {
|
||||||
id: heightUpdateDebounce
|
if (count <= 0)
|
||||||
interval: Theme.mediumDuration + 20
|
return contentHeight;
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
let total = topMargin + bottomMargin + Math.max(0, count - 1) * spacing;
|
||||||
if (!listView.isAnimatingExpansion && Math.abs(listView.__pendingStableHeight - listView.stableContentHeight) > listView.__heightUpdateThreshold) {
|
for (let i = 0; i < count; i++) {
|
||||||
listView.stableContentHeight = listView.__pendingStableHeight;
|
const item = itemAtIndex(i);
|
||||||
}
|
if (!item || item.nonAnimHeight === undefined)
|
||||||
|
return contentHeight;
|
||||||
|
total += item.nonAnimHeight;
|
||||||
}
|
}
|
||||||
|
return Math.max(0, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncStableContentHeight(useTarget) {
|
||||||
|
const nextHeight = useTarget ? targetContentHeight() : contentHeight;
|
||||||
|
if (Math.abs(nextHeight - stableContentHeight) <= 0.5)
|
||||||
|
return;
|
||||||
|
stableContentHeight = nextHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueStableContentHeightUpdate(useTarget) {
|
||||||
|
if (_stableHeightUpdatePending)
|
||||||
|
return;
|
||||||
|
_stableHeightUpdatePending = true;
|
||||||
|
Qt.callLater(() => {
|
||||||
|
_stableHeightUpdatePending = false;
|
||||||
|
syncStableContentHeight(useTarget || isAnimatingExpansion);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onContentHeightChanged: {
|
onContentHeightChanged: {
|
||||||
if (!isAnimatingExpansion) {
|
if (!isAnimatingExpansion)
|
||||||
__pendingStableHeight = contentHeight;
|
queueStableContentHeightUpdate(false);
|
||||||
if (Math.abs(contentHeight - stableContentHeight) > __heightUpdateThreshold) {
|
|
||||||
heightUpdateDebounce.restart();
|
|
||||||
} else {
|
|
||||||
stableContentHeight = contentHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onIsAnimatingExpansionChanged: {
|
onIsAnimatingExpansionChanged: {
|
||||||
if (isAnimatingExpansion) {
|
if (isAnimatingExpansion) {
|
||||||
heightUpdateDebounce.stop();
|
syncStableContentHeight(true);
|
||||||
let delta = 0;
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
const item = itemAtIndex(i);
|
|
||||||
if (item && item.children[0] && item.children[0].isAnimating) {
|
|
||||||
const targetDelegateHeight = item.children[0].targetHeight + listView.delegateShadowGutter;
|
|
||||||
delta += targetDelegateHeight - item.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const targetHeight = contentHeight + delta;
|
|
||||||
// During expansion, always update immediately without threshold check
|
|
||||||
stableContentHeight = targetHeight;
|
|
||||||
} else {
|
} else {
|
||||||
__pendingStableHeight = contentHeight;
|
queueStableContentHeightUpdate(false);
|
||||||
heightUpdateDebounce.stop();
|
|
||||||
stableContentHeight = __pendingStableHeight;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,11 +148,14 @@ DankListView {
|
|||||||
readonly property real adjacentScaleInfluence: isAdjacentToSwipe ? 1.0 - Math.abs(listView.swipingCardOffset) / width * 0.02 : 1.0
|
readonly property real adjacentScaleInfluence: isAdjacentToSwipe ? 1.0 - Math.abs(listView.swipingCardOffset) / width * 0.02 : 1.0
|
||||||
readonly property real swipeFadeStartOffset: width * 0.75
|
readonly property real swipeFadeStartOffset: width * 0.75
|
||||||
readonly property real swipeFadeDistance: Math.max(1, width - swipeFadeStartOffset)
|
readonly property real swipeFadeDistance: Math.max(1, width - swipeFadeStartOffset)
|
||||||
|
readonly property real nonAnimHeight: notificationCard.targetHeight + listView.delegateShadowGutter
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (delegateRoot)
|
if (delegateRoot) {
|
||||||
delegateRoot.__delegateInitialized = true;
|
delegateRoot.__delegateInitialized = true;
|
||||||
|
listView.queueStableContentHeightUpdate(listView.isAnimatingExpansion);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,6 +183,7 @@ DankListView {
|
|||||||
onIsAnimatingChanged: {
|
onIsAnimatingChanged: {
|
||||||
if (isAnimating) {
|
if (isAnimating) {
|
||||||
listView.isAnimatingExpansion = true;
|
listView.isAnimatingExpansion = true;
|
||||||
|
listView.syncStableContentHeight(true);
|
||||||
} else {
|
} else {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (!notificationCard || !listView)
|
if (!notificationCard || !listView)
|
||||||
@@ -197,6 +201,13 @@ DankListView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTargetHeightChanged: {
|
||||||
|
if (isAnimating || listView.isAnimatingExpansion)
|
||||||
|
listView.syncStableContentHeight(true);
|
||||||
|
else
|
||||||
|
listView.queueStableContentHeightUpdate(false);
|
||||||
|
}
|
||||||
|
|
||||||
isGroupSelected: {
|
isGroupSelected: {
|
||||||
if (!keyboardController || !keyboardController.keyboardNavigationActive || !listView.keyboardActive)
|
if (!keyboardController || !keyboardController.keyboardNavigationActive || !listView.keyboardActive)
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ Rectangle {
|
|||||||
property bool userInitiatedExpansion: false
|
property bool userInitiatedExpansion: false
|
||||||
property bool isAnimating: false
|
property bool isAnimating: false
|
||||||
property bool animateExpansion: true
|
property bool animateExpansion: true
|
||||||
|
property bool _retainedExpandedContent: false
|
||||||
|
property bool _clipAnimatedContent: false
|
||||||
|
property real expandedContentOpacity: expanded ? 1 : 0
|
||||||
|
property real collapsedContentOpacity: expanded ? 0 : 1
|
||||||
|
readonly property bool renderExpandedContent: expanded || _retainedExpandedContent
|
||||||
|
readonly property bool renderCollapsedContent: !expanded
|
||||||
|
|
||||||
property bool isGroupSelected: false
|
property bool isGroupSelected: false
|
||||||
property int selectedNotificationIndex: -1
|
property int selectedNotificationIndex: -1
|
||||||
@@ -34,11 +40,12 @@ Rectangle {
|
|||||||
readonly property real actionButtonHeight: compactMode ? 20 : 24
|
readonly property real actionButtonHeight: compactMode ? 20 : 24
|
||||||
readonly property real collapsedContentHeight: Math.max(iconSize, Theme.fontSizeSmall * 1.2 + Theme.fontSizeMedium * 1.2 + Theme.fontSizeSmall * 1.2 * (compactMode ? 1 : 2))
|
readonly property real collapsedContentHeight: Math.max(iconSize, Theme.fontSizeSmall * 1.2 + Theme.fontSizeMedium * 1.2 + Theme.fontSizeSmall * 1.2 * (compactMode ? 1 : 2))
|
||||||
readonly property real baseCardHeight: cardPadding * 2 + collapsedContentHeight + actionButtonHeight + contentSpacing
|
readonly property real baseCardHeight: cardPadding * 2 + collapsedContentHeight + actionButtonHeight + contentSpacing
|
||||||
|
readonly property bool connectedFrameMode: SettingsData.connectedFrameModeActive
|
||||||
|
|
||||||
width: parent ? parent.width : 400
|
width: parent ? parent.width : 400
|
||||||
height: expanded ? (expandedContent.height + cardPadding * 2) : (baseCardHeight + collapsedContent.extraHeight)
|
height: expanded ? (expandedContent.height + cardPadding * 2) : (baseCardHeight + collapsedContent.extraHeight)
|
||||||
readonly property real targetHeight: expanded ? (expandedContent.height + cardPadding * 2) : (baseCardHeight + collapsedContent.extraHeight)
|
readonly property real targetHeight: expanded ? (expandedContent.height + cardPadding * 2) : (baseCardHeight + collapsedContent.extraHeight)
|
||||||
radius: Theme.cornerRadius
|
radius: connectedFrameMode ? Theme.connectedSurfaceRadius : Theme.cornerRadius
|
||||||
scale: (cardHoverHandler.hovered ? 1.004 : 1.0) * listLevelAdjacentScaleInfluence
|
scale: (cardHoverHandler.hovered ? 1.004 : 1.0) * listLevelAdjacentScaleInfluence
|
||||||
readonly property bool shadowsAllowed: Theme.elevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
readonly property bool shadowsAllowed: Theme.elevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
||||||
readonly property var shadowElevation: Theme.elevationLevel1
|
readonly property var shadowElevation: Theme.elevationLevel1
|
||||||
@@ -56,6 +63,14 @@ Rectangle {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function expansionMotionDuration() {
|
||||||
|
return root.connectedFrameMode ? Theme.variantDuration(Theme.popoutAnimationDuration, root.expanded) : (root.expanded ? Theme.notificationExpandDuration : Theme.notificationCollapseDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expansionMotionCurve() {
|
||||||
|
return root.connectedFrameMode ? (root.expanded ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve) : Theme.expressiveCurves.emphasized;
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on scale {
|
Behavior on scale {
|
||||||
enabled: listLevelScaleAnimationsEnabled
|
enabled: listLevelScaleAnimationsEnabled
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
@@ -65,6 +80,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Behavior on shadowBlurPx {
|
Behavior on shadowBlurPx {
|
||||||
|
enabled: !root.connectedFrameMode
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
@@ -72,6 +88,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Behavior on shadowOffsetXPx {
|
Behavior on shadowOffsetXPx {
|
||||||
|
enabled: !root.connectedFrameMode
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
@@ -79,6 +96,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Behavior on shadowOffsetYPx {
|
Behavior on shadowOffsetYPx {
|
||||||
|
enabled: !root.connectedFrameMode
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
@@ -93,6 +111,24 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on expandedContentOpacity {
|
||||||
|
enabled: root.__initialized && root.userInitiatedExpansion && root.animateExpansion
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.expansionMotionDuration()
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.expansionMotionCurve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on collapsedContentOpacity {
|
||||||
|
enabled: root.__initialized && root.userInitiatedExpansion && root.animateExpansion
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.expansionMotionDuration()
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: root.expansionMotionCurve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
color: {
|
color: {
|
||||||
if (isGroupSelected && keyboardNavigationActive) {
|
if (isGroupSelected && keyboardNavigationActive) {
|
||||||
return Theme.primaryPressed;
|
return Theme.primaryPressed;
|
||||||
@@ -100,6 +136,8 @@ Rectangle {
|
|||||||
if (keyboardNavigationActive && expanded && selectedNotificationIndex >= 0) {
|
if (keyboardNavigationActive && expanded && selectedNotificationIndex >= 0) {
|
||||||
return Theme.primaryHoverLight;
|
return Theme.primaryHoverLight;
|
||||||
}
|
}
|
||||||
|
if (connectedFrameMode)
|
||||||
|
return Theme.popupLayerColor(Theme.surfaceContainerHigh);
|
||||||
return Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency);
|
return Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency);
|
||||||
}
|
}
|
||||||
border.color: {
|
border.color: {
|
||||||
@@ -126,7 +164,31 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
clip: false
|
clip: connectedFrameMode && _clipAnimatedContent
|
||||||
|
|
||||||
|
onExpandedChanged: {
|
||||||
|
if (connectedFrameMode && __initialized && userInitiatedExpansion && animateExpansion)
|
||||||
|
_clipAnimatedContent = true;
|
||||||
|
if (expanded) {
|
||||||
|
_retainedExpandedContent = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (connectedFrameMode && __initialized && userInitiatedExpansion && animateExpansion)
|
||||||
|
_retainedExpandedContent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onHeightChanged: {
|
||||||
|
if (Math.abs(height - targetHeight) > 0.5)
|
||||||
|
return;
|
||||||
|
_clipAnimatedContent = false;
|
||||||
|
if (!expanded && _retainedExpandedContent)
|
||||||
|
_retainedExpandedContent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onExpandedContentOpacityChanged: {
|
||||||
|
if (!expanded && _retainedExpandedContent && expandedContentOpacity <= 0.01)
|
||||||
|
_retainedExpandedContent = false;
|
||||||
|
}
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: cardHoverHandler
|
id: cardHoverHandler
|
||||||
@@ -146,7 +208,7 @@ Rectangle {
|
|||||||
shadowOffsetX: root.shadowOffsetXPx
|
shadowOffsetX: root.shadowOffsetXPx
|
||||||
shadowOffsetY: root.shadowOffsetYPx
|
shadowOffsetY: root.shadowOffsetYPx
|
||||||
shadowColor: root.shadowElevation ? Theme.elevationShadowColor(root.shadowElevation) : "transparent"
|
shadowColor: root.shadowElevation ? Theme.elevationShadowColor(root.shadowElevation) : "transparent"
|
||||||
shadowEnabled: root.shadowsAllowed
|
shadowEnabled: root.shadowsAllowed && !root.connectedFrameMode
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -186,7 +248,8 @@ Rectangle {
|
|||||||
anchors.leftMargin: Theme.spacingL
|
anchors.leftMargin: Theme.spacingL
|
||||||
anchors.rightMargin: Theme.spacingL + Theme.notificationHoverRevealMargin
|
anchors.rightMargin: Theme.spacingL + Theme.notificationHoverRevealMargin
|
||||||
height: collapsedContentHeight + extraHeight
|
height: collapsedContentHeight + extraHeight
|
||||||
visible: !expanded
|
visible: renderCollapsedContent
|
||||||
|
opacity: root.collapsedContentOpacity
|
||||||
|
|
||||||
DankCircularImage {
|
DankCircularImage {
|
||||||
id: iconContainer
|
id: iconContainer
|
||||||
@@ -385,7 +448,8 @@ Rectangle {
|
|||||||
anchors.leftMargin: Theme.spacingL
|
anchors.leftMargin: Theme.spacingL
|
||||||
anchors.rightMargin: Theme.spacingL
|
anchors.rightMargin: Theme.spacingL
|
||||||
spacing: compactMode ? Theme.spacingXS : Theme.spacingS
|
spacing: compactMode ? Theme.spacingXS : Theme.spacingS
|
||||||
visible: expanded
|
visible: renderExpandedContent
|
||||||
|
opacity: root.expandedContentOpacity
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -828,7 +892,8 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
visible: !expanded
|
visible: renderCollapsedContent
|
||||||
|
opacity: root.collapsedContentOpacity
|
||||||
anchors.right: clearButton.visible ? clearButton.left : parent.right
|
anchors.right: clearButton.visible ? clearButton.left : parent.right
|
||||||
anchors.rightMargin: clearButton.visible ? contentSpacing : Theme.spacingL
|
anchors.rightMargin: clearButton.visible ? contentSpacing : Theme.spacingL
|
||||||
anchors.top: collapsedContent.bottom
|
anchors.top: collapsedContent.bottom
|
||||||
@@ -884,7 +949,8 @@ Rectangle {
|
|||||||
property bool isHovered: false
|
property bool isHovered: false
|
||||||
readonly property int actionCount: (notificationGroup?.latestNotification?.actions || []).length
|
readonly property int actionCount: (notificationGroup?.latestNotification?.actions || []).length
|
||||||
|
|
||||||
visible: !expanded && actionCount < 3
|
visible: renderCollapsedContent && actionCount < 3
|
||||||
|
opacity: root.collapsedContentOpacity
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Theme.spacingL
|
anchors.rightMargin: Theme.spacingL
|
||||||
anchors.top: collapsedContent.bottom
|
anchors.top: collapsedContent.bottom
|
||||||
@@ -915,7 +981,7 @@ Rectangle {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: !expanded && (notificationGroup?.count || 0) > 1 && !descriptionExpanded
|
visible: renderCollapsedContent && (notificationGroup?.count || 0) > 1 && !descriptionExpanded
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.userInitiatedExpansion = true;
|
root.userInitiatedExpansion = true;
|
||||||
@@ -959,15 +1025,17 @@ Rectangle {
|
|||||||
Behavior on height {
|
Behavior on height {
|
||||||
enabled: root.__initialized && root.userInitiatedExpansion && root.animateExpansion
|
enabled: root.__initialized && root.userInitiatedExpansion && root.animateExpansion
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: root.expanded ? Theme.notificationExpandDuration : Theme.notificationCollapseDuration
|
duration: root.expansionMotionDuration()
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasized
|
easing.bezierCurve: root.expansionMotionCurve()
|
||||||
onRunningChanged: {
|
onRunningChanged: {
|
||||||
if (running) {
|
if (running) {
|
||||||
root.isAnimating = true;
|
root.isAnimating = true;
|
||||||
} else {
|
} else {
|
||||||
root.isAnimating = false;
|
root.isAnimating = false;
|
||||||
root.userInitiatedExpansion = false;
|
root.userInitiatedExpansion = false;
|
||||||
|
root._retainedExpandedContent = false;
|
||||||
|
root._clipAnimatedContent = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ DankPopout {
|
|||||||
property real stablePopupHeight: 400
|
property real stablePopupHeight: 400
|
||||||
property real _lastAlignedContentHeight: -1
|
property real _lastAlignedContentHeight: -1
|
||||||
property bool _pendingSizedOpen: false
|
property bool _pendingSizedOpen: false
|
||||||
|
property bool _heightUpdatePending: false
|
||||||
|
|
||||||
function updateStablePopupHeight() {
|
function updateStablePopupHeight() {
|
||||||
const item = contentLoader.item;
|
const item = contentLoader.item;
|
||||||
@@ -30,6 +31,16 @@ DankPopout {
|
|||||||
stablePopupHeight = target;
|
stablePopupHeight = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function queueStablePopupHeightUpdate() {
|
||||||
|
if (_heightUpdatePending)
|
||||||
|
return;
|
||||||
|
_heightUpdatePending = true;
|
||||||
|
Qt.callLater(() => {
|
||||||
|
_heightUpdatePending = false;
|
||||||
|
updateStablePopupHeight();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
NotificationKeyboardController {
|
NotificationKeyboardController {
|
||||||
id: keyboardController
|
id: keyboardController
|
||||||
listView: null
|
listView: null
|
||||||
@@ -39,11 +50,9 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
popupWidth: triggerScreen ? Math.min(500, Math.max(380, triggerScreen.width - 48)) : 400
|
popupWidth: 400
|
||||||
popupHeight: stablePopupHeight
|
popupHeight: stablePopupHeight
|
||||||
positioning: ""
|
positioning: ""
|
||||||
animationScaleCollapsed: 0.94
|
|
||||||
animationOffset: 0
|
|
||||||
suspendShadowWhileResizing: false
|
suspendShadowWhileResizing: false
|
||||||
|
|
||||||
screen: triggerScreen
|
screen: triggerScreen
|
||||||
@@ -130,7 +139,7 @@ DankPopout {
|
|||||||
Connections {
|
Connections {
|
||||||
target: contentLoader.item
|
target: contentLoader.item
|
||||||
function onImplicitHeightChanged() {
|
function onImplicitHeightChanged() {
|
||||||
root.updateStablePopupHeight();
|
root.queueStablePopupHeightUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,29 @@ import qs.Widgets
|
|||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: win
|
id: win
|
||||||
|
|
||||||
|
readonly property bool connectedFrameMode: SettingsData.frameEnabled
|
||||||
|
&& Theme.isConnectedEffect
|
||||||
|
&& SettingsData.isScreenInPreferences(win.screen, SettingsData.frameScreenPreferences)
|
||||||
|
readonly property string notifBarSide: {
|
||||||
|
const pos = SettingsData.notificationPopupPosition;
|
||||||
|
if (pos === -1) return "top";
|
||||||
|
switch (pos) {
|
||||||
|
case SettingsData.Position.Top: return "right";
|
||||||
|
case SettingsData.Position.Left: return "left";
|
||||||
|
case SettingsData.Position.BottomCenter: return "bottom";
|
||||||
|
case SettingsData.Position.Right: return "right";
|
||||||
|
case SettingsData.Position.Bottom: return "left";
|
||||||
|
default: return "top";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WindowBlur {
|
WindowBlur {
|
||||||
targetWindow: win
|
targetWindow: win
|
||||||
blurX: content.x + content.cardInset + swipeTx.x + tx.x
|
blurX: content.x + content.cardInset + swipeTx.x + tx.x
|
||||||
blurY: content.y + content.cardInset + swipeTx.y + tx.y
|
blurY: content.y + content.cardInset + swipeTx.y + tx.y
|
||||||
blurWidth: !win._finalized ? Math.max(0, content.width - content.cardInset * 2) : 0
|
blurWidth: !win._finalized && !win.connectedFrameMode ? Math.max(0, content.width - content.cardInset * 2) : 0
|
||||||
blurHeight: !win._finalized ? Math.max(0, content.height - content.cardInset * 2) : 0
|
blurHeight: !win._finalized && !win.connectedFrameMode ? Math.max(0, content.height - content.cardInset * 2) : 0
|
||||||
blurRadius: Theme.cornerRadius
|
blurRadius: win.connectedFrameMode ? Theme.connectedSurfaceRadius : Theme.cornerRadius
|
||||||
}
|
}
|
||||||
|
|
||||||
WlrLayershell.namespace: "dms:notification-popup"
|
WlrLayershell.namespace: "dms:notification-popup"
|
||||||
@@ -25,6 +41,15 @@ PanelWindow {
|
|||||||
required property string notificationId
|
required property string notificationId
|
||||||
readonly property bool hasValidData: notificationData && notificationData.notification
|
readonly property bool hasValidData: notificationData && notificationData.notification
|
||||||
readonly property alias hovered: cardHoverHandler.hovered
|
readonly property alias hovered: cardHoverHandler.hovered
|
||||||
|
readonly property alias swipeActive: content.swipeActive
|
||||||
|
readonly property alias swipeDismissing: content.swipeDismissing
|
||||||
|
readonly property bool swipeDismissTowardEdge: {
|
||||||
|
if (content.swipeDismissing)
|
||||||
|
return _swipeDismissesTowardFrameEdge();
|
||||||
|
if (content.swipeActive)
|
||||||
|
return content.swipeOffset * _frameEdgeSwipeDirection() > 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
property int screenY: 0
|
property int screenY: 0
|
||||||
property bool exiting: false
|
property bool exiting: false
|
||||||
property bool _isDestroying: false
|
property bool _isDestroying: false
|
||||||
@@ -32,6 +57,29 @@ PanelWindow {
|
|||||||
property real _lastReportedAlignedHeight: -1
|
property real _lastReportedAlignedHeight: -1
|
||||||
property real _storedTopMargin: 0
|
property real _storedTopMargin: 0
|
||||||
property real _storedBottomMargin: 0
|
property real _storedBottomMargin: 0
|
||||||
|
readonly property bool directionalEffect: Theme.isDirectionalEffect
|
||||||
|
readonly property bool depthEffect: Theme.isDepthEffect
|
||||||
|
readonly property real entryTravel: {
|
||||||
|
const base = Math.abs(Theme.effectAnimOffset);
|
||||||
|
if (directionalEffect) {
|
||||||
|
if (isCenterPosition)
|
||||||
|
return Math.max(base, Math.round(content.height * 1.1));
|
||||||
|
return Math.max(base, Math.round(content.width * 0.95));
|
||||||
|
}
|
||||||
|
if (depthEffect)
|
||||||
|
return Math.max(base, 44);
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
readonly property real exitTravel: {
|
||||||
|
if (directionalEffect) {
|
||||||
|
if (isCenterPosition)
|
||||||
|
return Math.max(1, content.height);
|
||||||
|
return Math.max(1, content.width);
|
||||||
|
}
|
||||||
|
if (depthEffect)
|
||||||
|
return Math.round(entryTravel * 1.35);
|
||||||
|
return Anims.slidePx;
|
||||||
|
}
|
||||||
readonly property string clearText: I18n.tr("Dismiss")
|
readonly property string clearText: I18n.tr("Dismiss")
|
||||||
property bool descriptionExpanded: false
|
property bool descriptionExpanded: false
|
||||||
readonly property bool hasExpandableBody: (notificationData?.htmlBody || "").replace(/<[^>]*>/g, "").trim().length > 0
|
readonly property bool hasExpandableBody: (notificationData?.htmlBody || "").replace(/<[^>]*>/g, "").trim().length > 0
|
||||||
@@ -61,6 +109,7 @@ PanelWindow {
|
|||||||
signal exitStarted
|
signal exitStarted
|
||||||
signal exitFinished
|
signal exitFinished
|
||||||
signal popupHeightChanged
|
signal popupHeightChanged
|
||||||
|
signal popupChromeGeometryChanged
|
||||||
|
|
||||||
function startExit() {
|
function startExit() {
|
||||||
if (exiting || _isDestroying) {
|
if (exiting || _isDestroying) {
|
||||||
@@ -68,6 +117,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
exiting = true;
|
exiting = true;
|
||||||
exitStarted();
|
exitStarted();
|
||||||
|
popupChromeGeometryChanged();
|
||||||
exitAnim.restart();
|
exitAnim.restart();
|
||||||
exitWatchdog.restart();
|
exitWatchdog.restart();
|
||||||
if (NotificationService.removeFromVisibleNotifications)
|
if (NotificationService.removeFromVisibleNotifications)
|
||||||
@@ -147,7 +197,8 @@ PanelWindow {
|
|||||||
id: implicitHeightAnim
|
id: implicitHeightAnim
|
||||||
duration: descriptionExpanded ? Theme.notificationExpandDuration : Theme.notificationCollapseDuration
|
duration: descriptionExpanded ? Theme.notificationExpandDuration : Theme.notificationCollapseDuration
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasized
|
easing.bezierCurve: descriptionExpanded ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
|
||||||
|
onFinished: win.popupHeightChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +246,8 @@ PanelWindow {
|
|||||||
readonly property real maxPopupShadowBlurPx: Math.max((Theme.elevationLevel3 && Theme.elevationLevel3.blurPx !== undefined) ? Theme.elevationLevel3.blurPx : 12, (Theme.elevationLevel4 && Theme.elevationLevel4.blurPx !== undefined) ? Theme.elevationLevel4.blurPx : 16)
|
readonly property real maxPopupShadowBlurPx: Math.max((Theme.elevationLevel3 && Theme.elevationLevel3.blurPx !== undefined) ? Theme.elevationLevel3.blurPx : 12, (Theme.elevationLevel4 && Theme.elevationLevel4.blurPx !== undefined) ? Theme.elevationLevel4.blurPx : 16)
|
||||||
readonly property real maxPopupShadowOffsetXPx: Math.max(Math.abs(Theme.elevationOffsetX(Theme.elevationLevel3)), Math.abs(Theme.elevationOffsetX(Theme.elevationLevel4)))
|
readonly property real maxPopupShadowOffsetXPx: Math.max(Math.abs(Theme.elevationOffsetX(Theme.elevationLevel3)), Math.abs(Theme.elevationOffsetX(Theme.elevationLevel4)))
|
||||||
readonly property real maxPopupShadowOffsetYPx: Math.max(Math.abs(Theme.elevationOffsetY(Theme.elevationLevel3, 6)), Math.abs(Theme.elevationOffsetY(Theme.elevationLevel4, 8)))
|
readonly property real maxPopupShadowOffsetYPx: Math.max(Math.abs(Theme.elevationOffsetY(Theme.elevationLevel3, 6)), Math.abs(Theme.elevationOffsetY(Theme.elevationLevel4, 8)))
|
||||||
readonly property real windowShadowPad: Theme.elevationEnabled && SettingsData.notificationPopupShadowEnabled ? Theme.snap(Math.max(16, maxPopupShadowBlurPx + Math.max(maxPopupShadowOffsetXPx, maxPopupShadowOffsetYPx) + 8), dpr) : 0
|
readonly property bool popupWindowShadowActive: Theme.elevationEnabled && SettingsData.notificationPopupShadowEnabled && !connectedFrameMode
|
||||||
|
readonly property real windowShadowPad: popupWindowShadowActive ? Theme.snap(Math.max(16, maxPopupShadowBlurPx + Math.max(maxPopupShadowOffsetXPx, maxPopupShadowOffsetYPx) + 8), dpr) : 0
|
||||||
|
|
||||||
anchors.top: true
|
anchors.top: true
|
||||||
anchors.left: true
|
anchors.left: true
|
||||||
@@ -240,12 +292,26 @@ PanelWindow {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _frameEdgeInset(side) {
|
||||||
|
if (!screen)
|
||||||
|
return 0;
|
||||||
|
const edges = SettingsData.getActiveBarEdgesForScreen(screen);
|
||||||
|
const raw = edges.includes(side) ? SettingsData.frameBarSize : SettingsData.frameThickness;
|
||||||
|
return Math.max(0, Math.round(Theme.px(raw, dpr)));
|
||||||
|
}
|
||||||
|
|
||||||
function getTopMargin() {
|
function getTopMargin() {
|
||||||
const popupPos = SettingsData.notificationPopupPosition;
|
const popupPos = SettingsData.notificationPopupPosition;
|
||||||
const isTop = isTopCenter || popupPos === SettingsData.Position.Top || popupPos === SettingsData.Position.Left;
|
const isTop = isTopCenter || popupPos === SettingsData.Position.Top || popupPos === SettingsData.Position.Left;
|
||||||
if (!isTop)
|
if (!isTop)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (connectedFrameMode) {
|
||||||
|
const cornerClear = (isCenterPosition || SettingsData.frameCloseGaps)
|
||||||
|
? 0
|
||||||
|
: (Theme.px(SettingsData.frameRounding, dpr) + Theme.px(Theme.connectedCornerRadius, dpr));
|
||||||
|
return _frameEdgeInset("top") + cornerClear + screenY;
|
||||||
|
}
|
||||||
const barInfo = getBarInfo();
|
const barInfo = getBarInfo();
|
||||||
const base = barInfo.topBar > 0 ? barInfo.topBar : Theme.popupDistance;
|
const base = barInfo.topBar > 0 ? barInfo.topBar : Theme.popupDistance;
|
||||||
return base + screenY;
|
return base + screenY;
|
||||||
@@ -257,6 +323,12 @@ PanelWindow {
|
|||||||
if (!isBottom)
|
if (!isBottom)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (connectedFrameMode) {
|
||||||
|
const cornerClear = (isCenterPosition || SettingsData.frameCloseGaps)
|
||||||
|
? 0
|
||||||
|
: (Theme.px(SettingsData.frameRounding, dpr) + Theme.px(Theme.connectedCornerRadius, dpr));
|
||||||
|
return _frameEdgeInset("bottom") + cornerClear + screenY;
|
||||||
|
}
|
||||||
const barInfo = getBarInfo();
|
const barInfo = getBarInfo();
|
||||||
const base = barInfo.bottomBar > 0 ? barInfo.bottomBar : Theme.popupDistance;
|
const base = barInfo.bottomBar > 0 ? barInfo.bottomBar : Theme.popupDistance;
|
||||||
return base + screenY;
|
return base + screenY;
|
||||||
@@ -271,6 +343,8 @@ PanelWindow {
|
|||||||
if (!isLeft)
|
if (!isLeft)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (connectedFrameMode)
|
||||||
|
return _frameEdgeInset("left");
|
||||||
const barInfo = getBarInfo();
|
const barInfo = getBarInfo();
|
||||||
return barInfo.leftBar > 0 ? barInfo.leftBar : Theme.popupDistance;
|
return barInfo.leftBar > 0 ? barInfo.leftBar : Theme.popupDistance;
|
||||||
}
|
}
|
||||||
@@ -284,6 +358,8 @@ PanelWindow {
|
|||||||
if (!isRight)
|
if (!isRight)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (connectedFrameMode)
|
||||||
|
return _frameEdgeInset("right");
|
||||||
const barInfo = getBarInfo();
|
const barInfo = getBarInfo();
|
||||||
return barInfo.rightBar > 0 ? barInfo.rightBar : Theme.popupDistance;
|
return barInfo.rightBar > 0 ? barInfo.rightBar : Theme.popupDistance;
|
||||||
}
|
}
|
||||||
@@ -328,10 +404,89 @@ PanelWindow {
|
|||||||
return Theme.snap(getContentY() - windowShadowPad, dpr);
|
return Theme.snap(getContentY() - windowShadowPad, dpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _swipeDismissTarget() {
|
||||||
|
return (content.swipeDismissDirection < 0 ? -1 : 1) * content.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _frameEdgeSwipeDirection() {
|
||||||
|
const popupPos = SettingsData.notificationPopupPosition;
|
||||||
|
return (popupPos === SettingsData.Position.Left || popupPos === SettingsData.Position.Bottom) ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _swipeDismissesTowardFrameEdge() {
|
||||||
|
return content.swipeDismissDirection === _frameEdgeSwipeDirection();
|
||||||
|
}
|
||||||
|
|
||||||
|
function popupChromeMotionActive() {
|
||||||
|
return popupChromeOpenProgress() < 1 || exiting || content.swipeActive || content.swipeDismissing || Math.abs(content.swipeOffset) > 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
function popupLayoutReservesSlot() {
|
||||||
|
return !content.swipeDismissing;
|
||||||
|
}
|
||||||
|
|
||||||
|
function popupChromeReservesSlot() {
|
||||||
|
return !content.swipeDismissing;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _chromeMotionOffset() {
|
||||||
|
return isCenterPosition ? tx.y : tx.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _chromeCardTravel() {
|
||||||
|
return Math.max(1, isCenterPosition ? alignedHeight : alignedWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
function popupChromeOpenProgress() {
|
||||||
|
if (exiting || content.swipeDismissing)
|
||||||
|
return 1;
|
||||||
|
return Math.max(0, Math.min(1, 1 - Math.abs(_chromeMotionOffset()) / _chromeCardTravel()));
|
||||||
|
}
|
||||||
|
|
||||||
|
function popupChromeReleaseProgress() {
|
||||||
|
if (exiting) {
|
||||||
|
const exitRel = Math.max(0, Math.min(1, Math.abs(_chromeMotionOffset()) / _chromeCardTravel()));
|
||||||
|
if (content.swipeDismissing) {
|
||||||
|
const swipeRel = Math.max(0, Math.min(1, Math.abs(content.swipeOffset) / Math.max(1, content.swipeTravelDistance)));
|
||||||
|
return Math.max(exitRel, swipeRel);
|
||||||
|
}
|
||||||
|
return exitRel;
|
||||||
|
}
|
||||||
|
if (content.swipeDismissing)
|
||||||
|
return Math.max(0, Math.min(1, Math.abs(content.swipeOffset) / Math.max(1, content.swipeTravelDistance)));
|
||||||
|
if (content.swipeActive && content.swipeOffset * _frameEdgeSwipeDirection() > 0)
|
||||||
|
return Math.max(0, Math.min(1, Math.abs(content.swipeOffset) / Math.max(1, content.swipeTravelDistance)));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function popupChromeFollowsCardMotion() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function popupChromeMotionX() {
|
||||||
|
if (!popupChromeMotionActive() || isCenterPosition)
|
||||||
|
return 0;
|
||||||
|
const motion = content.swipeOffset + tx.x;
|
||||||
|
if (content.swipeDismissing && !_swipeDismissesTowardFrameEdge())
|
||||||
|
return exiting ? Theme.snap(tx.x, dpr) : 0;
|
||||||
|
if (content.swipeActive && motion * _frameEdgeSwipeDirection() < 0)
|
||||||
|
return 0;
|
||||||
|
return Theme.snap(motion, dpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
function popupChromeMotionY() {
|
||||||
|
return popupChromeMotionActive() ? Theme.snap(tx.y, dpr) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
readonly property bool screenValid: win.screen && !_isDestroying
|
readonly property bool screenValid: win.screen && !_isDestroying
|
||||||
readonly property real dpr: screenValid ? CompositorService.getScreenScale(win.screen) : 1
|
readonly property real dpr: screenValid ? CompositorService.getScreenScale(win.screen) : 1
|
||||||
readonly property real alignedWidth: Theme.px(Math.max(0, implicitWidth - (windowShadowPad * 2)), dpr)
|
readonly property real alignedWidth: Theme.px(Math.max(0, implicitWidth - (windowShadowPad * 2)), dpr)
|
||||||
readonly property real alignedHeight: Theme.px(Math.max(0, implicitHeight - (windowShadowPad * 2)), dpr)
|
readonly property real alignedHeight: Theme.px(Math.max(0, implicitHeight - (windowShadowPad * 2)), dpr)
|
||||||
|
onScreenYChanged: popupChromeGeometryChanged()
|
||||||
|
onScreenChanged: popupChromeGeometryChanged()
|
||||||
|
onConnectedFrameModeChanged: popupChromeGeometryChanged()
|
||||||
|
onAlignedWidthChanged: popupChromeGeometryChanged()
|
||||||
|
onAlignedHeightChanged: popupChromeGeometryChanged()
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: content
|
id: content
|
||||||
@@ -340,7 +495,7 @@ PanelWindow {
|
|||||||
y: Theme.snap(windowShadowPad, dpr)
|
y: Theme.snap(windowShadowPad, dpr)
|
||||||
width: alignedWidth
|
width: alignedWidth
|
||||||
height: alignedHeight
|
height: alignedHeight
|
||||||
visible: !win._finalized
|
visible: !win._finalized && !chromeOnlyExit
|
||||||
scale: cardHoverHandler.hovered ? 1.01 : 1.0
|
scale: cardHoverHandler.hovered ? 1.01 : 1.0
|
||||||
transformOrigin: Item.Center
|
transformOrigin: Item.Center
|
||||||
|
|
||||||
@@ -352,15 +507,27 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property real swipeOffset: 0
|
property real swipeOffset: 0
|
||||||
readonly property real dismissThreshold: isCenterPosition ? height * 0.4 : width * 0.35
|
property real swipeDismissDirection: 1
|
||||||
|
property bool chromeOnlyExit: false
|
||||||
|
readonly property real dismissThreshold: width * 0.35
|
||||||
readonly property real swipeFadeStartRatio: 0.75
|
readonly property real swipeFadeStartRatio: 0.75
|
||||||
readonly property real swipeTravelDistance: isCenterPosition ? height : width
|
readonly property real swipeTravelDistance: width
|
||||||
readonly property real swipeFadeStartOffset: swipeTravelDistance * swipeFadeStartRatio
|
readonly property real swipeFadeStartOffset: swipeTravelDistance * swipeFadeStartRatio
|
||||||
readonly property real swipeFadeDistance: Math.max(1, swipeTravelDistance - swipeFadeStartOffset)
|
readonly property real swipeFadeDistance: Math.max(1, swipeTravelDistance - swipeFadeStartOffset)
|
||||||
readonly property bool swipeActive: swipeDragHandler.active
|
readonly property bool swipeActive: swipeDragHandler.active
|
||||||
property bool swipeDismissing: false
|
property bool swipeDismissing: false
|
||||||
|
onSwipeDismissingChanged: {
|
||||||
|
if (!win.connectedFrameMode)
|
||||||
|
return;
|
||||||
|
win.popupHeightChanged();
|
||||||
|
win.popupChromeGeometryChanged();
|
||||||
|
}
|
||||||
|
onSwipeOffsetChanged: {
|
||||||
|
if (win.connectedFrameMode)
|
||||||
|
win.popupChromeGeometryChanged();
|
||||||
|
}
|
||||||
|
|
||||||
readonly property bool shadowsAllowed: Theme.elevationEnabled && SettingsData.notificationPopupShadowEnabled
|
readonly property bool shadowsAllowed: win.popupWindowShadowActive
|
||||||
readonly property var elevLevel: cardHoverHandler.hovered ? Theme.elevationLevel4 : Theme.elevationLevel3
|
readonly property var elevLevel: cardHoverHandler.hovered ? Theme.elevationLevel4 : Theme.elevationLevel3
|
||||||
readonly property real cardInset: Theme.snap(4, win.dpr)
|
readonly property real cardInset: Theme.snap(4, win.dpr)
|
||||||
readonly property real shadowRenderPadding: shadowsAllowed ? Theme.snap(Math.max(16, shadowBlurPx + Math.max(Math.abs(shadowOffsetX), Math.abs(shadowOffsetY)) + 8), win.dpr) : 0
|
readonly property real shadowRenderPadding: shadowsAllowed ? Theme.snap(Math.max(16, shadowBlurPx + Math.max(Math.abs(shadowOffsetX), Math.abs(shadowOffsetY)) + 8), win.dpr) : 0
|
||||||
@@ -370,21 +537,21 @@ PanelWindow {
|
|||||||
|
|
||||||
Behavior on shadowBlurPx {
|
Behavior on shadowBlurPx {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: win.descriptionExpanded ? Theme.notificationExpandDuration : Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on shadowOffsetX {
|
Behavior on shadowOffsetX {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: win.descriptionExpanded ? Theme.notificationExpandDuration : Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on shadowOffsetY {
|
Behavior on shadowOffsetY {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.shortDuration
|
duration: win.descriptionExpanded ? Theme.notificationExpandDuration : Theme.shortDuration
|
||||||
easing.type: Theme.standardEasing
|
easing.type: Theme.standardEasing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -399,7 +566,7 @@ PanelWindow {
|
|||||||
shadowOffsetX: content.shadowOffsetX
|
shadowOffsetX: content.shadowOffsetX
|
||||||
shadowOffsetY: content.shadowOffsetY
|
shadowOffsetY: content.shadowOffsetY
|
||||||
shadowColor: content.shadowsAllowed && content.elevLevel ? Theme.elevationShadowColor(content.elevLevel) : "transparent"
|
shadowColor: content.shadowsAllowed && content.elevLevel ? Theme.elevationShadowColor(content.elevLevel) : "transparent"
|
||||||
shadowEnabled: !win._isDestroying && win.screenValid && content.shadowsAllowed
|
shadowEnabled: !win._isDestroying && win.screenValid && content.shadowsAllowed && !win.connectedFrameMode
|
||||||
layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr))
|
layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr))
|
||||||
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
||||||
|
|
||||||
@@ -408,38 +575,42 @@ PanelWindow {
|
|||||||
sourceRect.y: content.shadowRenderPadding + content.cardInset
|
sourceRect.y: content.shadowRenderPadding + content.cardInset
|
||||||
sourceRect.width: Math.max(0, content.width - (content.cardInset * 2))
|
sourceRect.width: Math.max(0, content.width - (content.cardInset * 2))
|
||||||
sourceRect.height: Math.max(0, content.height - (content.cardInset * 2))
|
sourceRect.height: Math.max(0, content.height - (content.cardInset * 2))
|
||||||
sourceRect.radius: Theme.cornerRadius
|
sourceRect.radius: win.connectedFrameMode ? Theme.connectedSurfaceRadius : Theme.cornerRadius
|
||||||
sourceRect.color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
sourceRect.color: win.connectedFrameMode ? Theme.popupLayerColor(Theme.surfaceContainer) : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
|
sourceRect.antialiasing: true
|
||||||
|
sourceRect.layer.enabled: false
|
||||||
|
sourceRect.layer.textureSize: Qt.size(0, 0)
|
||||||
sourceRect.border.color: notificationData && notificationData.urgency === NotificationUrgency.Critical ? Theme.withAlpha(Theme.primary, 0.3) : Theme.withAlpha(Theme.outline, 0.08)
|
sourceRect.border.color: notificationData && notificationData.urgency === NotificationUrgency.Critical ? Theme.withAlpha(Theme.primary, 0.3) : Theme.withAlpha(Theme.outline, 0.08)
|
||||||
sourceRect.border.width: notificationData && notificationData.urgency === NotificationUrgency.Critical ? 2 : 0
|
sourceRect.border.width: notificationData && notificationData.urgency === NotificationUrgency.Critical ? 2 : 0
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
// Keep critical accent outside shadow rendering so connected mode still shows it.
|
||||||
x: bgShadowLayer.sourceRect.x
|
Rectangle {
|
||||||
y: bgShadowLayer.sourceRect.y
|
x: content.cardInset
|
||||||
width: bgShadowLayer.sourceRect.width
|
y: content.cardInset
|
||||||
height: bgShadowLayer.sourceRect.height
|
width: Math.max(0, content.width - content.cardInset * 2)
|
||||||
radius: bgShadowLayer.sourceRect.radius
|
height: Math.max(0, content.height - content.cardInset * 2)
|
||||||
visible: notificationData && notificationData.urgency === NotificationUrgency.Critical
|
radius: win.connectedFrameMode ? Theme.connectedSurfaceRadius : Theme.cornerRadius
|
||||||
opacity: 1
|
visible: win.notificationData && win.notificationData.urgency === NotificationUrgency.Critical
|
||||||
clip: true
|
opacity: 1
|
||||||
|
clip: true
|
||||||
|
|
||||||
gradient: Gradient {
|
gradient: Gradient {
|
||||||
orientation: Gradient.Horizontal
|
orientation: Gradient.Horizontal
|
||||||
|
|
||||||
GradientStop {
|
GradientStop {
|
||||||
position: 0
|
position: 0
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
}
|
}
|
||||||
|
|
||||||
GradientStop {
|
GradientStop {
|
||||||
position: 0.02
|
position: 0.02
|
||||||
color: Theme.primary
|
color: Theme.primary
|
||||||
}
|
}
|
||||||
|
|
||||||
GradientStop {
|
GradientStop {
|
||||||
position: 0.021
|
position: 0.021
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,10 +618,10 @@ PanelWindow {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: content.cardInset
|
anchors.margins: content.cardInset
|
||||||
radius: Theme.cornerRadius
|
radius: win.connectedFrameMode ? Theme.connectedSurfaceRadius : Theme.cornerRadius
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
border.color: BlurService.borderColor
|
border.color: win.connectedFrameMode ? "transparent" : BlurService.borderColor
|
||||||
border.width: BlurService.borderWidth
|
border.width: win.connectedFrameMode ? 0 : BlurService.borderWidth
|
||||||
z: 100
|
z: 100
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -850,14 +1021,15 @@ PanelWindow {
|
|||||||
DragHandler {
|
DragHandler {
|
||||||
id: swipeDragHandler
|
id: swipeDragHandler
|
||||||
target: null
|
target: null
|
||||||
xAxis.enabled: !isCenterPosition
|
xAxis.enabled: true
|
||||||
yAxis.enabled: isCenterPosition
|
yAxis.enabled: false
|
||||||
|
|
||||||
onActiveChanged: {
|
onActiveChanged: {
|
||||||
if (active || win.exiting || content.swipeDismissing)
|
if (active || win.exiting || content.swipeDismissing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Math.abs(content.swipeOffset) > content.dismissThreshold) {
|
if (Math.abs(content.swipeOffset) > content.dismissThreshold) {
|
||||||
|
content.swipeDismissDirection = content.swipeOffset < 0 ? -1 : 1;
|
||||||
content.swipeDismissing = true;
|
content.swipeDismissing = true;
|
||||||
swipeDismissAnim.start();
|
swipeDismissAnim.start();
|
||||||
} else {
|
} else {
|
||||||
@@ -869,15 +1041,7 @@ PanelWindow {
|
|||||||
if (win.exiting)
|
if (win.exiting)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const raw = isCenterPosition ? translation.y : translation.x;
|
content.swipeOffset = translation.x;
|
||||||
if (isTopCenter) {
|
|
||||||
content.swipeOffset = Math.min(0, raw);
|
|
||||||
} else if (isBottomCenter) {
|
|
||||||
content.swipeOffset = Math.max(0, raw);
|
|
||||||
} else {
|
|
||||||
const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom;
|
|
||||||
content.swipeOffset = isLeft ? Math.min(0, raw) : Math.max(0, raw);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -908,20 +1072,28 @@ PanelWindow {
|
|||||||
id: swipeDismissAnim
|
id: swipeDismissAnim
|
||||||
target: content
|
target: content
|
||||||
property: "swipeOffset"
|
property: "swipeOffset"
|
||||||
to: isTopCenter ? -content.height : isBottomCenter ? content.height : (SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom ? -content.width : content.width)
|
to: win._swipeDismissTarget()
|
||||||
duration: Theme.notificationExitDuration
|
duration: Theme.notificationExitDuration
|
||||||
easing.type: Easing.OutCubic
|
easing.type: Easing.OutCubic
|
||||||
onStopped: {
|
onStopped: {
|
||||||
NotificationService.dismissNotification(notificationData);
|
const inwardConnectedExit = win.connectedFrameMode && !win.isCenterPosition && !win._swipeDismissesTowardFrameEdge();
|
||||||
win.forceExit();
|
if (inwardConnectedExit)
|
||||||
|
content.chromeOnlyExit = true;
|
||||||
|
if (win.connectedFrameMode) {
|
||||||
|
win.startExit();
|
||||||
|
NotificationService.dismissNotification(notificationData);
|
||||||
|
} else {
|
||||||
|
NotificationService.dismissNotification(notificationData);
|
||||||
|
win.forceExit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transform: [
|
transform: [
|
||||||
Translate {
|
Translate {
|
||||||
id: swipeTx
|
id: swipeTx
|
||||||
x: isCenterPosition ? 0 : content.swipeOffset
|
x: content.swipeOffset
|
||||||
y: isCenterPosition ? content.swipeOffset : 0
|
y: 0
|
||||||
},
|
},
|
||||||
Translate {
|
Translate {
|
||||||
id: tx
|
id: tx
|
||||||
@@ -929,9 +1101,17 @@ PanelWindow {
|
|||||||
if (isCenterPosition)
|
if (isCenterPosition)
|
||||||
return 0;
|
return 0;
|
||||||
const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom;
|
const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom;
|
||||||
return isLeft ? -Anims.slidePx : Anims.slidePx;
|
return isLeft ? -entryTravel : entryTravel;
|
||||||
|
}
|
||||||
|
y: isTopCenter ? -entryTravel : isBottomCenter ? entryTravel : 0
|
||||||
|
onXChanged: {
|
||||||
|
if (win.connectedFrameMode)
|
||||||
|
win.popupChromeGeometryChanged();
|
||||||
|
}
|
||||||
|
onYChanged: {
|
||||||
|
if (win.connectedFrameMode)
|
||||||
|
win.popupChromeGeometryChanged();
|
||||||
}
|
}
|
||||||
y: isTopCenter ? -Anims.slidePx : isBottomCenter ? Anims.slidePx : 0
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -943,16 +1123,16 @@ PanelWindow {
|
|||||||
property: isCenterPosition ? "y" : "x"
|
property: isCenterPosition ? "y" : "x"
|
||||||
from: {
|
from: {
|
||||||
if (isTopCenter)
|
if (isTopCenter)
|
||||||
return -Anims.slidePx;
|
return -entryTravel;
|
||||||
if (isBottomCenter)
|
if (isBottomCenter)
|
||||||
return Anims.slidePx;
|
return entryTravel;
|
||||||
const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom;
|
const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom;
|
||||||
return isLeft ? -Anims.slidePx : Anims.slidePx;
|
return isLeft ? -entryTravel : entryTravel;
|
||||||
}
|
}
|
||||||
to: 0
|
to: 0
|
||||||
duration: Theme.notificationEnterDuration
|
duration: Theme.notificationEnterDuration
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: isCenterPosition ? Theme.expressiveCurves.standardDecel : Theme.expressiveCurves.emphasizedDecel
|
easing.bezierCurve: Theme.variantPopoutEnterCurve
|
||||||
onStopped: {
|
onStopped: {
|
||||||
if (!win.exiting && !win._isDestroying) {
|
if (!win.exiting && !win._isDestroying) {
|
||||||
if (isCenterPosition) {
|
if (isCenterPosition) {
|
||||||
@@ -977,35 +1157,35 @@ PanelWindow {
|
|||||||
from: 0
|
from: 0
|
||||||
to: {
|
to: {
|
||||||
if (isTopCenter)
|
if (isTopCenter)
|
||||||
return -Anims.slidePx;
|
return -exitTravel;
|
||||||
if (isBottomCenter)
|
if (isBottomCenter)
|
||||||
return Anims.slidePx;
|
return exitTravel;
|
||||||
const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom;
|
const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom;
|
||||||
return isLeft ? -Anims.slidePx : Anims.slidePx;
|
return isLeft ? -exitTravel : exitTravel;
|
||||||
}
|
}
|
||||||
duration: Theme.notificationExitDuration
|
duration: Theme.notificationExitDuration
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedAccel
|
easing.bezierCurve: Theme.variantPopoutExitCurve
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: content
|
target: content
|
||||||
property: "opacity"
|
property: "opacity"
|
||||||
from: 1
|
from: 1
|
||||||
to: 0
|
to: Theme.isDirectionalEffect ? 1 : 0
|
||||||
duration: Theme.notificationExitDuration
|
duration: Theme.notificationExitDuration
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.standardAccel
|
easing.bezierCurve: Theme.variantPopoutExitCurve
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: content
|
target: content
|
||||||
property: "scale"
|
property: "scale"
|
||||||
from: 1
|
from: 1
|
||||||
to: 0.98
|
to: Theme.isDirectionalEffect ? 1 : Theme.effectScaleCollapsed
|
||||||
duration: Theme.notificationExitDuration
|
duration: Theme.notificationExitDuration
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedAccel
|
easing.bezierCurve: Theme.variantPopoutExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,23 +8,46 @@ QtObject {
|
|||||||
property var modelData
|
property var modelData
|
||||||
property int topMargin: 0
|
property int topMargin: 0
|
||||||
readonly property bool compactMode: SettingsData.notificationCompactMode
|
readonly property bool compactMode: SettingsData.notificationCompactMode
|
||||||
|
readonly property bool notificationConnectedMode: SettingsData.frameEnabled
|
||||||
|
&& Theme.isConnectedEffect
|
||||||
|
&& SettingsData.isScreenInPreferences(manager.modelData, SettingsData.frameScreenPreferences)
|
||||||
|
readonly property bool closeGapNotifications: notificationConnectedMode && SettingsData.frameCloseGaps
|
||||||
|
readonly property string notifBarSide: {
|
||||||
|
const pos = SettingsData.notificationPopupPosition;
|
||||||
|
if (pos === -1) return "top";
|
||||||
|
switch (pos) {
|
||||||
|
case SettingsData.Position.Top: return "right";
|
||||||
|
case SettingsData.Position.Left: return "left";
|
||||||
|
case SettingsData.Position.BottomCenter: return "bottom";
|
||||||
|
case SettingsData.Position.Right: return "right";
|
||||||
|
case SettingsData.Position.Bottom: return "left";
|
||||||
|
default: return "top";
|
||||||
|
}
|
||||||
|
}
|
||||||
readonly property real cardPadding: compactMode ? Theme.notificationCardPaddingCompact : Theme.notificationCardPadding
|
readonly property real cardPadding: compactMode ? Theme.notificationCardPaddingCompact : Theme.notificationCardPadding
|
||||||
readonly property real popupIconSize: compactMode ? Theme.notificationIconSizeCompact : Theme.notificationIconSizeNormal
|
readonly property real popupIconSize: compactMode ? Theme.notificationIconSizeCompact : Theme.notificationIconSizeNormal
|
||||||
readonly property real actionButtonHeight: compactMode ? 20 : 24
|
readonly property real actionButtonHeight: compactMode ? 20 : 24
|
||||||
readonly property real contentSpacing: compactMode ? Theme.spacingXS : Theme.spacingS
|
readonly property real contentSpacing: compactMode ? Theme.spacingXS : Theme.spacingS
|
||||||
readonly property real popupSpacing: compactMode ? 0 : Theme.spacingXS
|
readonly property real popupSpacing: notificationConnectedMode ? 0 : (compactMode ? 0 : Theme.spacingXS)
|
||||||
readonly property real collapsedContentHeight: Math.max(popupIconSize, Theme.fontSizeSmall * 1.2 + Theme.fontSizeMedium * 1.2 + Theme.fontSizeSmall * 1.2 * (compactMode ? 1 : 2))
|
readonly property real collapsedContentHeight: Math.max(popupIconSize, Theme.fontSizeSmall * 1.2 + Theme.fontSizeMedium * 1.2 + Theme.fontSizeSmall * 1.2 * (compactMode ? 1 : 2))
|
||||||
readonly property int baseNotificationHeight: cardPadding * 2 + collapsedContentHeight + actionButtonHeight + contentSpacing + popupSpacing
|
readonly property int baseNotificationHeight: cardPadding * 2 + collapsedContentHeight + actionButtonHeight + contentSpacing + popupSpacing
|
||||||
property var popupWindows: []
|
property var popupWindows: []
|
||||||
property var destroyingWindows: new Set()
|
property var destroyingWindows: new Set()
|
||||||
property var pendingDestroys: []
|
property var pendingDestroys: []
|
||||||
property int destroyDelayMs: 100
|
property int destroyDelayMs: 100
|
||||||
|
property bool _chromeSyncPending: false
|
||||||
|
property bool _syncingVisibleNotifications: false
|
||||||
|
readonly property real chromeOpenProgressThreshold: 0.10
|
||||||
|
readonly property real chromeReleaseTailStart: 0.90
|
||||||
|
readonly property real chromeReleaseDropProgress: 0.995
|
||||||
property Component popupComponent
|
property Component popupComponent
|
||||||
|
|
||||||
popupComponent: Component {
|
popupComponent: Component {
|
||||||
NotificationPopup {
|
NotificationPopup {
|
||||||
onExitFinished: manager._onPopupExitFinished(this)
|
onExitFinished: manager._onPopupExitFinished(this)
|
||||||
|
onExitStarted: manager._onPopupExitStarted(this)
|
||||||
onPopupHeightChanged: manager._onPopupHeightChanged(this)
|
onPopupHeightChanged: manager._onPopupHeightChanged(this)
|
||||||
|
onPopupChromeGeometryChanged: manager._onPopupChromeGeometryChanged(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +131,27 @@ QtObject {
|
|||||||
return p && p.status !== Component.Null && !p._isDestroying && p.hasValidData;
|
return p && p.status !== Component.Null && !p._isDestroying && p.hasValidData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _layoutWindows() {
|
||||||
|
return popupWindows.filter(p => _isValidWindow(p) && p.notificationData?.popup && !p.exiting && (!p.popupLayoutReservesSlot || p.popupLayoutReservesSlot()));
|
||||||
|
}
|
||||||
|
|
||||||
|
function _chromeWindows() {
|
||||||
|
return popupWindows.filter(p => {
|
||||||
|
if (!p || p.status === Component.Null || !p.visible || p._finalized || !p.hasValidData)
|
||||||
|
return false;
|
||||||
|
if (!p.notificationData?.popup && !p.exiting)
|
||||||
|
return false;
|
||||||
|
if (!p.exiting && p.popupChromeOpenProgress && p.popupChromeOpenProgress() < chromeOpenProgressThreshold)
|
||||||
|
return false;
|
||||||
|
// Keep the connected shell until the card is almost fully closed.
|
||||||
|
if (p.exiting && !p.swipeActive && p.popupChromeReleaseProgress) {
|
||||||
|
if (p.popupChromeReleaseProgress() > chromeReleaseDropProgress)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function _isFocusedScreen() {
|
function _isFocusedScreen() {
|
||||||
if (!SettingsData.notificationFocusedMonitor)
|
if (!SettingsData.notificationFocusedMonitor)
|
||||||
return true;
|
return true;
|
||||||
@@ -116,27 +160,34 @@ QtObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _sync(newWrappers) {
|
function _sync(newWrappers) {
|
||||||
|
let needsReposition = false;
|
||||||
|
_syncingVisibleNotifications = true;
|
||||||
for (const p of popupWindows.slice()) {
|
for (const p of popupWindows.slice()) {
|
||||||
if (!_isValidWindow(p) || p.exiting)
|
if (!_isValidWindow(p) || p.exiting)
|
||||||
continue;
|
continue;
|
||||||
if (p.notificationData && newWrappers.indexOf(p.notificationData) === -1) {
|
if (p.notificationData && newWrappers.indexOf(p.notificationData) === -1) {
|
||||||
p.notificationData.removedByLimit = true;
|
p.notificationData.removedByLimit = true;
|
||||||
p.notificationData.popup = false;
|
p.notificationData.popup = false;
|
||||||
|
needsReposition = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const w of newWrappers) {
|
for (const w of newWrappers) {
|
||||||
if (w && !_hasWindowFor(w) && _isFocusedScreen())
|
if (w && !_hasWindowFor(w) && _isFocusedScreen()) {
|
||||||
_insertAtTop(w);
|
needsReposition = _insertAtTop(w, true) || needsReposition;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_syncingVisibleNotifications = false;
|
||||||
|
if (needsReposition)
|
||||||
|
_repositionAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _popupHeight(p) {
|
function _popupHeight(p) {
|
||||||
return (p.alignedHeight || p.implicitHeight || (baseNotificationHeight - popupSpacing)) + popupSpacing;
|
return (p.alignedHeight || p.implicitHeight || (baseNotificationHeight - popupSpacing)) + popupSpacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _insertAtTop(wrapper) {
|
function _insertAtTop(wrapper, deferReposition) {
|
||||||
if (!wrapper)
|
if (!wrapper)
|
||||||
return;
|
return false;
|
||||||
const notificationId = wrapper?.notification ? wrapper.notification.id : "";
|
const notificationId = wrapper?.notification ? wrapper.notification.id : "";
|
||||||
const win = popupComponent.createObject(null, {
|
const win = popupComponent.createObject(null, {
|
||||||
"notificationData": wrapper,
|
"notificationData": wrapper,
|
||||||
@@ -145,19 +196,21 @@ QtObject {
|
|||||||
"screen": manager.modelData
|
"screen": manager.modelData
|
||||||
});
|
});
|
||||||
if (!win)
|
if (!win)
|
||||||
return;
|
return false;
|
||||||
if (!win.hasValidData) {
|
if (!win.hasValidData) {
|
||||||
win.destroy();
|
win.destroy();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
popupWindows.unshift(win);
|
popupWindows.unshift(win);
|
||||||
_repositionAll();
|
if (!deferReposition)
|
||||||
|
_repositionAll();
|
||||||
if (!sweeper.running)
|
if (!sweeper.running)
|
||||||
sweeper.start();
|
sweeper.start();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _repositionAll() {
|
function _repositionAll() {
|
||||||
const active = popupWindows.filter(p => _isValidWindow(p) && p.notificationData?.popup && !p.exiting);
|
const active = _layoutWindows();
|
||||||
|
|
||||||
const pinnedSlots = [];
|
const pinnedSlots = [];
|
||||||
for (const p of active) {
|
for (const p of active) {
|
||||||
@@ -181,6 +234,282 @@ QtObject {
|
|||||||
win.screenY = currentY;
|
win.screenY = currentY;
|
||||||
currentY += _popupHeight(win);
|
currentY += _popupHeight(win);
|
||||||
}
|
}
|
||||||
|
_scheduleNotificationChromeSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _scheduleNotificationChromeSync() {
|
||||||
|
if (_chromeSyncPending)
|
||||||
|
return;
|
||||||
|
_chromeSyncPending = true;
|
||||||
|
Qt.callLater(() => {
|
||||||
|
_chromeSyncPending = false;
|
||||||
|
_syncNotificationChromeState();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _clamp01(value) {
|
||||||
|
return Math.max(0, Math.min(1, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function _clipRectFromBarSide(rect, visibleFraction) {
|
||||||
|
const fraction = _clamp01(visibleFraction);
|
||||||
|
const w = Math.max(0, rect.right - rect.x);
|
||||||
|
const h = Math.max(0, rect.bottom - rect.y);
|
||||||
|
|
||||||
|
if (notifBarSide === "right") {
|
||||||
|
rect.x = rect.right - w * fraction;
|
||||||
|
} else if (notifBarSide === "left") {
|
||||||
|
rect.right = rect.x + w * fraction;
|
||||||
|
} else if (notifBarSide === "bottom") {
|
||||||
|
rect.y = rect.bottom - h * fraction;
|
||||||
|
} else {
|
||||||
|
rect.bottom = rect.y + h * fraction;
|
||||||
|
}
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _popupChromeVisibleFraction(p) {
|
||||||
|
if (p.popupChromeReleaseProgress) {
|
||||||
|
const rel = p.popupChromeReleaseProgress();
|
||||||
|
if (p.exiting)
|
||||||
|
return Math.max(0, 1 - rel);
|
||||||
|
if (rel > 0)
|
||||||
|
return p.swipeDismissTowardEdge ? Math.max(0, 1 - rel) : 1 - _chromeReleaseTailProgress(rel);
|
||||||
|
}
|
||||||
|
if (p.popupChromeOpenProgress)
|
||||||
|
return _clamp01(p.popupChromeOpenProgress());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _popupChromeRect(p, useMotionOffset) {
|
||||||
|
if (!p || !p.screen)
|
||||||
|
return null;
|
||||||
|
const x = p.getContentX ? p.getContentX() : 0;
|
||||||
|
const y = p.getContentY ? p.getContentY() : 0;
|
||||||
|
const w = p.alignedWidth || 0;
|
||||||
|
const h = Math.max(p.alignedHeight || 0, baseNotificationHeight);
|
||||||
|
if (w <= 0 || h <= 0)
|
||||||
|
return null;
|
||||||
|
const rect = {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
right: x + w,
|
||||||
|
bottom: y + h
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!useMotionOffset)
|
||||||
|
return rect;
|
||||||
|
|
||||||
|
if (p.popupChromeFollowsCardMotion && p.popupChromeFollowsCardMotion()) {
|
||||||
|
const motionX = p.popupChromeMotionX ? p.popupChromeMotionX() : 0;
|
||||||
|
const motionY = p.popupChromeMotionY ? p.popupChromeMotionY() : 0;
|
||||||
|
rect.x += motionX;
|
||||||
|
rect.y += motionY;
|
||||||
|
rect.right += motionX;
|
||||||
|
rect.bottom += motionY;
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _clipRectFromBarSide(rect, _popupChromeVisibleFraction(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
function _chromeReleaseTailProgress(rawProgress) {
|
||||||
|
const progress = Math.max(0, Math.min(1, rawProgress));
|
||||||
|
if (progress <= chromeReleaseTailStart)
|
||||||
|
return 0;
|
||||||
|
return Math.max(0, Math.min(1, (progress - chromeReleaseTailStart) / Math.max(0.001, 1 - chromeReleaseTailStart)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function _popupChromeBoundsRect(p, trailing, useMotionOffset) {
|
||||||
|
const rect = _popupChromeRect(p, useMotionOffset);
|
||||||
|
if (!rect || p !== trailing || !p.popupChromeReleaseProgress)
|
||||||
|
return rect;
|
||||||
|
|
||||||
|
// Keep maxed-stack chrome anchored while a replacement tail exits.
|
||||||
|
if (p.exiting && p.notificationData?.removedByLimit && _layoutWindows().length > 0)
|
||||||
|
return rect;
|
||||||
|
|
||||||
|
const progress = _chromeReleaseTailProgress(p.popupChromeReleaseProgress());
|
||||||
|
if (progress <= 0)
|
||||||
|
return rect;
|
||||||
|
|
||||||
|
const anchorsTop = _stackAnchorsTop();
|
||||||
|
const h = Math.max(0, rect.bottom - rect.y);
|
||||||
|
const shrink = h * progress;
|
||||||
|
if (anchorsTop)
|
||||||
|
rect.bottom = Math.max(rect.y, rect.bottom - shrink);
|
||||||
|
else
|
||||||
|
rect.y = Math.min(rect.bottom, rect.y + shrink);
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _stackAnchorsTop() {
|
||||||
|
const pos = SettingsData.notificationPopupPosition;
|
||||||
|
return pos === -1 || pos === SettingsData.Position.Top || pos === SettingsData.Position.Left;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _frameEdgeInset(side) {
|
||||||
|
if (!manager.modelData)
|
||||||
|
return 0;
|
||||||
|
const edges = SettingsData.getActiveBarEdgesForScreen(manager.modelData);
|
||||||
|
const raw = edges.includes(side) ? SettingsData.frameBarSize : SettingsData.frameThickness;
|
||||||
|
const dpr = CompositorService.getScreenScale(manager.modelData);
|
||||||
|
return Math.max(0, Math.round(Theme.px(raw, dpr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function _closeGapChromeAnchorEdge(anchorsTop) {
|
||||||
|
if (!closeGapNotifications || !manager.modelData)
|
||||||
|
return null;
|
||||||
|
if (anchorsTop)
|
||||||
|
return _frameEdgeInset("top") + topMargin;
|
||||||
|
return manager.modelData.height - _frameEdgeInset("bottom") - topMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _trailingChromeWindow(candidates) {
|
||||||
|
const anchorsTop = _stackAnchorsTop();
|
||||||
|
let trailing = null;
|
||||||
|
let edge = anchorsTop ? -Infinity : Infinity;
|
||||||
|
for (const p of candidates) {
|
||||||
|
const rect = _popupChromeRect(p, false);
|
||||||
|
if (!rect)
|
||||||
|
continue;
|
||||||
|
const candidateEdge = anchorsTop ? rect.bottom : rect.y;
|
||||||
|
if ((anchorsTop && candidateEdge > edge) || (!anchorsTop && candidateEdge < edge)) {
|
||||||
|
edge = candidateEdge;
|
||||||
|
trailing = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trailing;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _chromeWindowReservesSlot(p, trailing) {
|
||||||
|
if (p === trailing)
|
||||||
|
return true;
|
||||||
|
return !p.popupChromeReservesSlot || p.popupChromeReservesSlot();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _stackAnchoredChromeEdge(candidates) {
|
||||||
|
const anchorsTop = _stackAnchorsTop();
|
||||||
|
let edge = anchorsTop ? Infinity : -Infinity;
|
||||||
|
for (const p of candidates) {
|
||||||
|
const rect = _popupChromeRect(p, false);
|
||||||
|
if (!rect)
|
||||||
|
continue;
|
||||||
|
if (anchorsTop && rect.y < edge)
|
||||||
|
edge = rect.y;
|
||||||
|
if (!anchorsTop && rect.bottom > edge)
|
||||||
|
edge = rect.bottom;
|
||||||
|
}
|
||||||
|
if (edge === Infinity || edge === -Infinity)
|
||||||
|
return null;
|
||||||
|
return {
|
||||||
|
anchorsTop: anchorsTop,
|
||||||
|
edge: edge
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function _syncNotificationChromeState() {
|
||||||
|
const screenName = manager.modelData?.name || "";
|
||||||
|
if (!screenName)
|
||||||
|
return;
|
||||||
|
if (!notificationConnectedMode) {
|
||||||
|
ConnectedModeState.clearNotificationState(screenName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const chromeCandidates = _chromeWindows();
|
||||||
|
if (chromeCandidates.length === 0) {
|
||||||
|
ConnectedModeState.clearNotificationState(screenName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trailing = chromeCandidates.length > 1 ? _trailingChromeWindow(chromeCandidates) : null;
|
||||||
|
let active = chromeCandidates;
|
||||||
|
if (chromeCandidates.length > 1) {
|
||||||
|
const reserving = chromeCandidates.filter(p => _chromeWindowReservesSlot(p, trailing));
|
||||||
|
if (reserving.length > 0)
|
||||||
|
active = reserving;
|
||||||
|
}
|
||||||
|
|
||||||
|
let minX = Infinity;
|
||||||
|
let minY = Infinity;
|
||||||
|
let maxXEnd = -Infinity;
|
||||||
|
let maxYEnd = -Infinity;
|
||||||
|
const useMotionOffset = active.length === 1 && active[0].popupChromeMotionActive && active[0].popupChromeMotionActive();
|
||||||
|
for (const p of active) {
|
||||||
|
const rect = _popupChromeBoundsRect(p, trailing, useMotionOffset);
|
||||||
|
if (!rect)
|
||||||
|
continue;
|
||||||
|
if (rect.x < minX)
|
||||||
|
minX = rect.x;
|
||||||
|
if (rect.y < minY)
|
||||||
|
minY = rect.y;
|
||||||
|
if (rect.right > maxXEnd)
|
||||||
|
maxXEnd = rect.right;
|
||||||
|
if (rect.bottom > maxYEnd)
|
||||||
|
maxYEnd = rect.bottom;
|
||||||
|
}
|
||||||
|
const stackEdge = _stackAnchoredChromeEdge(chromeCandidates);
|
||||||
|
if (stackEdge !== null) {
|
||||||
|
if (stackEdge.anchorsTop && stackEdge.edge < minY)
|
||||||
|
minY = stackEdge.edge;
|
||||||
|
if (!stackEdge.anchorsTop && stackEdge.edge > maxYEnd)
|
||||||
|
maxYEnd = stackEdge.edge;
|
||||||
|
}
|
||||||
|
const anchorsTop = stackEdge !== null ? stackEdge.anchorsTop : _stackAnchorsTop();
|
||||||
|
const closeGapAnchorEdge = _closeGapChromeAnchorEdge(anchorsTop);
|
||||||
|
if (closeGapAnchorEdge !== null) {
|
||||||
|
if (anchorsTop)
|
||||||
|
minY = closeGapAnchorEdge;
|
||||||
|
else
|
||||||
|
maxYEnd = closeGapAnchorEdge;
|
||||||
|
}
|
||||||
|
if (minX === Infinity || minY === Infinity || maxXEnd <= minX || maxYEnd <= minY) {
|
||||||
|
ConnectedModeState.clearNotificationState(screenName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ConnectedModeState.setNotificationState(screenName, {
|
||||||
|
visible: true,
|
||||||
|
barSide: notifBarSide,
|
||||||
|
bodyX: minX,
|
||||||
|
bodyY: minY,
|
||||||
|
bodyW: maxXEnd - minX,
|
||||||
|
bodyH: maxYEnd - minY,
|
||||||
|
omitStartConnector: _notificationOmitStartConnector(),
|
||||||
|
omitEndConnector: _notificationOmitEndConnector()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _notificationOmitStartConnector() {
|
||||||
|
return closeGapNotifications
|
||||||
|
&& (SettingsData.notificationPopupPosition === SettingsData.Position.Top
|
||||||
|
|| SettingsData.notificationPopupPosition === SettingsData.Position.Left);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _notificationOmitEndConnector() {
|
||||||
|
return closeGapNotifications
|
||||||
|
&& (SettingsData.notificationPopupPosition === SettingsData.Position.Right
|
||||||
|
|| SettingsData.notificationPopupPosition === SettingsData.Position.Bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _onPopupChromeGeometryChanged(p) {
|
||||||
|
if (!p || popupWindows.indexOf(p) === -1)
|
||||||
|
return;
|
||||||
|
_scheduleNotificationChromeSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Coalesce resize repositioning; exit-path moves remain immediate.
|
||||||
|
property bool _repositionPending: false
|
||||||
|
|
||||||
|
function _queueReposition() {
|
||||||
|
if (_repositionPending)
|
||||||
|
return;
|
||||||
|
_repositionPending = true;
|
||||||
|
Qt.callLater(_flushReposition);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _flushReposition() {
|
||||||
|
_repositionPending = false;
|
||||||
|
_repositionAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _onPopupHeightChanged(p) {
|
function _onPopupHeightChanged(p) {
|
||||||
@@ -188,6 +517,14 @@ QtObject {
|
|||||||
return;
|
return;
|
||||||
if (popupWindows.indexOf(p) === -1)
|
if (popupWindows.indexOf(p) === -1)
|
||||||
return;
|
return;
|
||||||
|
_queueReposition();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _onPopupExitStarted(p) {
|
||||||
|
if (!p || popupWindows.indexOf(p) === -1)
|
||||||
|
return;
|
||||||
|
if (_syncingVisibleNotifications)
|
||||||
|
return;
|
||||||
_repositionAll();
|
_repositionAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,8 +564,16 @@ QtObject {
|
|||||||
}
|
}
|
||||||
popupWindows = [];
|
popupWindows = [];
|
||||||
destroyingWindows.clear();
|
destroyingWindows.clear();
|
||||||
|
_chromeSyncPending = false;
|
||||||
|
_syncNotificationChromeState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onNotificationConnectedModeChanged: _scheduleNotificationChromeSync()
|
||||||
|
onCloseGapNotificationsChanged: _scheduleNotificationChromeSync()
|
||||||
|
onNotifBarSideChanged: _scheduleNotificationChromeSync()
|
||||||
|
onModelDataChanged: _scheduleNotificationChromeSync()
|
||||||
|
onTopMarginChanged: _repositionAll()
|
||||||
|
|
||||||
onPopupWindowsChanged: {
|
onPopupWindowsChanged: {
|
||||||
if (popupWindows.length > 0 && !sweeper.running) {
|
if (popupWindows.length > 0 && !sweeper.running) {
|
||||||
sweeper.start();
|
sweeper.start();
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ Item {
|
|||||||
const pos = selectedBarConfig?.position ?? SettingsData.Position.Top;
|
const pos = selectedBarConfig?.position ?? SettingsData.Position.Top;
|
||||||
return pos === SettingsData.Position.Left || pos === SettingsData.Position.Right;
|
return pos === SettingsData.Position.Left || pos === SettingsData.Position.Right;
|
||||||
}
|
}
|
||||||
|
readonly property bool connectedFrameModeActive: SettingsData.connectedFrameModeActive
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: horizontalBarChangeDebounce
|
id: horizontalBarChangeDebounce
|
||||||
@@ -693,6 +694,8 @@ Item {
|
|||||||
|
|
||||||
SettingsToggleRow {
|
SettingsToggleRow {
|
||||||
visible: CompositorService.isNiri
|
visible: CompositorService.isNiri
|
||||||
|
enabled: !SettingsData.frameEnabled
|
||||||
|
opacity: SettingsData.frameEnabled ? 0.5 : 1.0
|
||||||
text: I18n.tr("Show on Overview")
|
text: I18n.tr("Show on Overview")
|
||||||
checked: selectedBarConfig?.openOnOverview ?? false
|
checked: selectedBarConfig?.openOnOverview ?? false
|
||||||
onToggled: toggled => {
|
onToggled: toggled => {
|
||||||
@@ -798,11 +801,42 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: SettingsData.frameEnabled
|
||||||
|
width: parent.width
|
||||||
|
implicitHeight: frameNote.implicitHeight + Theme.spacingS * 2
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: frameNote
|
||||||
|
x: Theme.spacingM
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "frame_source"
|
||||||
|
size: Theme.fontSizeMedium
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Spacing and size are managed by Frame mode")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: parent.width - Theme.fontSizeMedium - Theme.spacingS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
iconName: "space_bar"
|
iconName: "space_bar"
|
||||||
title: I18n.tr("Spacing")
|
title: I18n.tr("Spacing")
|
||||||
settingKey: "barSpacing"
|
settingKey: "barSpacing"
|
||||||
visible: selectedBarConfig?.enabled
|
visible: selectedBarConfig?.enabled
|
||||||
|
enabled: !SettingsData.frameEnabled
|
||||||
|
opacity: SettingsData.frameEnabled ? 0.5 : 1.0
|
||||||
|
|
||||||
SettingsSliderRow {
|
SettingsSliderRow {
|
||||||
id: edgeSpacingSlider
|
id: edgeSpacingSlider
|
||||||
@@ -1003,6 +1037,8 @@ Item {
|
|||||||
|
|
||||||
SettingsSliderRow {
|
SettingsSliderRow {
|
||||||
id: barTransparencySlider
|
id: barTransparencySlider
|
||||||
|
enabled: !SettingsData.frameEnabled
|
||||||
|
opacity: SettingsData.frameEnabled ? 0.5 : 1.0
|
||||||
text: I18n.tr("Bar Transparency")
|
text: I18n.tr("Bar Transparency")
|
||||||
value: (selectedBarConfig?.transparency ?? 1.0) * 100
|
value: (selectedBarConfig?.transparency ?? 1.0) * 100
|
||||||
minimum: 0
|
minimum: 0
|
||||||
@@ -1044,6 +1080,64 @@ Item {
|
|||||||
restoreMode: Binding.RestoreBinding
|
restoreMode: Binding.RestoreBinding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: SettingsData.frameEnabled
|
||||||
|
width: parent.width
|
||||||
|
implicitHeight: transparencyFrameNote.implicitHeight + Theme.spacingS * 2
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: transparencyFrameNote
|
||||||
|
x: Theme.spacingM
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "frame_source"
|
||||||
|
size: Theme.fontSizeMedium
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Opacity is controlled by Frame Border Opacity in Frame settings")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: parent.width - Theme.fontSizeMedium - Theme.spacingS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: dankBarTab.connectedFrameModeActive
|
||||||
|
width: parent.width
|
||||||
|
implicitHeight: connectedFrameStyleNote.implicitHeight + Theme.spacingS * 2
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: connectedFrameStyleNote
|
||||||
|
x: Theme.spacingM
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "frame_source"
|
||||||
|
size: Theme.fontSizeMedium
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Connected Frame mode keeps bar shadow override, border, and corner overrides off while active")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: parent.width - Theme.fontSizeMedium - Theme.spacingS
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
@@ -1054,6 +1148,8 @@ Item {
|
|||||||
collapsible: true
|
collapsible: true
|
||||||
expanded: true
|
expanded: true
|
||||||
visible: selectedBarConfig?.enabled
|
visible: selectedBarConfig?.enabled
|
||||||
|
enabled: !dankBarTab.connectedFrameModeActive
|
||||||
|
opacity: dankBarTab.connectedFrameModeActive ? 0.5 : 1.0
|
||||||
|
|
||||||
readonly property bool shadowActive: (selectedBarConfig?.shadowIntensity ?? 0) > 0
|
readonly property bool shadowActive: (selectedBarConfig?.shadowIntensity ?? 0) > 0
|
||||||
readonly property bool isCustomColor: (selectedBarConfig?.shadowColorMode ?? "default") === "custom"
|
readonly property bool isCustomColor: (selectedBarConfig?.shadowColorMode ?? "default") === "custom"
|
||||||
@@ -1287,6 +1383,8 @@ Item {
|
|||||||
|
|
||||||
SettingsToggleRow {
|
SettingsToggleRow {
|
||||||
text: I18n.tr("Square Corners")
|
text: I18n.tr("Square Corners")
|
||||||
|
enabled: !SettingsData.frameEnabled
|
||||||
|
opacity: SettingsData.frameEnabled ? 0.5 : 1.0
|
||||||
checked: selectedBarConfig?.squareCorners ?? false
|
checked: selectedBarConfig?.squareCorners ?? false
|
||||||
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
||||||
squareCorners: checked
|
squareCorners: checked
|
||||||
@@ -1334,6 +1432,8 @@ Item {
|
|||||||
|
|
||||||
SettingsToggleRow {
|
SettingsToggleRow {
|
||||||
text: I18n.tr("Goth Corners")
|
text: I18n.tr("Goth Corners")
|
||||||
|
enabled: !SettingsData.frameEnabled
|
||||||
|
opacity: SettingsData.frameEnabled ? 0.5 : 1.0
|
||||||
checked: selectedBarConfig?.gothCornersEnabled ?? false
|
checked: selectedBarConfig?.gothCornersEnabled ?? false
|
||||||
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
||||||
gothCornersEnabled: checked
|
gothCornersEnabled: checked
|
||||||
@@ -1383,6 +1483,8 @@ Item {
|
|||||||
iconName: "border_style"
|
iconName: "border_style"
|
||||||
title: I18n.tr("Border")
|
title: I18n.tr("Border")
|
||||||
visible: selectedBarConfig?.enabled
|
visible: selectedBarConfig?.enabled
|
||||||
|
enabled: !dankBarTab.connectedFrameModeActive
|
||||||
|
opacity: dankBarTab.connectedFrameModeActive ? 0.5 : 1.0
|
||||||
checked: selectedBarConfig?.borderEnabled ?? false
|
checked: selectedBarConfig?.borderEnabled ?? false
|
||||||
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
||||||
borderEnabled: checked
|
borderEnabled: checked
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import qs.Modules.Settings.Widgets
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
readonly property bool connectedFrameModeActive: SettingsData.frameEnabled
|
||||||
|
&& SettingsData.motionEffect === 1
|
||||||
|
&& SettingsData.directionalAnimationMode === 3
|
||||||
|
|
||||||
FileBrowserModal {
|
FileBrowserModal {
|
||||||
id: dockLogoFileBrowser
|
id: dockLogoFileBrowser
|
||||||
@@ -544,6 +547,8 @@ Item {
|
|||||||
|
|
||||||
SettingsSliderRow {
|
SettingsSliderRow {
|
||||||
text: I18n.tr("Exclusive Zone Offset")
|
text: I18n.tr("Exclusive Zone Offset")
|
||||||
|
enabled: !root.connectedFrameModeActive
|
||||||
|
opacity: root.connectedFrameModeActive ? 0.5 : 1.0
|
||||||
value: SettingsData.dockBottomGap
|
value: SettingsData.dockBottomGap
|
||||||
minimum: -100
|
minimum: -100
|
||||||
maximum: 100
|
maximum: 100
|
||||||
@@ -553,6 +558,8 @@ Item {
|
|||||||
|
|
||||||
SettingsSliderRow {
|
SettingsSliderRow {
|
||||||
text: I18n.tr("Margin")
|
text: I18n.tr("Margin")
|
||||||
|
enabled: !root.connectedFrameModeActive
|
||||||
|
opacity: root.connectedFrameModeActive ? 0.5 : 1.0
|
||||||
value: SettingsData.dockMargin
|
value: SettingsData.dockMargin
|
||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: 100
|
maximum: 100
|
||||||
@@ -561,11 +568,42 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: root.connectedFrameModeActive
|
||||||
|
width: parent.width
|
||||||
|
implicitHeight: dockConnectedNote.implicitHeight + Theme.spacingS * 2
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: dockConnectedNote
|
||||||
|
x: Theme.spacingM
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "frame_source"
|
||||||
|
size: Theme.fontSizeMedium
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Connected Frame mode manages dock edge offset, transparency, blur, and border styling")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: parent.width - Theme.fontSizeMedium - Theme.spacingS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
iconName: "opacity"
|
iconName: "opacity"
|
||||||
title: I18n.tr("Transparency")
|
title: I18n.tr("Transparency")
|
||||||
settingKey: "dockTransparency"
|
settingKey: "dockTransparency"
|
||||||
|
enabled: !root.connectedFrameModeActive
|
||||||
|
opacity: root.connectedFrameModeActive ? 0.5 : 1.0
|
||||||
|
|
||||||
SettingsSliderRow {
|
SettingsSliderRow {
|
||||||
text: I18n.tr("Dock Transparency")
|
text: I18n.tr("Dock Transparency")
|
||||||
@@ -585,6 +623,8 @@ Item {
|
|||||||
settingKey: "dockBorder"
|
settingKey: "dockBorder"
|
||||||
collapsible: true
|
collapsible: true
|
||||||
expanded: false
|
expanded: false
|
||||||
|
enabled: !root.connectedFrameModeActive
|
||||||
|
opacity: root.connectedFrameModeActive ? 0.5 : 1.0
|
||||||
|
|
||||||
SettingsToggleRow {
|
SettingsToggleRow {
|
||||||
text: I18n.tr("Border")
|
text: I18n.tr("Border")
|
||||||
|
|||||||
336
quickshell/Modules/Settings/FrameTab.qml
Normal file
336
quickshell/Modules/Settings/FrameTab.qml
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
import qs.Modules.Settings.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
LayoutMirroring.enabled: I18n.isRtl
|
||||||
|
LayoutMirroring.childrenInherit: true
|
||||||
|
|
||||||
|
DankFlickable {
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
|
contentHeight: mainColumn.height + Theme.spacingXL
|
||||||
|
contentWidth: width
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: mainColumn
|
||||||
|
topPadding: 4
|
||||||
|
width: Math.min(550, parent.width - Theme.spacingL * 2)
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Theme.spacingXL
|
||||||
|
|
||||||
|
// ── Enable Frame ──────────────────────────────────────────────────
|
||||||
|
SettingsCard {
|
||||||
|
width: parent.width
|
||||||
|
iconName: "frame_source"
|
||||||
|
title: I18n.tr("Frame")
|
||||||
|
settingKey: "frameEnabled"
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
settingKey: "frameEnable"
|
||||||
|
tags: ["frame", "border", "outline", "display"]
|
||||||
|
text: I18n.tr("Enable Frame")
|
||||||
|
description: I18n.tr("Draw a connected picture-frame border around the entire display")
|
||||||
|
checked: SettingsData.frameEnabled
|
||||||
|
onToggled: checked => SettingsData.set("frameEnabled", checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Border ────────────────────────────────────────────────────────
|
||||||
|
SettingsCard {
|
||||||
|
width: parent.width
|
||||||
|
iconName: "border_outer"
|
||||||
|
title: I18n.tr("Border")
|
||||||
|
settingKey: "frameBorder"
|
||||||
|
collapsible: true
|
||||||
|
visible: SettingsData.frameEnabled
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
id: roundingSlider
|
||||||
|
settingKey: "frameRounding"
|
||||||
|
tags: ["frame", "border", "rounding", "radius", "corner"]
|
||||||
|
text: I18n.tr("Border Radius")
|
||||||
|
description: SettingsData.connectedFrameModeActive
|
||||||
|
? I18n.tr("Controls the radius of the frame and all connected popout, dock, and modal surfaces while Connected Mode is active")
|
||||||
|
: I18n.tr("Controls the frame border radius. This also becomes the connected surface radius whenever Connected Mode is active")
|
||||||
|
unit: "px"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
step: 1
|
||||||
|
defaultValue: 23
|
||||||
|
value: SettingsData.frameRounding
|
||||||
|
onSliderDragFinished: v => SettingsData.set("frameRounding", v)
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: roundingSlider
|
||||||
|
property: "value"
|
||||||
|
value: SettingsData.frameRounding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
id: thicknessSlider
|
||||||
|
settingKey: "frameThickness"
|
||||||
|
tags: ["frame", "border", "thickness", "size", "width"]
|
||||||
|
text: I18n.tr("Border Width")
|
||||||
|
unit: "px"
|
||||||
|
minimum: 2
|
||||||
|
maximum: 100
|
||||||
|
step: 1
|
||||||
|
defaultValue: 16
|
||||||
|
value: SettingsData.frameThickness
|
||||||
|
onSliderDragFinished: v => SettingsData.set("frameThickness", v)
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: thicknessSlider
|
||||||
|
property: "value"
|
||||||
|
value: SettingsData.frameThickness
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
id: barThicknessSlider
|
||||||
|
settingKey: "frameBarSize"
|
||||||
|
tags: ["frame", "bar", "thickness", "size", "height", "width"]
|
||||||
|
text: I18n.tr("Size")
|
||||||
|
description: I18n.tr("Height of horizontal bars / width of vertical bars in frame mode")
|
||||||
|
unit: "px"
|
||||||
|
minimum: 24
|
||||||
|
maximum: 100
|
||||||
|
step: 1
|
||||||
|
defaultValue: 40
|
||||||
|
value: SettingsData.frameBarSize
|
||||||
|
onSliderDragFinished: v => SettingsData.set("frameBarSize", v)
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: barThicknessSlider
|
||||||
|
property: "value"
|
||||||
|
value: SettingsData.frameBarSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
id: opacitySlider
|
||||||
|
settingKey: "frameOpacity"
|
||||||
|
tags: ["frame", "border", "surface", "popup", "opacity", "transparency"]
|
||||||
|
text: I18n.tr("Surface Opacity")
|
||||||
|
description: I18n.tr("Frame border opacity. Controls all surface opacity globally when Connected Mode is active")
|
||||||
|
unit: "%"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
defaultValue: 100
|
||||||
|
value: SettingsData.frameOpacity * 100
|
||||||
|
onSliderDragFinished: v => SettingsData.set("frameOpacity", v / 100)
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: opacitySlider
|
||||||
|
property: "value"
|
||||||
|
value: SettingsData.frameOpacity * 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
id: frameBlurToggle
|
||||||
|
settingKey: "frameBlurEnabled"
|
||||||
|
tags: ["frame", "blur", "background", "glass", "transparency", "frosted"]
|
||||||
|
text: I18n.tr("Frame Blur")
|
||||||
|
description: !BlurService.available
|
||||||
|
? I18n.tr("Requires a newer version of Quickshell")
|
||||||
|
: I18n.tr("Apply compositor blur behind the frame border")
|
||||||
|
checked: SettingsData.frameBlurEnabled
|
||||||
|
onToggled: checked => SettingsData.set("frameBlurEnabled", checked)
|
||||||
|
enabled: BlurService.available && SettingsData.blurEnabled
|
||||||
|
opacity: enabled ? 1.0 : 0.5
|
||||||
|
visible: BlurService.available
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: BlurService.available && !SettingsData.blurEnabled
|
||||||
|
width: parent.width
|
||||||
|
height: blurToggleNote.height + Theme.spacingM * 2
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: blurToggleNote
|
||||||
|
x: Theme.spacingM
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "blur_on"
|
||||||
|
size: Theme.fontSizeMedium
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Frame Blur is controlled by Background Blur in Theme & Colors")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
width: parent.width - Theme.fontSizeMedium - Theme.spacingS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color mode buttons
|
||||||
|
SettingsButtonGroupRow {
|
||||||
|
settingKey: "frameColor"
|
||||||
|
tags: ["frame", "border", "color", "theme", "primary", "surface", "default"]
|
||||||
|
text: I18n.tr("Border color")
|
||||||
|
model: [I18n.tr("Default"), I18n.tr("Primary"), I18n.tr("Surface"), I18n.tr("Custom")]
|
||||||
|
currentIndex: {
|
||||||
|
const fc = SettingsData.frameColor;
|
||||||
|
if (!fc || fc === "default") return 0;
|
||||||
|
if (fc === "primary") return 1;
|
||||||
|
if (fc === "surface") return 2;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
onSelectionChanged: (index, selected) => {
|
||||||
|
if (!selected) return;
|
||||||
|
switch (index) {
|
||||||
|
case 0: SettingsData.set("frameColor", ""); break;
|
||||||
|
case 1: SettingsData.set("frameColor", "primary"); break;
|
||||||
|
case 2: SettingsData.set("frameColor", "surface"); break;
|
||||||
|
case 3:
|
||||||
|
const cur = SettingsData.frameColor;
|
||||||
|
const isPreset = !cur || cur === "primary" || cur === "surface";
|
||||||
|
if (isPreset) SettingsData.set("frameColor", "#2a2a2a");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom color swatch — only visible when a hex color is stored (Custom mode)
|
||||||
|
Item {
|
||||||
|
visible: {
|
||||||
|
const fc = SettingsData.frameColor;
|
||||||
|
return !!(fc && fc !== "primary" && fc !== "surface");
|
||||||
|
}
|
||||||
|
width: parent.width
|
||||||
|
height: customColorRow.height + Theme.spacingM * 2
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: customColorRow
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
x: Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: I18n.tr("Custom color")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: colorSwatch
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 32
|
||||||
|
height: 32
|
||||||
|
radius: 16
|
||||||
|
color: SettingsData.effectiveFrameColor
|
||||||
|
border.color: Theme.outline
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
PopoutService.colorPickerModal.selectedColor = SettingsData.effectiveFrameColor;
|
||||||
|
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Frame Border Color");
|
||||||
|
PopoutService.colorPickerModal.onColorSelectedCallback = function (color) {
|
||||||
|
SettingsData.set("frameColor", color.toString());
|
||||||
|
};
|
||||||
|
PopoutService.colorPickerModal.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Bar Integration ───────────────────────────────────────────────
|
||||||
|
SettingsCard {
|
||||||
|
width: parent.width
|
||||||
|
iconName: "toolbar"
|
||||||
|
title: I18n.tr("Integrations")
|
||||||
|
settingKey: "frameBarIntegration"
|
||||||
|
collapsible: true
|
||||||
|
expanded: true
|
||||||
|
visible: SettingsData.frameEnabled
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
visible: CompositorService.isNiri
|
||||||
|
settingKey: "frameShowOnOverview"
|
||||||
|
tags: ["frame", "overview", "show", "hide", "niri"]
|
||||||
|
text: I18n.tr("Show on Overview")
|
||||||
|
description: I18n.tr("Show the bar and frame during Niri overview mode")
|
||||||
|
checked: SettingsData.frameShowOnOverview
|
||||||
|
onToggled: checked => SettingsData.set("frameShowOnOverview", checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
visible: SettingsData.frameEnabled
|
||||||
|
settingKey: "directionalAnimationMode"
|
||||||
|
tags: ["frame", "connected", "popout", "corner", "animation"]
|
||||||
|
text: I18n.tr("Connected Mode")
|
||||||
|
description: I18n.tr("Popouts emerge flush from the bar edge as one continuous piece")
|
||||||
|
checked: SettingsData.connectedFrameModeActive
|
||||||
|
onToggled: checked => {
|
||||||
|
if (checked) {
|
||||||
|
if (SettingsData.directionalAnimationMode !== 3)
|
||||||
|
SettingsData.set("previousDirectionalMode", SettingsData.directionalAnimationMode);
|
||||||
|
SettingsData.set("motionEffect", 1);
|
||||||
|
SettingsData.set("directionalAnimationMode", 3);
|
||||||
|
} else {
|
||||||
|
SettingsData.set("directionalAnimationMode", SettingsData.previousDirectionalMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onDirectionalAnimationModeChanged() {}
|
||||||
|
function onMotionEffectChanged() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
visible: SettingsData.frameEnabled
|
||||||
|
settingKey: "frameCloseGaps"
|
||||||
|
tags: ["frame", "connected", "gap", "edge", "flush", "popout", "notification"]
|
||||||
|
text: I18n.tr("Close the Gaps")
|
||||||
|
description: I18n.tr("Connected popouts and notification corners sit flush against the frame edge")
|
||||||
|
checked: SettingsData.frameCloseGaps
|
||||||
|
enabled: SettingsData.connectedFrameModeActive
|
||||||
|
opacity: enabled ? 1.0 : 0.5
|
||||||
|
onToggled: checked => SettingsData.set("frameCloseGaps", checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Display Assignment ────────────────────────────────────────────
|
||||||
|
SettingsCard {
|
||||||
|
width: parent.width
|
||||||
|
iconName: "monitor"
|
||||||
|
title: I18n.tr("Display Assignment")
|
||||||
|
settingKey: "frameDisplays"
|
||||||
|
collapsible: true
|
||||||
|
expanded: false
|
||||||
|
visible: SettingsData.frameEnabled
|
||||||
|
|
||||||
|
SettingsDisplayPicker {
|
||||||
|
displayPreferences: SettingsData.frameScreenPreferences
|
||||||
|
onPreferencesChanged: prefs => SettingsData.set("frameScreenPreferences", prefs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import qs.Modules.Settings.Widgets
|
|||||||
Item {
|
Item {
|
||||||
id: themeColorsTab
|
id: themeColorsTab
|
||||||
|
|
||||||
|
readonly property bool connectedFrameModeActive: SettingsData.connectedFrameModeActive
|
||||||
property var cachedIconThemes: SettingsData.availableIconThemes
|
property var cachedIconThemes: SettingsData.availableIconThemes
|
||||||
property var cachedCursorThemes: SettingsData.availableCursorThemes
|
property var cachedCursorThemes: SettingsData.availableCursorThemes
|
||||||
property var cachedMatugenSchemes: Theme.availableMatugenSchemes.map(option => option.label)
|
property var cachedMatugenSchemes: Theme.availableMatugenSchemes.map(option => option.label)
|
||||||
@@ -1615,10 +1616,14 @@ Item {
|
|||||||
|
|
||||||
SettingsSliderRow {
|
SettingsSliderRow {
|
||||||
tab: "theme"
|
tab: "theme"
|
||||||
tags: ["popup", "transparency", "opacity", "modal"]
|
tags: ["surface", "popup", "transparency", "opacity", "modal"]
|
||||||
settingKey: "popupTransparency"
|
settingKey: "popupTransparency"
|
||||||
text: I18n.tr("Popup Transparency")
|
text: I18n.tr("Surface Opacity")
|
||||||
description: I18n.tr("Controls opacity of all popouts, modals, and their content layers")
|
description: themeColorsTab.connectedFrameModeActive
|
||||||
|
? I18n.tr("Connected Frame mode follows Surface Opacity from the Frame tab for connected popouts, docks, and modal surfaces")
|
||||||
|
: I18n.tr("Controls opacity of all popouts, modals, and their content layers")
|
||||||
|
enabled: !themeColorsTab.connectedFrameModeActive
|
||||||
|
opacity: themeColorsTab.connectedFrameModeActive ? 0.5 : 1.0
|
||||||
value: Math.round(SettingsData.popupTransparency * 100)
|
value: Math.round(SettingsData.popupTransparency * 100)
|
||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: 100
|
maximum: 100
|
||||||
@@ -1632,7 +1637,9 @@ Item {
|
|||||||
tags: ["corner", "radius", "rounded", "square"]
|
tags: ["corner", "radius", "rounded", "square"]
|
||||||
settingKey: "cornerRadius"
|
settingKey: "cornerRadius"
|
||||||
text: I18n.tr("Corner Radius")
|
text: I18n.tr("Corner Radius")
|
||||||
description: I18n.tr("0 = square corners")
|
description: themeColorsTab.connectedFrameModeActive
|
||||||
|
? I18n.tr("Controls general UI rounding. Connected frame popouts, docks, and modal surfaces follow Border Radius in the Frame tab while Connected Frame mode is active")
|
||||||
|
: I18n.tr("0 = square corners")
|
||||||
value: SettingsData.cornerRadius
|
value: SettingsData.cornerRadius
|
||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: 32
|
maximum: 32
|
||||||
@@ -1837,7 +1844,11 @@ Item {
|
|||||||
tags: ["blur", "background", "transparency", "glass", "frosted"]
|
tags: ["blur", "background", "transparency", "glass", "frosted"]
|
||||||
settingKey: "blurEnabled"
|
settingKey: "blurEnabled"
|
||||||
text: I18n.tr("Background Blur")
|
text: I18n.tr("Background Blur")
|
||||||
description: BlurService.available ? I18n.tr("Blur the background behind bars, popouts, modals, and notifications. Requires compositor support and configuration.") : I18n.tr("Requires a newer version of Quickshell")
|
description: !BlurService.available
|
||||||
|
? I18n.tr("Requires a newer version of Quickshell")
|
||||||
|
: (themeColorsTab.connectedFrameModeActive
|
||||||
|
? I18n.tr("Connected Frame mode follows Frame Blur for connected surfaces while this remains the master blur availability toggle")
|
||||||
|
: I18n.tr("Blur the background behind bars, popouts, modals, and notifications. Requires compositor support and configuration."))
|
||||||
checked: SettingsData.blurEnabled ?? false
|
checked: SettingsData.blurEnabled ?? false
|
||||||
enabled: BlurService.available
|
enabled: BlurService.available
|
||||||
onToggled: checked => SettingsData.set("blurEnabled", checked)
|
onToggled: checked => SettingsData.set("blurEnabled", checked)
|
||||||
|
|||||||
@@ -55,6 +55,192 @@ Item {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
spacing: Theme.spacingXL
|
spacing: Theme.spacingXL
|
||||||
|
|
||||||
|
SettingsCard {
|
||||||
|
tab: "typography"
|
||||||
|
tags: ["animation", "variant", "style", "slide", "fluent", "dynamic", "motion"]
|
||||||
|
title: I18n.tr("Animation Style")
|
||||||
|
settingKey: "animationVariant"
|
||||||
|
iconName: "auto_awesome_motion"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: animVariantGroup.implicitHeight
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
DankButtonGroup {
|
||||||
|
id: animVariantGroup
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
buttonPadding: parent.width < 480 ? Theme.spacingS : Theme.spacingL
|
||||||
|
minButtonWidth: parent.width < 480 ? 64 : 96
|
||||||
|
textSize: parent.width < 480 ? Theme.fontSizeSmall : Theme.fontSizeMedium
|
||||||
|
model: [I18n.tr("Material"), I18n.tr("Fluent"), I18n.tr("Dynamic")]
|
||||||
|
selectionMode: "single"
|
||||||
|
currentIndex: SettingsData.animationVariant
|
||||||
|
onSelectionChanged: (index, selected) => {
|
||||||
|
if (!selected)
|
||||||
|
return;
|
||||||
|
SettingsData.set("animationVariant", index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onAnimationVariantChanged() {
|
||||||
|
animVariantGroup.currentIndex = SettingsData.animationVariant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Theme.outline
|
||||||
|
opacity: 0.15
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: variantDescription.implicitHeight + Theme.spacingS * 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: variantDescription
|
||||||
|
x: Theme.spacingM
|
||||||
|
y: Theme.spacingS
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: {
|
||||||
|
switch (SettingsData.animationVariant) {
|
||||||
|
case 1:
|
||||||
|
return I18n.tr("Fluent: Smooth cubic deceleration in, quick snap out — clean, elegant curves.");
|
||||||
|
case 2:
|
||||||
|
return I18n.tr("Dynamic: Spring bezier with overshoot — entry briefly exceeds its target then settles. Expressive and alive.");
|
||||||
|
default:
|
||||||
|
return I18n.tr("Material: Material Design 3 Expressive bezier curves. The DMS default feel.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsCard {
|
||||||
|
tab: "typography"
|
||||||
|
tags: ["animation", "motion", "effect", "slide", "directional", "depth", "spring", "physics"]
|
||||||
|
title: I18n.tr("Motion Effects")
|
||||||
|
settingKey: "motionEffect"
|
||||||
|
iconName: "motion_photos_on"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: motionEffectGroup.implicitHeight
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
DankButtonGroup {
|
||||||
|
id: motionEffectGroup
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
buttonPadding: parent.width < 480 ? Theme.spacingS : Theme.spacingL
|
||||||
|
minButtonWidth: parent.width < 480 ? 64 : 96
|
||||||
|
textSize: parent.width < 480 ? Theme.fontSizeSmall : Theme.fontSizeMedium
|
||||||
|
model: [I18n.tr("Standard"), I18n.tr("Directional"), I18n.tr("Depth")]
|
||||||
|
selectionMode: "single"
|
||||||
|
currentIndex: SettingsData.motionEffect
|
||||||
|
onSelectionChanged: (index, selected) => {
|
||||||
|
if (!selected)
|
||||||
|
return;
|
||||||
|
SettingsData.set("motionEffect", index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onMotionEffectChanged() {
|
||||||
|
motionEffectGroup.currentIndex = SettingsData.motionEffect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Theme.outline
|
||||||
|
opacity: 0.15
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: motionEffectDescription.implicitHeight + Theme.spacingS * 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: motionEffectDescription
|
||||||
|
x: Theme.spacingM
|
||||||
|
y: Theme.spacingS
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: {
|
||||||
|
switch (SettingsData.motionEffect) {
|
||||||
|
case 1:
|
||||||
|
return I18n.tr("Directional: Panels glide in from a larger distance at full size — no scale change, pure clean motion.");
|
||||||
|
case 2:
|
||||||
|
return I18n.tr("Depth: Panels scale up from small as they slide in — a dramatic pop-forward depth effect.");
|
||||||
|
default:
|
||||||
|
return I18n.tr("Standard: Classic Material Design 3 — panels rise from below with a subtle scale. The DMS default.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Theme.outline
|
||||||
|
opacity: 0.15
|
||||||
|
visible: SettingsData.motionEffect === 1
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsDropdownRow {
|
||||||
|
visible: SettingsData.motionEffect === 1
|
||||||
|
tab: "typography"
|
||||||
|
tags: ["animation", "directional", "behavior", "overlap", "sticky", "roll", "connected"]
|
||||||
|
settingKey: "directionalAnimationMode"
|
||||||
|
text: I18n.tr("Directional Behavior")
|
||||||
|
description: {
|
||||||
|
if (SettingsData.connectedFrameModeActive)
|
||||||
|
return I18n.tr("Popouts emerge flush from the bar edge as a single continuous piece, with corner connectors bridging the junction");
|
||||||
|
return I18n.tr("How the popout emerges from the DankBar");
|
||||||
|
}
|
||||||
|
options: SettingsData.frameEnabled
|
||||||
|
? [I18n.tr("Overlap"), I18n.tr("Slide"), I18n.tr("Roll"), I18n.tr("Connected")]
|
||||||
|
: [I18n.tr("Overlap"), I18n.tr("Slide"), I18n.tr("Roll")]
|
||||||
|
currentValue: {
|
||||||
|
switch (SettingsData.directionalAnimationMode) {
|
||||||
|
case 1:
|
||||||
|
return I18n.tr("Slide");
|
||||||
|
case 2:
|
||||||
|
return I18n.tr("Roll");
|
||||||
|
case 3:
|
||||||
|
return SettingsData.frameEnabled ? I18n.tr("Connected") : I18n.tr("Slide");
|
||||||
|
default:
|
||||||
|
return I18n.tr("Overlap");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onValueChanged: value => {
|
||||||
|
if (value === I18n.tr("Slide"))
|
||||||
|
SettingsData.set("directionalAnimationMode", 1);
|
||||||
|
else if (value === I18n.tr("Roll"))
|
||||||
|
SettingsData.set("directionalAnimationMode", 2);
|
||||||
|
else if (value === I18n.tr("Connected") && SettingsData.frameEnabled) {
|
||||||
|
if (SettingsData.directionalAnimationMode !== 3)
|
||||||
|
SettingsData.set("previousDirectionalMode", SettingsData.directionalAnimationMode);
|
||||||
|
SettingsData.set("directionalAnimationMode", 3);
|
||||||
|
} else
|
||||||
|
SettingsData.set("directionalAnimationMode", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
tab: "typography"
|
tab: "typography"
|
||||||
tags: ["font", "family", "text", "typography"]
|
tags: ["font", "family", "text", "typography"]
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ Item {
|
|||||||
description: modelData.width + "×" + modelData.height
|
description: modelData.width + "×" + modelData.height
|
||||||
checked: localChecked
|
checked: localChecked
|
||||||
onToggled: isChecked => {
|
onToggled: isChecked => {
|
||||||
localChecked = isChecked;
|
|
||||||
var prefs = JSON.parse(JSON.stringify(root.displayPreferences));
|
var prefs = JSON.parse(JSON.stringify(root.displayPreferences));
|
||||||
if (!Array.isArray(prefs) || prefs.includes("all"))
|
if (!Array.isArray(prefs) || prefs.includes("all"))
|
||||||
prefs = [];
|
prefs = [];
|
||||||
@@ -94,6 +93,11 @@ Item {
|
|||||||
model: modelData.model || ""
|
model: modelData.model || ""
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (prefs.length === 0) {
|
||||||
|
localChecked = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
localChecked = isChecked;
|
||||||
root.preferencesChanged(prefs);
|
root.preferencesChanged(prefs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,9 +121,9 @@ Scope {
|
|||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: overviewScope.overviewOpen ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
|
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,45 +154,69 @@ Scope {
|
|||||||
id: scaleTransform
|
id: scaleTransform
|
||||||
origin.x: contentContainer.width / 2
|
origin.x: contentContainer.width / 2
|
||||||
origin.y: contentContainer.height / 2
|
origin.y: contentContainer.height / 2
|
||||||
xScale: overviewScope.overviewOpen ? 1 : 0.96
|
xScale: overviewScope.overviewOpen ? 1 : Theme.effectScaleCollapsed
|
||||||
yScale: overviewScope.overviewOpen ? 1 : 0.96
|
yScale: overviewScope.overviewOpen ? 1 : Theme.effectScaleCollapsed
|
||||||
|
|
||||||
Behavior on xScale {
|
Behavior on xScale {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: overviewScope.overviewOpen ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
|
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on yScale {
|
Behavior on yScale {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: overviewScope.overviewOpen ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
|
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Translate {
|
Translate {
|
||||||
id: motionTransform
|
id: motionTransform
|
||||||
x: 0
|
x: {
|
||||||
y: overviewScope.overviewOpen ? 0 : Theme.spacingL
|
if (overviewScope.overviewOpen)
|
||||||
|
return 0;
|
||||||
|
if (Theme.isDirectionalEffect)
|
||||||
|
return 0;
|
||||||
|
if (Theme.isDepthEffect)
|
||||||
|
return Theme.effectAnimOffset * 0.25;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
y: {
|
||||||
|
if (overviewScope.overviewOpen)
|
||||||
|
return 0;
|
||||||
|
if (Theme.isDirectionalEffect)
|
||||||
|
return -Math.max(contentContainer.height * 0.8, Theme.effectAnimOffset * 1.1);
|
||||||
|
if (Theme.isDepthEffect)
|
||||||
|
return Math.max(Theme.effectAnimOffset * 0.85, 28);
|
||||||
|
return Theme.effectAnimOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on y {
|
Behavior on y {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: overviewScope.overviewOpen ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
|
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: overviewScope.overviewOpen ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
|
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -202,8 +202,18 @@ Scope {
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: spotlightContainer
|
id: spotlightContainer
|
||||||
x: Theme.snap((parent.width - width) / 2, overlayWindow.dpr)
|
readonly property bool directionalEffect: Theme.isDirectionalEffect
|
||||||
y: Theme.snap((parent.height - height) / 2, overlayWindow.dpr)
|
readonly property bool depthEffect: Theme.isDepthEffect
|
||||||
|
readonly property real collapsedMotionX: depthEffect ? Theme.effectAnimOffset * 0.25 : 0
|
||||||
|
readonly property real collapsedMotionY: {
|
||||||
|
if (directionalEffect)
|
||||||
|
return Math.max(height * 0.85, Theme.effectAnimOffset * 1.1);
|
||||||
|
if (depthEffect)
|
||||||
|
return Math.max(Theme.effectAnimOffset * 0.8, 30);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
x: Theme.snap((parent.width - width) / 2 + (overlayWindow.shouldShowSpotlight ? 0 : collapsedMotionX), overlayWindow.dpr)
|
||||||
|
y: Theme.snap((parent.height - height) / 2 + (overlayWindow.shouldShowSpotlight ? 0 : collapsedMotionY), overlayWindow.dpr)
|
||||||
|
|
||||||
readonly property int baseWidth: {
|
readonly property int baseWidth: {
|
||||||
switch (SettingsData.dankLauncherV2Size) {
|
switch (SettingsData.dankLauncherV2Size) {
|
||||||
@@ -234,8 +244,8 @@ Scope {
|
|||||||
|
|
||||||
readonly property bool animatingOut: niriOverviewScope.isClosing && overlayWindow.isSpotlightScreen
|
readonly property bool animatingOut: niriOverviewScope.isClosing && overlayWindow.isSpotlightScreen
|
||||||
|
|
||||||
scale: overlayWindow.shouldShowSpotlight ? 1.0 : 0.96
|
scale: Theme.isDirectionalEffect ? 1 : (overlayWindow.shouldShowSpotlight ? 1.0 : Theme.effectScaleCollapsed)
|
||||||
opacity: overlayWindow.shouldShowSpotlight ? 1 : 0
|
opacity: Theme.isDirectionalEffect ? 1 : (overlayWindow.shouldShowSpotlight ? 1 : 0)
|
||||||
visible: overlayWindow.shouldShowSpotlight || animatingOut
|
visible: overlayWindow.shouldShowSpotlight || animatingOut
|
||||||
enabled: overlayWindow.shouldShowSpotlight
|
enabled: overlayWindow.shouldShowSpotlight
|
||||||
|
|
||||||
@@ -245,10 +255,11 @@ Scope {
|
|||||||
|
|
||||||
Behavior on scale {
|
Behavior on scale {
|
||||||
id: scaleAnimation
|
id: scaleAnimation
|
||||||
|
enabled: !Theme.isDirectionalEffect
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.fast
|
duration: Theme.variantDuration(Theme.expressiveDurations.fast, overlayWindow.shouldShowSpotlight)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: spotlightContainer.visible ? Theme.expressiveCurves.expressiveFastSpatial : Theme.expressiveCurves.standardAccel
|
easing.bezierCurve: spotlightContainer.visible ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
|
||||||
onRunningChanged: {
|
onRunningChanged: {
|
||||||
if (running || !spotlightContainer.animatingOut)
|
if (running || !spotlightContainer.animatingOut)
|
||||||
return;
|
return;
|
||||||
@@ -258,10 +269,27 @@ Scope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
|
enabled: !Theme.isDirectionalEffect
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.fast
|
duration: Theme.variantDuration(Theme.expressiveDurations.fast, overlayWindow.shouldShowSpotlight)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: spotlightContainer.visible ? Theme.expressiveCurves.expressiveFastSpatial : Theme.expressiveCurves.standardAccel
|
easing.bezierCurve: spotlightContainer.visible ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.variantDuration(Theme.expressiveDurations.fast, overlayWindow.shouldShowSpotlight)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: spotlightContainer.visible ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on y {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.variantDuration(Theme.expressiveDurations.fast, overlayWindow.shouldShowSpotlight)
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: spotlightContainer.visible ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,30 +62,30 @@ Item {
|
|||||||
|
|
||||||
Behavior on x {
|
Behavior on x {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewOpen)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel
|
easing.bezierCurve: Theme.variantModalEnterCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Behavior on y {
|
Behavior on y {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewOpen)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel
|
easing.bezierCurve: Theme.variantModalEnterCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewOpen)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel
|
easing.bezierCurve: Theme.variantModalEnterCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Behavior on height {
|
Behavior on height {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewOpen)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel
|
easing.bezierCurve: Theme.variantModalEnterCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,16 +124,16 @@ Item {
|
|||||||
|
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewOpen)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel
|
easing.bezierCurve: Theme.variantModalEnterCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Behavior on height {
|
Behavior on height {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Theme.expressiveDurations.expressiveDefaultSpatial
|
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewOpen)
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: Theme.expressiveCurves.emphasizedDecel
|
easing.bezierCurve: Theme.variantModalEnterCurve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
159
quickshell/Widgets/ConnectedCorner.qml
Normal file
159
quickshell/Widgets/ConnectedCorner.qml
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
import QtQuick
|
||||||
|
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
|
||||||
|
|
||||||
|
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: {
|
||||||
|
if (barSide === "top")
|
||||||
|
return isPlacementLeft ? "bottomLeft" : "bottomRight";
|
||||||
|
if (barSide === "bottom")
|
||||||
|
return isPlacementLeft ? "topLeft" : "topRight";
|
||||||
|
if (barSide === "left")
|
||||||
|
return isPlacementLeft ? "topRight" : "bottomRight";
|
||||||
|
return isPlacementLeft ? "topLeft" : "bottomLeft";
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
415
quickshell/Widgets/ConnectedShape.qml
Normal file
415
quickshell/Widgets/ConnectedShape.qml
Normal file
@@ -0,0 +1,415 @@
|
|||||||
|
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"
|
||||||
|
|
||||||
|
// ── Derived layout ──
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -8,6 +9,7 @@ Item {
|
|||||||
|
|
||||||
required property var targetWindow
|
required property var targetWindow
|
||||||
property var blurItem: null
|
property var blurItem: null
|
||||||
|
property bool blurEnabled: Theme.connectedSurfaceBlurEnabled
|
||||||
property real blurX: 0
|
property real blurX: 0
|
||||||
property real blurY: 0
|
property real blurY: 0
|
||||||
property real blurWidth: 0
|
property real blurWidth: 0
|
||||||
@@ -17,7 +19,7 @@ Item {
|
|||||||
property var _region: null
|
property var _region: null
|
||||||
|
|
||||||
function _apply() {
|
function _apply() {
|
||||||
if (!BlurService.enabled || !targetWindow) {
|
if (!blurEnabled || !BlurService.enabled || !targetWindow) {
|
||||||
_cleanup();
|
_cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -43,6 +45,8 @@ Item {
|
|||||||
_region = null;
|
_region = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onBlurEnabledChanged: _apply()
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: BlurService
|
target: BlurService
|
||||||
function onEnabledChanged() {
|
function onEnabledChanged() {
|
||||||
|
|||||||
@@ -3549,7 +3549,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"section": "popupTransparency",
|
"section": "popupTransparency",
|
||||||
"label": "Popup Transparency",
|
"label": "Surface Opacity",
|
||||||
"tabIndex": 10,
|
"tabIndex": 10,
|
||||||
"category": "Theme & Colors",
|
"category": "Theme & Colors",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -3567,6 +3567,7 @@
|
|||||||
"popup",
|
"popup",
|
||||||
"scheme",
|
"scheme",
|
||||||
"style",
|
"style",
|
||||||
|
"surface",
|
||||||
"their",
|
"their",
|
||||||
"theme",
|
"theme",
|
||||||
"translucent",
|
"translucent",
|
||||||
|
|||||||
Reference in New Issue
Block a user