1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-06 05:25:41 -05:00
Files
DankMaterialShell/quickshell/Widgets/DankOSD.qml

279 lines
10 KiB
QML

import QtQuick
import QtQuick.Effects
import Quickshell
import Quickshell.Wayland
import qs.Common
import qs.Services
import qs.Widgets
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() {
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
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
color: "transparent"
readonly property real dpr: CompositorService.getScreenScale(screen)
readonly property real screenWidth: screen.width
readonly property real screenHeight: screen.height
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: {
if (!SettingsData.dankBarVisible) return 0
const widgetThickness = Math.max(20, 26 + SettingsData.dankBarInnerPadding * 0.6)
return Math.max(widgetThickness + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
}
readonly property real barOffset: {
if (!SettingsData.dankBarVisible) return 0
return barThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap
}
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) return 0
return dockThickness + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin
}
readonly property real alignedX: {
const margin = Theme.spacingM
const centerX = (screenWidth - alignedWidth) / 2
switch (SettingsData.osdPosition) {
case SettingsData.Position.Left:
case SettingsData.Position.Bottom:
const leftBarOffset = SettingsData.dankBarPosition === 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 = SettingsData.dankBarPosition === 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 = SettingsData.dankBarPosition === 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 = SettingsData.dankBarPosition === 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
switch (SettingsData.osdPosition) {
case SettingsData.Position.Top:
case SettingsData.Position.Left:
case SettingsData.Position.TopCenter:
const topBarOffset = SettingsData.dankBarPosition === 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 = SettingsData.dankBarPosition === 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
right: true
bottom: true
}
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: alignedX
y: alignedY
width: alignedWidth
height: alignedHeight
opacity: shouldBeVisible ? 1 : 0
scale: shouldBeVisible ? 1 : 0.9
property bool childHovered: false
property real shadowBlurPx: 10
property real shadowSpreadPx: 0
property real shadowBaseAlpha: 0.60
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha * osdContainer.opacity))
DankRectangle {
id: background
anchors.fill: parent
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainer, osdContainer.popupSurfaceAlpha)
z: -1
}
Item {
id: bgShadowLayer
anchors.fill: parent
visible: osdContainer.popupSurfaceAlpha >= 0.95
layer.enabled: Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
layer.smooth: false
layer.textureSize: Qt.size(Math.round(width * root.dpr), Math.round(height * root.dpr))
layer.textureMirroring: ShaderEffectSource.MirrorVertically
layer.effect: MultiEffect {
id: shadowFx
autoPaddingEnabled: true
shadowEnabled: true
blurEnabled: false
maskEnabled: false
property int blurMax: 64
shadowBlur: Math.max(0, Math.min(1, osdContainer.shadowBlurPx / blurMax))
shadowScale: 1 + (2 * osdContainer.shadowSpreadPx) / Math.max(1, Math.min(bgShadowLayer.width, bgShadowLayer.height))
shadowColor: {
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest
return Theme.withAlpha(baseColor, osdContainer.effectiveShadowAlpha)
}
}
DankRectangle {
anchors.fill: parent
radius: Theme.cornerRadius
}
}
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
}
}