mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-26 13:07:49 -04:00
387 lines
13 KiB
QML
387 lines
13 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import Quickshell
|
|
import Quickshell.Wayland
|
|
import Quickshell.Hyprland
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.Widgets
|
|
|
|
Item {
|
|
id: root
|
|
|
|
required property string pluginId
|
|
required property var screen
|
|
|
|
property string instanceId: ""
|
|
property var instanceData: null
|
|
property bool widgetEnabled: true
|
|
|
|
readonly property bool showOnOverlay: instanceData?.config?.showOnOverlay ?? false
|
|
readonly property bool showOnOverview: instanceData?.config?.showOnOverview ?? false
|
|
readonly property bool showOnOverviewOnly: instanceData?.config?.showOnOverviewOnly ?? false
|
|
readonly property bool overviewActive: CompositorService.isNiri && NiriService.inOverview
|
|
readonly property bool clickThrough: instanceData?.config?.clickThrough ?? false
|
|
|
|
// Unmapping with the widget still in the last buffer leaves a stale image in
|
|
// Hyprland's blur cache behind transparent tiled windows (#2955), so hide the
|
|
// content, present a transparent frame, then unmap.
|
|
readonly property bool contentShowing: widgetEnabled && content.activeComponent !== null && (!showOnOverviewOnly || overviewActive)
|
|
property bool surfaceLingering: false
|
|
|
|
onContentShowingChanged: {
|
|
if (contentShowing) {
|
|
lingerTimer.stop();
|
|
surfaceLingering = false;
|
|
return;
|
|
}
|
|
surfaceLingering = true;
|
|
lingerTimer.restart();
|
|
}
|
|
|
|
Timer {
|
|
id: lingerTimer
|
|
interval: 64
|
|
onTriggered: root.surfaceLingering = false
|
|
}
|
|
|
|
DesktopWidgetGeometry {
|
|
id: geometry
|
|
instanceId: root.instanceId
|
|
instanceData: root.instanceData
|
|
screen: root.screen
|
|
minWidth: content.contentMinWidth
|
|
minHeight: content.contentMinHeight
|
|
forceSquare: content.contentForceSquare
|
|
}
|
|
|
|
readonly property bool useGhostPreview: !CompositorService.isNiri
|
|
|
|
property real previewX: geometry.widgetX
|
|
property real previewY: geometry.widgetY
|
|
property real previewWidth: geometry.widgetWidth
|
|
property real previewHeight: geometry.widgetHeight
|
|
|
|
property bool isInteracting: dragArea.pressed || resizeArea.pressed
|
|
|
|
PanelWindow {
|
|
id: widgetWindow
|
|
screen: root.screen
|
|
visible: root.contentShowing || root.surfaceLingering
|
|
color: "transparent"
|
|
|
|
Region {
|
|
id: emptyMask
|
|
}
|
|
|
|
mask: root.clickThrough ? emptyMask : null
|
|
|
|
WlrLayershell.namespace: "dms:desktop-widget:" + root.pluginId + (root.instanceId ? ":" + root.instanceId : "")
|
|
WlrLayershell.layer: {
|
|
if (root.isInteracting && !CompositorService.useHyprlandFocusGrab)
|
|
return WlrLayer.Overlay;
|
|
if (root.showOnOverlay)
|
|
return WlrLayer.Overlay;
|
|
if (root.overviewActive && (root.showOnOverview || root.showOnOverviewOnly))
|
|
return WlrLayer.Overlay;
|
|
return WlrLayer.Bottom;
|
|
}
|
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
|
WlrLayershell.keyboardFocus: {
|
|
if (PopoutManager.screenshotActive)
|
|
return WlrKeyboardFocus.None;
|
|
if (root.isInteracting) {
|
|
if (CompositorService.useHyprlandFocusGrab)
|
|
return WlrKeyboardFocus.OnDemand;
|
|
return WlrKeyboardFocus.Exclusive;
|
|
}
|
|
if (content.acceptsKeyboardFocus)
|
|
return WlrKeyboardFocus.OnDemand;
|
|
return WlrKeyboardFocus.None;
|
|
}
|
|
|
|
HyprlandFocusGrab {
|
|
active: CompositorService.isHyprland && root.isInteracting
|
|
windows: [widgetWindow]
|
|
}
|
|
|
|
Item {
|
|
anchors.fill: parent
|
|
focus: root.isInteracting
|
|
|
|
Keys.onPressed: event => {
|
|
if (!root.isInteracting)
|
|
return;
|
|
switch (event.key) {
|
|
case Qt.Key_G:
|
|
SessionData.setWidgetGridSetting(false, geometry.screenKey, "enabled", !geometry.gridEnabled);
|
|
event.accepted = true;
|
|
break;
|
|
case Qt.Key_Z:
|
|
SessionData.setWidgetGridSetting(false, geometry.screenKey, "size", Math.max(10, geometry.gridSize - 10));
|
|
event.accepted = true;
|
|
break;
|
|
case Qt.Key_X:
|
|
SessionData.setWidgetGridSetting(false, geometry.screenKey, "size", Math.min(200, geometry.gridSize + 10));
|
|
event.accepted = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
anchors {
|
|
left: true
|
|
top: true
|
|
}
|
|
|
|
WlrLayershell.margins {
|
|
left: geometry.widgetX
|
|
top: geometry.widgetY
|
|
}
|
|
|
|
implicitWidth: geometry.widgetWidth
|
|
implicitHeight: geometry.widgetHeight
|
|
|
|
DesktopWidgetContent {
|
|
id: content
|
|
anchors.fill: parent
|
|
active: root.widgetEnabled
|
|
visible: root.contentShowing
|
|
pluginId: root.pluginId
|
|
instanceId: root.instanceId
|
|
instanceData: root.instanceData
|
|
screen: root.screen
|
|
geometry: geometry
|
|
}
|
|
|
|
Rectangle {
|
|
id: interactionBorder
|
|
anchors.fill: parent
|
|
color: "transparent"
|
|
border.color: Theme.primary
|
|
border.width: 2
|
|
radius: Theme.cornerRadius
|
|
visible: root.isInteracting && !root.useGhostPreview
|
|
opacity: 0.8
|
|
|
|
Rectangle {
|
|
anchors.right: parent.right
|
|
anchors.bottom: parent.bottom
|
|
width: 48
|
|
height: 48
|
|
topLeftRadius: Theme.cornerRadius
|
|
bottomRightRadius: Theme.cornerRadius
|
|
color: Theme.primary
|
|
opacity: resizeArea.pressed ? 1 : 0.6
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
id: dragArea
|
|
anchors.fill: parent
|
|
acceptedButtons: Qt.RightButton
|
|
enabled: !root.clickThrough
|
|
cursorShape: pressed ? Qt.ClosedHandCursor : Qt.ArrowCursor
|
|
|
|
property point startPos
|
|
property real startX
|
|
property real startY
|
|
|
|
onPressed: mouse => {
|
|
startPos = root.useGhostPreview ? Qt.point(mouse.x, mouse.y) : mapToGlobal(mouse.x, mouse.y);
|
|
startX = geometry.widgetX;
|
|
startY = geometry.widgetY;
|
|
root.previewX = geometry.widgetX;
|
|
root.previewY = geometry.widgetY;
|
|
geometry.dragOverrideX = geometry.widgetX;
|
|
geometry.dragOverrideY = geometry.widgetY;
|
|
}
|
|
|
|
onPositionChanged: mouse => {
|
|
if (!pressed)
|
|
return;
|
|
const currentPos = root.useGhostPreview ? Qt.point(mouse.x, mouse.y) : mapToGlobal(mouse.x, mouse.y);
|
|
const next = geometry.dragMoveTo(startX, startY, currentPos.x - startPos.x, currentPos.y - startPos.y);
|
|
if (root.useGhostPreview) {
|
|
root.previewX = next.x;
|
|
root.previewY = next.y;
|
|
return;
|
|
}
|
|
geometry.dragOverrideX = next.x;
|
|
geometry.dragOverrideY = next.y;
|
|
}
|
|
|
|
onReleased: {
|
|
const finalX = root.useGhostPreview ? root.previewX : geometry.dragOverrideX;
|
|
const finalY = root.useGhostPreview ? root.previewY : geometry.dragOverrideY;
|
|
geometry.savePosition(finalX, finalY);
|
|
geometry.clearDragOverrides();
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
id: resizeArea
|
|
width: 48
|
|
height: 48
|
|
anchors.right: parent.right
|
|
anchors.bottom: parent.bottom
|
|
acceptedButtons: Qt.RightButton
|
|
enabled: !root.clickThrough
|
|
cursorShape: pressed ? Qt.SizeFDiagCursor : Qt.ArrowCursor
|
|
|
|
property point startPos
|
|
property real startWidth
|
|
property real startHeight
|
|
|
|
onPressed: mouse => {
|
|
startPos = root.useGhostPreview ? Qt.point(mouse.x, mouse.y) : mapToGlobal(mouse.x, mouse.y);
|
|
startWidth = geometry.widgetWidth;
|
|
startHeight = geometry.widgetHeight;
|
|
root.previewWidth = geometry.widgetWidth;
|
|
root.previewHeight = geometry.widgetHeight;
|
|
geometry.dragOverrideW = geometry.widgetWidth;
|
|
geometry.dragOverrideH = geometry.widgetHeight;
|
|
}
|
|
|
|
onPositionChanged: mouse => {
|
|
if (!pressed)
|
|
return;
|
|
const currentPos = root.useGhostPreview ? Qt.point(mouse.x, mouse.y) : mapToGlobal(mouse.x, mouse.y);
|
|
const next = geometry.dragResizeTo(startWidth, startHeight, currentPos.x - startPos.x, currentPos.y - startPos.y);
|
|
if (root.useGhostPreview) {
|
|
root.previewWidth = next.width;
|
|
root.previewHeight = next.height;
|
|
return;
|
|
}
|
|
geometry.dragOverrideW = next.width;
|
|
geometry.dragOverrideH = next.height;
|
|
}
|
|
|
|
onReleased: {
|
|
const finalW = root.useGhostPreview ? root.previewWidth : geometry.dragOverrideW;
|
|
const finalH = root.useGhostPreview ? root.previewHeight : geometry.dragOverrideH;
|
|
geometry.saveSize(finalW, finalH);
|
|
geometry.clearDragOverrides();
|
|
}
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
active: root.isInteracting && root.useGhostPreview
|
|
|
|
sourceComponent: PanelWindow {
|
|
id: ghostPreviewWindow
|
|
screen: root.screen
|
|
color: "transparent"
|
|
|
|
anchors {
|
|
left: true
|
|
right: true
|
|
top: true
|
|
bottom: true
|
|
}
|
|
|
|
mask: Region {}
|
|
|
|
WlrLayershell.namespace: "dms:desktop-widget-preview"
|
|
WlrLayershell.layer: WlrLayer.Bottom
|
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
|
|
|
DesktopWidgetGridLines {
|
|
id: gridOverlay
|
|
anchors.fill: parent
|
|
visible: geometry.gridEnabled
|
|
gridSize: geometry.gridSize
|
|
extentWidth: geometry.screenWidth
|
|
extentHeight: geometry.screenHeight
|
|
}
|
|
|
|
Rectangle {
|
|
x: root.previewX
|
|
y: root.previewY
|
|
width: root.previewWidth
|
|
height: root.previewHeight
|
|
color: "transparent"
|
|
border.color: Theme.primary
|
|
border.width: 2
|
|
radius: Theme.cornerRadius
|
|
|
|
Rectangle {
|
|
width: 48
|
|
height: 48
|
|
anchors {
|
|
right: parent.right
|
|
bottom: parent.bottom
|
|
}
|
|
topLeftRadius: Theme.cornerRadius
|
|
bottomRightRadius: Theme.cornerRadius
|
|
color: Theme.primary
|
|
opacity: resizeArea.pressed ? 1 : 0.6
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
active: root.isInteracting && geometry.gridEnabled && !root.useGhostPreview
|
|
|
|
sourceComponent: PanelWindow {
|
|
screen: root.screen
|
|
color: "transparent"
|
|
|
|
anchors {
|
|
left: true
|
|
right: true
|
|
top: true
|
|
bottom: true
|
|
}
|
|
|
|
mask: Region {}
|
|
|
|
WlrLayershell.namespace: "dms:desktop-widget-grid"
|
|
WlrLayershell.layer: root.overviewActive && (root.showOnOverview || root.showOnOverviewOnly) ? WlrLayer.Overlay : WlrLayer.Background
|
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
|
|
|
DesktopWidgetGridLines {
|
|
anchors.fill: parent
|
|
gridSize: geometry.gridSize
|
|
extentWidth: geometry.screenWidth
|
|
extentHeight: geometry.screenHeight
|
|
}
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
active: root.isInteracting
|
|
|
|
sourceComponent: PanelWindow {
|
|
id: helperWindow
|
|
screen: root.screen
|
|
color: "transparent"
|
|
|
|
WlrLayershell.namespace: "dms:desktop-widget-helper"
|
|
WlrLayershell.layer: WlrLayer.Overlay
|
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
|
|
|
anchors {
|
|
bottom: true
|
|
left: true
|
|
right: true
|
|
}
|
|
|
|
implicitHeight: 60
|
|
|
|
DesktopWidgetGridHint {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: Theme.spacingL
|
|
gridEnabled: geometry.gridEnabled
|
|
gridSize: geometry.gridSize
|
|
}
|
|
}
|
|
}
|
|
}
|