mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-10 05:03:28 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f8b32cc298 |
@@ -7,6 +7,7 @@ Item {
|
||||
|
||||
required property var modalHandle
|
||||
required property string claimPrefix
|
||||
property string surfaceKind: "modal"
|
||||
property string screenName: ""
|
||||
property bool enabled: false
|
||||
property bool active: false
|
||||
@@ -14,112 +15,97 @@ Item {
|
||||
property bool dockBlocked: false
|
||||
property string dockSide: ""
|
||||
|
||||
property string claimId: ""
|
||||
property string claimedScreenName: ""
|
||||
property alias claimId: lease.claimId
|
||||
property alias claimedScreenName: lease.claimedScreenName
|
||||
|
||||
signal recoveryRequested
|
||||
|
||||
visible: false
|
||||
|
||||
function _nextClaimId() {
|
||||
return claimPrefix + ":" + (new Date()).getTime() + ":" + Math.floor(Math.random() * 1000);
|
||||
}
|
||||
|
||||
function _isCurrentModal(name) {
|
||||
return !!name && ModalManager.isCurrentModal(modalHandle, name);
|
||||
}
|
||||
|
||||
function _shouldRecover() {
|
||||
return active && enabled && _isCurrentModal(screenName);
|
||||
}
|
||||
|
||||
function _requestRecovery() {
|
||||
if (_shouldRecover())
|
||||
recoveryRequested();
|
||||
ConnectedSurfaceLease {
|
||||
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) {
|
||||
return ConnectedModeState.hasModalOwner(name, ownerId);
|
||||
}
|
||||
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) {
|
||||
if (!enabled || !screenName || !state) {
|
||||
release();
|
||||
return false;
|
||||
}
|
||||
if (claimedScreenName && claimedScreenName !== screenName)
|
||||
release();
|
||||
|
||||
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;
|
||||
return lease.publish(Object.assign({}, state, {
|
||||
"kind": root.surfaceKind,
|
||||
"screenName": root.screenName,
|
||||
"presented": root.presented,
|
||||
"dockRetractSide": root.dockBlocked ? root.dockSide : ""
|
||||
}), false);
|
||||
}
|
||||
|
||||
function updateAnim(animX, animY) {
|
||||
if (!enabled || !claimId || !claimedScreenName)
|
||||
return false;
|
||||
if (!ConnectedModeState.hasModalOwner(claimedScreenName, claimId)) {
|
||||
_requestRecovery();
|
||||
return false;
|
||||
}
|
||||
return ConnectedModeState.setModalAnim(claimedScreenName, animX, animY, claimId);
|
||||
return lease.updateAnim(animX, animY);
|
||||
}
|
||||
|
||||
function updateBody(bodyX, bodyY, bodyW, bodyH) {
|
||||
if (!enabled || !claimId || !claimedScreenName)
|
||||
return false;
|
||||
if (!ConnectedModeState.hasModalOwner(claimedScreenName, claimId)) {
|
||||
_requestRecovery();
|
||||
return false;
|
||||
}
|
||||
return ConnectedModeState.setModalBody(claimedScreenName, bodyX, bodyY, bodyW, bodyH, claimId);
|
||||
return lease.updateBody(bodyX, bodyY, bodyW, bodyH);
|
||||
}
|
||||
|
||||
function release() {
|
||||
if (!claimId)
|
||||
return;
|
||||
ConnectedModeState.releaseDockRetract(claimId);
|
||||
const releasedClaimId = claimId;
|
||||
const releasedScreenName = claimedScreenName;
|
||||
claimId = "";
|
||||
claimedScreenName = "";
|
||||
if (releasedScreenName)
|
||||
ConnectedModeState.clearModalState(releasedScreenName, releasedClaimId);
|
||||
return lease.release();
|
||||
}
|
||||
|
||||
Component.onDestruction: release()
|
||||
|
||||
Connections {
|
||||
target: ModalManager
|
||||
function onModalChanged() {
|
||||
root._requestRecovery();
|
||||
lease.requestRecovery();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ConnectedModeState
|
||||
function onModalOwnersChanged() {
|
||||
if (!ConnectedModeState.hasModalOwner(root.screenName, root.claimId))
|
||||
root._requestRecovery();
|
||||
lease.checkOwnershipRecovery();
|
||||
}
|
||||
function onModalStatesChanged() {
|
||||
if (!ConnectedModeState.modalStates[root.screenName])
|
||||
root._requestRecovery();
|
||||
lease.checkStateRecovery();
|
||||
}
|
||||
function onSurfaceDescriptorsChanged() {
|
||||
lease.checkStateRecovery();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,141 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import "ConnectedSurfaceDescriptor.js" as SurfaceDescriptor
|
||||
|
||||
Singleton {
|
||||
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 legacySurfaceState(screenName, kind) {
|
||||
return SurfaceDescriptor.toLegacyState(surfaceDescriptor(screenName, kind));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function _setSurfaceAnimation(screenName, kind, ownerId, x, y) {
|
||||
const current = surfaceDescriptor(screenName, kind);
|
||||
if (current.phase === "hidden" || (ownerId && current.ownerId !== ownerId))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function _setSurfaceBody(screenName, kind, ownerId, x, y, width, height) {
|
||||
const current = surfaceDescriptor(screenName, kind);
|
||||
if (current.phase === "hidden" || (ownerId && current.ownerId !== ownerId))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
readonly property var emptyDockState: ({
|
||||
"reveal": false,
|
||||
"barSide": "bottom",
|
||||
@@ -69,8 +200,10 @@ Singleton {
|
||||
popoutOwnerId = claimId;
|
||||
const ok = updatePopout(claimId, state);
|
||||
if (ok) {
|
||||
if (previousScreen && previousScreen !== popoutScreen)
|
||||
if (previousScreen && previousScreen !== popoutScreen) {
|
||||
_clearSurfaceDescriptor(previousScreen, "popout");
|
||||
_bumpSurfaceRevision(previousScreen);
|
||||
}
|
||||
_bumpSurfaceRevision(popoutScreen);
|
||||
}
|
||||
return ok;
|
||||
@@ -103,6 +236,21 @@ Singleton {
|
||||
if (state.omitEndConnector !== undefined)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -123,6 +271,7 @@ Singleton {
|
||||
popoutScreen = "";
|
||||
popoutOmitStartConnector = false;
|
||||
popoutOmitEndConnector = false;
|
||||
_clearSurfaceDescriptor(releasedScreen, "popout", claimId);
|
||||
_bumpSurfaceRevision(releasedScreen);
|
||||
return true;
|
||||
}
|
||||
@@ -140,6 +289,7 @@ Singleton {
|
||||
if (!isNaN(nextY) && popoutAnimY !== nextY)
|
||||
popoutAnimY = nextY;
|
||||
}
|
||||
_setSurfaceAnimation(popoutScreen, "popout", claimId, animX, animY);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -166,6 +316,7 @@ Singleton {
|
||||
if (!isNaN(nextH) && popoutBodyH !== nextH)
|
||||
popoutBodyH = nextH;
|
||||
}
|
||||
_setSurfaceBody(popoutScreen, "popout", claimId, bodyX, bodyY, bodyW, bodyH);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -193,13 +344,21 @@ Singleton {
|
||||
return false;
|
||||
|
||||
const normalized = _normalizeDockState(state);
|
||||
if (_sameDockState(dockStates[screenName], normalized))
|
||||
return true;
|
||||
const descriptorState = Object.assign({}, state, normalized, {
|
||||
"kind": "dock",
|
||||
"screenName": screenName,
|
||||
"visible": normalized.reveal,
|
||||
"presented": normalized.reveal,
|
||||
"phase": normalized.reveal ? (state.phase || "open") : "hidden"
|
||||
});
|
||||
const previous = dockStates[screenName] || emptyDockState;
|
||||
|
||||
const next = _cloneDict(dockStates);
|
||||
next[screenName] = normalized;
|
||||
dockStates = next;
|
||||
const legacyChanged = !_sameDockState(dockStates[screenName], normalized);
|
||||
if (legacyChanged) {
|
||||
const next = _cloneDict(dockStates);
|
||||
next[screenName] = normalized;
|
||||
dockStates = next;
|
||||
}
|
||||
_setSurfaceDescriptor(screenName, "dock", descriptorState, "dock:" + screenName);
|
||||
if (!!previous.reveal !== !!normalized.reveal)
|
||||
_bumpSurfaceRevision(screenName);
|
||||
return true;
|
||||
@@ -212,6 +371,7 @@ Singleton {
|
||||
const next = _cloneDict(dockStates);
|
||||
delete next[screenName];
|
||||
dockStates = next;
|
||||
_clearSurfaceDescriptor(screenName, "dock");
|
||||
|
||||
// Also clear corresponding slide
|
||||
if (dockSlides[screenName]) {
|
||||
@@ -237,6 +397,7 @@ Singleton {
|
||||
"y": numY
|
||||
};
|
||||
dockSlides = next;
|
||||
_setSurfaceAnimation(screenName, "dock", "dock:" + screenName, numX, numY);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -283,13 +444,20 @@ Singleton {
|
||||
return false;
|
||||
|
||||
const normalized = _normalizeNotificationState(state);
|
||||
if (_sameNotificationState(notificationStates[screenName], normalized))
|
||||
return true;
|
||||
const descriptorState = Object.assign({}, state, normalized, {
|
||||
"kind": "notification",
|
||||
"screenName": screenName,
|
||||
"presented": normalized.visible,
|
||||
"phase": normalized.visible ? (state.phase || "open") : "hidden"
|
||||
});
|
||||
const previous = notificationStates[screenName] || emptyNotificationState;
|
||||
|
||||
const next = _cloneDict(notificationStates);
|
||||
next[screenName] = normalized;
|
||||
notificationStates = next;
|
||||
const legacyChanged = !_sameNotificationState(notificationStates[screenName], normalized);
|
||||
if (legacyChanged) {
|
||||
const next = _cloneDict(notificationStates);
|
||||
next[screenName] = normalized;
|
||||
notificationStates = next;
|
||||
}
|
||||
_setSurfaceDescriptor(screenName, "notification", descriptorState, "notification:" + screenName);
|
||||
if (!!previous.visible !== !!normalized.visible)
|
||||
_bumpSurfaceRevision(screenName);
|
||||
return true;
|
||||
@@ -302,6 +470,7 @@ Singleton {
|
||||
const next = _cloneDict(notificationStates);
|
||||
delete next[screenName];
|
||||
notificationStates = next;
|
||||
_clearSurfaceDescriptor(screenName, "notification");
|
||||
_bumpSurfaceRevision(screenName);
|
||||
return true;
|
||||
}
|
||||
@@ -362,6 +531,10 @@ Singleton {
|
||||
const next = _cloneDict(modalStates);
|
||||
next[screenName] = normalized;
|
||||
modalStates = next;
|
||||
_setSurfaceDescriptor(screenName, "modal", Object.assign({}, state, normalized, {
|
||||
"kind": state.kind || "modal",
|
||||
"screenName": screenName
|
||||
}), ownerId || "");
|
||||
_bumpSurfaceRevision(screenName);
|
||||
return true;
|
||||
}
|
||||
@@ -372,11 +545,16 @@ Singleton {
|
||||
if (ownerId && modalOwners[screenName] !== ownerId)
|
||||
return false;
|
||||
const normalized = _normalizeModalState(state);
|
||||
if (_sameModalState(modalStates[screenName], normalized))
|
||||
return true;
|
||||
const next = _cloneDict(modalStates);
|
||||
next[screenName] = normalized;
|
||||
modalStates = next;
|
||||
const descriptorState = Object.assign({}, state, normalized, {
|
||||
"kind": state.kind || (surfaceDescriptor(screenName, "modal").kind || "modal"),
|
||||
"screenName": screenName
|
||||
});
|
||||
if (!_sameModalState(modalStates[screenName], normalized)) {
|
||||
const next = _cloneDict(modalStates);
|
||||
next[screenName] = normalized;
|
||||
modalStates = next;
|
||||
}
|
||||
_setSurfaceDescriptor(screenName, "modal", descriptorState, ownerId || modalOwners[screenName] || "");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -418,6 +596,7 @@ Singleton {
|
||||
delete nextOwners[screenName];
|
||||
modalOwners = nextOwners;
|
||||
}
|
||||
_clearSurfaceDescriptor(screenName, "modal", ownerId);
|
||||
_bumpSurfaceRevision(screenName);
|
||||
return true;
|
||||
}
|
||||
@@ -438,6 +617,7 @@ Singleton {
|
||||
"animY": nay
|
||||
});
|
||||
modalStates = next;
|
||||
_setSurfaceAnimation(screenName, "modal", ownerId, animX, animY);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -461,6 +641,7 @@ Singleton {
|
||||
"bodyH": nh
|
||||
});
|
||||
modalStates = next;
|
||||
_setSurfaceBody(screenName, "modal", ownerId, bodyX, bodyY, bodyW, bodyH);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -543,6 +724,9 @@ Singleton {
|
||||
const nextSurfaceRevisions = pruneKeyed(surfaceRevisions);
|
||||
if (nextSurfaceRevisions !== null)
|
||||
surfaceRevisions = nextSurfaceRevisions;
|
||||
const nextDescriptors = pruneKeyed(surfaceDescriptors);
|
||||
if (nextDescriptors !== null)
|
||||
surfaceDescriptors = nextDescriptors;
|
||||
|
||||
let retractChanged = false;
|
||||
const nextRetract = {};
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
.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;
|
||||
}
|
||||
|
||||
function toLegacyState(descriptor) {
|
||||
var d = normalize(descriptor);
|
||||
return {
|
||||
"visible": d.visible,
|
||||
"reveal": d.visible,
|
||||
"barSide": d.barSide,
|
||||
"bodyX": d.bodyRect.x,
|
||||
"bodyY": d.bodyRect.y,
|
||||
"bodyW": d.bodyRect.width,
|
||||
"bodyH": d.bodyRect.height,
|
||||
"animX": d.animationOffset.x,
|
||||
"animY": d.animationOffset.y,
|
||||
"slideX": d.animationOffset.x,
|
||||
"slideY": d.animationOffset.y,
|
||||
"screen": d.screenName,
|
||||
"omitStartConnector": d.omitStartConnector,
|
||||
"omitEndConnector": d.omitEndConnector
|
||||
};
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -115,6 +115,7 @@ Item {
|
||||
id: modalChrome
|
||||
modalHandle: root.modalHandle
|
||||
claimPrefix: root.layerNamespace + ":modal"
|
||||
surfaceKind: "modal"
|
||||
screenName: root._currentScreenName()
|
||||
enabled: root.frameOwnsConnectedChrome
|
||||
active: root.shouldBeVisible
|
||||
@@ -125,17 +126,38 @@ Item {
|
||||
}
|
||||
|
||||
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 = {
|
||||
"visible": shouldBeVisible || contentWindow.visible,
|
||||
"kind": "modal",
|
||||
"screenName": root._currentScreenName(),
|
||||
"phase": phase,
|
||||
"visible": presented,
|
||||
"presented": presented,
|
||||
"barSide": resolvedConnectedBarSide,
|
||||
"bodyRect": bodyRect,
|
||||
"animationOffset": animationOffset,
|
||||
"scale": 1,
|
||||
"opacity": Theme.connectedSurfaceColor.a,
|
||||
"bodyX": alignedX,
|
||||
"bodyY": alignedY,
|
||||
"bodyW": alignedWidth,
|
||||
"bodyH": alignedHeight,
|
||||
"animX": modalContainer ? modalContainer.animX : 0,
|
||||
"animY": modalContainer ? modalContainer.animY : 0,
|
||||
"animX": animationOffset.x,
|
||||
"animY": animationOffset.y,
|
||||
"omitStartConnector": false,
|
||||
"omitEndConnector": false
|
||||
"omitEndConnector": false,
|
||||
"dockRetractSide": root._dockBlocksEmergence ? resolvedConnectedBarSide : ""
|
||||
};
|
||||
return modalChrome.publish(state);
|
||||
}
|
||||
|
||||
@@ -242,6 +242,7 @@ Item {
|
||||
id: modalChrome
|
||||
modalHandle: root.modalHandle
|
||||
claimPrefix: "dms:launcher-v2"
|
||||
surfaceKind: "launcher"
|
||||
screenName: root._currentScreenName()
|
||||
enabled: root.frameOwnsConnectedChrome
|
||||
active: root.spotlightOpen
|
||||
@@ -252,17 +253,38 @@ Item {
|
||||
}
|
||||
|
||||
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 = {
|
||||
"visible": spotlightOpen || contentWindow.visible,
|
||||
"kind": "launcher",
|
||||
"screenName": root._currentScreenName(),
|
||||
"phase": phase,
|
||||
"visible": presented,
|
||||
"presented": presented,
|
||||
"barSide": resolvedConnectedBarSide,
|
||||
"bodyRect": bodyRect,
|
||||
"animationOffset": animationOffset,
|
||||
"scale": 1,
|
||||
"opacity": Theme.connectedSurfaceColor.a,
|
||||
"bodyX": _connectedChromeX,
|
||||
"bodyY": _connectedChromeY,
|
||||
"bodyW": _connectedChromeWidth,
|
||||
"bodyH": _connectedChromeHeight,
|
||||
"animX": contentContainer ? contentContainer.animX : 0,
|
||||
"animY": contentContainer ? contentContainer.animY : 0,
|
||||
"animX": animationOffset.x,
|
||||
"animY": animationOffset.y,
|
||||
"omitStartConnector": false,
|
||||
"omitEndConnector": false
|
||||
"omitEndConnector": false,
|
||||
"dockRetractSide": root._dockBlocksEmergence ? resolvedConnectedBarSide : ""
|
||||
};
|
||||
return modalChrome.publish(state);
|
||||
}
|
||||
|
||||
@@ -186,13 +186,36 @@ Variants {
|
||||
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, {
|
||||
"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,
|
||||
"bodyX": dock._dockWindowOriginX() + dockBackground.x + dockContainer.x + dockMouseArea.x + dockCore.x,
|
||||
"bodyY": dock._dockWindowOriginY() + dockBackground.y + dockContainer.y + dockMouseArea.y + dockCore.y,
|
||||
"bodyW": dock.hasApps ? dockBackground.width : 0,
|
||||
"bodyH": dock.hasApps ? dockBackground.height : 0,
|
||||
"bodyRect": {
|
||||
"x": bodyX,
|
||||
"y": bodyY,
|
||||
"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,
|
||||
"slideY": dockSlide.y
|
||||
});
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Shapes
|
||||
import qs.Common
|
||||
|
||||
// Frame perimeter ring: the full window rectangle with a rounded-rectangle
|
||||
// cutout. Drawn as a single even-odd Shape so the ring is one primitive: no
|
||||
// full-output mask textures, and a translucent fill never double-blends at the
|
||||
// corners.
|
||||
Item {
|
||||
id: root
|
||||
|
||||
@@ -16,39 +20,42 @@ Item {
|
||||
required property real cutoutRadius
|
||||
property color borderColor: Qt.rgba(SettingsData.effectiveFrameColor.r, SettingsData.effectiveFrameColor.g, SettingsData.effectiveFrameColor.b, SettingsData.frameOpacity)
|
||||
|
||||
Rectangle {
|
||||
id: borderRect
|
||||
// Path elements can't reference `parent`; expose cutout edges as root props.
|
||||
readonly property real _left: cutoutLeftInset
|
||||
readonly property real _top: cutoutTopInset
|
||||
readonly property real _right: width - cutoutRightInset
|
||||
readonly property real _bottom: height - cutoutBottomInset
|
||||
readonly property real _radius: Math.max(0, Math.min(cutoutRadius, (_right - _left) / 2, (_bottom - _top) / 2))
|
||||
|
||||
Shape {
|
||||
anchors.fill: parent
|
||||
// Bake frameOpacity into the color alpha rather than using the `opacity` property
|
||||
color: root.borderColor
|
||||
asynchronous: false
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
antialiasing: true
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: MultiEffect {
|
||||
maskSource: cutoutMask
|
||||
maskEnabled: true
|
||||
maskInverted: true
|
||||
maskThresholdMin: 0.5
|
||||
maskSpreadAtMin: 1
|
||||
}
|
||||
}
|
||||
ShapePath {
|
||||
fillColor: root.borderColor
|
||||
strokeWidth: -1
|
||||
fillRule: ShapePath.OddEvenFill
|
||||
|
||||
Item {
|
||||
id: cutoutMask
|
||||
// Outer rectangle (window edge, square corners)
|
||||
startX: 0
|
||||
startY: 0
|
||||
PathLine { x: root.width; y: 0 }
|
||||
PathLine { x: root.width; y: root.height }
|
||||
PathLine { x: 0; y: root.height }
|
||||
PathLine { x: 0; y: 0 }
|
||||
|
||||
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
|
||||
// Inner rounded-rectangle cutout (second subpath → even-odd hole)
|
||||
PathMove { x: root._left + root._radius; y: root._top }
|
||||
PathLine { x: root._right - root._radius; y: root._top }
|
||||
PathArc { x: root._right; y: root._top + root._radius; radiusX: root._radius; radiusY: root._radius }
|
||||
PathLine { x: root._right; y: root._bottom - root._radius }
|
||||
PathArc { x: root._right - root._radius; y: root._bottom; radiusX: root._radius; radiusY: root._radius }
|
||||
PathLine { x: root._left + root._radius; y: root._bottom }
|
||||
PathArc { x: root._left; y: root._bottom - root._radius; radiusX: root._radius; radiusY: root._radius }
|
||||
PathLine { x: root._left; y: root._top + root._radius }
|
||||
PathArc { x: root._left + root._radius; y: root._top; radiusX: root._radius; radiusY: root._radius }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -513,13 +513,30 @@ QtObject {
|
||||
ConnectedModeState.clearNotificationState(screenName);
|
||||
return;
|
||||
}
|
||||
const bodyRect = {
|
||||
x: minX,
|
||||
y: minY,
|
||||
width: maxXEnd - minX,
|
||||
height: maxYEnd - minY
|
||||
};
|
||||
ConnectedModeState.setNotificationState(screenName, {
|
||||
kind: "notification",
|
||||
screenName: screenName,
|
||||
phase: "open",
|
||||
visible: true,
|
||||
presented: true,
|
||||
barSide: notifBarSide,
|
||||
bodyRect: bodyRect,
|
||||
animationOffset: {
|
||||
x: 0,
|
||||
y: 0
|
||||
},
|
||||
scale: 1,
|
||||
opacity: Theme.connectedSurfaceColor.a,
|
||||
bodyX: minX,
|
||||
bodyY: minY,
|
||||
bodyW: maxXEnd - minX,
|
||||
bodyH: maxYEnd - minY,
|
||||
bodyW: bodyRect.width,
|
||||
bodyH: bodyRect.height,
|
||||
omitStartConnector: _notificationOmitStartConnector(),
|
||||
omitEndConnector: _notificationOmitEndConnector()
|
||||
});
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
#version 450
|
||||
|
||||
// Connected Frame Mode silhouette as a signed-distance field: the frame ring
|
||||
// (an inverted rounded rectangle) smooth-unioned with each active chrome
|
||||
// (popout/modal, dock, notification). The smooth-min radius IS the connector
|
||||
// fillet. Antialiasing is analytic via fwidth -> crisp at any scale, no FBO.
|
||||
|
||||
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
|
||||
// Up to four chrome slots. rect = x,y,w,h (px). corner = per-corner radii
|
||||
// (topLeft, topRight, bottomRight, bottomLeft). param = connectorR, active, 0, 0
|
||||
vec4 chromeRect0;
|
||||
vec4 chromeCorner0;
|
||||
vec4 chromeParam0;
|
||||
vec4 chromeRect1;
|
||||
vec4 chromeCorner1;
|
||||
vec4 chromeParam1;
|
||||
vec4 chromeRect2;
|
||||
vec4 chromeCorner2;
|
||||
vec4 chromeParam2;
|
||||
vec4 chromeRect3;
|
||||
vec4 chromeCorner3;
|
||||
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;
|
||||
}
|
||||
|
||||
// Per-corner rounded box. r = (topLeft, topRight, bottomRight, bottomLeft).
|
||||
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;
|
||||
}
|
||||
|
||||
// Circular smooth-min: blends two SDFs with a fillet of radius k.
|
||||
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);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 px = qt_TexCoord0 * vec2(ubuf.widthPx, ubuf.heightPx);
|
||||
|
||||
// Frame ring: inside the screen rect AND outside the rounded cutout (hole).
|
||||
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);
|
||||
|
||||
// Smooth-union the active chrome surfaces; smin radius = connector fillet.
|
||||
if (ubuf.chromeParam0.y > 0.5)
|
||||
d = smin(d, chromeDist(px, ubuf.chromeRect0, ubuf.chromeCorner0), ubuf.chromeParam0.x);
|
||||
if (ubuf.chromeParam1.y > 0.5)
|
||||
d = smin(d, chromeDist(px, ubuf.chromeRect1, ubuf.chromeCorner1), ubuf.chromeParam1.x);
|
||||
if (ubuf.chromeParam2.y > 0.5)
|
||||
d = smin(d, chromeDist(px, ubuf.chromeRect2, ubuf.chromeCorner2), ubuf.chromeParam2.x);
|
||||
if (ubuf.chromeParam3.y > 0.5)
|
||||
d = smin(d, chromeDist(px, ubuf.chromeRect3, ubuf.chromeCorner3), ubuf.chromeParam3.x);
|
||||
|
||||
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.
@@ -38,8 +38,6 @@ Item {
|
||||
property bool fullHeightSurface: false
|
||||
property bool _primeContent: false
|
||||
property bool _resizeActive: false
|
||||
property string _chromeClaimId: ""
|
||||
property int _connectedChromeSerial: 0
|
||||
property real _chromeAnimTravelX: 1
|
||||
property real _chromeAnimTravelY: 1
|
||||
property bool _fullSyncQueued: false
|
||||
@@ -100,6 +98,43 @@ Item {
|
||||
onTriggered: root._flushSync()
|
||||
}
|
||||
|
||||
ConnectedSurfaceLease {
|
||||
id: chromeLease
|
||||
claimPrefix: root.layerNamespace
|
||||
screenName: root.screen ? root.screen.name : ""
|
||||
enabled: root.frameOwnsConnectedChrome
|
||||
active: contentWindow.visible || root.shouldBeVisible
|
||||
presented: contentWindow.visible || root.shouldBeVisible
|
||||
renewTokenOnRecovery: false
|
||||
isCurrentOwner: function(name) {
|
||||
return PopoutManager.isCurrentPopout(root.popoutHandle, name);
|
||||
}
|
||||
hasOwner: function(_name, ownerId) {
|
||||
return ConnectedModeState.hasPopoutOwner(ownerId);
|
||||
}
|
||||
statePresent: function(name, ownerId) {
|
||||
return ConnectedModeState.hasPopoutOwner(ownerId) && ConnectedModeState.hasSurfaceDescriptor(name, "popout", ownerId);
|
||||
}
|
||||
claimState: function(_name, state, ownerId) {
|
||||
return ConnectedModeState.claimPopout(ownerId, state);
|
||||
}
|
||||
ensureState: function(_name, state, ownerId) {
|
||||
if (!ConnectedModeState.hasPopoutOwner(ownerId))
|
||||
return false;
|
||||
return ConnectedModeState.updatePopout(ownerId, state);
|
||||
}
|
||||
releaseState: function(_name, ownerId) {
|
||||
return ConnectedModeState.releasePopout(ownerId);
|
||||
}
|
||||
updateAnimationState: function(_name, ownerId, animX, animY) {
|
||||
return ConnectedModeState.setPopoutAnim(ownerId, animX, animY);
|
||||
}
|
||||
updateBodyState: function(_name, ownerId, bodyX, bodyY, bodyW, bodyH) {
|
||||
return ConnectedModeState.setPopoutBody(ownerId, bodyX, bodyY, bodyW, bodyH);
|
||||
}
|
||||
onRecoveryRequested: root._queueFullSync()
|
||||
}
|
||||
|
||||
property var _lastOpenedScreen: null
|
||||
property bool isClosing: false
|
||||
|
||||
@@ -169,11 +204,6 @@ Item {
|
||||
setBarContext(pos, bottomGap);
|
||||
}
|
||||
|
||||
function _nextChromeClaimId() {
|
||||
_connectedChromeSerial += 1;
|
||||
return layerNamespace + ":" + _connectedChromeSerial + ":" + (new Date()).getTime();
|
||||
}
|
||||
|
||||
function _captureChromeAnimTravel() {
|
||||
_chromeAnimTravelX = Math.max(1, Math.abs(contentContainer.offsetX));
|
||||
_chromeAnimTravelY = Math.max(1, Math.abs(contentContainer.offsetY));
|
||||
@@ -203,15 +233,35 @@ Item {
|
||||
|
||||
function _connectedChromeState(visibleOverride) {
|
||||
const visible = visibleOverride !== undefined ? !!visibleOverride : contentWindow.visible;
|
||||
const presented = contentWindow.visible || root.shouldBeVisible;
|
||||
const phase = root.isClosing ? "closing" : (!presented ? "hidden" : (!contentWindow.visible && root.shouldBeVisible ? "opening" : "open"));
|
||||
const bodyRect = {
|
||||
"x": root.alignedX,
|
||||
"y": root.renderedAlignedY,
|
||||
"width": root.alignedWidth,
|
||||
"height": root.renderedAlignedHeight
|
||||
};
|
||||
const animationOffset = {
|
||||
"x": _connectedChromeAnimX(),
|
||||
"y": _connectedChromeAnimY()
|
||||
};
|
||||
return {
|
||||
"kind": "popout",
|
||||
"screenName": root.screen ? root.screen.name : "",
|
||||
"phase": phase,
|
||||
"visible": visible,
|
||||
"presented": presented,
|
||||
"barSide": contentContainer.connectedBarSide,
|
||||
"bodyRect": bodyRect,
|
||||
"animationOffset": animationOffset,
|
||||
"scale": 1,
|
||||
"opacity": Theme.connectedSurfaceColor.a,
|
||||
"bodyX": root.alignedX,
|
||||
"bodyY": root.renderedAlignedY,
|
||||
"bodyW": root.alignedWidth,
|
||||
"bodyH": root.renderedAlignedHeight,
|
||||
"animX": _connectedChromeAnimX(),
|
||||
"animY": _connectedChromeAnimY(),
|
||||
"animX": animationOffset.x,
|
||||
"animY": animationOffset.y,
|
||||
"screen": root.screen ? root.screen.name : "",
|
||||
"omitStartConnector": root._closeGapOmitStartConnector(),
|
||||
"omitEndConnector": root._closeGapOmitEndConnector()
|
||||
@@ -219,27 +269,13 @@ Item {
|
||||
}
|
||||
|
||||
function _publishConnectedChromeState(forceClaim, visibleOverride) {
|
||||
if (!root.frameOwnsConnectedChrome || !root.screen || !_chromeClaimId)
|
||||
if (!root.frameOwnsConnectedChrome || !root.screen)
|
||||
return false;
|
||||
|
||||
const screenName = root.screen.name;
|
||||
const isCurrent = PopoutManager.isCurrentPopout(popoutHandle, screenName);
|
||||
if (!ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
|
||||
if (!isCurrent)
|
||||
return false;
|
||||
forceClaim = true;
|
||||
} else if (forceClaim && !isCurrent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const state = _connectedChromeState(visibleOverride);
|
||||
return forceClaim ? ConnectedModeState.claimPopout(_chromeClaimId, state) : ConnectedModeState.updatePopout(_chromeClaimId, state);
|
||||
return chromeLease.publish(_connectedChromeState(visibleOverride), !!forceClaim);
|
||||
}
|
||||
|
||||
function _releaseConnectedChromeState() {
|
||||
if (_chromeClaimId)
|
||||
ConnectedModeState.releasePopout(_chromeClaimId);
|
||||
_chromeClaimId = "";
|
||||
chromeLease.release();
|
||||
}
|
||||
|
||||
// ─── Exposed animation state for ConnectedModeState ────────────────────
|
||||
@@ -258,16 +294,11 @@ Item {
|
||||
}
|
||||
if (!contentWindow.visible && !shouldBeVisible)
|
||||
return;
|
||||
if (!_chromeClaimId) {
|
||||
if (!PopoutManager.isCurrentPopout(popoutHandle, root.screen.name))
|
||||
return;
|
||||
_chromeClaimId = _nextChromeClaimId();
|
||||
}
|
||||
_publishConnectedChromeState(!ConnectedModeState.hasPopoutOwner(_chromeClaimId));
|
||||
_publishConnectedChromeState(false);
|
||||
}
|
||||
|
||||
function _syncPopoutAnim(axis) {
|
||||
if (!root.frameOwnsConnectedChrome || !_chromeClaimId)
|
||||
if (!root.frameOwnsConnectedChrome || !chromeLease.claimId)
|
||||
return;
|
||||
if (!contentWindow.visible && !shouldBeVisible)
|
||||
return;
|
||||
@@ -276,25 +307,15 @@ Item {
|
||||
const syncY = axis === "y" && (barSide === "top" || barSide === "bottom");
|
||||
if (!syncX && !syncY)
|
||||
return;
|
||||
if (!ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
|
||||
if (root.screen && PopoutManager.isCurrentPopout(popoutHandle, root.screen.name))
|
||||
_queueFullSync();
|
||||
return;
|
||||
}
|
||||
ConnectedModeState.setPopoutAnim(_chromeClaimId, syncX ? _connectedChromeAnimX() : undefined, syncY ? _connectedChromeAnimY() : undefined);
|
||||
chromeLease.updateAnim(syncX ? _connectedChromeAnimX() : undefined, syncY ? _connectedChromeAnimY() : undefined);
|
||||
}
|
||||
|
||||
function _syncPopoutBody() {
|
||||
if (!root.frameOwnsConnectedChrome || !_chromeClaimId)
|
||||
if (!root.frameOwnsConnectedChrome || !chromeLease.claimId)
|
||||
return;
|
||||
if (!contentWindow.visible && !shouldBeVisible)
|
||||
return;
|
||||
if (!ConnectedModeState.hasPopoutOwner(_chromeClaimId)) {
|
||||
if (root.screen && PopoutManager.isCurrentPopout(popoutHandle, root.screen.name))
|
||||
_queueFullSync();
|
||||
return;
|
||||
}
|
||||
ConnectedModeState.setPopoutBody(_chromeClaimId, root.alignedX, root.renderedAlignedY, root.alignedWidth, root.renderedAlignedHeight);
|
||||
chromeLease.updateBody(root.alignedX, root.renderedAlignedY, root.alignedWidth, root.renderedAlignedHeight);
|
||||
}
|
||||
|
||||
property bool _animSyncQueued: false
|
||||
@@ -345,13 +366,10 @@ Item {
|
||||
Connections {
|
||||
target: contentWindow
|
||||
function onVisibleChanged() {
|
||||
if (contentWindow.visible) {
|
||||
if (!root._chromeClaimId)
|
||||
root._chromeClaimId = root._nextChromeClaimId();
|
||||
if (contentWindow.visible)
|
||||
root._publishConnectedChromeState(true);
|
||||
} else {
|
||||
else
|
||||
root._releaseConnectedChromeState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,11 +377,8 @@ Item {
|
||||
target: SettingsData
|
||||
function onConnectedFrameModeActiveChanged() {
|
||||
if (root.frameOwnsConnectedChrome) {
|
||||
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name)) {
|
||||
if (!root._chromeClaimId)
|
||||
root._chromeClaimId = root._nextChromeClaimId();
|
||||
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name))
|
||||
root._publishConnectedChromeState(true);
|
||||
}
|
||||
} else {
|
||||
root._releaseConnectedChromeState();
|
||||
}
|
||||
@@ -376,16 +391,17 @@ Item {
|
||||
Connections {
|
||||
target: ConnectedModeState
|
||||
function onPopoutOwnerIdChanged() {
|
||||
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name) && !ConnectedModeState.hasPopoutOwner(root._chromeClaimId))
|
||||
root._queueFullSync();
|
||||
chromeLease.checkOwnershipRecovery();
|
||||
}
|
||||
function onSurfaceDescriptorsChanged() {
|
||||
chromeLease.checkStateRecovery();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: PopoutManager
|
||||
function onPopoutChanged() {
|
||||
if ((contentWindow.visible || root.shouldBeVisible) && root.screen && PopoutManager.isCurrentPopout(root.popoutHandle, root.screen.name))
|
||||
root._queueFullSync();
|
||||
chromeLease.requestRecovery();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,10 +438,10 @@ Item {
|
||||
}
|
||||
|
||||
if (root.frameOwnsConnectedChrome) {
|
||||
_chromeClaimId = _nextChromeClaimId();
|
||||
chromeLease.beginClaim();
|
||||
_publishConnectedChromeState(true, true);
|
||||
} else {
|
||||
_chromeClaimId = "";
|
||||
chromeLease.release();
|
||||
}
|
||||
|
||||
if (screenChanged) {
|
||||
|
||||
Reference in New Issue
Block a user