Files
dotfiles/dot_config/omarchy/plugins/private_joe.workspaces/Workspaces.qml
T
2026-09-30 21:50:14 -04:00

121 lines
3.8 KiB
QML

import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import qs.Commons
import qs.Ui
BarWidget {
id: root
moduleName: "joe.workspaces"
// The Hyprland monitor this bar surface lives on. One bar is built per
// screen, so each instance filters to its own monitor's workspaces.
readonly property var screen: root.QsWindow.window ? root.QsWindow.window.screen : null
readonly property var monitor: screen ? Hyprland.monitorFor(screen) : null
// Green from the current theme's colors.toml. The shell's Color singleton
// doesn't expose it, so read it here; `activeColor` in shell.json overrides.
property color themeGreen: "#9ece6a"
readonly property color activeColor: root.setting("activeColor", root.themeGreen)
FileView {
id: colorsFile
path: Color.currentThemePath + "/colors.toml"
printErrors: false
onLoaded: {
var match = text().match(/^\s*(?:green|color2)\s*=\s*["']?(#[0-9A-Fa-f]{6})/m)
if (match) root.themeGreen = match[1]
}
}
// Theme switches push new colors into Color; re-read ours alongside.
Connections {
target: Color
function onAccentChanged() { colorsFile.reload() }
}
function workspaceById(id) {
var values = Hyprland.workspaces.values
for (var i = 0; i < values.length; i++) {
if (values[i].id === id) return values[i]
}
return null
}
function workspaceIds() {
var ids = []
var values = Hyprland.workspaces.values
for (var i = 0; i < values.length; i++) {
var ws = values[i]
if (ws.id <= 0 || ws.id > 10) continue
if (root.monitor && ws.monitor !== root.monitor) continue
ids.push(ws.id)
}
ids.sort(function(left, right) { return left - right })
return ids
}
function focusWorkspace(id) {
if (!root.bar) return
root.bar.run("hyprctl dispatch " + Util.shellQuote("hl.dsp.focus({ workspace = \"" + id + "\" })"))
}
readonly property real trailingGap: root.vertical ? 0 : Style.spaceReal(1.5)
implicitWidth: grid.implicitWidth + trailingGap
implicitHeight: grid.implicitHeight
GridLayout {
id: grid
anchors.fill: parent
anchors.rightMargin: root.trailingGap
columns: root.vertical ? 1 : Math.max(1, root.workspaceIds().length)
columnSpacing: root.vertical ? 0 : Style.space(1)
rowSpacing: root.vertical ? Style.space(2) : 0
Repeater {
model: root.workspaceIds()
WidgetButton {
required property int modelData
readonly property var workspace: root.workspaceById(modelData)
readonly property bool occupied: workspace !== null && workspace.toplevels.values.length > 0
// The workspace shown on this bar's monitor (each monitor has one).
readonly property bool focused: root.monitor !== null && root.monitor.activeWorkspace !== null
&& root.monitor.activeWorkspace.id === modelData
id: button
bar: root.bar
text: modelData === 10 ? "0" : String(modelData)
// WidgetButton's own label can't be bolded, so draw our own instead.
labelVisible: false
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) }
Text {
anchors.centerIn: parent
textFormat: Text.PlainText
text: button.text
color: button.focused ? root.activeColor : button.foreground
font.family: button.fontFamily
font.pixelSize: button.fontSize
font.bold: button.focused
renderType: Text.NativeRendering
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
}
}