mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-16 16:15:23 -04:00
Compare commits
10 Commits
connected
...
3701b3d7a3
| Author | SHA1 | Date | |
|---|---|---|---|
| 3701b3d7a3 | |||
| bae98daa5c | |||
| b34a04f723 | |||
| 1c0245f2db | |||
| 7777e87dc8 | |||
| 820fa07846 | |||
| 66794582c9 | |||
| 73eb471ae3 | |||
| 0f2f4b96c4 | |||
| d53809cf2b |
@@ -235,7 +235,7 @@ Conditionally show/hide the bar pill:
|
|||||||
```qml
|
```qml
|
||||||
PluginComponent {
|
PluginComponent {
|
||||||
visibilityCommand: "pgrep -x myapp"
|
visibilityCommand: "pgrep -x myapp"
|
||||||
visibilityInterval: 5000 // check every 5 seconds
|
visibilityInterval: 5 // seconds between checks; polling pauses while the bar is hidden
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ Item {
|
|||||||
|
|
||||||
required property var modalHandle
|
required property var modalHandle
|
||||||
required property string claimPrefix
|
required property string claimPrefix
|
||||||
|
property string surfaceKind: "modal"
|
||||||
property string screenName: ""
|
property string screenName: ""
|
||||||
property bool enabled: false
|
property bool enabled: false
|
||||||
property bool active: false
|
property bool active: false
|
||||||
@@ -14,112 +15,97 @@ Item {
|
|||||||
property bool dockBlocked: false
|
property bool dockBlocked: false
|
||||||
property string dockSide: ""
|
property string dockSide: ""
|
||||||
|
|
||||||
property string claimId: ""
|
property alias claimId: lease.claimId
|
||||||
property string claimedScreenName: ""
|
property alias claimedScreenName: lease.claimedScreenName
|
||||||
|
|
||||||
signal recoveryRequested
|
signal recoveryRequested
|
||||||
|
|
||||||
visible: false
|
visible: false
|
||||||
|
|
||||||
function _nextClaimId() {
|
|
||||||
return claimPrefix + ":" + (new Date()).getTime() + ":" + Math.floor(Math.random() * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _isCurrentModal(name) {
|
function _isCurrentModal(name) {
|
||||||
return !!name && ModalManager.isCurrentModal(modalHandle, name);
|
return !!name && ModalManager.isCurrentModal(modalHandle, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _shouldRecover() {
|
ConnectedSurfaceLease {
|
||||||
return active && enabled && _isCurrentModal(screenName);
|
id: lease
|
||||||
|
claimPrefix: root.claimPrefix
|
||||||
|
screenName: root.screenName
|
||||||
|
enabled: root.enabled
|
||||||
|
active: root.active
|
||||||
|
presented: root.presented
|
||||||
|
dockBlocked: root.dockBlocked
|
||||||
|
dockSide: root.dockSide
|
||||||
|
isCurrentOwner: function(name) {
|
||||||
|
return root._isCurrentModal(name);
|
||||||
}
|
}
|
||||||
|
hasOwner: function(name, ownerId) {
|
||||||
function _requestRecovery() {
|
return ConnectedModeState.hasModalOwner(name, ownerId);
|
||||||
if (_shouldRecover())
|
}
|
||||||
recoveryRequested();
|
statePresent: function(name, ownerId) {
|
||||||
|
return ConnectedModeState.hasModalOwner(name, ownerId) && ConnectedModeState.hasSurfaceDescriptor(name, root.surfaceKind, ownerId);
|
||||||
|
}
|
||||||
|
claimState: function(name, state, ownerId) {
|
||||||
|
return ConnectedModeState.claimModalState(name, state, ownerId);
|
||||||
|
}
|
||||||
|
ensureState: function(name, state, ownerId) {
|
||||||
|
return ConnectedModeState.ensureModalState(name, state, ownerId);
|
||||||
|
}
|
||||||
|
releaseState: function(name, ownerId) {
|
||||||
|
return ConnectedModeState.clearModalState(name, ownerId);
|
||||||
|
}
|
||||||
|
updateAnimationState: function(name, ownerId, animX, animY) {
|
||||||
|
return ConnectedModeState.setModalAnim(name, animX, animY, ownerId);
|
||||||
|
}
|
||||||
|
updateBodyState: function(name, ownerId, bodyX, bodyY, bodyW, bodyH) {
|
||||||
|
return ConnectedModeState.setModalBody(name, bodyX, bodyY, bodyW, bodyH, ownerId);
|
||||||
|
}
|
||||||
|
requestDockRetract: function(ownerId, name, side) {
|
||||||
|
return ConnectedModeState.requestDockRetract(ownerId, name, side);
|
||||||
|
}
|
||||||
|
releaseDockRetract: function(ownerId) {
|
||||||
|
return ConnectedModeState.releaseDockRetract(ownerId);
|
||||||
|
}
|
||||||
|
onRecoveryRequested: root.recoveryRequested()
|
||||||
}
|
}
|
||||||
|
|
||||||
function publish(state) {
|
function publish(state) {
|
||||||
if (!enabled || !screenName || !state) {
|
return lease.publish(Object.assign({}, state, {
|
||||||
release();
|
"kind": root.surfaceKind,
|
||||||
return false;
|
"screenName": root.screenName,
|
||||||
}
|
"presented": root.presented,
|
||||||
if (claimedScreenName && claimedScreenName !== screenName)
|
"dockRetractSide": root.dockBlocked ? root.dockSide : ""
|
||||||
release();
|
}), false);
|
||||||
|
|
||||||
const isCurrent = _isCurrentModal(screenName);
|
|
||||||
let isClaim = !claimId;
|
|
||||||
if (isClaim && !isCurrent)
|
|
||||||
return false;
|
|
||||||
if (isClaim)
|
|
||||||
claimId = _nextClaimId();
|
|
||||||
|
|
||||||
let published = isClaim ? ConnectedModeState.claimModalState(screenName, state, claimId) : ConnectedModeState.ensureModalState(screenName, state, claimId);
|
|
||||||
if (!published && !isClaim && isCurrent) {
|
|
||||||
ConnectedModeState.releaseDockRetract(claimId);
|
|
||||||
claimId = _nextClaimId();
|
|
||||||
published = ConnectedModeState.claimModalState(screenName, state, claimId);
|
|
||||||
}
|
|
||||||
if (!published)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
claimedScreenName = screenName;
|
|
||||||
if (dockBlocked && presented)
|
|
||||||
ConnectedModeState.requestDockRetract(claimId, screenName, dockSide);
|
|
||||||
else
|
|
||||||
ConnectedModeState.releaseDockRetract(claimId);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAnim(animX, animY) {
|
function updateAnim(animX, animY) {
|
||||||
if (!enabled || !claimId || !claimedScreenName)
|
return lease.updateAnim(animX, animY);
|
||||||
return false;
|
|
||||||
if (!ConnectedModeState.hasModalOwner(claimedScreenName, claimId)) {
|
|
||||||
_requestRecovery();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return ConnectedModeState.setModalAnim(claimedScreenName, animX, animY, claimId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateBody(bodyX, bodyY, bodyW, bodyH) {
|
function updateBody(bodyX, bodyY, bodyW, bodyH) {
|
||||||
if (!enabled || !claimId || !claimedScreenName)
|
return lease.updateBody(bodyX, bodyY, bodyW, bodyH);
|
||||||
return false;
|
|
||||||
if (!ConnectedModeState.hasModalOwner(claimedScreenName, claimId)) {
|
|
||||||
_requestRecovery();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return ConnectedModeState.setModalBody(claimedScreenName, bodyX, bodyY, bodyW, bodyH, claimId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function release() {
|
function release() {
|
||||||
if (!claimId)
|
return lease.release();
|
||||||
return;
|
|
||||||
ConnectedModeState.releaseDockRetract(claimId);
|
|
||||||
const releasedClaimId = claimId;
|
|
||||||
const releasedScreenName = claimedScreenName;
|
|
||||||
claimId = "";
|
|
||||||
claimedScreenName = "";
|
|
||||||
if (releasedScreenName)
|
|
||||||
ConnectedModeState.clearModalState(releasedScreenName, releasedClaimId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onDestruction: release()
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ModalManager
|
target: ModalManager
|
||||||
function onModalChanged() {
|
function onModalChanged() {
|
||||||
root._requestRecovery();
|
lease.requestRecovery();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ConnectedModeState
|
target: ConnectedModeState
|
||||||
function onModalOwnersChanged() {
|
function onModalOwnersChanged() {
|
||||||
if (!ConnectedModeState.hasModalOwner(root.screenName, root.claimId))
|
lease.checkOwnershipRecovery();
|
||||||
root._requestRecovery();
|
|
||||||
}
|
}
|
||||||
function onModalStatesChanged() {
|
function onModalStatesChanged() {
|
||||||
if (!ConnectedModeState.modalStates[root.screenName])
|
lease.checkStateRecovery();
|
||||||
root._requestRecovery();
|
}
|
||||||
|
function onSurfaceDescriptorsChanged() {
|
||||||
|
lease.checkStateRecovery();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,123 @@ pragma ComponentBehavior: Bound
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import "ConnectedSurfaceDescriptor.js" as SurfaceDescriptor
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property var surfaceDescriptors: ({})
|
||||||
|
|
||||||
|
function _surfaceSlot(kind) {
|
||||||
|
return SurfaceDescriptor.slotForKind(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
function surfaceDescriptor(screenName, kind) {
|
||||||
|
const slot = _surfaceSlot(kind);
|
||||||
|
const screenDescriptors = screenName ? surfaceDescriptors[screenName] : null;
|
||||||
|
const descriptor = screenDescriptors && screenDescriptors[slot] ? screenDescriptors[slot] : SurfaceDescriptor.empty(kind, screenName);
|
||||||
|
let bodyRect = descriptor.bodyRect;
|
||||||
|
let animationOffset = descriptor.animationOffset;
|
||||||
|
if (slot === "popout" && popoutScreen === screenName) {
|
||||||
|
bodyRect = {
|
||||||
|
"x": popoutBodyX,
|
||||||
|
"y": popoutBodyY,
|
||||||
|
"width": popoutBodyW,
|
||||||
|
"height": popoutBodyH
|
||||||
|
};
|
||||||
|
animationOffset = {
|
||||||
|
"x": popoutAnimX,
|
||||||
|
"y": popoutAnimY
|
||||||
|
};
|
||||||
|
} else if (slot === "modal" && modalStates[screenName]) {
|
||||||
|
const modal = modalStates[screenName];
|
||||||
|
bodyRect = {
|
||||||
|
"x": modal.bodyX,
|
||||||
|
"y": modal.bodyY,
|
||||||
|
"width": modal.bodyW,
|
||||||
|
"height": modal.bodyH
|
||||||
|
};
|
||||||
|
animationOffset = {
|
||||||
|
"x": modal.animX,
|
||||||
|
"y": modal.animY
|
||||||
|
};
|
||||||
|
} else if (slot === "dock" && dockStates[screenName]) {
|
||||||
|
const dock = dockStates[screenName];
|
||||||
|
const slide = dockSlides[screenName] || {
|
||||||
|
"x": dock.slideX,
|
||||||
|
"y": dock.slideY
|
||||||
|
};
|
||||||
|
bodyRect = {
|
||||||
|
"x": dock.bodyX,
|
||||||
|
"y": dock.bodyY,
|
||||||
|
"width": dock.bodyW,
|
||||||
|
"height": dock.bodyH
|
||||||
|
};
|
||||||
|
animationOffset = {
|
||||||
|
"x": slide.x,
|
||||||
|
"y": slide.y
|
||||||
|
};
|
||||||
|
} else if (slot === "notification" && notificationStates[screenName]) {
|
||||||
|
const notification = notificationStates[screenName];
|
||||||
|
bodyRect = {
|
||||||
|
"x": notification.bodyX,
|
||||||
|
"y": notification.bodyY,
|
||||||
|
"width": notification.bodyW,
|
||||||
|
"height": notification.bodyH
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return SurfaceDescriptor.normalize({
|
||||||
|
"bodyRect": bodyRect,
|
||||||
|
"animationOffset": animationOffset
|
||||||
|
}, descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasSurfaceDescriptor(screenName, kind, ownerId) {
|
||||||
|
const descriptor = surfaceDescriptor(screenName, kind);
|
||||||
|
return descriptor.phase !== "hidden" && (!ownerId || descriptor.ownerId === ownerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _setSurfaceDescriptor(screenName, slotKind, state, ownerId) {
|
||||||
|
if (!screenName || !state)
|
||||||
|
return false;
|
||||||
|
const slot = _surfaceSlot(slotKind);
|
||||||
|
const currentScreen = surfaceDescriptors[screenName] || {};
|
||||||
|
const previous = currentScreen[slot] || SurfaceDescriptor.empty(state.kind || slotKind, screenName);
|
||||||
|
let normalized = SurfaceDescriptor.normalize(Object.assign({}, state, {
|
||||||
|
"ownerId": ownerId !== undefined ? ownerId : previous.ownerId,
|
||||||
|
"screenName": screenName,
|
||||||
|
"revision": previous.revision
|
||||||
|
}), previous);
|
||||||
|
if (SurfaceDescriptor.same(previous, normalized))
|
||||||
|
return true;
|
||||||
|
normalized = SurfaceDescriptor.withRevision(normalized, previous.revision + 1);
|
||||||
|
const nextScreen = _cloneDict(currentScreen);
|
||||||
|
nextScreen[slot] = normalized;
|
||||||
|
const next = _cloneDict(surfaceDescriptors);
|
||||||
|
next[screenName] = nextScreen;
|
||||||
|
surfaceDescriptors = next;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _clearSurfaceDescriptor(screenName, kind, ownerId) {
|
||||||
|
if (!screenName)
|
||||||
|
return false;
|
||||||
|
const slot = _surfaceSlot(kind);
|
||||||
|
const currentScreen = surfaceDescriptors[screenName];
|
||||||
|
const current = currentScreen ? currentScreen[slot] : null;
|
||||||
|
if (!current || (ownerId && current.ownerId !== ownerId))
|
||||||
|
return false;
|
||||||
|
const nextScreen = _cloneDict(currentScreen);
|
||||||
|
delete nextScreen[slot];
|
||||||
|
const next = _cloneDict(surfaceDescriptors);
|
||||||
|
if (Object.keys(nextScreen).length > 0)
|
||||||
|
next[screenName] = nextScreen;
|
||||||
|
else
|
||||||
|
delete next[screenName];
|
||||||
|
surfaceDescriptors = next;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
readonly property var emptyDockState: ({
|
readonly property var emptyDockState: ({
|
||||||
"reveal": false,
|
"reveal": false,
|
||||||
"barSide": "bottom",
|
"barSide": "bottom",
|
||||||
@@ -18,7 +131,6 @@ Singleton {
|
|||||||
"slideY": 0
|
"slideY": 0
|
||||||
})
|
})
|
||||||
|
|
||||||
// Popout state (updated by DankPopout when connectedFrameModeActive)
|
|
||||||
property string popoutOwnerId: ""
|
property string popoutOwnerId: ""
|
||||||
property bool popoutVisible: false
|
property bool popoutVisible: false
|
||||||
property string popoutBarSide: "top"
|
property string popoutBarSide: "top"
|
||||||
@@ -32,14 +144,10 @@ Singleton {
|
|||||||
property bool popoutOmitStartConnector: false
|
property bool popoutOmitStartConnector: false
|
||||||
property bool popoutOmitEndConnector: false
|
property bool popoutOmitEndConnector: false
|
||||||
|
|
||||||
// Dock state (updated by Dock when connectedFrameModeActive), keyed by screen.name
|
|
||||||
property var dockStates: ({})
|
property var dockStates: ({})
|
||||||
|
|
||||||
// Dock slide offsets — hot-path updates separated from full geometry state
|
|
||||||
property var dockSlides: ({})
|
property var dockSlides: ({})
|
||||||
|
|
||||||
// Surfaces are keyed by screen.name. FrameWindow watches to refresh connected chrome
|
|
||||||
// after claim/release boundaries without tracking each animation frame
|
|
||||||
property var surfaceRevisions: ({})
|
property var surfaceRevisions: ({})
|
||||||
|
|
||||||
function _cloneDict(src) {
|
function _cloneDict(src) {
|
||||||
@@ -69,8 +177,10 @@ Singleton {
|
|||||||
popoutOwnerId = claimId;
|
popoutOwnerId = claimId;
|
||||||
const ok = updatePopout(claimId, state);
|
const ok = updatePopout(claimId, state);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
if (previousScreen && previousScreen !== popoutScreen)
|
if (previousScreen && previousScreen !== popoutScreen) {
|
||||||
|
_clearSurfaceDescriptor(previousScreen, "popout");
|
||||||
_bumpSurfaceRevision(previousScreen);
|
_bumpSurfaceRevision(previousScreen);
|
||||||
|
}
|
||||||
_bumpSurfaceRevision(popoutScreen);
|
_bumpSurfaceRevision(popoutScreen);
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
@@ -103,6 +213,21 @@ Singleton {
|
|||||||
if (state.omitEndConnector !== undefined)
|
if (state.omitEndConnector !== undefined)
|
||||||
popoutOmitEndConnector = !!state.omitEndConnector;
|
popoutOmitEndConnector = !!state.omitEndConnector;
|
||||||
|
|
||||||
|
_setSurfaceDescriptor(popoutScreen, "popout", Object.assign({}, state, {
|
||||||
|
"kind": "popout",
|
||||||
|
"screenName": popoutScreen,
|
||||||
|
"visible": popoutVisible,
|
||||||
|
"presented": state.presented !== undefined ? !!state.presented : popoutVisible,
|
||||||
|
"barSide": popoutBarSide,
|
||||||
|
"bodyX": popoutBodyX,
|
||||||
|
"bodyY": popoutBodyY,
|
||||||
|
"bodyW": popoutBodyW,
|
||||||
|
"bodyH": popoutBodyH,
|
||||||
|
"animX": popoutAnimX,
|
||||||
|
"animY": popoutAnimY,
|
||||||
|
"omitStartConnector": popoutOmitStartConnector,
|
||||||
|
"omitEndConnector": popoutOmitEndConnector
|
||||||
|
}), claimId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +248,7 @@ Singleton {
|
|||||||
popoutScreen = "";
|
popoutScreen = "";
|
||||||
popoutOmitStartConnector = false;
|
popoutOmitStartConnector = false;
|
||||||
popoutOmitEndConnector = false;
|
popoutOmitEndConnector = false;
|
||||||
|
_clearSurfaceDescriptor(releasedScreen, "popout", claimId);
|
||||||
_bumpSurfaceRevision(releasedScreen);
|
_bumpSurfaceRevision(releasedScreen);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -193,13 +319,21 @@ Singleton {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
const normalized = _normalizeDockState(state);
|
const normalized = _normalizeDockState(state);
|
||||||
if (_sameDockState(dockStates[screenName], normalized))
|
const descriptorState = Object.assign({}, state, normalized, {
|
||||||
return true;
|
"kind": "dock",
|
||||||
|
"screenName": screenName,
|
||||||
|
"visible": normalized.reveal,
|
||||||
|
"presented": normalized.reveal,
|
||||||
|
"phase": normalized.reveal ? (state.phase || "open") : "hidden"
|
||||||
|
});
|
||||||
const previous = dockStates[screenName] || emptyDockState;
|
const previous = dockStates[screenName] || emptyDockState;
|
||||||
|
const stateChanged = !_sameDockState(dockStates[screenName], normalized);
|
||||||
|
if (stateChanged) {
|
||||||
const next = _cloneDict(dockStates);
|
const next = _cloneDict(dockStates);
|
||||||
next[screenName] = normalized;
|
next[screenName] = normalized;
|
||||||
dockStates = next;
|
dockStates = next;
|
||||||
|
}
|
||||||
|
_setSurfaceDescriptor(screenName, "dock", descriptorState, "dock:" + screenName);
|
||||||
if (!!previous.reveal !== !!normalized.reveal)
|
if (!!previous.reveal !== !!normalized.reveal)
|
||||||
_bumpSurfaceRevision(screenName);
|
_bumpSurfaceRevision(screenName);
|
||||||
return true;
|
return true;
|
||||||
@@ -212,8 +346,8 @@ Singleton {
|
|||||||
const next = _cloneDict(dockStates);
|
const next = _cloneDict(dockStates);
|
||||||
delete next[screenName];
|
delete next[screenName];
|
||||||
dockStates = next;
|
dockStates = next;
|
||||||
|
_clearSurfaceDescriptor(screenName, "dock");
|
||||||
|
|
||||||
// Also clear corresponding slide
|
|
||||||
if (dockSlides[screenName]) {
|
if (dockSlides[screenName]) {
|
||||||
const nextSlides = _cloneDict(dockSlides);
|
const nextSlides = _cloneDict(dockSlides);
|
||||||
delete nextSlides[screenName];
|
delete nextSlides[screenName];
|
||||||
@@ -283,13 +417,20 @@ Singleton {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
const normalized = _normalizeNotificationState(state);
|
const normalized = _normalizeNotificationState(state);
|
||||||
if (_sameNotificationState(notificationStates[screenName], normalized))
|
const descriptorState = Object.assign({}, state, normalized, {
|
||||||
return true;
|
"kind": "notification",
|
||||||
|
"screenName": screenName,
|
||||||
|
"presented": normalized.visible,
|
||||||
|
"phase": normalized.visible ? (state.phase || "open") : "hidden"
|
||||||
|
});
|
||||||
const previous = notificationStates[screenName] || emptyNotificationState;
|
const previous = notificationStates[screenName] || emptyNotificationState;
|
||||||
|
const stateChanged = !_sameNotificationState(notificationStates[screenName], normalized);
|
||||||
|
if (stateChanged) {
|
||||||
const next = _cloneDict(notificationStates);
|
const next = _cloneDict(notificationStates);
|
||||||
next[screenName] = normalized;
|
next[screenName] = normalized;
|
||||||
notificationStates = next;
|
notificationStates = next;
|
||||||
|
}
|
||||||
|
_setSurfaceDescriptor(screenName, "notification", descriptorState, "notification:" + screenName);
|
||||||
if (!!previous.visible !== !!normalized.visible)
|
if (!!previous.visible !== !!normalized.visible)
|
||||||
_bumpSurfaceRevision(screenName);
|
_bumpSurfaceRevision(screenName);
|
||||||
return true;
|
return true;
|
||||||
@@ -302,11 +443,11 @@ Singleton {
|
|||||||
const next = _cloneDict(notificationStates);
|
const next = _cloneDict(notificationStates);
|
||||||
delete next[screenName];
|
delete next[screenName];
|
||||||
notificationStates = next;
|
notificationStates = next;
|
||||||
|
_clearSurfaceDescriptor(screenName, "notification");
|
||||||
_bumpSurfaceRevision(screenName);
|
_bumpSurfaceRevision(screenName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DankModal / DankLauncherV2Modal State
|
|
||||||
readonly property var emptyModalState: ({
|
readonly property var emptyModalState: ({
|
||||||
"visible": false,
|
"visible": false,
|
||||||
"barSide": "bottom",
|
"barSide": "bottom",
|
||||||
@@ -362,6 +503,10 @@ Singleton {
|
|||||||
const next = _cloneDict(modalStates);
|
const next = _cloneDict(modalStates);
|
||||||
next[screenName] = normalized;
|
next[screenName] = normalized;
|
||||||
modalStates = next;
|
modalStates = next;
|
||||||
|
_setSurfaceDescriptor(screenName, "modal", Object.assign({}, state, normalized, {
|
||||||
|
"kind": state.kind || "modal",
|
||||||
|
"screenName": screenName
|
||||||
|
}), ownerId || "");
|
||||||
_bumpSurfaceRevision(screenName);
|
_bumpSurfaceRevision(screenName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -372,11 +517,16 @@ Singleton {
|
|||||||
if (ownerId && modalOwners[screenName] !== ownerId)
|
if (ownerId && modalOwners[screenName] !== ownerId)
|
||||||
return false;
|
return false;
|
||||||
const normalized = _normalizeModalState(state);
|
const normalized = _normalizeModalState(state);
|
||||||
if (_sameModalState(modalStates[screenName], normalized))
|
const descriptorState = Object.assign({}, state, normalized, {
|
||||||
return true;
|
"kind": state.kind || (surfaceDescriptor(screenName, "modal").kind || "modal"),
|
||||||
|
"screenName": screenName
|
||||||
|
});
|
||||||
|
if (!_sameModalState(modalStates[screenName], normalized)) {
|
||||||
const next = _cloneDict(modalStates);
|
const next = _cloneDict(modalStates);
|
||||||
next[screenName] = normalized;
|
next[screenName] = normalized;
|
||||||
modalStates = next;
|
modalStates = next;
|
||||||
|
}
|
||||||
|
_setSurfaceDescriptor(screenName, "modal", descriptorState, ownerId || modalOwners[screenName] || "");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,10 +545,6 @@ Singleton {
|
|||||||
return updateModalState(screenName, state, ownerId);
|
return updateModalState(screenName, state, ownerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setModalState(screenName, state) {
|
|
||||||
return updateModalState(screenName, state, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearModalState(screenName, ownerId) {
|
function clearModalState(screenName, ownerId) {
|
||||||
if (!screenName)
|
if (!screenName)
|
||||||
return false;
|
return false;
|
||||||
@@ -418,6 +564,7 @@ Singleton {
|
|||||||
delete nextOwners[screenName];
|
delete nextOwners[screenName];
|
||||||
modalOwners = nextOwners;
|
modalOwners = nextOwners;
|
||||||
}
|
}
|
||||||
|
_clearSurfaceDescriptor(screenName, "modal", ownerId);
|
||||||
_bumpSurfaceRevision(screenName);
|
_bumpSurfaceRevision(screenName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -501,9 +648,6 @@ Singleton {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prune state for screens that are no longer connected. Stale entries
|
|
||||||
// accumulate across hotplug cycles otherwise — Frame's per-screen
|
|
||||||
// FrameInstance doesn't notice when its peer dicts go orphan.
|
|
||||||
function _pruneToLiveScreens() {
|
function _pruneToLiveScreens() {
|
||||||
const live = {};
|
const live = {};
|
||||||
const screens = Quickshell.screens || [];
|
const screens = Quickshell.screens || [];
|
||||||
@@ -543,6 +687,9 @@ Singleton {
|
|||||||
const nextSurfaceRevisions = pruneKeyed(surfaceRevisions);
|
const nextSurfaceRevisions = pruneKeyed(surfaceRevisions);
|
||||||
if (nextSurfaceRevisions !== null)
|
if (nextSurfaceRevisions !== null)
|
||||||
surfaceRevisions = nextSurfaceRevisions;
|
surfaceRevisions = nextSurfaceRevisions;
|
||||||
|
const nextDescriptors = pruneKeyed(surfaceDescriptors);
|
||||||
|
if (nextDescriptors !== null)
|
||||||
|
surfaceDescriptors = nextDescriptors;
|
||||||
|
|
||||||
let retractChanged = false;
|
let retractChanged = false;
|
||||||
const nextRetract = {};
|
const nextRetract = {};
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
.pragma library
|
||||||
|
|
||||||
|
var VALID_KINDS = {
|
||||||
|
"popout": true,
|
||||||
|
"modal": true,
|
||||||
|
"launcher": true,
|
||||||
|
"dock": true,
|
||||||
|
"notification": true
|
||||||
|
};
|
||||||
|
|
||||||
|
var VALID_PHASES = {
|
||||||
|
"opening": true,
|
||||||
|
"open": true,
|
||||||
|
"closing": true,
|
||||||
|
"hidden": true,
|
||||||
|
"recovering": true
|
||||||
|
};
|
||||||
|
|
||||||
|
function _number(value, fallback) {
|
||||||
|
var n = Number(value);
|
||||||
|
return isNaN(n) ? fallback : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _bool(value, fallback) {
|
||||||
|
return value === undefined ? fallback : !!value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _kind(value, fallback) {
|
||||||
|
if (VALID_KINDS[value])
|
||||||
|
return value;
|
||||||
|
return VALID_KINDS[fallback] ? fallback : "modal";
|
||||||
|
}
|
||||||
|
|
||||||
|
function _defaultBarSide(kind) {
|
||||||
|
return kind === "popout" || kind === "notification" ? "top" : "bottom";
|
||||||
|
}
|
||||||
|
|
||||||
|
function _barSide(value, fallback) {
|
||||||
|
if (value === "top" || value === "bottom" || value === "left" || value === "right")
|
||||||
|
return value;
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function slotForKind(kind) {
|
||||||
|
return kind === "launcher" ? "modal" : _kind(kind, "modal");
|
||||||
|
}
|
||||||
|
|
||||||
|
function inferPhase(visible, presented, requestedPhase) {
|
||||||
|
if (VALID_PHASES[requestedPhase])
|
||||||
|
return requestedPhase;
|
||||||
|
if (!visible && !presented)
|
||||||
|
return "hidden";
|
||||||
|
if (!visible && presented)
|
||||||
|
return "closing";
|
||||||
|
return "open";
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalize(input, defaults) {
|
||||||
|
var source = input || {};
|
||||||
|
var base = defaults || {};
|
||||||
|
var kind = _kind(source.kind, base.kind);
|
||||||
|
var defaultSide = _defaultBarSide(kind);
|
||||||
|
var sourceRect = source.bodyRect || {};
|
||||||
|
var baseRect = base.bodyRect || {};
|
||||||
|
var sourceOffset = source.animationOffset || {};
|
||||||
|
var baseOffset = base.animationOffset || {};
|
||||||
|
var visible = _bool(source.visible !== undefined ? source.visible : source.reveal, _bool(base.visible !== undefined ? base.visible : base.reveal, false));
|
||||||
|
var presented = _bool(source.presented, _bool(base.presented, visible));
|
||||||
|
var bodyRect = {
|
||||||
|
"x": _number(sourceRect.x !== undefined ? sourceRect.x : source.bodyX, _number(baseRect.x !== undefined ? baseRect.x : base.bodyX, 0)),
|
||||||
|
"y": _number(sourceRect.y !== undefined ? sourceRect.y : source.bodyY, _number(baseRect.y !== undefined ? baseRect.y : base.bodyY, 0)),
|
||||||
|
"width": Math.max(0, _number(sourceRect.width !== undefined ? sourceRect.width : source.bodyW, _number(baseRect.width !== undefined ? baseRect.width : base.bodyW, 0))),
|
||||||
|
"height": Math.max(0, _number(sourceRect.height !== undefined ? sourceRect.height : source.bodyH, _number(baseRect.height !== undefined ? baseRect.height : base.bodyH, 0)))
|
||||||
|
};
|
||||||
|
var animationOffset = {
|
||||||
|
"x": _number(sourceOffset.x !== undefined ? sourceOffset.x : (source.animX !== undefined ? source.animX : source.slideX), _number(baseOffset.x !== undefined ? baseOffset.x : (base.animX !== undefined ? base.animX : base.slideX), 0)),
|
||||||
|
"y": _number(sourceOffset.y !== undefined ? sourceOffset.y : (source.animY !== undefined ? source.animY : source.slideY), _number(baseOffset.y !== undefined ? baseOffset.y : (base.animY !== undefined ? base.animY : base.slideY), 0))
|
||||||
|
};
|
||||||
|
var screenName = source.screenName !== undefined ? source.screenName : (source.screen !== undefined ? source.screen : (base.screenName !== undefined ? base.screenName : base.screen));
|
||||||
|
var opacity = Math.max(0, Math.min(1, _number(source.opacity, _number(base.opacity, 1))));
|
||||||
|
|
||||||
|
return {
|
||||||
|
"ownerId": String(source.ownerId !== undefined ? source.ownerId : (base.ownerId || "")),
|
||||||
|
"kind": kind,
|
||||||
|
"screenName": String(screenName || ""),
|
||||||
|
"phase": inferPhase(visible, presented, source.phase !== undefined ? source.phase : base.phase),
|
||||||
|
"visible": visible,
|
||||||
|
"presented": presented,
|
||||||
|
"barSide": _barSide(source.barSide, _barSide(base.barSide, defaultSide)),
|
||||||
|
"bodyRect": bodyRect,
|
||||||
|
"animationOffset": animationOffset,
|
||||||
|
"scale": Math.max(0, _number(source.scale, _number(base.scale, 1))),
|
||||||
|
"opacity": opacity,
|
||||||
|
"omitStartConnector": _bool(source.omitStartConnector, _bool(base.omitStartConnector, false)),
|
||||||
|
"omitEndConnector": _bool(source.omitEndConnector, _bool(base.omitEndConnector, false)),
|
||||||
|
"dockRetractSide": String(source.dockRetractSide !== undefined ? source.dockRetractSide : (base.dockRetractSide || "")),
|
||||||
|
"revision": Math.max(0, Math.floor(_number(source.revision, _number(base.revision, 0))))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function empty(kind, screenName) {
|
||||||
|
return normalize({
|
||||||
|
"kind": kind,
|
||||||
|
"screenName": screenName || "",
|
||||||
|
"phase": "hidden",
|
||||||
|
"visible": false,
|
||||||
|
"presented": false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function withRevision(descriptor, revision) {
|
||||||
|
var next = normalize(descriptor);
|
||||||
|
next.revision = Math.max(0, Math.floor(_number(revision, next.revision)));
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function withAnimationOffset(descriptor, x, y) {
|
||||||
|
var next = normalize(descriptor);
|
||||||
|
next.animationOffset = {
|
||||||
|
"x": x === undefined ? next.animationOffset.x : _number(x, next.animationOffset.x),
|
||||||
|
"y": y === undefined ? next.animationOffset.y : _number(y, next.animationOffset.y)
|
||||||
|
};
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function withBodyRect(descriptor, x, y, width, height) {
|
||||||
|
var next = normalize(descriptor);
|
||||||
|
next.bodyRect = {
|
||||||
|
"x": x === undefined ? next.bodyRect.x : _number(x, next.bodyRect.x),
|
||||||
|
"y": y === undefined ? next.bodyRect.y : _number(y, next.bodyRect.y),
|
||||||
|
"width": width === undefined ? next.bodyRect.width : Math.max(0, _number(width, next.bodyRect.width)),
|
||||||
|
"height": height === undefined ? next.bodyRect.height : Math.max(0, _number(height, next.bodyRect.height))
|
||||||
|
};
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function same(a, b, threshold) {
|
||||||
|
if (!a || !b)
|
||||||
|
return false;
|
||||||
|
var epsilon = threshold === undefined ? 0.5 : Math.max(0, Number(threshold));
|
||||||
|
return a.ownerId === b.ownerId
|
||||||
|
&& a.kind === b.kind
|
||||||
|
&& a.screenName === b.screenName
|
||||||
|
&& a.phase === b.phase
|
||||||
|
&& a.visible === b.visible
|
||||||
|
&& a.presented === b.presented
|
||||||
|
&& a.barSide === b.barSide
|
||||||
|
&& Math.abs(a.bodyRect.x - b.bodyRect.x) < epsilon
|
||||||
|
&& Math.abs(a.bodyRect.y - b.bodyRect.y) < epsilon
|
||||||
|
&& Math.abs(a.bodyRect.width - b.bodyRect.width) < epsilon
|
||||||
|
&& Math.abs(a.bodyRect.height - b.bodyRect.height) < epsilon
|
||||||
|
&& Math.abs(a.animationOffset.x - b.animationOffset.x) < epsilon
|
||||||
|
&& Math.abs(a.animationOffset.y - b.animationOffset.y) < epsilon
|
||||||
|
&& Math.abs(a.scale - b.scale) < 0.0001
|
||||||
|
&& Math.abs(a.opacity - b.opacity) < 0.0001
|
||||||
|
&& a.omitStartConnector === b.omitStartConnector
|
||||||
|
&& a.omitEndConnector === b.omitEndConnector
|
||||||
|
&& a.dockRetractSide === b.dockRetractSide;
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
.pragma library
|
||||||
|
|
||||||
|
function _number(value, fallback) {
|
||||||
|
var n = Number(value);
|
||||||
|
return isNaN(n) ? fallback : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function snap(value, dpr) {
|
||||||
|
var scale = dpr || 1;
|
||||||
|
return Math.round(_number(value, 0) * scale) / scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isHorizontal(side) {
|
||||||
|
return side === "top" || side === "bottom";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVertical(side) {
|
||||||
|
return side === "left" || side === "right";
|
||||||
|
}
|
||||||
|
|
||||||
|
function bodyRect(descriptor, dpr) {
|
||||||
|
var source = descriptor && descriptor.bodyRect ? descriptor.bodyRect : descriptor || {};
|
||||||
|
return {
|
||||||
|
"x": snap(source.x !== undefined ? source.x : source.bodyX, dpr),
|
||||||
|
"y": snap(source.y !== undefined ? source.y : source.bodyY, dpr),
|
||||||
|
"width": Math.max(0, snap(source.width !== undefined ? source.width : source.bodyW, dpr)),
|
||||||
|
"height": Math.max(0, snap(source.height !== undefined ? source.height : source.bodyH, dpr))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function animatedBodyRect(descriptor, dpr) {
|
||||||
|
var rect = bodyRect(descriptor, dpr);
|
||||||
|
var offset = descriptor && descriptor.animationOffset ? descriptor.animationOffset : descriptor || {};
|
||||||
|
var side = descriptor && descriptor.barSide ? descriptor.barSide : "bottom";
|
||||||
|
var dx = isVertical(side) ? Math.max(-rect.width, Math.min(_number(offset.x !== undefined ? offset.x : offset.animX, 0), rect.width)) : 0;
|
||||||
|
var dy = isHorizontal(side) ? Math.max(-rect.height, Math.min(_number(offset.y !== undefined ? offset.y : offset.animY, 0), rect.height)) : 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
"x": snap(rect.x + (side === "right" ? dx : 0), dpr),
|
||||||
|
"y": snap(rect.y + (side === "bottom" ? dy : 0), dpr),
|
||||||
|
"width": Math.max(0, snap(rect.width - Math.abs(dx), dpr)),
|
||||||
|
"height": Math.max(0, snap(rect.height - Math.abs(dy), dpr)),
|
||||||
|
"dx": snap(dx, dpr),
|
||||||
|
"dy": snap(dy, dpr)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function translatedBodyRect(descriptor, dpr) {
|
||||||
|
var rect = bodyRect(descriptor, dpr);
|
||||||
|
var offset = descriptor && descriptor.animationOffset ? descriptor.animationOffset : {};
|
||||||
|
return {
|
||||||
|
"x": snap(rect.x + _number(offset.x, 0), dpr),
|
||||||
|
"y": snap(rect.y + _number(offset.y, 0), dpr),
|
||||||
|
"width": rect.width,
|
||||||
|
"height": rect.height
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectorRadii(descriptor, rect, connectedRadius, surfaceRadius, dpr, nearIncludesSurface) {
|
||||||
|
var side = descriptor && descriptor.barSide ? descriptor.barSide : "bottom";
|
||||||
|
var horizontal = isHorizontal(side);
|
||||||
|
var extent = horizontal ? rect.height : rect.width;
|
||||||
|
var crossSize = horizontal ? rect.width : rect.height;
|
||||||
|
var nearLimit = nearIncludesSurface ? Math.min(connectedRadius, surfaceRadius, extent, crossSize / 2) : Math.min(connectedRadius, extent, crossSize / 2);
|
||||||
|
var farLimit = Math.min(connectedRadius, surfaceRadius, crossSize / 2);
|
||||||
|
var near = snap(Math.max(0, nearLimit), dpr);
|
||||||
|
var far = snap(Math.max(0, farLimit), dpr);
|
||||||
|
var omitStart = !!(descriptor && descriptor.omitStartConnector);
|
||||||
|
var omitEnd = !!(descriptor && descriptor.omitEndConnector);
|
||||||
|
return {
|
||||||
|
"near": near,
|
||||||
|
"far": far,
|
||||||
|
"start": omitStart ? 0 : near,
|
||||||
|
"end": omitEnd ? 0 : near,
|
||||||
|
"farStart": omitStart ? far : 0,
|
||||||
|
"farEnd": omitEnd ? far : 0,
|
||||||
|
"farExtent": Math.max(omitStart ? far : 0, omitEnd ? far : 0)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function _connectorWidth(side, spacing, radius) {
|
||||||
|
return isVertical(side) ? spacing + radius : radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _connectorHeight(side, spacing, radius) {
|
||||||
|
return isVertical(side) ? radius : spacing + radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectorRect(side, rect, placement, spacing, radius, dpr) {
|
||||||
|
var width = _connectorWidth(side, spacing, radius);
|
||||||
|
var height = _connectorHeight(side, spacing, radius);
|
||||||
|
var seamX = isVertical(side) ? (side === "left" ? rect.x : rect.x + rect.width) : (placement === "left" ? rect.x : rect.x + rect.width);
|
||||||
|
var seamY = side === "top" ? rect.y : (side === "bottom" ? rect.y + rect.height : (placement === "left" ? rect.y : rect.y + rect.height));
|
||||||
|
var x = isVertical(side) ? (side === "left" ? seamX : seamX - width) : (placement === "left" ? seamX - width : seamX);
|
||||||
|
var y = side === "top" ? seamY : (side === "bottom" ? seamY - height : (placement === "left" ? seamY - height : seamY));
|
||||||
|
return {
|
||||||
|
"x": snap(x, dpr),
|
||||||
|
"y": snap(y, dpr),
|
||||||
|
"width": Math.max(0, snap(width, dpr)),
|
||||||
|
"height": Math.max(0, snap(height, dpr))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function farConnectorRect(side, rect, placement, radius, dpr) {
|
||||||
|
var x;
|
||||||
|
var y;
|
||||||
|
if (isHorizontal(side)) {
|
||||||
|
x = placement === "left" ? rect.x : rect.x + rect.width - radius;
|
||||||
|
y = side === "top" ? rect.y + rect.height : rect.y - radius;
|
||||||
|
} else {
|
||||||
|
x = side === "left" ? rect.x + rect.width : rect.x - radius;
|
||||||
|
y = placement === "left" ? rect.y : rect.y + rect.height - radius;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"x": snap(x, dpr),
|
||||||
|
"y": snap(y, dpr),
|
||||||
|
"width": Math.max(0, snap(radius, dpr)),
|
||||||
|
"height": Math.max(0, snap(radius, dpr))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function farBodyCapRect(side, rect, placement, radius, dpr) {
|
||||||
|
var x;
|
||||||
|
var y;
|
||||||
|
if (isHorizontal(side)) {
|
||||||
|
x = placement === "left" ? rect.x : rect.x + rect.width - radius;
|
||||||
|
y = side === "top" ? rect.y + rect.height - radius : rect.y;
|
||||||
|
} else {
|
||||||
|
x = side === "left" ? rect.x + rect.width - radius : rect.x;
|
||||||
|
y = placement === "left" ? rect.y : rect.y + rect.height - radius;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"x": snap(x, dpr),
|
||||||
|
"y": snap(y, dpr),
|
||||||
|
"width": Math.max(0, snap(radius, dpr)),
|
||||||
|
"height": Math.max(0, snap(radius, dpr))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function chromeBounds(rect, side, startRadius, endRadius, farExtent, dpr) {
|
||||||
|
var horizontal = isHorizontal(side);
|
||||||
|
var bodyOffsetX = horizontal ? startRadius : (side === "right" ? farExtent : 0);
|
||||||
|
var bodyOffsetY = horizontal ? (side === "bottom" ? farExtent : 0) : startRadius;
|
||||||
|
return {
|
||||||
|
"x": snap(rect.x - bodyOffsetX, dpr),
|
||||||
|
"y": snap(rect.y - bodyOffsetY, dpr),
|
||||||
|
"width": Math.max(0, snap(horizontal ? rect.width + startRadius + endRadius : rect.width + farExtent, dpr)),
|
||||||
|
"height": Math.max(0, snap(horizontal ? rect.height + farExtent : rect.height + startRadius + endRadius, dpr)),
|
||||||
|
"bodyOffsetX": snap(bodyOffsetX, dpr),
|
||||||
|
"bodyOffsetY": snap(bodyOffsetY, dpr)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillBounds(rect, side, seamOverlap, dpr) {
|
||||||
|
var overlapX = isHorizontal(side) ? seamOverlap : 0;
|
||||||
|
var overlapY = isVertical(side) ? seamOverlap : 0;
|
||||||
|
return {
|
||||||
|
"x": snap(rect.x - overlapX, dpr),
|
||||||
|
"y": snap(rect.y - overlapY, dpr),
|
||||||
|
"width": Math.max(0, snap(rect.width + overlapX * 2, dpr)),
|
||||||
|
"height": Math.max(0, snap(rect.height + overlapY * 2, dpr))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function clipEnvelope(rect, side, radii, seamOverlap, dpr) {
|
||||||
|
var fill = fillBounds(rect, side, seamOverlap, dpr);
|
||||||
|
var chrome = chromeBounds(fill, side, radii.start, radii.end, radii.farExtent, dpr);
|
||||||
|
return {
|
||||||
|
"x": chrome.x,
|
||||||
|
"y": chrome.y,
|
||||||
|
"width": chrome.width,
|
||||||
|
"height": chrome.height,
|
||||||
|
"bodyX": snap(fill.x - chrome.x, dpr),
|
||||||
|
"bodyY": snap(fill.y - chrome.y, dpr),
|
||||||
|
"bodyWidth": fill.width,
|
||||||
|
"bodyHeight": fill.height
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function blurRegions(descriptor, rect, radii, dpr) {
|
||||||
|
var side = descriptor.barSide;
|
||||||
|
var regions = [bodyRect(rect, dpr)];
|
||||||
|
if (radii.start > 0)
|
||||||
|
regions.push(connectorRect(side, rect, "left", 0, radii.start, dpr));
|
||||||
|
if (radii.end > 0)
|
||||||
|
regions.push(connectorRect(side, rect, "right", 0, radii.end, dpr));
|
||||||
|
if (radii.farStart > 0) {
|
||||||
|
regions.push(farConnectorRect(side, rect, "left", radii.farStart, dpr));
|
||||||
|
regions.push(farBodyCapRect(side, rect, "left", radii.farStart, dpr));
|
||||||
|
}
|
||||||
|
if (radii.farEnd > 0) {
|
||||||
|
regions.push(farConnectorRect(side, rect, "right", radii.farEnd, dpr));
|
||||||
|
regions.push(farBodyCapRect(side, rect, "right", radii.farEnd, dpr));
|
||||||
|
}
|
||||||
|
return regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unionBounds(rects, padding, dpr) {
|
||||||
|
var minX = Infinity;
|
||||||
|
var minY = Infinity;
|
||||||
|
var maxX = -Infinity;
|
||||||
|
var maxY = -Infinity;
|
||||||
|
for (var i = 0; i < rects.length; i++) {
|
||||||
|
var rect = rects[i];
|
||||||
|
if (!rect || rect.width <= 0 || rect.height <= 0)
|
||||||
|
continue;
|
||||||
|
minX = Math.min(minX, rect.x);
|
||||||
|
minY = Math.min(minY, rect.y);
|
||||||
|
maxX = Math.max(maxX, rect.x + rect.width);
|
||||||
|
maxY = Math.max(maxY, rect.y + rect.height);
|
||||||
|
}
|
||||||
|
if (minX === Infinity)
|
||||||
|
return {"x": 0, "y": 0, "width": 0, "height": 0};
|
||||||
|
var pad = Math.max(0, _number(padding, 0));
|
||||||
|
return {
|
||||||
|
"x": snap(minX - pad, dpr),
|
||||||
|
"y": snap(minY - pad, dpr),
|
||||||
|
"width": Math.max(0, snap(maxX - minX + pad * 2, dpr)),
|
||||||
|
"height": Math.max(0, snap(maxY - minY + pad * 2, dpr))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function shadowSourceBounds(descriptor, rect, radii, padding, dpr) {
|
||||||
|
return unionBounds(blurRegions(descriptor, rect, radii, dpr), padding, dpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stableEqual(a, b, dpr) {
|
||||||
|
if (!a || !b)
|
||||||
|
return false;
|
||||||
|
var threshold = 0.5 / (dpr || 1);
|
||||||
|
return Math.abs(a.x - b.x) < threshold && Math.abs(a.y - b.y) < threshold && Math.abs(a.width - b.width) < threshold && Math.abs(a.height - b.height) < threshold;
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string claimPrefix
|
||||||
|
required property var isCurrentOwner
|
||||||
|
required property var hasOwner
|
||||||
|
required property var claimState
|
||||||
|
required property var ensureState
|
||||||
|
required property var releaseState
|
||||||
|
|
||||||
|
property var statePresent: null
|
||||||
|
property var updateAnimationState: null
|
||||||
|
property var updateBodyState: null
|
||||||
|
property var requestDockRetract: null
|
||||||
|
property var releaseDockRetract: null
|
||||||
|
|
||||||
|
property string screenName: ""
|
||||||
|
property bool enabled: false
|
||||||
|
property bool active: false
|
||||||
|
property bool presented: false
|
||||||
|
property bool dockBlocked: false
|
||||||
|
property string dockSide: ""
|
||||||
|
property bool renewTokenOnRecovery: true
|
||||||
|
|
||||||
|
property string claimId: ""
|
||||||
|
property string claimedScreenName: ""
|
||||||
|
property int _claimSerial: 0
|
||||||
|
|
||||||
|
signal recoveryRequested
|
||||||
|
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
function _nextClaimId() {
|
||||||
|
_claimSerial += 1;
|
||||||
|
return claimPrefix + ":" + (new Date()).getTime() + ":" + _claimSerial + ":" + Math.floor(Math.random() * 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _isCurrent(name) {
|
||||||
|
return !!name && !!isCurrentOwner && !!isCurrentOwner(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _hasOwner(name, ownerId) {
|
||||||
|
return !!name && !!ownerId && !!hasOwner && !!hasOwner(name, ownerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _hasState(name, ownerId) {
|
||||||
|
return !statePresent || !!statePresent(name, ownerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _shouldRecover() {
|
||||||
|
return active && enabled && _isCurrent(screenName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestRecovery() {
|
||||||
|
if (!_shouldRecover())
|
||||||
|
return false;
|
||||||
|
recoveryRequested();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkOwnershipRecovery() {
|
||||||
|
if (!_shouldRecover())
|
||||||
|
return false;
|
||||||
|
if (claimedScreenName === screenName && _hasOwner(screenName, claimId))
|
||||||
|
return false;
|
||||||
|
recoveryRequested();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkStateRecovery() {
|
||||||
|
if (!_shouldRecover())
|
||||||
|
return false;
|
||||||
|
if (claimedScreenName === screenName && _hasOwner(screenName, claimId) && _hasState(screenName, claimId))
|
||||||
|
return false;
|
||||||
|
recoveryRequested();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkRecovery() {
|
||||||
|
return checkStateRecovery();
|
||||||
|
}
|
||||||
|
|
||||||
|
function beginClaim() {
|
||||||
|
if (claimId && releaseDockRetract)
|
||||||
|
releaseDockRetract(claimId);
|
||||||
|
claimId = _nextClaimId();
|
||||||
|
claimedScreenName = "";
|
||||||
|
return claimId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _syncDockRetract() {
|
||||||
|
if (!claimId)
|
||||||
|
return;
|
||||||
|
if (dockBlocked && presented && dockSide && requestDockRetract)
|
||||||
|
requestDockRetract(claimId, screenName, dockSide);
|
||||||
|
else if (releaseDockRetract)
|
||||||
|
releaseDockRetract(claimId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function publish(state, forceClaim) {
|
||||||
|
if (!enabled || !screenName || !state) {
|
||||||
|
release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (claimedScreenName && claimedScreenName !== screenName)
|
||||||
|
release();
|
||||||
|
|
||||||
|
const current = _isCurrent(screenName);
|
||||||
|
let claiming = !!forceClaim || !claimId;
|
||||||
|
if (claiming && !current)
|
||||||
|
return false;
|
||||||
|
if (!claimId)
|
||||||
|
beginClaim();
|
||||||
|
|
||||||
|
let published = claiming ? claimState(screenName, state, claimId) : ensureState(screenName, state, claimId);
|
||||||
|
if (!published && !claiming && current) {
|
||||||
|
if (renewTokenOnRecovery) {
|
||||||
|
beginClaim();
|
||||||
|
} else if (releaseDockRetract) {
|
||||||
|
releaseDockRetract(claimId);
|
||||||
|
}
|
||||||
|
published = claimState(screenName, state, claimId);
|
||||||
|
}
|
||||||
|
if (!published)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
claimedScreenName = screenName;
|
||||||
|
_syncDockRetract();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAnim(animX, animY) {
|
||||||
|
if (!enabled || !claimId || !claimedScreenName || !updateAnimationState)
|
||||||
|
return false;
|
||||||
|
if (!_hasOwner(claimedScreenName, claimId)) {
|
||||||
|
requestRecovery();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return updateAnimationState(claimedScreenName, claimId, animX, animY);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBody(bodyX, bodyY, bodyW, bodyH) {
|
||||||
|
if (!enabled || !claimId || !claimedScreenName || !updateBodyState)
|
||||||
|
return false;
|
||||||
|
if (!_hasOwner(claimedScreenName, claimId)) {
|
||||||
|
requestRecovery();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return updateBodyState(claimedScreenName, claimId, bodyX, bodyY, bodyW, bodyH);
|
||||||
|
}
|
||||||
|
|
||||||
|
function release() {
|
||||||
|
if (!claimId) {
|
||||||
|
claimedScreenName = "";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const releasedClaimId = claimId;
|
||||||
|
const releasedScreenName = claimedScreenName;
|
||||||
|
claimId = "";
|
||||||
|
claimedScreenName = "";
|
||||||
|
|
||||||
|
if (releaseDockRetract)
|
||||||
|
releaseDockRetract(releasedClaimId);
|
||||||
|
if (releasedScreenName)
|
||||||
|
return !!releaseState(releasedScreenName, releasedClaimId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: release()
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Effects
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -19,7 +18,11 @@ Item {
|
|||||||
property real bottomRightRadius: 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 real sourceX: 0
|
||||||
|
property real sourceY: 0
|
||||||
|
property real sourceWidth: width
|
||||||
|
property real sourceHeight: height
|
||||||
|
|
||||||
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
|
||||||
@@ -28,36 +31,24 @@ Item {
|
|||||||
property real shadowOffsetY: Theme.elevationOffsetYFor(level, direction, fallbackOffset)
|
property real shadowOffsetY: Theme.elevationOffsetYFor(level, direction, fallbackOffset)
|
||||||
property color shadowColor: Theme.elevationShadowColor(level)
|
property color shadowColor: Theme.elevationShadowColor(level)
|
||||||
property real shadowOpacity: 1
|
property real shadowOpacity: 1
|
||||||
property real blurMax: Theme.elevationBlurMax
|
|
||||||
|
|
||||||
property alias sourceRect: sourceRect
|
readonly property var _ambient: Theme.elevationAmbient(level)
|
||||||
|
readonly property real _pad: shadowEnabled ? Math.ceil(Math.max(shadowBlurPx + shadowSpreadPx + Math.max(Math.abs(shadowOffsetX), Math.abs(shadowOffsetY)), _ambient.blurPx + _ambient.spreadPx) + 2) : 0
|
||||||
|
|
||||||
layer.enabled: shadowEnabled
|
ShaderEffect {
|
||||||
|
|
||||||
layer.effect: MultiEffect {
|
|
||||||
autoPaddingEnabled: true
|
|
||||||
shadowEnabled: true
|
|
||||||
blurEnabled: false
|
|
||||||
maskEnabled: false
|
|
||||||
shadowBlur: Math.max(0, Math.min(1, root.shadowBlurPx / Math.max(1, root.blurMax)))
|
|
||||||
shadowScale: 1 + (2 * root.shadowSpreadPx) / Math.max(1, Math.min(root.width, root.height))
|
|
||||||
shadowHorizontalOffset: root.shadowOffsetX
|
|
||||||
shadowVerticalOffset: root.shadowOffsetY
|
|
||||||
blurMax: root.blurMax
|
|
||||||
shadowColor: root.shadowColor
|
|
||||||
shadowOpacity: root.shadowOpacity
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: sourceRect
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: !root.useCustomSource
|
anchors.margins: -root._pad
|
||||||
topLeftRadius: root.topLeftRadius
|
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/elevation_rect.frag.qsb")
|
||||||
topRightRadius: root.topRightRadius
|
|
||||||
bottomLeftRadius: root.bottomLeftRadius
|
property real widthPx: width
|
||||||
bottomRightRadius: root.bottomRightRadius
|
property real heightPx: height
|
||||||
color: root.targetColor
|
property real borderWidth: root.borderWidth
|
||||||
border.color: root.borderColor
|
property vector4d rectPx: Qt.vector4d(root._pad + root.sourceX, root._pad + root.sourceY, root.sourceWidth, root.sourceHeight)
|
||||||
border.width: root.borderWidth
|
property vector4d cornerRadius: Qt.vector4d(root.topLeftRadius, root.topRightRadius, root.bottomRightRadius, root.bottomLeftRadius)
|
||||||
|
property vector4d fillColor: Qt.vector4d(root.targetColor.r, root.targetColor.g, root.targetColor.b, root.targetColor.a)
|
||||||
|
property vector4d borderColor: Qt.vector4d(root.borderColor.r, root.borderColor.g, root.borderColor.b, root.borderColor.a)
|
||||||
|
property vector4d shadowColor: Qt.vector4d(root.shadowColor.r, root.shadowColor.g, root.shadowColor.b, root.shadowEnabled ? root.shadowColor.a * root.shadowOpacity : 0)
|
||||||
|
property vector4d shadowParam: Qt.vector4d(Math.max(0, root.shadowBlurPx), root.shadowSpreadPx, root.shadowOffsetX, root.shadowOffsetY)
|
||||||
|
property vector4d ambientParam: Qt.vector4d(root._ambient.blurPx, root._ambient.spreadPx, root.shadowEnabled ? root._ambient.alpha * root.shadowOpacity : 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
// Manages keyboard focus policy for popouts, modals, and Hyprland focus grabs
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
function keyboardFocus(active, customFocus) {
|
||||||
|
if (PopoutManager.screenshotActive)
|
||||||
|
return WlrKeyboardFocus.None;
|
||||||
|
if (customFocus !== null && customFocus !== undefined)
|
||||||
|
return customFocus;
|
||||||
|
if (!active)
|
||||||
|
return WlrKeyboardFocus.None;
|
||||||
|
if (CompositorService.useHyprlandFocusGrab)
|
||||||
|
return WlrKeyboardFocus.OnDemand;
|
||||||
|
return WlrKeyboardFocus.Exclusive;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wantsGrab(active, customFocus) {
|
||||||
|
return CompositorService.useHyprlandFocusGrab && keyboardFocus(active, customFocus) === WlrKeyboardFocus.OnDemand;
|
||||||
|
}
|
||||||
|
|
||||||
|
property list<var> barWindows: []
|
||||||
|
|
||||||
|
function registerBarWindow(window) {
|
||||||
|
if (!window || barWindows.indexOf(window) !== -1)
|
||||||
|
return;
|
||||||
|
barWindows = barWindows.concat([window]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unregisterBarWindow(window) {
|
||||||
|
const idx = barWindows.indexOf(window);
|
||||||
|
if (idx === -1)
|
||||||
|
return;
|
||||||
|
const next = barWindows.slice();
|
||||||
|
next.splice(idx, 1);
|
||||||
|
barWindows = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -108,6 +108,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property bool clipboardEnterToPaste: false
|
property bool clipboardEnterToPaste: false
|
||||||
|
property var clipboardVisibleEntryActions: ["pin", "edit", "delete"]
|
||||||
|
|
||||||
property var launcherPluginVisibility: ({})
|
property var launcherPluginVisibility: ({})
|
||||||
|
|
||||||
@@ -1650,6 +1651,15 @@ Singleton {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function effectiveBarConfigForRender(config, usesFrameBarChrome) {
|
||||||
|
if (!config || !connectedFrameModeActive || usesFrameBarChrome)
|
||||||
|
return config;
|
||||||
|
const backup = connectedFrameBarStyleBackups[config.id];
|
||||||
|
if (!backup)
|
||||||
|
return config;
|
||||||
|
return Object.assign({}, config, backup);
|
||||||
|
}
|
||||||
|
|
||||||
// Single entry point for connected-mode settings state.
|
// Single entry point for connected-mode settings state.
|
||||||
// !active → restore backups
|
// !active → restore backups
|
||||||
function _reconcileConnectedFrameBarStyles() {
|
function _reconcileConnectedFrameBarStyles() {
|
||||||
|
|||||||
@@ -911,6 +911,16 @@ Singleton {
|
|||||||
}
|
}
|
||||||
return Qt.rgba(r, g, b, alpha);
|
return Qt.rgba(r, g, b, alpha);
|
||||||
}
|
}
|
||||||
|
function elevationAmbient(level) {
|
||||||
|
const blur = (level && level.blurPx !== undefined) ? Math.max(0, level.blurPx) : 0;
|
||||||
|
const alpha = ((level && level.alpha !== undefined) ? level.alpha : 0.3) * 0.5;
|
||||||
|
return {
|
||||||
|
blurPx: blur * 1.75,
|
||||||
|
spreadPx: 1,
|
||||||
|
alpha: alpha
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function elevationTintOpacity(level) {
|
function elevationTintOpacity(level) {
|
||||||
if (!level)
|
if (!level)
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -572,6 +572,7 @@ var SPEC = {
|
|||||||
|
|
||||||
builtInPluginSettings: { def: {} },
|
builtInPluginSettings: { def: {} },
|
||||||
clipboardEnterToPaste: { def: false },
|
clipboardEnterToPaste: { def: false },
|
||||||
|
clipboardVisibleEntryActions: { def: ["pin", "edit", "delete"] },
|
||||||
|
|
||||||
launcherPluginVisibility: { def: {} },
|
launcherPluginVisibility: { def: {} },
|
||||||
launcherPluginOrder: { def: [] },
|
launcherPluginOrder: { def: [] },
|
||||||
|
|||||||
+2
-19
@@ -64,27 +64,15 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property bool wallpaperSurfacesLoaded: true
|
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: blurredWallpaperBackgroundLoader
|
id: blurredWallpaperBackgroundLoader
|
||||||
active: root.wallpaperSurfacesLoaded && SettingsData.blurredWallpaperLayer && CompositorService.isNiri
|
active: SettingsData.blurredWallpaperLayer && CompositorService.isNiri
|
||||||
asynchronous: false
|
asynchronous: false
|
||||||
|
|
||||||
sourceComponent: BlurredWallpaperBackground {}
|
sourceComponent: BlurredWallpaperBackground {}
|
||||||
}
|
}
|
||||||
|
|
||||||
DeferredAction {
|
WallpaperBackground {}
|
||||||
id: wallpaperSurfaceReloadAction
|
|
||||||
onTriggered: root.wallpaperSurfacesLoaded = true
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: wallpaperBackgroundLoader
|
|
||||||
active: root.wallpaperSurfacesLoaded
|
|
||||||
asynchronous: false
|
|
||||||
sourceComponent: WallpaperBackground {}
|
|
||||||
}
|
|
||||||
|
|
||||||
DesktopWidgetLayer {}
|
DesktopWidgetLayer {}
|
||||||
|
|
||||||
@@ -398,11 +386,6 @@ Item {
|
|||||||
frameSurfaceReloadAction.schedule();
|
frameSurfaceReloadAction.schedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.wallpaperSurfacesLoaded) {
|
|
||||||
root.wallpaperSurfacesLoaded = false;
|
|
||||||
wallpaperSurfaceReloadAction.schedule();
|
|
||||||
}
|
|
||||||
|
|
||||||
root.dockEnabled = false;
|
root.dockEnabled = false;
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
root.dockEnabled = true;
|
root.dockEnabled = true;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Hyprland
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -11,11 +10,6 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:bluetooth-pairing"
|
layerNamespace: "dms:bluetooth-pairing"
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [root.contentWindow]
|
|
||||||
active: root.useHyprlandFocusGrab && root.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
property string deviceName: ""
|
property string deviceName: ""
|
||||||
property string deviceAddress: ""
|
property string deviceAddress: ""
|
||||||
property string requestType: ""
|
property string requestType: ""
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ Item {
|
|||||||
id: clipboardContent
|
id: clipboardContent
|
||||||
|
|
||||||
required property var modal
|
required property var modal
|
||||||
required property var clearConfirmDialog
|
|
||||||
|
|
||||||
property alias searchField: searchField
|
property alias searchField: searchField
|
||||||
property alias clipboardListView: clipboardListView
|
property alias clipboardListView: clipboardListView
|
||||||
@@ -33,14 +32,7 @@ Item {
|
|||||||
pinnedCount: modal.pinnedCount
|
pinnedCount: modal.pinnedCount
|
||||||
onKeyboardHintsToggled: modal.showKeyboardHints = !modal.showKeyboardHints
|
onKeyboardHintsToggled: modal.showKeyboardHints = !modal.showKeyboardHints
|
||||||
onTabChanged: tabName => modal.activeTab = tabName
|
onTabChanged: tabName => modal.activeTab = tabName
|
||||||
onClearAllClicked: {
|
onClearAllClicked: modal.confirmClearAll()
|
||||||
const hasPinned = modal.pinnedCount > 0;
|
|
||||||
const message = hasPinned ? I18n.tr("This will delete all unpinned entries. %1 pinned entries will be kept.").arg(modal.pinnedCount) : I18n.tr("This will permanently delete all clipboard history.");
|
|
||||||
clearConfirmDialog.show(I18n.tr("Clear History?"), message, function () {
|
|
||||||
modal.clearAll();
|
|
||||||
modal.hide();
|
|
||||||
}, function () {});
|
|
||||||
}
|
|
||||||
onCloseClicked: modal.hide()
|
onCloseClicked: modal.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,14 @@ Rectangle {
|
|||||||
readonly property string entryType: modal ? modal.getEntryType(entry) : "text"
|
readonly property string entryType: modal ? modal.getEntryType(entry) : "text"
|
||||||
readonly property string entryPreview: modal ? modal.getEntryPreview(entry) : ""
|
readonly property string entryPreview: modal ? modal.getEntryPreview(entry) : ""
|
||||||
readonly property var pinnedDuplicateEntry: !entry.pinned ? ClipboardService.getPinnedEntryByHash(entry.hash) : null
|
readonly property var pinnedDuplicateEntry: !entry.pinned ? ClipboardService.getPinnedEntryByHash(entry.hash) : null
|
||||||
readonly property bool effectivePinned: entry.pinned || pinnedDuplicateEntry !== null
|
readonly property bool hasPinnedDuplicate: pinnedDuplicateEntry !== null
|
||||||
|
readonly property bool effectivePinned: entry.pinned || hasPinnedDuplicate
|
||||||
|
readonly property var visibleEntryActions: SettingsData.clipboardVisibleEntryActions || ["pin", "edit", "delete"]
|
||||||
|
readonly property bool showPinAction: visibleEntryActions.includes("pin")
|
||||||
|
readonly property bool showEditAction: visibleEntryActions.includes("edit")
|
||||||
|
readonly property bool showDeleteAction: visibleEntryActions.includes("delete")
|
||||||
|
readonly property bool showPinnedIndicator: hasPinnedDuplicate && !showPinAction
|
||||||
|
readonly property bool showAnyAction: showPinAction || showEditAction || showDeleteAction || showPinnedIndicator
|
||||||
|
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: {
|
color: {
|
||||||
@@ -63,12 +70,28 @@ Rectangle {
|
|||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
visible: root.showAnyAction
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 40
|
||||||
|
height: 40
|
||||||
|
visible: root.showPinnedIndicator
|
||||||
|
|
||||||
|
// Status indicator only; the Pin action remains hidden.
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "push_pin"
|
||||||
|
size: Theme.iconSize - 6
|
||||||
|
color: Theme.primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
iconName: "push_pin"
|
iconName: "push_pin"
|
||||||
iconSize: Theme.iconSize - 6
|
iconSize: Theme.iconSize - 6
|
||||||
iconColor: effectivePinned ? Theme.primary : Theme.surfaceText
|
iconColor: (entry.pinned || hasPinnedDuplicate) ? Theme.primary : Theme.surfaceText
|
||||||
backgroundColor: effectivePinned ? Theme.primarySelected : "transparent"
|
backgroundColor: (entry.pinned || hasPinnedDuplicate) ? Theme.primarySelected : "transparent"
|
||||||
|
visible: root.showPinAction
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (entry.pinned) {
|
if (entry.pinned) {
|
||||||
unpinRequested(entry);
|
unpinRequested(entry);
|
||||||
@@ -86,6 +109,7 @@ Rectangle {
|
|||||||
iconName: "edit"
|
iconName: "edit"
|
||||||
iconSize: Theme.iconSize - 6
|
iconSize: Theme.iconSize - 6
|
||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
|
visible: root.showEditAction
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (entryType === "image") {
|
if (entryType === "image") {
|
||||||
@@ -99,6 +123,7 @@ Rectangle {
|
|||||||
iconName: "close"
|
iconName: "close"
|
||||||
iconSize: Theme.iconSize - 6
|
iconSize: Theme.iconSize - 6
|
||||||
iconColor: Theme.surfaceText
|
iconColor: Theme.surfaceText
|
||||||
|
visible: root.showDeleteAction
|
||||||
onClicked: deleteRequested()
|
onClicked: deleteRequested()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,8 +131,8 @@ Rectangle {
|
|||||||
Item {
|
Item {
|
||||||
anchors.left: indexBadge.right
|
anchors.left: indexBadge.right
|
||||||
anchors.leftMargin: Theme.spacingM
|
anchors.leftMargin: Theme.spacingM
|
||||||
anchors.right: actionButtons.left
|
anchors.right: root.showAnyAction ? actionButtons.left : parent.right
|
||||||
anchors.rightMargin: Theme.spacingM
|
anchors.rightMargin: root.showAnyAction ? Theme.spacingM : Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
// height: contentColumn.implicitHeight
|
// height: contentColumn.implicitHeight
|
||||||
height: ClipboardConstants.itemHeight
|
height: ClipboardConstants.itemHeight
|
||||||
@@ -168,8 +193,8 @@ Rectangle {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: actionButtons.left
|
anchors.right: root.showAnyAction ? actionButtons.left : parent.right
|
||||||
anchors.rightMargin: Theme.spacingS
|
anchors.rightMargin: root.showAnyAction ? Theme.spacingS : 0
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|||||||
@@ -82,6 +82,15 @@ FocusScope {
|
|||||||
ClipboardService.clearAll();
|
ClipboardService.clearAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function confirmClearAll() {
|
||||||
|
const hasPinned = pinnedCount > 0;
|
||||||
|
const message = hasPinned ? I18n.tr("This will delete all unpinned entries. %1 pinned entries will be kept.").arg(pinnedCount) : I18n.tr("This will permanently delete all clipboard history.");
|
||||||
|
clearConfirmDialog.show(I18n.tr("Clear History?"), message, function () {
|
||||||
|
clearAll();
|
||||||
|
hide();
|
||||||
|
}, function () {});
|
||||||
|
}
|
||||||
|
|
||||||
function getEntryPreview(entry) {
|
function getEntryPreview(entry) {
|
||||||
return ClipboardService.getEntryPreview(entry);
|
return ClipboardService.getEntryPreview(entry);
|
||||||
}
|
}
|
||||||
@@ -135,7 +144,6 @@ FocusScope {
|
|||||||
id: historyContent
|
id: historyContent
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
modal: root
|
modal: root
|
||||||
clearConfirmDialog: root.clearConfirmDialog
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Hyprland
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Clipboard
|
import qs.Modals.Clipboard
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
@@ -12,11 +11,6 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:clipboard"
|
layerNamespace: "dms:clipboard"
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [clipboardHistoryModal.contentWindow]
|
|
||||||
active: clipboardHistoryModal.useHyprlandFocusGrab && clipboardHistoryModal.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
if (shouldBeVisible) {
|
if (shouldBeVisible) {
|
||||||
hide();
|
hide();
|
||||||
@@ -64,6 +58,7 @@ DankModal {
|
|||||||
readonly property bool clipboardAvailable: ClipboardService.clipboardAvailable
|
readonly property bool clipboardAvailable: ClipboardService.clipboardAvailable
|
||||||
|
|
||||||
visible: false
|
visible: false
|
||||||
|
keepContentLoaded: true
|
||||||
modalWidth: ClipboardConstants.modalWidth
|
modalWidth: ClipboardConstants.modalWidth
|
||||||
modalHeight: ClipboardConstants.modalHeight
|
modalHeight: ClipboardConstants.modalHeight
|
||||||
backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
@@ -82,22 +77,35 @@ DankModal {
|
|||||||
id: clearConfirmDialog
|
id: clearConfirmDialog
|
||||||
confirmButtonText: I18n.tr("Clear All")
|
confirmButtonText: I18n.tr("Clear All")
|
||||||
confirmButtonColor: Theme.primary
|
confirmButtonColor: Theme.primary
|
||||||
onVisibleChanged: {
|
onShouldBeVisibleChanged: {
|
||||||
if (visible) {
|
if (shouldBeVisible) {
|
||||||
clipboardHistoryModal.shouldHaveFocus = false;
|
clipboardHistoryModal.shouldHaveFocus = false;
|
||||||
|
selectedButton = 0;
|
||||||
|
keyboardNavigation = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Qt.callLater(function () {
|
Qt.callLater(function () {
|
||||||
if (!clipboardHistoryModal.shouldBeVisible) {
|
if (!clipboardHistoryModal.shouldBeVisible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
clipboardHistoryModal.shouldHaveFocus = true;
|
clipboardHistoryModal.shouldHaveFocus = Qt.binding(() => clipboardHistoryModal.shouldBeVisible);
|
||||||
clipboardHistoryModal.modalFocusScope.forceActiveFocus();
|
clipboardHistoryModal.modalFocusScope.forceActiveFocus();
|
||||||
if (clipboardHistoryModal.contentLoader.item?.searchField) {
|
if (clipboardHistoryModal.contentLoader.item?.searchField) {
|
||||||
clipboardHistoryModal.contentLoader.item.searchField.forceActiveFocus();
|
clipboardHistoryModal.contentLoader.item.searchField.forceActiveFocus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Connections {
|
||||||
|
target: clearConfirmDialog.modalFocusScope.Keys
|
||||||
|
function onPressed(event) {
|
||||||
|
if (!clearConfirmDialog.shouldBeVisible || event.key !== Qt.Key_Backtab) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clearConfirmDialog.selectedButton = clearConfirmDialog.selectedButton === -1 ? 1 : (clearConfirmDialog.selectedButton - 1 + 2) % 2;
|
||||||
|
clearConfirmDialog.keyboardNavigation = true;
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import Quickshell.Wayland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Clipboard
|
import qs.Modals.Clipboard
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
@@ -95,6 +96,35 @@ DankPopout {
|
|||||||
id: clearConfirmDialog
|
id: clearConfirmDialog
|
||||||
confirmButtonText: I18n.tr("Clear All")
|
confirmButtonText: I18n.tr("Clear All")
|
||||||
confirmButtonColor: Theme.primary
|
confirmButtonColor: Theme.primary
|
||||||
|
onShouldBeVisibleChanged: {
|
||||||
|
if (shouldBeVisible) {
|
||||||
|
root.customKeyboardFocus = WlrKeyboardFocus.None;
|
||||||
|
selectedButton = 0;
|
||||||
|
keyboardNavigation = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
root.customKeyboardFocus = null;
|
||||||
|
Qt.callLater(function () {
|
||||||
|
if (!root.shouldBeVisible || !root.contentLoader.item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
root.contentLoader.item.forceActiveFocus();
|
||||||
|
if (root.contentLoader.item.searchField) {
|
||||||
|
root.contentLoader.item.searchField.forceActiveFocus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: clearConfirmDialog.modalFocusScope.Keys
|
||||||
|
function onPressed(event) {
|
||||||
|
if (!clearConfirmDialog.shouldBeVisible || event.key !== Qt.Key_Backtab) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clearConfirmDialog.selectedButton = clearConfirmDialog.selectedButton === -1 ? 1 : (clearConfirmDialog.selectedButton - 1 + 2) % 2;
|
||||||
|
clearConfirmDialog.keyboardNavigation = true;
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
|
|||||||
@@ -125,8 +125,6 @@ QtObject {
|
|||||||
if (!ClipboardService.keyboardNavigationActive) {
|
if (!ClipboardService.keyboardNavigationActive) {
|
||||||
ClipboardService.keyboardNavigationActive = true;
|
ClipboardService.keyboardNavigationActive = true;
|
||||||
ClipboardService.selectedIndex = 0;
|
ClipboardService.selectedIndex = 0;
|
||||||
} else if (ClipboardService.selectedIndex === 0) {
|
|
||||||
ClipboardService.keyboardNavigationActive = false;
|
|
||||||
} else {
|
} else {
|
||||||
selectPrevious();
|
selectPrevious();
|
||||||
}
|
}
|
||||||
@@ -155,8 +153,6 @@ QtObject {
|
|||||||
if (!ClipboardService.keyboardNavigationActive) {
|
if (!ClipboardService.keyboardNavigationActive) {
|
||||||
ClipboardService.keyboardNavigationActive = true;
|
ClipboardService.keyboardNavigationActive = true;
|
||||||
ClipboardService.selectedIndex = 0;
|
ClipboardService.selectedIndex = 0;
|
||||||
} else if (ClipboardService.selectedIndex === 0) {
|
|
||||||
ClipboardService.keyboardNavigationActive = false;
|
|
||||||
} else {
|
} else {
|
||||||
selectPrevious();
|
selectPrevious();
|
||||||
}
|
}
|
||||||
@@ -184,8 +180,7 @@ QtObject {
|
|||||||
if (event.modifiers & Qt.ShiftModifier) {
|
if (event.modifiers & Qt.ShiftModifier) {
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case Qt.Key_Delete:
|
case Qt.Key_Delete:
|
||||||
modal.clearAll();
|
modal.confirmClearAll();
|
||||||
modal.hide();
|
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
return;
|
return;
|
||||||
case Qt.Key_Return:
|
case Qt.Key_Return:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
@@ -52,8 +53,13 @@ Item {
|
|||||||
focus: true
|
focus: true
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hyprland OnDemand grab delivers keyboard focus to the modal content surface.
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
windows: root.contentWindow ? [root.contentWindow] : []
|
||||||
|
active: KeyboardFocus.wantsGrab(root.shouldHaveFocus, root.customKeyboardFocus)
|
||||||
|
}
|
||||||
readonly property var contentWindow: impl.item ? impl.item.contentWindow : null
|
readonly property var contentWindow: impl.item ? impl.item.contentWindow : null
|
||||||
readonly property var clickCatcher: impl.item ? impl.item.clickCatcher : null
|
|
||||||
readonly property var effectiveScreen: impl.item ? impl.item.effectiveScreen : null
|
readonly property var effectiveScreen: impl.item ? impl.item.effectiveScreen : null
|
||||||
readonly property real screenWidth: impl.item ? impl.item.screenWidth : 1920
|
readonly property real screenWidth: impl.item ? impl.item.screenWidth : 1920
|
||||||
readonly property real screenHeight: impl.item ? impl.item.screenHeight : 1080
|
readonly property real screenHeight: impl.item ? impl.item.screenHeight : 1080
|
||||||
@@ -96,8 +102,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defer Loader source-component swap until impl is fully closed; avoids
|
|
||||||
// tearing down a modal mid-animation when frame mode is toggled.
|
|
||||||
function _maybeResolveBackend() {
|
function _maybeResolveBackend() {
|
||||||
if (_resolvedBackend === _desiredBackend)
|
if (_resolvedBackend === _desiredBackend)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ Item {
|
|||||||
property bool closeOnBackgroundClick: true
|
property bool closeOnBackgroundClick: true
|
||||||
property string animationType: "scale"
|
property string animationType: "scale"
|
||||||
|
|
||||||
// Opposite side from the launcher by default; subclasses may override
|
|
||||||
property string preferredConnectedBarSide: SettingsData.frameModalEmergeSide
|
property string preferredConnectedBarSide: SettingsData.frameModalEmergeSide
|
||||||
|
|
||||||
readonly property bool frameConnectedMode: SettingsData.frameEnabled && Theme.isConnectedEffect && !!effectiveScreen && SettingsData.isScreenInPreferences(effectiveScreen, SettingsData.frameScreenPreferences)
|
readonly property bool frameConnectedMode: SettingsData.frameEnabled && Theme.isConnectedEffect && !!effectiveScreen && SettingsData.isScreenInPreferences(effectiveScreen, SettingsData.frameScreenPreferences)
|
||||||
@@ -87,16 +86,13 @@ Item {
|
|||||||
property real frozenMotionOffsetX: 0
|
property real frozenMotionOffsetX: 0
|
||||||
property real frozenMotionOffsetY: 0
|
property real frozenMotionOffsetY: 0
|
||||||
readonly property alias contentWindow: contentWindow
|
readonly property alias contentWindow: contentWindow
|
||||||
readonly property alias clickCatcher: clickCatcher
|
|
||||||
readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
|
readonly property bool useHyprlandFocusGrab: CompositorService.useHyprlandFocusGrab
|
||||||
readonly property bool useBackground: false
|
readonly property bool useBackground: false
|
||||||
readonly property bool useSingleWindow: CompositorService.isHyprland
|
|
||||||
|
|
||||||
signal opened
|
signal opened
|
||||||
signal dialogClosed
|
signal dialogClosed
|
||||||
signal backgroundClicked
|
signal backgroundClicked
|
||||||
|
|
||||||
// Coalesce per-channel dirty bits; one ConnectedModeState write per tick.
|
|
||||||
Timer {
|
Timer {
|
||||||
id: _syncTimer
|
id: _syncTimer
|
||||||
interval: 0
|
interval: 0
|
||||||
@@ -115,6 +111,7 @@ Item {
|
|||||||
id: modalChrome
|
id: modalChrome
|
||||||
modalHandle: root.modalHandle
|
modalHandle: root.modalHandle
|
||||||
claimPrefix: root.layerNamespace + ":modal"
|
claimPrefix: root.layerNamespace + ":modal"
|
||||||
|
surfaceKind: "modal"
|
||||||
screenName: root._currentScreenName()
|
screenName: root._currentScreenName()
|
||||||
enabled: root.frameOwnsConnectedChrome
|
enabled: root.frameOwnsConnectedChrome
|
||||||
active: root.shouldBeVisible
|
active: root.shouldBeVisible
|
||||||
@@ -125,17 +122,38 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _publishModalChromeState() {
|
function _publishModalChromeState() {
|
||||||
|
const presented = shouldBeVisible || contentWindow.visible;
|
||||||
|
const phase = !presented ? "hidden" : (!shouldBeVisible && contentWindow.visible ? "closing" : (!contentWindow.visible ? "opening" : "open"));
|
||||||
|
const bodyRect = {
|
||||||
|
"x": alignedX,
|
||||||
|
"y": alignedY,
|
||||||
|
"width": alignedWidth,
|
||||||
|
"height": alignedHeight
|
||||||
|
};
|
||||||
|
const animationOffset = {
|
||||||
|
"x": modalContainer ? modalContainer.animX : 0,
|
||||||
|
"y": modalContainer ? modalContainer.animY : 0
|
||||||
|
};
|
||||||
const state = {
|
const state = {
|
||||||
"visible": shouldBeVisible || contentWindow.visible,
|
"kind": "modal",
|
||||||
|
"screenName": root._currentScreenName(),
|
||||||
|
"phase": phase,
|
||||||
|
"visible": presented,
|
||||||
|
"presented": presented,
|
||||||
"barSide": resolvedConnectedBarSide,
|
"barSide": resolvedConnectedBarSide,
|
||||||
|
"bodyRect": bodyRect,
|
||||||
|
"animationOffset": animationOffset,
|
||||||
|
"scale": 1,
|
||||||
|
"opacity": Theme.connectedSurfaceColor.a,
|
||||||
"bodyX": alignedX,
|
"bodyX": alignedX,
|
||||||
"bodyY": alignedY,
|
"bodyY": alignedY,
|
||||||
"bodyW": alignedWidth,
|
"bodyW": alignedWidth,
|
||||||
"bodyH": alignedHeight,
|
"bodyH": alignedHeight,
|
||||||
"animX": modalContainer ? modalContainer.animX : 0,
|
"animX": animationOffset.x,
|
||||||
"animY": modalContainer ? modalContainer.animY : 0,
|
"animY": animationOffset.y,
|
||||||
"omitStartConnector": false,
|
"omitStartConnector": false,
|
||||||
"omitEndConnector": false
|
"omitEndConnector": false,
|
||||||
|
"dockRetractSide": root._dockBlocksEmergence ? resolvedConnectedBarSide : ""
|
||||||
};
|
};
|
||||||
return modalChrome.publish(state);
|
return modalChrome.publish(state);
|
||||||
}
|
}
|
||||||
@@ -222,22 +240,16 @@ Item {
|
|||||||
const focusedScreen = CompositorService.getFocusedScreen();
|
const focusedScreen = CompositorService.getFocusedScreen();
|
||||||
if (focusedScreen) {
|
if (focusedScreen) {
|
||||||
contentWindow.screen = focusedScreen;
|
contentWindow.screen = focusedScreen;
|
||||||
if (!useSingleWindow)
|
|
||||||
clickCatcher.screen = focusedScreen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ModalManager.openModal(modalHandle);
|
ModalManager.openModal(modalHandle);
|
||||||
if (Theme.isDirectionalEffect || root.useBackground) {
|
if (Theme.isDirectionalEffect || root.useBackground) {
|
||||||
if (!useSingleWindow)
|
|
||||||
clickCatcher.visible = true;
|
|
||||||
contentWindow.visible = true;
|
contentWindow.visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
animationsEnabled = true;
|
animationsEnabled = true;
|
||||||
shouldBeVisible = true;
|
shouldBeVisible = true;
|
||||||
if (!useSingleWindow && !clickCatcher.visible)
|
|
||||||
clickCatcher.visible = true;
|
|
||||||
if (!contentWindow.visible)
|
if (!contentWindow.visible)
|
||||||
contentWindow.visible = true;
|
contentWindow.visible = true;
|
||||||
opened();
|
opened();
|
||||||
@@ -264,8 +276,6 @@ Item {
|
|||||||
ModalManager.closeModal(modalHandle);
|
ModalManager.closeModal(modalHandle);
|
||||||
closeTimer.stop();
|
closeTimer.stop();
|
||||||
contentWindow.visible = false;
|
contentWindow.visible = false;
|
||||||
if (!useSingleWindow)
|
|
||||||
clickCatcher.visible = false;
|
|
||||||
dialogClosed();
|
dialogClosed();
|
||||||
Qt.callLater(() => animationsEnabled = true);
|
Qt.callLater(() => animationsEnabled = true);
|
||||||
}
|
}
|
||||||
@@ -304,8 +314,6 @@ Item {
|
|||||||
const newScreen = CompositorService.getFocusedScreen();
|
const newScreen = CompositorService.getFocusedScreen();
|
||||||
if (newScreen) {
|
if (newScreen) {
|
||||||
contentWindow.screen = newScreen;
|
contentWindow.screen = newScreen;
|
||||||
if (!useSingleWindow)
|
|
||||||
clickCatcher.screen = newScreen;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,29 +325,12 @@ Item {
|
|||||||
if (shouldBeVisible)
|
if (shouldBeVisible)
|
||||||
return;
|
return;
|
||||||
contentWindow.visible = false;
|
contentWindow.visible = false;
|
||||||
if (!useSingleWindow)
|
|
||||||
clickCatcher.visible = false;
|
|
||||||
dialogClosed();
|
dialogClosed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// shadowRenderPadding is zeroed when frame owns the chrome
|
|
||||||
// Wayland then clips any content translating past
|
|
||||||
readonly property var shadowLevel: Theme.elevationLevel3
|
readonly property var shadowLevel: Theme.elevationLevel3
|
||||||
readonly property real shadowFallbackOffset: 6
|
readonly property real shadowFallbackOffset: 6
|
||||||
readonly property real shadowRenderPadding: (!frameOwnsConnectedChrome && root.enableShadow && Theme.elevationEnabled && SettingsData.modalElevationEnabled) ? Theme.elevationRenderPadding(shadowLevel, Theme.elevationLightDirection, shadowFallbackOffset, 8, 16) : 0
|
|
||||||
readonly property real shadowMotionPadding: {
|
|
||||||
if (frameOwnsConnectedChrome)
|
|
||||||
return 0;
|
|
||||||
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 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)
|
||||||
|
|
||||||
@@ -349,7 +340,6 @@ Item {
|
|||||||
return SettingsData.frameEdgeInsetForSide(effectiveScreen, side);
|
return SettingsData.frameEdgeInsetForSide(effectiveScreen, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
// frameEdgeInsetForSide is the full inset; do not add frameBarSize
|
|
||||||
readonly property real _connectedAlignedX: {
|
readonly property real _connectedAlignedX: {
|
||||||
switch (resolvedConnectedBarSide) {
|
switch (resolvedConnectedBarSide) {
|
||||||
case "top":
|
case "top":
|
||||||
@@ -412,57 +402,6 @@ Item {
|
|||||||
}
|
}
|
||||||
})(), dpr)
|
})(), dpr)
|
||||||
|
|
||||||
PanelWindow {
|
|
||||||
id: clickCatcher
|
|
||||||
visible: false
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
WlrLayershell.namespace: root.layerNamespace + ":clickcatcher"
|
|
||||||
WlrLayershell.layer: WlrLayershell.Top
|
|
||||||
WlrLayershell.exclusiveZone: -1
|
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
left: true
|
|
||||||
right: true
|
|
||||||
bottom: true
|
|
||||||
}
|
|
||||||
|
|
||||||
mask: Region {
|
|
||||||
item: Rectangle {
|
|
||||||
x: root.alignedX
|
|
||||||
y: root.alignedY
|
|
||||||
width: root.alignedWidth
|
|
||||||
height: root.alignedHeight
|
|
||||||
}
|
|
||||||
intersection: Intersection.Xor
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: !root.useSingleWindow && root.closeOnBackgroundClick && root.shouldBeVisible
|
|
||||||
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 {
|
||||||
id: contentWindow
|
id: contentWindow
|
||||||
visible: false
|
visible: false
|
||||||
@@ -472,8 +411,8 @@ Item {
|
|||||||
targetWindow: contentWindow
|
targetWindow: contentWindow
|
||||||
blurEnabled: root.effectiveBlurEnabled && !root.frameOwnsConnectedChrome
|
blurEnabled: root.effectiveBlurEnabled && !root.frameOwnsConnectedChrome
|
||||||
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: connectedReveal.x + 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: connectedReveal.y + modalContainer.y + modalContainer.height * (1 - s) * 0.5 + Theme.snap(modalContainer.animY, root.dpr)
|
||||||
blurWidth: (root.shouldBeVisible && !root.frameOwnsConnectedChrome) ? modalContainer.width * s : 0
|
blurWidth: (root.shouldBeVisible && !root.frameOwnsConnectedChrome) ? modalContainer.width * s : 0
|
||||||
blurHeight: (root.shouldBeVisible && !root.frameOwnsConnectedChrome) ? modalContainer.height * s : 0
|
blurHeight: (root.shouldBeVisible && !root.frameOwnsConnectedChrome) ? modalContainer.height * s : 0
|
||||||
blurRadius: root.effectiveCornerRadius
|
blurRadius: root.effectiveCornerRadius
|
||||||
@@ -487,36 +426,15 @@ Item {
|
|||||||
"error": true
|
"error": true
|
||||||
})
|
})
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: {
|
WlrLayershell.keyboardFocus: KeyboardFocus.keyboardFocus(shouldHaveFocus, customKeyboardFocus)
|
||||||
if (customKeyboardFocus !== null)
|
|
||||||
return customKeyboardFocus;
|
|
||||||
if (!shouldHaveFocus)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (root.useHyprlandFocusGrab)
|
|
||||||
return WlrKeyboardFocus.OnDemand;
|
|
||||||
return WlrKeyboardFocus.Exclusive;
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
left: true
|
left: true
|
||||||
top: true
|
top: true
|
||||||
right: root.useSingleWindow
|
right: true
|
||||||
bottom: root.useSingleWindow
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|
||||||
left: actualMarginLeft
|
|
||||||
top: actualMarginTop
|
|
||||||
right: 0
|
|
||||||
bottom: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: root.useSingleWindow ? 0 : root.alignedWidth + (shadowBuffer * 2)
|
|
||||||
implicitHeight: root.useSingleWindow ? 0 : root.alignedHeight + (shadowBuffer * 2)
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible)
|
if (visible)
|
||||||
return;
|
return;
|
||||||
@@ -528,7 +446,7 @@ Item {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: root.useSingleWindow && root.closeOnBackgroundClick && root.shouldBeVisible
|
enabled: root.closeOnBackgroundClick && root.shouldBeVisible
|
||||||
z: -2
|
z: -2
|
||||||
onClicked: root.backgroundClicked()
|
onClicked: root.backgroundClicked()
|
||||||
}
|
}
|
||||||
@@ -537,7 +455,7 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
z: -1
|
z: -1
|
||||||
color: "black"
|
color: "black"
|
||||||
opacity: (root.useSingleWindow && root.useBackground) ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
|
opacity: root.useBackground ? (root.shouldBeVisible ? root.backgroundOpacity : 0) : 0
|
||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
@@ -550,17 +468,26 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: connectedReveal
|
||||||
|
// Clip to final footprint while frame-owned chrome grows from the bar edge.
|
||||||
|
x: root.alignedX
|
||||||
|
y: root.alignedY
|
||||||
|
width: root.alignedWidth
|
||||||
|
height: root.alignedHeight
|
||||||
|
clip: root.frameOwnsConnectedChrome
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: modalContainer
|
id: modalContainer
|
||||||
x: (root.useSingleWindow ? root.alignedX : (root.alignedX - contentWindow.actualMarginLeft)) + Theme.snap(animX, root.dpr)
|
x: Theme.snap(animX, root.dpr)
|
||||||
y: (root.useSingleWindow ? root.alignedY : (root.alignedY - contentWindow.actualMarginTop)) + Theme.snap(animY, root.dpr)
|
y: Theme.snap(animY, root.dpr)
|
||||||
|
|
||||||
width: root.alignedWidth
|
width: root.alignedWidth
|
||||||
height: root.alignedHeight
|
height: root.alignedHeight
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: root.useSingleWindow && root.shouldBeVisible
|
enabled: root.shouldBeVisible
|
||||||
hoverEnabled: false
|
hoverEnabled: false
|
||||||
acceptedButtons: Qt.AllButtons
|
acceptedButtons: Qt.AllButtons
|
||||||
onPressed: mouse.accepted = true
|
onPressed: mouse.accepted = true
|
||||||
@@ -579,7 +506,6 @@ Item {
|
|||||||
readonly property real customDistRight: root.screenWidth - customAnchorX
|
readonly property real customDistRight: root.screenWidth - customAnchorX
|
||||||
readonly property real customDistTop: customAnchorY
|
readonly property real customDistTop: customAnchorY
|
||||||
readonly property real customDistBottom: root.screenHeight - customAnchorY
|
readonly property real customDistBottom: root.screenHeight - customAnchorY
|
||||||
// Connected emergence: travel from the resolved bar edge, matching DankPopout cadence.
|
|
||||||
readonly property real connectedEmergenceTravelX: Math.max(root.animationOffset, root.alignedWidth + Theme.spacingL)
|
readonly property real connectedEmergenceTravelX: Math.max(root.animationOffset, root.alignedWidth + Theme.spacingL)
|
||||||
readonly property real connectedEmergenceTravelY: Math.max(root.animationOffset, root.alignedHeight + Theme.spacingL)
|
readonly property real connectedEmergenceTravelY: Math.max(root.animationOffset, root.alignedHeight + Theme.spacingL)
|
||||||
readonly property real offsetX: {
|
readonly property real offsetX: {
|
||||||
@@ -647,7 +573,6 @@ Item {
|
|||||||
return directionalTravel;
|
return directionalTravel;
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
// Default to sliding down from top when centered
|
|
||||||
return -Math.max(directionalTravel, root.screenHeight * 0.24);
|
return -Math.max(directionalTravel, root.screenHeight * 0.24);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -670,7 +595,6 @@ Item {
|
|||||||
|
|
||||||
readonly property real computedScaleCollapsed: root.animationScaleCollapsed
|
readonly property real computedScaleCollapsed: root.animationScaleCollapsed
|
||||||
|
|
||||||
// openProgress: 0 = closed (at frozenMotionOffset, scaleCollapsed), 1 = open (at 0, scale 1).
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: morph
|
id: morph
|
||||||
property real openProgress: root.shouldBeVisible ? 1 : 0
|
property real openProgress: root.shouldBeVisible ? 1 : 0
|
||||||
@@ -801,6 +725,7 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
id: focusScope
|
id: focusScope
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ Item {
|
|||||||
id: clickCatcher
|
id: clickCatcher
|
||||||
visible: false
|
visible: false
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
updatesEnabled: false
|
||||||
|
|
||||||
WlrLayershell.namespace: root.layerNamespace + ":clickcatcher"
|
WlrLayershell.namespace: root.layerNamespace + ":clickcatcher"
|
||||||
WlrLayershell.layer: WlrLayershell.Top
|
WlrLayershell.layer: WlrLayershell.Top
|
||||||
@@ -259,15 +260,7 @@ Item {
|
|||||||
"error": true
|
"error": true
|
||||||
})
|
})
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: {
|
WlrLayershell.keyboardFocus: KeyboardFocus.keyboardFocus(shouldHaveFocus, customKeyboardFocus)
|
||||||
if (customKeyboardFocus !== null)
|
|
||||||
return customKeyboardFocus;
|
|
||||||
if (!shouldHaveFocus)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (root.useHyprlandFocusGrab)
|
|
||||||
return WlrKeyboardFocus.OnDemand;
|
|
||||||
return WlrKeyboardFocus.Exclusive;
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
left: true
|
left: true
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
@@ -13,11 +12,6 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:color-picker"
|
layerNamespace: "dms:color-picker"
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [root.contentWindow]
|
|
||||||
active: root.useHyprlandFocusGrab && root.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
property string pickerTitle: I18n.tr("Choose Color")
|
property string pickerTitle: I18n.tr("Choose Color")
|
||||||
property color selectedColor: SessionData.recentColors.length > 0 ? SessionData.recentColors[0] : Theme.primary
|
property color selectedColor: SessionData.recentColors.length > 0 ? SessionData.recentColors[0] : Theme.primary
|
||||||
property var onColorSelectedCallback: null
|
property var onColorSelectedCallback: null
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ Item {
|
|||||||
property string _pendingMode: ""
|
property string _pendingMode: ""
|
||||||
readonly property bool unloadContentOnClose: SettingsData.dankLauncherV2UnloadOnClose
|
readonly property bool unloadContentOnClose: SettingsData.dankLauncherV2UnloadOnClose
|
||||||
|
|
||||||
// Animation state — matches DankPopout/DankModal pattern
|
|
||||||
property bool animationsEnabled: true
|
property bool animationsEnabled: true
|
||||||
property bool _motionActive: false
|
property bool _motionActive: false
|
||||||
property real _frozenMotionX: 0
|
property real _frozenMotionX: 0
|
||||||
@@ -108,8 +107,6 @@ Item {
|
|||||||
return SettingsData.frameEdgeInsetForSide(effectiveScreen, side);
|
return SettingsData.frameEdgeInsetForSide(effectiveScreen, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
// frameEdgeInsetForSide is the full inset; do not add frameBarSize.
|
|
||||||
// Positions the modal flush to the emerge side, centered on the cross axis.
|
|
||||||
readonly property var _connectedModalPos: {
|
readonly property var _connectedModalPos: {
|
||||||
const fallback = {
|
const fallback = {
|
||||||
"x": (screenWidth - modalWidth) / 2,
|
"x": (screenWidth - modalWidth) / 2,
|
||||||
@@ -175,8 +172,6 @@ Item {
|
|||||||
readonly property int effectiveBorderWidth: connectedSurfaceOverride ? 0 : borderWidth
|
readonly property int effectiveBorderWidth: connectedSurfaceOverride ? 0 : borderWidth
|
||||||
readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled
|
readonly property bool effectiveBlurEnabled: Theme.connectedSurfaceBlurEnabled
|
||||||
|
|
||||||
// Shadow padding for the content window (render padding only, no motion padding).
|
|
||||||
// Zeroed when frame owns the chrome and Wayland clips past the bar edge
|
|
||||||
readonly property var shadowLevel: Theme.elevationLevel3
|
readonly property var shadowLevel: Theme.elevationLevel3
|
||||||
readonly property real shadowFallbackOffset: 6
|
readonly property real shadowFallbackOffset: 6
|
||||||
readonly property real shadowRenderPadding: (!frameOwnsConnectedChrome && Theme.elevationEnabled && SettingsData.modalElevationEnabled) ? Theme.elevationRenderPadding(shadowLevel, Theme.elevationLightDirection, shadowFallbackOffset, 8, 16) : 0
|
readonly property real shadowRenderPadding: (!frameOwnsConnectedChrome && Theme.elevationEnabled && SettingsData.modalElevationEnabled) ? Theme.elevationRenderPadding(shadowLevel, Theme.elevationLightDirection, shadowFallbackOffset, 8, 16) : 0
|
||||||
@@ -203,29 +198,11 @@ Item {
|
|||||||
}
|
}
|
||||||
readonly property real contentSurfaceHeight: launcherArcExtenderActive ? _connectedChromeHeight : alignedHeight
|
readonly property real contentSurfaceHeight: launcherArcExtenderActive ? _connectedChromeHeight : alignedHeight
|
||||||
|
|
||||||
// For directional/depth: window extends from screen top (content slides within)
|
readonly property real _ccX: _connectedChromeX
|
||||||
// For standard: small window tightly around the modal + shadow padding
|
readonly property real _ccY: _connectedChromeY
|
||||||
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: launcherArcExtenderActive ? _connectedChromeY : (_needsExtendedWindow ? 0 : Theme.snap(alignedY - shadowPad, dpr))
|
|
||||||
readonly property real _cwWidth: alignedWidth + shadowPad * 2
|
|
||||||
readonly property real _cwHeight: {
|
|
||||||
if (launcherArcExtenderActive)
|
|
||||||
return _connectedChromeHeight;
|
|
||||||
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: launcherArcExtenderActive ? 0 : (_needsExtendedWindow ? alignedY : shadowPad)
|
|
||||||
|
|
||||||
signal dialogClosed
|
signal dialogClosed
|
||||||
|
|
||||||
// Coalesce per-channel dirty bits; one ConnectedModeState write per tick.
|
|
||||||
Timer {
|
Timer {
|
||||||
id: _syncTimer
|
id: _syncTimer
|
||||||
interval: 0
|
interval: 0
|
||||||
@@ -242,6 +219,7 @@ Item {
|
|||||||
id: modalChrome
|
id: modalChrome
|
||||||
modalHandle: root.modalHandle
|
modalHandle: root.modalHandle
|
||||||
claimPrefix: "dms:launcher-v2"
|
claimPrefix: "dms:launcher-v2"
|
||||||
|
surfaceKind: "launcher"
|
||||||
screenName: root._currentScreenName()
|
screenName: root._currentScreenName()
|
||||||
enabled: root.frameOwnsConnectedChrome
|
enabled: root.frameOwnsConnectedChrome
|
||||||
active: root.spotlightOpen
|
active: root.spotlightOpen
|
||||||
@@ -252,17 +230,38 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _publishModalChromeState() {
|
function _publishModalChromeState() {
|
||||||
|
const presented = spotlightOpen || contentWindow.visible;
|
||||||
|
const phase = !presented ? "hidden" : (isClosing ? "closing" : (!contentWindow.visible ? "opening" : "open"));
|
||||||
|
const bodyRect = {
|
||||||
|
"x": _connectedChromeX,
|
||||||
|
"y": _connectedChromeY,
|
||||||
|
"width": _connectedChromeWidth,
|
||||||
|
"height": _connectedChromeHeight
|
||||||
|
};
|
||||||
|
const animationOffset = {
|
||||||
|
"x": contentContainer ? contentContainer.animX : 0,
|
||||||
|
"y": contentContainer ? contentContainer.animY : 0
|
||||||
|
};
|
||||||
const state = {
|
const state = {
|
||||||
"visible": spotlightOpen || contentWindow.visible,
|
"kind": "launcher",
|
||||||
|
"screenName": root._currentScreenName(),
|
||||||
|
"phase": phase,
|
||||||
|
"visible": presented,
|
||||||
|
"presented": presented,
|
||||||
"barSide": resolvedConnectedBarSide,
|
"barSide": resolvedConnectedBarSide,
|
||||||
|
"bodyRect": bodyRect,
|
||||||
|
"animationOffset": animationOffset,
|
||||||
|
"scale": 1,
|
||||||
|
"opacity": Theme.connectedSurfaceColor.a,
|
||||||
"bodyX": _connectedChromeX,
|
"bodyX": _connectedChromeX,
|
||||||
"bodyY": _connectedChromeY,
|
"bodyY": _connectedChromeY,
|
||||||
"bodyW": _connectedChromeWidth,
|
"bodyW": _connectedChromeWidth,
|
||||||
"bodyH": _connectedChromeHeight,
|
"bodyH": _connectedChromeHeight,
|
||||||
"animX": contentContainer ? contentContainer.animX : 0,
|
"animX": animationOffset.x,
|
||||||
"animY": contentContainer ? contentContainer.animY : 0,
|
"animY": animationOffset.y,
|
||||||
"omitStartConnector": false,
|
"omitStartConnector": false,
|
||||||
"omitEndConnector": false
|
"omitEndConnector": false,
|
||||||
|
"dockRetractSide": root._dockBlocksEmergence ? resolvedConnectedBarSide : ""
|
||||||
};
|
};
|
||||||
return modalChrome.publish(state);
|
return modalChrome.publish(state);
|
||||||
}
|
}
|
||||||
@@ -359,8 +358,6 @@ Item {
|
|||||||
return;
|
return;
|
||||||
contentVisible = true;
|
contentVisible = true;
|
||||||
spotlightContent.closeTransientUi?.();
|
spotlightContent.closeTransientUi?.();
|
||||||
// NOTE: forceActiveFocus() is deliberately NOT called here.
|
|
||||||
// It is deferred to after animation starts to avoid compositor IPC stalls.
|
|
||||||
|
|
||||||
if (spotlightContent.searchField) {
|
if (spotlightContent.searchField) {
|
||||||
spotlightContent.searchField.text = query;
|
spotlightContent.searchField.text = query;
|
||||||
@@ -398,40 +395,29 @@ Item {
|
|||||||
isClosing = false;
|
isClosing = false;
|
||||||
openedFromOverview = false;
|
openedFromOverview = false;
|
||||||
|
|
||||||
// Disable animations so the snap is instant
|
|
||||||
animationsEnabled = false;
|
animationsEnabled = false;
|
||||||
|
|
||||||
// Freeze the collapsed offsets (they depend on height which could change)
|
|
||||||
_frozenMotionX = contentContainer ? contentContainer.collapsedMotionX : 0;
|
_frozenMotionX = contentContainer ? contentContainer.collapsedMotionX : 0;
|
||||||
_frozenMotionY = contentContainer ? contentContainer.collapsedMotionY : (Theme.isDirectionalEffect ? Math.max(root.screenHeight - root._ccY + root.shadowPad, Theme.effectAnimOffset * 1.1) : -Theme.effectAnimOffset);
|
_frozenMotionY = contentContainer ? contentContainer.collapsedMotionY : (Theme.isDirectionalEffect ? Math.max(root.screenHeight - root._ccY + root.shadowPad, Theme.effectAnimOffset * 1.1) : -Theme.effectAnimOffset);
|
||||||
|
|
||||||
var focusedScreen = CompositorService.getFocusedScreen();
|
var focusedScreen = CompositorService.getFocusedScreen();
|
||||||
if (focusedScreen) {
|
if (focusedScreen) {
|
||||||
backgroundWindow.screen = focusedScreen;
|
|
||||||
contentWindow.screen = focusedScreen;
|
contentWindow.screen = focusedScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
// _motionActive = false ensures motionX/Y snap to frozen collapsed position
|
|
||||||
_motionActive = false;
|
_motionActive = false;
|
||||||
|
|
||||||
// Make windows visible but do NOT request keyboard focus yet
|
|
||||||
ModalManager.openModal(modalHandle);
|
ModalManager.openModal(modalHandle);
|
||||||
spotlightOpen = true;
|
spotlightOpen = true;
|
||||||
backgroundWindow.visible = true;
|
|
||||||
contentWindow.visible = true;
|
contentWindow.visible = true;
|
||||||
if (useHyprlandFocusGrab)
|
|
||||||
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
|
// Defer focus until after enter motion starts (avoids compositor IPC stalls).
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
root.animationsEnabled = true;
|
root.animationsEnabled = true;
|
||||||
root._motionActive = true;
|
root._motionActive = true;
|
||||||
|
|
||||||
// Frame 2: request keyboard focus + activate search field
|
|
||||||
// Double-deferred to avoid compositor IPC competing with animation frames
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
root.keyboardActive = true;
|
root.keyboardActive = true;
|
||||||
if (root.spotlightContent && root.spotlightContent.searchField)
|
if (root.spotlightContent && root.spotlightContent.searchField)
|
||||||
@@ -454,16 +440,13 @@ Item {
|
|||||||
spotlightContent?.closeTransientUi?.();
|
spotlightContent?.closeTransientUi?.();
|
||||||
openedFromOverview = false;
|
openedFromOverview = false;
|
||||||
isClosing = true;
|
isClosing = true;
|
||||||
// For directional effects, defer contentVisible=false so content stays rendered during exit slide
|
|
||||||
if (!Theme.isDirectionalEffect)
|
if (!Theme.isDirectionalEffect)
|
||||||
contentVisible = false;
|
contentVisible = false;
|
||||||
|
|
||||||
// Trigger exit animation — Behaviors will animate motionX/Y to frozen collapsed position
|
|
||||||
_motionActive = false;
|
_motionActive = false;
|
||||||
|
|
||||||
keyboardActive = false;
|
keyboardActive = false;
|
||||||
spotlightOpen = false;
|
spotlightOpen = false;
|
||||||
focusGrab.active = false;
|
|
||||||
ModalManager.closeModal(modalHandle);
|
ModalManager.closeModal(modalHandle);
|
||||||
closeCleanupTimer.start();
|
closeCleanupTimer.start();
|
||||||
}
|
}
|
||||||
@@ -500,7 +483,6 @@ Item {
|
|||||||
isClosing = false;
|
isClosing = false;
|
||||||
contentVisible = false;
|
contentVisible = false;
|
||||||
contentWindow.visible = false;
|
contentWindow.visible = false;
|
||||||
backgroundWindow.visible = false;
|
|
||||||
if (root.unloadContentOnClose)
|
if (root.unloadContentOnClose)
|
||||||
launcherContentLoader.active = false;
|
launcherContentLoader.active = false;
|
||||||
dialogClosed();
|
dialogClosed();
|
||||||
@@ -519,7 +501,7 @@ Item {
|
|||||||
HyprlandFocusGrab {
|
HyprlandFocusGrab {
|
||||||
id: focusGrab
|
id: focusGrab
|
||||||
windows: [contentWindow]
|
windows: [contentWindow]
|
||||||
active: false
|
active: root.useHyprlandFocusGrab && root.spotlightOpen
|
||||||
|
|
||||||
onCleared: {
|
onCleared: {
|
||||||
if (spotlightOpen) {
|
if (spotlightOpen) {
|
||||||
@@ -569,7 +551,6 @@ Item {
|
|||||||
|
|
||||||
root._releaseModalChrome();
|
root._releaseModalChrome();
|
||||||
root._windowEnabled = false;
|
root._windowEnabled = false;
|
||||||
backgroundWindow.screen = newScreen;
|
|
||||||
contentWindow.screen = newScreen;
|
contentWindow.screen = newScreen;
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
root._windowEnabled = true;
|
root._windowEnabled = true;
|
||||||
@@ -577,73 +558,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelWindow {
|
|
||||||
id: backgroundWindow
|
|
||||||
visible: false
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
readonly property real _topMargin: contentContainer.dockTop ? contentContainer.dockThickness : (typeof SettingsData !== "undefined" && SettingsData.barPosition === 0 ? Theme.px(42, root.dpr) : 0)
|
|
||||||
readonly property real _bottomMargin: contentContainer.dockBottom ? contentContainer.dockThickness : (typeof SettingsData !== "undefined" && SettingsData.barPosition === 1 ? Theme.px(42, root.dpr) : 0)
|
|
||||||
readonly property real _leftMargin: contentContainer.dockLeft ? contentContainer.dockThickness : (typeof SettingsData !== "undefined" && SettingsData.barPosition === 2 ? Theme.px(42, root.dpr) : 0)
|
|
||||||
readonly property real _rightMargin: contentContainer.dockRight ? contentContainer.dockThickness : (typeof SettingsData !== "undefined" && SettingsData.barPosition === 3 ? Theme.px(42, root.dpr) : 0)
|
|
||||||
|
|
||||||
WlrLayershell.namespace: "dms:spotlight:bg"
|
|
||||||
WlrLayershell.layer: root.effectiveLauncherLayer
|
|
||||||
WlrLayershell.exclusiveZone: -1
|
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
|
||||||
|
|
||||||
WlrLayershell.margins {
|
|
||||||
top: backgroundWindow._topMargin
|
|
||||||
bottom: backgroundWindow._bottomMargin
|
|
||||||
left: backgroundWindow._leftMargin
|
|
||||||
right: backgroundWindow._rightMargin
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
bottom: true
|
|
||||||
left: true
|
|
||||||
right: true
|
|
||||||
}
|
|
||||||
|
|
||||||
mask: Region {
|
|
||||||
item: (spotlightOpen || isClosing) ? bgFullScreenMask : null
|
|
||||||
|
|
||||||
Region {
|
|
||||||
item: bgContentHole
|
|
||||||
intersection: Intersection.Subtract
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: bgFullScreenMask
|
|
||||||
anchors.fill: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: bgContentHole
|
|
||||||
visible: false
|
|
||||||
x: root._cwMarginLeft + contentContainer.x - backgroundWindow._leftMargin
|
|
||||||
y: root._cwMarginTop + contentContainer.y - backgroundWindow._topMargin
|
|
||||||
width: root.alignedWidth
|
|
||||||
height: root.contentSurfaceHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: backgroundDarken
|
|
||||||
anchors.fill: parent
|
|
||||||
color: "black"
|
|
||||||
opacity: 0
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: spotlightOpen
|
|
||||||
onClicked: root.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: contentWindow
|
id: contentWindow
|
||||||
visible: false
|
visible: false
|
||||||
@@ -663,23 +577,31 @@ Item {
|
|||||||
WlrLayershell.namespace: "dms:spotlight"
|
WlrLayershell.namespace: "dms:spotlight"
|
||||||
WlrLayershell.layer: root.effectiveLauncherLayer
|
WlrLayershell.layer: root.effectiveLauncherLayer
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: PopoutManager.screenshotActive ? WlrKeyboardFocus.None : (keyboardActive ? (root.useHyprlandFocusGrab ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.Exclusive) : WlrKeyboardFocus.None)
|
WlrLayershell.keyboardFocus: KeyboardFocus.keyboardFocus(keyboardActive, null)
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
left: true
|
left: true
|
||||||
top: true
|
top: true
|
||||||
|
right: true
|
||||||
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
WlrLayershell.margins {
|
|
||||||
left: root._cwMarginLeft
|
|
||||||
top: root._cwMarginTop
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: root._cwWidth
|
|
||||||
implicitHeight: root._cwHeight
|
|
||||||
|
|
||||||
mask: Region {
|
mask: Region {
|
||||||
item: contentInputMask
|
item: (root.spotlightOpen || root.isClosing) ? dismissArea : contentInputMask
|
||||||
|
|
||||||
|
Region {
|
||||||
|
item: (root.spotlightOpen || root.isClosing) ? contentInputMask : null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: dismissArea
|
||||||
|
visible: false
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.topMargin: contentContainer.dockTop ? contentContainer.dockThickness : (typeof SettingsData !== "undefined" && SettingsData.barPosition === 0 ? Theme.px(42, root.dpr) : 0)
|
||||||
|
anchors.bottomMargin: contentContainer.dockBottom ? contentContainer.dockThickness : (typeof SettingsData !== "undefined" && SettingsData.barPosition === 1 ? Theme.px(42, root.dpr) : 0)
|
||||||
|
anchors.leftMargin: contentContainer.dockLeft ? contentContainer.dockThickness : (typeof SettingsData !== "undefined" && SettingsData.barPosition === 2 ? Theme.px(42, root.dpr) : 0)
|
||||||
|
anchors.rightMargin: contentContainer.dockRight ? contentContainer.dockThickness : (typeof SettingsData !== "undefined" && SettingsData.barPosition === 3 ? Theme.px(42, root.dpr) : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -691,16 +613,31 @@ Item {
|
|||||||
height: root.contentSurfaceHeight
|
height: root.contentSurfaceHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: dismissArea
|
||||||
|
enabled: root.spotlightOpen
|
||||||
|
z: -2
|
||||||
|
onClicked: root.hide()
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: contentContainer
|
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
|
x: root._ccX
|
||||||
y: root._ccY
|
y: root._ccY
|
||||||
width: root.alignedWidth
|
width: root.alignedWidth
|
||||||
height: root.contentSurfaceHeight
|
height: root.contentSurfaceHeight
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: root.spotlightOpen
|
||||||
|
hoverEnabled: false
|
||||||
|
acceptedButtons: Qt.AllButtons
|
||||||
|
onPressed: mouse.accepted = true
|
||||||
|
onClicked: mouse.accepted = true
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
|
|
||||||
readonly property int dockEdge: typeof SettingsData !== "undefined" ? SettingsData.dockPosition : 1
|
readonly property int dockEdge: typeof SettingsData !== "undefined" ? SettingsData.dockPosition : 1
|
||||||
readonly property bool dockTop: dockEdge === 0
|
readonly property bool dockTop: dockEdge === 0
|
||||||
readonly property bool dockBottom: dockEdge === 1
|
readonly property bool dockBottom: dockEdge === 1
|
||||||
@@ -755,7 +692,6 @@ Item {
|
|||||||
return -Math.max((root.shadowPad || 0) + Theme.effectAnimOffset, 40);
|
return -Math.max((root.shadowPad || 0) + Theme.effectAnimOffset, 40);
|
||||||
}
|
}
|
||||||
|
|
||||||
// openProgress: 0 = closed (at frozenMotion, scaleCollapsed), 1 = open (at 0, scale 1).
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: morph
|
id: morph
|
||||||
property real openProgress: root._motionActive ? 1 : 0
|
property real openProgress: root._motionActive ? 1 : 0
|
||||||
@@ -814,7 +750,6 @@ Item {
|
|||||||
width: contentContainer.width
|
width: contentContainer.width
|
||||||
height: contentContainer.height
|
height: contentContainer.height
|
||||||
|
|
||||||
// Shadow mirrors contentWrapper position/scale/opacity
|
|
||||||
ElevationShadow {
|
ElevationShadow {
|
||||||
id: launcherShadowLayer
|
id: launcherShadowLayer
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -832,7 +767,6 @@ Item {
|
|||||||
shadowEnabled: !root.frameOwnsConnectedChrome && Theme.elevationEnabled && SettingsData.modalElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
shadowEnabled: !root.frameOwnsConnectedChrome && Theme.elevationEnabled && SettingsData.modalElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
||||||
}
|
}
|
||||||
|
|
||||||
// contentWrapper moves inside static contentContainer — DankPopout pattern
|
|
||||||
Item {
|
Item {
|
||||||
id: contentWrapper
|
id: contentWrapper
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|||||||
@@ -84,14 +84,14 @@ Item {
|
|||||||
readonly property real alignedX: Theme.snap(modalX, dpr)
|
readonly property real alignedX: Theme.snap(modalX, dpr)
|
||||||
readonly property real alignedY: Theme.snap(modalY, dpr)
|
readonly property real alignedY: Theme.snap(modalY, dpr)
|
||||||
|
|
||||||
// Extra headroom above the window for the slide-in animation
|
// Extra headroom above the content for the slide-in animation
|
||||||
readonly property real _animHeadroom: 16
|
readonly property real _animHeadroom: 16
|
||||||
readonly property real windowX: Math.max(0, Theme.snap(alignedX - shadowPad, dpr))
|
readonly property real windowX: Math.max(0, Theme.snap(alignedX - shadowPad, dpr))
|
||||||
readonly property real windowY: Math.max(0, Theme.snap(alignedY - shadowPad - _animHeadroom, dpr))
|
readonly property real windowY: Math.max(0, Theme.snap(alignedY - shadowPad - _animHeadroom, dpr))
|
||||||
readonly property real contentX: Theme.snap(alignedX - windowX, dpr)
|
readonly property real contentX: Theme.snap(alignedX - windowX, dpr)
|
||||||
readonly property real contentY: Theme.snap(alignedY - windowY, dpr)
|
readonly property real contentY: Theme.snap(alignedY - windowY, dpr)
|
||||||
readonly property real windowWidth: alignedWidth + contentX + shadowPad
|
|
||||||
readonly property real _animatedContentH: Theme.snap(_contentImplicitH, dpr)
|
readonly property real _animatedContentH: Theme.snap(_contentImplicitH, dpr)
|
||||||
|
readonly property real windowWidth: alignedWidth + contentX + shadowPad
|
||||||
readonly property real windowHeight: _animatedContentH + contentY + shadowPad + _animHeadroom
|
readonly property real windowHeight: _animatedContentH + contentY + shadowPad + _animHeadroom
|
||||||
|
|
||||||
readonly property color backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
readonly property color backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
@@ -114,6 +114,7 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
readonly property int borderWidth: SettingsData.dankLauncherV2BorderEnabled ? SettingsData.dankLauncherV2BorderThickness : 0
|
readonly property int borderWidth: SettingsData.dankLauncherV2BorderEnabled ? SettingsData.dankLauncherV2BorderThickness : 0
|
||||||
|
readonly property bool useSingleWindow: CompositorService.isHyprland || useBackgroundDarken
|
||||||
|
|
||||||
signal dialogClosed
|
signal dialogClosed
|
||||||
|
|
||||||
@@ -164,8 +165,6 @@ Item {
|
|||||||
openedFromOverview = false;
|
openedFromOverview = false;
|
||||||
keyboardActive = true;
|
keyboardActive = true;
|
||||||
ModalManager.openModal(modalHandle);
|
ModalManager.openModal(modalHandle);
|
||||||
if (useHyprlandFocusGrab)
|
|
||||||
focusGrab.active = true;
|
|
||||||
_ensureContentLoadedAndInitialize(query || "", mode || "");
|
_ensureContentLoadedAndInitialize(query || "", mode || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +200,6 @@ Item {
|
|||||||
contentVisible = false;
|
contentVisible = false;
|
||||||
keyboardActive = false;
|
keyboardActive = false;
|
||||||
spotlightOpen = false;
|
spotlightOpen = false;
|
||||||
focusGrab.active = false;
|
|
||||||
ModalManager.closeModal(modalHandle);
|
ModalManager.closeModal(modalHandle);
|
||||||
closeCleanupTimer.start();
|
closeCleanupTimer.start();
|
||||||
}
|
}
|
||||||
@@ -231,7 +229,7 @@ Item {
|
|||||||
HyprlandFocusGrab {
|
HyprlandFocusGrab {
|
||||||
id: focusGrab
|
id: focusGrab
|
||||||
windows: [launcherWindow]
|
windows: [launcherWindow]
|
||||||
active: false
|
active: root.useHyprlandFocusGrab && root.keyboardActive
|
||||||
onCleared: {
|
onCleared: {
|
||||||
if (spotlightOpen)
|
if (spotlightOpen)
|
||||||
hide();
|
hide();
|
||||||
@@ -270,8 +268,9 @@ Item {
|
|||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: clickCatcher
|
id: clickCatcher
|
||||||
screen: launcherWindow.screen
|
screen: launcherWindow.screen
|
||||||
visible: (spotlightOpen || isClosing) && !root.useBackgroundDarken
|
visible: (spotlightOpen || isClosing) && !root.useSingleWindow
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
updatesEnabled: false
|
||||||
|
|
||||||
WlrLayershell.namespace: "dms:spotlight:clickcatcher"
|
WlrLayershell.namespace: "dms:spotlight:clickcatcher"
|
||||||
WlrLayershell.layer: root.effectiveLauncherLayer
|
WlrLayershell.layer: root.effectiveLauncherLayer
|
||||||
@@ -337,24 +336,24 @@ Item {
|
|||||||
WlrLayershell.namespace: "dms:spotlight"
|
WlrLayershell.namespace: "dms:spotlight"
|
||||||
WlrLayershell.layer: root.effectiveLauncherLayer
|
WlrLayershell.layer: root.effectiveLauncherLayer
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: PopoutManager.screenshotActive ? WlrKeyboardFocus.None : (keyboardActive ? (root.useHyprlandFocusGrab ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.Exclusive) : WlrKeyboardFocus.None)
|
WlrLayershell.keyboardFocus: KeyboardFocus.keyboardFocus(keyboardActive, null)
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: true
|
top: true
|
||||||
left: true
|
left: true
|
||||||
right: root.useBackgroundDarken
|
right: root.useSingleWindow
|
||||||
bottom: root.useBackgroundDarken
|
bottom: root.useSingleWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
WlrLayershell.margins {
|
WlrLayershell.margins {
|
||||||
left: root.useBackgroundDarken ? 0 : root.windowX
|
left: root.useSingleWindow ? 0 : root.windowX
|
||||||
top: root.useBackgroundDarken ? 0 : root.windowY
|
top: root.useSingleWindow ? 0 : root.windowY
|
||||||
right: 0
|
right: 0
|
||||||
bottom: 0
|
bottom: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitWidth: root.useBackgroundDarken ? 0 : root.windowWidth
|
implicitWidth: root.useSingleWindow ? 0 : root.windowWidth
|
||||||
implicitHeight: root.useBackgroundDarken ? 0 : root.windowHeight
|
implicitHeight: root.useSingleWindow ? 0 : root.windowHeight
|
||||||
|
|
||||||
mask: Region {
|
mask: Region {
|
||||||
item: inputMask
|
item: inputMask
|
||||||
@@ -364,15 +363,15 @@ Item {
|
|||||||
id: inputMask
|
id: inputMask
|
||||||
visible: false
|
visible: false
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
x: root.useBackgroundDarken ? 0 : modalContainer.x
|
x: root.useSingleWindow ? 0 : modalContainer.x
|
||||||
y: root.useBackgroundDarken ? 0 : modalContainer.y + modalContainer.slideOffset
|
y: root.useSingleWindow ? 0 : modalContainer.y + modalContainer.slideOffset
|
||||||
width: root.useBackgroundDarken ? launcherWindow.width : root.alignedWidth
|
width: root.useSingleWindow ? launcherWindow.width : root.alignedWidth
|
||||||
height: root.useBackgroundDarken ? launcherWindow.height : root._contentImplicitH
|
height: root.useSingleWindow ? launcherWindow.height : root._contentImplicitH
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: root.useBackgroundDarken && spotlightOpen
|
enabled: root.useSingleWindow && spotlightOpen
|
||||||
z: -2
|
z: -2
|
||||||
onClicked: root.hide()
|
onClicked: root.hide()
|
||||||
}
|
}
|
||||||
@@ -396,13 +395,23 @@ Item {
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: modalContainer
|
id: modalContainer
|
||||||
x: root.useBackgroundDarken ? root.alignedX : root.contentX
|
x: root.useSingleWindow ? root.alignedX : root.contentX
|
||||||
y: root.useBackgroundDarken ? root.alignedY : root.contentY
|
y: root.useSingleWindow ? root.alignedY : root.contentY
|
||||||
width: root.alignedWidth
|
width: root.alignedWidth
|
||||||
height: root._animatedContentH
|
height: root._animatedContentH
|
||||||
visible: _renderActive
|
visible: _renderActive
|
||||||
z: 0
|
z: 0
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: spotlightOpen
|
||||||
|
hoverEnabled: false
|
||||||
|
acceptedButtons: Qt.AllButtons
|
||||||
|
onPressed: mouse.accepted = true
|
||||||
|
onClicked: mouse.accepted = true
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
|
|
||||||
property bool _renderActive: contentVisible
|
property bool _renderActive: contentVisible
|
||||||
property real slideOffset: contentVisible ? 0 : -root._animHeadroom
|
property real slideOffset: contentVisible ? 0 : -root._animHeadroom
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ Item {
|
|||||||
|
|
||||||
readonly property color backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
readonly property color backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
readonly property bool useBackgroundDarken: !SettingsData.frameEnabled && SettingsData.modalDarkenBackground
|
readonly property bool useBackgroundDarken: !SettingsData.frameEnabled && SettingsData.modalDarkenBackground
|
||||||
|
readonly property bool useSingleWindow: CompositorService.isHyprland || useBackgroundDarken
|
||||||
readonly property bool usesOverlayLayer: useBackgroundDarken || SettingsData.launcherUseOverlayLayer || triggerUsesOverlayLayer
|
readonly property bool usesOverlayLayer: useBackgroundDarken || SettingsData.launcherUseOverlayLayer || triggerUsesOverlayLayer
|
||||||
readonly property var effectiveLauncherLayer: LayerShell.fromEnv("DMS_MODAL_LAYER", root.usesOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top, {
|
readonly property var effectiveLauncherLayer: LayerShell.fromEnv("DMS_MODAL_LAYER", root.usesOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top, {
|
||||||
"allow": ["top", "overlay"],
|
"allow": ["top", "overlay"],
|
||||||
@@ -172,8 +173,6 @@ Item {
|
|||||||
|
|
||||||
keyboardActive = true;
|
keyboardActive = true;
|
||||||
ModalManager.openModal(modalHandle);
|
ModalManager.openModal(modalHandle);
|
||||||
if (useHyprlandFocusGrab)
|
|
||||||
focusGrab.active = true;
|
|
||||||
|
|
||||||
_ensureContentLoadedAndInitialize(query || "", mode || "");
|
_ensureContentLoadedAndInitialize(query || "", mode || "");
|
||||||
}
|
}
|
||||||
@@ -211,7 +210,6 @@ Item {
|
|||||||
|
|
||||||
keyboardActive = false;
|
keyboardActive = false;
|
||||||
spotlightOpen = false;
|
spotlightOpen = false;
|
||||||
focusGrab.active = false;
|
|
||||||
ModalManager.closeModal(modalHandle);
|
ModalManager.closeModal(modalHandle);
|
||||||
|
|
||||||
closeCleanupTimer.start();
|
closeCleanupTimer.start();
|
||||||
@@ -262,7 +260,7 @@ Item {
|
|||||||
HyprlandFocusGrab {
|
HyprlandFocusGrab {
|
||||||
id: focusGrab
|
id: focusGrab
|
||||||
windows: [launcherWindow]
|
windows: [launcherWindow]
|
||||||
active: false
|
active: root.useHyprlandFocusGrab && root.keyboardActive
|
||||||
|
|
||||||
onCleared: {
|
onCleared: {
|
||||||
if (spotlightOpen) {
|
if (spotlightOpen) {
|
||||||
@@ -306,8 +304,9 @@ Item {
|
|||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: clickCatcher
|
id: clickCatcher
|
||||||
screen: launcherWindow.screen
|
screen: launcherWindow.screen
|
||||||
visible: (spotlightOpen || isClosing) && !root.useBackgroundDarken
|
visible: (spotlightOpen || isClosing) && !root.useSingleWindow
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
updatesEnabled: false
|
||||||
|
|
||||||
WlrLayershell.namespace: "dms:spotlight:clickcatcher"
|
WlrLayershell.namespace: "dms:spotlight:clickcatcher"
|
||||||
WlrLayershell.layer: root.effectiveLauncherLayer
|
WlrLayershell.layer: root.effectiveLauncherLayer
|
||||||
@@ -373,24 +372,24 @@ Item {
|
|||||||
WlrLayershell.namespace: "dms:spotlight"
|
WlrLayershell.namespace: "dms:spotlight"
|
||||||
WlrLayershell.layer: root.effectiveLauncherLayer
|
WlrLayershell.layer: root.effectiveLauncherLayer
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: PopoutManager.screenshotActive ? WlrKeyboardFocus.None : (keyboardActive ? (root.useHyprlandFocusGrab ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.Exclusive) : WlrKeyboardFocus.None)
|
WlrLayershell.keyboardFocus: KeyboardFocus.keyboardFocus(keyboardActive, null)
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: true
|
top: true
|
||||||
left: true
|
left: true
|
||||||
right: root.useBackgroundDarken
|
right: root.useSingleWindow
|
||||||
bottom: root.useBackgroundDarken
|
bottom: root.useSingleWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
WlrLayershell.margins {
|
WlrLayershell.margins {
|
||||||
left: root.useBackgroundDarken ? 0 : root.windowX
|
left: root.useSingleWindow ? 0 : root.windowX
|
||||||
top: root.useBackgroundDarken ? 0 : root.windowY
|
top: root.useSingleWindow ? 0 : root.windowY
|
||||||
right: 0
|
right: 0
|
||||||
bottom: 0
|
bottom: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitWidth: root.useBackgroundDarken ? 0 : root.windowWidth
|
implicitWidth: root.useSingleWindow ? 0 : root.windowWidth
|
||||||
implicitHeight: root.useBackgroundDarken ? 0 : root.windowHeight
|
implicitHeight: root.useSingleWindow ? 0 : root.windowHeight
|
||||||
|
|
||||||
mask: Region {
|
mask: Region {
|
||||||
item: launcherInputMask
|
item: launcherInputMask
|
||||||
@@ -400,15 +399,15 @@ Item {
|
|||||||
id: launcherInputMask
|
id: launcherInputMask
|
||||||
visible: false
|
visible: false
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
x: root.useBackgroundDarken ? 0 : modalContainer.x
|
x: root.useSingleWindow ? 0 : modalContainer.x
|
||||||
y: root.useBackgroundDarken ? 0 : modalContainer.y
|
y: root.useSingleWindow ? 0 : modalContainer.y
|
||||||
width: root.useBackgroundDarken ? launcherWindow.width : modalContainer.width
|
width: root.useSingleWindow ? launcherWindow.width : modalContainer.width
|
||||||
height: root.useBackgroundDarken ? launcherWindow.height : modalContainer.height
|
height: root.useSingleWindow ? launcherWindow.height : modalContainer.height
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: root.useBackgroundDarken && spotlightOpen
|
enabled: root.useSingleWindow && spotlightOpen
|
||||||
z: -2
|
z: -2
|
||||||
onClicked: root.hide()
|
onClicked: root.hide()
|
||||||
}
|
}
|
||||||
@@ -432,13 +431,23 @@ Item {
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: modalContainer
|
id: modalContainer
|
||||||
x: root.useBackgroundDarken ? root.alignedX : root.contentX
|
x: root.useSingleWindow ? root.alignedX : root.contentX
|
||||||
y: root.useBackgroundDarken ? root.alignedY : root.contentY
|
y: root.useSingleWindow ? root.alignedY : root.contentY
|
||||||
width: root.alignedWidth
|
width: root.alignedWidth
|
||||||
height: root.alignedHeight
|
height: root.alignedHeight
|
||||||
visible: _renderActive
|
visible: _renderActive
|
||||||
z: 0
|
z: 0
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: spotlightOpen
|
||||||
|
hoverEnabled: false
|
||||||
|
acceptedButtons: Qt.AllButtons
|
||||||
|
onPressed: mouse.accepted = true
|
||||||
|
onClicked: mouse.accepted = true
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
|
|
||||||
property bool _renderActive: contentVisible
|
property bool _renderActive: contentVisible
|
||||||
property real publishedScale: contentVisible ? 1 : 0.96
|
property real publishedScale: contentVisible ? 1 : 0.96
|
||||||
property real publishedOpacity: contentVisible ? 1 : 0
|
property real publishedOpacity: contentVisible ? 1 : 0
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell.Hyprland
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -29,11 +28,6 @@ DankModal {
|
|||||||
KeybindsService.loadCheatsheet();
|
KeybindsService.loadCheatsheet();
|
||||||
}
|
}
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [root.contentWindow]
|
|
||||||
active: root.useHyprlandFocusGrab && root.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollDown() {
|
function scrollDown() {
|
||||||
if (!root.activeFlickable)
|
if (!root.activeFlickable)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell.Hyprland
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import qs.Common
|
import qs.Common
|
||||||
@@ -45,12 +44,6 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
id: grab
|
|
||||||
windows: [muxModal.contentWindow]
|
|
||||||
active: CompositorService.isHyprland && muxModal.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
if (shouldBeVisible) {
|
if (shouldBeVisible) {
|
||||||
hide();
|
hide();
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Hyprland
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
@@ -11,11 +10,6 @@ DankModal {
|
|||||||
|
|
||||||
layerNamespace: "dms:notification-center-modal"
|
layerNamespace: "dms:notification-center-modal"
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [notificationModal.contentWindow]
|
|
||||||
active: notificationModal.useHyprlandFocusGrab && notificationModal.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool notificationModalOpen: false
|
property bool notificationModalOpen: false
|
||||||
property var notificationListRef: null
|
property var notificationListRef: null
|
||||||
property var historyListRef: null
|
property var historyListRef: null
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modals.Common
|
import qs.Modals.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
@@ -13,11 +12,6 @@ DankModal {
|
|||||||
layerNamespace: "dms:power-menu"
|
layerNamespace: "dms:power-menu"
|
||||||
keepPopoutsOpen: true
|
keepPopoutsOpen: true
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
windows: [root.contentWindow]
|
|
||||||
active: root.useHyprlandFocusGrab && root.shouldHaveFocus
|
|
||||||
}
|
|
||||||
|
|
||||||
property int selectedIndex: 0
|
property int selectedIndex: 0
|
||||||
property int selectedRow: 0
|
property int selectedRow: 0
|
||||||
property int selectedCol: 0
|
property int selectedCol: 0
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ Rectangle {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "compositor_layout",
|
"id": "compositor_layout",
|
||||||
"text": CompositorService.isNiri ? "niri" : (CompositorService.isHyprland ? "Hyprland" : "MangoWC"),
|
"text": CompositorService.isNiri ? "Niri" : (CompositorService.isHyprland ? "Hyprland" : "MangoWC"),
|
||||||
"icon": "crop_square",
|
"icon": "layers",
|
||||||
"tabIndex": 37,
|
"tabIndex": 37,
|
||||||
"layoutCapable": true
|
"layoutCapable": true
|
||||||
}
|
}
|
||||||
@@ -117,18 +117,18 @@ Rectangle {
|
|||||||
"text": I18n.tr("Dank Bar"),
|
"text": I18n.tr("Dank Bar"),
|
||||||
"icon": "toolbar",
|
"icon": "toolbar",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
|
||||||
"id": "dankbar_settings",
|
|
||||||
"text": I18n.tr("Settings"),
|
|
||||||
"icon": "tune",
|
|
||||||
"tabIndex": 3
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "dankbar_appearance",
|
"id": "dankbar_appearance",
|
||||||
"text": I18n.tr("Appearance"),
|
"text": I18n.tr("Appearance"),
|
||||||
"icon": "palette",
|
"icon": "palette",
|
||||||
"tabIndex": 6
|
"tabIndex": 6
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "dankbar_settings",
|
||||||
|
"text": I18n.tr("Settings"),
|
||||||
|
"icon": "tune",
|
||||||
|
"tabIndex": 3
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "dankbar_widgets",
|
"id": "dankbar_widgets",
|
||||||
"text": I18n.tr("Widgets"),
|
"text": I18n.tr("Widgets"),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import qs.Widgets
|
|||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
Variants {
|
Variants {
|
||||||
|
readonly property var log: Log.scoped("BlurredWallpaperBackground")
|
||||||
model: {
|
model: {
|
||||||
if (SessionData.isGreeterMode) {
|
if (SessionData.isGreeterMode) {
|
||||||
return Quickshell.screens;
|
return Quickshell.screens;
|
||||||
@@ -32,6 +33,8 @@ Variants {
|
|||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
|
updatesEnabled: root.renderActive || root._settleFrames > 0
|
||||||
|
|
||||||
mask: Region {
|
mask: Region {
|
||||||
item: Item {}
|
item: Item {}
|
||||||
}
|
}
|
||||||
@@ -85,7 +88,6 @@ Variants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
blurWallpaperWindow.updatesEnabled = Qt.binding(() => !root.source || root.effectActive || root._renderSettling || currentWallpaper.status === Image.Loading || nextWallpaper.status === Image.Loading);
|
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,51 +95,67 @@ Variants {
|
|||||||
property real transitionProgress: 0
|
property real transitionProgress: 0
|
||||||
readonly property bool transitioning: transitionAnimation.running
|
readonly property bool transitioning: transitionAnimation.running
|
||||||
property bool effectActive: false
|
property bool effectActive: false
|
||||||
property bool _renderSettling: true
|
|
||||||
property bool useNextForEffect: false
|
property bool useNextForEffect: false
|
||||||
|
readonly property var backingWindow: Window.window
|
||||||
|
readonly property bool renderActive: !source || effectActive || currentWallpaper.status === Image.Loading || nextWallpaper.status === Image.Loading
|
||||||
|
property int _settleFrames: 3
|
||||||
|
|
||||||
Connections {
|
function invalidate() {
|
||||||
target: currentWallpaper
|
_settleFrames = 3;
|
||||||
function onStatusChanged() {
|
backingWindow?.update();
|
||||||
if (currentWallpaper.status !== Image.Ready && currentWallpaper.status !== Image.Error)
|
|
||||||
return;
|
|
||||||
root._renderSettling = true;
|
|
||||||
renderSettleTimer.restart();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onRenderActiveChanged: invalidate()
|
||||||
|
onBackingWindowChanged: invalidate()
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: blurWallpaperWindow
|
target: root.backingWindow
|
||||||
|
function onFrameSwapped() {
|
||||||
|
if (root._settleFrames > 0)
|
||||||
|
root._settleFrames--;
|
||||||
|
}
|
||||||
|
function onVisibleChanged() {
|
||||||
|
root.invalidate();
|
||||||
|
}
|
||||||
function onWidthChanged() {
|
function onWidthChanged() {
|
||||||
root._renderSettling = true;
|
root.invalidate();
|
||||||
renderSettleTimer.restart();
|
|
||||||
}
|
}
|
||||||
function onHeightChanged() {
|
function onHeightChanged() {
|
||||||
root._renderSettling = true;
|
root.invalidate();
|
||||||
renderSettleTimer.restart();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Quickshell
|
target: Quickshell
|
||||||
function onScreensChanged() {
|
function onScreensChanged() {
|
||||||
root._renderSettling = true;
|
root.invalidate();
|
||||||
renderSettleTimer.restart();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: SettingsData
|
target: SettingsData
|
||||||
function onWallpaperFillModeChanged() {
|
function onWallpaperFillModeChanged() {
|
||||||
root._renderSettling = true;
|
root.invalidate();
|
||||||
renderSettleTimer.restart();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Connections {
|
||||||
id: renderSettleTimer
|
target: IdleService
|
||||||
interval: 1000
|
function onIsShellLockedChanged() {
|
||||||
onTriggered: root._renderSettling = false
|
if (IdleService.isShellLocked)
|
||||||
|
return;
|
||||||
|
root.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTransitionLoadError(failedSource) {
|
||||||
|
log.warn("failed to load candidate wallpaper for", modelData.name + ":", failedSource);
|
||||||
|
transitionDelayTimer.stop();
|
||||||
|
transitionAnimation.stop();
|
||||||
|
root.useNextForEffect = false;
|
||||||
|
root.effectActive = false;
|
||||||
|
root.transitionProgress = 0.0;
|
||||||
|
nextWallpaper.source = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
onSourceChanged: {
|
onSourceChanged: {
|
||||||
@@ -164,8 +182,6 @@ Variants {
|
|||||||
transitionAnimation.stop();
|
transitionAnimation.stop();
|
||||||
root.transitionProgress = 0.0;
|
root.transitionProgress = 0.0;
|
||||||
root.effectActive = false;
|
root.effectActive = false;
|
||||||
root._renderSettling = true;
|
|
||||||
renderSettleTimer.restart();
|
|
||||||
currentWallpaper.source = newSource;
|
currentWallpaper.source = newSource;
|
||||||
nextWallpaper.source = "";
|
nextWallpaper.source = "";
|
||||||
}
|
}
|
||||||
@@ -194,8 +210,6 @@ Variants {
|
|||||||
transitionAnimation.stop();
|
transitionAnimation.stop();
|
||||||
root.transitionProgress = 0;
|
root.transitionProgress = 0;
|
||||||
root.effectActive = false;
|
root.effectActive = false;
|
||||||
root._renderSettling = true;
|
|
||||||
renderSettleTimer.restart();
|
|
||||||
currentWallpaper.source = nextWallpaper.source;
|
currentWallpaper.source = nextWallpaper.source;
|
||||||
nextWallpaper.source = "";
|
nextWallpaper.source = "";
|
||||||
}
|
}
|
||||||
@@ -204,9 +218,6 @@ Variants {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
root._renderSettling = true;
|
|
||||||
renderSettleTimer.restart();
|
|
||||||
|
|
||||||
nextWallpaper.source = newPath;
|
nextWallpaper.source = newPath;
|
||||||
|
|
||||||
if (nextWallpaper.status === Image.Ready)
|
if (nextWallpaper.status === Image.Ready)
|
||||||
@@ -215,7 +226,7 @@ Variants {
|
|||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: !root.source || root.isColorSource
|
active: !root.source || root.isColorSource || currentWallpaper.status === Image.Error
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
sourceComponent: DankBackdrop {
|
sourceComponent: DankBackdrop {
|
||||||
@@ -238,6 +249,12 @@ Variants {
|
|||||||
cache: true
|
cache: true
|
||||||
sourceSize: Qt.size(root.textureWidth, root.textureHeight)
|
sourceSize: Qt.size(root.textureWidth, root.textureHeight)
|
||||||
fillMode: root.getFillMode(SessionData.isGreeterMode ? GreetdSettings.wallpaperFillMode : SessionData.getMonitorWallpaperFillMode(modelData.name))
|
fillMode: root.getFillMode(SessionData.isGreeterMode ? GreetdSettings.wallpaperFillMode : SessionData.getMonitorWallpaperFillMode(modelData.name))
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === Image.Error) {
|
||||||
|
log.warn("failed to load active wallpaper for", modelData.name + ":", source);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
@@ -253,6 +270,10 @@ Variants {
|
|||||||
fillMode: root.getFillMode(SessionData.isGreeterMode ? GreetdSettings.wallpaperFillMode : SessionData.getMonitorWallpaperFillMode(modelData.name))
|
fillMode: root.getFillMode(SessionData.isGreeterMode ? GreetdSettings.wallpaperFillMode : SessionData.getMonitorWallpaperFillMode(modelData.name))
|
||||||
|
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
|
if (status === Image.Error) {
|
||||||
|
root.handleTransitionLoadError(source);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (status !== Image.Ready)
|
if (status !== Image.Ready)
|
||||||
return;
|
return;
|
||||||
if (!root.transitioning) {
|
if (!root.transitioning) {
|
||||||
@@ -329,8 +350,6 @@ Variants {
|
|||||||
root.useNextForEffect = false;
|
root.useNextForEffect = false;
|
||||||
nextWallpaper.source = "";
|
nextWallpaper.source = "";
|
||||||
root.transitionProgress = 0.0;
|
root.transitionProgress = 0.0;
|
||||||
root._renderSettling = true;
|
|
||||||
renderSettleTimer.restart();
|
|
||||||
root.effectActive = false;
|
root.effectActive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,15 +109,7 @@ DankPopout {
|
|||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
customKeyboardFocus: {
|
customKeyboardFocus: anyModalOpen ? WlrKeyboardFocus.None : null
|
||||||
if (!shouldBeVisible)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (anyModalOpen)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (CompositorService.useHyprlandFocusGrab)
|
|
||||||
return WlrKeyboardFocus.OnDemand;
|
|
||||||
return WlrKeyboardFocus.Exclusive;
|
|
||||||
}
|
|
||||||
|
|
||||||
onBackgroundClicked: close()
|
onBackgroundClicked: close()
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ Item {
|
|||||||
// M3 elevation shadow — Level 2 baseline (navigation bar), with per-bar override support
|
// M3 elevation shadow — Level 2 baseline (navigation bar), with per-bar override support
|
||||||
readonly property bool hasPerBarOverride: (barConfig?.shadowIntensity ?? 0) > 0
|
readonly property bool hasPerBarOverride: (barConfig?.shadowIntensity ?? 0) > 0
|
||||||
readonly property var elevLevel: Theme.elevationLevel2
|
readonly property var elevLevel: Theme.elevationLevel2
|
||||||
readonly property bool shadowEnabled: !BlurService.enabled && ((Theme.elevationEnabled && (typeof SettingsData !== "undefined" ? (SettingsData.barElevationEnabled ?? true) : false)) || hasPerBarOverride)
|
readonly property bool shadowEnabled: (Theme.elevationEnabled && (typeof SettingsData !== "undefined" ? (SettingsData.barElevationEnabled ?? true) : false)) || hasPerBarOverride
|
||||||
readonly property string autoBarShadowDirection: isTop ? "top" : (isBottom ? "bottom" : (isLeft ? "left" : (isRight ? "right" : "top")))
|
readonly property string autoBarShadowDirection: isTop ? "top" : (isBottom ? "bottom" : (isLeft ? "left" : (isRight ? "right" : "top")))
|
||||||
readonly property string globalShadowDirection: Theme.elevationLightDirection === "autoBar" ? autoBarShadowDirection : Theme.elevationLightDirection
|
readonly property string globalShadowDirection: Theme.elevationLightDirection === "autoBar" ? autoBarShadowDirection : Theme.elevationLightDirection
|
||||||
readonly property string perBarShadowDirectionMode: barConfig?.shadowDirectionMode ?? "inherit"
|
readonly property string perBarShadowDirectionMode: barConfig?.shadowDirectionMode ?? "inherit"
|
||||||
@@ -207,7 +207,6 @@ Item {
|
|||||||
shadowOffsetX: root.shadowOffsetX
|
shadowOffsetX: root.shadowOffsetX
|
||||||
shadowOffsetY: root.shadowOffsetY
|
shadowOffsetY: root.shadowOffsetY
|
||||||
shadowColor: root.shadowColor
|
shadowColor: root.shadowColor
|
||||||
blurMax: Theme.elevationBlurMax
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ PanelWindow {
|
|||||||
id: barWindow
|
id: barWindow
|
||||||
readonly property var log: Log.scoped("DankBarWindow")
|
readonly property var log: Log.scoped("DankBarWindow")
|
||||||
|
|
||||||
|
Component.onDestruction: KeyboardFocus.unregisterBarWindow(barWindow)
|
||||||
|
|
||||||
required property var rootWindow
|
required property var rootWindow
|
||||||
required property var barConfig
|
required property var barConfig
|
||||||
property var modelData: item
|
property var modelData: item
|
||||||
@@ -18,6 +20,8 @@ PanelWindow {
|
|||||||
property var centerWidgetsModel
|
property var centerWidgetsModel
|
||||||
property var rightWidgetsModel
|
property var rightWidgetsModel
|
||||||
|
|
||||||
|
readonly property bool barRevealed: inputMask.showing
|
||||||
|
|
||||||
property var controlCenterButtonRef: null
|
property var controlCenterButtonRef: null
|
||||||
property var clockButtonRef: null
|
property var clockButtonRef: null
|
||||||
property var systemUpdateButtonRef: null
|
property var systemUpdateButtonRef: null
|
||||||
@@ -282,9 +286,6 @@ PanelWindow {
|
|||||||
|
|
||||||
readonly property bool isVertical: axis.isVertical
|
readonly property bool isVertical: axis.isVertical
|
||||||
|
|
||||||
property bool gothCornersEnabled: barConfig?.gothCornersEnabled ?? false
|
|
||||||
property real wingtipsRadius: barConfig?.gothCornerRadiusOverride ? (barConfig?.gothCornerRadiusValue ?? 12) : Theme.cornerRadius
|
|
||||||
readonly property real _wingR: Math.max(0, wingtipsRadius)
|
|
||||||
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
|
||||||
@@ -296,25 +297,30 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
readonly property real _dpr: CompositorService.getScreenScale(barWindow.screen)
|
readonly property real _dpr: CompositorService.getScreenScale(barWindow.screen)
|
||||||
|
|
||||||
|
property string screenName: modelData.name
|
||||||
|
|
||||||
|
readonly property bool usesConnectedFrameChrome: CompositorService.usesConnectedFrameChromeForScreen(screenName)
|
||||||
|
readonly property bool usesFrameBarChrome: CompositorService.frameWindowVisibleForScreen(screenName)
|
||||||
|
readonly property var renderBarConfig: SettingsData.effectiveBarConfigForRender(barConfig, usesFrameBarChrome)
|
||||||
|
|
||||||
|
property bool gothCornersEnabled: renderBarConfig?.gothCornersEnabled ?? false
|
||||||
|
property real wingtipsRadius: renderBarConfig?.gothCornerRadiusOverride ? (renderBarConfig?.gothCornerRadiusValue ?? 12) : Theme.cornerRadius
|
||||||
|
readonly property real _wingR: Math.max(0, wingtipsRadius)
|
||||||
|
|
||||||
// Shadow buffer: extra window space for shadow to render beyond bar bounds
|
// Shadow buffer: extra window space for shadow to render beyond bar bounds
|
||||||
readonly property bool _shadowActive: (Theme.elevationEnabled && (typeof SettingsData !== "undefined" ? (SettingsData.barElevationEnabled ?? true) : false)) || (barConfig?.shadowIntensity ?? 0) > 0
|
readonly property bool _shadowActive: (Theme.elevationEnabled && (typeof SettingsData !== "undefined" ? (SettingsData.barElevationEnabled ?? true) : false)) || (renderBarConfig?.shadowIntensity ?? 0) > 0
|
||||||
readonly property real _shadowBuffer: {
|
readonly property real _shadowBuffer: {
|
||||||
if (!_shadowActive)
|
if (!_shadowActive)
|
||||||
return 0;
|
return 0;
|
||||||
const hasOverride = (barConfig?.shadowIntensity ?? 0) > 0;
|
const hasOverride = (renderBarConfig?.shadowIntensity ?? 0) > 0;
|
||||||
if (hasOverride) {
|
if (hasOverride) {
|
||||||
const blur = (barConfig.shadowIntensity ?? 0) * 0.2;
|
const blur = (renderBarConfig.shadowIntensity ?? 0) * 0.2;
|
||||||
const offset = blur * 0.5;
|
const offset = blur * 0.5;
|
||||||
return Theme.snap(Math.max(16, blur + offset + 8), _dpr);
|
return Theme.snap(Math.max(16, blur + offset + 8), _dpr);
|
||||||
}
|
}
|
||||||
return Theme.snap(Theme.elevationRenderPadding(Theme.elevationLevel2, "top", 4, 8, 16), _dpr);
|
return Theme.snap(Theme.elevationRenderPadding(Theme.elevationLevel2, "top", 4, 8, 16), _dpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
property string screenName: modelData.name
|
|
||||||
|
|
||||||
readonly property bool usesConnectedFrameChrome: CompositorService.usesConnectedFrameChromeForScreen(screenName)
|
|
||||||
readonly property bool usesFrameBarChrome: CompositorService.frameWindowVisibleForScreen(screenName)
|
|
||||||
|
|
||||||
// Flatten/spacing collapse for maximized windows is only for frame-integrated layout.
|
// Flatten/spacing collapse for maximized windows is only for frame-integrated layout.
|
||||||
// When the bar draws its own pill, keep rounded corners and spacing like the dock.
|
// When the bar draws its own pill, keep rounded corners and spacing like the dock.
|
||||||
readonly property bool flattenForMaximizedWindow: !SettingsData.frameEnabled || usesFrameBarChrome
|
readonly property bool flattenForMaximizedWindow: !SettingsData.frameEnabled || usesFrameBarChrome
|
||||||
@@ -550,11 +556,12 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
screen: modelData
|
screen: modelData
|
||||||
implicitHeight: !isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((barConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0
|
implicitHeight: !isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((renderBarConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0
|
||||||
implicitWidth: isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((barConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0
|
implicitWidth: isVertical ? Theme.px(effectiveBarThickness + effectiveSpacing + ((renderBarConfig?.gothCornersEnabled ?? false) && !hasMaximizedToplevel ? _wingR : 0), _dpr) + _shadowBuffer : 0
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
|
KeyboardFocus.registerBarWindow(barWindow);
|
||||||
updateGpuTempConfig();
|
updateGpuTempConfig();
|
||||||
_updateBackgroundAlpha();
|
_updateBackgroundAlpha();
|
||||||
_updateHasMaximizedToplevel();
|
_updateHasMaximizedToplevel();
|
||||||
@@ -947,7 +954,7 @@ PanelWindow {
|
|||||||
id: barBackground
|
id: barBackground
|
||||||
barWindow: barWindow
|
barWindow: barWindow
|
||||||
axis: axis
|
axis: axis
|
||||||
barConfig: barWindow.barConfig
|
barConfig: barWindow.renderBarConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -956,8 +963,13 @@ PanelWindow {
|
|||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||||
onClicked: {
|
onClicked: {
|
||||||
const screenName = barWindow.screen?.name;
|
const screenName = barWindow.screen?.name;
|
||||||
if (screenName && PopoutManager.currentPopoutsByScreen[screenName])
|
if (!screenName)
|
||||||
|
return;
|
||||||
|
if (PopoutManager.currentPopoutsByScreen[screenName])
|
||||||
PopoutManager.closeAllPopouts();
|
PopoutManager.closeAllPopouts();
|
||||||
|
if (ModalManager.currentModalsByScreen[screenName])
|
||||||
|
ModalManager.closeAllModalsExcept(null);
|
||||||
|
TrayMenuManager.closeAllMenus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,15 +38,7 @@ DankPopout {
|
|||||||
|
|
||||||
backgroundInteractive: !anyModalOpen
|
backgroundInteractive: !anyModalOpen
|
||||||
|
|
||||||
customKeyboardFocus: {
|
customKeyboardFocus: anyModalOpen ? WlrKeyboardFocus.None : null
|
||||||
if (!shouldBeVisible)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (anyModalOpen)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (CompositorService.useHyprlandFocusGrab)
|
|
||||||
return WlrKeyboardFocus.OnDemand;
|
|
||||||
return WlrKeyboardFocus.Exclusive;
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: SystemUpdateService
|
target: SystemUpdateService
|
||||||
|
|||||||
@@ -980,21 +980,13 @@ BasePill {
|
|||||||
screen: root.parentScreen
|
screen: root.parentScreen
|
||||||
WlrLayershell.layer: root.barUsesOverlayLayer ? WlrLayershell.Overlay : WlrLayershell.Top
|
WlrLayershell.layer: root.barUsesOverlayLayer ? WlrLayershell.Overlay : WlrLayershell.Top
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: {
|
WlrLayershell.keyboardFocus: KeyboardFocus.keyboardFocus(root.menuOpen, null)
|
||||||
if (PopoutManager.screenshotActive)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (!root.menuOpen)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (CompositorService.useHyprlandFocusGrab)
|
|
||||||
return WlrKeyboardFocus.OnDemand;
|
|
||||||
return WlrKeyboardFocus.Exclusive;
|
|
||||||
}
|
|
||||||
WlrLayershell.namespace: "dms:tray-overflow-menu"
|
WlrLayershell.namespace: "dms:tray-overflow-menu"
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
HyprlandFocusGrab {
|
||||||
windows: [overflowMenu]
|
windows: [overflowMenu].concat(KeyboardFocus.barWindows)
|
||||||
active: CompositorService.useHyprlandFocusGrab && root.useOverflowPopup && root.menuOpen
|
active: root.useOverflowPopup && KeyboardFocus.wantsGrab(root.menuOpen, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -1051,32 +1043,21 @@ BasePill {
|
|||||||
"leftBar": 0,
|
"leftBar": 0,
|
||||||
"rightBar": 0
|
"rightBar": 0
|
||||||
})
|
})
|
||||||
readonly property real effectiveBarSize: root.barThickness + root.barSpacing
|
readonly property real maskX: _overflowDismissZone.x
|
||||||
|
readonly property real maskY: _overflowDismissZone.y
|
||||||
|
readonly property real maskWidth: _overflowDismissZone.width
|
||||||
|
readonly property real maskHeight: _overflowDismissZone.height
|
||||||
|
|
||||||
readonly property real maskX: {
|
DismissZone {
|
||||||
const triggeringBarX = (barPosition === 2) ? effectiveBarSize : 0;
|
id: _overflowDismissZone
|
||||||
const adjacentLeftBar = adjacentBarInfo?.leftBar ?? 0;
|
barPosition: overflowMenu.barPosition
|
||||||
return Math.max(triggeringBarX, adjacentLeftBar);
|
barX: overflowMenu.barX
|
||||||
}
|
barY: overflowMenu.barY
|
||||||
|
barWidth: overflowMenu.barWidth
|
||||||
readonly property real maskY: {
|
barHeight: overflowMenu.barHeight
|
||||||
const triggeringBarY = (barPosition === 0) ? effectiveBarSize : 0;
|
screenWidth: overflowMenu.width
|
||||||
const adjacentTopBar = adjacentBarInfo?.topBar ?? 0;
|
screenHeight: overflowMenu.height
|
||||||
return Math.max(triggeringBarY, adjacentTopBar);
|
adjacentBarInfo: overflowMenu.adjacentBarInfo
|
||||||
}
|
|
||||||
|
|
||||||
readonly property real maskWidth: {
|
|
||||||
const triggeringBarRight = (barPosition === 3) ? effectiveBarSize : 0;
|
|
||||||
const adjacentRightBar = adjacentBarInfo?.rightBar ?? 0;
|
|
||||||
const rightExclusion = Math.max(triggeringBarRight, adjacentRightBar);
|
|
||||||
return Math.max(100, width - maskX - rightExclusion);
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property real maskHeight: {
|
|
||||||
const triggeringBarBottom = (barPosition === 1) ? effectiveBarSize : 0;
|
|
||||||
const adjacentBottomBar = adjacentBarInfo?.bottomBar ?? 0;
|
|
||||||
const bottomExclusion = Math.max(triggeringBarBottom, adjacentBottomBar);
|
|
||||||
return Math.max(100, height - maskY - bottomExclusion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mask: Region {
|
mask: Region {
|
||||||
@@ -1237,13 +1218,7 @@ BasePill {
|
|||||||
fallbackOffset: 6
|
fallbackOffset: 6
|
||||||
targetColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
targetColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
targetRadius: Theme.cornerRadius
|
targetRadius: Theme.cornerRadius
|
||||||
sourceRect.antialiasing: true
|
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled
|
||||||
sourceRect.smooth: true
|
|
||||||
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && !BlurService.enabled
|
|
||||||
layer.smooth: true
|
|
||||||
layer.textureSize: Qt.size(Math.round(width * overflowMenu.dpr * 2), Math.round(height * overflowMenu.dpr * 2))
|
|
||||||
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
|
||||||
layer.samples: 4
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -1450,20 +1425,12 @@ BasePill {
|
|||||||
screen: menuRoot.parentScreen
|
screen: menuRoot.parentScreen
|
||||||
WlrLayershell.layer: root.barUsesOverlayLayer ? WlrLayershell.Overlay : WlrLayershell.Top
|
WlrLayershell.layer: root.barUsesOverlayLayer ? WlrLayershell.Overlay : WlrLayershell.Top
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: {
|
WlrLayershell.keyboardFocus: KeyboardFocus.keyboardFocus(menuRoot.showMenu, null)
|
||||||
if (PopoutManager.screenshotActive)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (!menuRoot.showMenu)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (CompositorService.useHyprlandFocusGrab)
|
|
||||||
return WlrKeyboardFocus.OnDemand;
|
|
||||||
return WlrKeyboardFocus.Exclusive;
|
|
||||||
}
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
HyprlandFocusGrab {
|
||||||
windows: [menuWindow]
|
windows: [menuWindow].concat(KeyboardFocus.barWindows)
|
||||||
active: CompositorService.useHyprlandFocusGrab && menuRoot.showMenu
|
active: KeyboardFocus.wantsGrab(menuRoot.showMenu, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@@ -1502,32 +1469,21 @@ BasePill {
|
|||||||
"leftBar": 0,
|
"leftBar": 0,
|
||||||
"rightBar": 0
|
"rightBar": 0
|
||||||
})
|
})
|
||||||
readonly property real effectiveBarSize: root.barThickness + root.barSpacing
|
readonly property real maskX: _menuDismissZone.x
|
||||||
|
readonly property real maskY: _menuDismissZone.y
|
||||||
|
readonly property real maskWidth: _menuDismissZone.width
|
||||||
|
readonly property real maskHeight: _menuDismissZone.height
|
||||||
|
|
||||||
readonly property real maskX: {
|
DismissZone {
|
||||||
const triggeringBarX = (barPosition === 2) ? effectiveBarSize : 0;
|
id: _menuDismissZone
|
||||||
const adjacentLeftBar = adjacentBarInfo?.leftBar ?? 0;
|
barPosition: menuWindow.barPosition
|
||||||
return Math.max(triggeringBarX, adjacentLeftBar);
|
barX: menuWindow.barX
|
||||||
}
|
barY: menuWindow.barY
|
||||||
|
barWidth: menuWindow.barWidth
|
||||||
readonly property real maskY: {
|
barHeight: menuWindow.barHeight
|
||||||
const triggeringBarY = (barPosition === 0) ? effectiveBarSize : 0;
|
screenWidth: menuWindow.width
|
||||||
const adjacentTopBar = adjacentBarInfo?.topBar ?? 0;
|
screenHeight: menuWindow.height
|
||||||
return Math.max(triggeringBarY, adjacentTopBar);
|
adjacentBarInfo: menuWindow.adjacentBarInfo
|
||||||
}
|
|
||||||
|
|
||||||
readonly property real maskWidth: {
|
|
||||||
const triggeringBarRight = (barPosition === 3) ? effectiveBarSize : 0;
|
|
||||||
const adjacentRightBar = adjacentBarInfo?.rightBar ?? 0;
|
|
||||||
const rightExclusion = Math.max(triggeringBarRight, adjacentRightBar);
|
|
||||||
return Math.max(100, width - maskX - rightExclusion);
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property real maskHeight: {
|
|
||||||
const triggeringBarBottom = (barPosition === 1) ? effectiveBarSize : 0;
|
|
||||||
const adjacentBottomBar = adjacentBarInfo?.bottomBar ?? 0;
|
|
||||||
const bottomExclusion = Math.max(triggeringBarBottom, adjacentBottomBar);
|
|
||||||
return Math.max(100, height - maskY - bottomExclusion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mask: Region {
|
mask: Region {
|
||||||
@@ -1689,11 +1645,7 @@ BasePill {
|
|||||||
fallbackOffset: 6
|
fallbackOffset: 6
|
||||||
targetColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
targetColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
targetRadius: Theme.cornerRadius
|
targetRadius: Theme.cornerRadius
|
||||||
sourceRect.antialiasing: true
|
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled
|
||||||
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && !BlurService.enabled
|
|
||||||
layer.smooth: true
|
|
||||||
layer.textureSize: Qt.size(Math.round(width * menuWindow.dpr), Math.round(height * menuWindow.dpr))
|
|
||||||
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ Item {
|
|||||||
borderColor: volumePanel.border.color
|
borderColor: volumePanel.border.color
|
||||||
borderWidth: volumePanel.border.width
|
borderWidth: volumePanel.border.width
|
||||||
shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25
|
shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25
|
||||||
shadowEnabled: Theme.elevationEnabled && !BlurService.enabled
|
shadowEnabled: Theme.elevationEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -272,7 +272,7 @@ Item {
|
|||||||
borderColor: audioDevicesPanel.border.color
|
borderColor: audioDevicesPanel.border.color
|
||||||
borderWidth: audioDevicesPanel.border.width
|
borderWidth: audioDevicesPanel.border.width
|
||||||
shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25
|
shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25
|
||||||
shadowEnabled: Theme.elevationEnabled && !BlurService.enabled
|
shadowEnabled: Theme.elevationEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -444,7 +444,7 @@ Item {
|
|||||||
borderColor: playersPanel.border.color
|
borderColor: playersPanel.border.color
|
||||||
borderWidth: playersPanel.border.width
|
borderWidth: playersPanel.border.width
|
||||||
shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25
|
shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25
|
||||||
shadowEnabled: Theme.elevationEnabled && !BlurService.enabled
|
shadowEnabled: Theme.elevationEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
|||||||
@@ -186,13 +186,36 @@ Variants {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const presented = dock.visible && (dock.reveal || slideXAnimation.running || slideYAnimation.running) && dock.hasApps;
|
||||||
|
const phase = !presented ? "hidden" : ((!dock.reveal && (slideXAnimation.running || slideYAnimation.running)) ? "closing" : ((slideXAnimation.running || slideYAnimation.running) ? "opening" : "open"));
|
||||||
|
const bodyX = dock._dockWindowOriginX() + dockBackground.x + dockContainer.x + dockMouseArea.x + dockCore.x;
|
||||||
|
const bodyY = dock._dockWindowOriginY() + dockBackground.y + dockContainer.y + dockMouseArea.y + dockCore.y;
|
||||||
|
const bodyW = dock.hasApps ? dockBackground.width : 0;
|
||||||
|
const bodyH = dock.hasApps ? dockBackground.height : 0;
|
||||||
ConnectedModeState.setDockState(dock._dockScreenName, {
|
ConnectedModeState.setDockState(dock._dockScreenName, {
|
||||||
"reveal": dock.visible && (dock.reveal || slideXAnimation.running || slideYAnimation.running) && dock.hasApps,
|
"kind": "dock",
|
||||||
|
"screenName": dock._dockScreenName,
|
||||||
|
"phase": phase,
|
||||||
|
"visible": presented,
|
||||||
|
"presented": presented,
|
||||||
|
"reveal": presented,
|
||||||
"barSide": dock.connectedBarSide,
|
"barSide": dock.connectedBarSide,
|
||||||
"bodyX": dock._dockWindowOriginX() + dockBackground.x + dockContainer.x + dockMouseArea.x + dockCore.x,
|
"bodyRect": {
|
||||||
"bodyY": dock._dockWindowOriginY() + dockBackground.y + dockContainer.y + dockMouseArea.y + dockCore.y,
|
"x": bodyX,
|
||||||
"bodyW": dock.hasApps ? dockBackground.width : 0,
|
"y": bodyY,
|
||||||
"bodyH": dock.hasApps ? dockBackground.height : 0,
|
"width": bodyW,
|
||||||
|
"height": bodyH
|
||||||
|
},
|
||||||
|
"animationOffset": {
|
||||||
|
"x": dockSlide.x,
|
||||||
|
"y": dockSlide.y
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"opacity": Theme.connectedSurfaceColor.a,
|
||||||
|
"bodyX": bodyX,
|
||||||
|
"bodyY": bodyY,
|
||||||
|
"bodyW": bodyW,
|
||||||
|
"bodyH": bodyH,
|
||||||
"slideX": dockSlide.x,
|
"slideX": dockSlide.x,
|
||||||
"slideY": dockSlide.y
|
"slideY": dockSlide.y
|
||||||
});
|
});
|
||||||
@@ -724,16 +747,36 @@ Variants {
|
|||||||
onHeightChanged: dock._syncDockChromeState()
|
onHeightChanged: dock._syncDockChromeState()
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectedShape {
|
Item {
|
||||||
|
id: dockConnectedChrome
|
||||||
visible: Theme.isConnectedEffect && dock.reveal && !SettingsData.connectedFrameModeActive
|
visible: Theme.isConnectedEffect && dock.reveal && !SettingsData.connectedFrameModeActive
|
||||||
barSide: dock.connectedBarSide
|
readonly property real extraLeft: dock.isVertical ? 0 : Theme.connectedCornerRadius
|
||||||
bodyWidth: dockBackground.width
|
readonly property real extraTop: dock.isVertical ? Theme.connectedCornerRadius : 0
|
||||||
bodyHeight: dockBackground.height
|
readonly property real bodyRadius: dock.surfaceRadius
|
||||||
connectorRadius: Theme.connectedCornerRadius
|
readonly property bool barTop: dock.connectedBarSide === "top"
|
||||||
surfaceRadius: dock.surfaceRadius
|
readonly property bool barBottom: dock.connectedBarSide === "bottom"
|
||||||
fillColor: dock.surfaceColor
|
readonly property bool barLeft: dock.connectedBarSide === "left"
|
||||||
x: dockBackground.x - bodyX
|
readonly property bool barRight: dock.connectedBarSide === "right"
|
||||||
y: dockBackground.y - bodyY
|
|
||||||
|
x: dockBackground.x - extraLeft
|
||||||
|
y: dockBackground.y - extraTop
|
||||||
|
width: dockBackground.width + extraLeft * 2
|
||||||
|
height: dockBackground.height + extraTop * 2
|
||||||
|
|
||||||
|
ShaderEffect {
|
||||||
|
anchors.fill: parent
|
||||||
|
fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/connected_chrome.frag.qsb")
|
||||||
|
|
||||||
|
property real widthPx: width
|
||||||
|
property real heightPx: height
|
||||||
|
property vector4d surfaceColor: Qt.vector4d(dock.surfaceColor.r, dock.surfaceColor.g, dock.surfaceColor.b, dock.surfaceColor.a)
|
||||||
|
property vector4d shadowColor: Qt.vector4d(0, 0, 0, 0)
|
||||||
|
property vector4d shadowParam: Qt.vector4d(0, 0, 0, 0)
|
||||||
|
property vector4d ambientParam: Qt.vector4d(0, 0, 0, 0)
|
||||||
|
property vector4d bodyRect: Qt.vector4d(dockConnectedChrome.extraLeft, dockConnectedChrome.extraTop, dockBackground.width, dockBackground.height)
|
||||||
|
property vector4d cornerRadius: Qt.vector4d(dockConnectedChrome.barTop || dockConnectedChrome.barLeft ? 0 : dockConnectedChrome.bodyRadius, dockConnectedChrome.barTop || dockConnectedChrome.barRight ? 0 : dockConnectedChrome.bodyRadius, dockConnectedChrome.barBottom || dockConnectedChrome.barRight ? 0 : dockConnectedChrome.bodyRadius, dockConnectedChrome.barBottom || dockConnectedChrome.barLeft ? 0 : dockConnectedChrome.bodyRadius)
|
||||||
|
property vector4d edgeParam: Qt.vector4d(dockConnectedChrome.barTop ? 0 : (dockConnectedChrome.barBottom ? 1 : (dockConnectedChrome.barLeft ? 2 : 3)), Theme.connectedCornerRadius, 0, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Shape {
|
Shape {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Effects
|
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
|
||||||
|
// Frame perimeter ring with rounded cutout (SDF).
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
@@ -16,39 +16,14 @@ Item {
|
|||||||
required property real cutoutRadius
|
required property real cutoutRadius
|
||||||
property color borderColor: Qt.rgba(SettingsData.effectiveFrameColor.r, SettingsData.effectiveFrameColor.g, SettingsData.effectiveFrameColor.b, SettingsData.frameOpacity)
|
property color borderColor: Qt.rgba(SettingsData.effectiveFrameColor.r, SettingsData.effectiveFrameColor.g, SettingsData.effectiveFrameColor.b, SettingsData.frameOpacity)
|
||||||
|
|
||||||
Rectangle {
|
ShaderEffect {
|
||||||
id: borderRect
|
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
// Bake frameOpacity into the color alpha rather than using the `opacity` property
|
fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/frame_arc.frag.qsb")
|
||||||
color: root.borderColor
|
|
||||||
|
|
||||||
layer.enabled: true
|
property real widthPx: width
|
||||||
layer.effect: MultiEffect {
|
property real heightPx: height
|
||||||
maskSource: cutoutMask
|
property real cutoutRadius: root.cutoutRadius
|
||||||
maskEnabled: true
|
property vector4d cutout: Qt.vector4d(root.cutoutLeftInset, root.cutoutTopInset, root.width - root.cutoutRightInset, root.height - root.cutoutBottomInset)
|
||||||
maskInverted: true
|
property vector4d surfaceColor: Qt.vector4d(root.borderColor.r, root.borderColor.g, root.borderColor.b, root.borderColor.a)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,7 @@ Rectangle {
|
|||||||
height: baseCardHeight + contentItem.extraHeight
|
height: baseCardHeight + contentItem.extraHeight
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
clip: false
|
clip: false
|
||||||
readonly property bool shadowsAllowed: Theme.elevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !BlurService.enabled
|
readonly property bool shadowsAllowed: Theme.elevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
||||||
|
|
||||||
ElevationShadow {
|
ElevationShadow {
|
||||||
id: shadowLayer
|
id: shadowLayer
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ Rectangle {
|
|||||||
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: connectedFrameMode ? Theme.connectedSurfaceRadius : 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" && !BlurService.enabled
|
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
|
||||||
readonly property real baseShadowBlurPx: (shadowElevation && shadowElevation.blurPx !== undefined) ? shadowElevation.blurPx : 4
|
readonly property real baseShadowBlurPx: (shadowElevation && shadowElevation.blurPx !== undefined) ? shadowElevation.blurPx : 4
|
||||||
readonly property real hoverShadowBlurBoost: cardHoverHandler.hovered ? Math.min(2, baseShadowBlurPx * 0.25) : 0
|
readonly property real hoverShadowBlurBoost: cardHoverHandler.hovered ? Math.min(2, baseShadowBlurPx * 0.25) : 0
|
||||||
|
|||||||
@@ -641,21 +641,15 @@ PanelWindow {
|
|||||||
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 && !win.connectedFrameMode
|
shadowEnabled: !win._isDestroying && win.screenValid && content.shadowsAllowed && !win.connectedFrameMode
|
||||||
layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr))
|
|
||||||
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
|
||||||
|
|
||||||
sourceRect.anchors.fill: undefined
|
sourceX: content.shadowRenderPadding + content.cardInset
|
||||||
sourceRect.x: content.shadowRenderPadding + content.cardInset
|
sourceY: content.shadowRenderPadding + content.cardInset
|
||||||
sourceRect.y: content.shadowRenderPadding + content.cardInset
|
sourceWidth: Math.max(0, content.width - (content.cardInset * 2))
|
||||||
sourceRect.width: Math.max(0, content.width - (content.cardInset * 2))
|
sourceHeight: Math.max(0, content.height - (content.cardInset * 2))
|
||||||
sourceRect.height: Math.max(0, content.height - (content.cardInset * 2))
|
targetRadius: win.connectedFrameMode ? Theme.connectedSurfaceRadius : Theme.cornerRadius
|
||||||
sourceRect.radius: win.connectedFrameMode ? Theme.connectedSurfaceRadius : Theme.cornerRadius
|
targetColor: win.connectedFrameMode ? Theme.floatingSurface : Theme.readableSurface
|
||||||
sourceRect.color: win.connectedFrameMode ? Theme.floatingSurface : Theme.readableSurface
|
borderColor: win.notificationData && win.notificationData.urgency === NotificationUrgency.Critical ? Theme.withAlpha(Theme.primary, 0.3) : Theme.withAlpha(Theme.outline, 0.08)
|
||||||
sourceRect.antialiasing: true
|
borderWidth: win.notificationData && win.notificationData.urgency === NotificationUrgency.Critical ? 2 : 0
|
||||||
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.width: notificationData && notificationData.urgency === NotificationUrgency.Critical ? 2 : 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep critical accent outside shadow rendering so connected mode still shows it.
|
// Keep critical accent outside shadow rendering so connected mode still shows it.
|
||||||
|
|||||||
@@ -513,13 +513,30 @@ QtObject {
|
|||||||
ConnectedModeState.clearNotificationState(screenName);
|
ConnectedModeState.clearNotificationState(screenName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const bodyRect = {
|
||||||
|
x: minX,
|
||||||
|
y: minY,
|
||||||
|
width: maxXEnd - minX,
|
||||||
|
height: maxYEnd - minY
|
||||||
|
};
|
||||||
ConnectedModeState.setNotificationState(screenName, {
|
ConnectedModeState.setNotificationState(screenName, {
|
||||||
|
kind: "notification",
|
||||||
|
screenName: screenName,
|
||||||
|
phase: "open",
|
||||||
visible: true,
|
visible: true,
|
||||||
|
presented: true,
|
||||||
barSide: notifBarSide,
|
barSide: notifBarSide,
|
||||||
|
bodyRect: bodyRect,
|
||||||
|
animationOffset: {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
},
|
||||||
|
scale: 1,
|
||||||
|
opacity: Theme.connectedSurfaceColor.a,
|
||||||
bodyX: minX,
|
bodyX: minX,
|
||||||
bodyY: minY,
|
bodyY: minY,
|
||||||
bodyW: maxXEnd - minX,
|
bodyW: bodyRect.width,
|
||||||
bodyH: maxYEnd - minY,
|
bodyH: bodyRect.height,
|
||||||
omitStartConnector: _notificationOmitStartConnector(),
|
omitStartConnector: _notificationOmitStartConnector(),
|
||||||
omitEndConnector: _notificationOmitEndConnector()
|
omitEndConnector: _notificationOmitEndConnector()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ Item {
|
|||||||
property bool conditionVisible: true
|
property bool conditionVisible: true
|
||||||
property bool _visibilityOverride: false
|
property bool _visibilityOverride: false
|
||||||
property bool _visibilityOverrideValue: true
|
property bool _visibilityOverrideValue: true
|
||||||
|
readonly property bool _barRevealed: blurBarWindow?.barRevealed ?? true
|
||||||
|
|
||||||
readonly property bool effectiveVisible: {
|
readonly property bool effectiveVisible: {
|
||||||
if (_visibilityOverride)
|
if (_visibilityOverride)
|
||||||
@@ -122,6 +123,11 @@ Item {
|
|||||||
conditionVisible = true;
|
conditionVisible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
on_BarRevealedChanged: {
|
||||||
|
if (_barRevealed && visibilityCommand && !_visibilityOverride)
|
||||||
|
checkVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
onVisibilityIntervalChanged: {
|
onVisibilityIntervalChanged: {
|
||||||
if (visibilityInterval > 0 && visibilityCommand) {
|
if (visibilityInterval > 0 && visibilityCommand) {
|
||||||
visibilityTimer.restart();
|
visibilityTimer.restart();
|
||||||
@@ -134,7 +140,7 @@ Item {
|
|||||||
id: visibilityTimer
|
id: visibilityTimer
|
||||||
interval: root.visibilityInterval * 1000
|
interval: root.visibilityInterval * 1000
|
||||||
repeat: true
|
repeat: true
|
||||||
running: root.visibilityInterval > 0 && root.visibilityCommand !== ""
|
running: root.visibilityInterval > 0 && root.visibilityCommand !== "" && root._barRevealed && !root._visibilityOverride
|
||||||
onTriggered: root.checkVisibility()
|
onTriggered: root.checkVisibility()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -152,6 +152,9 @@ Item {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
readonly property var entryActionKeys: ["pin", "edit", "delete"]
|
||||||
|
readonly property var entryActionLabels: [I18n.tr("Pin"), I18n.tr("Edit"), I18n.tr("Delete")]
|
||||||
|
|
||||||
function getMaxHistoryText(value) {
|
function getMaxHistoryText(value) {
|
||||||
if (value <= 0)
|
if (value <= 0)
|
||||||
return "∞";
|
return "∞";
|
||||||
@@ -187,6 +190,29 @@ Item {
|
|||||||
return value.toString();
|
return value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function visibleEntryActionKeys() {
|
||||||
|
return SettingsData.clipboardVisibleEntryActions || ["pin", "edit", "delete"];
|
||||||
|
}
|
||||||
|
|
||||||
|
function visibleEntryActionLabels() {
|
||||||
|
const visibleKeys = visibleEntryActionKeys();
|
||||||
|
return entryActionKeys.map((key, index) => visibleKeys.includes(key) ? entryActionLabels[index] : null).filter(label => label !== null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setVisibleEntryAction(index, selected) {
|
||||||
|
const actionKey = entryActionKeys[index];
|
||||||
|
if (!actionKey)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let actions = visibleEntryActionKeys().slice();
|
||||||
|
if (selected && !actions.includes(actionKey)) {
|
||||||
|
actions.push(actionKey);
|
||||||
|
} else if (!selected && actions.includes(actionKey)) {
|
||||||
|
actions = actions.filter(action => action !== actionKey);
|
||||||
|
}
|
||||||
|
SettingsData.set("clipboardVisibleEntryActions", actions);
|
||||||
|
}
|
||||||
|
|
||||||
function loadConfig() {
|
function loadConfig() {
|
||||||
configLoaded = false;
|
configLoaded = false;
|
||||||
configError = false;
|
configError = false;
|
||||||
@@ -437,6 +463,24 @@ Item {
|
|||||||
checked: SettingsData.clipboardEnterToPaste
|
checked: SettingsData.clipboardEnterToPaste
|
||||||
onToggled: checked => SettingsData.set("clipboardEnterToPaste", checked)
|
onToggled: checked => SettingsData.set("clipboardEnterToPaste", checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsButtonGroupRow {
|
||||||
|
tab: "clipboard"
|
||||||
|
tags: ["clipboard", "actions", "buttons", "hide", "density", "pin", "edit", "delete"]
|
||||||
|
settingKey: "clipboardVisibleEntryActions"
|
||||||
|
text: I18n.tr("Visible Entry Actions")
|
||||||
|
description: I18n.tr("Choose which action buttons appear on clipboard entries")
|
||||||
|
selectionMode: "multi"
|
||||||
|
model: root.entryActionLabels
|
||||||
|
currentSelection: root.visibleEntryActionLabels()
|
||||||
|
checkEnabled: false
|
||||||
|
buttonHeight: 28
|
||||||
|
minButtonWidth: 56
|
||||||
|
buttonPadding: Theme.spacingS
|
||||||
|
textSize: Theme.fontSizeSmall
|
||||||
|
spacing: 1
|
||||||
|
onSelectionChanged: (index, selected) => root.setVisibleEntryAction(index, selected)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ Item {
|
|||||||
SettingsCard {
|
SettingsCard {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
tags: ["niri", "layout", "gaps", "radius", "window", "border"]
|
tags: ["niri", "layout", "gaps", "radius", "window", "border"]
|
||||||
title: I18n.tr("Niri Layout Overrides").replace("Niri", "niri")
|
title: I18n.tr("Niri Layout Overrides")
|
||||||
settingKey: "niriLayout"
|
settingKey: "niriLayout"
|
||||||
iconName: "crop_square"
|
iconName: "layers"
|
||||||
visible: CompositorService.isNiri
|
visible: CompositorService.isNiri
|
||||||
|
|
||||||
SettingsToggleRow {
|
SettingsToggleRow {
|
||||||
|
|||||||
@@ -796,18 +796,81 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsCard {
|
||||||
|
tab: "appearance"
|
||||||
|
iconName: "opacity"
|
||||||
|
title: I18n.tr("Opacity")
|
||||||
|
settingKey: "barTransparency"
|
||||||
|
visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
id: barTransparencySlider
|
||||||
|
visible: !SettingsData.frameEnabled
|
||||||
|
text: I18n.tr("Bar Opacity")
|
||||||
|
description: I18n.tr("Controls opacity of the bar background")
|
||||||
|
value: (selectedBarConfig?.transparency ?? 1.0) * 100
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
unit: "%"
|
||||||
|
defaultValue: 100
|
||||||
|
onSliderDragFinished: finalValue => {
|
||||||
|
SettingsData.updateBarConfig(selectedBarId, {
|
||||||
|
transparency: finalValue / 100
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: barTransparencySlider
|
||||||
|
property: "value"
|
||||||
|
value: (selectedBarConfig?.transparency ?? 1.0) * 100
|
||||||
|
restoreMode: Binding.RestoreBinding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
id: widgetTransparencySlider
|
||||||
|
text: I18n.tr("Widget Opacity")
|
||||||
|
description: I18n.tr("Controls opacity of widget backgrounds")
|
||||||
|
value: (selectedBarConfig?.widgetTransparency ?? 1.0) * 100
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
unit: "%"
|
||||||
|
defaultValue: 100
|
||||||
|
onSliderDragFinished: finalValue => {
|
||||||
|
SettingsData.updateBarConfig(selectedBarId, {
|
||||||
|
widgetTransparency: finalValue / 100
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: widgetTransparencySlider
|
||||||
|
property: "value"
|
||||||
|
value: (selectedBarConfig?.widgetTransparency ?? 1.0) * 100
|
||||||
|
restoreMode: Binding.RestoreBinding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SettingsControlledByFrame {
|
SettingsControlledByFrame {
|
||||||
visible: !dankBarTab.appearanceOnly && SettingsData.frameEnabled
|
visible: SettingsData.frameEnabled
|
||||||
|
parentModal: dankBarTab.parentModal
|
||||||
|
settingLabel: I18n.tr("Bar Opacity")
|
||||||
|
reason: I18n.tr("Managed by Frame")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsControlledByFrame {
|
||||||
|
visible: dankBarTab.appearanceOnly && SettingsData.frameEnabled
|
||||||
parentModal: dankBarTab.parentModal
|
parentModal: dankBarTab.parentModal
|
||||||
settingLabel: I18n.tr("Bar spacing and size")
|
settingLabel: I18n.tr("Bar spacing and size")
|
||||||
reason: I18n.tr("Managed by Frame")
|
reason: I18n.tr("Managed by Frame")
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
|
tab: "appearance"
|
||||||
iconName: "space_bar"
|
iconName: "space_bar"
|
||||||
title: I18n.tr("Spacing")
|
title: I18n.tr("Spacing")
|
||||||
settingKey: "barSpacing"
|
settingKey: "barSpacing"
|
||||||
visible: !dankBarTab.appearanceOnly && (selectedBarConfig?.enabled ?? false) && !SettingsData.frameEnabled
|
visible: dankBarTab.appearanceOnly && (selectedBarConfig?.enabled ?? false) && !SettingsData.frameEnabled
|
||||||
|
|
||||||
SettingsSliderRow {
|
SettingsSliderRow {
|
||||||
id: edgeSpacingSlider
|
id: edgeSpacingSlider
|
||||||
@@ -956,68 +1019,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsCard {
|
|
||||||
tab: "appearance"
|
|
||||||
iconName: "opacity"
|
|
||||||
title: I18n.tr("Transparency")
|
|
||||||
settingKey: "barTransparency"
|
|
||||||
visible: dankBarTab.appearanceOnly && selectedBarConfig?.enabled
|
|
||||||
|
|
||||||
SettingsSliderRow {
|
|
||||||
id: barTransparencySlider
|
|
||||||
visible: !SettingsData.frameEnabled
|
|
||||||
text: I18n.tr("Bar Transparency")
|
|
||||||
description: I18n.tr("Opacity of the bar background")
|
|
||||||
value: (selectedBarConfig?.transparency ?? 1.0) * 100
|
|
||||||
minimum: 0
|
|
||||||
maximum: 100
|
|
||||||
unit: "%"
|
|
||||||
defaultValue: 100
|
|
||||||
onSliderDragFinished: finalValue => {
|
|
||||||
SettingsData.updateBarConfig(selectedBarId, {
|
|
||||||
transparency: finalValue / 100
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Binding {
|
|
||||||
target: barTransparencySlider
|
|
||||||
property: "value"
|
|
||||||
value: (selectedBarConfig?.transparency ?? 1.0) * 100
|
|
||||||
restoreMode: Binding.RestoreBinding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsSliderRow {
|
|
||||||
id: widgetTransparencySlider
|
|
||||||
text: I18n.tr("Widget Transparency")
|
|
||||||
description: I18n.tr("Opacity of widget backgrounds")
|
|
||||||
value: (selectedBarConfig?.widgetTransparency ?? 1.0) * 100
|
|
||||||
minimum: 0
|
|
||||||
maximum: 100
|
|
||||||
unit: "%"
|
|
||||||
defaultValue: 100
|
|
||||||
onSliderDragFinished: finalValue => {
|
|
||||||
SettingsData.updateBarConfig(selectedBarId, {
|
|
||||||
widgetTransparency: finalValue / 100
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Binding {
|
|
||||||
target: widgetTransparencySlider
|
|
||||||
property: "value"
|
|
||||||
value: (selectedBarConfig?.widgetTransparency ?? 1.0) * 100
|
|
||||||
restoreMode: Binding.RestoreBinding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsControlledByFrame {
|
|
||||||
visible: SettingsData.frameEnabled
|
|
||||||
parentModal: dankBarTab.parentModal
|
|
||||||
settingLabel: I18n.tr("Bar Transparency")
|
|
||||||
reason: I18n.tr("Managed by Frame")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsSliderCard {
|
SettingsSliderCard {
|
||||||
id: fontScaleSliderCard
|
id: fontScaleSliderCard
|
||||||
tab: "appearance"
|
tab: "appearance"
|
||||||
@@ -1358,7 +1359,7 @@ Item {
|
|||||||
SettingsSliderRow {
|
SettingsSliderRow {
|
||||||
id: borderOpacitySlider
|
id: borderOpacitySlider
|
||||||
text: I18n.tr("Opacity")
|
text: I18n.tr("Opacity")
|
||||||
description: I18n.tr("Transparency of the border")
|
description: I18n.tr("Controls opacity of the border")
|
||||||
value: (selectedBarConfig?.borderOpacity ?? 1.0) * 100
|
value: (selectedBarConfig?.borderOpacity ?? 1.0) * 100
|
||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: 100
|
maximum: 100
|
||||||
@@ -1453,7 +1454,7 @@ Item {
|
|||||||
SettingsSliderRow {
|
SettingsSliderRow {
|
||||||
id: widgetOutlineOpacitySlider
|
id: widgetOutlineOpacitySlider
|
||||||
text: I18n.tr("Opacity")
|
text: I18n.tr("Opacity")
|
||||||
description: I18n.tr("Transparency of the widget outline")
|
description: I18n.tr("Controls opacity of the widget outline")
|
||||||
value: (selectedBarConfig?.widgetOutlineOpacity ?? 1.0) * 100
|
value: (selectedBarConfig?.widgetOutlineOpacity ?? 1.0) * 100
|
||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: 100
|
maximum: 100
|
||||||
@@ -1562,7 +1563,7 @@ Item {
|
|||||||
SettingsSliderRow {
|
SettingsSliderRow {
|
||||||
visible: shadowCard.shadowActive
|
visible: shadowCard.shadowActive
|
||||||
text: I18n.tr("Opacity")
|
text: I18n.tr("Opacity")
|
||||||
description: I18n.tr("Transparency of the shadow layer")
|
description: I18n.tr("Controls opacity of the shadow layer")
|
||||||
minimum: 10
|
minimum: 10
|
||||||
maximum: 100
|
maximum: 100
|
||||||
unit: "%"
|
unit: "%"
|
||||||
|
|||||||
@@ -643,19 +643,19 @@ Item {
|
|||||||
SettingsControlledByFrame {
|
SettingsControlledByFrame {
|
||||||
visible: root.connectedFrameModeActive
|
visible: root.connectedFrameModeActive
|
||||||
parentModal: root.parentModal
|
parentModal: root.parentModal
|
||||||
settingLabel: I18n.tr("Dock margin, transparency, and border")
|
settingLabel: I18n.tr("Dock margin, opacity, and border")
|
||||||
reason: I18n.tr("Managed by Frame in Connected Mode")
|
reason: I18n.tr("Managed by Frame in Connected Mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
iconName: "opacity"
|
iconName: "opacity"
|
||||||
title: I18n.tr("Transparency")
|
title: I18n.tr("Opacity")
|
||||||
settingKey: "dockTransparency"
|
settingKey: "dockTransparency"
|
||||||
visible: !root.connectedFrameModeActive
|
visible: !root.connectedFrameModeActive
|
||||||
|
|
||||||
SettingsSliderRow {
|
SettingsSliderRow {
|
||||||
text: I18n.tr("Dock Transparency")
|
text: I18n.tr("Dock Opacity")
|
||||||
value: Math.round(SettingsData.dockTransparency * 100)
|
value: Math.round(SettingsData.dockTransparency * 100)
|
||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: 100
|
maximum: 100
|
||||||
|
|||||||
@@ -1639,7 +1639,7 @@ Item {
|
|||||||
SettingsControlledByFrame {
|
SettingsControlledByFrame {
|
||||||
visible: themeColorsTab.connectedFrameModeActive
|
visible: themeColorsTab.connectedFrameModeActive
|
||||||
parentModal: themeColorsTab.parentModal
|
parentModal: themeColorsTab.parentModal
|
||||||
settingLabel: I18n.tr("Transparency")
|
settingLabel: I18n.tr("Surface Opacity")
|
||||||
reason: I18n.tr("Managed by Frame in Connected Mode")
|
reason: I18n.tr("Managed by Frame in Connected Mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1647,8 +1647,8 @@ Item {
|
|||||||
tab: "theme"
|
tab: "theme"
|
||||||
tags: ["surface", "popup", "transparency", "opacity", "modal"]
|
tags: ["surface", "popup", "transparency", "opacity", "modal"]
|
||||||
settingKey: "popupTransparency"
|
settingKey: "popupTransparency"
|
||||||
text: I18n.tr("Transparency")
|
text: I18n.tr("Surface Opacity")
|
||||||
description: I18n.tr("Controls opacity of all popouts, modals, and their content layers")
|
description: I18n.tr("Controls opacity of shell surfaces, popouts, and modals")
|
||||||
visible: !themeColorsTab.connectedFrameModeActive
|
visible: !themeColorsTab.connectedFrameModeActive
|
||||||
value: Math.round(SettingsData.popupTransparency * 100)
|
value: Math.round(SettingsData.popupTransparency * 100)
|
||||||
minimum: 0
|
minimum: 0
|
||||||
@@ -1671,6 +1671,113 @@ Item {
|
|||||||
defaultValue: 12
|
defaultValue: 12
|
||||||
onSliderValueChanged: newValue => SettingsData.setCornerRadius(newValue)
|
onSliderValueChanged: newValue => SettingsData.setCornerRadius(newValue)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsCard {
|
||||||
|
tab: "theme"
|
||||||
|
tags: ["blur", "background", "transparency", "glass", "frosted"]
|
||||||
|
title: I18n.tr("Background Blur")
|
||||||
|
settingKey: "blurEnabled"
|
||||||
|
iconName: "blur_on"
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
tab: "theme"
|
||||||
|
tags: ["blur", "background", "transparency", "glass", "frosted"]
|
||||||
|
settingKey: "blurEnabled"
|
||||||
|
text: I18n.tr("Background Blur")
|
||||||
|
description: !BlurService.available ? I18n.tr("Your compositor does not support background blur (ext-background-effect-v1)") : I18n.tr("Blur the background behind bars, popouts, modals, and notifications. Requires compositor support. Adjust Opacity accordingly.")
|
||||||
|
checked: SettingsData.blurEnabled ?? false
|
||||||
|
enabled: BlurService.available
|
||||||
|
onToggled: checked => SettingsData.set("blurEnabled", checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
tab: "theme"
|
||||||
|
tags: ["blur", "foreground", "layers", "contrast", "glass", "frosted"]
|
||||||
|
settingKey: "blurForegroundLayers"
|
||||||
|
text: I18n.tr("Foreground Layers")
|
||||||
|
description: I18n.tr("Show foreground surfaces on blurred panels for stronger contrast")
|
||||||
|
checked: SettingsData.blurForegroundLayers ?? true
|
||||||
|
visible: BlurService.available && (SettingsData.blurEnabled ?? false)
|
||||||
|
enabled: BlurService.available
|
||||||
|
onToggled: checked => SettingsData.set("blurForegroundLayers", checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
tab: "theme"
|
||||||
|
tags: ["blur", "foreground", "layers", "outline", "border", "cards", "widgets", "notifications", "control center"]
|
||||||
|
settingKey: "blurLayerOutlineOpacity"
|
||||||
|
text: I18n.tr("Layer Outline Opacity")
|
||||||
|
description: I18n.tr("Controls outlines around blurred foreground cards, pills, and notification cards")
|
||||||
|
visible: BlurService.available && (SettingsData.blurEnabled ?? false)
|
||||||
|
value: Math.round((SettingsData.blurLayerOutlineOpacity ?? 0.12) * 100)
|
||||||
|
minimum: 0
|
||||||
|
maximum: 40
|
||||||
|
unit: "%"
|
||||||
|
defaultValue: 12
|
||||||
|
onSliderValueChanged: newValue => SettingsData.set("blurLayerOutlineOpacity", newValue / 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsDropdownRow {
|
||||||
|
tab: "theme"
|
||||||
|
tags: ["blur", "border", "outline", "edge"]
|
||||||
|
settingKey: "blurBorderColor"
|
||||||
|
text: I18n.tr("Blur Border Color")
|
||||||
|
description: I18n.tr("Border color around blurred surfaces")
|
||||||
|
visible: SettingsData.blurEnabled
|
||||||
|
options: [I18n.tr("Outline", "blur border color"), I18n.tr("Primary", "blur border color"), I18n.tr("Secondary", "blur border color"), I18n.tr("Text Color", "blur border color"), I18n.tr("Custom", "blur border color")]
|
||||||
|
currentValue: {
|
||||||
|
switch (SettingsData.blurBorderColor) {
|
||||||
|
case "primary":
|
||||||
|
return I18n.tr("Primary", "blur border color");
|
||||||
|
case "secondary":
|
||||||
|
return I18n.tr("Secondary", "blur border color");
|
||||||
|
case "surfaceText":
|
||||||
|
return I18n.tr("Text Color", "blur border color");
|
||||||
|
case "custom":
|
||||||
|
return I18n.tr("Custom", "blur border color");
|
||||||
|
default:
|
||||||
|
return I18n.tr("Outline", "blur border color");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onValueChanged: value => {
|
||||||
|
if (value === I18n.tr("Primary", "blur border color")) {
|
||||||
|
SettingsData.set("blurBorderColor", "primary");
|
||||||
|
} else if (value === I18n.tr("Secondary", "blur border color")) {
|
||||||
|
SettingsData.set("blurBorderColor", "secondary");
|
||||||
|
} else if (value === I18n.tr("Text Color", "blur border color")) {
|
||||||
|
SettingsData.set("blurBorderColor", "surfaceText");
|
||||||
|
} else if (value === I18n.tr("Custom", "blur border color")) {
|
||||||
|
SettingsData.set("blurBorderColor", "custom");
|
||||||
|
openBlurBorderColorPicker();
|
||||||
|
} else {
|
||||||
|
SettingsData.set("blurBorderColor", "outline");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
tab: "theme"
|
||||||
|
tags: ["blur", "border", "opacity"]
|
||||||
|
settingKey: "blurBorderOpacity"
|
||||||
|
text: I18n.tr("Blur Border Opacity")
|
||||||
|
description: I18n.tr("Controls the outer edge of protocol-blurred windows")
|
||||||
|
visible: SettingsData.blurEnabled
|
||||||
|
value: Math.round((SettingsData.blurBorderOpacity ?? 0.35) * 100)
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
unit: "%"
|
||||||
|
defaultValue: 35
|
||||||
|
onSliderValueChanged: newValue => SettingsData.set("blurBorderOpacity", newValue / 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsCard {
|
||||||
|
tab: "theme"
|
||||||
|
tags: ["elevation", "shadow", "lift", "m3", "material"]
|
||||||
|
title: I18n.tr("Shadows")
|
||||||
|
settingKey: "m3ElevationEnabled"
|
||||||
|
iconName: "layers"
|
||||||
|
|
||||||
SettingsToggleRow {
|
SettingsToggleRow {
|
||||||
tab: "theme"
|
tab: "theme"
|
||||||
@@ -1702,7 +1809,7 @@ Item {
|
|||||||
tags: ["elevation", "shadow", "opacity", "transparency", "m3"]
|
tags: ["elevation", "shadow", "opacity", "transparency", "m3"]
|
||||||
settingKey: "m3ElevationOpacity"
|
settingKey: "m3ElevationOpacity"
|
||||||
text: I18n.tr("Shadow Opacity")
|
text: I18n.tr("Shadow Opacity")
|
||||||
description: I18n.tr("Controls the transparency of the shadow")
|
description: I18n.tr("Controls the opacity of the shadow")
|
||||||
value: SettingsData.m3ElevationOpacity ?? 30
|
value: SettingsData.m3ElevationOpacity ?? 30
|
||||||
minimum: 0
|
minimum: 0
|
||||||
maximum: 100
|
maximum: 100
|
||||||
@@ -1856,105 +1963,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsCard {
|
|
||||||
tab: "theme"
|
|
||||||
tags: ["blur", "background", "transparency", "glass", "frosted"]
|
|
||||||
title: I18n.tr("Background Blur")
|
|
||||||
settingKey: "blurEnabled"
|
|
||||||
iconName: "blur_on"
|
|
||||||
|
|
||||||
SettingsToggleRow {
|
|
||||||
tab: "theme"
|
|
||||||
tags: ["blur", "background", "transparency", "glass", "frosted"]
|
|
||||||
settingKey: "blurEnabled"
|
|
||||||
text: I18n.tr("Background Blur")
|
|
||||||
description: !BlurService.available ? I18n.tr("Your compositor does not support background blur (ext-background-effect-v1)") : I18n.tr("Blur the background behind bars, popouts, modals, and notifications. Requires compositor support and configuration.")
|
|
||||||
checked: SettingsData.blurEnabled ?? false
|
|
||||||
enabled: BlurService.available
|
|
||||||
onToggled: checked => SettingsData.set("blurEnabled", checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsToggleRow {
|
|
||||||
tab: "theme"
|
|
||||||
tags: ["blur", "foreground", "layers", "contrast", "glass", "frosted"]
|
|
||||||
settingKey: "blurForegroundLayers"
|
|
||||||
text: I18n.tr("Foreground Layers")
|
|
||||||
description: I18n.tr("Show foreground surfaces on blurred panels for stronger contrast")
|
|
||||||
checked: SettingsData.blurForegroundLayers ?? true
|
|
||||||
visible: BlurService.available && (SettingsData.blurEnabled ?? false)
|
|
||||||
enabled: BlurService.available
|
|
||||||
onToggled: checked => SettingsData.set("blurForegroundLayers", checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsSliderRow {
|
|
||||||
tab: "theme"
|
|
||||||
tags: ["blur", "foreground", "layers", "outline", "border", "cards", "widgets", "notifications", "control center"]
|
|
||||||
settingKey: "blurLayerOutlineOpacity"
|
|
||||||
text: I18n.tr("Layer Outline Opacity")
|
|
||||||
description: I18n.tr("Controls outlines around blurred foreground cards, pills, and notification cards")
|
|
||||||
visible: BlurService.available && (SettingsData.blurEnabled ?? false)
|
|
||||||
value: Math.round((SettingsData.blurLayerOutlineOpacity ?? 0.12) * 100)
|
|
||||||
minimum: 0
|
|
||||||
maximum: 40
|
|
||||||
unit: "%"
|
|
||||||
defaultValue: 12
|
|
||||||
onSliderValueChanged: newValue => SettingsData.set("blurLayerOutlineOpacity", newValue / 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsDropdownRow {
|
|
||||||
tab: "theme"
|
|
||||||
tags: ["blur", "border", "outline", "edge"]
|
|
||||||
settingKey: "blurBorderColor"
|
|
||||||
text: I18n.tr("Blur Border Color")
|
|
||||||
description: I18n.tr("Border color around blurred surfaces")
|
|
||||||
visible: SettingsData.blurEnabled
|
|
||||||
options: [I18n.tr("Outline", "blur border color"), I18n.tr("Primary", "blur border color"), I18n.tr("Secondary", "blur border color"), I18n.tr("Text Color", "blur border color"), I18n.tr("Custom", "blur border color")]
|
|
||||||
currentValue: {
|
|
||||||
switch (SettingsData.blurBorderColor) {
|
|
||||||
case "primary":
|
|
||||||
return I18n.tr("Primary", "blur border color");
|
|
||||||
case "secondary":
|
|
||||||
return I18n.tr("Secondary", "blur border color");
|
|
||||||
case "surfaceText":
|
|
||||||
return I18n.tr("Text Color", "blur border color");
|
|
||||||
case "custom":
|
|
||||||
return I18n.tr("Custom", "blur border color");
|
|
||||||
default:
|
|
||||||
return I18n.tr("Outline", "blur border color");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onValueChanged: value => {
|
|
||||||
if (value === I18n.tr("Primary", "blur border color")) {
|
|
||||||
SettingsData.set("blurBorderColor", "primary");
|
|
||||||
} else if (value === I18n.tr("Secondary", "blur border color")) {
|
|
||||||
SettingsData.set("blurBorderColor", "secondary");
|
|
||||||
} else if (value === I18n.tr("Text Color", "blur border color")) {
|
|
||||||
SettingsData.set("blurBorderColor", "surfaceText");
|
|
||||||
} else if (value === I18n.tr("Custom", "blur border color")) {
|
|
||||||
SettingsData.set("blurBorderColor", "custom");
|
|
||||||
openBlurBorderColorPicker();
|
|
||||||
} else {
|
|
||||||
SettingsData.set("blurBorderColor", "outline");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsSliderRow {
|
|
||||||
tab: "theme"
|
|
||||||
tags: ["blur", "border", "opacity"]
|
|
||||||
settingKey: "blurBorderOpacity"
|
|
||||||
text: I18n.tr("Blur Border Opacity")
|
|
||||||
description: I18n.tr("Controls the outer edge of protocol-blurred windows")
|
|
||||||
visible: SettingsData.blurEnabled
|
|
||||||
value: Math.round((SettingsData.blurBorderOpacity ?? 0.35) * 100)
|
|
||||||
minimum: 0
|
|
||||||
maximum: 100
|
|
||||||
unit: "%"
|
|
||||||
defaultValue: 35
|
|
||||||
onSliderValueChanged: newValue => SettingsData.set("blurBorderOpacity", newValue / 100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsCard {
|
SettingsCard {
|
||||||
tab: "theme"
|
tab: "theme"
|
||||||
tags: ["modal", "darken", "background", "overlay"]
|
tags: ["modal", "darken", "background", "overlay"]
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ Item {
|
|||||||
|
|
||||||
property alias model: buttonGroup.model
|
property alias model: buttonGroup.model
|
||||||
property alias currentIndex: buttonGroup.currentIndex
|
property alias currentIndex: buttonGroup.currentIndex
|
||||||
|
property alias initialSelection: buttonGroup.initialSelection
|
||||||
|
property alias currentSelection: buttonGroup.currentSelection
|
||||||
property alias selectionMode: buttonGroup.selectionMode
|
property alias selectionMode: buttonGroup.selectionMode
|
||||||
property alias buttonHeight: buttonGroup.buttonHeight
|
property alias buttonHeight: buttonGroup.buttonHeight
|
||||||
property alias minButtonWidth: buttonGroup.minButtonWidth
|
property alias minButtonWidth: buttonGroup.minButtonWidth
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ Variants {
|
|||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
|
updatesEnabled: root.renderActive || root._settleFrames > 0
|
||||||
|
|
||||||
mask: Region {
|
mask: Region {
|
||||||
item: Item {}
|
item: Item {}
|
||||||
}
|
}
|
||||||
@@ -84,20 +86,59 @@ Variants {
|
|||||||
|
|
||||||
readonly property bool transitioning: transitionAnimation.running
|
readonly property bool transitioning: transitionAnimation.running
|
||||||
property bool effectActive: false
|
property bool effectActive: false
|
||||||
property bool _renderSettling: true
|
|
||||||
property bool _overviewBlurSettling: false
|
|
||||||
property bool useNextForEffect: false
|
property bool useNextForEffect: false
|
||||||
property string pendingWallpaper: ""
|
property string pendingWallpaper: ""
|
||||||
property string _deferredSource: ""
|
property string _deferredSource: ""
|
||||||
readonly property bool overviewBlurActive: CompositorService.isNiri && SettingsData.blurWallpaperOnOverview && NiriService.inOverview && currentWallpaper.source !== ""
|
readonly property bool overviewBlurActive: CompositorService.isNiri && SettingsData.blurWallpaperOnOverview && NiriService.inOverview && currentWallpaper.source !== ""
|
||||||
|
readonly property var backingWindow: Window.window
|
||||||
|
readonly property bool renderActive: !source || effectActive || overviewBlurActive || pendingWallpaper !== "" || _deferredSource !== "" || currentWallpaper.status === Image.Loading || nextWallpaper.status === Image.Loading
|
||||||
|
property int _settleFrames: 3
|
||||||
|
|
||||||
|
function invalidate() {
|
||||||
|
_settleFrames = 3;
|
||||||
|
backingWindow?.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
onRenderActiveChanged: invalidate()
|
||||||
|
onBackingWindowChanged: invalidate()
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: currentWallpaper
|
target: root.backingWindow
|
||||||
function onStatusChanged() {
|
function onFrameSwapped() {
|
||||||
if (currentWallpaper.status !== Image.Ready && currentWallpaper.status !== Image.Error)
|
if (root._settleFrames > 0)
|
||||||
|
root._settleFrames--;
|
||||||
|
}
|
||||||
|
function onVisibleChanged() {
|
||||||
|
root.invalidate();
|
||||||
|
}
|
||||||
|
function onWidthChanged() {
|
||||||
|
root.invalidate();
|
||||||
|
}
|
||||||
|
function onHeightChanged() {
|
||||||
|
root.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Quickshell
|
||||||
|
function onScreensChanged() {
|
||||||
|
root.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SettingsData
|
||||||
|
function onWallpaperFillModeChanged() {
|
||||||
|
root.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: IdleService
|
||||||
|
function onIsShellLockedChanged() {
|
||||||
|
if (IdleService.isShellLocked)
|
||||||
return;
|
return;
|
||||||
root._renderSettling = true;
|
root.invalidate();
|
||||||
renderSettleTimer.restart();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,32 +150,11 @@ Variants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: wallpaperWindow
|
|
||||||
function onWidthChanged() {
|
|
||||||
root._renderSettling = true;
|
|
||||||
renderSettleTimer.restart();
|
|
||||||
}
|
|
||||||
function onHeightChanged() {
|
|
||||||
root._renderSettling = true;
|
|
||||||
renderSettleTimer.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: Quickshell
|
|
||||||
function onScreensChanged() {
|
|
||||||
root._renderSettling = true;
|
|
||||||
renderSettleTimer.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: NiriService
|
target: NiriService
|
||||||
function onDisplayScalesChanged() {
|
function onDisplayScalesChanged() {
|
||||||
root._recheckScreenScale();
|
root._recheckScreenScale();
|
||||||
root._renderSettling = true;
|
root.invalidate();
|
||||||
renderSettleTimer.restart();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,29 +162,7 @@ Variants {
|
|||||||
target: WlrOutputService
|
target: WlrOutputService
|
||||||
function onWlrOutputAvailableChanged() {
|
function onWlrOutputAvailableChanged() {
|
||||||
root._recheckScreenScale();
|
root._recheckScreenScale();
|
||||||
root._renderSettling = true;
|
root.invalidate();
|
||||||
renderSettleTimer.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: NiriService
|
|
||||||
function onInOverviewChanged() {
|
|
||||||
root._overviewBlurSettling = true;
|
|
||||||
overviewBlurSettleTimer.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: SettingsData
|
|
||||||
function onBlurWallpaperOnOverviewChanged() {
|
|
||||||
root._overviewBlurSettling = true;
|
|
||||||
overviewBlurSettleTimer.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onWallpaperFillModeChanged() {
|
|
||||||
root._renderSettling = true;
|
|
||||||
renderSettleTimer.restart();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,26 +179,22 @@ Variants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
function handleTransitionLoadError(failedSource) {
|
||||||
target: IdleService
|
log.warn("failed to load candidate wallpaper for", modelData.name + ":", failedSource);
|
||||||
function onIsShellLockedChanged() {
|
transitionDelayTimer.stop();
|
||||||
if (!IdleService.isShellLocked) {
|
transitionAnimation.stop();
|
||||||
root._renderSettling = true;
|
root.useNextForEffect = false;
|
||||||
renderSettleTimer.restart();
|
root.effectActive = false;
|
||||||
}
|
root.transitionProgress = 0.0;
|
||||||
}
|
currentWallpaper.layer.enabled = false;
|
||||||
}
|
nextWallpaper.layer.enabled = false;
|
||||||
|
nextWallpaper.source = "";
|
||||||
|
|
||||||
Timer {
|
if (!root.pendingWallpaper)
|
||||||
id: renderSettleTimer
|
return;
|
||||||
interval: 1000
|
const pending = root.pendingWallpaper;
|
||||||
onTriggered: root._renderSettling = false
|
root.pendingWallpaper = "";
|
||||||
}
|
Qt.callLater(() => root.changeWallpaper(pending, true));
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: overviewBlurSettleTimer
|
|
||||||
interval: 150
|
|
||||||
onTriggered: root._overviewBlurSettling = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFillMode(modeName) {
|
function getFillMode(modeName) {
|
||||||
@@ -227,11 +221,6 @@ Variants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
wallpaperWindow.updatesEnabled = Qt.binding(() => !root.source || root.effectActive || root._renderSettling || root.overviewBlurActive || root._overviewBlurSettling || root.pendingWallpaper !== "" || root._deferredSource !== "" || currentWallpaper.status === Image.Loading || nextWallpaper.status === Image.Loading);
|
|
||||||
|
|
||||||
if (!source) {
|
|
||||||
root._renderSettling = false;
|
|
||||||
}
|
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,8 +251,6 @@ Variants {
|
|||||||
transitionAnimation.stop();
|
transitionAnimation.stop();
|
||||||
root.transitionProgress = 0.0;
|
root.transitionProgress = 0.0;
|
||||||
root.effectActive = false;
|
root.effectActive = false;
|
||||||
root._renderSettling = true;
|
|
||||||
renderSettleTimer.restart();
|
|
||||||
root.screenScale = CompositorService.getScreenScale(modelData);
|
root.screenScale = CompositorService.getScreenScale(modelData);
|
||||||
currentWallpaper.source = newSource;
|
currentWallpaper.source = newSource;
|
||||||
nextWallpaper.source = "";
|
nextWallpaper.source = "";
|
||||||
@@ -328,9 +315,6 @@ Variants {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
root._renderSettling = true;
|
|
||||||
renderSettleTimer.restart();
|
|
||||||
|
|
||||||
nextWallpaper.source = newPath;
|
nextWallpaper.source = newPath;
|
||||||
|
|
||||||
if (nextWallpaper.status === Image.Ready)
|
if (nextWallpaper.status === Image.Ready)
|
||||||
@@ -339,7 +323,7 @@ Variants {
|
|||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: !root.source || root.isColorSource
|
active: !root.source || root.isColorSource || currentWallpaper.status === Image.Error
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
sourceComponent: DankBackdrop {
|
sourceComponent: DankBackdrop {
|
||||||
@@ -364,6 +348,12 @@ Variants {
|
|||||||
cache: true
|
cache: true
|
||||||
sourceSize: Qt.size(root.textureWidth, root.textureHeight)
|
sourceSize: Qt.size(root.textureWidth, root.textureHeight)
|
||||||
fillMode: root.getFillMode(SessionData.getMonitorWallpaperFillMode(modelData.name))
|
fillMode: root.getFillMode(SessionData.getMonitorWallpaperFillMode(modelData.name))
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === Image.Error) {
|
||||||
|
log.warn("failed to load active wallpaper for", modelData.name + ":", source);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
@@ -380,11 +370,13 @@ Variants {
|
|||||||
fillMode: root.getFillMode(SessionData.getMonitorWallpaperFillMode(modelData.name))
|
fillMode: root.getFillMode(SessionData.getMonitorWallpaperFillMode(modelData.name))
|
||||||
|
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
|
if (status === Image.Error) {
|
||||||
|
root.handleTransitionLoadError(source);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (status !== Image.Ready)
|
if (status !== Image.Ready)
|
||||||
return;
|
return;
|
||||||
if (root.actualTransitionType === "none") {
|
if (root.actualTransitionType === "none") {
|
||||||
root._renderSettling = true;
|
|
||||||
renderSettleTimer.restart();
|
|
||||||
currentWallpaper.source = source;
|
currentWallpaper.source = source;
|
||||||
nextWallpaper.source = "";
|
nextWallpaper.source = "";
|
||||||
root.transitionProgress = 0.0;
|
root.transitionProgress = 0.0;
|
||||||
@@ -632,8 +624,6 @@ Variants {
|
|||||||
root.transitionProgress = 0.0;
|
root.transitionProgress = 0.0;
|
||||||
currentWallpaper.layer.enabled = false;
|
currentWallpaper.layer.enabled = false;
|
||||||
nextWallpaper.layer.enabled = false;
|
nextWallpaper.layer.enabled = false;
|
||||||
root._renderSettling = true;
|
|
||||||
renderSettleTimer.restart();
|
|
||||||
root.effectActive = false;
|
root.effectActive = false;
|
||||||
|
|
||||||
if (!root.pendingWallpaper)
|
if (!root.pendingWallpaper)
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
// Connected frame silhouette: frame ring + chrome bodies as one SDF with elevation shadow.
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 qt_Matrix;
|
||||||
|
float qt_Opacity;
|
||||||
|
float widthPx;
|
||||||
|
float heightPx;
|
||||||
|
float cutoutRadius;
|
||||||
|
vec4 cutout; // inner cutout edges in px: x=left y=top z=right w=bottom
|
||||||
|
vec4 surfaceColor; // straight (non-premultiplied) rgba
|
||||||
|
vec4 shadowColor; // straight rgba; a = 0 disables both shadow terms
|
||||||
|
vec4 shadowParam; // key: x = blur px, y = spread px, z,w = offset px
|
||||||
|
vec4 ambientParam; // ambient: x = blur px, y = spread px, z = alpha
|
||||||
|
// Up to four chrome slots. rect = x,y,w,h (px). corner = per-corner radii,
|
||||||
|
// k = per-corner junction fillet radii (both topLeft, topRight, bottomRight,
|
||||||
|
// bottomLeft; a corner is sharp exactly where its k > 0). param = active, 0, 0, 0
|
||||||
|
vec4 chromeRect0;
|
||||||
|
vec4 chromeCorner0;
|
||||||
|
vec4 chromeK0;
|
||||||
|
vec4 chromeParam0;
|
||||||
|
vec4 chromeRect1;
|
||||||
|
vec4 chromeCorner1;
|
||||||
|
vec4 chromeK1;
|
||||||
|
vec4 chromeParam1;
|
||||||
|
vec4 chromeRect2;
|
||||||
|
vec4 chromeCorner2;
|
||||||
|
vec4 chromeK2;
|
||||||
|
vec4 chromeParam2;
|
||||||
|
vec4 chromeRect3;
|
||||||
|
vec4 chromeCorner3;
|
||||||
|
vec4 chromeK3;
|
||||||
|
vec4 chromeParam3;
|
||||||
|
} ubuf;
|
||||||
|
|
||||||
|
float sdBox(vec2 p, vec2 c, vec2 hs) {
|
||||||
|
vec2 q = abs(p - c) - hs;
|
||||||
|
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float sdRoundBox(vec2 p, vec2 c, vec2 hs, float r) {
|
||||||
|
r = min(r, min(hs.x, hs.y));
|
||||||
|
vec2 q = abs(p - c) - hs + r;
|
||||||
|
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - r;
|
||||||
|
}
|
||||||
|
|
||||||
|
float sdRoundBox4(vec2 p, vec2 c, vec2 hs, vec4 r) {
|
||||||
|
p -= c;
|
||||||
|
float rr = (p.x >= 0.0) ? (p.y >= 0.0 ? r.z : r.y) : (p.y >= 0.0 ? r.w : r.x);
|
||||||
|
rr = min(rr, min(hs.x, hs.y));
|
||||||
|
vec2 q = abs(p) - hs + rr;
|
||||||
|
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - rr;
|
||||||
|
}
|
||||||
|
|
||||||
|
float smin(float a, float b, float k) {
|
||||||
|
if (k <= 0.0)
|
||||||
|
return min(a, b);
|
||||||
|
return max(k, min(a, b)) - length(max(vec2(k) - vec2(a, b), vec2(0.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float chromeDist(vec2 px, vec4 rect, vec4 corner) {
|
||||||
|
vec2 c = rect.xy + rect.zw * 0.5;
|
||||||
|
return sdRoundBox4(px, c, rect.zw * 0.5, corner);
|
||||||
|
}
|
||||||
|
|
||||||
|
float chromeK(vec2 px, vec4 rect, vec4 ks) {
|
||||||
|
vec2 p = px - (rect.xy + rect.zw * 0.5);
|
||||||
|
return (p.x >= 0.0) ? (p.y >= 0.0 ? ks.z : ks.y) : (p.y >= 0.0 ? ks.w : ks.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
float sceneDist(vec2 px) {
|
||||||
|
vec2 sc = vec2(ubuf.widthPx, ubuf.heightPx) * 0.5;
|
||||||
|
float dOuter = sdBox(px, sc, sc);
|
||||||
|
vec2 cutC = vec2((ubuf.cutout.x + ubuf.cutout.z) * 0.5, (ubuf.cutout.y + ubuf.cutout.w) * 0.5);
|
||||||
|
vec2 cutH = vec2((ubuf.cutout.z - ubuf.cutout.x) * 0.5, (ubuf.cutout.w - ubuf.cutout.y) * 0.5);
|
||||||
|
float dCut = sdRoundBox(px, cutC, cutH, ubuf.cutoutRadius);
|
||||||
|
float d = max(dOuter, -dCut);
|
||||||
|
|
||||||
|
if (ubuf.chromeParam0.x > 0.5)
|
||||||
|
d = smin(d, chromeDist(px, ubuf.chromeRect0, ubuf.chromeCorner0), chromeK(px, ubuf.chromeRect0, ubuf.chromeK0));
|
||||||
|
if (ubuf.chromeParam1.x > 0.5)
|
||||||
|
d = smin(d, chromeDist(px, ubuf.chromeRect1, ubuf.chromeCorner1), chromeK(px, ubuf.chromeRect1, ubuf.chromeK1));
|
||||||
|
if (ubuf.chromeParam2.x > 0.5)
|
||||||
|
d = smin(d, chromeDist(px, ubuf.chromeRect2, ubuf.chromeCorner2), chromeK(px, ubuf.chromeRect2, ubuf.chromeK2));
|
||||||
|
if (ubuf.chromeParam3.x > 0.5)
|
||||||
|
d = smin(d, chromeDist(px, ubuf.chromeRect3, ubuf.chromeCorner3), chromeK(px, ubuf.chromeRect3, ubuf.chromeK3));
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 px = qt_TexCoord0 * vec2(ubuf.widthPx, ubuf.heightPx);
|
||||||
|
float d = sceneDist(px);
|
||||||
|
float fw = max(fwidth(d), 1e-4);
|
||||||
|
float cov = 1.0 - smoothstep(-fw, fw, d);
|
||||||
|
vec4 col = vec4(ubuf.surfaceColor.rgb, 1.0) * cov;
|
||||||
|
if (ubuf.shadowColor.a > 0.0) {
|
||||||
|
float dk = sceneDist(px - ubuf.shadowParam.zw) - ubuf.shadowParam.y;
|
||||||
|
float bk = max(ubuf.shadowParam.x, fw);
|
||||||
|
float covK = 1.0 - smoothstep(-bk, bk, dk);
|
||||||
|
float ba = max(ubuf.ambientParam.x, fw);
|
||||||
|
float covA = 1.0 - smoothstep(-ba, ba, d - ubuf.ambientParam.y);
|
||||||
|
float sh = 1.0 - (1.0 - covK * ubuf.shadowColor.a) * (1.0 - covA * ubuf.ambientParam.z);
|
||||||
|
col += vec4(ubuf.shadowColor.rgb, 1.0) * (sh * (1.0 - col.a));
|
||||||
|
}
|
||||||
|
fragColor = col * (ubuf.surfaceColor.a * ubuf.qt_Opacity);
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
// Popout-local connected chrome body + bar-edge connector as one SDF.
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 qt_Matrix;
|
||||||
|
float qt_Opacity;
|
||||||
|
float widthPx;
|
||||||
|
float heightPx;
|
||||||
|
vec4 surfaceColor; // straight (non-premultiplied) rgba
|
||||||
|
vec4 shadowColor; // straight rgba; a = 0 disables both shadow terms
|
||||||
|
vec4 shadowParam; // key: x = blur px, y = spread px, z,w = offset px
|
||||||
|
vec4 ambientParam; // ambient: x = blur px, y = spread px, z = alpha
|
||||||
|
vec4 bodyRect; // body rounded rect in item px: x, y, w, h
|
||||||
|
vec4 cornerRadius; // topLeft, topRight, bottomRight, bottomLeft
|
||||||
|
vec4 edgeParam; // x = bar side (0 top, 1 bottom, 2 left, 3 right), y = fillet k
|
||||||
|
} ubuf;
|
||||||
|
|
||||||
|
float sdRoundBox4(vec2 p, vec2 c, vec2 hs, vec4 r) {
|
||||||
|
p -= c;
|
||||||
|
float rr = (p.x >= 0.0) ? (p.y >= 0.0 ? r.z : r.y) : (p.y >= 0.0 ? r.w : r.x);
|
||||||
|
rr = min(rr, min(hs.x, hs.y));
|
||||||
|
vec2 q = abs(p) - hs + rr;
|
||||||
|
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - rr;
|
||||||
|
}
|
||||||
|
|
||||||
|
float smin(float a, float b, float k) {
|
||||||
|
if (k <= 0.0)
|
||||||
|
return min(a, b);
|
||||||
|
return max(k, min(a, b)) - length(max(vec2(k) - vec2(a, b), vec2(0.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float sceneDist(vec2 px) {
|
||||||
|
float side = ubuf.edgeParam.x;
|
||||||
|
float dEdge = side < 0.5 ? px.y
|
||||||
|
: side < 1.5 ? (ubuf.heightPx - px.y)
|
||||||
|
: side < 2.5 ? px.x
|
||||||
|
: (ubuf.widthPx - px.x);
|
||||||
|
vec2 hs = ubuf.bodyRect.zw * 0.5;
|
||||||
|
float dBody = sdRoundBox4(px, ubuf.bodyRect.xy + hs, hs, ubuf.cornerRadius);
|
||||||
|
return smin(dEdge, dBody, ubuf.edgeParam.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 px = qt_TexCoord0 * vec2(ubuf.widthPx, ubuf.heightPx);
|
||||||
|
float d = sceneDist(px);
|
||||||
|
float fw = max(fwidth(d), 1e-4);
|
||||||
|
float cov = 1.0 - smoothstep(-fw, fw, d);
|
||||||
|
vec4 col = vec4(ubuf.surfaceColor.rgb, 1.0) * cov;
|
||||||
|
if (ubuf.shadowColor.a > 0.0) {
|
||||||
|
float dk = sceneDist(px - ubuf.shadowParam.zw) - ubuf.shadowParam.y;
|
||||||
|
float bk = max(ubuf.shadowParam.x, fw);
|
||||||
|
float covK = 1.0 - smoothstep(-bk, bk, dk);
|
||||||
|
float ba = max(ubuf.ambientParam.x, fw);
|
||||||
|
float covA = 1.0 - smoothstep(-ba, ba, d - ubuf.ambientParam.y);
|
||||||
|
float sh = 1.0 - (1.0 - covK * ubuf.shadowColor.a) * (1.0 - covA * ubuf.ambientParam.z);
|
||||||
|
col += vec4(ubuf.shadowColor.rgb, 1.0) * (sh * (1.0 - col.a));
|
||||||
|
}
|
||||||
|
fragColor = col * (ubuf.surfaceColor.a * ubuf.qt_Opacity);
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
// Standalone rounded rect with border and M3 elevation shadow as one SDF.
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 qt_Matrix;
|
||||||
|
float qt_Opacity;
|
||||||
|
float widthPx;
|
||||||
|
float heightPx;
|
||||||
|
float borderWidth;
|
||||||
|
vec4 rectPx; // rounded rect in item px: x, y, w, h
|
||||||
|
vec4 cornerRadius; // topLeft, topRight, bottomRight, bottomLeft
|
||||||
|
vec4 fillColor; // straight (non-premultiplied) rgba
|
||||||
|
vec4 borderColor; // straight rgba
|
||||||
|
vec4 shadowColor; // straight rgba; a = 0 disables both shadow terms
|
||||||
|
vec4 shadowParam; // key: x = blur px, y = spread px, z,w = offset px
|
||||||
|
vec4 ambientParam; // ambient: x = blur px, y = spread px, z = alpha
|
||||||
|
} ubuf;
|
||||||
|
|
||||||
|
float sdRoundBox4(vec2 p, vec2 c, vec2 hs, vec4 r) {
|
||||||
|
p -= c;
|
||||||
|
float rr = (p.x >= 0.0) ? (p.y >= 0.0 ? r.z : r.y) : (p.y >= 0.0 ? r.w : r.x);
|
||||||
|
rr = min(rr, min(hs.x, hs.y));
|
||||||
|
vec2 q = abs(p) - hs + rr;
|
||||||
|
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - rr;
|
||||||
|
}
|
||||||
|
|
||||||
|
float rectDist(vec2 px) {
|
||||||
|
vec2 hs = ubuf.rectPx.zw * 0.5;
|
||||||
|
return sdRoundBox4(px, ubuf.rectPx.xy + hs, hs, ubuf.cornerRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 px = qt_TexCoord0 * vec2(ubuf.widthPx, ubuf.heightPx);
|
||||||
|
float d = rectDist(px);
|
||||||
|
float fw = max(fwidth(d), 1e-4);
|
||||||
|
float cov = 1.0 - smoothstep(-fw, fw, d);
|
||||||
|
float covInner = 1.0 - smoothstep(-fw, fw, d + ubuf.borderWidth);
|
||||||
|
vec4 col = vec4(ubuf.fillColor.rgb, 1.0) * (ubuf.fillColor.a * covInner)
|
||||||
|
+ vec4(ubuf.borderColor.rgb, 1.0) * (ubuf.borderColor.a * max(0.0, cov - covInner));
|
||||||
|
if (ubuf.shadowColor.a > 0.0) {
|
||||||
|
float dk = rectDist(px - ubuf.shadowParam.zw) - ubuf.shadowParam.y;
|
||||||
|
float bk = max(ubuf.shadowParam.x, fw);
|
||||||
|
float covK = 1.0 - smoothstep(-bk, bk, dk);
|
||||||
|
float ba = max(ubuf.ambientParam.x, fw);
|
||||||
|
float covA = 1.0 - smoothstep(-ba, ba, d - ubuf.ambientParam.y);
|
||||||
|
float sh = 1.0 - (1.0 - covK * ubuf.shadowColor.a) * (1.0 - covA * ubuf.ambientParam.z);
|
||||||
|
col += vec4(ubuf.shadowColor.rgb, 1.0) * (sh * (1.0 - cov));
|
||||||
|
}
|
||||||
|
fragColor = col * ubuf.qt_Opacity;
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
// Frame perimeter ring with rounded cutout as one SDF.
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 qt_Matrix;
|
||||||
|
float qt_Opacity;
|
||||||
|
float widthPx;
|
||||||
|
float heightPx;
|
||||||
|
float cutoutRadius;
|
||||||
|
vec4 cutout; // inner cutout edges in px: x=left y=top z=right w=bottom
|
||||||
|
vec4 surfaceColor; // straight (non-premultiplied) rgba
|
||||||
|
} ubuf;
|
||||||
|
|
||||||
|
float sdBox(vec2 p, vec2 c, vec2 hs) {
|
||||||
|
vec2 q = abs(p - c) - hs;
|
||||||
|
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float sdRoundBox(vec2 p, vec2 c, vec2 hs, float r) {
|
||||||
|
r = min(r, min(hs.x, hs.y));
|
||||||
|
vec2 q = abs(p - c) - hs + r;
|
||||||
|
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 px = qt_TexCoord0 * vec2(ubuf.widthPx, ubuf.heightPx);
|
||||||
|
vec2 sc = vec2(ubuf.widthPx, ubuf.heightPx) * 0.5;
|
||||||
|
float dOuter = sdBox(px, sc, sc);
|
||||||
|
vec2 cutC = vec2((ubuf.cutout.x + ubuf.cutout.z) * 0.5, (ubuf.cutout.y + ubuf.cutout.w) * 0.5);
|
||||||
|
vec2 cutH = vec2((ubuf.cutout.z - ubuf.cutout.x) * 0.5, (ubuf.cutout.w - ubuf.cutout.y) * 0.5);
|
||||||
|
float dCut = sdRoundBox(px, cutC, cutH, ubuf.cutoutRadius);
|
||||||
|
float d = max(dOuter, -dCut);
|
||||||
|
|
||||||
|
float fw = max(fwidth(d), 1e-4);
|
||||||
|
float cov = 1.0 - smoothstep(-fw, fw, d);
|
||||||
|
float a = ubuf.surfaceColor.a * cov * ubuf.qt_Opacity;
|
||||||
|
fragColor = vec4(ubuf.surfaceColor.rgb * a, a);
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,151 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Shapes
|
|
||||||
import "../Common/ConnectorGeometry.js" as ConnectorGeometry
|
|
||||||
|
|
||||||
// Concave arc connector filling the gap between a bar corner and an adjacent surface.
|
|
||||||
//
|
|
||||||
// NOTE: FrameWindow now uses ConnectedShape.qml for frame-owned connected chrome
|
|
||||||
// (unified single-path rendering). This component is still used by DankPopout's
|
|
||||||
// own shadow source for non-frame-owned chrome (popouts on non-frame screens).
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string barSide: "top"
|
|
||||||
property string placement: "left"
|
|
||||||
property real spacing: 4
|
|
||||||
property real connectorRadius: 12
|
|
||||||
property color color: "transparent"
|
|
||||||
property real edgeStrokeWidth: 0
|
|
||||||
property color edgeStrokeColor: color
|
|
||||||
property real dpr: 1
|
|
||||||
|
|
||||||
readonly property bool isHorizontalBar: barSide === "top" || barSide === "bottom"
|
|
||||||
readonly property bool isPlacementLeft: placement === "left"
|
|
||||||
readonly property real _edgeStrokeWidth: Math.max(0, edgeStrokeWidth)
|
|
||||||
readonly property string arcCorner: ConnectorGeometry.arcCorner(barSide, placement)
|
|
||||||
readonly property real pathStartX: {
|
|
||||||
switch (arcCorner) {
|
|
||||||
case "topLeft":
|
|
||||||
return width;
|
|
||||||
case "topRight":
|
|
||||||
case "bottomLeft":
|
|
||||||
return 0;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readonly property real pathStartY: {
|
|
||||||
switch (arcCorner) {
|
|
||||||
case "bottomRight":
|
|
||||||
return height;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readonly property real firstLineX: {
|
|
||||||
switch (arcCorner) {
|
|
||||||
case "topLeft":
|
|
||||||
case "bottomLeft":
|
|
||||||
return width;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readonly property real firstLineY: {
|
|
||||||
switch (arcCorner) {
|
|
||||||
case "topLeft":
|
|
||||||
case "topRight":
|
|
||||||
return height;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readonly property real secondLineX: {
|
|
||||||
switch (arcCorner) {
|
|
||||||
case "topRight":
|
|
||||||
case "bottomLeft":
|
|
||||||
case "bottomRight":
|
|
||||||
return width;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readonly property real secondLineY: {
|
|
||||||
switch (arcCorner) {
|
|
||||||
case "topLeft":
|
|
||||||
case "topRight":
|
|
||||||
case "bottomLeft":
|
|
||||||
return height;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readonly property real arcCenterX: arcCorner === "topRight" || arcCorner === "bottomRight" ? width : 0
|
|
||||||
readonly property real arcCenterY: arcCorner === "bottomLeft" || arcCorner === "bottomRight" ? height : 0
|
|
||||||
readonly property real arcStartAngle: {
|
|
||||||
switch (arcCorner) {
|
|
||||||
case "topLeft":
|
|
||||||
case "topRight":
|
|
||||||
return 90;
|
|
||||||
case "bottomLeft":
|
|
||||||
return 0;
|
|
||||||
default:
|
|
||||||
return -90;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readonly property real arcSweepAngle: {
|
|
||||||
switch (arcCorner) {
|
|
||||||
case "topRight":
|
|
||||||
return 90;
|
|
||||||
default:
|
|
||||||
return -90;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
width: isHorizontalBar ? connectorRadius : (spacing + connectorRadius)
|
|
||||||
height: isHorizontalBar ? (spacing + connectorRadius) : connectorRadius
|
|
||||||
|
|
||||||
Shape {
|
|
||||||
x: -root._edgeStrokeWidth
|
|
||||||
y: -root._edgeStrokeWidth
|
|
||||||
width: root.width + root._edgeStrokeWidth * 2
|
|
||||||
height: root.height + root._edgeStrokeWidth * 2
|
|
||||||
asynchronous: false
|
|
||||||
antialiasing: true
|
|
||||||
preferredRendererType: Shape.CurveRenderer
|
|
||||||
layer.enabled: true
|
|
||||||
layer.smooth: true
|
|
||||||
layer.textureSize: root.dpr > 1 ? Qt.size(Math.ceil(width * root.dpr), Math.ceil(height * root.dpr)) : Qt.size(0, 0)
|
|
||||||
|
|
||||||
ShapePath {
|
|
||||||
fillColor: root.color
|
|
||||||
strokeColor: root._edgeStrokeWidth > 0 ? root.edgeStrokeColor : "transparent"
|
|
||||||
strokeWidth: root._edgeStrokeWidth * 2
|
|
||||||
joinStyle: ShapePath.RoundJoin
|
|
||||||
capStyle: ShapePath.RoundCap
|
|
||||||
fillRule: ShapePath.WindingFill
|
|
||||||
startX: root.pathStartX + root._edgeStrokeWidth
|
|
||||||
startY: root.pathStartY + root._edgeStrokeWidth
|
|
||||||
|
|
||||||
PathLine {
|
|
||||||
x: root.firstLineX + root._edgeStrokeWidth
|
|
||||||
y: root.firstLineY + root._edgeStrokeWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
PathLine {
|
|
||||||
x: root.secondLineX + root._edgeStrokeWidth
|
|
||||||
y: root.secondLineY + root._edgeStrokeWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
PathAngleArc {
|
|
||||||
centerX: root.arcCenterX + root._edgeStrokeWidth
|
|
||||||
centerY: root.arcCenterY + root._edgeStrokeWidth
|
|
||||||
radiusX: root.connectorRadius
|
|
||||||
radiusY: root.connectorRadius
|
|
||||||
startAngle: root.arcStartAngle
|
|
||||||
sweepAngle: root.arcSweepAngle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,414 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Shapes
|
|
||||||
import qs.Common
|
|
||||||
|
|
||||||
// Unified connected silhouette: body + near/far concave arcs as one ShapePath.
|
|
||||||
// Keeping the connected chrome in one path avoids sibling alignment seams.
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string barSide: "top"
|
|
||||||
|
|
||||||
property real bodyWidth: 0
|
|
||||||
property real bodyHeight: 0
|
|
||||||
|
|
||||||
property real connectorRadius: 12
|
|
||||||
property real startConnectorRadius: connectorRadius
|
|
||||||
property real endConnectorRadius: connectorRadius
|
|
||||||
property real farStartConnectorRadius: 0
|
|
||||||
property real farEndConnectorRadius: 0
|
|
||||||
|
|
||||||
property real surfaceRadius: 12
|
|
||||||
|
|
||||||
property color fillColor: "transparent"
|
|
||||||
|
|
||||||
readonly property bool _horiz: barSide === "top" || barSide === "bottom"
|
|
||||||
readonly property real _sc: Math.max(0, startConnectorRadius)
|
|
||||||
readonly property real _ec: Math.max(0, endConnectorRadius)
|
|
||||||
readonly property real _fsc: Math.max(0, farStartConnectorRadius)
|
|
||||||
readonly property real _fec: Math.max(0, farEndConnectorRadius)
|
|
||||||
readonly property real _firstCr: barSide === "left" ? _sc : _ec
|
|
||||||
readonly property real _secondCr: barSide === "left" ? _ec : _sc
|
|
||||||
readonly property real _firstFarCr: barSide === "left" ? _fsc : _fec
|
|
||||||
readonly property real _secondFarCr: barSide === "left" ? _fec : _fsc
|
|
||||||
readonly property real _farExtent: Math.max(_fsc, _fec)
|
|
||||||
readonly property real _sr: Math.max(0, Math.min(surfaceRadius, (_horiz ? bodyWidth : bodyHeight) / 2, (_horiz ? bodyHeight : bodyWidth) / 2))
|
|
||||||
readonly property real _firstSr: _firstFarCr > 0 ? 0 : _sr
|
|
||||||
readonly property real _secondSr: _secondFarCr > 0 ? 0 : _sr
|
|
||||||
readonly property real _firstFarInset: _firstFarCr > 0 ? _firstFarCr : _firstSr
|
|
||||||
readonly property real _secondFarInset: _secondFarCr > 0 ? _secondFarCr : _secondSr
|
|
||||||
|
|
||||||
// Root-level aliases — PathArc/PathLine elements can't use `parent`.
|
|
||||||
readonly property real _bw: bodyWidth
|
|
||||||
readonly property real _bh: bodyHeight
|
|
||||||
readonly property real _bodyLeft: _horiz ? _sc : (barSide === "right" ? _farExtent : 0)
|
|
||||||
readonly property real _bodyRight: _bodyLeft + _bw
|
|
||||||
readonly property real _bodyTop: _horiz ? (barSide === "bottom" ? _farExtent : 0) : _sc
|
|
||||||
readonly property real _bodyBottom: _bodyTop + _bh
|
|
||||||
readonly property real _totalW: _horiz ? _bw + _sc + _ec : _bw + _farExtent
|
|
||||||
readonly property real _totalH: _horiz ? _bh + _farExtent : _bh + _sc + _ec
|
|
||||||
|
|
||||||
width: _totalW
|
|
||||||
height: _totalH
|
|
||||||
|
|
||||||
readonly property real bodyX: root._bodyLeft
|
|
||||||
readonly property real bodyY: root._bodyTop
|
|
||||||
|
|
||||||
Shape {
|
|
||||||
anchors.fill: parent
|
|
||||||
asynchronous: false
|
|
||||||
preferredRendererType: Shape.CurveRenderer
|
|
||||||
antialiasing: true
|
|
||||||
|
|
||||||
ShapePath {
|
|
||||||
fillColor: root.fillColor
|
|
||||||
strokeWidth: -1
|
|
||||||
fillRule: ShapePath.WindingFill
|
|
||||||
|
|
||||||
// CW path: bar edge → concave arc → body → convex arc → far edge → convex arc → body → concave arc
|
|
||||||
|
|
||||||
startX: root.barSide === "right" ? root._totalW : 0
|
|
||||||
startY: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "bottom":
|
|
||||||
return root._totalH;
|
|
||||||
case "left":
|
|
||||||
return root._totalH;
|
|
||||||
case "right":
|
|
||||||
return 0;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bar edge
|
|
||||||
PathLine {
|
|
||||||
x: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "left":
|
|
||||||
return 0;
|
|
||||||
case "right":
|
|
||||||
return root._totalW;
|
|
||||||
default:
|
|
||||||
return root._totalW;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
y: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "bottom":
|
|
||||||
return root._totalH;
|
|
||||||
case "left":
|
|
||||||
return 0;
|
|
||||||
case "right":
|
|
||||||
return root._totalH;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Concave arc 1
|
|
||||||
PathArc {
|
|
||||||
relativeX: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "left":
|
|
||||||
return root._firstCr;
|
|
||||||
case "right":
|
|
||||||
return -root._firstCr;
|
|
||||||
default:
|
|
||||||
return -root._firstCr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
relativeY: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "bottom":
|
|
||||||
return -root._firstCr;
|
|
||||||
case "left":
|
|
||||||
return root._firstCr;
|
|
||||||
case "right":
|
|
||||||
return -root._firstCr;
|
|
||||||
default:
|
|
||||||
return root._firstCr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
radiusX: root._firstCr
|
|
||||||
radiusY: root._firstCr
|
|
||||||
direction: root.barSide === "bottom" ? PathArc.Clockwise : PathArc.Counterclockwise
|
|
||||||
}
|
|
||||||
|
|
||||||
// Body edge to first convex corner
|
|
||||||
PathLine {
|
|
||||||
x: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "left":
|
|
||||||
return root._bodyRight - root._firstSr;
|
|
||||||
case "right":
|
|
||||||
return root._bodyLeft + root._firstSr;
|
|
||||||
default:
|
|
||||||
return root._bodyRight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
y: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "bottom":
|
|
||||||
return root._bodyTop + root._firstSr;
|
|
||||||
case "left":
|
|
||||||
return root._bodyTop;
|
|
||||||
case "right":
|
|
||||||
return root._bodyBottom;
|
|
||||||
default:
|
|
||||||
return root._bodyBottom - root._firstSr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convex arc 1
|
|
||||||
PathArc {
|
|
||||||
relativeX: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "left":
|
|
||||||
return root._firstSr;
|
|
||||||
case "right":
|
|
||||||
return -root._firstSr;
|
|
||||||
default:
|
|
||||||
return -root._firstSr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
relativeY: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "bottom":
|
|
||||||
return -root._firstSr;
|
|
||||||
case "left":
|
|
||||||
return root._firstSr;
|
|
||||||
case "right":
|
|
||||||
return -root._firstSr;
|
|
||||||
default:
|
|
||||||
return root._firstSr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
radiusX: root._firstSr
|
|
||||||
radiusY: root._firstSr
|
|
||||||
direction: root.barSide === "bottom" ? PathArc.Counterclockwise : PathArc.Clockwise
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opposite-side connector 1
|
|
||||||
PathLine {
|
|
||||||
x: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "left":
|
|
||||||
return root._firstFarCr > 0 ? root._bodyRight + root._firstFarCr : root._bodyRight;
|
|
||||||
case "right":
|
|
||||||
return root._firstFarCr > 0 ? root._bodyLeft - root._firstFarCr : root._bodyLeft;
|
|
||||||
default:
|
|
||||||
return root._firstFarCr > 0 ? root._bodyRight : root._bodyRight - root._firstSr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
y: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "bottom":
|
|
||||||
return root._firstFarCr > 0 ? root._bodyTop - root._firstFarCr : root._bodyTop;
|
|
||||||
case "left":
|
|
||||||
return root._firstFarCr > 0 ? root._bodyTop : root._bodyTop + root._firstSr;
|
|
||||||
case "right":
|
|
||||||
return root._firstFarCr > 0 ? root._bodyBottom : root._bodyBottom - root._firstSr;
|
|
||||||
default:
|
|
||||||
return root._firstFarCr > 0 ? root._bodyBottom + root._firstFarCr : root._bodyBottom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PathArc {
|
|
||||||
relativeX: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "left":
|
|
||||||
return -root._firstFarCr;
|
|
||||||
case "right":
|
|
||||||
return root._firstFarCr;
|
|
||||||
default:
|
|
||||||
return -root._firstFarCr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
relativeY: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "bottom":
|
|
||||||
return root._firstFarCr;
|
|
||||||
case "left":
|
|
||||||
return root._firstFarCr;
|
|
||||||
case "right":
|
|
||||||
return -root._firstFarCr;
|
|
||||||
default:
|
|
||||||
return -root._firstFarCr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
radiusX: root._firstFarCr
|
|
||||||
radiusY: root._firstFarCr
|
|
||||||
direction: root.barSide === "bottom" ? PathArc.Clockwise : PathArc.Counterclockwise
|
|
||||||
}
|
|
||||||
|
|
||||||
// Far edge
|
|
||||||
PathLine {
|
|
||||||
x: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "left":
|
|
||||||
return root._bodyRight;
|
|
||||||
case "right":
|
|
||||||
return root._bodyLeft;
|
|
||||||
default:
|
|
||||||
return root._bodyLeft + root._secondFarInset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
y: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "bottom":
|
|
||||||
return root._bodyTop;
|
|
||||||
case "left":
|
|
||||||
return root._bodyBottom - root._secondFarInset;
|
|
||||||
case "right":
|
|
||||||
return root._bodyTop + root._secondFarInset;
|
|
||||||
default:
|
|
||||||
return root._bodyBottom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opposite-side connector 2
|
|
||||||
PathArc {
|
|
||||||
relativeX: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "left":
|
|
||||||
return root._secondFarCr;
|
|
||||||
case "right":
|
|
||||||
return -root._secondFarCr;
|
|
||||||
default:
|
|
||||||
return -root._secondFarCr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
relativeY: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "bottom":
|
|
||||||
return -root._secondFarCr;
|
|
||||||
case "left":
|
|
||||||
return root._secondFarCr;
|
|
||||||
case "right":
|
|
||||||
return -root._secondFarCr;
|
|
||||||
default:
|
|
||||||
return root._secondFarCr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
radiusX: root._secondFarCr
|
|
||||||
radiusY: root._secondFarCr
|
|
||||||
direction: root.barSide === "bottom" ? PathArc.Clockwise : PathArc.Counterclockwise
|
|
||||||
}
|
|
||||||
|
|
||||||
PathLine {
|
|
||||||
x: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "left":
|
|
||||||
return root._secondFarCr > 0 ? root._bodyRight : root._bodyRight;
|
|
||||||
case "right":
|
|
||||||
return root._secondFarCr > 0 ? root._bodyLeft : root._bodyLeft;
|
|
||||||
default:
|
|
||||||
return root._secondFarCr > 0 ? root._bodyLeft : root._bodyLeft + root._secondSr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
y: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "bottom":
|
|
||||||
return root._secondFarCr > 0 ? root._bodyTop : root._bodyTop;
|
|
||||||
case "left":
|
|
||||||
return root._secondFarCr > 0 ? root._bodyBottom : root._bodyBottom - root._secondSr;
|
|
||||||
case "right":
|
|
||||||
return root._secondFarCr > 0 ? root._bodyTop : root._bodyTop + root._secondSr;
|
|
||||||
default:
|
|
||||||
return root._secondFarCr > 0 ? root._bodyBottom : root._bodyBottom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convex arc 2
|
|
||||||
PathArc {
|
|
||||||
relativeX: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "left":
|
|
||||||
return -root._secondSr;
|
|
||||||
case "right":
|
|
||||||
return root._secondSr;
|
|
||||||
default:
|
|
||||||
return -root._secondSr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
relativeY: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "bottom":
|
|
||||||
return root._secondSr;
|
|
||||||
case "left":
|
|
||||||
return root._secondSr;
|
|
||||||
case "right":
|
|
||||||
return -root._secondSr;
|
|
||||||
default:
|
|
||||||
return -root._secondSr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
radiusX: root._secondSr
|
|
||||||
radiusY: root._secondSr
|
|
||||||
direction: root.barSide === "bottom" ? PathArc.Counterclockwise : PathArc.Clockwise
|
|
||||||
}
|
|
||||||
|
|
||||||
// Body edge to second concave arc
|
|
||||||
PathLine {
|
|
||||||
x: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "left":
|
|
||||||
return root._bodyLeft + root._ec;
|
|
||||||
case "right":
|
|
||||||
return root._bodyRight - root._sc;
|
|
||||||
default:
|
|
||||||
return root._bodyLeft;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
y: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "bottom":
|
|
||||||
return root._bodyBottom - root._sc;
|
|
||||||
case "left":
|
|
||||||
return root._bodyBottom;
|
|
||||||
case "right":
|
|
||||||
return root._bodyTop;
|
|
||||||
default:
|
|
||||||
return root._bodyTop + root._sc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Concave arc 2
|
|
||||||
PathArc {
|
|
||||||
relativeX: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "left":
|
|
||||||
return -root._secondCr;
|
|
||||||
case "right":
|
|
||||||
return root._secondCr;
|
|
||||||
default:
|
|
||||||
return -root._secondCr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
relativeY: {
|
|
||||||
switch (root.barSide) {
|
|
||||||
case "bottom":
|
|
||||||
return root._secondCr;
|
|
||||||
case "left":
|
|
||||||
return root._secondCr;
|
|
||||||
case "right":
|
|
||||||
return -root._secondCr;
|
|
||||||
default:
|
|
||||||
return -root._secondCr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
radiusX: root._secondCr
|
|
||||||
radiusY: root._secondCr
|
|
||||||
direction: root.barSide === "bottom" ? PathArc.Clockwise : PathArc.Counterclockwise
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -289,8 +289,6 @@ PanelWindow {
|
|||||||
borderColor: Theme.outlineMedium
|
borderColor: Theme.outlineMedium
|
||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
||||||
layer.textureSize: Qt.size(Math.round(width * root.dpr), Math.round(height * root.dpr))
|
|
||||||
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
@@ -50,6 +51,20 @@ Item {
|
|||||||
readonly property var contentLoader: impl.item ? impl.item.contentLoader : _fallbackContentLoader
|
readonly property var contentLoader: impl.item ? impl.item.contentLoader : _fallbackContentLoader
|
||||||
readonly property var overlayLoader: impl.item ? impl.item.overlayLoader : _fallbackOverlayLoader
|
readonly property var overlayLoader: impl.item ? impl.item.overlayLoader : _fallbackOverlayLoader
|
||||||
readonly property var backgroundWindow: impl.item ? impl.item.backgroundWindow : null
|
readonly property var backgroundWindow: impl.item ? impl.item.backgroundWindow : null
|
||||||
|
readonly property var contentWindow: impl.item ? impl.item.contentWindow : null
|
||||||
|
|
||||||
|
// Hyprland OnDemand grab: whitelist popout surfaces and bars so dismiss clicks still land.
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
windows: {
|
||||||
|
const list = [];
|
||||||
|
if (root.contentWindow)
|
||||||
|
list.push(root.contentWindow);
|
||||||
|
if (root.backgroundWindow && root.backgroundWindow !== root.contentWindow)
|
||||||
|
list.push(root.backgroundWindow);
|
||||||
|
return list.concat(KeyboardFocus.barWindows);
|
||||||
|
}
|
||||||
|
active: KeyboardFocus.wantsGrab(root.shouldBeVisible, root.customKeyboardFocus)
|
||||||
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: _fallbackContentLoader
|
id: _fallbackContentLoader
|
||||||
@@ -127,8 +142,6 @@ Item {
|
|||||||
return _usesConnectedBackendForScreen(targetScreen) ? connectedComp : standaloneComp;
|
return _usesConnectedBackendForScreen(targetScreen) ? connectedComp : standaloneComp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defer Loader source-component swap until impl is fully closed; avoids
|
|
||||||
// tearing down a popout mid-animation when frame mode is toggled.
|
|
||||||
function _maybeResolveBackend() {
|
function _maybeResolveBackend() {
|
||||||
_resolveBackendForScreen(screen);
|
_resolveBackendForScreen(screen);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import Quickshell
|
|||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import "../Common/ConnectorGeometry.js" as ConnectorGeometry
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -18,6 +17,7 @@ Item {
|
|||||||
property Component overlayContent: null
|
property Component overlayContent: null
|
||||||
property alias overlayLoader: overlayLoader
|
property alias overlayLoader: overlayLoader
|
||||||
readonly property alias backgroundWindow: contentWindow
|
readonly property alias backgroundWindow: contentWindow
|
||||||
|
readonly property alias contentWindow: contentWindow
|
||||||
property real popupWidth: 400
|
property real popupWidth: 400
|
||||||
property real popupHeight: 300
|
property real popupHeight: 300
|
||||||
property real triggerX: 0
|
property real triggerX: 0
|
||||||
@@ -38,8 +38,6 @@ Item {
|
|||||||
property bool fullHeightSurface: false
|
property bool fullHeightSurface: false
|
||||||
property bool _primeContent: false
|
property bool _primeContent: false
|
||||||
property bool _resizeActive: false
|
property bool _resizeActive: false
|
||||||
property string _chromeClaimId: ""
|
|
||||||
property int _connectedChromeSerial: 0
|
|
||||||
property real _chromeAnimTravelX: 1
|
property real _chromeAnimTravelX: 1
|
||||||
property real _chromeAnimTravelY: 1
|
property real _chromeAnimTravelY: 1
|
||||||
property bool _fullSyncQueued: false
|
property bool _fullSyncQueued: false
|
||||||
@@ -55,7 +53,6 @@ Item {
|
|||||||
"rightBar": 0
|
"rightBar": 0
|
||||||
})
|
})
|
||||||
property var screen: null
|
property var screen: null
|
||||||
// Connected resize uses one full-screen surface; body-sized regions are masks.
|
|
||||||
readonly property bool useBackgroundWindow: false
|
readonly property bool useBackgroundWindow: false
|
||||||
readonly property var effectivePopoutLayer: LayerShell.fromEnv("DMS_POPOUT_LAYER", root.triggerUsesOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top, {
|
readonly property var effectivePopoutLayer: LayerShell.fromEnv("DMS_POPOUT_LAYER", root.triggerUsesOverlayLayer ? WlrLayer.Overlay : WlrLayer.Top, {
|
||||||
"allow": ["top", "overlay"],
|
"allow": ["top", "overlay"],
|
||||||
@@ -93,13 +90,49 @@ Item {
|
|||||||
signal popoutClosed
|
signal popoutClosed
|
||||||
signal backgroundClicked
|
signal backgroundClicked
|
||||||
|
|
||||||
// Coalesce per-channel dirty bits; one ConnectedModeState write per tick.
|
|
||||||
Timer {
|
Timer {
|
||||||
id: _syncTimer
|
id: _syncTimer
|
||||||
interval: 0
|
interval: 0
|
||||||
onTriggered: root._flushSync()
|
onTriggered: root._flushSync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConnectedSurfaceLease {
|
||||||
|
id: chromeLease
|
||||||
|
claimPrefix: root.layerNamespace
|
||||||
|
screenName: root.screen ? root.screen.name : ""
|
||||||
|
enabled: root.frameOwnsConnectedChrome
|
||||||
|
active: contentWindow.visible || root.shouldBeVisible
|
||||||
|
presented: contentWindow.visible || root.shouldBeVisible
|
||||||
|
renewTokenOnRecovery: false
|
||||||
|
isCurrentOwner: function(name) {
|
||||||
|
return PopoutManager.isCurrentPopout(root.popoutHandle, name);
|
||||||
|
}
|
||||||
|
hasOwner: function(_name, ownerId) {
|
||||||
|
return ConnectedModeState.hasPopoutOwner(ownerId);
|
||||||
|
}
|
||||||
|
statePresent: function(name, ownerId) {
|
||||||
|
return ConnectedModeState.hasPopoutOwner(ownerId) && ConnectedModeState.hasSurfaceDescriptor(name, "popout", ownerId);
|
||||||
|
}
|
||||||
|
claimState: function(_name, state, ownerId) {
|
||||||
|
return ConnectedModeState.claimPopout(ownerId, state);
|
||||||
|
}
|
||||||
|
ensureState: function(_name, state, ownerId) {
|
||||||
|
if (!ConnectedModeState.hasPopoutOwner(ownerId))
|
||||||
|
return false;
|
||||||
|
return ConnectedModeState.updatePopout(ownerId, state);
|
||||||
|
}
|
||||||
|
releaseState: function(_name, ownerId) {
|
||||||
|
return ConnectedModeState.releasePopout(ownerId);
|
||||||
|
}
|
||||||
|
updateAnimationState: function(_name, ownerId, animX, animY) {
|
||||||
|
return ConnectedModeState.setPopoutAnim(ownerId, animX, animY);
|
||||||
|
}
|
||||||
|
updateBodyState: function(_name, ownerId, bodyX, bodyY, bodyW, bodyH) {
|
||||||
|
return ConnectedModeState.setPopoutBody(ownerId, bodyX, bodyY, bodyW, bodyH);
|
||||||
|
}
|
||||||
|
onRecoveryRequested: root._queueFullSync()
|
||||||
|
}
|
||||||
|
|
||||||
property var _lastOpenedScreen: null
|
property var _lastOpenedScreen: null
|
||||||
property bool isClosing: false
|
property bool isClosing: false
|
||||||
|
|
||||||
@@ -169,11 +202,6 @@ Item {
|
|||||||
setBarContext(pos, bottomGap);
|
setBarContext(pos, bottomGap);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _nextChromeClaimId() {
|
|
||||||
_connectedChromeSerial += 1;
|
|
||||||
return layerNamespace + ":" + _connectedChromeSerial + ":" + (new Date()).getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _captureChromeAnimTravel() {
|
function _captureChromeAnimTravel() {
|
||||||
_chromeAnimTravelX = Math.max(1, Math.abs(contentContainer.offsetX));
|
_chromeAnimTravelX = Math.max(1, Math.abs(contentContainer.offsetX));
|
||||||
_chromeAnimTravelY = Math.max(1, Math.abs(contentContainer.offsetY));
|
_chromeAnimTravelY = Math.max(1, Math.abs(contentContainer.offsetY));
|
||||||
@@ -203,15 +231,35 @@ Item {
|
|||||||
|
|
||||||
function _connectedChromeState(visibleOverride) {
|
function _connectedChromeState(visibleOverride) {
|
||||||
const visible = visibleOverride !== undefined ? !!visibleOverride : contentWindow.visible;
|
const visible = visibleOverride !== undefined ? !!visibleOverride : contentWindow.visible;
|
||||||
|
const presented = contentWindow.visible || root.shouldBeVisible;
|
||||||
|
const phase = root.isClosing ? "closing" : (!presented ? "hidden" : (!contentWindow.visible && root.shouldBeVisible ? "opening" : "open"));
|
||||||
|
const bodyRect = {
|
||||||
|
"x": root.alignedX,
|
||||||
|
"y": root.renderedAlignedY,
|
||||||
|
"width": root.alignedWidth,
|
||||||
|
"height": root.renderedAlignedHeight
|
||||||
|
};
|
||||||
|
const animationOffset = {
|
||||||
|
"x": _connectedChromeAnimX(),
|
||||||
|
"y": _connectedChromeAnimY()
|
||||||
|
};
|
||||||
return {
|
return {
|
||||||
|
"kind": "popout",
|
||||||
|
"screenName": root.screen ? root.screen.name : "",
|
||||||
|
"phase": phase,
|
||||||
"visible": visible,
|
"visible": visible,
|
||||||
|
"presented": presented,
|
||||||
"barSide": contentContainer.connectedBarSide,
|
"barSide": contentContainer.connectedBarSide,
|
||||||
|
"bodyRect": bodyRect,
|
||||||
|
"animationOffset": animationOffset,
|
||||||
|
"scale": 1,
|
||||||
|
"opacity": Theme.connectedSurfaceColor.a,
|
||||||
"bodyX": root.alignedX,
|
"bodyX": root.alignedX,
|
||||||
"bodyY": root.renderedAlignedY,
|
"bodyY": root.renderedAlignedY,
|
||||||
"bodyW": root.alignedWidth,
|
"bodyW": root.alignedWidth,
|
||||||
"bodyH": root.renderedAlignedHeight,
|
"bodyH": root.renderedAlignedHeight,
|
||||||
"animX": _connectedChromeAnimX(),
|
"animX": animationOffset.x,
|
||||||
"animY": _connectedChromeAnimY(),
|
"animY": animationOffset.y,
|
||||||
"screen": root.screen ? root.screen.name : "",
|
"screen": root.screen ? root.screen.name : "",
|
||||||
"omitStartConnector": root._closeGapOmitStartConnector(),
|
"omitStartConnector": root._closeGapOmitStartConnector(),
|
||||||
"omitEndConnector": root._closeGapOmitEndConnector()
|
"omitEndConnector": root._closeGapOmitEndConnector()
|
||||||
@@ -219,34 +267,18 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _publishConnectedChromeState(forceClaim, visibleOverride) {
|
function _publishConnectedChromeState(forceClaim, visibleOverride) {
|
||||||
if (!root.frameOwnsConnectedChrome || !root.screen || !_chromeClaimId)
|
if (!root.frameOwnsConnectedChrome || !root.screen)
|
||||||
return false;
|
return false;
|
||||||
|
return chromeLease.publish(_connectedChromeState(visibleOverride), !!forceClaim);
|
||||||
const screenName = root.screen.name;
|
|
||||||
const isCurrent = PopoutManager.isCurrentPopout(popoutHandle, screenName);
|
|
||||||
if (!ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
|
|
||||||
if (!isCurrent)
|
|
||||||
return false;
|
|
||||||
forceClaim = true;
|
|
||||||
} else if (forceClaim && !isCurrent) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = _connectedChromeState(visibleOverride);
|
|
||||||
return forceClaim ? ConnectedModeState.claimPopout(_chromeClaimId, state) : ConnectedModeState.updatePopout(_chromeClaimId, state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _releaseConnectedChromeState() {
|
function _releaseConnectedChromeState() {
|
||||||
if (_chromeClaimId)
|
chromeLease.release();
|
||||||
ConnectedModeState.releasePopout(_chromeClaimId);
|
|
||||||
_chromeClaimId = "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Exposed animation state for ConnectedModeState ────────────────────
|
|
||||||
readonly property real contentAnimX: contentContainer.animX
|
readonly property real contentAnimX: contentContainer.animX
|
||||||
readonly property real contentAnimY: contentContainer.animY
|
readonly property real contentAnimY: contentContainer.animY
|
||||||
|
|
||||||
// ─── ConnectedModeState sync ────────────────────────────────────────────
|
|
||||||
function _syncPopoutChromeState() {
|
function _syncPopoutChromeState() {
|
||||||
if (!root.frameOwnsConnectedChrome) {
|
if (!root.frameOwnsConnectedChrome) {
|
||||||
_releaseConnectedChromeState();
|
_releaseConnectedChromeState();
|
||||||
@@ -258,16 +290,11 @@ Item {
|
|||||||
}
|
}
|
||||||
if (!contentWindow.visible && !shouldBeVisible)
|
if (!contentWindow.visible && !shouldBeVisible)
|
||||||
return;
|
return;
|
||||||
if (!_chromeClaimId) {
|
_publishConnectedChromeState(false);
|
||||||
if (!PopoutManager.isCurrentPopout(popoutHandle, root.screen.name))
|
|
||||||
return;
|
|
||||||
_chromeClaimId = _nextChromeClaimId();
|
|
||||||
}
|
|
||||||
_publishConnectedChromeState(!ConnectedModeState.hasPopoutOwner(_chromeClaimId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _syncPopoutAnim(axis) {
|
function _syncPopoutAnim(axis) {
|
||||||
if (!root.frameOwnsConnectedChrome || !_chromeClaimId)
|
if (!root.frameOwnsConnectedChrome || !chromeLease.claimId)
|
||||||
return;
|
return;
|
||||||
if (!contentWindow.visible && !shouldBeVisible)
|
if (!contentWindow.visible && !shouldBeVisible)
|
||||||
return;
|
return;
|
||||||
@@ -276,25 +303,15 @@ Item {
|
|||||||
const syncY = axis === "y" && (barSide === "top" || barSide === "bottom");
|
const syncY = axis === "y" && (barSide === "top" || barSide === "bottom");
|
||||||
if (!syncX && !syncY)
|
if (!syncX && !syncY)
|
||||||
return;
|
return;
|
||||||
if (!ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
|
chromeLease.updateAnim(syncX ? _connectedChromeAnimX() : undefined, syncY ? _connectedChromeAnimY() : undefined);
|
||||||
if (root.screen && PopoutManager.isCurrentPopout(popoutHandle, root.screen.name))
|
|
||||||
_queueFullSync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ConnectedModeState.setPopoutAnim(_chromeClaimId, syncX ? _connectedChromeAnimX() : undefined, syncY ? _connectedChromeAnimY() : undefined);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _syncPopoutBody() {
|
function _syncPopoutBody() {
|
||||||
if (!root.frameOwnsConnectedChrome || !_chromeClaimId)
|
if (!root.frameOwnsConnectedChrome || !chromeLease.claimId)
|
||||||
return;
|
return;
|
||||||
if (!contentWindow.visible && !shouldBeVisible)
|
if (!contentWindow.visible && !shouldBeVisible)
|
||||||
return;
|
return;
|
||||||
if (!ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
|
chromeLease.updateBody(root.alignedX, root.renderedAlignedY, root.alignedWidth, root.renderedAlignedHeight);
|
||||||
if (root.screen && PopoutManager.isCurrentPopout(popoutHandle, root.screen.name))
|
|
||||||
_queueFullSync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ConnectedModeState.setPopoutBody(_chromeClaimId, root.alignedX, root.renderedAlignedY, root.alignedWidth, root.renderedAlignedHeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
property bool _animSyncQueued: false
|
property bool _animSyncQueued: false
|
||||||
@@ -345,25 +362,19 @@ Item {
|
|||||||
Connections {
|
Connections {
|
||||||
target: contentWindow
|
target: contentWindow
|
||||||
function onVisibleChanged() {
|
function onVisibleChanged() {
|
||||||
if (contentWindow.visible) {
|
if (contentWindow.visible)
|
||||||
if (!root._chromeClaimId)
|
|
||||||
root._chromeClaimId = root._nextChromeClaimId();
|
|
||||||
root._publishConnectedChromeState(true);
|
root._publishConnectedChromeState(true);
|
||||||
} else {
|
else
|
||||||
root._releaseConnectedChromeState();
|
root._releaseConnectedChromeState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: SettingsData
|
target: SettingsData
|
||||||
function onConnectedFrameModeActiveChanged() {
|
function onConnectedFrameModeActiveChanged() {
|
||||||
if (root.frameOwnsConnectedChrome) {
|
if (root.frameOwnsConnectedChrome) {
|
||||||
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name)) {
|
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name))
|
||||||
if (!root._chromeClaimId)
|
|
||||||
root._chromeClaimId = root._nextChromeClaimId();
|
|
||||||
root._publishConnectedChromeState(true);
|
root._publishConnectedChromeState(true);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
root._releaseConnectedChromeState();
|
root._releaseConnectedChromeState();
|
||||||
}
|
}
|
||||||
@@ -376,16 +387,17 @@ Item {
|
|||||||
Connections {
|
Connections {
|
||||||
target: ConnectedModeState
|
target: ConnectedModeState
|
||||||
function onPopoutOwnerIdChanged() {
|
function onPopoutOwnerIdChanged() {
|
||||||
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name) && !ConnectedModeState.hasPopoutOwner(root._chromeClaimId))
|
chromeLease.checkOwnershipRecovery();
|
||||||
root._queueFullSync();
|
}
|
||||||
|
function onSurfaceDescriptorsChanged() {
|
||||||
|
chromeLease.checkStateRecovery();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: PopoutManager
|
target: PopoutManager
|
||||||
function onPopoutChanged() {
|
function onPopoutChanged() {
|
||||||
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name))
|
chromeLease.requestRecovery();
|
||||||
root._queueFullSync();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,34 +418,26 @@ Item {
|
|||||||
|
|
||||||
const screenChanged = _lastOpenedScreen !== null && _lastOpenedScreen !== screen;
|
const screenChanged = _lastOpenedScreen !== null && _lastOpenedScreen !== screen;
|
||||||
if (screenChanged) {
|
if (screenChanged) {
|
||||||
// Hide on this tick so Qt actually tears down the wl_surface; the show
|
|
||||||
// gets deferred below so the unmap is processed before the remap.
|
|
||||||
contentWindow.visible = false;
|
contentWindow.visible = false;
|
||||||
}
|
}
|
||||||
_lastOpenedScreen = screen;
|
_lastOpenedScreen = screen;
|
||||||
PopoutManager.showPopout(popoutHandle);
|
PopoutManager.showPopout(popoutHandle);
|
||||||
|
|
||||||
if (contentContainer) {
|
if (contentContainer) {
|
||||||
// Snap morph closed only on a fresh open; on screen-change re-open we stay at 1
|
|
||||||
// because shouldBeVisible doesn't change and won't drive morph back to 1.
|
|
||||||
if (!shouldBeVisible)
|
if (!shouldBeVisible)
|
||||||
morph.openProgress = 0;
|
morph.openProgress = 0;
|
||||||
_captureChromeAnimTravel();
|
_captureChromeAnimTravel();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.frameOwnsConnectedChrome) {
|
if (root.frameOwnsConnectedChrome) {
|
||||||
_chromeClaimId = _nextChromeClaimId();
|
chromeLease.beginClaim();
|
||||||
_publishConnectedChromeState(true, true);
|
_publishConnectedChromeState(true, true);
|
||||||
} else {
|
} else {
|
||||||
_chromeClaimId = "";
|
chromeLease.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (screenChanged) {
|
if (screenChanged) {
|
||||||
// Defer the show one event-loop tick. Qt coalesces a synchronous
|
// Unmap/remap wl_surface across ticks so blur republishes on the new screen.
|
||||||
// false→true visibility flip into a no-op, leaving WindowBlur committed
|
|
||||||
// to the previous screen's wl_surface. Splitting the flip across ticks
|
|
||||||
// forces a real surface destroy+create so BackgroundEffect.surfaceCreated
|
|
||||||
// fires and the blur region republishes on the new surface.
|
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (!root.shouldBeVisible)
|
if (!root.shouldBeVisible)
|
||||||
return;
|
return;
|
||||||
@@ -552,6 +556,18 @@ Item {
|
|||||||
return Math.abs(value - bound) <= Math.max(1, Theme.hairline(root.dpr) * 2);
|
return Math.abs(value - bound) <= Math.max(1, Theme.hairline(root.dpr) * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Snap positions within connector radius flush to the frame edge (avoids pinched arcs).
|
||||||
|
function _snapNearFrameBound(value, minBound, maxBound, minIsFrame, maxIsFrame) {
|
||||||
|
if (!root.usesConnectedSurfaceChrome || !root.closeFrameGapsActive)
|
||||||
|
return value;
|
||||||
|
const snapDist = Theme.connectedCornerRadius;
|
||||||
|
if (maxIsFrame && value < maxBound && maxBound - value < snapDist && maxBound - value <= value - minBound)
|
||||||
|
return maxBound;
|
||||||
|
if (minIsFrame && value > minBound && value - minBound < snapDist)
|
||||||
|
return minBound;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
function _closeGapClampedToFrameSide(side) {
|
function _closeGapClampedToFrameSide(side) {
|
||||||
if (!root.closeFrameGapsActive)
|
if (!root.closeFrameGapsActive)
|
||||||
return false;
|
return false;
|
||||||
@@ -607,7 +623,6 @@ Item {
|
|||||||
property real renderedAlignedY: alignedY
|
property real renderedAlignedY: alignedY
|
||||||
property real renderedAlignedHeight: alignedHeight
|
property real renderedAlignedHeight: alignedHeight
|
||||||
readonly property bool renderedGeometryGrowing: alignedHeight >= renderedAlignedHeight
|
readonly property bool renderedGeometryGrowing: alignedHeight >= renderedAlignedHeight
|
||||||
// Snap rendered geometry while the entrance morph runs so it doesn't ride a second animation (side-bar ramp).
|
|
||||||
readonly property bool _settlingToOpen: fullHeightSurface && shouldBeVisible && morphAnim.running
|
readonly property bool _settlingToOpen: fullHeightSurface && shouldBeVisible && morphAnim.running
|
||||||
|
|
||||||
Behavior on renderedAlignedY {
|
Behavior on renderedAlignedY {
|
||||||
@@ -657,8 +672,6 @@ Item {
|
|||||||
return 0;
|
return 0;
|
||||||
if (!root.usesConnectedSurfaceChrome)
|
if (!root.usesConnectedSurfaceChrome)
|
||||||
return exclusion;
|
return exclusion;
|
||||||
// In a shared frame corner, the adjacent connected bar already occupies
|
|
||||||
// one rounded-corner radius before the popout's own connector begins.
|
|
||||||
return exclusion + Theme.connectedCornerRadius * 2;
|
return exclusion + Theme.connectedCornerRadius * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -691,16 +704,16 @@ Item {
|
|||||||
|
|
||||||
switch (effectiveBarPosition) {
|
switch (effectiveBarPosition) {
|
||||||
case SettingsData.Position.Left:
|
case SettingsData.Position.Left:
|
||||||
// bar on left: left side is bar-adjacent (popupGap), right side is frame-perpendicular (edgeGap)
|
|
||||||
return Math.max(popupGap, Math.min(screenWidth - popupWidth - edgeGapRight, anchorX));
|
return Math.max(popupGap, Math.min(screenWidth - popupWidth - edgeGapRight, anchorX));
|
||||||
case SettingsData.Position.Right:
|
case SettingsData.Position.Right:
|
||||||
// bar on right: right side is bar-adjacent (popupGap), left side is frame-perpendicular (edgeGap)
|
|
||||||
return Math.max(edgeGapLeft, Math.min(screenWidth - popupWidth - popupGap, anchorX - popupWidth));
|
return Math.max(edgeGapLeft, Math.min(screenWidth - popupWidth - popupGap, anchorX - popupWidth));
|
||||||
default:
|
default:
|
||||||
const rawX = triggerX + (triggerWidth / 2) - (popupWidth / 2);
|
const rawX = triggerX + (triggerWidth / 2) - (popupWidth / 2);
|
||||||
const minX = Math.max(edgeGapLeft, adjacentBarClearance(adjacentBarInfo.leftBar));
|
const clearLeft = adjacentBarClearance(adjacentBarInfo.leftBar);
|
||||||
const maxX = screenWidth - popupWidth - Math.max(edgeGapRight, adjacentBarClearance(adjacentBarInfo.rightBar));
|
const clearRight = adjacentBarClearance(adjacentBarInfo.rightBar);
|
||||||
return Math.max(minX, Math.min(maxX, rawX));
|
const minX = Math.max(edgeGapLeft, clearLeft);
|
||||||
|
const maxX = screenWidth - popupWidth - Math.max(edgeGapRight, clearRight);
|
||||||
|
return _snapNearFrameBound(Math.max(minX, Math.min(maxX, rawX)), minX, maxX, edgeGapLeft >= clearLeft, edgeGapRight >= clearRight);
|
||||||
}
|
}
|
||||||
})(), dpr)
|
})(), dpr)
|
||||||
|
|
||||||
@@ -712,44 +725,34 @@ Item {
|
|||||||
|
|
||||||
switch (effectiveBarPosition) {
|
switch (effectiveBarPosition) {
|
||||||
case SettingsData.Position.Bottom:
|
case SettingsData.Position.Bottom:
|
||||||
// bar on bottom: bottom side is bar-adjacent (popupGap), top side is frame-perpendicular (edgeGap)
|
|
||||||
return Math.max(edgeGapTop, Math.min(screenHeight - popupHeight - popupGap, anchorY - popupHeight));
|
return Math.max(edgeGapTop, Math.min(screenHeight - popupHeight - popupGap, anchorY - popupHeight));
|
||||||
case SettingsData.Position.Top:
|
case SettingsData.Position.Top:
|
||||||
// bar on top: top side is bar-adjacent (popupGap), bottom side is frame-perpendicular (edgeGap)
|
|
||||||
return Math.max(popupGap, Math.min(screenHeight - popupHeight - edgeGapBottom, anchorY));
|
return Math.max(popupGap, Math.min(screenHeight - popupHeight - edgeGapBottom, anchorY));
|
||||||
default:
|
default:
|
||||||
const rawY = triggerY - (popupHeight / 2);
|
const rawY = triggerY - (popupHeight / 2);
|
||||||
const minY = Math.max(edgeGapTop, adjacentBarClearance(adjacentBarInfo.topBar));
|
const clearTop = adjacentBarClearance(adjacentBarInfo.topBar);
|
||||||
const maxY = screenHeight - popupHeight - Math.max(edgeGapBottom, adjacentBarClearance(adjacentBarInfo.bottomBar));
|
const clearBottom = adjacentBarClearance(adjacentBarInfo.bottomBar);
|
||||||
return Math.max(minY, Math.min(maxY, rawY));
|
const minY = Math.max(edgeGapTop, clearTop);
|
||||||
|
const maxY = screenHeight - popupHeight - Math.max(edgeGapBottom, clearBottom);
|
||||||
|
return _snapNearFrameBound(Math.max(minY, Math.min(maxY, rawY)), minY, maxY, edgeGapTop >= clearTop, edgeGapBottom >= clearBottom);
|
||||||
}
|
}
|
||||||
})(), dpr)
|
})(), dpr)
|
||||||
|
|
||||||
readonly property real triggeringBarLeftExclusion: (effectiveBarPosition === SettingsData.Position.Left && barWidth > 0) ? Math.max(0, barX + barWidth) : 0
|
readonly property real maskX: _dismissZone.x
|
||||||
readonly property real triggeringBarTopExclusion: (effectiveBarPosition === SettingsData.Position.Top && barHeight > 0) ? Math.max(0, barY + barHeight) : 0
|
readonly property real maskY: _dismissZone.y
|
||||||
readonly property real triggeringBarRightExclusion: (effectiveBarPosition === SettingsData.Position.Right && barWidth > 0) ? Math.max(0, screenWidth - barX) : 0
|
readonly property real maskWidth: _dismissZone.width
|
||||||
readonly property real triggeringBarBottomExclusion: (effectiveBarPosition === SettingsData.Position.Bottom && barHeight > 0) ? Math.max(0, screenHeight - barY) : 0
|
readonly property real maskHeight: _dismissZone.height
|
||||||
|
|
||||||
readonly property real maskX: {
|
DismissZone {
|
||||||
const adjacentLeftBar = adjacentBarInfo?.leftBar ?? 0;
|
id: _dismissZone
|
||||||
return Math.max(triggeringBarLeftExclusion, adjacentLeftBar);
|
barPosition: root.effectiveBarPosition
|
||||||
}
|
barX: root.barX
|
||||||
|
barY: root.barY
|
||||||
readonly property real maskY: {
|
barWidth: root.barWidth
|
||||||
const adjacentTopBar = adjacentBarInfo?.topBar ?? 0;
|
barHeight: root.barHeight
|
||||||
return Math.max(triggeringBarTopExclusion, adjacentTopBar);
|
screenWidth: root.screenWidth
|
||||||
}
|
screenHeight: root.screenHeight
|
||||||
|
adjacentBarInfo: root.adjacentBarInfo
|
||||||
readonly property real maskWidth: {
|
|
||||||
const adjacentRightBar = adjacentBarInfo?.rightBar ?? 0;
|
|
||||||
const rightExclusion = Math.max(triggeringBarRightExclusion, adjacentRightBar);
|
|
||||||
return Math.max(100, screenWidth - maskX - rightExclusion);
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property real maskHeight: {
|
|
||||||
const adjacentBottomBar = adjacentBarInfo?.bottomBar ?? 0;
|
|
||||||
const bottomExclusion = Math.max(triggeringBarBottomExclusion, adjacentBottomBar);
|
|
||||||
return Math.max(100, screenHeight - maskY - bottomExclusion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
@@ -764,10 +767,8 @@ Item {
|
|||||||
blurEnabled: root.effectiveSurfaceBlurEnabled && !root.frameOwnsConnectedChrome
|
blurEnabled: root.effectiveSurfaceBlurEnabled && !root.frameOwnsConnectedChrome
|
||||||
|
|
||||||
readonly property real s: Math.min(1, contentContainer.scaleValue)
|
readonly property real s: Math.min(1, contentContainer.scaleValue)
|
||||||
readonly property bool trackBlurFromBarEdge: root.usesConnectedSurfaceChrome || Theme.isDirectionalEffect
|
readonly property bool trackBlurFromBarEdge: root.usesConnectedSurfaceChrome
|
||||||
|
|
||||||
// Directional popouts clip to the bar edge, so the blur needs to grow from
|
|
||||||
// that same edge instead of translating through the bar before settling.
|
|
||||||
readonly property real _dyClamp: (contentContainer.barTop || contentContainer.barBottom) ? Math.max(-contentContainer.height, Math.min(contentContainer.animY, contentContainer.height)) : 0
|
readonly property real _dyClamp: (contentContainer.barTop || contentContainer.barBottom) ? Math.max(-contentContainer.height, Math.min(contentContainer.animY, contentContainer.height)) : 0
|
||||||
readonly property real _dxClamp: (contentContainer.barLeft || contentContainer.barRight) ? Math.max(-contentContainer.width, Math.min(contentContainer.animX, contentContainer.width)) : 0
|
readonly property real _dxClamp: (contentContainer.barLeft || contentContainer.barRight) ? Math.max(-contentContainer.width, Math.min(contentContainer.animX, contentContainer.width)) : 0
|
||||||
|
|
||||||
@@ -781,17 +782,7 @@ Item {
|
|||||||
WlrLayershell.namespace: root.layerNamespace
|
WlrLayershell.namespace: root.layerNamespace
|
||||||
WlrLayershell.layer: root.effectivePopoutLayer
|
WlrLayershell.layer: root.effectivePopoutLayer
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: {
|
WlrLayershell.keyboardFocus: KeyboardFocus.keyboardFocus(shouldBeVisible, customKeyboardFocus)
|
||||||
if (PopoutManager.screenshotActive)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (customKeyboardFocus !== null)
|
|
||||||
return customKeyboardFocus;
|
|
||||||
if (!shouldBeVisible)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (CompositorService.useHyprlandFocusGrab)
|
|
||||||
return WlrKeyboardFocus.OnDemand;
|
|
||||||
return WlrKeyboardFocus.Exclusive;
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property bool _fullHeight: root.fullHeightSurface
|
readonly property bool _fullHeight: root.fullHeightSurface
|
||||||
anchors {
|
anchors {
|
||||||
@@ -813,7 +804,6 @@ Item {
|
|||||||
|
|
||||||
Region {
|
Region {
|
||||||
id: contentInputMask
|
id: contentInputMask
|
||||||
// Use bar-aware mask so bar widget clicks pass through when a popout is open.
|
|
||||||
item: (shouldBeVisible && backgroundInteractive) ? backgroundDismissalMask : contentMaskRect
|
item: (shouldBeVisible && backgroundInteractive) ? backgroundDismissalMask : contentMaskRect
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -924,7 +914,6 @@ Item {
|
|||||||
|
|
||||||
readonly property real computedScaleCollapsed: root.animationScaleCollapsed
|
readonly property real computedScaleCollapsed: root.animationScaleCollapsed
|
||||||
|
|
||||||
// openProgress: 0 = closed (at offset, scaleCollapsed), 1 = open (at 0, scale 1).
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: morph
|
id: morph
|
||||||
property real openProgress: 0
|
property real openProgress: 0
|
||||||
@@ -971,7 +960,6 @@ Item {
|
|||||||
|
|
||||||
clip: shouldClip
|
clip: shouldClip
|
||||||
|
|
||||||
// Bound the clipping strictly to the bar side, allowing massive overflow on the other 3 sides for shadows
|
|
||||||
x: shouldClip ? (contentContainer.barLeft ? -connectedClipAllowance : -clipOversize) : 0
|
x: shouldClip ? (contentContainer.barLeft ? -connectedClipAllowance : -clipOversize) : 0
|
||||||
y: shouldClip ? (contentContainer.barTop ? -connectedClipAllowance : -clipOversize) : 0
|
y: shouldClip ? (contentContainer.barTop ? -connectedClipAllowance : -clipOversize) : 0
|
||||||
|
|
||||||
@@ -1008,22 +996,13 @@ Item {
|
|||||||
|
|
||||||
ElevationShadow {
|
ElevationShadow {
|
||||||
id: shadowSource
|
id: shadowSource
|
||||||
readonly property real connectorExtent: root.usesConnectedSurfaceChrome ? Theme.connectedCornerRadius : 0
|
visible: !root.usesConnectedSurfaceChrome
|
||||||
readonly property real extraLeft: root.usesConnectedSurfaceChrome && (contentContainer.barTop || contentContainer.barBottom) ? connectorExtent : 0
|
width: rollOutAdjuster.baseWidth
|
||||||
readonly property real extraRight: root.usesConnectedSurfaceChrome && (contentContainer.barTop || contentContainer.barBottom) ? connectorExtent : 0
|
height: rollOutAdjuster.baseHeight
|
||||||
readonly property real extraTop: root.usesConnectedSurfaceChrome && (contentContainer.barLeft || contentContainer.barRight) ? connectorExtent : 0
|
|
||||||
readonly property real extraBottom: root.usesConnectedSurfaceChrome && (contentContainer.barLeft || contentContainer.barRight) ? connectorExtent : 0
|
|
||||||
readonly property real bodyX: extraLeft
|
|
||||||
readonly property real bodyY: extraTop
|
|
||||||
readonly property real bodyWidth: rollOutAdjuster.baseWidth
|
|
||||||
readonly property real bodyHeight: rollOutAdjuster.baseHeight
|
|
||||||
|
|
||||||
width: rollOutAdjuster.baseWidth + extraLeft + extraRight
|
|
||||||
height: rollOutAdjuster.baseHeight + extraTop + extraBottom
|
|
||||||
opacity: contentWrapper.publishedOpacity
|
opacity: contentWrapper.publishedOpacity
|
||||||
scale: contentWrapper.scale
|
scale: contentWrapper.scale
|
||||||
x: contentWrapper.x - extraLeft
|
x: contentWrapper.x
|
||||||
y: contentWrapper.y - extraTop
|
y: contentWrapper.y
|
||||||
level: root.shadowLevel
|
level: root.shadowLevel
|
||||||
direction: root.effectiveShadowDirection
|
direction: root.effectiveShadowDirection
|
||||||
fallbackOffset: root.shadowFallbackOffset
|
fallbackOffset: root.shadowFallbackOffset
|
||||||
@@ -1035,49 +1014,49 @@ Item {
|
|||||||
targetColor: contentContainer.surfaceColor
|
targetColor: contentContainer.surfaceColor
|
||||||
borderColor: contentContainer.surfaceBorderColor
|
borderColor: contentContainer.surfaceBorderColor
|
||||||
borderWidth: contentContainer.surfaceBorderWidth
|
borderWidth: contentContainer.surfaceBorderWidth
|
||||||
useCustomSource: root.usesConnectedSurfaceChrome
|
|
||||||
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive) && !root.frameOwnsConnectedChrome
|
shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive) && !root.frameOwnsConnectedChrome
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
anchors.fill: parent
|
id: localChrome
|
||||||
visible: root.usesLocalConnectedSurfaceChrome
|
visible: root.usesLocalConnectedSurfaceChrome
|
||||||
clip: false
|
|
||||||
|
|
||||||
Rectangle {
|
readonly property real extraLeft: (contentContainer.barTop || contentContainer.barBottom) ? Theme.connectedCornerRadius : 0
|
||||||
x: shadowSource.bodyX
|
readonly property real extraTop: (contentContainer.barLeft || contentContainer.barRight) ? Theme.connectedCornerRadius : 0
|
||||||
y: shadowSource.bodyY
|
|
||||||
width: shadowSource.bodyWidth
|
|
||||||
height: shadowSource.bodyHeight
|
|
||||||
topLeftRadius: contentContainer.surfaceTopLeftRadius
|
|
||||||
topRightRadius: contentContainer.surfaceTopRightRadius
|
|
||||||
bottomLeftRadius: contentContainer.surfaceBottomLeftRadius
|
|
||||||
bottomRightRadius: contentContainer.surfaceBottomRightRadius
|
|
||||||
color: contentContainer.surfaceColor
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectedCorner {
|
readonly property bool shadowsOn: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive)
|
||||||
visible: root.usesConnectedSurfaceChrome
|
readonly property real shadowBlurPx: root.shadowLevel && root.shadowLevel.blurPx !== undefined ? root.shadowLevel.blurPx : 0
|
||||||
barSide: contentContainer.connectedBarSide
|
readonly property real shadowSpreadPx: root.shadowLevel && root.shadowLevel.spreadPx !== undefined ? root.shadowLevel.spreadPx : 0
|
||||||
placement: "left"
|
readonly property real shadowOffsetX: Theme.elevationOffsetXFor(root.shadowLevel, root.effectiveShadowDirection, root.shadowFallbackOffset)
|
||||||
spacing: 0
|
readonly property real shadowOffsetY: Theme.elevationOffsetYFor(root.shadowLevel, root.effectiveShadowDirection, root.shadowFallbackOffset)
|
||||||
connectorRadius: Theme.connectedCornerRadius
|
readonly property color shadowTint: Theme.elevationShadowColor(root.shadowLevel)
|
||||||
color: contentContainer.surfaceColor
|
readonly property var ambient: Theme.elevationAmbient(root.shadowLevel)
|
||||||
dpr: root.dpr
|
readonly property real pad: shadowsOn ? Math.ceil(Math.max(shadowBlurPx + shadowSpreadPx + Math.max(Math.abs(shadowOffsetX), Math.abs(shadowOffsetY)), ambient.blurPx + ambient.spreadPx) + 2) : 0
|
||||||
x: Theme.snap(ConnectorGeometry.connectorX(contentContainer.connectedBarSide, shadowSource.bodyX, shadowSource.bodyWidth, placement, spacing, Theme.connectedCornerRadius), root.dpr)
|
|
||||||
y: Theme.snap(ConnectorGeometry.connectorY(contentContainer.connectedBarSide, shadowSource.bodyY, shadowSource.bodyHeight, placement, spacing, Theme.connectedCornerRadius), root.dpr)
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectedCorner {
|
width: rollOutAdjuster.baseWidth + extraLeft * 2
|
||||||
visible: root.usesConnectedSurfaceChrome
|
height: rollOutAdjuster.baseHeight + extraTop * 2
|
||||||
barSide: contentContainer.connectedBarSide
|
opacity: contentWrapper.publishedOpacity
|
||||||
placement: "right"
|
scale: contentWrapper.scale
|
||||||
spacing: 0
|
x: contentWrapper.x - extraLeft
|
||||||
connectorRadius: Theme.connectedCornerRadius
|
y: contentWrapper.y - extraTop
|
||||||
color: contentContainer.surfaceColor
|
|
||||||
dpr: root.dpr
|
ShaderEffect {
|
||||||
x: Theme.snap(ConnectorGeometry.connectorX(contentContainer.connectedBarSide, shadowSource.bodyX, shadowSource.bodyWidth, placement, spacing, Theme.connectedCornerRadius), root.dpr)
|
anchors.fill: parent
|
||||||
y: Theme.snap(ConnectorGeometry.connectorY(contentContainer.connectedBarSide, shadowSource.bodyY, shadowSource.bodyHeight, placement, spacing, Theme.connectedCornerRadius), root.dpr)
|
anchors.topMargin: contentContainer.barTop ? 0 : -localChrome.pad
|
||||||
}
|
anchors.bottomMargin: contentContainer.barBottom ? 0 : -localChrome.pad
|
||||||
|
anchors.leftMargin: contentContainer.barLeft ? 0 : -localChrome.pad
|
||||||
|
anchors.rightMargin: contentContainer.barRight ? 0 : -localChrome.pad
|
||||||
|
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/connected_chrome.frag.qsb")
|
||||||
|
|
||||||
|
property real widthPx: width
|
||||||
|
property real heightPx: height
|
||||||
|
property vector4d surfaceColor: Qt.vector4d(contentContainer.surfaceColor.r, contentContainer.surfaceColor.g, contentContainer.surfaceColor.b, contentContainer.surfaceColor.a)
|
||||||
|
property vector4d shadowColor: Qt.vector4d(localChrome.shadowTint.r, localChrome.shadowTint.g, localChrome.shadowTint.b, localChrome.shadowsOn ? localChrome.shadowTint.a : 0)
|
||||||
|
property vector4d shadowParam: Qt.vector4d(Math.max(0, localChrome.shadowBlurPx), localChrome.shadowSpreadPx, localChrome.shadowOffsetX, localChrome.shadowOffsetY)
|
||||||
|
property vector4d ambientParam: Qt.vector4d(localChrome.ambient.blurPx, localChrome.ambient.spreadPx, localChrome.shadowsOn ? localChrome.ambient.alpha : 0, 0)
|
||||||
|
property vector4d bodyRect: Qt.vector4d((contentContainer.barLeft ? 0 : localChrome.pad) + localChrome.extraLeft, (contentContainer.barTop ? 0 : localChrome.pad) + localChrome.extraTop, rollOutAdjuster.baseWidth, rollOutAdjuster.baseHeight)
|
||||||
|
property vector4d cornerRadius: Qt.vector4d(contentContainer.surfaceTopLeftRadius, contentContainer.surfaceTopRightRadius, contentContainer.surfaceBottomRightRadius, contentContainer.surfaceBottomLeftRadius)
|
||||||
|
property vector4d edgeParam: Qt.vector4d(contentContainer.barTop ? 0 : (contentContainer.barBottom ? 1 : (contentContainer.barLeft ? 2 : 3)), Theme.connectedCornerRadius, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1135,8 +1114,6 @@ Item {
|
|||||||
Connections {
|
Connections {
|
||||||
target: contentWindow
|
target: contentWindow
|
||||||
function onVisibleChanged() {
|
function onVisibleChanged() {
|
||||||
// open() flips contentWindow.visible to rebind the layer surface to
|
|
||||||
// a new screen; don't deactivate the wrapper while still open.
|
|
||||||
if (!contentWindow.visible && !root.shouldBeVisible)
|
if (!contentWindow.visible && !root.shouldBeVisible)
|
||||||
contentWrapper._renderActive = false;
|
contentWrapper._renderActive = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ Item {
|
|||||||
property Component overlayContent: null
|
property Component overlayContent: null
|
||||||
property alias overlayLoader: overlayLoader
|
property alias overlayLoader: overlayLoader
|
||||||
readonly property alias backgroundWindow: backgroundWindow
|
readonly property alias backgroundWindow: backgroundWindow
|
||||||
|
readonly property alias contentWindow: contentWindow
|
||||||
property real popupWidth: 400
|
property real popupWidth: 400
|
||||||
property real popupHeight: 300
|
property real popupHeight: 300
|
||||||
property real triggerX: 0
|
property real triggerX: 0
|
||||||
@@ -494,31 +495,21 @@ Item {
|
|||||||
}
|
}
|
||||||
})(), dpr)
|
})(), dpr)
|
||||||
|
|
||||||
readonly property real triggeringBarLeftExclusion: (effectiveBarPosition === SettingsData.Position.Left && barWidth > 0) ? Math.max(0, barX + barWidth) : 0
|
readonly property real maskX: _dismissZone.x
|
||||||
readonly property real triggeringBarTopExclusion: (effectiveBarPosition === SettingsData.Position.Top && barHeight > 0) ? Math.max(0, barY + barHeight) : 0
|
readonly property real maskY: _dismissZone.y
|
||||||
readonly property real triggeringBarRightExclusion: (effectiveBarPosition === SettingsData.Position.Right && barWidth > 0) ? Math.max(0, screenWidth - barX) : 0
|
readonly property real maskWidth: _dismissZone.width
|
||||||
readonly property real triggeringBarBottomExclusion: (effectiveBarPosition === SettingsData.Position.Bottom && barHeight > 0) ? Math.max(0, screenHeight - barY) : 0
|
readonly property real maskHeight: _dismissZone.height
|
||||||
|
|
||||||
readonly property real maskX: {
|
DismissZone {
|
||||||
const adjacentLeftBar = adjacentBarInfo?.leftBar ?? 0;
|
id: _dismissZone
|
||||||
return Math.max(triggeringBarLeftExclusion, adjacentLeftBar);
|
barPosition: root.effectiveBarPosition
|
||||||
}
|
barX: root.barX
|
||||||
|
barY: root.barY
|
||||||
readonly property real maskY: {
|
barWidth: root.barWidth
|
||||||
const adjacentTopBar = adjacentBarInfo?.topBar ?? 0;
|
barHeight: root.barHeight
|
||||||
return Math.max(triggeringBarTopExclusion, adjacentTopBar);
|
screenWidth: root.screenWidth
|
||||||
}
|
screenHeight: root.screenHeight
|
||||||
|
adjacentBarInfo: root.adjacentBarInfo
|
||||||
readonly property real maskWidth: {
|
|
||||||
const adjacentRightBar = adjacentBarInfo?.rightBar ?? 0;
|
|
||||||
const rightExclusion = Math.max(triggeringBarRightExclusion, adjacentRightBar);
|
|
||||||
return Math.max(100, screenWidth - maskX - rightExclusion);
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property real maskHeight: {
|
|
||||||
const adjacentBottomBar = adjacentBarInfo?.bottomBar ?? 0;
|
|
||||||
const bottomExclusion = Math.max(triggeringBarBottomExclusion, adjacentBottomBar);
|
|
||||||
return Math.max(100, screenHeight - maskY - bottomExclusion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
@@ -598,31 +589,25 @@ Item {
|
|||||||
id: popoutBlur
|
id: popoutBlur
|
||||||
targetWindow: contentWindow
|
targetWindow: contentWindow
|
||||||
readonly property real s: Math.min(1, contentContainer.scaleValue)
|
readonly property real s: Math.min(1, contentContainer.scaleValue)
|
||||||
readonly property bool trackBlurFromBarEdge: root.fluidStandaloneActive
|
|
||||||
readonly property real op: Math.max(0, Math.min(1, (morph.openProgress - 0.08) * 1.6))
|
readonly property real op: Math.max(0, Math.min(1, (morph.openProgress - 0.08) * 1.6))
|
||||||
readonly property bool blurAlive: trackBlurFromBarEdge ? (contentContainer.revealWidth > 0 && contentContainer.revealHeight > 0) : root.shouldBeVisible
|
readonly property bool revealClipActive: root.fluidStandaloneActive
|
||||||
|
|
||||||
blurX: trackBlurFromBarEdge ? contentContainer.x + contentContainer.revealX : contentContainer.x + contentContainer.width * (1 - s * op) * 0.5 + Theme.snap(contentContainer.animX, root.dpr)
|
blurX: revealClipActive ? contentContainer.x : contentContainer.x + contentContainer.width * (1 - s * op) * 0.5 + Theme.snap(contentContainer.animX, root.dpr)
|
||||||
blurY: trackBlurFromBarEdge ? contentContainer.y + contentContainer.revealY : contentContainer.y + contentContainer.height * (1 - s * op) * 0.5 + Theme.snap(contentContainer.animY, root.dpr)
|
blurY: revealClipActive ? contentContainer.y : contentContainer.y + contentContainer.height * (1 - s * op) * 0.5 + Theme.snap(contentContainer.animY, root.dpr)
|
||||||
blurWidth: blurAlive ? (trackBlurFromBarEdge ? contentContainer.revealWidth : contentContainer.width * s * op) : 0
|
blurWidth: root.shouldBeVisible ? (revealClipActive ? contentContainer.width : contentContainer.width * s * op) : 0
|
||||||
blurHeight: blurAlive ? (trackBlurFromBarEdge ? contentContainer.revealHeight : contentContainer.height * s * op) : 0
|
blurHeight: root.shouldBeVisible ? (revealClipActive ? contentContainer.height : contentContainer.height * s * op) : 0
|
||||||
blurRadius: Theme.cornerRadius
|
blurRadius: Theme.cornerRadius
|
||||||
|
clipEnabled: revealClipActive
|
||||||
|
clipX: contentContainer.x + contentContainer.revealX
|
||||||
|
clipY: contentContainer.y + contentContainer.revealY
|
||||||
|
clipWidth: root.shouldBeVisible ? contentContainer.revealWidth : 0
|
||||||
|
clipHeight: root.shouldBeVisible ? contentContainer.revealHeight : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
WlrLayershell.namespace: root.layerNamespace
|
WlrLayershell.namespace: root.layerNamespace
|
||||||
WlrLayershell.layer: root.effectivePopoutLayer
|
WlrLayershell.layer: root.effectivePopoutLayer
|
||||||
WlrLayershell.exclusiveZone: -1
|
WlrLayershell.exclusiveZone: -1
|
||||||
WlrLayershell.keyboardFocus: {
|
WlrLayershell.keyboardFocus: KeyboardFocus.keyboardFocus(shouldBeVisible, customKeyboardFocus)
|
||||||
if (PopoutManager.screenshotActive)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (customKeyboardFocus !== null)
|
|
||||||
return customKeyboardFocus;
|
|
||||||
if (!shouldBeVisible)
|
|
||||||
return WlrKeyboardFocus.None;
|
|
||||||
if (CompositorService.useHyprlandFocusGrab)
|
|
||||||
return WlrKeyboardFocus.OnDemand;
|
|
||||||
return WlrKeyboardFocus.Exclusive;
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
left: true
|
left: true
|
||||||
@@ -721,6 +706,8 @@ Item {
|
|||||||
QtObject {
|
QtObject {
|
||||||
id: morph
|
id: morph
|
||||||
property real openProgress: 0
|
property real openProgress: 0
|
||||||
|
onOpenProgressChanged: if (root.fluidStandaloneActive)
|
||||||
|
root._kickBlurCommit()
|
||||||
Behavior on openProgress {
|
Behavior on openProgress {
|
||||||
enabled: root.animationsEnabled
|
enabled: root.animationsEnabled
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int barPosition: 0
|
||||||
|
property real barX: 0
|
||||||
|
property real barY: 0
|
||||||
|
property real barWidth: 0
|
||||||
|
property real barHeight: 0
|
||||||
|
property real screenWidth: 0
|
||||||
|
property real screenHeight: 0
|
||||||
|
property var adjacentBarInfo: null
|
||||||
|
|
||||||
|
readonly property real _leftExclusion: (barPosition === SettingsData.Position.Left && barWidth > 0) ? Math.max(0, barX + barWidth) : 0
|
||||||
|
readonly property real _topExclusion: (barPosition === SettingsData.Position.Top && barHeight > 0) ? Math.max(0, barY + barHeight) : 0
|
||||||
|
readonly property real _rightExclusion: (barPosition === SettingsData.Position.Right && barWidth > 0) ? Math.max(0, screenWidth - barX) : 0
|
||||||
|
readonly property real _bottomExclusion: (barPosition === SettingsData.Position.Bottom && barHeight > 0) ? Math.max(0, screenHeight - barY) : 0
|
||||||
|
|
||||||
|
readonly property real x: Math.max(_leftExclusion, adjacentBarInfo?.leftBar ?? 0)
|
||||||
|
readonly property real y: Math.max(_topExclusion, adjacentBarInfo?.topBar ?? 0)
|
||||||
|
readonly property real width: Math.max(100, screenWidth - x - Math.max(_rightExclusion, adjacentBarInfo?.rightBar ?? 0))
|
||||||
|
readonly property real height: Math.max(100, screenHeight - y - Math.max(_bottomExclusion, adjacentBarInfo?.bottomBar ?? 0))
|
||||||
|
}
|
||||||
@@ -16,6 +16,11 @@ Item {
|
|||||||
property real blurWidth: 0
|
property real blurWidth: 0
|
||||||
property real blurHeight: 0
|
property real blurHeight: 0
|
||||||
property real blurRadius: 0
|
property real blurRadius: 0
|
||||||
|
property bool clipEnabled: false
|
||||||
|
property real clipX: blurX
|
||||||
|
property real clipY: blurY
|
||||||
|
property real clipWidth: blurWidth
|
||||||
|
property real clipHeight: blurHeight
|
||||||
|
|
||||||
readonly property bool _active: blurEnabled && BlurService.enabled && !!targetWindow
|
readonly property bool _active: blurEnabled && BlurService.enabled && !!targetWindow
|
||||||
|
|
||||||
@@ -26,6 +31,14 @@ Item {
|
|||||||
width: root.blurWidth
|
width: root.blurWidth
|
||||||
height: root.blurHeight
|
height: root.blurHeight
|
||||||
radius: root.blurRadius
|
radius: root.blurRadius
|
||||||
|
|
||||||
|
Region {
|
||||||
|
intersection: Intersection.Intersect
|
||||||
|
x: root.clipEnabled ? root.clipX : root.blurX
|
||||||
|
y: root.clipEnabled ? root.clipY : root.blurY
|
||||||
|
width: root.clipEnabled ? root.clipWidth : root.blurWidth
|
||||||
|
height: root.clipEnabled ? root.clipHeight : root.blurHeight
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _apply() {
|
function _apply() {
|
||||||
@@ -39,10 +52,7 @@ Item {
|
|||||||
targetWindow.BackgroundEffect.blurRegion = null;
|
targetWindow.BackgroundEffect.blurRegion = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force BackgroundEffect to re-publish the blur region on the current wl_surface.
|
// Re-publish blur region after wl_surface remaps (e.g. screen change).
|
||||||
// Clearing first bypasses Quickshell's same-Region dedup in BackgroundEffect::setBlurRegion,
|
|
||||||
// setting pendingBlurRegion=true so the next polish actually ships the region — needed
|
|
||||||
// when the underlying surface has been remapped (e.g. PanelWindow.screen change).
|
|
||||||
function kick() {
|
function kick() {
|
||||||
if (!targetWindow)
|
if (!targetWindow)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -546,6 +546,7 @@ def main():
|
|||||||
output_path = script_dir / "settings_search_index.json"
|
output_path = script_dir / "settings_search_index.json"
|
||||||
with open(output_path, "w", encoding="utf-8") as f:
|
with open(output_path, "w", encoding="utf-8") as f:
|
||||||
json.dump(all_entries, f, indent=2, ensure_ascii=False)
|
json.dump(all_entries, f, indent=2, ensure_ascii=False)
|
||||||
|
f.write("\n")
|
||||||
|
|
||||||
print(f"Found {len(settings_entries)} searchable settings")
|
print(f"Found {len(settings_entries)} searchable settings")
|
||||||
print(f"Found {len(tab_entries)} tab entries")
|
print(f"Found {len(tab_entries)} tab entries")
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
"targetable",
|
"targetable",
|
||||||
"wallpaper"
|
"wallpaper"
|
||||||
],
|
],
|
||||||
|
"icon": "blur_on",
|
||||||
"description": "Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.",
|
"description": "Enable compositor-targetable blur layer (namespace: dms:blurwallpaper). Requires manual niri configuration.",
|
||||||
"conditionKey": "isNiri"
|
"conditionKey": "isNiri"
|
||||||
},
|
},
|
||||||
@@ -727,21 +728,6 @@
|
|||||||
],
|
],
|
||||||
"icon": "dashboard"
|
"icon": "dashboard"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"section": "_tab_3",
|
|
||||||
"label": "Dank Bar",
|
|
||||||
"tabIndex": 3,
|
|
||||||
"category": "Dank Bar",
|
|
||||||
"keywords": [
|
|
||||||
"bar",
|
|
||||||
"dank",
|
|
||||||
"panel",
|
|
||||||
"statusbar",
|
|
||||||
"taskbar",
|
|
||||||
"topbar"
|
|
||||||
],
|
|
||||||
"icon": "toolbar"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"section": "barDisplay",
|
"section": "barDisplay",
|
||||||
"label": "Display Assignment",
|
"label": "Display Assignment",
|
||||||
@@ -777,30 +763,19 @@
|
|||||||
"icon": "vertical_align_center"
|
"icon": "vertical_align_center"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"section": "barSpacing",
|
"section": "_tab_3",
|
||||||
"label": "Spacing",
|
"label": "Settings",
|
||||||
"tabIndex": 3,
|
"tabIndex": 3,
|
||||||
"category": "Dank Bar",
|
"category": "Dank Bar",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"bar",
|
"bar",
|
||||||
"between",
|
|
||||||
"dank",
|
"dank",
|
||||||
"edges",
|
|
||||||
"gap",
|
|
||||||
"gaps",
|
|
||||||
"margin",
|
|
||||||
"margins",
|
|
||||||
"padding",
|
|
||||||
"panel",
|
"panel",
|
||||||
"screen",
|
"settings",
|
||||||
"space",
|
|
||||||
"spacing",
|
|
||||||
"statusbar",
|
"statusbar",
|
||||||
"taskbar",
|
|
||||||
"topbar"
|
"topbar"
|
||||||
],
|
],
|
||||||
"icon": "space_bar",
|
"icon": "tune"
|
||||||
"description": "Space between the bar and screen edges"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"section": "barUseOverlayLayer",
|
"section": "barUseOverlayLayer",
|
||||||
@@ -1528,6 +1503,19 @@
|
|||||||
"windows"
|
"windows"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "dockTransparency",
|
||||||
|
"label": "Opacity",
|
||||||
|
"tabIndex": 5,
|
||||||
|
"category": "Dock",
|
||||||
|
"keywords": [
|
||||||
|
"dock",
|
||||||
|
"launcher bar",
|
||||||
|
"opacity",
|
||||||
|
"taskbar"
|
||||||
|
],
|
||||||
|
"icon": "opacity"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "dockTrashFileManager",
|
"section": "dockTrashFileManager",
|
||||||
"label": "Open Trash With",
|
"label": "Open Trash With",
|
||||||
@@ -1745,23 +1733,6 @@
|
|||||||
],
|
],
|
||||||
"icon": "space_bar"
|
"icon": "space_bar"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"section": "dockTransparency",
|
|
||||||
"label": "Transparency",
|
|
||||||
"tabIndex": 5,
|
|
||||||
"category": "Dock",
|
|
||||||
"keywords": [
|
|
||||||
"alpha",
|
|
||||||
"dock",
|
|
||||||
"launcher bar",
|
|
||||||
"opacity",
|
|
||||||
"taskbar",
|
|
||||||
"translucent",
|
|
||||||
"transparency",
|
|
||||||
"transparent"
|
|
||||||
],
|
|
||||||
"icon": "opacity"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"section": "dockTrash",
|
"section": "dockTrash",
|
||||||
"label": "Trash",
|
"label": "Trash",
|
||||||
@@ -1798,21 +1769,6 @@
|
|||||||
],
|
],
|
||||||
"description": "Place the dock on the Wayland overlay layer"
|
"description": "Place the dock on the Wayland overlay layer"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"section": "_tab_6",
|
|
||||||
"label": "Appearance",
|
|
||||||
"tabIndex": 6,
|
|
||||||
"category": "Dank Bar",
|
|
||||||
"keywords": [
|
|
||||||
"appearance",
|
|
||||||
"bar",
|
|
||||||
"dank",
|
|
||||||
"panel",
|
|
||||||
"statusbar",
|
|
||||||
"topbar"
|
|
||||||
],
|
|
||||||
"icon": "palette"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"section": "barBorder",
|
"section": "barBorder",
|
||||||
"label": "Border",
|
"label": "Border",
|
||||||
@@ -1862,6 +1818,21 @@
|
|||||||
"icon": "rounded_corner",
|
"icon": "rounded_corner",
|
||||||
"description": "Remove corner rounding from the bar"
|
"description": "Remove corner rounding from the bar"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "_tab_6",
|
||||||
|
"label": "Dank Bar",
|
||||||
|
"tabIndex": 6,
|
||||||
|
"category": "Dank Bar",
|
||||||
|
"keywords": [
|
||||||
|
"bar",
|
||||||
|
"dank",
|
||||||
|
"panel",
|
||||||
|
"statusbar",
|
||||||
|
"taskbar",
|
||||||
|
"topbar"
|
||||||
|
],
|
||||||
|
"icon": "toolbar"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "barAppearance",
|
"section": "barAppearance",
|
||||||
"label": "Dank Bar",
|
"label": "Dank Bar",
|
||||||
@@ -1982,6 +1953,25 @@
|
|||||||
],
|
],
|
||||||
"description": "Use a fixed shadow direction for this bar"
|
"description": "Use a fixed shadow direction for this bar"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "barTransparency",
|
||||||
|
"label": "Opacity",
|
||||||
|
"tabIndex": 6,
|
||||||
|
"category": "Dank Bar",
|
||||||
|
"keywords": [
|
||||||
|
"background",
|
||||||
|
"bar",
|
||||||
|
"controls",
|
||||||
|
"dank",
|
||||||
|
"opacity",
|
||||||
|
"panel",
|
||||||
|
"statusbar",
|
||||||
|
"taskbar",
|
||||||
|
"topbar"
|
||||||
|
],
|
||||||
|
"icon": "opacity",
|
||||||
|
"description": "Controls opacity of the bar background"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "barShadow",
|
"section": "barShadow",
|
||||||
"label": "Shadow Override",
|
"label": "Shadow Override",
|
||||||
@@ -2002,6 +1992,32 @@
|
|||||||
"icon": "layers",
|
"icon": "layers",
|
||||||
"description": "Override the global shadow with per-bar settings"
|
"description": "Override the global shadow with per-bar settings"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "barSpacing",
|
||||||
|
"label": "Spacing",
|
||||||
|
"tabIndex": 6,
|
||||||
|
"category": "Dank Bar",
|
||||||
|
"keywords": [
|
||||||
|
"bar",
|
||||||
|
"between",
|
||||||
|
"dank",
|
||||||
|
"edges",
|
||||||
|
"gap",
|
||||||
|
"gaps",
|
||||||
|
"margin",
|
||||||
|
"margins",
|
||||||
|
"padding",
|
||||||
|
"panel",
|
||||||
|
"screen",
|
||||||
|
"space",
|
||||||
|
"spacing",
|
||||||
|
"statusbar",
|
||||||
|
"taskbar",
|
||||||
|
"topbar"
|
||||||
|
],
|
||||||
|
"icon": "space_bar",
|
||||||
|
"description": "Space between the bar and screen edges"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "trayIconTint",
|
"section": "trayIconTint",
|
||||||
"label": "System Tray Icon Tint",
|
"label": "System Tray Icon Tint",
|
||||||
@@ -2030,28 +2046,6 @@
|
|||||||
"icon": "filter_b_and_w",
|
"icon": "filter_b_and_w",
|
||||||
"description": "Controls how much original icon color is removed before applying tint"
|
"description": "Controls how much original icon color is removed before applying tint"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"section": "barTransparency",
|
|
||||||
"label": "Transparency",
|
|
||||||
"tabIndex": 6,
|
|
||||||
"category": "Dank Bar",
|
|
||||||
"keywords": [
|
|
||||||
"alpha",
|
|
||||||
"background",
|
|
||||||
"bar",
|
|
||||||
"dank",
|
|
||||||
"opacity",
|
|
||||||
"panel",
|
|
||||||
"statusbar",
|
|
||||||
"taskbar",
|
|
||||||
"topbar",
|
|
||||||
"translucent",
|
|
||||||
"transparency",
|
|
||||||
"transparent"
|
|
||||||
],
|
|
||||||
"icon": "opacity",
|
|
||||||
"description": "Opacity of the bar background"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"section": "barWidgetOutline",
|
"section": "barWidgetOutline",
|
||||||
"label": "Widget Outline",
|
"label": "Widget Outline",
|
||||||
@@ -3792,7 +3786,6 @@
|
|||||||
"tabIndex": 10,
|
"tabIndex": 10,
|
||||||
"category": "Theme & Colors",
|
"category": "Theme & Colors",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"alpha",
|
|
||||||
"appearance",
|
"appearance",
|
||||||
"colors",
|
"colors",
|
||||||
"controls",
|
"controls",
|
||||||
@@ -3804,11 +3797,9 @@
|
|||||||
"shadow",
|
"shadow",
|
||||||
"style",
|
"style",
|
||||||
"theme",
|
"theme",
|
||||||
"translucent",
|
"transparency"
|
||||||
"transparency",
|
|
||||||
"transparent"
|
|
||||||
],
|
],
|
||||||
"description": "Controls the transparency of the shadow"
|
"description": "Controls the opacity of the shadow"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"section": "m3ElevationEnabled",
|
"section": "m3ElevationEnabled",
|
||||||
@@ -3833,8 +3824,34 @@
|
|||||||
"style",
|
"style",
|
||||||
"theme"
|
"theme"
|
||||||
],
|
],
|
||||||
|
"icon": "layers",
|
||||||
"description": "Material inspired shadows and elevation on modals, popouts, and dialogs"
|
"description": "Material inspired shadows and elevation on modals, popouts, and dialogs"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "popupTransparency",
|
||||||
|
"label": "Surface Opacity",
|
||||||
|
"tabIndex": 10,
|
||||||
|
"category": "Theme & Colors",
|
||||||
|
"keywords": [
|
||||||
|
"appearance",
|
||||||
|
"colors",
|
||||||
|
"controls",
|
||||||
|
"look",
|
||||||
|
"modal",
|
||||||
|
"modals",
|
||||||
|
"opacity",
|
||||||
|
"popouts",
|
||||||
|
"popup",
|
||||||
|
"scheme",
|
||||||
|
"shell",
|
||||||
|
"style",
|
||||||
|
"surface",
|
||||||
|
"surfaces",
|
||||||
|
"theme",
|
||||||
|
"transparency"
|
||||||
|
],
|
||||||
|
"description": "Controls opacity of shell surfaces, popouts, and modals"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "syncModeWithPortal",
|
"section": "syncModeWithPortal",
|
||||||
"label": "Sync Mode with Portal",
|
"label": "Sync Mode with Portal",
|
||||||
@@ -3966,35 +3983,6 @@
|
|||||||
"icon": "palette",
|
"icon": "palette",
|
||||||
"description": "Select the palette algorithm used for wallpaper-based colors"
|
"description": "Select the palette algorithm used for wallpaper-based colors"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"section": "popupTransparency",
|
|
||||||
"label": "Transparency",
|
|
||||||
"tabIndex": 10,
|
|
||||||
"category": "Theme & Colors",
|
|
||||||
"keywords": [
|
|
||||||
"alpha",
|
|
||||||
"appearance",
|
|
||||||
"colors",
|
|
||||||
"content",
|
|
||||||
"controls",
|
|
||||||
"layers",
|
|
||||||
"look",
|
|
||||||
"modal",
|
|
||||||
"modals",
|
|
||||||
"opacity",
|
|
||||||
"popouts",
|
|
||||||
"popup",
|
|
||||||
"scheme",
|
|
||||||
"style",
|
|
||||||
"surface",
|
|
||||||
"their",
|
|
||||||
"theme",
|
|
||||||
"translucent",
|
|
||||||
"transparency",
|
|
||||||
"transparent"
|
|
||||||
],
|
|
||||||
"description": "Controls opacity of all popouts, modals, and their content layers"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"section": "matugenTemplateVscode",
|
"section": "matugenTemplateVscode",
|
||||||
"label": "VS Code",
|
"label": "VS Code",
|
||||||
@@ -4563,6 +4551,27 @@
|
|||||||
],
|
],
|
||||||
"description": "Automatically lock the screen when DMS starts"
|
"description": "Automatically lock the screen when DMS starts"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "lockBeforeSuspend",
|
||||||
|
"label": "Lock before suspend",
|
||||||
|
"tabIndex": 11,
|
||||||
|
"category": "Lock Screen",
|
||||||
|
"keywords": [
|
||||||
|
"automatic",
|
||||||
|
"automatically",
|
||||||
|
"before",
|
||||||
|
"lock",
|
||||||
|
"login",
|
||||||
|
"password",
|
||||||
|
"prepares",
|
||||||
|
"screen",
|
||||||
|
"security",
|
||||||
|
"sleep",
|
||||||
|
"suspend",
|
||||||
|
"system"
|
||||||
|
],
|
||||||
|
"description": "Automatically lock the screen when the system prepares to suspend"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "lockScreenNotificationMode",
|
"section": "lockScreenNotificationMode",
|
||||||
"label": "Notification Display",
|
"label": "Notification Display",
|
||||||
@@ -5470,6 +5479,26 @@
|
|||||||
],
|
],
|
||||||
"icon": "dashboard"
|
"icon": "dashboard"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "notificationBodyFontSize",
|
||||||
|
"label": "Body Font Size",
|
||||||
|
"tabIndex": 17,
|
||||||
|
"category": "Notifications",
|
||||||
|
"keywords": [
|
||||||
|
"alert",
|
||||||
|
"alerts",
|
||||||
|
"body",
|
||||||
|
"font",
|
||||||
|
"messages",
|
||||||
|
"notif",
|
||||||
|
"notification",
|
||||||
|
"notifications",
|
||||||
|
"size",
|
||||||
|
"text",
|
||||||
|
"toast"
|
||||||
|
],
|
||||||
|
"description": "Set the font size for notification body text (htmlBody)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "notificationCompactMode",
|
"section": "notificationCompactMode",
|
||||||
"label": "Compact",
|
"label": "Compact",
|
||||||
@@ -5867,22 +5896,19 @@
|
|||||||
"keywords": [
|
"keywords": [
|
||||||
"alert",
|
"alert",
|
||||||
"alerts",
|
"alerts",
|
||||||
"appear",
|
"font",
|
||||||
"choose",
|
|
||||||
"location",
|
|
||||||
"messages",
|
"messages",
|
||||||
"notif",
|
"notif",
|
||||||
"notification",
|
"notification",
|
||||||
"notifications",
|
"notifications",
|
||||||
"popup",
|
|
||||||
"popups",
|
"popups",
|
||||||
"position",
|
"size",
|
||||||
"screen",
|
"summary",
|
||||||
"toast",
|
"text",
|
||||||
"where"
|
"toast"
|
||||||
],
|
],
|
||||||
"icon": "notifications",
|
"icon": "notifications",
|
||||||
"description": "Choose where notification popups appear on screen"
|
"description": "Set the font size for notification summary text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"section": "notificationRules",
|
"section": "notificationRules",
|
||||||
@@ -6032,6 +6058,26 @@
|
|||||||
],
|
],
|
||||||
"description": "Hide notification content until expanded; popups show collapsed by default"
|
"description": "Hide notification content until expanded; popups show collapsed by default"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "notificationSummaryFontSize",
|
||||||
|
"label": "Summary Font Size",
|
||||||
|
"tabIndex": 17,
|
||||||
|
"category": "Notifications",
|
||||||
|
"keywords": [
|
||||||
|
"alert",
|
||||||
|
"alerts",
|
||||||
|
"font",
|
||||||
|
"messages",
|
||||||
|
"notif",
|
||||||
|
"notification",
|
||||||
|
"notifications",
|
||||||
|
"size",
|
||||||
|
"summary",
|
||||||
|
"text",
|
||||||
|
"toast"
|
||||||
|
],
|
||||||
|
"description": "Set the font size for notification summary text"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "notificationDedupeEnabled",
|
"section": "notificationDedupeEnabled",
|
||||||
"label": "Suppress Duplicate Notifications",
|
"label": "Suppress Duplicate Notifications",
|
||||||
@@ -6054,6 +6100,32 @@
|
|||||||
"toast"
|
"toast"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "notificationShowTimeoutBar",
|
||||||
|
"label": "Timeout Progress Bar",
|
||||||
|
"tabIndex": 17,
|
||||||
|
"category": "Notifications",
|
||||||
|
"keywords": [
|
||||||
|
"alerts",
|
||||||
|
"bar",
|
||||||
|
"countdown",
|
||||||
|
"drains",
|
||||||
|
"messages",
|
||||||
|
"notification",
|
||||||
|
"notifications",
|
||||||
|
"panel",
|
||||||
|
"popup",
|
||||||
|
"progress",
|
||||||
|
"show",
|
||||||
|
"statusbar",
|
||||||
|
"taskbar",
|
||||||
|
"timeout",
|
||||||
|
"timer",
|
||||||
|
"toast",
|
||||||
|
"topbar"
|
||||||
|
],
|
||||||
|
"description": "Show a bar that drains as the popup"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "osdAlwaysShowValue",
|
"section": "osdAlwaysShowValue",
|
||||||
"label": "Always Show Percentage",
|
"label": "Always Show Percentage",
|
||||||
@@ -6697,27 +6769,6 @@
|
|||||||
"icon": "schedule",
|
"icon": "schedule",
|
||||||
"description": "Gradually fade the screen before locking with a configurable grace period"
|
"description": "Gradually fade the screen before locking with a configurable grace period"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"section": "lockBeforeSuspend",
|
|
||||||
"label": "Lock before suspend",
|
|
||||||
"tabIndex": 21,
|
|
||||||
"category": "Power & Sleep",
|
|
||||||
"keywords": [
|
|
||||||
"automatically",
|
|
||||||
"before",
|
|
||||||
"energy",
|
|
||||||
"lock",
|
|
||||||
"power",
|
|
||||||
"prepares",
|
|
||||||
"screen",
|
|
||||||
"security",
|
|
||||||
"shutdown",
|
|
||||||
"sleep",
|
|
||||||
"suspend",
|
|
||||||
"system"
|
|
||||||
],
|
|
||||||
"description": "Automatically lock the screen when the system prepares to suspend"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"section": "fadeToLockGracePeriod",
|
"section": "fadeToLockGracePeriod",
|
||||||
"label": "Lock fade grace period",
|
"label": "Lock fade grace period",
|
||||||
@@ -7119,6 +7170,36 @@
|
|||||||
],
|
],
|
||||||
"description": "Maximum number of entries that can be saved"
|
"description": "Maximum number of entries that can be saved"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "clipboardVisibleEntryActions",
|
||||||
|
"label": "Visible Entry Actions",
|
||||||
|
"tabIndex": 23,
|
||||||
|
"category": "System",
|
||||||
|
"keywords": [
|
||||||
|
"action",
|
||||||
|
"actions",
|
||||||
|
"appear",
|
||||||
|
"buttons",
|
||||||
|
"choose",
|
||||||
|
"clipboard",
|
||||||
|
"cliphist",
|
||||||
|
"copy",
|
||||||
|
"delete",
|
||||||
|
"density",
|
||||||
|
"edit",
|
||||||
|
"entries",
|
||||||
|
"entry",
|
||||||
|
"hide",
|
||||||
|
"history",
|
||||||
|
"linux",
|
||||||
|
"os",
|
||||||
|
"paste",
|
||||||
|
"pin",
|
||||||
|
"system",
|
||||||
|
"visible"
|
||||||
|
],
|
||||||
|
"description": "Choose which action buttons appear on clipboard entries"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "_tab_24",
|
"section": "_tab_24",
|
||||||
"label": "Displays",
|
"label": "Displays",
|
||||||
@@ -8399,7 +8480,7 @@
|
|||||||
"topbar",
|
"topbar",
|
||||||
"window"
|
"window"
|
||||||
],
|
],
|
||||||
"icon": "crop_square",
|
"icon": "layers",
|
||||||
"description": "Use custom gaps instead of bar spacing",
|
"description": "Use custom gaps instead of bar spacing",
|
||||||
"conditionKey": "isNiri"
|
"conditionKey": "isNiri"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user