Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8b49abd4b | ||
|
|
e05733448a |
+131
-28
@@ -6,7 +6,24 @@ import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
id: root
|
||||
moduleName: "omarchy.workspaces"
|
||||
moduleName: "pastilhas.workspaces"
|
||||
|
||||
// Workspaces grouped in physical monitor order, mirroring the pinning in
|
||||
// ~/.config/hypr/workspaces.lua:
|
||||
//
|
||||
// HDMI-A-1 (left) 11-15
|
||||
// DP-1 (centre) 1-10
|
||||
// HDMI-A-2 (right) 16-20
|
||||
//
|
||||
// Stock built its list from occupancy and hard-capped it at 10, so 11-20
|
||||
// could never appear no matter how they were assigned. This is a fixed
|
||||
// layout instead: every workspace is always shown, in the order the monitors
|
||||
// physically sit, so the bar reads as a map of the desk.
|
||||
readonly property var workspaceGroups: [
|
||||
[11, 12, 13, 14, 15],
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||
[16, 17, 18, 19, 20]
|
||||
]
|
||||
|
||||
function workspaceById(id) {
|
||||
var values = Hyprland.workspaces.values
|
||||
@@ -17,25 +34,73 @@ BarWidget {
|
||||
return null
|
||||
}
|
||||
|
||||
function workspaceIds() {
|
||||
var ids = [1, 2, 3, 4, 5]
|
||||
var values = Hyprland.workspaces.values
|
||||
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var id = values[i].id
|
||||
if (id > 0 && id <= 10 && ids.indexOf(id) === -1) ids.push(id)
|
||||
// Flattened to a single model with separator markers between the groups, so
|
||||
// one Repeater can lay out buttons and dividers in order.
|
||||
function entries() {
|
||||
var out = []
|
||||
for (var g = 0; g < workspaceGroups.length; g++) {
|
||||
if (g > 0) out.push({ separator: true, id: -g })
|
||||
var group = workspaceGroups[g]
|
||||
for (var i = 0; i < group.length; i++) out.push({ separator: false, id: group[i] })
|
||||
}
|
||||
|
||||
ids.sort(function(left, right) { return left - right })
|
||||
return ids
|
||||
return out
|
||||
}
|
||||
|
||||
// Which monitor each group is pinned to, parallel to workspaceGroups above.
|
||||
readonly property var groupMonitors: ["HDMI-A-1", "DP-1", "HDMI-A-2"]
|
||||
|
||||
function monitorForWorkspace(id) {
|
||||
for (var g = 0; g < workspaceGroups.length; g++) {
|
||||
if (workspaceGroups[g].indexOf(id) !== -1) return groupMonitors[g]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Clicking a workspace that lives on another monitor should change what that
|
||||
// monitor shows without dragging the pointer across the desk. A plain
|
||||
// `focus({ workspace })` can't do that: Omarchy sets
|
||||
// cursor.warp_on_change_workspace = 1, so the pointer teleports to whichever
|
||||
// screen the workspace is pinned to.
|
||||
//
|
||||
// So for a cross-monitor click: suppress warping, hop focus to the target
|
||||
// monitor, change its workspace, hop back, restore warping. It goes through
|
||||
// `hyprctl eval` rather than `dispatch` because dispatch takes exactly one
|
||||
// dispatcher object, and because `hyprctl keyword` no longer works under the
|
||||
// Lua parser ("keyword can't work with non-legacy parsers. Use eval.").
|
||||
//
|
||||
// Clicking a workspace on the monitor you're already on keeps stock
|
||||
// behaviour — plain focus, cursor unaffected anyway.
|
||||
function focusWorkspace(id) {
|
||||
if (!root.bar) return
|
||||
root.bar.run("hyprctl dispatch " + Util.shellQuote("hl.dsp.focus({ workspace = \"" + id + "\" })"))
|
||||
|
||||
var target = monitorForWorkspace(id)
|
||||
var monitor = Hyprland.focusedMonitor
|
||||
var current = monitor ? String(monitor.name || "") : ""
|
||||
|
||||
if (target === "" || current === "" || target === current) {
|
||||
root.bar.run("hyprctl dispatch " + Util.shellQuote("hl.dsp.focus({ workspace = \"" + id + "\" })"))
|
||||
return
|
||||
}
|
||||
|
||||
var lua =
|
||||
'hl.config({ cursor = { warp_on_change_workspace = 0, no_warps = true } }) ' +
|
||||
'hl.dispatch(hl.dsp.focus({ monitor = "' + target + '" })) ' +
|
||||
'hl.dispatch(hl.dsp.focus({ workspace = "' + id + '" })) ' +
|
||||
'hl.dispatch(hl.dsp.focus({ monitor = "' + current + '" })) ' +
|
||||
'hl.config({ cursor = { warp_on_change_workspace = 1, no_warps = false } })'
|
||||
|
||||
root.bar.run("hyprctl eval " + Util.shellQuote(lua))
|
||||
}
|
||||
|
||||
readonly property real trailingGap: root.vertical ? 0 : Style.spaceReal(1.5)
|
||||
readonly property color lineColor: root.bar ? root.bar.foreground : Color.foreground
|
||||
|
||||
// Spacing between adjacent workspace buttons, and the extra breathing room
|
||||
// added on each side of a group divider. The divider sits in the same
|
||||
// GridLayout as the buttons, so without this it would inherit the ordinary
|
||||
// inter-button spacing and the three groups would read as one run.
|
||||
readonly property real itemSpacing: 0
|
||||
readonly property real groupGap: Style.space(5)
|
||||
|
||||
implicitWidth: grid.implicitWidth + trailingGap
|
||||
implicitHeight: grid.implicitHeight
|
||||
@@ -44,28 +109,66 @@ BarWidget {
|
||||
id: grid
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: root.trailingGap
|
||||
columns: root.vertical ? 1 : root.workspaceIds().length
|
||||
columnSpacing: root.vertical ? 0 : Style.space(1)
|
||||
columns: root.vertical ? 1 : root.entries().length
|
||||
columnSpacing: root.vertical ? 0 : root.itemSpacing
|
||||
rowSpacing: root.vertical ? Style.space(2) : 0
|
||||
|
||||
Repeater {
|
||||
model: root.workspaceIds()
|
||||
model: root.entries()
|
||||
|
||||
WidgetButton {
|
||||
required property int modelData
|
||||
Loader {
|
||||
required property var modelData
|
||||
|
||||
readonly property var workspace: root.workspaceById(modelData)
|
||||
readonly property bool occupied: workspace !== null && workspace.toplevels.values.length > 0
|
||||
readonly property bool focused: Hyprland.focusedWorkspace !== null && Hyprland.focusedWorkspace.id === modelData
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
// Widen the gap either side of a divider so the groups read as three
|
||||
// distinct blocks rather than one long row.
|
||||
Layout.leftMargin: (modelData.separator && !root.vertical) ? root.groupGap : 0
|
||||
Layout.rightMargin: (modelData.separator && !root.vertical) ? root.groupGap : 0
|
||||
Layout.topMargin: (modelData.separator && root.vertical) ? root.groupGap : 0
|
||||
Layout.bottomMargin: (modelData.separator && root.vertical) ? root.groupGap : 0
|
||||
sourceComponent: modelData.separator ? separatorComponent : workspaceComponent
|
||||
|
||||
bar: root.bar
|
||||
text: focused ? "\uDB85\uDCFB" : (modelData === 10 ? "0" : String(modelData))
|
||||
opacity: occupied || focused ? 1 : 0.5
|
||||
horizontalMargin: 6
|
||||
verticalPadding: 6
|
||||
fixedWidth: root.vertical ? root.barSize : Style.space(20)
|
||||
fixedHeight: root.barSize
|
||||
onPressed: function() { root.focusWorkspace(modelData) }
|
||||
Component {
|
||||
id: separatorComponent
|
||||
|
||||
Rectangle {
|
||||
implicitWidth: root.vertical ? Math.round(root.barSize * 0.45) : 1
|
||||
implicitHeight: root.vertical ? 1 : Math.round(root.barSize * 0.45)
|
||||
color: root.lineColor
|
||||
opacity: 0.3
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: workspaceComponent
|
||||
|
||||
WidgetButton {
|
||||
readonly property int wsId: modelData.id
|
||||
readonly property var workspace: root.workspaceById(wsId)
|
||||
readonly property bool occupied: workspace !== null && workspace.toplevels.values.length > 0
|
||||
readonly property bool focused: Hyprland.focusedWorkspace !== null && Hyprland.focusedWorkspace.id === wsId
|
||||
|
||||
bar: root.bar
|
||||
// Single digit per workspace: id mod 10, so 11-15 read "1 2 3 4 5",
|
||||
// 1-10 read "1 2 3 4 5 6 7 8 9 0", and 16-20 read "6 7 8 9 0".
|
||||
//
|
||||
// That is not just for width — it makes the label the key you press.
|
||||
// The bindings in ~/.config/hypr/bindings.lua are SUPER+<n> for 1-10,
|
||||
// SUPER+CTRL+1..5 for 11-15 and SUPER+CTRL+6..0 for 16-20, so the
|
||||
// digit shown is the digit in the shortcut for that workspace.
|
||||
text: focused ? "" : String(wsId % 10)
|
||||
opacity: occupied || focused ? 1 : 0.5
|
||||
horizontalMargin: 6
|
||||
verticalPadding: 6
|
||||
// Twenty workspaces plus dividers is a lot of bar. Every label is a
|
||||
// single character now, so these can be narrower still than the
|
||||
// stock space(20), with a font a step below body.
|
||||
fontSize: Style.font.bodySmall
|
||||
fixedWidth: root.vertical ? root.barSize : Style.space(13)
|
||||
fixedHeight: root.barSize
|
||||
onPressed: function() { root.focusWorkspace(wsId) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user