mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-13 06:33:30 -04:00
feat(popouts): implement hover popout functionality
This commit is contained in:
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
@@ -16,8 +17,76 @@ Singleton {
|
||||
signal popoutOpening
|
||||
signal popoutChanged
|
||||
|
||||
property real hoverCursorGlobalX: 0
|
||||
property real hoverCursorGlobalY: 0
|
||||
|
||||
function updateHoverCursor(gx, gy) {
|
||||
hoverCursorGlobalX = gx;
|
||||
hoverCursorGlobalY = gy;
|
||||
}
|
||||
|
||||
function cursorOverBar(gx, gy, padding) {
|
||||
const pad = padding !== undefined ? padding : 16;
|
||||
const bars = KeyboardFocus.barWindows || [];
|
||||
for (let i = 0; i < bars.length; i++) {
|
||||
const w = bars[i];
|
||||
if (!w?.visible)
|
||||
continue;
|
||||
if (typeof w.containsGlobalPoint === "function") {
|
||||
if (w.containsGlobalPoint(gx, gy, pad))
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
const item = w.contentItem;
|
||||
if (!item || typeof item.mapToItem !== "function")
|
||||
continue;
|
||||
const topLeft = item.mapToItem(null, 0, 0);
|
||||
if (!topLeft)
|
||||
continue;
|
||||
if (gx >= topLeft.x - pad && gx < topLeft.x + item.width + pad && gy >= topLeft.y - pad && gy < topLeft.y + item.height + pad)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function _isPopoutPresented(popout) {
|
||||
if (!popout)
|
||||
return false;
|
||||
try {
|
||||
if (popout.dashVisible !== undefined)
|
||||
return !!popout.dashVisible;
|
||||
if (popout.notificationHistoryVisible !== undefined)
|
||||
return !!popout.notificationHistoryVisible;
|
||||
return !!(popout.shouldBeVisible || popout.isClosing);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function _openPopout(popout) {
|
||||
if (popout.dashVisible !== undefined) {
|
||||
if (popout.dashVisible && !popout.shouldBeVisible && !popout.isClosing)
|
||||
popout.dashVisible = false;
|
||||
popout.dashVisible = true;
|
||||
return;
|
||||
}
|
||||
if (popout.notificationHistoryVisible !== undefined) {
|
||||
popout.notificationHistoryVisible = true;
|
||||
return;
|
||||
}
|
||||
popout.open();
|
||||
}
|
||||
|
||||
function _closePopout(popout) {
|
||||
try {
|
||||
if (popout?.hoverDismissEnabled) {
|
||||
if (typeof popout.closeFromHoverDismiss === "function") {
|
||||
popout.closeFromHoverDismiss();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (popout.hoverDismissEnabled !== undefined)
|
||||
popout.hoverDismissEnabled = false;
|
||||
switch (true) {
|
||||
case popout.dashVisible !== undefined:
|
||||
popout.dashVisible = false;
|
||||
@@ -89,7 +158,26 @@ Singleton {
|
||||
continue;
|
||||
_closePopout(popout);
|
||||
}
|
||||
currentPopoutsByScreen = {};
|
||||
// Keep map entries until each popout's close animation finishes (hidePopout).
|
||||
}
|
||||
|
||||
function closePopoutForScreen(screen) {
|
||||
if (!screen)
|
||||
return;
|
||||
const screenName = screen.name;
|
||||
const popout = currentPopoutsByScreen[screenName];
|
||||
if (!popout || _isStale(popout)) {
|
||||
currentPopoutsByScreen[screenName] = null;
|
||||
currentPopoutTriggers[screenName] = null;
|
||||
return;
|
||||
}
|
||||
_closePopout(popout);
|
||||
}
|
||||
|
||||
function cancelHoverDismiss(screen) {
|
||||
const popout = getActivePopout(screen);
|
||||
if (popout?.cancelHoverDismiss)
|
||||
popout.cancelHoverDismiss();
|
||||
}
|
||||
|
||||
function getActivePopout(screen) {
|
||||
@@ -106,6 +194,8 @@ Singleton {
|
||||
function requestPopout(popout, tabIndex, triggerSource) {
|
||||
if (!popout || !popout.screen)
|
||||
return;
|
||||
if (popout.hoverDismissEnabled !== undefined)
|
||||
popout.hoverDismissEnabled = false;
|
||||
screenshotActive = false;
|
||||
const screenName = popout.screen.name;
|
||||
const currentPopout = currentPopoutsByScreen[screenName];
|
||||
@@ -181,16 +271,81 @@ Singleton {
|
||||
ModalManager.closeAllModalsExcept(null);
|
||||
}
|
||||
|
||||
if (movedFromOtherScreen) {
|
||||
popout.open();
|
||||
} else {
|
||||
if (popout.dashVisible !== undefined) {
|
||||
popout.dashVisible = true;
|
||||
} else if (popout.notificationHistoryVisible !== undefined) {
|
||||
popout.notificationHistoryVisible = true;
|
||||
_openPopout(popout);
|
||||
}
|
||||
|
||||
function requestHoverPopout(popout, tabIndex, triggerSource) {
|
||||
if (!popout || !popout.screen)
|
||||
return;
|
||||
screenshotActive = false;
|
||||
const screenName = popout.screen.name;
|
||||
const currentPopout = currentPopoutsByScreen[screenName];
|
||||
const triggerId = triggerSource !== undefined ? triggerSource : tabIndex;
|
||||
|
||||
const willOpen = !(currentPopout === popout && _isPopoutPresented(popout) && triggerId !== undefined && currentPopoutTriggers[screenName] === triggerId);
|
||||
if (willOpen)
|
||||
popoutOpening();
|
||||
|
||||
let movedFromOtherScreen = false;
|
||||
for (const otherScreenName in currentPopoutsByScreen) {
|
||||
if (otherScreenName === screenName)
|
||||
continue;
|
||||
const otherPopout = currentPopoutsByScreen[otherScreenName];
|
||||
if (!otherPopout)
|
||||
continue;
|
||||
|
||||
if (_isStale(otherPopout)) {
|
||||
currentPopoutsByScreen[otherScreenName] = null;
|
||||
currentPopoutTriggers[otherScreenName] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (otherPopout === popout) {
|
||||
movedFromOtherScreen = true;
|
||||
currentPopoutsByScreen[otherScreenName] = null;
|
||||
currentPopoutTriggers[otherScreenName] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
_closePopout(otherPopout);
|
||||
}
|
||||
|
||||
if (currentPopout && currentPopout !== popout) {
|
||||
if (_isStale(currentPopout)) {
|
||||
currentPopoutsByScreen[screenName] = null;
|
||||
currentPopoutTriggers[screenName] = null;
|
||||
} else {
|
||||
popout.open();
|
||||
_closePopout(currentPopout);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentPopout === popout && _isPopoutPresented(popout) && !movedFromOtherScreen) {
|
||||
if (triggerId !== undefined && currentPopoutTriggers[screenName] === triggerId)
|
||||
return;
|
||||
|
||||
if (tabIndex !== undefined && popout.currentTabIndex !== undefined)
|
||||
popout.currentTabIndex = tabIndex;
|
||||
if (popout.updateSurfacePosition)
|
||||
popout.updateSurfacePosition();
|
||||
currentPopoutTriggers[screenName] = triggerId;
|
||||
if (popout.hoverDismissEnabled !== undefined)
|
||||
popout.hoverDismissEnabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
currentPopoutTriggers[screenName] = triggerId;
|
||||
currentPopoutsByScreen[screenName] = popout;
|
||||
popoutChanged();
|
||||
|
||||
if (tabIndex !== undefined && popout.currentTabIndex !== undefined)
|
||||
popout.currentTabIndex = tabIndex;
|
||||
|
||||
if (currentPopout !== popout)
|
||||
ModalManager.closeAllModalsExcept(null);
|
||||
|
||||
if (popout.hoverDismissEnabled !== undefined)
|
||||
popout.hoverDismissEnabled = true;
|
||||
|
||||
_openPopout(popout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -810,7 +810,8 @@ Singleton {
|
||||
"shadowOpacity": 60,
|
||||
"shadowColorMode": "default",
|
||||
"shadowCustomColor": "#000000",
|
||||
"clickThrough": false
|
||||
"clickThrough": false,
|
||||
"hoverPopouts": false
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -709,6 +709,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 {
|
||||
@@ -1010,7 +1018,7 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
|
||||
onWheel: wheel => {
|
||||
function processWheel(wheel) {
|
||||
if (!(barConfig?.scrollEnabled ?? true) || actionInProgress) {
|
||||
wheel.accepted = false;
|
||||
return;
|
||||
@@ -1079,6 +1087,8 @@ PanelWindow {
|
||||
|
||||
wheel.accepted = false;
|
||||
}
|
||||
|
||||
onWheel: wheel => processWheel(wheel)
|
||||
}
|
||||
|
||||
DankBarContent {
|
||||
@@ -1090,6 +1100,36 @@ PanelWindow {
|
||||
centerWidgetsModel: barWindow.centerWidgetsModel
|
||||
rightWidgetsModel: barWindow.rightWidgetsModel
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: hoverPopoutArea
|
||||
anchors.fill: parent
|
||||
z: 1
|
||||
hoverEnabled: barConfig?.hoverPopouts ?? false
|
||||
enabled: hoverPopoutArea.hoverEnabled && !barWindow.clickThroughEnabled
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
|
||||
property real lastGlobalX: 0
|
||||
property real lastGlobalY: 0
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
const gp = mapToItem(null, mouse.x, mouse.y);
|
||||
lastGlobalX = gp.x;
|
||||
lastGlobalY = gp.y;
|
||||
topBarContent.checkHoverPopout(gp.x, gp.y);
|
||||
}
|
||||
|
||||
onWheel: wheel => scrollArea.processWheel(wheel)
|
||||
|
||||
onContainsMouseChanged: {
|
||||
if (containsMouse)
|
||||
return;
|
||||
if (topBarContent.cursorOverHoverChain(lastGlobalX, lastGlobalY))
|
||||
return;
|
||||
topBarContent.closeHoverSurfaces();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1922,4 +1922,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -164,6 +164,7 @@ Item {
|
||||
scrollEnabled: defaultBar.scrollEnabled ?? true,
|
||||
scrollXBehavior: defaultBar.scrollXBehavior ?? "column",
|
||||
scrollYBehavior: defaultBar.scrollYBehavior ?? "workspace",
|
||||
hoverPopouts: defaultBar.hoverPopouts ?? false,
|
||||
shadowIntensity: defaultBar.shadowIntensity ?? 0,
|
||||
shadowOpacity: defaultBar.shadowOpacity ?? 60,
|
||||
shadowDirectionMode: defaultBar.shadowDirectionMode ?? "inherit",
|
||||
@@ -1741,6 +1742,19 @@ 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
|
||||
})
|
||||
}
|
||||
|
||||
SettingsToggleCard {
|
||||
iconName: "mouse"
|
||||
title: I18n.tr("Scroll Wheel")
|
||||
|
||||
@@ -24,6 +24,7 @@ Item {
|
||||
property list<real> animationExitCurve: Theme.variantPopoutExitCurve
|
||||
property bool suspendShadowWhileResizing: false
|
||||
property bool shouldBeVisible: false
|
||||
property bool hoverDismissEnabled: false
|
||||
property var customKeyboardFocus: null
|
||||
property bool backgroundInteractive: true
|
||||
property bool contentHandlesKeys: false
|
||||
@@ -82,6 +83,8 @@ Item {
|
||||
readonly property real alignedY: impl.item ? impl.item.alignedY : 0
|
||||
readonly property real alignedWidth: impl.item ? impl.item.alignedWidth : 0
|
||||
readonly property real alignedHeight: impl.item ? impl.item.alignedHeight : 0
|
||||
readonly property real renderedAlignedY: impl.item ? (impl.item.renderedAlignedY ?? impl.item.alignedY) : 0
|
||||
readonly property real renderedAlignedHeight: impl.item ? (impl.item.renderedAlignedHeight ?? impl.item.alignedHeight) : 0
|
||||
readonly property real maskX: impl.item ? impl.item.maskX : 0
|
||||
readonly property real maskY: impl.item ? impl.item.maskY : 0
|
||||
readonly property real maskWidth: impl.item ? impl.item.maskWidth : 0
|
||||
@@ -172,6 +175,32 @@ Item {
|
||||
impl.item.close();
|
||||
}
|
||||
|
||||
function cancelHoverDismiss() {
|
||||
if (impl.item?.cancelHoverDismiss)
|
||||
impl.item.cancelHoverDismiss();
|
||||
}
|
||||
|
||||
function closeFromHoverDismiss() {
|
||||
hoverDismissEnabled = false;
|
||||
if (impl.item) {
|
||||
impl.item.animationsEnabled = true;
|
||||
impl.item.animationDuration = Math.round(Theme.expressiveDurations.expressiveDefaultSpatial);
|
||||
impl.item.animationExitCurve = Theme.expressiveCurves.expressiveDefaultSpatial;
|
||||
}
|
||||
if (dashVisible !== undefined) {
|
||||
dashVisible = false;
|
||||
return;
|
||||
}
|
||||
if (notificationHistoryVisible !== undefined) {
|
||||
notificationHistoryVisible = false;
|
||||
return;
|
||||
}
|
||||
if (impl.item)
|
||||
impl.item.close();
|
||||
else
|
||||
close();
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
(shouldBeVisible || _pendingOpen) ? close() : open();
|
||||
}
|
||||
@@ -210,6 +239,20 @@ Item {
|
||||
impl.item.updateSurfacePosition();
|
||||
}
|
||||
|
||||
function containsGlobalPoint(gx, gy) {
|
||||
if (!screen)
|
||||
return false;
|
||||
const presented = shouldBeVisible || (impl.item?.isClosing ?? false);
|
||||
if (!presented)
|
||||
return false;
|
||||
const padding = 24;
|
||||
const x = alignedX - padding;
|
||||
const y = renderedAlignedY - padding;
|
||||
const w = alignedWidth + padding * 2;
|
||||
const h = renderedAlignedHeight + padding * 2;
|
||||
return gx >= x && gx <= x + w && gy >= y && gy <= y + h;
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: impl
|
||||
active: root.screen !== null
|
||||
@@ -261,6 +304,7 @@ Item {
|
||||
it.screen = Qt.binding(() => root.screen);
|
||||
it.effectiveBarPosition = Qt.binding(() => root.effectiveBarPosition);
|
||||
it.effectiveBarBottomGap = Qt.binding(() => root.effectiveBarBottomGap);
|
||||
it.hoverDismissEnabled = Qt.binding(() => root.hoverDismissEnabled);
|
||||
|
||||
it.shouldBeVisible = root.shouldBeVisible;
|
||||
if (root._primeContent && typeof it.primeContent === "function")
|
||||
|
||||
@@ -5,6 +5,7 @@ import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -407,6 +408,20 @@ Item {
|
||||
onFrameOwnsConnectedChromeChanged: _syncPopoutChromeState()
|
||||
|
||||
property bool animationsEnabled: true
|
||||
property bool hoverDismissEnabled: false
|
||||
|
||||
function cancelHoverDismiss() {
|
||||
hoverDismissTracker.cancelPending();
|
||||
}
|
||||
|
||||
function closeFromHoverDismiss() {
|
||||
if (isClosing || !shouldBeVisible)
|
||||
return;
|
||||
if (popoutHandle?.closeFromHoverDismiss)
|
||||
popoutHandle.closeFromHoverDismiss();
|
||||
else
|
||||
close();
|
||||
}
|
||||
|
||||
function open() {
|
||||
if (!screen)
|
||||
@@ -761,6 +776,27 @@ Item {
|
||||
visible: false
|
||||
color: "transparent"
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
z: -1
|
||||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: true
|
||||
onPositionChanged: mouse => {
|
||||
const gp = mapToItem(null, mouse.x, mouse.y);
|
||||
PopoutManager.updateHoverCursor(gp.x, gp.y);
|
||||
}
|
||||
}
|
||||
|
||||
HoverDismissTracker {
|
||||
id: hoverDismissTracker
|
||||
anchors.fill: parent
|
||||
enabled: root.hoverDismissEnabled && root.shouldBeVisible
|
||||
shouldDismiss: function () {
|
||||
return !PopoutManager.cursorOverBar(PopoutManager.hoverCursorGlobalX, PopoutManager.hoverCursorGlobalY);
|
||||
}
|
||||
onDismissRequested: root.closeFromHoverDismiss()
|
||||
}
|
||||
|
||||
WindowBlur {
|
||||
id: popoutBlur
|
||||
targetWindow: contentWindow
|
||||
|
||||
@@ -5,6 +5,7 @@ import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -35,6 +36,21 @@ Item {
|
||||
property bool shouldBeVisible: false
|
||||
property bool isClosing: false
|
||||
property bool animationsEnabled: true
|
||||
property bool hoverDismissEnabled: false
|
||||
|
||||
function cancelHoverDismiss() {
|
||||
hoverDismissTracker.cancelPending();
|
||||
}
|
||||
|
||||
function closeFromHoverDismiss() {
|
||||
if (isClosing || !shouldBeVisible)
|
||||
return;
|
||||
if (popoutHandle?.closeFromHoverDismiss)
|
||||
popoutHandle.closeFromHoverDismiss();
|
||||
else
|
||||
close();
|
||||
}
|
||||
|
||||
property var customKeyboardFocus: null
|
||||
property bool backgroundInteractive: true
|
||||
property bool contentHandlesKeys: false
|
||||
@@ -585,6 +601,27 @@ Item {
|
||||
color: "transparent"
|
||||
readonly property bool closeVisualActive: root.shouldBeVisible || root.isClosing
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
z: -1
|
||||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: true
|
||||
onPositionChanged: mouse => {
|
||||
const gp = mapToItem(null, mouse.x, mouse.y);
|
||||
PopoutManager.updateHoverCursor(gp.x, gp.y);
|
||||
}
|
||||
}
|
||||
|
||||
HoverDismissTracker {
|
||||
id: hoverDismissTracker
|
||||
anchors.fill: parent
|
||||
enabled: root.hoverDismissEnabled && root.shouldBeVisible
|
||||
shouldDismiss: function () {
|
||||
return !PopoutManager.cursorOverBar(PopoutManager.hoverCursorGlobalX, PopoutManager.hoverCursorGlobalY);
|
||||
}
|
||||
onDismissRequested: root.closeFromHoverDismiss()
|
||||
}
|
||||
|
||||
WindowBlur {
|
||||
id: popoutBlur
|
||||
targetWindow: contentWindow
|
||||
|
||||
@@ -13,6 +13,7 @@ PanelWindow {
|
||||
WlrLayershell.namespace: layerNamespace
|
||||
|
||||
property bool isVisible: false
|
||||
property bool hoverDismissEnabled: false
|
||||
property var targetScreen: null
|
||||
property var modelData: null
|
||||
property bool triggerUsesOverlayLayer: false
|
||||
@@ -39,6 +40,24 @@ PanelWindow {
|
||||
isVisible = false;
|
||||
}
|
||||
|
||||
function hideFromHoverDismiss() {
|
||||
hoverDismissEnabled = false;
|
||||
slideAnimation.duration = Math.round(Theme.expressiveDurations.expressiveDefaultSpatial);
|
||||
hide();
|
||||
}
|
||||
|
||||
function cancelHoverDismiss() {
|
||||
hoverDismissTracker.cancelPending();
|
||||
}
|
||||
|
||||
function containsGlobalPoint(gx, gy) {
|
||||
if (!isVisible || !modelData)
|
||||
return false;
|
||||
const padding = 24;
|
||||
const topLeft = slideContainer.mapToItem(null, 0, 0);
|
||||
return gx >= topLeft.x - padding && gx < topLeft.x + slideContainer.width + padding && gy >= topLeft.y - padding && gy < topLeft.y + slideContainer.height + padding;
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
if (isVisible) {
|
||||
hide();
|
||||
@@ -60,6 +79,27 @@ PanelWindow {
|
||||
|
||||
color: "transparent"
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
z: -1
|
||||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: true
|
||||
onPositionChanged: mouse => {
|
||||
const gp = mapToItem(null, mouse.x, mouse.y);
|
||||
PopoutManager.updateHoverCursor(gp.x, gp.y);
|
||||
}
|
||||
}
|
||||
|
||||
HoverDismissTracker {
|
||||
id: hoverDismissTracker
|
||||
anchors.fill: parent
|
||||
enabled: root.hoverDismissEnabled && root.isVisible
|
||||
shouldDismiss: function () {
|
||||
return !PopoutManager.cursorOverBar(PopoutManager.hoverCursorGlobalX, PopoutManager.hoverCursorGlobalY);
|
||||
}
|
||||
onDismissRequested: root.hideFromHoverDismiss()
|
||||
}
|
||||
|
||||
readonly property bool slideoutBlurActive: root.visible && BlurService.enabled && Theme.connectedSurfaceBlurEnabled
|
||||
|
||||
WlrLayershell.layer: (triggerUsesOverlayLayer || CompositorService.framePeerSurfacesUseOverlayForScreen(modelData)) ? WlrLayershell.Overlay : WlrLayershell.Top
|
||||
@@ -104,8 +144,10 @@ PanelWindow {
|
||||
easing.type: Easing.OutCubic
|
||||
|
||||
onRunningChanged: {
|
||||
if (!running && !root.isVisible) {
|
||||
root.mappedVisible = false;
|
||||
if (!running) {
|
||||
if (!root.isVisible)
|
||||
root.mappedVisible = false;
|
||||
slideAnimation.duration = 450;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property bool enabled: false
|
||||
property var shouldDismiss: null
|
||||
|
||||
signal dismissRequested
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
enabled: root.enabled
|
||||
onHoveredChanged: {
|
||||
if (hoverHandler.hovered || !root.enabled)
|
||||
return;
|
||||
if (typeof root.shouldDismiss === "function" && !root.shouldDismiss())
|
||||
return;
|
||||
root.dismissRequested();
|
||||
}
|
||||
}
|
||||
|
||||
function cancelPending() {}
|
||||
}
|
||||
Reference in New Issue
Block a user