adds overview, removes old stuff
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import "../../common"
|
||||
import "../../services"
|
||||
import "../.."
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user