diff options
Diffstat (limited to 'raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Widgets/DankOSD.qml')
| -rw-r--r-- | raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Widgets/DankOSD.qml | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Widgets/DankOSD.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Widgets/DankOSD.qml new file mode 100644 index 0000000..0eb0e5a --- /dev/null +++ b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Widgets/DankOSD.qml @@ -0,0 +1,334 @@ +import QtQuick +import Quickshell +import Quickshell.Wayland +import qs.Common +import qs.Services + +PanelWindow { + id: root + + property string blurNamespace: "dms:osd" + WlrLayershell.namespace: blurNamespace + + property alias content: contentLoader.sourceComponent + property alias contentLoader: contentLoader + property var modelData + property bool shouldBeVisible: false + property int autoHideInterval: 2000 + property bool enableMouseInteraction: false + property real osdWidth: Theme.iconSize + Theme.spacingS * 2 + property real osdHeight: Theme.iconSize + Theme.spacingS * 2 + property int animationDuration: Theme.mediumDuration + property var animationEasing: Theme.emphasizedEasing + + signal osdShown + signal osdHidden + + function show() { + if (SessionData.suppressOSD) + return; + if (shouldBeVisible) { + hideTimer.restart(); + return; + } + OSDManager.showOSD(root); + closeTimer.stop(); + shouldBeVisible = true; + visible = true; + hideTimer.restart(); + osdShown(); + } + + function hide() { + shouldBeVisible = false; + closeTimer.restart(); + } + + function resetHideTimer() { + if (shouldBeVisible) { + hideTimer.restart(); + } + } + + function updateHoverState() { + let isHovered = (enableMouseInteraction && mouseArea.containsMouse) || osdContainer.childHovered; + if (enableMouseInteraction) { + if (isHovered) { + hideTimer.stop(); + } else if (shouldBeVisible) { + hideTimer.restart(); + } + } + } + + function setChildHovered(hovered) { + osdContainer.childHovered = hovered; + updateHoverState(); + } + + screen: modelData + visible: false + + Connections { + target: Quickshell + function onScreensChanged() { + if (!root.visible && !root.shouldBeVisible) + return; + const currentScreenName = root.screen?.name; + if (!currentScreenName) { + root.hide(); + return; + } + for (let i = 0; i < Quickshell.screens.length; i++) { + if (Quickshell.screens[i].name === currentScreenName) + return; + } + root.shouldBeVisible = false; + root.visible = false; + hideTimer.stop(); + closeTimer.stop(); + osdHidden(); + } + } + + WlrLayershell.layer: { + switch (Quickshell.env("DMS_OSD_LAYER")) { + case "bottom": + console.warn("DankOSD: 'bottom' layer is not valid for OSDs. Defaulting to 'overlay' layer."); + return WlrLayershell.Overlay; + case "background": + console.warn("DankOSD: 'background' layer is not valid for OSDs. Defaulting to 'overlay' layer."); + return WlrLayershell.Overlay; + case "top": + return WlrLayershell.Top; + default: + return WlrLayershell.Overlay; + } + } + WlrLayershell.exclusiveZone: -1 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.None + + WindowBlur { + targetWindow: root + blurX: shadowBuffer + blurY: shadowBuffer + blurWidth: shouldBeVisible ? alignedWidth : 0 + blurHeight: shouldBeVisible ? alignedHeight : 0 + blurRadius: Theme.cornerRadius + } + + color: "transparent" + + readonly property real dpr: CompositorService.getScreenScale(screen) + readonly property real screenWidth: screen.width + readonly property real screenHeight: screen.height + readonly property real shadowBuffer: 15 + readonly property real alignedWidth: Theme.px(osdWidth, dpr) + readonly property real alignedHeight: Theme.px(osdHeight, dpr) + + readonly property bool isVerticalLayout: SettingsData.osdPosition === SettingsData.Position.LeftCenter || SettingsData.osdPosition === SettingsData.Position.RightCenter + + readonly property real barThickness: { + const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default"); + if (!defaultBar || !(defaultBar.visible ?? true)) + return 0; + const innerPadding = defaultBar.innerPadding ?? 4; + const widgetThickness = Math.max(20, 26 + innerPadding * 0.6); + return Math.max(widgetThickness + innerPadding + 4, Theme.barHeight - 4 - (8 - innerPadding)); + } + + readonly property real barOffset: { + const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default"); + if (!defaultBar || !(defaultBar.visible ?? true)) + return 0; + const spacing = defaultBar.spacing ?? 4; + const bottomGap = defaultBar.bottomGap ?? 0; + return barThickness + spacing + bottomGap; + } + + readonly property real dockThickness: { + if (!SettingsData.showDock) + return 0; + return SettingsData.dockIconSize + SettingsData.dockSpacing * 2 + 10; + } + + readonly property real dockOffset: { + if (!SettingsData.showDock || SettingsData.dockAutoHide || SettingsData.dockSmartAutoHide) + return 0; + return dockThickness + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin; + } + + readonly property real alignedX: { + const margin = Theme.spacingM; + const centerX = (screenWidth - alignedWidth) / 2; + + const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default"); + const barPos = defaultBar?.position ?? SettingsData.Position.Top; + + switch (SettingsData.osdPosition) { + case SettingsData.Position.Left: + case SettingsData.Position.Bottom: + const leftBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0; + const leftDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0; + return Theme.snap(margin + Math.max(leftBarOffset, leftDockOffset), dpr); + case SettingsData.Position.Top: + case SettingsData.Position.Right: + const rightBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0; + const rightDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0; + return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightBarOffset, rightDockOffset), dpr); + case SettingsData.Position.LeftCenter: + const leftCenterBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0; + const leftCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0; + return Theme.snap(margin + Math.max(leftCenterBarOffset, leftCenterDockOffset), dpr); + case SettingsData.Position.RightCenter: + const rightCenterBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0; + const rightCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0; + return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightCenterBarOffset, rightCenterDockOffset), dpr); + case SettingsData.Position.TopCenter: + case SettingsData.Position.BottomCenter: + default: + return Theme.snap(centerX, dpr); + } + } + + readonly property real alignedY: { + const margin = Theme.spacingM; + const centerY = (screenHeight - alignedHeight) / 2; + + const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default"); + const barPos = defaultBar?.position ?? SettingsData.Position.Top; + + switch (SettingsData.osdPosition) { + case SettingsData.Position.Top: + case SettingsData.Position.Left: + case SettingsData.Position.TopCenter: + const topBarOffset = barPos === SettingsData.Position.Top ? barOffset : 0; + const topDockOffset = SettingsData.dockPosition === SettingsData.Position.Top ? dockOffset : 0; + return Theme.snap(margin + Math.max(topBarOffset, topDockOffset), dpr); + case SettingsData.Position.Right: + case SettingsData.Position.Bottom: + case SettingsData.Position.BottomCenter: + const bottomBarOffset = barPos === SettingsData.Position.Bottom ? barOffset : 0; + const bottomDockOffset = SettingsData.dockPosition === SettingsData.Position.Bottom ? dockOffset : 0; + return Theme.snap(screenHeight - alignedHeight - margin - Math.max(bottomBarOffset, bottomDockOffset), dpr); + case SettingsData.Position.LeftCenter: + case SettingsData.Position.RightCenter: + default: + return Theme.snap(centerY, dpr); + } + } + + anchors { + top: true + left: true + } + + WlrLayershell.margins { + left: Math.max(0, Theme.snap(alignedX - shadowBuffer, dpr)) + top: Math.max(0, Theme.snap(alignedY - shadowBuffer, dpr)) + } + + implicitWidth: alignedWidth + (shadowBuffer * 2) + implicitHeight: alignedHeight + (shadowBuffer * 2) + + Timer { + id: hideTimer + + interval: autoHideInterval + repeat: false + onTriggered: { + if (!enableMouseInteraction || !mouseArea.containsMouse) { + hide(); + } else { + hideTimer.restart(); + } + } + } + + Timer { + id: closeTimer + interval: animationDuration + 50 + onTriggered: { + if (!shouldBeVisible) { + visible = false; + osdHidden(); + } + } + } + + Item { + id: osdContainer + x: shadowBuffer + y: shadowBuffer + width: alignedWidth + height: alignedHeight + opacity: shouldBeVisible ? 1 : 0 + scale: shouldBeVisible ? 1 : 0.9 + + property bool childHovered: false + readonly property real popupSurfaceAlpha: Theme.popupTransparency + + Rectangle { + id: background + anchors.fill: parent + radius: Theme.cornerRadius + color: Theme.withAlpha(Theme.surfaceContainer, osdContainer.popupSurfaceAlpha) + border.color: BlurService.enabled ? BlurService.borderColor : Theme.outlineMedium + border.width: BlurService.enabled ? BlurService.borderWidth : 1 + z: -1 + } + + ElevationShadow { + id: bgShadowLayer + anchors.fill: parent + visible: osdContainer.popupSurfaceAlpha >= 0.95 + z: -1 + level: Theme.elevationLevel3 + fallbackOffset: 6 + targetRadius: Theme.cornerRadius + targetColor: Theme.withAlpha(Theme.surfaceContainer, osdContainer.popupSurfaceAlpha) + borderColor: Theme.outlineMedium + borderWidth: 1 + shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" + layer.textureSize: Qt.size(Math.round(width * root.dpr), Math.round(height * root.dpr)) + layer.textureMirroring: ShaderEffectSource.MirrorVertically + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: enableMouseInteraction + acceptedButtons: Qt.NoButton + propagateComposedEvents: true + z: -1 + onContainsMouseChanged: updateHoverState() + } + + onChildHoveredChanged: updateHoverState() + + Loader { + id: contentLoader + anchors.fill: parent + active: root.visible + asynchronous: false + } + + Behavior on opacity { + NumberAnimation { + duration: animationDuration + easing.type: animationEasing + } + } + + Behavior on scale { + NumberAnimation { + duration: animationDuration + easing.type: animationEasing + } + } + } + + mask: Region { + item: bgShadowLayer + } +} |