mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-15 07:35:20 -04:00
feat(popouts): implement hover popout functionality
This commit is contained in:
@@ -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