aboutsummaryrefslogtreecommitdiffstats
path: root/config/quickshell/overview-backup/modules/overview
diff options
context:
space:
mode:
authorDon Williams <Don.e.williams@gmail.com>2026-07-11 20:50:52 -0400
committerPinapelz <yukais@pinapelz.com>2026-08-29 21:40:34 -0700
commit9dab91abe13fc6cf1c8e609f71131609ae9c64bb (patch)
treed848f7b3c180aba0ae1576d1f7ede0f951e63f11 /config/quickshell/overview-backup/modules/overview
parentcfdf5b2c2c24d312b2f2ff3da8eb73f9dced8dcf (diff)
Using previous version of overview config seems to work better
Signed-off-by: Don Williams <Don.e.williams@gmail.com> On branch development Your branch is up to date with 'origin/development'. Changes to be committed: new file: overview-backup/README.md renamed: overview/assets/Workspace_Wallpaper.png -> overview-backup/assets/Workspace_Wallpaper.png new file: overview-backup/assets/image.png new file: overview-backup/common/Appearance.qml new file: overview-backup/common/Config.qml new file: overview-backup/common/functions/ColorUtils.qml new file: overview-backup/common/functions/qmldir new file: overview-backup/common/qmldir renamed: overview/common/widgets/RectangularShadow.qml -> overview-backup/common/widgets/RectangularShadow.qml new file: overview-backup/common/widgets/StyledRectangularShadow.qml new file: overview-backup/common/widgets/StyledText.qml new file: overview-backup/common/widgets/StyledToolTip.qml new file: overview-backup/common/widgets/StyledToolTipContent.qml new file: overview-backup/common/widgets/qmldir renamed: overview/config.example.json -> overview-backup/config.example.json renamed: overview/config.json -> overview-backup/config.json new file: overview-backup/modules/overview/Overview.qml new file: overview-backup/modules/overview/OverviewWidget.qml new file: overview-backup/modules/overview/OverviewWindow.qml new file: overview-backup/modules/overview/qmldir renamed: overview/quickshell-overview.qml -> overview-backup/quickshell-overview.qml new file: overview-backup/services/GlobalStates.qml new file: overview-backup/services/HyprlandData.qml new file: overview-backup/services/qmldir new file: overview-backup/shell.qml modified: overview/README.md modified: overview/assets/image.png modified: overview/common/Appearance.qml modified: overview/common/Config.qml modified: overview/common/widgets/StyledRectangularShadow.qml modified: overview/common/widgets/qmldir modified: overview/modules/overview/Overview.qml modified: overview/modules/overview/OverviewWidget.qml modified: overview/modules/overview/OverviewWindow.qml modified: overview/services/HyprlandData.qml modified: overview/shell.qml
Diffstat (limited to 'config/quickshell/overview-backup/modules/overview')
-rw-r--r--config/quickshell/overview-backup/modules/overview/Overview.qml252
-rw-r--r--config/quickshell/overview-backup/modules/overview/OverviewWidget.qml1214
-rw-r--r--config/quickshell/overview-backup/modules/overview/OverviewWindow.qml264
-rw-r--r--config/quickshell/overview-backup/modules/overview/qmldir3
4 files changed, 1733 insertions, 0 deletions
diff --git a/config/quickshell/overview-backup/modules/overview/Overview.qml b/config/quickshell/overview-backup/modules/overview/Overview.qml
new file mode 100644
index 00000000..2fb08bb9
--- /dev/null
+++ b/config/quickshell/overview-backup/modules/overview/Overview.qml
@@ -0,0 +1,252 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Io
+import Quickshell.Wayland
+import Quickshell.Hyprland
+import "../../common"
+import "../../services"
+import "."
+
+Scope {
+ id: overviewScope
+ Variants {
+ id: overviewVariants
+ model: Quickshell.screens
+ PanelWindow {
+ id: root
+ required property var modelData
+ readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
+ property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id)
+ property bool blurEnabled: Config.options.overview.effects.enableBlur
+ property bool backdropEnabled: Config.options.overview.effects.enableBackdrop
+ property real backdropOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.backdropOpacity))
+ property bool closeOnFocusLoss: Config.options.overview.closeOnFocusLoss ?? true
+ screen: modelData
+ visible: GlobalStates.overviewOpen
+
+ WlrLayershell.namespace: blurEnabled ? "quickshell:overview-blur" : "quickshell:overview"
+ WlrLayershell.layer: WlrLayer.Overlay
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
+ color: "transparent"
+
+ anchors {
+ top: true
+ bottom: true
+ left: true
+ right: true
+ }
+
+ HyprlandFocusGrab {
+ id: grab
+ windows: [root]
+ property bool canBeActive: root.monitorIsFocused
+ active: false
+ onCleared: () => {
+ // Only the monitor that owns the grab may close the overview
+ if (root.closeOnFocusLoss && !active && canBeActive)
+ GlobalStates.overviewOpen = false;
+ }
+ }
+
+ Connections {
+ target: GlobalStates
+ function onOverviewOpenChanged() {
+ if (GlobalStates.overviewOpen) {
+ delayedGrabTimer.start();
+ }
+ }
+ }
+
+ // Re-evaluate grab ownership when focused monitor changes
+ Connections {
+ target: Hyprland
+ function onFocusedMonitorChanged() {
+ if (!GlobalStates.overviewOpen)
+ return;
+ // Transfer the grab to the newly focused monitor
+ if (root.monitorIsFocused && !grab.active) {
+ grab.active = true;
+ } else if (!root.monitorIsFocused && grab.active) {
+ grab.active = false;
+ }
+ }
+ }
+
+ Timer {
+ id: delayedGrabTimer
+ interval: Config.options.hacks.arbitraryRaceConditionDelay
+ repeat: false
+ onTriggered: {
+ if (!grab.canBeActive)
+ return;
+ grab.active = GlobalStates.overviewOpen;
+ }
+ }
+
+ // Keep the layershell surface full-screen so backdrop/blur are not constrained by content size.
+ implicitWidth: screen.width
+ implicitHeight: screen.height
+
+ Item {
+ id: keyHandler
+ anchors.fill: parent
+ visible: GlobalStates.overviewOpen
+ focus: GlobalStates.overviewOpen
+ z: 0
+
+ Rectangle {
+ id: backdropLayer
+ anchors.fill: parent
+ visible: root.backdropEnabled
+ color: "#000000"
+ opacity: root.backdropOpacity
+ z: 0
+ }
+
+ MouseArea {
+ id: outsideClickCatcher
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
+ enabled: root.closeOnFocusLoss && GlobalStates.overviewOpen
+ z: 0
+ onPressed: mouse => {
+ GlobalStates.overviewOpen = false;
+ mouse.accepted = true;
+ }
+ }
+
+ Keys.onPressed: event => {
+ // close: Escape or Enter
+ if (event.key === Qt.Key_Escape || event.key === Qt.Key_Return) {
+ GlobalStates.overviewOpen = false;
+ event.accepted = true;
+ return;
+ }
+
+ // Helper: compute current group bounds
+ const workspacesPerGroup = Config.options.overview.rows * Config.options.overview.columns;
+ const currentId = Hyprland.focusedMonitor?.activeWorkspace?.id ?? 1;
+ const useWorkspaceMap = Config.options.overview.useWorkspaceMap;
+ const workspaceMap = Config.options.overview.workspaceMap ?? [];
+ const focusedMonitorId = Hyprland.focusedMonitor?.id ?? root.monitor?.id ?? 0;
+ const workspaceOffset = useWorkspaceMap ? Number(workspaceMap[focusedMonitorId] ?? 0) : 0;
+ const currentGroup = Math.floor((currentId - workspaceOffset - 1) / workspacesPerGroup);
+ const minWorkspaceId = currentGroup * workspacesPerGroup + 1 + workspaceOffset;
+ const maxWorkspaceId = minWorkspaceId + workspacesPerGroup - 1;
+
+ const rows = Config.options.overview.rows;
+ const columns = Config.options.overview.columns;
+ const reverseColumns = Config.options.overview.orderRightLeft;
+ const reverseRows = Config.options.overview.orderBottomUp;
+
+ const clampedIndex = Math.max(0, Math.min(workspacesPerGroup - 1, currentId - minWorkspaceId));
+ const currentNormalRow = Math.floor(clampedIndex / columns);
+ const currentNormalColumn = clampedIndex % columns;
+
+ function toVisualRow(normalRow) {
+ return reverseRows ? (rows - normalRow - 1) : normalRow;
+ }
+
+ function toVisualColumn(normalColumn) {
+ return reverseColumns ? (columns - normalColumn - 1) : normalColumn;
+ }
+
+ function toNormalRow(visualRow) {
+ return reverseRows ? (rows - visualRow - 1) : visualRow;
+ }
+
+ function toNormalColumn(visualColumn) {
+ return reverseColumns ? (columns - visualColumn - 1) : visualColumn;
+ }
+
+ let targetVisualRow = toVisualRow(currentNormalRow);
+ let targetVisualColumn = toVisualColumn(currentNormalColumn);
+
+ let targetId = null;
+
+ // Arrow keys and vim-style hjkl
+ if (event.key === Qt.Key_Left || event.key === Qt.Key_H) {
+ targetVisualColumn = (targetVisualColumn - 1 + columns) % columns;
+ } else if (event.key === Qt.Key_Right || event.key === Qt.Key_L) {
+ targetVisualColumn = (targetVisualColumn + 1) % columns;
+ } else if (event.key === Qt.Key_Up || event.key === Qt.Key_K) {
+ targetVisualRow = (targetVisualRow - 1 + rows) % rows;
+ } else if (event.key === Qt.Key_Down || event.key === Qt.Key_J) {
+ targetVisualRow = (targetVisualRow + 1) % rows;
+ }
+
+ // Number keys: jump to workspace within the current group
+ // 1-9 map to positions 1-9, 0 maps to position 10
+ else if (event.key >= Qt.Key_1 && event.key <= Qt.Key_9) {
+ const position = event.key - Qt.Key_0; // 1-9
+ if (position <= workspacesPerGroup) {
+ targetId = minWorkspaceId + position - 1;
+ }
+ } else if (event.key === Qt.Key_0) {
+ // 0 = 10th workspace in the group (if group has 10+ workspaces)
+ if (workspacesPerGroup >= 10) {
+ targetId = minWorkspaceId + 9; // 10th position = offset 9
+ }
+ }
+
+ if (targetId === null && (
+ event.key === Qt.Key_Left || event.key === Qt.Key_H ||
+ event.key === Qt.Key_Right || event.key === Qt.Key_L ||
+ event.key === Qt.Key_Up || event.key === Qt.Key_K ||
+ event.key === Qt.Key_Down || event.key === Qt.Key_J
+ )) {
+ const targetNormalRow = toNormalRow(targetVisualRow);
+ const targetNormalColumn = toNormalColumn(targetVisualColumn);
+ targetId = minWorkspaceId + targetNormalRow * columns + targetNormalColumn;
+ }
+
+ if (targetId !== null) {
+ const clampedTarget = Math.max(minWorkspaceId, Math.min(maxWorkspaceId, targetId));
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.focus({workspace = '${clampedTarget}'})`);
+ } else {
+ Hyprland.dispatch("workspace " + clampedTarget);
+ }
+ event.accepted = true;
+ }
+ }
+ }
+
+ ColumnLayout {
+ id: columnLayout
+ visible: GlobalStates.overviewOpen
+ z: 1
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: parent.top
+ topMargin: Config.options.position.topMargin
+ }
+
+ Loader {
+ id: overviewLoader
+ active: Config?.options.overview.enable ?? true
+ sourceComponent: OverviewWidget {
+ panelWindow: root
+ visible: true
+ }
+ }
+ }
+ }
+ }
+
+ IpcHandler {
+ target: "overview"
+
+ function toggle() {
+ GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
+ }
+ function close() {
+ GlobalStates.overviewOpen = false;
+ }
+ function open() {
+ GlobalStates.overviewOpen = true;
+ }
+ }
+}
diff --git a/config/quickshell/overview-backup/modules/overview/OverviewWidget.qml b/config/quickshell/overview-backup/modules/overview/OverviewWidget.qml
new file mode 100644
index 00000000..6d00803b
--- /dev/null
+++ b/config/quickshell/overview-backup/modules/overview/OverviewWidget.qml
@@ -0,0 +1,1214 @@
+import QtQuick
+import QtQuick.Effects
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Wayland
+import Quickshell.Hyprland
+import "../../common"
+import "../../common/functions"
+import "../../common/widgets"
+import "../../services"
+import "."
+
+Item {
+ id: root
+ required property var panelWindow
+ readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen)
+ readonly property var toplevels: ToplevelManager.toplevels
+ readonly property int effectiveActiveWorkspaceId: Math.max(1, Math.min(100, monitor?.activeWorkspace?.id ?? 1))
+ readonly property int workspacesShown: Config.options.overview.rows * Config.options.overview.columns
+ readonly property bool useWorkspaceMap: Config.options.overview.useWorkspaceMap
+ readonly property var workspaceMap: Config.options.overview.workspaceMap
+ readonly property int workspaceOffset: useWorkspaceMap ? Number(workspaceMap[root.monitor?.id] ?? 0) : 0
+ readonly property int workspaceGroup: Math.floor((effectiveActiveWorkspaceId - workspaceOffset - 1) / workspacesShown)
+ property bool monitorIsFocused: (Hyprland.focusedMonitor?.name == monitor.name)
+ property var windows: HyprlandData.windowList
+ property var windowByAddress: HyprlandData.windowByAddress
+ property var windowAddresses: HyprlandData.addresses
+ property var workspaceIds: HyprlandData.workspaceIds
+ property var monitorData: HyprlandData.monitors.find(m => m.id === root.monitor?.id)
+ property real scale: Config.options.overview.scale
+ property color activeBorderColor: Appearance.colors.colSecondary
+
+ property real workspaceImplicitWidth: Math.round((monitorData?.transform % 2 === 1) ?
+ ((monitor.height / monitor.scale - (monitorData?.reserved?.[0] ?? 0) - (monitorData?.reserved?.[2] ?? 0)) * root.scale) :
+ ((monitor.width / monitor.scale - (monitorData?.reserved?.[0] ?? 0) - (monitorData?.reserved?.[2] ?? 0)) * root.scale))
+ property real workspaceImplicitHeight: Math.round((monitorData?.transform % 2 === 1) ?
+ ((monitor.width / monitor.scale - (monitorData?.reserved?.[1] ?? 0) - (monitorData?.reserved?.[3] ?? 0)) * root.scale) :
+ ((monitor.height / monitor.scale - (monitorData?.reserved?.[1] ?? 0) - (monitorData?.reserved?.[3] ?? 0)) * root.scale))
+
+ property real workspaceNumberMargin: 80
+ property real workspaceNumberSize: Config.options.overview.workspaceNumberBaseSize * monitor.scale
+ property int workspaceZ: 0
+ property int windowZ: 1
+ property int windowDraggingZ: 99999
+ property real workspaceSpacing: Config.options.overview.workspaceSpacing
+ property string emptyWorkspaceWallpaperPath: Config.options.overview.emptyWorkspaceWallpaper
+ property string specialEmptyWorkspaceWallpaperPath: Config.options.overview.specialEmptyWorkspaceWallpaper
+ property bool showSpecialWorkspaces: Config.options.overview.showSpecialWorkspaces
+ property var configuredSpecialWorkspaces: Config.options.overview.specialWorkspaces ?? []
+ property int specialWorkspaceColumns: Math.max(1, Config.options.overview.specialWorkspaceColumns)
+ property real panelOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.panelOpacity))
+ property real workspaceOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.workspaceOpacity))
+ property real emptyWorkspaceWallpaperOverlayOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.emptyWorkspaceWallpaperOverlayOpacity))
+ property bool glassMode: Config.options.overview.effects.glassMode
+ property real glassTintStrength: Math.max(0, Math.min(1, Config.options.overview.effects.glassTintStrength))
+ property real glassBorderOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.glassBorderOpacity))
+ property real glassShineOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.glassShineOpacity))
+ property real effectivePanelOpacity: glassMode ? Math.min(panelOpacity, 0.72) : panelOpacity
+ property real effectiveWorkspaceOpacity: glassMode ? Math.min(workspaceOpacity, 0.62) : workspaceOpacity
+
+ property int draggingFromWorkspace: -1
+ property int draggingTargetWorkspace: -1
+ property string draggingTargetSpecialWorkspace: ""
+ property int previewRecaptureToken: 0
+ property var allWorkspaces: HyprlandData.allWorkspaces
+ property bool previewsEnabled: Config.options.overview.previewsEnabled
+ 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 useEventPreviewRefresh: previewsEnabled && previewMode === "event"
+
+ readonly property var monitorSpecialWorkspaceNames: {
+ const names = [];
+ for (const ws of (allWorkspaces ?? [])) {
+ const name = `${ws?.name ?? ""}`;
+ if (!name.startsWith("special:"))
+ continue;
+ if (`${ws?.monitor ?? ""}` !== `${root.monitor?.name ?? ""}`)
+ continue;
+ names.push(name.slice(8));
+ }
+ return names;
+ }
+
+ readonly property var specialWorkspaceNamesFromWindows: {
+ const names = [];
+ for (const addr in windowByAddress) {
+ const win = windowByAddress[addr];
+ if ((win?.monitor ?? -1) !== (root.monitor?.id ?? -1))
+ continue;
+ const wsName = `${win?.workspace?.name ?? ""}`;
+ if (!wsName.startsWith("special:"))
+ continue;
+ names.push(wsName.slice(8));
+ }
+ return names;
+ }
+
+ readonly property var visibleSpecialWorkspaces: {
+ if (!showSpecialWorkspaces)
+ return [];
+
+ const out = [];
+ const pushUnique = (value) => {
+ const cleaned = `${value ?? ""}`.trim();
+ if (cleaned.length === 0 || out.includes(cleaned))
+ return;
+ out.push(cleaned);
+ };
+
+ for (const configured of configuredSpecialWorkspaces ?? [])
+ pushUnique(configured);
+ for (const name of monitorSpecialWorkspaceNames)
+ pushUnique(name);
+ for (const name of specialWorkspaceNamesFromWindows)
+ pushUnique(name);
+
+ return out;
+ }
+
+ readonly property bool hasSpecialWorkspaceSection: visibleSpecialWorkspaces.length > 0
+ readonly property bool hasEmptyWorkspaceWallpaper: `${emptyWorkspaceWallpaperPath ?? ""}`.trim().length > 0
+ readonly property bool hasSpecialEmptyWorkspaceWallpaper: `${specialEmptyWorkspaceWallpaperPath ?? ""}`.trim().length > 0
+ readonly property string createSpecialWorkspaceTarget: "__create_special_workspace__"
+ readonly property real specialWorkspaceTileHeight: root.workspaceImplicitHeight
+ readonly property real specialStripGap: workspaceSpacing * 1.8
+ readonly property real specialStripPadding: Math.max(8, 12 * root.scale)
+ readonly property real specialStripTitleHeight: Math.max(14, Appearance.font.pixelSize.small * root.scale)
+ readonly property real specialStripTitleGap: Math.max(6, 8 * root.scale)
+ readonly property int totalSpecialTiles: visibleSpecialWorkspaces.length + 1
+ readonly property real specialSectionWidth: workspaceColumnLayout.implicitWidth
+ readonly property real specialGridInnerWidth: Math.max(0, root.specialSectionWidth - root.specialStripPadding * 2)
+ readonly property int effectiveSpecialColumns: Math.max(1, Math.min(root.specialWorkspaceColumns, root.totalSpecialTiles))
+ readonly property int specialWorkspaceRows: Math.ceil(root.totalSpecialTiles / root.effectiveSpecialColumns)
+ readonly property real specialWorkspaceAspectCap: {
+ let maxAspect = Math.max(1, root.workspaceImplicitWidth / Math.max(1, root.workspaceImplicitHeight));
+ for (const name of visibleSpecialWorkspaces) {
+ const geometry = root.specialWorkspaceGeometry(name, root.monitor?.id);
+ const width = geometry?.width;
+ const height = geometry?.height;
+ if (!Number.isFinite(width) || !Number.isFinite(height) || height <= 0)
+ continue;
+ maxAspect = Math.max(maxAspect, width / height);
+ }
+ return maxAspect;
+ }
+ readonly property real specialWorkspaceTileWidth: {
+ const gaps = Math.max(0, root.effectiveSpecialColumns - 1);
+ const rawWidth = (root.specialGridInnerWidth - gaps * workspaceSpacing) / root.effectiveSpecialColumns;
+ const aspectWidth = root.specialWorkspaceTileHeight * root.specialWorkspaceAspectCap;
+ const cappedWidth = Math.min(rawWidth, aspectWidth);
+ return Math.max(80 * root.scale, cappedWidth);
+ }
+ readonly property real specialGridUsedWidth: root.effectiveSpecialColumns * root.specialWorkspaceTileWidth + Math.max(0, root.effectiveSpecialColumns - 1) * workspaceSpacing
+ readonly property real specialGridOffsetX: root.specialStripPadding + Math.max(0, (root.specialGridInnerWidth - root.specialGridUsedWidth) / 2)
+ readonly property real specialStripTop: workspaceColumnLayout.implicitHeight + workspaceSpacing + root.specialStripGap
+ readonly property real specialStripTilesTop: root.specialStripTop + root.specialStripPadding + root.specialStripTitleHeight + root.specialStripTitleGap
+ readonly property real specialGridHeight: root.specialWorkspaceRows * root.specialWorkspaceTileHeight + Math.max(0, root.specialWorkspaceRows - 1) * workspaceSpacing
+ readonly property real specialStripHeight: root.specialStripPadding * 2 + root.specialStripTitleHeight + root.specialStripTitleGap + root.specialGridHeight
+
+ function getWorkspaceRow(workspaceId) {
+ if (!Number.isFinite(workspaceId))
+ return 0;
+ const adjusted = workspaceId - workspaceOffset;
+ const normalRow = Math.floor((adjusted - 1) / Config.options.overview.columns) % Config.options.overview.rows;
+ return Config.options.overview.orderBottomUp ? (Config.options.overview.rows - normalRow - 1) : normalRow;
+ }
+
+ function getWorkspaceColumn(workspaceId) {
+ if (!Number.isFinite(workspaceId))
+ return 0;
+ const adjusted = workspaceId - workspaceOffset;
+ const normalCol = (adjusted - 1) % Config.options.overview.columns;
+ return Config.options.overview.orderRightLeft ? (Config.options.overview.columns - normalCol - 1) : normalCol;
+ }
+
+ function getWorkspaceInCell(rowIndex, colIndex) {
+ const mappedRow = Config.options.overview.orderBottomUp ? (Config.options.overview.rows - rowIndex - 1) : rowIndex;
+ const mappedCol = Config.options.overview.orderRightLeft ? (Config.options.overview.columns - colIndex - 1) : colIndex;
+ return (workspaceGroup * workspacesShown) + (mappedRow * Config.options.overview.columns) + mappedCol + 1 + workspaceOffset;
+ }
+
+ function getVisibleRowPosition(rowIndex) {
+ if (!Number.isFinite(rowIndex) || rowIndex < 0)
+ return 0;
+ if (!Config.options.overview.hideEmptyRows || !(rowsWithContent instanceof Set))
+ return rowIndex;
+
+ let visibleRow = 0;
+ for (let i = 0; i < rowIndex; i += 1) {
+ if (rowsWithContent.has(i))
+ visibleRow += 1;
+ }
+ return visibleRow;
+ }
+
+ function stepWorkspace(delta) {
+ if (!Number.isFinite(delta) || delta === 0)
+ return;
+
+ const currentId = monitor?.activeWorkspace?.id ?? effectiveActiveWorkspaceId;
+ const minWorkspaceId = workspaceOffset + 1;
+ let maxWorkspaceId = minWorkspaceId + workspacesShown - 1;
+ for (const workspaceId of (workspaceIds ?? [])) {
+ if (Number.isFinite(workspaceId) && workspaceId >= minWorkspaceId) {
+ maxWorkspaceId = Math.max(maxWorkspaceId, workspaceId);
+ }
+ }
+ maxWorkspaceId = Math.max(maxWorkspaceId, currentId);
+
+ let targetId = currentId + delta;
+ if (targetId < minWorkspaceId) {
+ targetId = maxWorkspaceId;
+ } else if (targetId > maxWorkspaceId) {
+ targetId = minWorkspaceId;
+ }
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.focus({workspace = '${targetId}'})`);
+ } else {
+ Hyprland.dispatch(`workspace ${targetId}`);
+ }
+ }
+
+ function isSpecialWorkspace(windowData) {
+ const wsName = `${windowData?.workspace?.name ?? ""}`;
+ return wsName.startsWith("special:");
+ }
+
+ function specialWorkspaceName(windowData) {
+ const wsName = `${windowData?.workspace?.name ?? ""}`;
+ return wsName.startsWith("special:") ? wsName.slice(8) : "";
+ }
+
+ function specialWorkspaceIndex(name) {
+ return visibleSpecialWorkspaces.indexOf(`${name ?? ""}`);
+ }
+
+ function specialWorkspaceLabel(name) {
+ const raw = `${name ?? ""}`.trim();
+ if (raw.length === 0)
+ return "Special";
+ return raw.replace(/[-_]+/g, " ");
+ }
+
+ function nextSpecialWorkspaceName() {
+ const taken = new Set();
+ for (const name of visibleSpecialWorkspaces)
+ taken.add(`${name ?? ""}`.trim().toLowerCase());
+
+ const base = "stash";
+ if (!taken.has(base))
+ return base;
+
+ let index = 2;
+ while (taken.has(`${base}-${index}`))
+ index += 1;
+
+ return `${base}-${index}`;
+ }
+
+ function wallpaperSource(path) {
+ const trimmed = `${path ?? ""}`.trim();
+ if (trimmed.length === 0)
+ return "";
+ if (trimmed.startsWith("file:/") || trimmed.startsWith("qrc:/") || trimmed.startsWith("image://") || trimmed.startsWith("http://") || trimmed.startsWith("https://"))
+ return trimmed;
+ if (trimmed.startsWith("/"))
+ return `file://${trimmed}`;
+ return trimmed;
+ }
+
+ function workspaceHasWindows(workspaceId) {
+ if (!Number.isFinite(workspaceId))
+ return false;
+
+ for (const addr in windowByAddress) {
+ const win = windowByAddress[addr];
+ if (root.isSpecialWorkspace(win))
+ continue;
+ if ((win?.workspace?.id ?? -1) === workspaceId)
+ return true;
+ }
+ return false;
+ }
+
+ function specialWindowZ(win) {
+ const pinned = win?.pinned ? 200000 : 0;
+ const floating = win?.floating ? 100000 : 0;
+ const focus = 10000 - (win?.focusHistoryID ?? 9999);
+ return pinned + floating + focus;
+ }
+
+ function specialWorkspaceGeometry(name, monitorId) {
+ const trimmedName = `${name ?? ""}`.trim();
+ const currentMonitorId = monitorId ?? -1;
+ let minX = null;
+ let minY = null;
+ let maxX = null;
+ let maxY = null;
+
+ for (const addr in windowByAddress) {
+ const win = windowByAddress[addr];
+ if ((win?.monitor ?? -1) !== currentMonitorId)
+ continue;
+ if (root.specialWorkspaceName(win) !== trimmedName)
+ continue;
+
+ const atX = win?.at?.[0];
+ const atY = win?.at?.[1];
+ const width = win?.size?.[0];
+ const height = win?.size?.[1];
+ if (!Number.isFinite(atX) || !Number.isFinite(atY))
+ continue;
+ if (!Number.isFinite(width) || !Number.isFinite(height))
+ continue;
+
+ minX = minX === null ? atX : Math.min(minX, atX);
+ minY = minY === null ? atY : Math.min(minY, atY);
+ maxX = maxX === null ? (atX + width) : Math.max(maxX, atX + width);
+ maxY = maxY === null ? (atY + height) : Math.max(maxY, atY + height);
+ }
+
+ return {
+ x: minX,
+ y: minY,
+ width: (minX !== null && maxX !== null) ? Math.max(1, maxX - minX) : null,
+ height: (minY !== null && maxY !== null) ? Math.max(1, maxY - minY) : null
+ };
+ }
+
+ // Calculate which rows have windows or current workspace
+ property var rowsWithContent: {
+ if (!Config.options.overview.hideEmptyRows) return null;
+
+ let rows = new Set();
+ const firstWorkspace = root.workspaceGroup * root.workspacesShown + 1 + workspaceOffset;
+ const lastWorkspace = (root.workspaceGroup + 1) * root.workspacesShown + workspaceOffset;
+
+ // Add row containing current workspace
+ const currentWorkspace = effectiveActiveWorkspaceId;
+ if (currentWorkspace >= firstWorkspace && currentWorkspace <= lastWorkspace) {
+ rows.add(getWorkspaceRow(currentWorkspace));
+ }
+
+ // Add rows with windows
+ for (let addr in windowByAddress) {
+ const win = windowByAddress[addr];
+ const wsId = win?.workspace?.id;
+ if (wsId >= firstWorkspace && wsId <= lastWorkspace) {
+ const rowIndex = getWorkspaceRow(wsId);
+ rows.add(rowIndex);
+ }
+ }
+
+ return rows;
+ }
+
+ implicitWidth: overviewBackground.implicitWidth + Appearance.sizes.elevationMargin * 2
+ implicitHeight: overviewBackground.implicitHeight + Appearance.sizes.elevationMargin * 2
+
+ property Component windowComponent: OverviewWindow {}
+ property list<OverviewWindow> windowWidgets: []
+
+ Connections {
+ target: Hyprland
+ function onRawEvent(event) {
+ if (!GlobalStates.overviewOpen || !root.useEventPreviewRefresh)
+ return;
+
+ const eventName = `${event?.name ?? event?.event ?? event?.type ?? ""}`;
+ if (eventName === "closewindow" || eventName === "openwindow" || eventName === "movewindow") {
+ root.previewRecaptureToken += 1;
+ }
+ }
+ }
+
+ StyledRectangularShadow {
+ target: overviewBackground
+ }
+ Rectangle { // Background
+ id: overviewBackground
+ property real padding: Config.options.overview.backgroundPadding
+ anchors.fill: parent
+ anchors.margins: Appearance.sizes.elevationMargin
+
+ implicitWidth: contentLayout.implicitWidth + padding * 2
+ implicitHeight: contentLayout.implicitHeight + padding * 2
+ radius: Appearance.rounding.screenRounding * root.scale + padding
+ clip: true
+ color: ColorUtils.applyAlpha(
+ root.glassMode
+ ? ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colLayer1, 0.78 - root.glassTintStrength * 0.35)
+ : Appearance.colors.colLayer0,
+ root.effectivePanelOpacity
+ )
+ border.width: 1
+ border.color: ColorUtils.applyAlpha(
+ root.glassMode
+ ? ColorUtils.mix(Appearance.colors.colLayer0Border, Appearance.m3colors.m3outline, 0.52)
+ : Appearance.colors.colLayer0Border,
+ root.glassMode ? root.glassBorderOpacity : root.effectivePanelOpacity
+ )
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
+ onPressed: mouse => mouse.accepted = true
+ }
+
+ 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.35) }
+ GradientStop { position: 0.42; color: ColorUtils.applyAlpha("#FFFFFF", 0.0) }
+ GradientStop { position: 1.0; color: ColorUtils.applyAlpha("#000000", root.glassShineOpacity * 0.22) }
+ }
+ }
+
+ 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.glassBorderOpacity * 0.20)
+ }
+
+ ColumnLayout { // Workspaces
+ id: contentLayout
+
+ z: root.workspaceZ
+ anchors.centerIn: parent
+ spacing: workspaceSpacing
+ ColumnLayout {
+ id: workspaceColumnLayout
+ spacing: workspaceSpacing
+
+ Repeater {
+ model: Config.options.overview.rows
+ delegate: RowLayout {
+ id: row
+ property int rowIndex: index
+ spacing: workspaceSpacing
+ visible: !Config.options.overview.hideEmptyRows ||
+ (root.rowsWithContent && root.rowsWithContent.has(rowIndex))
+ height: visible ? implicitHeight : 0
+
+ Repeater { // Workspace repeater
+ model: Config.options.overview.columns
+ Rectangle { // Workspace
+ id: workspace
+ property int colIndex: index
+ property int workspaceValue: root.getWorkspaceInCell(rowIndex, colIndex)
+ property bool showWallpaper: root.hasEmptyWorkspaceWallpaper
+ property color defaultWorkspaceColor: Appearance.colors.colLayer1
+ property color hoveredWorkspaceColor: ColorUtils.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1)
+ property color hoveredBorderColor: Appearance.colors.colLayer2Hover
+ property bool hoveredWhileDragging: false
+
+ implicitWidth: root.workspaceImplicitWidth
+ implicitHeight: root.workspaceImplicitHeight
+ color: showWallpaper ? "transparent" : ColorUtils.applyAlpha(
+ root.glassMode
+ ? ColorUtils.mix(hoveredWhileDragging ? hoveredWorkspaceColor : defaultWorkspaceColor, Appearance.colors.colLayer0, 0.46)
+ : (hoveredWhileDragging ? hoveredWorkspaceColor : defaultWorkspaceColor),
+ root.effectiveWorkspaceOpacity
+ )
+ radius: Appearance.rounding.screenRounding * root.scale
+ border.width: 2
+ border.color: hoveredWhileDragging
+ ? ColorUtils.applyAlpha(hoveredBorderColor, root.glassMode ? root.glassBorderOpacity : 1)
+ : "transparent"
+
+ Image {
+ id: workspaceWallpaper
+ visible: workspace.showWallpaper
+ anchors.fill: parent
+ source: root.wallpaperSource(root.emptyWorkspaceWallpaperPath)
+ fillMode: Image.PreserveAspectCrop
+ asynchronous: true
+ cache: true
+ smooth: true
+ mipmap: true
+ layer.enabled: workspace.showWallpaper
+ layer.smooth: true
+ layer.effect: MultiEffect {
+ maskEnabled: true
+ maskSource: workspaceWallpaperMask
+ maskThresholdMin: 0.5
+ maskSpreadAtMin: 1.0
+ }
+ }
+
+ Item {
+ id: workspaceWallpaperMask
+ anchors.fill: parent
+ visible: false
+ layer.enabled: true
+ layer.smooth: true
+ Rectangle {
+ anchors.fill: parent
+ radius: workspace.radius
+ }
+ }
+
+ Rectangle {
+ visible: workspace.showWallpaper
+ anchors.fill: parent
+ radius: parent.radius
+ color: ColorUtils.applyAlpha(
+ root.glassMode
+ ? ColorUtils.mix(workspace.hoveredWhileDragging ? workspace.hoveredWorkspaceColor : workspace.defaultWorkspaceColor, Appearance.colors.colLayer0, 0.40)
+ : (workspace.hoveredWhileDragging ? workspace.hoveredWorkspaceColor : workspace.defaultWorkspaceColor),
+ workspace.hoveredWhileDragging
+ ? Math.min(0.28, root.emptyWorkspaceWallpaperOverlayOpacity + 0.08)
+ : root.emptyWorkspaceWallpaperOverlayOpacity
+ )
+ }
+
+ 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.22) }
+ GradientStop { position: 0.46; 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.glassBorderOpacity * 0.16)
+ }
+
+ StyledText {
+ anchors.centerIn: parent
+ visible: !workspace.showWallpaper
+ text: workspaceValue
+ font {
+ pixelSize: root.workspaceNumberSize * root.scale
+ weight: Font.DemiBold
+ family: Appearance.font.family.expressive
+ }
+ color: ColorUtils.transparentize(Appearance.colors.colOnLayer1, 0.8)
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ MouseArea {
+ id: workspaceArea
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton
+ onClicked: {
+ if (root.draggingTargetWorkspace === -1) {
+ GlobalStates.overviewOpen = false
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.focus({workspace = '${workspaceValue}'})`);
+ } else {
+ Hyprland.dispatch(`workspace ${workspaceValue}`)
+ }
+ }
+ }
+ }
+
+ DropArea {
+ anchors.fill: parent
+ onEntered: {
+ root.draggingTargetWorkspace = workspaceValue
+ root.draggingTargetSpecialWorkspace = ""
+ if (root.draggingFromWorkspace == root.draggingTargetWorkspace) return;
+ hoveredWhileDragging = true
+ }
+ onExited: {
+ hoveredWhileDragging = false
+ if (root.draggingTargetWorkspace == workspaceValue) root.draggingTargetWorkspace = -1
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+
+ Item {
+ visible: root.showSpecialWorkspaces
+ implicitWidth: 1
+ implicitHeight: root.specialStripGap
+ }
+
+ Item {
+ id: specialWorkspaceSection
+ visible: root.showSpecialWorkspaces
+ implicitWidth: root.specialSectionWidth
+ implicitHeight: root.specialStripHeight
+
+ Rectangle {
+ anchors.fill: parent
+ radius: Appearance.rounding.normal * root.scale
+ color: ColorUtils.applyAlpha(
+ root.glassMode
+ ? ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colLayer1, 0.70)
+ : Appearance.colors.colLayer1,
+ root.glassMode ? Math.min(0.74, root.effectivePanelOpacity) : root.effectiveWorkspaceOpacity
+ )
+ border.width: 1
+ border.color: ColorUtils.applyAlpha(Appearance.colors.colLayer2Border, root.glassMode ? root.glassBorderOpacity : 0.65)
+
+ Rectangle {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ height: Math.max(18 * root.scale, root.specialStripPadding + root.specialStripTitleHeight * 0.8)
+ radius: parent.radius
+ color: ColorUtils.applyAlpha(Appearance.colors.colSecondary, root.glassMode ? 0.12 : 0.08)
+ }
+
+ StyledText {
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.leftMargin: root.specialStripPadding
+ anchors.topMargin: root.specialStripPadding
+ text: "Special Workspaces"
+ font.family: Appearance.font.family.title
+ font.pixelSize: root.specialStripTitleHeight
+ font.weight: Font.DemiBold
+ color: ColorUtils.applyAlpha(Appearance.colors.colOnLayer1, 0.84)
+ }
+
+ Grid {
+ id: specialWorkspaceGrid
+ x: root.specialGridOffsetX
+ y: root.specialStripPadding + root.specialStripTitleHeight + root.specialStripTitleGap
+ width: root.specialGridUsedWidth
+ columns: root.effectiveSpecialColumns
+ rowSpacing: workspaceSpacing
+ columnSpacing: workspaceSpacing
+
+ Repeater {
+ model: root.visibleSpecialWorkspaces
+ delegate: Rectangle {
+ id: specialWorkspaceTile
+ required property string modelData
+ property string specialName: modelData
+ property var specialGeometry: root.specialWorkspaceGeometry(specialName, root.monitor?.id)
+ property color baseColor: ColorUtils.mix(Appearance.colors.colLayer1, Appearance.colors.colLayer0, 0.52)
+ property bool hasRenderableGeometry: Number.isFinite(specialGeometry?.width)
+ && Number.isFinite(specialGeometry?.height)
+ && specialGeometry.width > 0
+ && specialGeometry.height > 0
+ property bool showWallpaper: root.hasSpecialEmptyWorkspaceWallpaper
+ property real geometryWidth: hasRenderableGeometry ? specialGeometry.width : Math.max(1, root.workspaceImplicitWidth / root.scale)
+ property real geometryHeight: hasRenderableGeometry ? specialGeometry.height : Math.max(1, root.workspaceImplicitHeight / root.scale)
+ property real fitScale: hasRenderableGeometry ? Math.min(width / geometryWidth, height / geometryHeight) : root.scale
+ property real contentWidth: hasRenderableGeometry ? (geometryWidth * fitScale) : width
+ property real contentHeight: hasRenderableGeometry ? (geometryHeight * fitScale) : height
+ property real contentOffsetX: Math.max(0, (width - contentWidth) / 2)
+ property real contentOffsetY: Math.max(0, (height - contentHeight) / 2)
+ implicitWidth: root.specialWorkspaceTileWidth
+ implicitHeight: root.specialWorkspaceTileHeight
+ radius: Appearance.rounding.screenRounding * root.scale
+ clip: true
+ color: showWallpaper ? "transparent" : ColorUtils.applyAlpha(
+ root.glassMode
+ ? ColorUtils.mix(baseColor, Appearance.colors.colLayer0, 0.44)
+ : baseColor,
+ root.effectiveWorkspaceOpacity
+ )
+ border.width: 1
+ border.color: ColorUtils.applyAlpha(Appearance.colors.colLayer2Border, root.glassMode ? root.glassBorderOpacity : 0.75)
+
+ Image {
+ visible: specialWorkspaceTile.showWallpaper
+ anchors.fill: parent
+ source: root.wallpaperSource(root.specialEmptyWorkspaceWallpaperPath)
+ fillMode: Image.PreserveAspectCrop
+ asynchronous: true
+ cache: true
+ smooth: true
+ mipmap: true
+ }
+
+ Rectangle {
+ visible: specialWorkspaceTile.showWallpaper
+ anchors.fill: parent
+ radius: parent.radius
+ color: ColorUtils.applyAlpha(
+ root.glassMode
+ ? ColorUtils.mix(specialWorkspaceTile.baseColor, Appearance.colors.colLayer0, 0.40)
+ : specialWorkspaceTile.baseColor,
+ root.emptyWorkspaceWallpaperOverlayOpacity
+ )
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton
+ onClicked: {
+ if (root.draggingTargetWorkspace === -1 && !root.draggingTargetSpecialWorkspace) {
+ GlobalStates.overviewOpen = false;
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.workspace.toggle_special('${specialWorkspaceTile.specialName}')`);
+ } else {
+ Hyprland.dispatch(`togglespecialworkspace ${specialWorkspaceTile.specialName}`);
+ }
+ }
+ }
+ }
+
+ DropArea {
+ anchors.fill: parent
+ onEntered: {
+ root.draggingTargetWorkspace = -1;
+ root.draggingTargetSpecialWorkspace = specialWorkspaceTile.specialName;
+ }
+ onExited: {
+ if (root.draggingTargetSpecialWorkspace === specialWorkspaceTile.specialName)
+ root.draggingTargetSpecialWorkspace = "";
+ }
+ }
+
+ Item {
+ id: specialWorkspaceContent
+ x: specialWorkspaceTile.contentOffsetX
+ y: specialWorkspaceTile.contentOffsetY
+ width: specialWorkspaceTile.contentWidth
+ height: specialWorkspaceTile.contentHeight
+ clip: true
+
+ Repeater {
+ model: ScriptModel {
+ values: {
+ if (!specialWorkspaceTile.hasRenderableGeometry)
+ return [];
+ return ToplevelManager.toplevels.values.filter((toplevel) => {
+ const address = `0x${toplevel.HyprlandToplevel.address}`;
+ const win = windowByAddress[address];
+ if ((win?.monitor ?? -1) !== (root.monitor?.id ?? -1))
+ return false;
+ return root.specialWorkspaceName(win) === specialWorkspaceTile.specialName;
+ }).sort((a, b) => {
+ const addrA = `0x${a.HyprlandToplevel.address}`;
+ const addrB = `0x${b.HyprlandToplevel.address}`;
+ return addrA.localeCompare(addrB);
+ });
+ }
+ }
+ delegate: OverviewWindow {
+ id: specialWindow
+ required property var modelData
+ required property int index
+ property var address: `0x${modelData.HyprlandToplevel.address}`
+ property int monitorId: windowData?.monitor
+ property var monitor: HyprlandData.monitors.find(m => m.id === monitorId)
+ property Item homeParent: specialWorkspaceContent
+ windowData: windowByAddress[address]
+ toplevel: modelData
+ monitorData: monitor
+ widgetMonitorData: root.monitorData
+ scale: root.scale
+ availableWorkspaceWidth: specialWorkspaceContent.width
+ availableWorkspaceHeight: specialWorkspaceContent.height
+ positionBaseX: Number.isFinite(specialWorkspaceTile.specialGeometry?.x) ? specialWorkspaceTile.specialGeometry.x : ((monitor?.x ?? 0) + (monitor?.reserved?.[0] ?? 0))
+ positionBaseY: Number.isFinite(specialWorkspaceTile.specialGeometry?.y) ? specialWorkspaceTile.specialGeometry.y : ((monitor?.y ?? 0) + (monitor?.reserved?.[1] ?? 0))
+ geometryScaleX: specialWorkspaceTile.fitScale / root.scale
+ geometryScaleY: specialWorkspaceTile.fitScale / root.scale
+ xOffset: 0
+ yOffset: 0
+ widgetMonitorId: root.monitor.id
+ recaptureToken: root.previewRecaptureToken
+ restrictToWorkspace: false
+ animateSize: false
+ z: root.specialWindowZ(windowData)
+
+ function moveToDragLayer() {
+ const mapped = specialWindow.mapToItem(specialWindowDragLayer, 0, 0);
+ specialWindow.suspendPositionAnimation = true;
+ specialWindow.parent = specialWindowDragLayer;
+ specialWindow.x = mapped.x;
+ specialWindow.y = mapped.y;
+ specialWindow.z = root.windowDraggingZ + 1;
+ Qt.callLater(() => specialWindow.suspendPositionAnimation = false);
+ }
+
+ function returnToHomeParent() {
+ specialWindow.suspendPositionAnimation = true;
+ specialWindow.parent = homeParent;
+ specialWindow.z = root.specialWindowZ(windowData);
+ Qt.callLater(() => specialWindow.suspendPositionAnimation = false);
+ }
+
+ MouseArea {
+ id: specialDragArea
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: hovered = true
+ onExited: hovered = false
+ acceptedButtons: Qt.LeftButton | Qt.MiddleButton
+ drag.target: parent
+ onPressed: (mouse) => {
+ root.draggingFromWorkspace = -1
+ root.draggingTargetSpecialWorkspace = ""
+ specialWindow.pressed = true
+ specialWindow.dragInProgress = true
+ specialWindow.Drag.source = specialWindow
+ specialWindow.Drag.hotSpot.x = mouse.x
+ specialWindow.Drag.hotSpot.y = mouse.y
+ specialWindow.moveToDragLayer()
+ specialWindow.Drag.active = true
+ }
+ onReleased: {
+ const targetWorkspace = root.draggingTargetWorkspace
+ const targetSpecialWorkspace = root.draggingTargetSpecialWorkspace
+ specialWindow.pressed = false
+ specialWindow.Drag.active = false
+ specialWindow.dragInProgress = false
+ root.draggingFromWorkspace = -1
+ root.draggingTargetWorkspace = -1
+ root.draggingTargetSpecialWorkspace = ""
+ if (targetSpecialWorkspace === root.createSpecialWorkspaceTarget) {
+ const createdName = root.nextSpecialWorkspaceName()
+ //INFO: From special TO NEW special
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.window.move({workspace = 'special:${createdName}', follow = false, window = 'address:${specialWindow.windowData?.address}'})`);
+ } else {
+ Hyprland.dispatch(`movetoworkspacesilent special:${createdName}, address:${specialWindow.windowData?.address}`)
+ }
+ specialWindow.returnToHomeParent()
+ specialWindow.x = specialWindow.initX
+ specialWindow.y = specialWindow.initY
+ }
+ else if (targetSpecialWorkspace && targetSpecialWorkspace !== specialWorkspaceTile.specialName) {
+ // Idk
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.window.move({workspace = 'special:${targetSpecialWorkspace}', follow = false, window = 'address:${specialWindow.windowData?.address}'})`);
+ } else {
+ Hyprland.dispatch(`movetoworkspacesilent special:${targetSpecialWorkspace}, address:${specialWindow.windowData?.address}`)
+ }
+ specialWindow.returnToHomeParent()
+ specialWindow.x = specialWindow.initX
+ specialWindow.y = specialWindow.initY
+ }
+ else if (targetWorkspace !== -1) {
+ //INFO: From Special TO Normal workspace
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.window.move({workspace = '${targetWorkspace}', follow = false, window = 'address:${specialWindow.windowData?.address}'})`);
+ } else {
+ Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${specialWindow.windowData?.address}`)
+ }
+ specialWindow.returnToHomeParent()
+ specialWindow.x = specialWindow.initX
+ specialWindow.y = specialWindow.initY
+ }
+ else {
+ specialWindow.returnToHomeParent()
+ specialWindow.x = specialWindow.initX
+ specialWindow.y = specialWindow.initY
+ }
+ }
+ onClicked: (event) => {
+ if (!windowData)
+ return;
+ if (event.button === Qt.LeftButton) {
+ GlobalStates.overviewOpen = false;
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.focus({ window = 'address:${windowData.address}' })`);
+ } else {
+ Hyprland.dispatch(`focuswindow address:${windowData.address}`);
+ }
+ event.accepted = true;
+ } else if (event.button === Qt.MiddleButton) {
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.window.close('address:${windowData.address}')`);
+ } else {
+ Hyprland.dispatch(`closewindow address:${windowData.address}`);
+ }
+ event.accepted = true;
+ }
+ }
+
+ StyledToolTip {
+ extraVisibleCondition: false
+ alternativeVisibleCondition: specialDragArea.containsMouse && !specialWindow.Drag.active
+ text: `${windowData?.title ?? "Unknown"}\n[${windowData?.class ?? "unknown"}] ${windowData?.xwayland ? "[XWayland] " : ""}`
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ Rectangle {
+ id: createSpecialWorkspaceTile
+ property bool showWallpaper: root.hasSpecialEmptyWorkspaceWallpaper
+ implicitWidth: root.specialWorkspaceTileWidth
+ implicitHeight: root.specialWorkspaceTileHeight
+ radius: Appearance.rounding.screenRounding * root.scale
+ color: showWallpaper ? "transparent" : ColorUtils.applyAlpha(
+ root.glassMode
+ ? ColorUtils.mix(Appearance.colors.colSecondaryContainer, Appearance.colors.colLayer1, 0.58)
+ : ColorUtils.mix(Appearance.colors.colLayer2, Appearance.colors.colLayer1, 0.55),
+ root.draggingTargetSpecialWorkspace === root.createSpecialWorkspaceTarget ? 0.90 : root.effectiveWorkspaceOpacity
+ )
+ border.width: 1
+ border.color: root.draggingTargetSpecialWorkspace === root.createSpecialWorkspaceTarget
+ ? ColorUtils.applyAlpha(root.activeBorderColor, 0.96)
+ : ColorUtils.applyAlpha(Appearance.colors.colSecondary, 0.46)
+
+ Image {
+ visible: createSpecialWorkspaceTile.showWallpaper
+ anchors.fill: parent
+ source: root.wallpaperSource(root.specialEmptyWorkspaceWallpaperPath)
+ fillMode: Image.PreserveAspectCrop
+ asynchronous: true
+ cache: true
+ smooth: true
+ mipmap: true
+ }
+
+ Rectangle {
+ visible: createSpecialWorkspaceTile.showWallpaper
+ anchors.fill: parent
+ radius: parent.radius
+ color: ColorUtils.applyAlpha(
+ root.glassMode
+ ? ColorUtils.mix(Appearance.colors.colSecondaryContainer, Appearance.colors.colLayer0, 0.40)
+ : ColorUtils.mix(Appearance.colors.colLayer2, Appearance.colors.colLayer1, 0.55),
+ root.draggingTargetSpecialWorkspace === root.createSpecialWorkspaceTarget
+ ? Math.min(0.28, root.emptyWorkspaceWallpaperOverlayOpacity + 0.08)
+ : root.emptyWorkspaceWallpaperOverlayOpacity
+ )
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 1
+ radius: Math.max(parent.radius - 1, 0)
+ color: "transparent"
+ border.width: 1
+ border.color: ColorUtils.applyAlpha("#FFFFFF", root.glassMode ? 0.12 : 0.08)
+ }
+
+ Column {
+ anchors.centerIn: parent
+ spacing: 0
+
+ StyledText {
+ anchors.horizontalCenter: parent.horizontalCenter
+ visible: !createSpecialWorkspaceTile.showWallpaper
+ text: root.draggingTargetSpecialWorkspace === root.createSpecialWorkspaceTarget ? "Release" : "+"
+ font.family: Appearance.font.family.expressive
+ font.pixelSize: root.draggingTargetSpecialWorkspace === root.createSpecialWorkspaceTarget
+ ? Appearance.font.pixelSize.larger * root.scale
+ : Appearance.font.pixelSize.huge * 1.25 * root.scale
+ font.weight: Font.DemiBold
+ color: ColorUtils.applyAlpha(Appearance.colors.colOnLayer1, 0.92)
+ horizontalAlignment: Text.AlignHCenter
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton
+ onClicked: {
+ const createdName = root.nextSpecialWorkspaceName();
+ GlobalStates.overviewOpen = false;
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.workspace.toggle_special('${createdName}')`);
+ } else {
+ Hyprland.dispatch(`togglespecialworkspace ${createdName}`);
+ }
+ }
+ }
+
+ DropArea {
+ anchors.fill: parent
+ onEntered: {
+ root.draggingTargetWorkspace = -1;
+ root.draggingTargetSpecialWorkspace = root.createSpecialWorkspaceTarget;
+ }
+ onExited: {
+ if (root.draggingTargetSpecialWorkspace === root.createSpecialWorkspaceTarget)
+ root.draggingTargetSpecialWorkspace = "";
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Item { // Windows & focused workspace indicator
+ id: windowSpace
+ anchors.centerIn: parent
+ implicitWidth: contentLayout.implicitWidth
+ implicitHeight: contentLayout.implicitHeight
+
+ WheelHandler {
+ target: null
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ onWheel: event => {
+ const deltaY = event.angleDelta.y;
+ if (!deltaY)
+ return;
+ root.stepWorkspace(deltaY > 0 ? -1 : 1);
+ event.accepted = true;
+ }
+ }
+
+ Repeater { // Window repeater
+ model: ScriptModel {
+ values: {
+ return ToplevelManager.toplevels.values.filter((toplevel) => {
+ const address = `0x${toplevel.HyprlandToplevel.address}`
+ var win = windowByAddress[address]
+ if (root.isSpecialWorkspace(win))
+ return false;
+ const minWorkspace = root.workspaceGroup * root.workspacesShown + 1 + workspaceOffset;
+ const maxWorkspace = (root.workspaceGroup + 1) * root.workspacesShown + workspaceOffset;
+ const inWorkspaceGroup = (minWorkspace <= win?.workspace?.id && win?.workspace?.id <= maxWorkspace)
+ return inWorkspaceGroup;
+ }).sort((a, b) => {
+ // Proper stacking order based on Hyprland's window properties
+ const addrA = `0x${a.HyprlandToplevel.address}`
+ const addrB = `0x${b.HyprlandToplevel.address}`
+ const winA = windowByAddress[addrA]
+ const winB = windowByAddress[addrB]
+
+ // 1. Pinned windows are always on top
+ if (winA?.pinned !== winB?.pinned) {
+ return winA?.pinned ? 1 : -1
+ }
+
+ // 2. Floating windows above tiled windows
+ if (winA?.floating !== winB?.floating) {
+ return winA?.floating ? 1 : -1
+ }
+
+ // 3. Within same category, sort by focus history
+ // Lower focusHistoryID = more recently focused = higher in stack
+ return (winB?.focusHistoryID ?? 0) - (winA?.focusHistoryID ?? 0)
+ })
+ }
+ }
+ delegate: OverviewWindow {
+ id: window
+ required property var modelData
+ required property int index
+ property int monitorId: windowData?.monitor
+ property var monitor: HyprlandData.monitors.find(m => m.id === monitorId)
+ property var address: `0x${modelData.HyprlandToplevel.address}`
+ windowData: windowByAddress[address]
+ toplevel: modelData
+ monitorData: monitor
+ widgetMonitorData: root.monitorData
+ scale: root.scale
+ availableWorkspaceWidth: root.workspaceImplicitWidth
+ availableWorkspaceHeight: root.workspaceImplicitHeight
+ widgetMonitorId: root.monitor.id
+ recaptureToken: root.previewRecaptureToken
+
+ property bool atInitPosition: (initX == x && initY == y)
+
+ property int workspaceColIndex: root.getWorkspaceColumn(windowData?.workspace.id)
+ property int workspaceRowIndex: root.getWorkspaceRow(windowData?.workspace.id)
+ property int visibleWorkspaceRowIndex: root.getVisibleRowPosition(workspaceRowIndex)
+ xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex
+ yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * visibleWorkspaceRowIndex
+
+ Timer {
+ id: updateWindowPosition
+ interval: Config.options.hacks.arbitraryRaceConditionDelay
+ repeat: false
+ running: false
+ onTriggered: {
+ window.x = Math.round(Math.max((windowData?.at[0] - (monitor?.x ?? 0) - (monitorData?.reserved?.[0] ?? 0)) * root.scale * window.widthRatio, 0) + xOffset)
+ window.y = Math.round(Math.max((windowData?.at[1] - (monitor?.y ?? 0) - (monitorData?.reserved?.[1] ?? 0)) * root.scale * window.heightRatio, 0) + yOffset)
+ }
+ }
+
+ z: atInitPosition ? (root.windowZ + index) : root.windowDraggingZ
+ Drag.hotSpot.x: targetWindowWidth / 2
+ Drag.hotSpot.y: targetWindowHeight / 2
+ MouseArea {
+ id: dragArea
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: hovered = true
+ onExited: hovered = false
+ acceptedButtons: Qt.LeftButton | Qt.MiddleButton
+ drag.target: parent
+ onPressed: (mouse) => {
+ root.draggingFromWorkspace = windowData?.workspace.id
+ root.draggingTargetSpecialWorkspace = ""
+ window.pressed = true
+ window.Drag.active = true
+ window.Drag.source = window
+ window.Drag.hotSpot.x = mouse.x
+ window.Drag.hotSpot.y = mouse.y
+ }
+ onReleased: {
+ const targetWorkspace = root.draggingTargetWorkspace
+ const targetSpecialWorkspace = root.draggingTargetSpecialWorkspace
+ window.pressed = false
+ window.Drag.active = false
+ root.draggingFromWorkspace = -1
+ root.draggingTargetWorkspace = -1
+ root.draggingTargetSpecialWorkspace = ""
+ if (targetSpecialWorkspace === root.createSpecialWorkspaceTarget) {
+ const createdName = root.nextSpecialWorkspaceName()
+ //INFO: From normal TO special
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.window.move({workspace = 'special:${createdName}', follow = false, window = 'address:${window.windowData?.address}'})`);
+ } else {
+ Hyprland.dispatch(`movetoworkspacesilent special:${createdName}, address:${window.windowData?.address}`)
+ }
+ updateWindowPosition.restart()
+ }
+ else if (targetSpecialWorkspace && targetSpecialWorkspace !== root.specialWorkspaceName(windowData)) {
+ // Idk
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.window.move({workspace = 'special:${targetSpecialWorkspace}', follow = false, window = 'address:${window.windowData?.address}'})`);
+ } else {
+ Hyprland.dispatch(`movetoworkspacesilent special:${targetSpecialWorkspace}, address:${window.windowData?.address}`)
+ }
+ updateWindowPosition.restart()
+ }
+ else if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) {
+ //INFO: From normal TO normal
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.window.move({workspace = '${targetWorkspace}', follow = false, window = 'address:${window.windowData?.address}'})`);
+ } else {
+ Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${window.windowData?.address}`)
+ }
+ updateWindowPosition.restart()
+ }
+ else {
+ window.x = window.initX
+ window.y = window.initY
+ }
+ }
+ onClicked: (event) => {
+ if (!windowData) return;
+
+ if (event.button === Qt.LeftButton) {
+ GlobalStates.overviewOpen = false
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.focus({ window = 'address:${windowData.address}' })`);
+ } else {
+ Hyprland.dispatch(`focuswindow address:${windowData.address}`)
+ }
+ event.accepted = true
+ } else if (event.button === Qt.MiddleButton) {
+ if (Hyprland.usingLua) {
+ Hyprland.dispatch(`hl.dsp.window.close('address:${windowData.address}')`);
+ } else {
+ Hyprland.dispatch(`closewindow address:${windowData.address}`)
+ }
+ event.accepted = true
+ }
+ }
+
+ StyledToolTip {
+ extraVisibleCondition: false
+ alternativeVisibleCondition: dragArea.containsMouse && !window.Drag.active
+ text: `${windowData?.title ?? "Unknown"}\n[${windowData?.class ?? "unknown"}] ${windowData?.xwayland ? "[XWayland] " : ""}`
+ }
+ }
+ }
+ }
+
+ Rectangle { // Focused workspace indicator
+ id: focusedWorkspaceIndicator
+ property int activeWorkspaceRowIndex: root.getWorkspaceRow(root.effectiveActiveWorkspaceId)
+ property int visibleActiveWorkspaceRowIndex: root.getVisibleRowPosition(activeWorkspaceRowIndex)
+ property int activeWorkspaceColIndex: root.getWorkspaceColumn(root.effectiveActiveWorkspaceId)
+ x: (root.workspaceImplicitWidth + workspaceSpacing) * activeWorkspaceColIndex
+ y: (root.workspaceImplicitHeight + workspaceSpacing) * visibleActiveWorkspaceRowIndex
+ z: root.windowDraggingZ - 1
+ width: root.workspaceImplicitWidth
+ height: root.workspaceImplicitHeight
+ color: "transparent"
+ radius: Appearance.rounding.screenRounding * root.scale
+ border.width: 2
+ border.color: root.activeBorderColor
+ Behavior on x {
+ animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
+ }
+ Behavior on y {
+ animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
+ }
+ }
+ }
+
+ Item {
+ id: specialWindowDragLayer
+ anchors.fill: parent
+ z: root.windowDraggingZ + 1
+ }
+ }
+}
diff --git a/config/quickshell/overview-backup/modules/overview/OverviewWindow.qml b/config/quickshell/overview-backup/modules/overview/OverviewWindow.qml
new file mode 100644
index 00000000..3b2652a9
--- /dev/null
+++ b/config/quickshell/overview-backup/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();
+ }
+}
diff --git a/config/quickshell/overview-backup/modules/overview/qmldir b/config/quickshell/overview-backup/modules/overview/qmldir
new file mode 100644
index 00000000..9b15b45b
--- /dev/null
+++ b/config/quickshell/overview-backup/modules/overview/qmldir
@@ -0,0 +1,3 @@
+Overview 1.0 Overview.qml
+OverviewWidget 1.0 OverviewWidget.qml
+OverviewWindow 1.0 OverviewWindow.qml
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage