aboutsummaryrefslogtreecommitdiffstats
path: root/config/quickshell/overview-trixie/modules/overview/OverviewWindow.qml
diff options
context:
space:
mode:
authorDon Williams <Don.e.williams@gmail.com>2026-07-13 14:55:49 -0400
committerPinapelz <yukais@pinapelz.com>2026-08-29 21:40:34 -0700
commit54b56290253719aa71d8ccfddff6fa0424e22804 (patch)
tree22f99020e4199597c80c70c57adf9ba4e95d7bf0 /config/quickshell/overview-trixie/modules/overview/OverviewWindow.qml
parent96abeed3ec3913e93d8eff60ab50daee0d27fd47 (diff)
Moved quickshell-backtup to -trixie
Signed-off-by: Don Williams <Don.e.williams@gmail.com>
Diffstat (limited to 'config/quickshell/overview-trixie/modules/overview/OverviewWindow.qml')
-rw-r--r--config/quickshell/overview-trixie/modules/overview/OverviewWindow.qml264
1 files changed, 264 insertions, 0 deletions
diff --git a/config/quickshell/overview-trixie/modules/overview/OverviewWindow.qml b/config/quickshell/overview-trixie/modules/overview/OverviewWindow.qml
new file mode 100644
index 00000000..3b2652a9
--- /dev/null
+++ b/config/quickshell/overview-trixie/modules/overview/OverviewWindow.qml
@@ -0,0 +1,264 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Effects
+import Quickshell
+import Quickshell.Wayland
+import "../../common"
+import "../../common/functions"
+import "../../services"
+
+Item { // Window
+ id: root
+ property var toplevel
+ property var windowData
+ property var monitorData
+ property var widgetMonitorData
+ property var scale
+ property var availableWorkspaceWidth
+ property var availableWorkspaceHeight
+ property real positionBaseX: (monitorData?.x ?? 0) + (monitorData?.reserved?.[0] ?? 0)
+ property real positionBaseY: (monitorData?.y ?? 0) + (monitorData?.reserved?.[1] ?? 0)
+ property int recaptureToken: 0
+ property bool restrictToWorkspace: true
+ property real widthRatio: {
+ if (!widgetMonitorData || !monitorData)
+ return 1;
+
+ const widgetWidth = (widgetMonitorData.transform % 2 === 1) ? (widgetMonitorData.height ?? 1) : (widgetMonitorData.width ?? 1);
+ const sourceWidth = (monitorData.transform % 2 === 1) ? (monitorData.height ?? 1) : (monitorData.width ?? 1);
+ const sourceScale = monitorData.scale ?? 1;
+ const widgetScale = widgetMonitorData.scale ?? 1;
+ return (widgetWidth * sourceScale) / (sourceWidth * widgetScale);
+ }
+ property real heightRatio: {
+ if (!widgetMonitorData || !monitorData)
+ return 1;
+
+ const widgetHeight = (widgetMonitorData.transform % 2 === 1) ? (widgetMonitorData.width ?? 1) : (widgetMonitorData.height ?? 1);
+ const sourceHeight = (monitorData.transform % 2 === 1) ? (monitorData.width ?? 1) : (monitorData.height ?? 1);
+ const sourceScale = monitorData.scale ?? 1;
+ const widgetScale = widgetMonitorData.scale ?? 1;
+ return (widgetHeight * sourceScale) / (sourceHeight * widgetScale);
+ }
+ property real initX: Math.max(((windowData?.at[0] ?? 0) - positionBaseX) * root.scale * geometryScaleX, 0) + xOffset
+ property real initY: Math.max(((windowData?.at[1] ?? 0) - positionBaseY) * root.scale * geometryScaleY, 0) + yOffset
+ property real xOffset: 0
+ property real yOffset: 0
+ property int widgetMonitorId: 0
+ property real geometryScaleX: widthRatio
+ property real geometryScaleY: heightRatio
+
+ property var targetWindowWidth: (windowData?.size[0] ?? 100) * scale * geometryScaleX
+ property var targetWindowHeight: (windowData?.size[1] ?? 100) * scale * geometryScaleY
+ property bool hovered: false
+ property bool pressed: false
+
+ property bool showIcons: Config.options.windowPreview.showIcons
+ property var iconToWindowRatio: Config.options.windowPreview.iconToWindowRatio
+ property var xwaylandIndicatorToIconRatio: Config.options.windowPreview.xwaylandIndicatorToIconRatio
+ property var iconToWindowRatioCompact: Config.options.windowPreview.iconToWindowRatioCompact
+ property bool cropToFill: Config.options.windowPreview.cropToFill
+ property bool previewsEnabled: Config.options.overview.previewsEnabled
+ property bool includeInactiveMonitorPreviews: Config.options.overview.includeInactiveMonitorPreviews
+ property int previewRecaptureDelayMs: Config.options.overview.previewRecaptureDelayMs
+ property real windowOverlayOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.windowOverlayOpacity))
+ property bool glassMode: Config.options.overview.effects.glassMode
+ property real glassShineOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.glassShineOpacity))
+ property real effectiveWindowOverlayOpacity: glassMode ? Math.min(windowOverlayOpacity, 0.10) : windowOverlayOpacity
+ property string previewModeRaw: Config.options.overview.previewMode
+ property string previewMode: {
+ const mode = `${previewModeRaw ?? "live"}`.trim().toLowerCase();
+ return (mode === "event" || mode === "snapshot") ? "event" : "live";
+ }
+ property bool livePreviewEnabled: previewsEnabled && previewMode === "live"
+ property bool shouldCapturePreview: {
+ if (!GlobalStates.overviewOpen || !previewsEnabled || !previewCaptureEnabled)
+ return false;
+ if (includeInactiveMonitorPreviews)
+ return true;
+ return (windowData?.monitor ?? -1) === widgetMonitorId;
+ }
+ property var entry: {
+ DesktopEntries.applications.values; // re-run when the entry index updates
+ return DesktopEntries.heuristicLookup(windowData?.class);
+ }
+ property string iconName: {
+ const raw = `${entry?.icon ?? ""}`.trim();
+ const withoutProviderPrefix = raw.replace(/^image:\/\/icon\//, "");
+ const withoutQuery = withoutProviderPrefix.split("?")[0].trim();
+ return withoutQuery.length > 0 ? withoutQuery : "application-x-executable";
+ }
+ property var iconPath: Quickshell.iconPath(iconName, "image-missing")
+ property bool compactMode: Appearance.font.pixelSize.smaller * 4 > targetWindowHeight || Appearance.font.pixelSize.smaller * 4 > targetWindowWidth
+
+ property bool indicateXWayland: windowData?.xwayland ?? false
+ property bool previewCaptureEnabled: true
+ property bool initialized: false
+ property bool dragInProgress: false
+ property bool suspendPositionAnimation: false
+ property bool animateSize: true
+
+ x: initX
+ y: initY
+ width: Math.min(targetWindowWidth, availableWorkspaceWidth)
+ height: Math.min(targetWindowHeight, availableWorkspaceHeight)
+ opacity: (windowData?.monitor ?? -1) == widgetMonitorId ? 1 : Config.options.windowPreview.inactiveMonitorOpacity
+ visible: {
+ const thisWsId = windowData?.workspace?.id;
+ const isFullscreen = (windowData?.fullscreen ?? 0) > 0;
+ if (isFullscreen || thisWsId === undefined) return true;
+ return !HyprlandData.windowList.some(w => w.workspace?.id === thisWsId && (w.fullscreen ?? 0) > 0);
+ }
+
+ clip: true
+ Component.onCompleted: Qt.callLater(() => root.initialized = true)
+
+ Behavior on x {
+ enabled: root.initialized && !root.dragInProgress && !root.suspendPositionAnimation
+ animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
+ }
+ Behavior on y {
+ enabled: root.initialized && !root.dragInProgress && !root.suspendPositionAnimation
+ animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
+ }
+ Behavior on width {
+ enabled: root.initialized && root.animateSize && !root.dragInProgress && !root.suspendPositionAnimation
+ animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
+ }
+ Behavior on height {
+ enabled: root.initialized && root.animateSize && !root.dragInProgress && !root.suspendPositionAnimation
+ animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
+ }
+
+ // Opaque background for windows on the active monitor.
+ // The simplest solution for making those windows fully opaque and not interacting with actual
+ // windows behind the overview, e.g., applying blur to them.
+ Rectangle {
+ visible: (root.windowData?.monitor ?? -1) === root.widgetMonitorId
+ anchors.fill: parent
+ radius: Appearance.rounding.windowRounding * root.scale
+ color: root.glassMode
+ ? ColorUtils.mix(Appearance.colors.colLayer2, Appearance.colors.colLayer0, 0.38)
+ : Appearance.colors.colLayer2
+ }
+
+ ScreencopyView {
+ id: windowPreview
+ readonly property real srcAspect: {
+ const w = root.windowData?.size?.[0] ?? 0;
+ const h = root.windowData?.size?.[1] ?? 0;
+ return (w > 0 && h > 0) ? (w / h) : 1;
+ }
+ anchors.centerIn: parent
+ width: root.cropToFill
+ ? Math.max(parent.width, parent.height * srcAspect)
+ : Math.min(parent.width, parent.height * srcAspect)
+ height: root.cropToFill
+ ? Math.max(parent.height, parent.width / srcAspect)
+ : Math.min(parent.height, parent.width / srcAspect)
+ captureSource: shouldCapturePreview ? root.toplevel : null
+ live: livePreviewEnabled
+ layer.enabled: true
+ layer.smooth: true
+ layer.effect: MultiEffect {
+ maskEnabled: true
+ maskSource: previewMask
+ maskThresholdMin: 0.5
+ maskSpreadAtMin: 1.0
+ }
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ radius: Appearance.rounding.windowRounding * root.scale
+ color: pressed ? ColorUtils.applyAlpha(Appearance.colors.colLayer2Active, Math.min(1, root.effectiveWindowOverlayOpacity + 0.30)) :
+ hovered ? ColorUtils.applyAlpha(Appearance.colors.colLayer2Hover, Math.min(1, root.effectiveWindowOverlayOpacity + 0.20)) :
+ ColorUtils.applyAlpha(
+ root.glassMode ? ColorUtils.mix(Appearance.colors.colLayer2, Appearance.colors.colLayer0, 0.38) : Appearance.colors.colLayer2,
+ root.effectiveWindowOverlayOpacity
+ )
+ border.color: root.glassMode
+ ? ColorUtils.applyAlpha(Appearance.m3colors.m3outline, 0.62)
+ : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.7)
+ border.width: 1
+
+ Rectangle {
+ visible: root.glassMode
+ anchors.fill: parent
+ radius: parent.radius
+ color: "transparent"
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: ColorUtils.applyAlpha("#FFFFFF", root.glassShineOpacity * 0.24) }
+ GradientStop { position: 0.5; color: ColorUtils.applyAlpha("#FFFFFF", 0.0) }
+ GradientStop { position: 1.0; color: ColorUtils.applyAlpha("#000000", root.glassShineOpacity * 0.14) }
+ }
+ }
+
+ Rectangle {
+ visible: root.glassMode
+ anchors.fill: parent
+ anchors.margins: 1
+ radius: Math.max(parent.radius - 1, 0)
+ color: "transparent"
+ border.width: 1
+ border.color: ColorUtils.applyAlpha("#FFFFFF", root.glassShineOpacity * 0.32)
+ }
+
+ ColumnLayout {
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: Appearance.font.pixelSize.smaller * 0.5
+
+ Image {
+ id: windowIcon
+ visible: root.showIcons
+ property var iconSize: {
+ const renderedSize = Math.min(root.width, root.height);
+ return renderedSize * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / (root.monitorData?.scale ?? 1);
+ }
+ Layout.alignment: Qt.AlignHCenter
+ source: root.iconPath
+ width: iconSize
+ height: iconSize
+ sourceSize: Qt.size(Math.max(1, Math.round(iconSize)), Math.max(1, Math.round(iconSize)))
+ }
+ }
+ }
+
+ Item {
+ id: previewMask
+ width: windowPreview.width
+ height: windowPreview.height
+ anchors.centerIn: parent
+ visible: false
+ layer.enabled: true
+ layer.smooth: true
+ Rectangle {
+ anchors.centerIn: parent
+ width: root.width
+ height: root.height
+ radius: Appearance.rounding.windowRounding * root.scale
+ }
+ }
+
+ function refreshCapture() {
+ if (!GlobalStates.overviewOpen || livePreviewEnabled || !previewsEnabled)
+ return;
+
+ root.previewCaptureEnabled = false;
+ previewResetTimer.restart();
+ }
+
+ Timer {
+ id: previewResetTimer
+ interval: Math.max(1, previewRecaptureDelayMs)
+ repeat: false
+ onTriggered: root.previewCaptureEnabled = true
+ }
+
+ onRecaptureTokenChanged: {
+ if (recaptureToken > 0)
+ root.refreshCapture();
+ }
+}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage