mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-28 22:12:10 -04:00
feat(HoverMode): implement hover popout & launcher functionality in all modes
- New Hover toggle found in DankBar Settings - New Hover to Reveal Launcher in FrameTab Settings
This commit is contained in:
@@ -98,6 +98,7 @@ DankPopout {
|
||||
property bool anyModalOpen: credentialsPromptOpen || wifiPasswordModalOpen || polkitModalOpen || powerMenuOpen
|
||||
|
||||
backgroundInteractive: !anyModalOpen
|
||||
hoverDismissSuspended: editMode || anyModalOpen
|
||||
|
||||
onCredentialsPromptOpenChanged: {
|
||||
if (credentialsPromptOpen && shouldBeVisible)
|
||||
|
||||
@@ -95,6 +95,14 @@ Item {
|
||||
enableFrameInsetAnimation.schedule();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: topBarContent._hasBarWindow ? topBarContent.barWindow.axis : null
|
||||
|
||||
function onEdgeChanged() {
|
||||
topBarContent.resetHoverForBarGeometryChange();
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on anchors.leftMargin {
|
||||
enabled: _animateFrameInsets && _usesFrameBarChrome
|
||||
NumberAnimation {
|
||||
@@ -380,6 +388,173 @@ Item {
|
||||
return "left";
|
||||
}
|
||||
|
||||
DankBarHoverController {
|
||||
id: hoverController
|
||||
barContent: topBarContent
|
||||
barWindow: topBarContent.barWindow
|
||||
barConfig: topBarContent.barConfig
|
||||
hLeftSection: topBarContent.hLeftSection
|
||||
hCenterSection: topBarContent.hCenterSection
|
||||
hRightSection: topBarContent.hRightSection
|
||||
vLeftSection: topBarContent.vLeftSection
|
||||
vCenterSection: topBarContent.vCenterSection
|
||||
vRightSection: topBarContent.vRightSection
|
||||
leftWidgetsModel: topBarContent.leftWidgetsModel
|
||||
centerWidgetsModel: topBarContent.centerWidgetsModel
|
||||
rightWidgetsModel: topBarContent.rightWidgetsModel
|
||||
}
|
||||
|
||||
readonly property string activeHoverTrigger: hoverController.activeHoverTrigger
|
||||
readonly property bool hoverPopoutsEnabled: hoverController.hoverPopoutsEnabled
|
||||
|
||||
function queueHoverPopout(gx, gy) {
|
||||
hoverController.queueHoverPoint(gx, gy);
|
||||
}
|
||||
|
||||
function checkHoverPopout(gx, gy) {
|
||||
hoverController.checkHoverPopout(gx, gy);
|
||||
}
|
||||
|
||||
function findWidgetAtGlobalPoint(gx, gy) {
|
||||
return hoverController.findWidgetAtGlobalPoint(gx, gy);
|
||||
}
|
||||
|
||||
function scheduleHoverClose(gx, gy) {
|
||||
hoverController.scheduleHoverClose(gx, gy);
|
||||
}
|
||||
|
||||
function updateHoverBarHovered(hovered) {
|
||||
hoverController.updateBarHovered(hovered);
|
||||
}
|
||||
|
||||
function resetHoverForBarGeometryChange() {
|
||||
hoverController.resetForBarGeometryChange();
|
||||
}
|
||||
|
||||
function _dashTriggerSource(section, tabIndex) {
|
||||
return hoverController.dashTriggerSource(section, tabIndex);
|
||||
}
|
||||
|
||||
function getBarPosition() {
|
||||
return barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
}
|
||||
|
||||
function resolveWidgetTriggerGeometry(widgetItem, section, opts) {
|
||||
opts = opts || {};
|
||||
if (opts.useCenterSection && section === "center") {
|
||||
const centerSection = barWindow.isVertical ? vCenterSection : hCenterSection;
|
||||
if (centerSection) {
|
||||
if (barWindow.isVertical) {
|
||||
const centerY = centerSection.height / 2;
|
||||
return {
|
||||
triggerPos: centerSection.mapToItem(null, 0, centerY),
|
||||
triggerWidth: centerSection.height
|
||||
};
|
||||
}
|
||||
return {
|
||||
triggerPos: centerSection.mapToItem(null, 0, 0),
|
||||
triggerWidth: centerSection.width
|
||||
};
|
||||
}
|
||||
}
|
||||
const ref = opts.visualItem || widgetItem.visualContent || widgetItem;
|
||||
const w = opts.triggerWidth !== undefined ? opts.triggerWidth : (widgetItem.visualWidth !== undefined ? widgetItem.visualWidth : widgetItem.width);
|
||||
return {
|
||||
triggerPos: ref.mapToItem(null, 0, 0),
|
||||
triggerWidth: w
|
||||
};
|
||||
}
|
||||
|
||||
function openWidgetPopout(spec) {
|
||||
if (!spec?.loader)
|
||||
return false;
|
||||
spec.loader.active = true;
|
||||
|
||||
let popout = _resolvePopoutFromLoader(spec.loader);
|
||||
if (!popout) {
|
||||
_queuePopoutLoaderOpen(spec);
|
||||
return false;
|
||||
}
|
||||
return _finishWidgetPopoutOpen(spec, popout);
|
||||
}
|
||||
|
||||
function _resolvePopoutFromLoader(loader) {
|
||||
if (!loader)
|
||||
return null;
|
||||
if (loader.item)
|
||||
return loader.item;
|
||||
|
||||
const pairs = [[PopoutService.appDrawerLoader, PopoutService.appDrawerPopout], [PopoutService.batteryPopoutLoader, PopoutService.batteryPopout], [PopoutService.clipboardHistoryPopoutLoader, PopoutService.clipboardHistoryPopout], [PopoutService.controlCenterLoader, PopoutService.controlCenterPopout], [PopoutService.dankDashPopoutLoader, PopoutService.dankDashPopout], [PopoutService.layoutPopoutLoader, PopoutService.layoutPopout], [PopoutService.notificationCenterLoader, PopoutService.notificationCenterPopout], [PopoutService.processListPopoutLoader, PopoutService.processListPopout], [PopoutService.systemUpdateLoader, PopoutService.systemUpdatePopout], [PopoutService.vpnPopoutLoader, PopoutService.vpnPopout]];
|
||||
for (let i = 0; i < pairs.length; i++) {
|
||||
if (loader === pairs[i][0] && pairs[i][1])
|
||||
return pairs[i][1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
property var _pendingPopoutOpenSpec: null
|
||||
|
||||
function _queuePopoutLoaderOpen(spec) {
|
||||
if (_pendingPopoutOpenSpec && _pendingPopoutOpenSpec.loader === spec.loader)
|
||||
return;
|
||||
_pendingPopoutOpenSpec = spec;
|
||||
const loader = spec.loader;
|
||||
const onLoaded = function () {
|
||||
if (!loader.item)
|
||||
return;
|
||||
if (loader.loaded)
|
||||
loader.loaded.disconnect(onLoaded);
|
||||
const pending = topBarContent._pendingPopoutOpenSpec;
|
||||
if (!pending || pending.loader !== loader)
|
||||
return;
|
||||
topBarContent._pendingPopoutOpenSpec = null;
|
||||
topBarContent._finishWidgetPopoutOpen(pending, loader.item);
|
||||
if (pending.mode === "hover")
|
||||
hoverController.recheckLatestPoint();
|
||||
};
|
||||
if (loader.item) {
|
||||
onLoaded();
|
||||
return;
|
||||
}
|
||||
if (loader.loaded)
|
||||
loader.loaded.connect(onLoaded);
|
||||
}
|
||||
|
||||
function _finishWidgetPopoutOpen(spec, popout) {
|
||||
const effectiveBarConfig = barConfig;
|
||||
const barPosition = getBarPosition();
|
||||
const widgetSection = spec.section || "right";
|
||||
const mode = spec.mode || "click";
|
||||
|
||||
if (popout.setBarContext)
|
||||
popout.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||
|
||||
if (spec.setTriggerScreen)
|
||||
popout.triggerScreen = barWindow.screen;
|
||||
|
||||
if (popout.setTriggerPosition && spec.widgetItem) {
|
||||
const geom = resolveWidgetTriggerGeometry(spec.widgetItem, widgetSection, {
|
||||
useCenterSection: spec.useCenterSection,
|
||||
visualItem: spec.visualItem,
|
||||
triggerWidth: spec.triggerWidth
|
||||
});
|
||||
if (geom.triggerPos) {
|
||||
const pos = SettingsData.getPopupTriggerPosition(geom.triggerPos, barWindow.screen, barWindow.effectiveBarThickness, geom.triggerWidth, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
popout.setTriggerPosition(pos.x, pos.y, pos.width, widgetSection, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof popout.prepareForTrigger === "function")
|
||||
popout.prepareForTrigger(spec.triggerSource, mode);
|
||||
|
||||
if (spec.prepare)
|
||||
spec.prepare(popout);
|
||||
|
||||
const request = mode === "hover" ? PopoutManager.requestHoverPopout : PopoutManager.requestPopout;
|
||||
request(popout, spec.tabIndex, spec.triggerSource);
|
||||
return true;
|
||||
}
|
||||
|
||||
readonly property var widgetVisibility: ({
|
||||
"cpuUsage": DgopService.dgopAvailable,
|
||||
"memUsage": DgopService.dgopAvailable,
|
||||
@@ -702,27 +877,18 @@ Item {
|
||||
parentScreen: barWindow.screen
|
||||
popoutTarget: clipboardHistoryPopoutLoader.item ?? null
|
||||
|
||||
function openClipboardPopout(initialTab) {
|
||||
clipboardHistoryPopoutLoader.active = true;
|
||||
if (!clipboardHistoryPopoutLoader.item) {
|
||||
return;
|
||||
}
|
||||
const popout = clipboardHistoryPopoutLoader.item;
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
if (popout.setBarContext) {
|
||||
popout.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||
}
|
||||
if (popout.setTriggerPosition) {
|
||||
const globalPos = clipboardWidget.mapToItem(null, 0, 0);
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, clipboardWidget.width, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
const widgetSection = topBarContent.getWidgetSection(parent) || "right";
|
||||
popout.setTriggerPosition(pos.x, pos.y, pos.width, widgetSection, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
}
|
||||
if (initialTab) {
|
||||
popout.activeTab = initialTab;
|
||||
}
|
||||
PopoutManager.requestPopout(popout, undefined, "clipboard");
|
||||
function openClipboardPopout(initialTab, mode) {
|
||||
openWidgetPopout({
|
||||
loader: clipboardHistoryPopoutLoader,
|
||||
widgetItem: clipboardWidget,
|
||||
section: topBarContent.getWidgetSection(parent) || "right",
|
||||
triggerSource: "clipboard",
|
||||
mode: mode || "click",
|
||||
prepare: popout => {
|
||||
if (initialTab)
|
||||
popout.activeTab = initialTab;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onClipboardClicked: openClipboardPopout("recents")
|
||||
@@ -821,9 +987,14 @@ Item {
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (!_preparePopout())
|
||||
return;
|
||||
PopoutManager.requestPopout(appDrawerLoader.item, undefined, "appDrawer");
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: appDrawerLoader,
|
||||
widgetItem: launcherButton,
|
||||
section: launcherButton.section,
|
||||
triggerSource: "appDrawer",
|
||||
mode: "click",
|
||||
visualItem: launcherButton
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -890,6 +1061,7 @@ Item {
|
||||
id: clockComponent
|
||||
|
||||
Clock {
|
||||
id: clockWidget
|
||||
axis: barWindow.axis
|
||||
compactMode: topBarContent.overlapping
|
||||
barThickness: barWindow.effectiveBarThickness
|
||||
@@ -909,43 +1081,17 @@ Item {
|
||||
}
|
||||
|
||||
onClockClicked: {
|
||||
dankDashPopoutLoader.active = true;
|
||||
if (dankDashPopoutLoader.item) {
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
if (dankDashPopoutLoader.item.setBarContext) {
|
||||
dankDashPopoutLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||
}
|
||||
if (dankDashPopoutLoader.item.setTriggerPosition) {
|
||||
let triggerPos, triggerWidth;
|
||||
if (section === "center") {
|
||||
const centerSection = barWindow.isVertical ? (barWindow.axis?.edge === "left" ? vCenterSection : vCenterSection) : hCenterSection;
|
||||
if (centerSection) {
|
||||
if (barWindow.isVertical) {
|
||||
const centerY = centerSection.height / 2;
|
||||
const centerGlobalPos = centerSection.mapToItem(null, 0, centerY);
|
||||
triggerPos = centerGlobalPos;
|
||||
triggerWidth = centerSection.height;
|
||||
} else {
|
||||
const centerGlobalPos = centerSection.mapToItem(null, 0, 0);
|
||||
triggerPos = centerGlobalPos;
|
||||
triggerWidth = centerSection.width;
|
||||
}
|
||||
} else {
|
||||
triggerPos = visualContent.mapToItem(null, 0, 0);
|
||||
triggerWidth = visualWidth;
|
||||
}
|
||||
} else {
|
||||
triggerPos = visualContent.mapToItem(null, 0, 0);
|
||||
triggerWidth = visualWidth;
|
||||
}
|
||||
const pos = SettingsData.getPopupTriggerPosition(triggerPos, barWindow.screen, barWindow.effectiveBarThickness, triggerWidth, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
dankDashPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, section, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
} else {
|
||||
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
|
||||
}
|
||||
PopoutManager.requestPopout(dankDashPopoutLoader.item, 0, (effectiveBarConfig?.id ?? "default") + "-" + section + "-0");
|
||||
}
|
||||
const section = topBarContent.getWidgetSection(parent) || "center";
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: dankDashPopoutLoader,
|
||||
widgetItem: clockWidget,
|
||||
section,
|
||||
tabIndex: 0,
|
||||
triggerSource: topBarContent._dashTriggerSource(section, 0),
|
||||
mode: "click",
|
||||
useCenterSection: true,
|
||||
setTriggerScreen: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -954,6 +1100,7 @@ Item {
|
||||
id: mediaComponent
|
||||
|
||||
Media {
|
||||
id: mediaWidget
|
||||
axis: barWindow.axis
|
||||
compactMode: topBarContent.spacingTight || topBarContent.overlapping
|
||||
barThickness: barWindow.effectiveBarThickness
|
||||
@@ -962,43 +1109,17 @@ Item {
|
||||
popoutTarget: dankDashPopoutLoader.item ?? null
|
||||
parentScreen: barWindow.screen
|
||||
onClicked: {
|
||||
dankDashPopoutLoader.active = true;
|
||||
if (dankDashPopoutLoader.item) {
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
if (dankDashPopoutLoader.item.setBarContext) {
|
||||
dankDashPopoutLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||
}
|
||||
if (dankDashPopoutLoader.item.setTriggerPosition) {
|
||||
let triggerPos, triggerWidth;
|
||||
if (section === "center") {
|
||||
const centerSection = barWindow.isVertical ? (barWindow.axis?.edge === "left" ? vCenterSection : vCenterSection) : hCenterSection;
|
||||
if (centerSection) {
|
||||
if (barWindow.isVertical) {
|
||||
const centerY = centerSection.height / 2;
|
||||
const centerGlobalPos = centerSection.mapToItem(null, 0, centerY);
|
||||
triggerPos = centerGlobalPos;
|
||||
triggerWidth = centerSection.height;
|
||||
} else {
|
||||
const centerGlobalPos = centerSection.mapToItem(null, 0, 0);
|
||||
triggerPos = centerGlobalPos;
|
||||
triggerWidth = centerSection.width;
|
||||
}
|
||||
} else {
|
||||
triggerPos = visualContent.mapToItem(null, 0, 0);
|
||||
triggerWidth = visualWidth;
|
||||
}
|
||||
} else {
|
||||
triggerPos = visualContent.mapToItem(null, 0, 0);
|
||||
triggerWidth = visualWidth;
|
||||
}
|
||||
const pos = SettingsData.getPopupTriggerPosition(triggerPos, barWindow.screen, barWindow.effectiveBarThickness, triggerWidth, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
dankDashPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, section, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
} else {
|
||||
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
|
||||
}
|
||||
PopoutManager.requestPopout(dankDashPopoutLoader.item, 1, (effectiveBarConfig?.id ?? "default") + "-" + section + "-1");
|
||||
}
|
||||
const section = topBarContent.getWidgetSection(parent) || "center";
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: dankDashPopoutLoader,
|
||||
widgetItem: mediaWidget,
|
||||
section,
|
||||
tabIndex: 1,
|
||||
triggerSource: topBarContent._dashTriggerSource(section, 1),
|
||||
mode: "click",
|
||||
useCenterSection: true,
|
||||
setTriggerScreen: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1007,6 +1128,7 @@ Item {
|
||||
id: weatherComponent
|
||||
|
||||
Weather {
|
||||
id: weatherWidget
|
||||
axis: barWindow.axis
|
||||
barThickness: barWindow.effectiveBarThickness
|
||||
widgetThickness: barWindow.widgetThickness
|
||||
@@ -1014,47 +1136,17 @@ Item {
|
||||
popoutTarget: dankDashPopoutLoader.item ?? null
|
||||
parentScreen: barWindow.screen
|
||||
onClicked: {
|
||||
dankDashPopoutLoader.active = true;
|
||||
if (dankDashPopoutLoader.item) {
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
// Calculate barPosition from axis.edge
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
if (dankDashPopoutLoader.item.setBarContext) {
|
||||
dankDashPopoutLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||
}
|
||||
if (dankDashPopoutLoader.item.setTriggerPosition) {
|
||||
// For center section widgets, use center section bounds for DankDash centering
|
||||
let triggerPos, triggerWidth;
|
||||
if (section === "center") {
|
||||
const centerSection = barWindow.isVertical ? (barWindow.axis?.edge === "left" ? vCenterSection : vCenterSection) : hCenterSection;
|
||||
if (centerSection) {
|
||||
// For vertical bars, use center Y of section; for horizontal, use left edge
|
||||
if (barWindow.isVertical) {
|
||||
const centerY = centerSection.height / 2;
|
||||
const centerGlobalPos = centerSection.mapToItem(null, 0, centerY);
|
||||
triggerPos = centerGlobalPos;
|
||||
triggerWidth = centerSection.height;
|
||||
} else {
|
||||
// For horizontal bars, use left edge (DankPopout will center it)
|
||||
const centerGlobalPos = centerSection.mapToItem(null, 0, 0);
|
||||
triggerPos = centerGlobalPos;
|
||||
triggerWidth = centerSection.width;
|
||||
}
|
||||
} else {
|
||||
triggerPos = visualContent.mapToItem(null, 0, 0);
|
||||
triggerWidth = visualWidth;
|
||||
}
|
||||
} else {
|
||||
triggerPos = visualContent.mapToItem(null, 0, 0);
|
||||
triggerWidth = visualWidth;
|
||||
}
|
||||
const pos = SettingsData.getPopupTriggerPosition(triggerPos, barWindow.screen, barWindow.effectiveBarThickness, triggerWidth, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
dankDashPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, section, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
} else {
|
||||
dankDashPopoutLoader.item.triggerScreen = barWindow.screen;
|
||||
}
|
||||
PopoutManager.requestPopout(dankDashPopoutLoader.item, 3, (effectiveBarConfig?.id ?? "default") + "-" + section + "-3");
|
||||
}
|
||||
const section = topBarContent.getWidgetSection(parent) || "center";
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: dankDashPopoutLoader,
|
||||
widgetItem: weatherWidget,
|
||||
section,
|
||||
tabIndex: 3,
|
||||
triggerSource: topBarContent._dashTriggerSource(section, 3),
|
||||
mode: "click",
|
||||
useCenterSection: true,
|
||||
setTriggerScreen: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1100,22 +1192,13 @@ Item {
|
||||
parentScreen: barWindow.screen
|
||||
widgetData: parent.widgetData
|
||||
onCpuClicked: {
|
||||
processListPopoutLoader.active = true;
|
||||
if (!processListPopoutLoader.item) {
|
||||
return;
|
||||
}
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
if (processListPopoutLoader.item.setBarContext) {
|
||||
processListPopoutLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||
}
|
||||
if (processListPopoutLoader.item.setTriggerPosition) {
|
||||
const globalPos = cpuWidget.mapToItem(null, 0, 0);
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, cpuWidget.width, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
const widgetSection = topBarContent.getWidgetSection(parent) || "right";
|
||||
processListPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, widgetSection, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
}
|
||||
PopoutManager.requestPopout(processListPopoutLoader.item, undefined, "cpu");
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: processListPopoutLoader,
|
||||
widgetItem: cpuWidget,
|
||||
section: topBarContent.getWidgetSection(parent) || "right",
|
||||
triggerSource: "cpu",
|
||||
mode: "click"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1133,22 +1216,13 @@ Item {
|
||||
parentScreen: barWindow.screen
|
||||
widgetData: parent.widgetData
|
||||
onRamClicked: {
|
||||
processListPopoutLoader.active = true;
|
||||
if (!processListPopoutLoader.item) {
|
||||
return;
|
||||
}
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
if (processListPopoutLoader.item.setBarContext) {
|
||||
processListPopoutLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||
}
|
||||
if (processListPopoutLoader.item.setTriggerPosition) {
|
||||
const globalPos = ramWidget.mapToItem(null, 0, 0);
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, ramWidget.width, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
const widgetSection = topBarContent.getWidgetSection(parent) || "right";
|
||||
processListPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, widgetSection, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
}
|
||||
PopoutManager.requestPopout(processListPopoutLoader.item, undefined, "memory");
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: processListPopoutLoader,
|
||||
widgetItem: ramWidget,
|
||||
section: topBarContent.getWidgetSection(parent) || "right",
|
||||
triggerSource: "memory",
|
||||
mode: "click"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1180,22 +1254,13 @@ Item {
|
||||
parentScreen: barWindow.screen
|
||||
widgetData: parent.widgetData
|
||||
onCpuTempClicked: {
|
||||
processListPopoutLoader.active = true;
|
||||
if (!processListPopoutLoader.item) {
|
||||
return;
|
||||
}
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
if (processListPopoutLoader.item.setBarContext) {
|
||||
processListPopoutLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||
}
|
||||
if (processListPopoutLoader.item.setTriggerPosition) {
|
||||
const globalPos = cpuTempWidget.mapToItem(null, 0, 0);
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, cpuTempWidget.width, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
const widgetSection = topBarContent.getWidgetSection(parent) || "right";
|
||||
processListPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, widgetSection, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
}
|
||||
PopoutManager.requestPopout(processListPopoutLoader.item, undefined, "cpu_temp");
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: processListPopoutLoader,
|
||||
widgetItem: cpuTempWidget,
|
||||
section: topBarContent.getWidgetSection(parent) || "right",
|
||||
triggerSource: "cpu_temp",
|
||||
mode: "click"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1213,22 +1278,13 @@ Item {
|
||||
parentScreen: barWindow.screen
|
||||
widgetData: parent.widgetData
|
||||
onGpuTempClicked: {
|
||||
processListPopoutLoader.active = true;
|
||||
if (!processListPopoutLoader.item) {
|
||||
return;
|
||||
}
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
if (processListPopoutLoader.item.setBarContext) {
|
||||
processListPopoutLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||
}
|
||||
if (processListPopoutLoader.item.setTriggerPosition) {
|
||||
const globalPos = gpuTempWidget.mapToItem(null, 0, 0);
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, gpuTempWidget.width, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
const widgetSection = topBarContent.getWidgetSection(parent) || "right";
|
||||
processListPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, widgetSection, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
}
|
||||
PopoutManager.requestPopout(processListPopoutLoader.item, undefined, "gpu_temp");
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: processListPopoutLoader,
|
||||
widgetItem: gpuTempWidget,
|
||||
section: topBarContent.getWidgetSection(parent) || "right",
|
||||
triggerSource: "gpu_temp",
|
||||
mode: "click"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1253,23 +1309,14 @@ Item {
|
||||
popoutTarget: notificationCenterLoader.item ?? null
|
||||
parentScreen: barWindow.screen
|
||||
onClicked: {
|
||||
notificationCenterLoader.active = true;
|
||||
if (!notificationCenterLoader.item) {
|
||||
return;
|
||||
}
|
||||
notificationCenterLoader.item.triggerScreen = barWindow.screen;
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
if (notificationCenterLoader.item.setBarContext) {
|
||||
notificationCenterLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||
}
|
||||
if (notificationCenterLoader.item.setTriggerPosition) {
|
||||
const globalPos = notificationButton.mapToItem(null, 0, 0);
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, notificationButton.width, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
const widgetSection = topBarContent.getWidgetSection(parent) || "right";
|
||||
notificationCenterLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, widgetSection, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
}
|
||||
PopoutManager.requestPopout(notificationCenterLoader.item, undefined, "notifications");
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: notificationCenterLoader,
|
||||
widgetItem: notificationButton,
|
||||
section: topBarContent.getWidgetSection(parent) || "right",
|
||||
triggerSource: "notifications",
|
||||
mode: "click",
|
||||
setTriggerScreen: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1289,22 +1336,13 @@ Item {
|
||||
popoutTarget: batteryPopoutLoader.item ?? null
|
||||
parentScreen: barWindow.screen
|
||||
onToggleBatteryPopup: {
|
||||
batteryPopoutLoader.active = true;
|
||||
if (!batteryPopoutLoader.item) {
|
||||
return;
|
||||
}
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
if (batteryPopoutLoader.item.setBarContext) {
|
||||
batteryPopoutLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||
}
|
||||
if (batteryPopoutLoader.item.setTriggerPosition) {
|
||||
const globalPos = batteryWidget.mapToItem(null, 0, 0);
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, batteryWidget.width, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
const widgetSection = topBarContent.getWidgetSection(parent) || "right";
|
||||
batteryPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, widgetSection, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
}
|
||||
PopoutManager.requestPopout(batteryPopoutLoader.item, undefined, "battery");
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: batteryPopoutLoader,
|
||||
widgetItem: batteryWidget,
|
||||
section: topBarContent.getWidgetSection(parent) || "right",
|
||||
triggerSource: "battery",
|
||||
mode: "click"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1322,20 +1360,13 @@ Item {
|
||||
popoutTarget: layoutPopoutLoader.item ?? null
|
||||
parentScreen: barWindow.screen
|
||||
onToggleLayoutPopup: {
|
||||
layoutPopoutLoader.active = true;
|
||||
if (!layoutPopoutLoader.item)
|
||||
return;
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
|
||||
if (layoutPopoutLoader.item.setTriggerPosition) {
|
||||
const globalPos = layoutWidget.mapToItem(null, 0, 0);
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, layoutWidget.width, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
const widgetSection = topBarContent.getWidgetSection(parent) || "center";
|
||||
layoutPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, widgetSection, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
}
|
||||
|
||||
PopoutManager.requestPopout(layoutPopoutLoader.item, undefined, "layout");
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: layoutPopoutLoader,
|
||||
widgetItem: layoutWidget,
|
||||
section: topBarContent.getWidgetSection(parent) || "center",
|
||||
triggerSource: "layout",
|
||||
mode: "click"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1355,24 +1386,13 @@ Item {
|
||||
popoutTarget: vpnPopoutLoader.item ?? null
|
||||
parentScreen: barWindow.screen
|
||||
onToggleVpnPopup: {
|
||||
vpnPopoutLoader.active = true;
|
||||
if (!vpnPopoutLoader.item)
|
||||
return;
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
|
||||
if (vpnPopoutLoader.item.setBarContext) {
|
||||
vpnPopoutLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||
}
|
||||
|
||||
if (vpnPopoutLoader.item.setTriggerPosition) {
|
||||
const globalPos = vpnWidget.mapToItem(null, 0, 0);
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, vpnWidget.width, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
const widgetSection = topBarContent.getWidgetSection(parent) || "right";
|
||||
vpnPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, widgetSection, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
}
|
||||
|
||||
PopoutManager.requestPopout(vpnPopoutLoader.item, undefined, "vpn");
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: vpnPopoutLoader,
|
||||
widgetItem: vpnWidget,
|
||||
section: topBarContent.getWidgetSection(parent) || "right",
|
||||
triggerSource: "vpn",
|
||||
mode: "click"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1381,6 +1401,7 @@ Item {
|
||||
id: controlCenterButtonComponent
|
||||
|
||||
ControlCenterButton {
|
||||
id: controlCenterButton
|
||||
isActive: controlCenterLoader.item ? controlCenterLoader.item.shouldBeVisible : false
|
||||
widgetThickness: barWindow.widgetThickness
|
||||
barThickness: barWindow.effectiveBarThickness
|
||||
@@ -1403,25 +1424,16 @@ Item {
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
controlCenterLoader.active = true;
|
||||
if (!controlCenterLoader.item) {
|
||||
return;
|
||||
}
|
||||
controlCenterLoader.item.triggerScreen = barWindow.screen;
|
||||
if (controlCenterLoader.item.setTriggerPosition) {
|
||||
const globalPos = mapToItem(null, 0, 0);
|
||||
// Use topBarContent.barConfig directly
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
// Calculate barPosition from axis.edge like Battery widget does
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, width, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
const section = topBarContent.getWidgetSection(parent) || "right";
|
||||
controlCenterLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, section, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
}
|
||||
PopoutManager.requestPopout(controlCenterLoader.item, undefined, "controlCenter");
|
||||
if (controlCenterLoader.item.shouldBeVisible && NetworkService.wifiEnabled) {
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: controlCenterLoader,
|
||||
widgetItem: controlCenterButton,
|
||||
section: topBarContent.getWidgetSection(parent) || "right",
|
||||
triggerSource: "controlCenter",
|
||||
mode: "click",
|
||||
setTriggerScreen: true
|
||||
});
|
||||
if (controlCenterLoader.item?.shouldBeVisible && NetworkService.wifiEnabled)
|
||||
NetworkService.scanWifi();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1531,6 +1543,7 @@ Item {
|
||||
id: systemUpdateComponent
|
||||
|
||||
SystemUpdate {
|
||||
id: systemUpdateWidget
|
||||
isActive: systemUpdateLoader.item ? systemUpdateLoader.item.shouldBeVisible : false
|
||||
widgetThickness: barWindow.widgetThickness
|
||||
barThickness: barWindow.effectiveBarThickness
|
||||
@@ -1549,22 +1562,14 @@ Item {
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
systemUpdateLoader.active = true;
|
||||
if (!systemUpdateLoader.item)
|
||||
return;
|
||||
const popout = systemUpdateLoader.item;
|
||||
const effectiveBarConfig = topBarContent.barConfig;
|
||||
const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1));
|
||||
if (popout.setBarContext) {
|
||||
popout.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0);
|
||||
}
|
||||
if (popout.setTriggerPosition) {
|
||||
const globalPos = visualContent.mapToItem(null, 0, 0);
|
||||
const currentScreen = parentScreen || Screen;
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barWindow.effectiveBarThickness, visualWidth, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig);
|
||||
popout.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig);
|
||||
}
|
||||
PopoutManager.requestPopout(popout, undefined, "systemUpdate");
|
||||
topBarContent.openWidgetPopout({
|
||||
loader: systemUpdateLoader,
|
||||
widgetItem: systemUpdateWidget,
|
||||
section: topBarContent.getWidgetSection(parent) || "right",
|
||||
triggerSource: "systemUpdate",
|
||||
mode: "click",
|
||||
visualItem: systemUpdateWidget
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,938 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property var barContent
|
||||
required property var barWindow
|
||||
required property var barConfig
|
||||
required property var hLeftSection
|
||||
required property var hCenterSection
|
||||
required property var hRightSection
|
||||
required property var vLeftSection
|
||||
required property var vCenterSection
|
||||
required property var vRightSection
|
||||
|
||||
property var leftWidgetsModel
|
||||
property var centerWidgetsModel
|
||||
property var rightWidgetsModel
|
||||
|
||||
property string activeHoverTrigger: ""
|
||||
readonly property bool hoverPopoutsEnabled: barConfig?.hoverPopouts ?? false
|
||||
readonly property int hoverPopoutDelay: Math.max(0, barConfig?.hoverPopoutDelay ?? 150)
|
||||
|
||||
property real _lastHoverGlobalX: 0
|
||||
property real _lastHoverGlobalY: 0
|
||||
property bool _hitTestPending: false
|
||||
property bool _barHovered: false
|
||||
property bool _barExitPending: false
|
||||
property var _pendingHoverHit: null
|
||||
property string _pendingHoverTrigger: ""
|
||||
|
||||
property bool _candidateCacheValid: false
|
||||
property var _candidateCache: []
|
||||
property var _candidateWatchers: []
|
||||
property bool _lastLookupWasMiss: false
|
||||
|
||||
width: 0
|
||||
height: 0
|
||||
|
||||
onLeftWidgetsModelChanged: invalidateCandidateCache()
|
||||
onCenterWidgetsModelChanged: invalidateCandidateCache()
|
||||
onRightWidgetsModelChanged: invalidateCandidateCache()
|
||||
|
||||
onHoverPopoutsEnabledChanged: {
|
||||
if (hoverPopoutsEnabled)
|
||||
return;
|
||||
cancelQueuedHitTest();
|
||||
_cancelPendingHover();
|
||||
_hoverCloseTimer.stop();
|
||||
if (hasOpenHoverSurface() && !isActiveHoverSurfacePinned())
|
||||
closeHoverSurfaces();
|
||||
activeHoverTrigger = "";
|
||||
}
|
||||
|
||||
Component.onDestruction: _disconnectCandidateWatchers()
|
||||
|
||||
Connections {
|
||||
target: root.barContent
|
||||
|
||||
function onWidthChanged() {
|
||||
root.invalidateCandidateCache();
|
||||
}
|
||||
|
||||
function onHeightChanged() {
|
||||
root.invalidateCandidateCache();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.barWindow
|
||||
|
||||
function onScreenChanged() {
|
||||
root.invalidateCandidateCache();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: BarWidgetService
|
||||
|
||||
function onWidgetRegistered(_widgetId, screenName) {
|
||||
if (screenName === root.barWindow?.screen?.name)
|
||||
root.invalidateCandidateCache();
|
||||
}
|
||||
|
||||
function onWidgetUnregistered(_widgetId, screenName) {
|
||||
if (screenName === root.barWindow?.screen?.name)
|
||||
root.invalidateCandidateCache();
|
||||
}
|
||||
}
|
||||
|
||||
FrameAnimation {
|
||||
running: root._hitTestPending
|
||||
onTriggered: {
|
||||
root._hitTestPending = false;
|
||||
root.checkHoverPopout(root._lastHoverGlobalX, root._lastHoverGlobalY);
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: _hoverIntentTimer
|
||||
interval: root.hoverPopoutDelay
|
||||
repeat: false
|
||||
onTriggered: root._commitPendingHover()
|
||||
}
|
||||
|
||||
// Grace timer to prevent flicker when crossing gaps.
|
||||
Timer {
|
||||
id: _hoverCloseTimer
|
||||
interval: 120
|
||||
repeat: false
|
||||
onTriggered: root._commitHoverClose()
|
||||
}
|
||||
|
||||
function queueHoverPoint(gx, gy) {
|
||||
_lastHoverGlobalX = gx;
|
||||
_lastHoverGlobalY = gy;
|
||||
_barHovered = true;
|
||||
_barExitPending = false;
|
||||
PopoutManager.updateHoverCursor(gx, gy);
|
||||
if (hoverPopoutsEnabled)
|
||||
_hitTestPending = true;
|
||||
}
|
||||
|
||||
function updateBarHovered(hovered) {
|
||||
_barHovered = hovered;
|
||||
if (hovered) {
|
||||
_barExitPending = false;
|
||||
_hoverCloseTimer.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
cancelQueuedHitTest();
|
||||
_cancelPendingHover();
|
||||
if (!hoverPopoutsEnabled || isActiveHoverSurfacePinned())
|
||||
return;
|
||||
_barExitPending = true;
|
||||
_hoverCloseTimer.restart();
|
||||
}
|
||||
|
||||
function cancelQueuedHitTest() {
|
||||
_hitTestPending = false;
|
||||
}
|
||||
|
||||
function recheckLatestPoint() {
|
||||
checkHoverPopout(_lastHoverGlobalX, _lastHoverGlobalY);
|
||||
}
|
||||
|
||||
function resetForBarGeometryChange() {
|
||||
invalidateCandidateCache();
|
||||
cancelQueuedHitTest();
|
||||
_cancelPendingHover();
|
||||
_hoverCloseTimer.stop();
|
||||
barContent._pendingPopoutOpenSpec = null;
|
||||
|
||||
const activePopout = PopoutManager.getActivePopout(barWindow?.screen);
|
||||
const hasTransientSurface = activeHoverTrigger !== "" || activePopout?.hoverDismissEnabled === true;
|
||||
if (hasTransientSurface && !isActiveHoverSurfacePinned())
|
||||
closeHoverSurfaces();
|
||||
else
|
||||
activeHoverTrigger = "";
|
||||
}
|
||||
|
||||
function invalidateCandidateCache() {
|
||||
_candidateCacheValid = false;
|
||||
_candidateCache = [];
|
||||
_lastLookupWasMiss = false;
|
||||
_disconnectCandidateWatchers();
|
||||
}
|
||||
|
||||
function _disconnectCandidateWatchers() {
|
||||
const watchers = _candidateWatchers;
|
||||
_candidateWatchers = [];
|
||||
for (let i = 0; i < watchers.length; i++) {
|
||||
const watcher = watchers[i];
|
||||
try {
|
||||
const signal = watcher.object?.[watcher.signalName];
|
||||
if (signal && typeof signal.disconnect === "function")
|
||||
signal.disconnect(watcher.callback);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
function _watchCandidateObject(object) {
|
||||
if (!object)
|
||||
return;
|
||||
for (let i = 0; i < _candidateWatchers.length; i++) {
|
||||
if (_candidateWatchers[i].object === object)
|
||||
return;
|
||||
}
|
||||
|
||||
const signalNames = ["xChanged", "yChanged", "widthChanged", "heightChanged", "visibleChanged", "parentChanged", "childrenChanged", "itemChanged", "activeChanged", "destroyed"];
|
||||
for (let i = 0; i < signalNames.length; i++) {
|
||||
const signalName = signalNames[i];
|
||||
try {
|
||||
const signal = object[signalName];
|
||||
if (!signal || typeof signal.connect !== "function")
|
||||
continue;
|
||||
const callback = function () {
|
||||
root.invalidateCandidateCache();
|
||||
};
|
||||
signal.connect(callback);
|
||||
_candidateWatchers.push({
|
||||
object,
|
||||
signalName,
|
||||
callback
|
||||
});
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
function _getBarSections() {
|
||||
if (barWindow.isVertical) {
|
||||
return [
|
||||
{
|
||||
section: vLeftSection,
|
||||
name: "left"
|
||||
},
|
||||
{
|
||||
section: vCenterSection,
|
||||
name: "center"
|
||||
},
|
||||
{
|
||||
section: vRightSection,
|
||||
name: "right"
|
||||
}
|
||||
];
|
||||
}
|
||||
return [
|
||||
{
|
||||
section: hLeftSection,
|
||||
name: "left"
|
||||
},
|
||||
{
|
||||
section: hCenterSection,
|
||||
name: "center"
|
||||
},
|
||||
{
|
||||
section: hRightSection,
|
||||
name: "right"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
// The widget registry is keyed by (widgetId, screenName)
|
||||
function _itemBelongsToThisBar(item) {
|
||||
const owner = barContent;
|
||||
if (!owner || !item)
|
||||
return true;
|
||||
let node = item;
|
||||
let guard = 0;
|
||||
while (node && guard < 100) {
|
||||
if (node === owner)
|
||||
return true;
|
||||
node = node.parent;
|
||||
guard++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function _findWidgetHostInWrapper(wrapper) {
|
||||
if (wrapper.widgetId !== undefined)
|
||||
return wrapper;
|
||||
const children = wrapper.children || [];
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (children[i].widgetId !== undefined)
|
||||
return children[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function _collectSectionWrappers(section) {
|
||||
_watchCandidateObject(section);
|
||||
const layoutLoader = section.widgetLayoutLoader;
|
||||
_watchCandidateObject(layoutLoader);
|
||||
const layout = layoutLoader?.item;
|
||||
if (layout) {
|
||||
_watchCandidateObject(layout);
|
||||
return layout.children || [];
|
||||
}
|
||||
const children = section.children || [];
|
||||
const wrappers = [];
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
if (!child || child === layoutLoader)
|
||||
continue;
|
||||
if (child.itemData !== undefined || child.widgetId !== undefined || _findWidgetHostInWrapper(child))
|
||||
wrappers.push(child);
|
||||
}
|
||||
return wrappers;
|
||||
}
|
||||
|
||||
function _widgetSupportsHoverPopout(widgetId, widgetItem) {
|
||||
if (!widgetId || !widgetItem)
|
||||
return false;
|
||||
if (typeof widgetItem.triggerHoverPopout === "function")
|
||||
return true;
|
||||
if (widgetId === "systemTray" && typeof widgetItem.openHoverAtGlobalPoint === "function")
|
||||
return true;
|
||||
switch (widgetId) {
|
||||
case "launcherButton":
|
||||
case "clipboard":
|
||||
case "clock":
|
||||
case "music":
|
||||
case "weather":
|
||||
case "cpuUsage":
|
||||
case "memUsage":
|
||||
case "cpuTemp":
|
||||
case "gpuTemp":
|
||||
case "notificationButton":
|
||||
case "battery":
|
||||
case "layout":
|
||||
case "vpn":
|
||||
case "controlCenterButton":
|
||||
case "systemUpdate":
|
||||
case "notepadButton":
|
||||
case "systemTray":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function _enumerateWidgetHosts() {
|
||||
const hosts = [];
|
||||
const sections = _getBarSections();
|
||||
for (let s = 0; s < sections.length; s++) {
|
||||
const sectionEntry = sections[s];
|
||||
const section = sectionEntry.section;
|
||||
if (!section)
|
||||
continue;
|
||||
const wrappers = _collectSectionWrappers(section);
|
||||
for (let i = 0; i < wrappers.length; i++) {
|
||||
const wrapper = wrappers[i];
|
||||
const host = _findWidgetHostInWrapper(wrapper);
|
||||
if (!host?.widgetId)
|
||||
continue;
|
||||
_watchCandidateObject(wrapper);
|
||||
_watchCandidateObject(host);
|
||||
hosts.push({
|
||||
host,
|
||||
wrapper,
|
||||
section: sectionEntry.name
|
||||
});
|
||||
}
|
||||
}
|
||||
return hosts;
|
||||
}
|
||||
|
||||
function _collectHoverCandidates() {
|
||||
const screenName = barWindow.screen?.name;
|
||||
const candidates = [];
|
||||
const seen = new Set();
|
||||
|
||||
function addCandidate(widgetId, widgetItem, sectionHint) {
|
||||
if (!widgetId || !widgetItem || seen.has(widgetItem))
|
||||
return;
|
||||
if (!root._itemBelongsToThisBar(widgetItem))
|
||||
return;
|
||||
if (!root._widgetSupportsHoverPopout(widgetId, widgetItem))
|
||||
return;
|
||||
if (!root.barContent.getWidgetVisible(widgetId))
|
||||
return;
|
||||
seen.add(widgetItem);
|
||||
candidates.push({
|
||||
widgetId,
|
||||
widgetItem,
|
||||
section: widgetItem.section || sectionHint || "right",
|
||||
wrapper: null,
|
||||
host: null
|
||||
});
|
||||
}
|
||||
|
||||
if (screenName) {
|
||||
const registry = BarWidgetService.widgetRegistry;
|
||||
if (registry && typeof registry === "object") {
|
||||
for (const widgetId in registry) {
|
||||
const screenMap = registry[widgetId];
|
||||
if (!screenMap || typeof screenMap !== "object")
|
||||
continue;
|
||||
const widgetItem = screenMap[screenName];
|
||||
if (widgetItem)
|
||||
addCandidate(widgetId, widgetItem, widgetItem.section);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hosts = _enumerateWidgetHosts();
|
||||
for (let i = 0; i < hosts.length; i++) {
|
||||
const entry = hosts[i];
|
||||
if (!entry.host?.item)
|
||||
continue;
|
||||
const existing = candidates.find(candidate => candidate.widgetItem === entry.host.item);
|
||||
if (existing) {
|
||||
existing.wrapper = entry.wrapper;
|
||||
existing.host = entry.host;
|
||||
if (!existing.section)
|
||||
existing.section = entry.section;
|
||||
continue;
|
||||
}
|
||||
if (!_widgetSupportsHoverPopout(entry.host.widgetId, entry.host.item))
|
||||
continue;
|
||||
candidates.push({
|
||||
widgetId: entry.host.widgetId,
|
||||
widgetItem: entry.host.item,
|
||||
section: entry.host.item.section || entry.section,
|
||||
wrapper: entry.wrapper,
|
||||
host: entry.host
|
||||
});
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
function _globalItemBounds(item) {
|
||||
try {
|
||||
const topLeft = item.mapToItem(null, 0, 0);
|
||||
return {
|
||||
x: topLeft.x,
|
||||
y: topLeft.y,
|
||||
width: item.width,
|
||||
height: item.height
|
||||
};
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function _hitBoundsForWidget(widgetItem, wrapper) {
|
||||
try {
|
||||
if (!widgetItem?.visible)
|
||||
return null;
|
||||
|
||||
if (widgetItem.visualContent !== undefined) {
|
||||
const visual = widgetItem.visualContent;
|
||||
if (visual && visual.width > 0 && visual.height > 0)
|
||||
return _globalItemBounds(visual);
|
||||
}
|
||||
|
||||
if (widgetItem.width > 0 && widgetItem.height > 0)
|
||||
return _globalItemBounds(widgetItem);
|
||||
|
||||
if (wrapper && wrapper.width > 0 && wrapper.height > 0)
|
||||
return _globalItemBounds(wrapper);
|
||||
} catch (e) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
function _pointInBounds(gx, gy, bounds) {
|
||||
return gx >= bounds.x && gx < bounds.x + bounds.width && gy >= bounds.y && gy < bounds.y + bounds.height;
|
||||
}
|
||||
|
||||
function _sameBounds(a, b) {
|
||||
return !!a && !!b && a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
||||
}
|
||||
|
||||
function _buildCandidateCache() {
|
||||
_disconnectCandidateWatchers();
|
||||
const candidates = _collectHoverCandidates();
|
||||
const cache = [];
|
||||
for (let i = 0; i < candidates.length; i++) {
|
||||
const entry = candidates[i];
|
||||
const bounds = _hitBoundsForWidget(entry.widgetItem, entry.wrapper);
|
||||
_watchCandidateObject(entry.widgetItem);
|
||||
_watchCandidateObject(entry.wrapper);
|
||||
_watchCandidateObject(entry.host);
|
||||
try {
|
||||
_watchCandidateObject(entry.widgetItem?.visualContent);
|
||||
} catch (e) {}
|
||||
if (!bounds || bounds.width <= 0 || bounds.height <= 0)
|
||||
continue;
|
||||
cache.push({
|
||||
widgetId: entry.widgetId,
|
||||
widgetItem: entry.widgetItem,
|
||||
section: entry.section,
|
||||
wrapper: entry.wrapper,
|
||||
bounds
|
||||
});
|
||||
}
|
||||
_candidateCache = cache;
|
||||
_candidateCacheValid = true;
|
||||
_lastLookupWasMiss = false;
|
||||
}
|
||||
|
||||
function _scanCandidateCache(gx, gy) {
|
||||
let best = null;
|
||||
let bestArea = Infinity;
|
||||
for (let i = 0; i < _candidateCache.length; i++) {
|
||||
const entry = _candidateCache[i];
|
||||
const bounds = entry.bounds;
|
||||
if (!_pointInBounds(gx, gy, bounds))
|
||||
continue;
|
||||
const area = bounds.width * bounds.height;
|
||||
if (area < bestArea) {
|
||||
bestArea = area;
|
||||
best = entry;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
function _validatedHit(entry, gx, gy) {
|
||||
if (!entry)
|
||||
return null;
|
||||
const liveBounds = _hitBoundsForWidget(entry.widgetItem, entry.wrapper);
|
||||
if (!liveBounds || !_pointInBounds(gx, gy, liveBounds))
|
||||
return null;
|
||||
if (!_sameBounds(entry.bounds, liveBounds))
|
||||
return null;
|
||||
return {
|
||||
widgetId: entry.widgetId,
|
||||
widgetItem: entry.widgetItem,
|
||||
section: entry.section
|
||||
};
|
||||
}
|
||||
|
||||
function findWidgetAtGlobalPoint(gx, gy) {
|
||||
if (!_candidateCacheValid)
|
||||
_buildCandidateCache();
|
||||
|
||||
let entry = _scanCandidateCache(gx, gy);
|
||||
let hit = _validatedHit(entry, gx, gy);
|
||||
if (entry && !hit) {
|
||||
invalidateCandidateCache();
|
||||
_buildCandidateCache();
|
||||
entry = _scanCandidateCache(gx, gy);
|
||||
hit = _validatedHit(entry, gx, gy);
|
||||
} else if (!entry && !_lastLookupWasMiss) {
|
||||
// One live rebuild on entry into an empty gap covers layout changes whose
|
||||
// source did not expose a QML geometry signal without rescanning every frame.
|
||||
invalidateCandidateCache();
|
||||
_buildCandidateCache();
|
||||
entry = _scanCandidateCache(gx, gy);
|
||||
hit = _validatedHit(entry, gx, gy);
|
||||
}
|
||||
|
||||
_lastLookupWasMiss = !hit;
|
||||
return hit;
|
||||
}
|
||||
|
||||
function dashTriggerSource(section, tabIndex) {
|
||||
return (barConfig?.id ?? "default") + "-" + section + "-" + tabIndex;
|
||||
}
|
||||
|
||||
function _notepadWidgetForScreen() {
|
||||
// Prefer this bar's own enumerated candidates; the registry is screen-keyed and a
|
||||
// sibling bar on the same screen can shadow it.
|
||||
if (!_candidateCacheValid)
|
||||
_buildCandidateCache();
|
||||
for (let i = 0; i < _candidateCache.length; i++) {
|
||||
if (_candidateCache[i].widgetId === "notepadButton")
|
||||
return _candidateCache[i].widgetItem;
|
||||
}
|
||||
const screenName = barWindow?.screen?.name;
|
||||
const fromRegistry = screenName ? BarWidgetService.getWidget("notepadButton", screenName) : null;
|
||||
if (fromRegistry && _itemBelongsToThisBar(fromRegistry))
|
||||
return fromRegistry;
|
||||
return null;
|
||||
}
|
||||
|
||||
function notepadContainsGlobalPoint(gx, gy) {
|
||||
const instance = _notepadWidgetForScreen()?.notepadInstance;
|
||||
if (!instance?.isVisible || typeof instance.containsGlobalPoint !== "function")
|
||||
return false;
|
||||
return instance.containsGlobalPoint(gx, gy);
|
||||
}
|
||||
|
||||
function isActiveHoverSurfacePinned() {
|
||||
if (activeHoverTrigger === "notepadButton") {
|
||||
const instance = _notepadWidgetForScreen()?.notepadInstance;
|
||||
if (instance?.hoverDismissSuspended === true)
|
||||
return true;
|
||||
}
|
||||
return PopoutManager.isActivePopoutPinned(barWindow?.screen);
|
||||
}
|
||||
|
||||
function cursorOverHoverChain(gx, gy, excludedBarWindow) {
|
||||
if (PopoutManager.cursorOverBar(gx, gy, undefined, excludedBarWindow))
|
||||
return true;
|
||||
const popout = PopoutManager.getActivePopout(barWindow?.screen);
|
||||
if (popout?.containsGlobalPoint?.(gx, gy))
|
||||
return true;
|
||||
if (notepadContainsGlobalPoint(gx, gy))
|
||||
return true;
|
||||
const screenName = barWindow.screen?.name;
|
||||
if (screenName && TrayMenuManager.activeTrayMenus[screenName])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function _closeHoverNotepad() {
|
||||
if (activeHoverTrigger !== "notepadButton")
|
||||
return;
|
||||
const instance = _notepadWidgetForScreen()?.notepadInstance;
|
||||
if (!instance)
|
||||
return;
|
||||
if (instance.hoverDismissEnabled !== undefined)
|
||||
instance.hoverDismissEnabled = false;
|
||||
if (typeof instance.hideFromHoverDismiss === "function")
|
||||
instance.hideFromHoverDismiss();
|
||||
else if (typeof instance.hide === "function")
|
||||
instance.hide();
|
||||
}
|
||||
|
||||
function closeHoverSurfaces() {
|
||||
_closeHoverNotepad();
|
||||
activeHoverTrigger = "";
|
||||
PopoutManager.closePopoutForScreen(barWindow?.screen);
|
||||
TrayMenuManager.closeAllMenus();
|
||||
}
|
||||
|
||||
function _beginSupersededCloseForActive() {
|
||||
const popout = PopoutManager.getActivePopout(barWindow?.screen);
|
||||
if (popout && typeof popout.beginSupersededClose === "function")
|
||||
popout.beginSupersededClose();
|
||||
}
|
||||
|
||||
function openNotepadHover(widgetItem) {
|
||||
const instance = widgetItem.prepareNotepadInstance?.(widgetItem.notepadInstance) ?? widgetItem.notepadInstance;
|
||||
if (!instance || typeof instance.show !== "function")
|
||||
return false;
|
||||
if (instance.hoverDismissEnabled !== undefined)
|
||||
instance.hoverDismissEnabled = true;
|
||||
instance.show();
|
||||
return true;
|
||||
}
|
||||
|
||||
function _syncHoverTriggerState() {
|
||||
if (activeHoverTrigger === "notepadButton") {
|
||||
const instance = _notepadWidgetForScreen()?.notepadInstance;
|
||||
if (!instance?.isVisible)
|
||||
activeHoverTrigger = "";
|
||||
return;
|
||||
}
|
||||
if (activeHoverTrigger !== "" && !hasOpenHoverSurface())
|
||||
activeHoverTrigger = "";
|
||||
}
|
||||
|
||||
function hasOpenHoverSurface() {
|
||||
if (activeHoverTrigger === "")
|
||||
return false;
|
||||
if (activeHoverTrigger === "notepadButton") {
|
||||
const instance = _notepadWidgetForScreen()?.notepadInstance;
|
||||
return instance?.isVisible ?? false;
|
||||
}
|
||||
if (activeHoverTrigger.startsWith("tray-")) {
|
||||
const screenName = barWindow.screen?.name;
|
||||
return !!(screenName && TrayMenuManager.activeTrayMenus[screenName]);
|
||||
}
|
||||
const popout = PopoutManager.getActivePopout(barWindow?.screen);
|
||||
if (!popout)
|
||||
return false;
|
||||
if (popout.dashVisible !== undefined)
|
||||
return !!popout.dashVisible || !!popout.isClosing;
|
||||
if (popout.notificationHistoryVisible !== undefined)
|
||||
return !!popout.notificationHistoryVisible || !!popout.isClosing;
|
||||
return !!(popout.shouldBeVisible || popout.isClosing);
|
||||
}
|
||||
|
||||
function _loaderForWidgetId(widgetId) {
|
||||
switch (widgetId) {
|
||||
case "launcherButton":
|
||||
return PopoutService.appDrawerLoader;
|
||||
case "clipboard":
|
||||
return PopoutService.clipboardHistoryPopoutLoader;
|
||||
case "clock":
|
||||
case "music":
|
||||
case "weather":
|
||||
return PopoutService.dankDashPopoutLoader;
|
||||
case "cpuUsage":
|
||||
case "memUsage":
|
||||
case "cpuTemp":
|
||||
case "gpuTemp":
|
||||
return PopoutService.processListPopoutLoader;
|
||||
case "notificationButton":
|
||||
return PopoutService.notificationCenterLoader;
|
||||
case "battery":
|
||||
return PopoutService.batteryPopoutLoader;
|
||||
case "layout":
|
||||
return PopoutService.layoutPopoutLoader;
|
||||
case "vpn":
|
||||
return PopoutService.vpnPopoutLoader;
|
||||
case "controlCenterButton":
|
||||
return PopoutService.controlCenterLoader;
|
||||
case "systemUpdate":
|
||||
return PopoutService.systemUpdateLoader;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function openHoverPopoutForHit(hit) {
|
||||
if (!hit?.widgetItem)
|
||||
return false;
|
||||
|
||||
const widgetId = hit.widgetId;
|
||||
const widgetItem = hit.widgetItem;
|
||||
const section = hit.section;
|
||||
const base = {
|
||||
widgetItem,
|
||||
section,
|
||||
mode: "hover"
|
||||
};
|
||||
|
||||
if (widgetId === "systemTray") {
|
||||
if (typeof widgetItem.openHoverAtGlobalPoint !== "function")
|
||||
return false;
|
||||
return !!widgetItem.openHoverAtGlobalPoint(hit.globalX, hit.globalY);
|
||||
}
|
||||
|
||||
if (typeof widgetItem.triggerHoverPopout === "function") {
|
||||
widgetItem.triggerHoverPopout(hit.widgetId);
|
||||
return true;
|
||||
}
|
||||
|
||||
const loader = _loaderForWidgetId(widgetId);
|
||||
switch (widgetId) {
|
||||
case "launcherButton":
|
||||
return barContent.openWidgetPopout(Object.assign({}, base, {
|
||||
loader,
|
||||
triggerSource: "appDrawer",
|
||||
visualItem: widgetItem
|
||||
}));
|
||||
case "clipboard":
|
||||
return barContent.openWidgetPopout(Object.assign({}, base, {
|
||||
loader,
|
||||
triggerSource: "clipboard",
|
||||
prepare: popout => {
|
||||
popout.activeTab = "recents";
|
||||
}
|
||||
}));
|
||||
case "clock":
|
||||
case "music":
|
||||
case "weather":
|
||||
{
|
||||
const tabIndex = widgetId === "clock" ? 0 : (widgetId === "music" ? 1 : 3);
|
||||
return barContent.openWidgetPopout(Object.assign({}, base, {
|
||||
loader,
|
||||
tabIndex,
|
||||
triggerSource: dashTriggerSource(section, tabIndex),
|
||||
useCenterSection: true,
|
||||
setTriggerScreen: true
|
||||
}));
|
||||
}
|
||||
case "cpuUsage":
|
||||
case "memUsage":
|
||||
case "cpuTemp":
|
||||
case "gpuTemp":
|
||||
{
|
||||
const triggerSources = {
|
||||
cpuUsage: "cpu",
|
||||
memUsage: "memory",
|
||||
cpuTemp: "cpu_temp",
|
||||
gpuTemp: "gpu_temp"
|
||||
};
|
||||
return barContent.openWidgetPopout(Object.assign({}, base, {
|
||||
loader,
|
||||
triggerSource: triggerSources[widgetId]
|
||||
}));
|
||||
}
|
||||
case "notificationButton":
|
||||
return barContent.openWidgetPopout(Object.assign({}, base, {
|
||||
loader,
|
||||
triggerSource: "notifications",
|
||||
setTriggerScreen: true
|
||||
}));
|
||||
case "battery":
|
||||
case "layout":
|
||||
case "vpn":
|
||||
{
|
||||
const triggerSources = {
|
||||
battery: "battery",
|
||||
layout: "layout",
|
||||
vpn: "vpn"
|
||||
};
|
||||
return barContent.openWidgetPopout(Object.assign({}, base, {
|
||||
loader,
|
||||
triggerSource: triggerSources[widgetId]
|
||||
}));
|
||||
}
|
||||
case "controlCenterButton":
|
||||
if (barContent.openWidgetPopout(Object.assign({}, base, {
|
||||
loader,
|
||||
triggerSource: "controlCenter",
|
||||
setTriggerScreen: true
|
||||
}))) {
|
||||
if (loader.item?.shouldBeVisible && NetworkService.wifiEnabled)
|
||||
NetworkService.scanWifi();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case "systemUpdate":
|
||||
return barContent.openWidgetPopout(Object.assign({}, base, {
|
||||
loader,
|
||||
triggerSource: "systemUpdate",
|
||||
visualItem: widgetItem
|
||||
}));
|
||||
case "notepadButton":
|
||||
return openNotepadHover(widgetItem);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function checkHoverPopout(gx, gy) {
|
||||
if (!hoverPopoutsEnabled)
|
||||
return;
|
||||
|
||||
_lastHoverGlobalX = gx;
|
||||
_lastHoverGlobalY = gy;
|
||||
PopoutManager.updateHoverCursor(gx, gy);
|
||||
_syncHoverTriggerState();
|
||||
|
||||
if (isActiveHoverSurfacePinned())
|
||||
return;
|
||||
|
||||
const hit = findWidgetAtGlobalPoint(gx, gy);
|
||||
if (!hit) {
|
||||
_cancelPendingHover();
|
||||
scheduleHoverClose(gx, gy);
|
||||
return;
|
||||
}
|
||||
|
||||
hit.globalX = gx;
|
||||
hit.globalY = gy;
|
||||
|
||||
let triggerKey = hit.widgetId;
|
||||
if (hit.widgetId === "systemTray")
|
||||
triggerKey = hit.widgetItem.hoverTriggerAtGlobalPoint?.(gx, gy) || "";
|
||||
else if (hit.widgetId === "clock")
|
||||
triggerKey = dashTriggerSource(hit.section, 0);
|
||||
else if (hit.widgetId === "music")
|
||||
triggerKey = dashTriggerSource(hit.section, 1);
|
||||
else if (hit.widgetId === "weather")
|
||||
triggerKey = dashTriggerSource(hit.section, 3);
|
||||
|
||||
if (!triggerKey) {
|
||||
_cancelPendingHover();
|
||||
scheduleHoverClose(gx, gy);
|
||||
return;
|
||||
}
|
||||
|
||||
_hoverCloseTimer.stop();
|
||||
|
||||
if (triggerKey === activeHoverTrigger && hasOpenHoverSurface()) {
|
||||
_cancelPendingHover();
|
||||
return;
|
||||
}
|
||||
|
||||
_pendingHoverHit = hit;
|
||||
if (_pendingHoverTrigger !== triggerKey) {
|
||||
_pendingHoverTrigger = triggerKey;
|
||||
if (hoverPopoutDelay <= 0)
|
||||
_commitPendingHover();
|
||||
else
|
||||
_hoverIntentTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
function _cancelPendingHover() {
|
||||
_hoverIntentTimer.stop();
|
||||
_pendingHoverHit = null;
|
||||
_pendingHoverTrigger = "";
|
||||
}
|
||||
|
||||
function _hitTargetsActivePopout(hit) {
|
||||
const active = PopoutManager.getActivePopout(barWindow?.screen);
|
||||
if (!active || !hit)
|
||||
return false;
|
||||
const loader = _loaderForWidgetId(hit.widgetId);
|
||||
if (!loader)
|
||||
return false;
|
||||
return barContent._resolvePopoutFromLoader(loader) === active;
|
||||
}
|
||||
|
||||
function _commitPendingHover() {
|
||||
const hit = _pendingHoverHit;
|
||||
const triggerKey = _pendingHoverTrigger;
|
||||
_pendingHoverHit = null;
|
||||
_pendingHoverTrigger = "";
|
||||
if (!hit || !hoverPopoutsEnabled)
|
||||
return;
|
||||
if (isActiveHoverSurfacePinned())
|
||||
return;
|
||||
if (!PopoutManager.cursorOverBar(_lastHoverGlobalX, _lastHoverGlobalY))
|
||||
return;
|
||||
|
||||
const activePopout = PopoutManager.getActivePopout(barWindow?.screen);
|
||||
const targetLoader = _loaderForWidgetId(hit.widgetId);
|
||||
const targetPopout = barContent._resolvePopoutFromLoader(targetLoader);
|
||||
const managerOwnsTransition = !!(activePopout && targetPopout);
|
||||
|
||||
if (triggerKey !== activeHoverTrigger && activeHoverTrigger !== "" && !_hitTargetsActivePopout(hit)) {
|
||||
if (!managerOwnsTransition) {
|
||||
_beginSupersededCloseForActive();
|
||||
closeHoverSurfaces();
|
||||
}
|
||||
}
|
||||
|
||||
if (!openHoverPopoutForHit(hit)) {
|
||||
if (activeHoverTrigger !== "")
|
||||
closeHoverSurfaces();
|
||||
return;
|
||||
}
|
||||
|
||||
activeHoverTrigger = triggerKey;
|
||||
}
|
||||
|
||||
function scheduleHoverClose(gx, gy) {
|
||||
cancelQueuedHitTest();
|
||||
_cancelPendingHover();
|
||||
_barExitPending = false;
|
||||
if (!hoverPopoutsEnabled)
|
||||
return;
|
||||
if (isActiveHoverSurfacePinned())
|
||||
return;
|
||||
if (cursorOverHoverChain(gx, gy))
|
||||
return;
|
||||
_hoverCloseTimer.restart();
|
||||
}
|
||||
|
||||
function _commitHoverClose() {
|
||||
const gx = PopoutManager.hoverCursorGlobalX;
|
||||
const gy = PopoutManager.hoverCursorGlobalY;
|
||||
if (isActiveHoverSurfacePinned())
|
||||
return;
|
||||
if (_barHovered)
|
||||
return;
|
||||
const excludedBar = _barExitPending ? barWindow : null;
|
||||
if (cursorOverHoverChain(gx, gy, excludedBar))
|
||||
return;
|
||||
_barExitPending = false;
|
||||
closeHoverSurfaces();
|
||||
}
|
||||
}
|
||||
@@ -719,6 +719,14 @@ PanelWindow {
|
||||
readonly property var _rightSection: topBarContent ? (barWindow.isVertical ? topBarContent.vRightSection : topBarContent.hRightSection) : null
|
||||
readonly property real _revealProgress: topBarSlide.x + topBarSlide.y
|
||||
|
||||
function containsGlobalPoint(gx, gy, padding) {
|
||||
const pad = padding !== undefined ? padding : 16;
|
||||
if (!inputMask.showing)
|
||||
return false;
|
||||
const topLeft = inputMask.mapToItem(null, 0, 0);
|
||||
return gx >= topLeft.x - pad && gx < topLeft.x + inputMask.width + pad && gy >= topLeft.y - pad && gy < topLeft.y + inputMask.height + pad;
|
||||
}
|
||||
|
||||
function sectionRect(section, isCenter, _dep) {
|
||||
if (!section)
|
||||
return {
|
||||
@@ -1020,7 +1028,7 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
|
||||
onWheel: wheel => {
|
||||
function processWheel(wheel) {
|
||||
if (!(barConfig?.scrollEnabled ?? true) || actionInProgress) {
|
||||
wheel.accepted = false;
|
||||
return;
|
||||
@@ -1089,6 +1097,8 @@ PanelWindow {
|
||||
|
||||
wheel.accepted = false;
|
||||
}
|
||||
|
||||
onWheel: wheel => processWheel(wheel)
|
||||
}
|
||||
|
||||
DankBarContent {
|
||||
@@ -1100,6 +1110,26 @@ PanelWindow {
|
||||
centerWidgetsModel: barWindow.centerWidgetsModel
|
||||
rightWidgetsModel: barWindow.rightWidgetsModel
|
||||
}
|
||||
|
||||
// Passive HoverHandler to track cursor without intercepting clicks or scroll events.
|
||||
HoverHandler {
|
||||
id: hoverPopoutHandler
|
||||
enabled: (barConfig?.hoverPopouts ?? false) && !barWindow.clickThroughEnabled
|
||||
|
||||
property real lastGlobalX: 0
|
||||
property real lastGlobalY: 0
|
||||
|
||||
onPointChanged: {
|
||||
const gp = barUnitInset.mapToItem(null, point.position.x, point.position.y);
|
||||
lastGlobalX = gp.x;
|
||||
lastGlobalY = gp.y;
|
||||
topBarContent.queueHoverPopout(gp.x, gp.y);
|
||||
}
|
||||
|
||||
onHoveredChanged: {
|
||||
topBarContent.updateHoverBarHovered(hovered);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ Item {
|
||||
property bool forceVerticalLayout: false
|
||||
|
||||
readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false)
|
||||
property alias widgetLayoutLoader: layoutLoader
|
||||
|
||||
implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0
|
||||
implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0
|
||||
|
||||
@@ -19,6 +19,7 @@ Item {
|
||||
property bool forceVerticalLayout: false
|
||||
|
||||
readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false)
|
||||
property alias widgetLayoutLoader: layoutLoader
|
||||
|
||||
implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0
|
||||
implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0
|
||||
|
||||
@@ -2103,4 +2103,53 @@ BasePill {
|
||||
return;
|
||||
currentTrayMenu.showForTrayItem(item, anchor, screen, atBottom, vertical ?? false, axisObj);
|
||||
}
|
||||
|
||||
function _trayLayoutRoot() {
|
||||
const contentChildren = root.visualContent?.children;
|
||||
if (!contentChildren || contentChildren.length === 0)
|
||||
return null;
|
||||
const contentRoot = contentChildren[0];
|
||||
return contentRoot?.layoutLoader?.item || null;
|
||||
}
|
||||
|
||||
function _trayHitAtGlobalPoint(gx, gy) {
|
||||
if (!root.visible || root.width <= 0 || root.height <= 0)
|
||||
return null;
|
||||
const local = root.mapFromItem(null, gx, gy);
|
||||
if (local.x < 0 || local.y < 0 || local.x > root.width || local.y > root.height)
|
||||
return null;
|
||||
const layout = _trayLayoutRoot();
|
||||
if (!layout)
|
||||
return null;
|
||||
const layoutLocal = layout.mapFromItem(null, gx, gy);
|
||||
const children = layout.children || [];
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
if (!child.visible || child.width <= 0 || child.height <= 0)
|
||||
continue;
|
||||
if (layoutLocal.x < child.x || layoutLocal.x >= child.x + child.width)
|
||||
continue;
|
||||
if (layoutLocal.y < child.y || layoutLocal.y >= child.y + child.height)
|
||||
continue;
|
||||
if (child.trayItem)
|
||||
return child;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function hoverTriggerAtGlobalPoint(gx, gy) {
|
||||
const hit = _trayHitAtGlobalPoint(gx, gy);
|
||||
if (!hit?.trayItem?.hasMenu)
|
||||
return "";
|
||||
return "tray-" + (hit.trayItem.id || hit.itemKey || "");
|
||||
}
|
||||
|
||||
function openHoverAtGlobalPoint(gx, gy) {
|
||||
const hit = _trayHitAtGlobalPoint(gx, gy);
|
||||
if (!hit?.trayItem?.hasMenu)
|
||||
return false;
|
||||
const anchor = hit.children?.length > 0 ? hit.children[0] : hit;
|
||||
showForTrayItem(hit.trayItem, anchor, parentScreen, isAtBottom, isVerticalOrientation, axis);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
|
||||
// Edge strip to trigger launcher hover-reveal when free of panel bars and dock.
|
||||
Variants {
|
||||
id: root
|
||||
|
||||
model: Quickshell.screens
|
||||
|
||||
delegate: Loader {
|
||||
id: zoneLoader
|
||||
|
||||
required property var modelData
|
||||
|
||||
readonly property string emergeSide: SettingsData.frameLauncherEmergeSide || "bottom"
|
||||
readonly property bool eligible: SettingsData.frameEnabled && SettingsData.frameLauncherEdgeHover && Theme.isConnectedEffect && SettingsData.isScreenInPreferences(zoneLoader.modelData, SettingsData.frameScreenPreferences) && CompositorService.usesConnectedFrameChromeForScreen(zoneLoader.modelData) && !SettingsData.barOccupiesSide(zoneLoader.modelData, zoneLoader.emergeSide) && !SettingsData.dockOccupiesSide(zoneLoader.emergeSide)
|
||||
|
||||
active: eligible
|
||||
asynchronous: false
|
||||
|
||||
sourceComponent: PanelWindow {
|
||||
id: zoneWindow
|
||||
|
||||
readonly property bool vertical: zoneLoader.emergeSide === "left" || zoneLoader.emergeSide === "right"
|
||||
readonly property real triggerThickness: Math.max(6, SettingsData.frameThickness)
|
||||
readonly property bool launcherOpen: PopoutService.dankLauncherV2Modal?.spotlightOpen ?? false
|
||||
property bool _openedForCurrentHover: false
|
||||
|
||||
// Hot zone dimensions centered on the emerge edge to cover the launcher footprint.
|
||||
readonly property real _launcherBaseW: SettingsData.dankLauncherV2Size === "micro" ? 500 : (SettingsData.dankLauncherV2Size === "medium" ? 720 : (SettingsData.dankLauncherV2Size === "large" ? 860 : 620))
|
||||
readonly property real _launcherBaseH: SettingsData.dankLauncherV2Size === "micro" ? 480 : (SettingsData.dankLauncherV2Size === "medium" ? 720 : (SettingsData.dankLauncherV2Size === "large" ? 860 : 600))
|
||||
readonly property real screenW: zoneLoader.modelData?.width ?? 0
|
||||
readonly property real screenH: zoneLoader.modelData?.height ?? 0
|
||||
readonly property real spanW: Math.round(Math.min(_launcherBaseW, screenW - 100) * 1.1)
|
||||
readonly property real spanH: Math.round(Math.min(_launcherBaseH, screenH - 100) * 1.1)
|
||||
|
||||
function requestLauncherOpen() {
|
||||
if (launcherOpen || _openedForCurrentHover)
|
||||
return;
|
||||
_openedForCurrentHover = true;
|
||||
PopoutService.openDankLauncherV2(CompositorService.framePeerSurfacesUseOverlayForScreen(zoneLoader.modelData), true);
|
||||
}
|
||||
|
||||
screen: zoneLoader.modelData
|
||||
color: "transparent"
|
||||
|
||||
WlrLayershell.namespace: "dms:frame-launcher-hover"
|
||||
WlrLayershell.layer: WlrLayer.Top
|
||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||
|
||||
// Anchor and center the hover zone alignment with the launcher.
|
||||
anchors {
|
||||
top: zoneLoader.emergeSide === "top" || zoneWindow.vertical
|
||||
bottom: zoneLoader.emergeSide === "bottom"
|
||||
left: zoneLoader.emergeSide === "left" || !zoneWindow.vertical
|
||||
right: zoneLoader.emergeSide === "right"
|
||||
}
|
||||
|
||||
margins {
|
||||
left: zoneWindow.vertical ? 0 : Math.max(0, (zoneWindow.screenW - zoneWindow.spanW) / 2)
|
||||
top: zoneWindow.vertical ? Math.max(0, (zoneWindow.screenH - zoneWindow.spanH) / 2) : 0
|
||||
}
|
||||
|
||||
implicitWidth: zoneWindow.vertical ? zoneWindow.triggerThickness : zoneWindow.spanW
|
||||
implicitHeight: zoneWindow.vertical ? zoneWindow.spanH : zoneWindow.triggerThickness
|
||||
|
||||
MouseArea {
|
||||
id: edgeHoverArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
|
||||
onContainsMouseChanged: {
|
||||
if (containsMouse)
|
||||
zoneWindow.requestLauncherOpen();
|
||||
else
|
||||
zoneWindow._openedForCurrentHover = false;
|
||||
}
|
||||
onPositionChanged: {
|
||||
if (containsMouse)
|
||||
zoneWindow.requestLauncherOpen();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ Item {
|
||||
property bool showSettingsMenu: false
|
||||
property string pendingSaveContent: ""
|
||||
readonly property bool conflictBannerVisible: currentTab !== null && NotepadStorageService.conflictTabId === currentTab.id
|
||||
readonly property bool anyModalOpen: fileDialogOpen || confirmationDialogOpen
|
||||
property var slideout: null
|
||||
property bool inPopout: false
|
||||
property bool surfaceVisible: slideout ? slideout.isVisible : true
|
||||
@@ -50,6 +51,14 @@ Item {
|
||||
slideout.suppressOverlayLayer = fileDialogOpen;
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: root.slideout
|
||||
property: "hoverDismissSuspended"
|
||||
value: root.anyModalOpen
|
||||
when: root.slideout !== null
|
||||
restoreMode: Binding.RestoreBindingOrValue
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: slideout
|
||||
enabled: slideout !== null
|
||||
|
||||
@@ -330,6 +330,24 @@ Item {
|
||||
pluginPopout.toggle();
|
||||
}
|
||||
|
||||
function triggerHoverPopout(widgetHostId) {
|
||||
if (pillClickAction) {
|
||||
triggerPopout();
|
||||
return;
|
||||
}
|
||||
if (!hasPopout)
|
||||
return;
|
||||
|
||||
const pill = isVertical ? verticalPill : horizontalPill;
|
||||
const globalPos = pill.visualContent.mapToItem(null, 0, 0);
|
||||
const currentScreen = parentScreen || Screen;
|
||||
const barPosition = axis?.edge === "left" ? 2 : (axis?.edge === "right" ? 3 : (axis?.edge === "top" ? 0 : 1));
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, pill.visualWidth, barSpacing, barPosition, barConfig);
|
||||
|
||||
pluginPopout.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen, barPosition, barThickness, barSpacing, barConfig);
|
||||
PopoutManager.requestHoverPopout(pluginPopout, undefined, widgetHostId || pluginId);
|
||||
}
|
||||
|
||||
PluginPopout {
|
||||
id: pluginPopout
|
||||
contentWidth: root.popoutWidth
|
||||
|
||||
@@ -26,6 +26,19 @@ DankPopout {
|
||||
open();
|
||||
}
|
||||
|
||||
function prepareForTrigger(triggerSource) {
|
||||
switch (triggerSource) {
|
||||
case "memory":
|
||||
DgopService.setSortBy("memory");
|
||||
break;
|
||||
case "cpu":
|
||||
case "cpu_temp":
|
||||
case "gpu_temp":
|
||||
DgopService.setSortBy("cpu");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
popupWidth: Math.round(Theme.fontSizeMedium * 46)
|
||||
popupHeight: Math.round(Theme.fontSizeMedium * 39)
|
||||
triggerWidth: 55
|
||||
|
||||
@@ -171,6 +171,8 @@ Item {
|
||||
scrollEnabled: defaultBar.scrollEnabled ?? true,
|
||||
scrollXBehavior: defaultBar.scrollXBehavior ?? "column",
|
||||
scrollYBehavior: defaultBar.scrollYBehavior ?? "workspace",
|
||||
hoverPopouts: defaultBar.hoverPopouts ?? false,
|
||||
hoverPopoutDelay: defaultBar.hoverPopoutDelay ?? 150,
|
||||
shadowIntensity: defaultBar.shadowIntensity ?? 0,
|
||||
shadowOpacity: defaultBar.shadowOpacity ?? 60,
|
||||
shadowDirectionMode: defaultBar.shadowDirectionMode ?? "inherit",
|
||||
@@ -1255,6 +1257,50 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsToggleCard {
|
||||
iconName: "touch_app"
|
||||
title: I18n.tr("Hover Popouts")
|
||||
description: I18n.tr("Open widget popouts by hovering over the bar. Moving to another widget switches the popout.")
|
||||
visible: !dankBarTab.appearanceOnly && selectedBarConfig?.enabled
|
||||
enabled: !(selectedBarConfig?.clickThrough ?? false)
|
||||
opacity: (selectedBarConfig?.clickThrough ?? false) ? 0.5 : 1.0
|
||||
checked: selectedBarConfig?.hoverPopouts ?? false
|
||||
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
|
||||
hoverPopouts: checked
|
||||
})
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
visible: selectedBarConfig?.hoverPopouts ?? false
|
||||
leftPadding: Theme.spacingM
|
||||
|
||||
SettingsSliderRow {
|
||||
id: hoverDelaySlider
|
||||
width: parent.width - parent.leftPadding
|
||||
text: I18n.tr("Open Delay")
|
||||
description: I18n.tr("Time to rest on a widget before its popout opens")
|
||||
value: selectedBarConfig?.hoverPopoutDelay ?? 150
|
||||
minimum: 0
|
||||
maximum: 1000
|
||||
unit: "ms"
|
||||
defaultValue: 150
|
||||
onSliderValueChanged: newValue => {
|
||||
SettingsData.updateBarConfig(selectedBarId, {
|
||||
hoverPopoutDelay: newValue
|
||||
});
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: hoverDelaySlider
|
||||
property: "value"
|
||||
value: selectedBarConfig?.hoverPopoutDelay ?? 150
|
||||
restoreMode: Binding.RestoreBinding
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsToggleCard {
|
||||
iconName: "fit_screen"
|
||||
title: I18n.tr("Maximize Detection")
|
||||
|
||||
@@ -357,6 +357,15 @@ Item {
|
||||
checked: SettingsData.frameLauncherArcExtender
|
||||
onToggled: checked => SettingsData.set("frameLauncherArcExtender", checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "frameLauncherEdgeHover"
|
||||
tags: ["frame", "connected", "launcher", "hover", "edge", "reveal"]
|
||||
text: I18n.tr("Edge Hover Reveal")
|
||||
description: I18n.tr("Open the launcher by hovering the emerge edge (when free of bar and dock)")
|
||||
checked: SettingsData.frameLauncherEdgeHover
|
||||
onToggled: checked => SettingsData.set("frameLauncherEdgeHover", checked)
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
|
||||
@@ -359,7 +359,7 @@ Item {
|
||||
tags: ["background", "color", "fill", "fit", "custom"]
|
||||
settingKey: "wallpaperBackgroundColorMode"
|
||||
text: I18n.tr("Background Color")
|
||||
description: I18n.tr("Color shown for areas not covered by wallpaper (e.g. Fit or Pad modes)")
|
||||
description: I18n.tr("Color shown for areas not covered by wallpaper")
|
||||
visible: root.currentWallpaper !== "" && !root.currentWallpaper.startsWith("#")
|
||||
dropdownWidth: 220
|
||||
options: [
|
||||
|
||||
Reference in New Issue
Block a user