diff options
Diffstat (limited to 'quickshell/gruvbar/shell.qml')
| -rw-r--r-- | quickshell/gruvbar/shell.qml | 631 |
1 files changed, 631 insertions, 0 deletions
diff --git a/quickshell/gruvbar/shell.qml b/quickshell/gruvbar/shell.qml new file mode 100644 index 0000000..0d03f70 --- /dev/null +++ b/quickshell/gruvbar/shell.qml @@ -0,0 +1,631 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.I3 +import Quickshell.Io + +ShellRoot { + id: root + + property bool expanded: false + property int pendingWorkspace: 0 + property int barHeight: 38 + property int drawerHeight: 118 + property string scriptDir: Quickshell.shellPath("scripts") + property string fontFamily: "FiraCode Nerd Font" + property string bg: "#282828" + property string bg1: "#3c3836" + property string bg2: "#504945" + property string bg3: "#665c54" + property string fg: "#ebdbb2" + property string fg1: "#d5c4a1" + property string red: "#cc241d" + property string green: "#98971a" + property string yellow: "#d79921" + property string orange: "#d65d0e" + property string purple: "#b16286" + property string aqua: "#689d6a" + property string gray: "#928374" + property string clockText: Qt.formatDateTime(new Date(), "hh:mm dd MMM") + + signal refreshRequested(string actionName) + + function statusCommand(name) { + return [root.scriptDir + "/status.sh", name]; + } + + function requestStatusRefresh(actionName) { + root.refreshRequested(actionName); + quickStatusRefresh.actionName = actionName; + settleStatusRefresh.actionName = actionName; + lateStatusRefresh.actionName = actionName; + quickStatusRefresh.restart(); + settleStatusRefresh.restart(); + lateStatusRefresh.restart(); + } + + function runAction(name, arg) { + var cmd = [root.scriptDir + "/action.sh", name]; + if (arg !== undefined && arg !== "") + cmd.push(arg); + + Quickshell.execDetached(cmd); + root.requestStatusRefresh(name); + } + + function switchWorkspace(workspaceNumber) { + root.pendingWorkspace = workspaceNumber; + pendingWorkspaceReset.restart(); + I3.dispatch("workspace number " + workspaceNumber); + } + + function textColor(value, fallback) { + if (value === "n/a") + return root.gray; + + if (value.indexOf("down") === 0 || value.indexOf("muted") === 0) + return root.red; + + if (value.indexOf("dnd") === 0 || value.indexOf("night") === 0) + return root.orange; + + if (value === "on" || value.indexOf("up") === 0 || value === "day") + return root.green; + + return fallback; + } + + Timer { + interval: 1000 + running: true + repeat: true + onTriggered: root.clockText = Qt.formatDateTime(new Date(), "hh:mm dd MMM") + } + + Timer { + id: quickStatusRefresh + + property string actionName: "" + + interval: 120 + onTriggered: root.refreshRequested(actionName) + } + + Timer { + id: settleStatusRefresh + + property string actionName: "" + + interval: 500 + onTriggered: root.refreshRequested(actionName) + } + + Timer { + id: lateStatusRefresh + + property string actionName: "" + + interval: 1600 + onTriggered: root.refreshRequested(actionName) + } + + Timer { + id: pendingWorkspaceReset + + interval: 700 + onTriggered: root.pendingWorkspace = 0 + } + + ListModel { + id: workspaceModel + + ListElement { + ws: 1 + } + + ListElement { + ws: 2 + } + + ListElement { + ws: 3 + } + + ListElement { + ws: 4 + } + + ListElement { + ws: 5 + } + + ListElement { + ws: 6 + } + + ListElement { + ws: 7 + } + + ListElement { + ws: 8 + } + + ListElement { + ws: 9 + } + + ListElement { + ws: 10 + } + + } + + Variants { + model: Quickshell.screens + + PanelWindow { + id: bar + + required property var modelData + readonly property bool canExpand: modelData.name === Quickshell.screens[0].name + + screen: modelData + implicitHeight: root.barHeight + exclusiveZone: root.barHeight + aboveWindows: true + + anchors { + top: true + left: true + right: true + } + + Rectangle { + anchors.fill: parent + color: root.bg + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: 1 + color: root.bg2 + } + + Row { + id: taskStrip + + anchors.centerIn: parent + height: 30 + spacing: 6 + + ToggleButton { + visible: bar.canExpand + } + + Repeater { + model: workspaceModel + + delegate: WorkspaceButton { + required property int ws + + workspaceNumber: ws + } + + } + + } + + Row { + id: tray + + anchors.right: parent.right + anchors.rightMargin: 8 + anchors.verticalCenter: parent.verticalCenter + height: 28 + spacing: 5 + + StatusPill { + title: "ntf" + command: root.statusCommand("dunst") + actionName: "dunst" + accent: root.yellow + } + + StatusPill { + title: "wake" + command: root.statusCommand("awake") + actionName: "awake" + accent: root.orange + } + + StatusPill { + title: "wg" + command: root.statusCommand("wg") + actionName: "wg" + accent: root.aqua + } + + StatusPill { + title: "vol" + command: root.statusCommand("volume") + actionName: "volume" + actionArg: "mute" + wheelAction: "volume" + wheelUpArg: "up" + wheelDownArg: "down" + accent: root.green + } + + StatusPill { + title: "kbd" + command: root.statusCommand("keyboard") + actionName: "keyboard" + accent: root.purple + } + + ClockPill { + } + + } + + } + + PopupWindow { + id: drawer + + visible: root.expanded && bar.canExpand + grabFocus: false + anchor.window: bar + anchor.rect.x: Math.max(8, Math.round((bar.width - implicitWidth) / 2)) + anchor.rect.y: root.barHeight + 6 + implicitWidth: Math.max(320, Math.min(760, bar.width - 24)) + implicitHeight: root.drawerHeight + color: "transparent" + + Rectangle { + anchors.fill: parent + radius: 7 + color: root.bg1 + border.width: 1 + border.color: root.bg3 + + RowLayout { + anchors.fill: parent + anchors.margins: 10 + spacing: 10 + + Flow { + Layout.fillWidth: true + Layout.fillHeight: true + spacing: 7 + + StatusPill { + title: "net" + command: root.statusCommand("net") + accent: root.green + } + + StatusPill { + title: "spd" + command: root.statusCommand("netspeed") + accent: root.green + } + + StatusPill { + title: "tmp" + command: root.statusCommand("temp") + accent: root.orange + } + + StatusPill { + title: "cpu" + command: root.statusCommand("cpu") + accent: root.orange + } + + StatusPill { + title: "ram" + command: root.statusCommand("mem") + accent: root.purple + } + + StatusPill { + title: "disk" + command: root.statusCommand("disk") + accent: root.aqua + } + + StatusPill { + title: "upd" + command: root.statusCommand("updates") + actionName: "updates" + interval: 900000 + accent: root.yellow + } + + StatusPill { + title: "disp" + command: root.statusCommand("display") + actionName: "display" + wheelAction: "display-brightness" + wheelUpArg: "up" + wheelDownArg: "down" + accent: root.orange + } + + StatusPill { + title: "mic" + command: root.statusCommand("mic") + actionName: "mic" + accent: root.purple + } + + StatusPill { + title: "mus" + command: root.statusCommand("music") + actionName: "music" + interval: 2000 + accent: root.green + } + + StatusPill { + title: "scratch" + command: root.statusCommand("scratch") + actionName: "scratch" + accent: root.yellow + } + + } + + ColumnLayout { + Layout.preferredWidth: 84 + Layout.fillHeight: true + spacing: 7 + + ActionButton { + label: "shot" + actionName: "screenshot" + accent: root.aqua + } + + ActionButton { + label: "clip" + actionName: "clipboard" + accent: root.purple + } + + ActionButton { + label: "power" + actionName: "power" + accent: root.red + } + + } + + } + + } + + } + + } + + } + + component WorkspaceButton: Rectangle { + id: wsButton + + required property int workspaceNumber + readonly property bool focused: I3.focusedWorkspace !== null && I3.focusedWorkspace.number === workspaceNumber + readonly property bool active: focused || root.pendingWorkspace === workspaceNumber + + Layout.preferredWidth: 30 + Layout.preferredHeight: 28 + width: 30 + height: 28 + radius: 4 + color: active ? root.yellow : (mouse.containsMouse ? root.bg2 : root.bg1) + border.width: 1 + border.color: active ? root.yellow : root.bg2 + + Text { + anchors.centerIn: parent + text: String(wsButton.workspaceNumber) + color: wsButton.active ? root.bg : root.fg1 + font.family: root.fontFamily + font.pixelSize: 13 + font.bold: wsButton.active + } + + MouseArea { + id: mouse + + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onClicked: root.switchWorkspace(wsButton.workspaceNumber) + onWheel: wheel.angleDelta.y > 0 ? I3.dispatch("workspace next") : I3.dispatch("workspace prev") + } + + } + + component ToggleButton: Rectangle { + Layout.preferredHeight: 28 + Layout.preferredWidth: 68 + width: 68 + height: 28 + radius: 5 + color: root.expanded ? root.bg2 : root.bg1 + border.width: 1 + border.color: root.yellow + + Text { + anchors.centerIn: parent + text: root.expanded ? "close" : "start" + color: root.yellow + font.family: root.fontFamily + font.pixelSize: 12 + font.bold: true + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: root.expanded = !root.expanded + } + + } + + component ClockPill: Rectangle { + width: 122 + height: 28 + radius: 5 + color: root.bg1 + border.width: 1 + border.color: root.bg2 + + Text { + anchors.centerIn: parent + text: root.clockText + color: root.fg + font.family: root.fontFamily + font.pixelSize: 12 + font.bold: true + } + + } + + component ActionButton: Rectangle { + id: button + + property string label: "" + property string actionName: "" + property string actionArg: "" + property string accent: root.fg1 + + Layout.preferredWidth: 76 + Layout.preferredHeight: 28 + width: 76 + height: 28 + radius: 5 + color: mouse.pressed ? root.bg3 : (mouse.containsMouse ? root.bg2 : root.bg) + border.width: 1 + border.color: accent + + Text { + id: labelText + + anchors.centerIn: parent + text: button.label + color: button.accent + font.family: root.fontFamily + font.pixelSize: 12 + font.bold: true + } + + MouseArea { + id: mouse + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: root.runAction(button.actionName, button.actionArg) + } + + } + + component StatusPill: Rectangle { + id: pill + + property string title: "" + property var command: [] + property int interval: 2000 + property string value: "..." + property string actionName: "" + property string actionArg: "" + property string wheelAction: "" + property string wheelUpArg: "" + property string wheelDownArg: "" + property string accent: root.fg1 + + function refresh() { + if (pill.command.length > 0) + proc.exec(pill.command); + + } + + function matchesRefresh(actionName) { + return actionName === "" || actionName === pill.actionName || actionName === pill.wheelAction; + } + + Layout.preferredHeight: 28 + Layout.preferredWidth: Math.max(56, label.implicitWidth + 18) + width: Math.max(56, label.implicitWidth + 18) + height: 28 + radius: 5 + color: mouse.pressed ? root.bg3 : (mouse.containsMouse ? root.bg2 : root.bg) + border.width: 1 + border.color: root.bg2 + + Text { + id: label + + anchors.centerIn: parent + text: pill.title + " " + pill.value + color: root.textColor(pill.value, pill.accent) + font.family: root.fontFamily + font.pixelSize: 12 + } + + Process { + id: proc + + command: pill.command + running: true + + stdout: StdioCollector { + onStreamFinished: pill.value = this.text.trim() + } + + } + + Connections { + function onRefreshRequested(actionName) { + if (pill.matchesRefresh(actionName)) + pill.refresh(); + + } + + target: root + } + + Timer { + interval: pill.interval + running: true + repeat: true + onTriggered: pill.refresh() + } + + MouseArea { + id: mouse + + anchors.fill: parent + hoverEnabled: true + cursorShape: pill.actionName !== "" ? Qt.PointingHandCursor : Qt.ArrowCursor + onClicked: { + if (pill.actionName !== "") + root.runAction(pill.actionName, pill.actionArg); + + } + onWheel: { + if (pill.wheelAction !== "") + root.runAction(pill.wheelAction, wheel.angleDelta.y > 0 ? pill.wheelUpArg : pill.wheelDownArg); + + } + } + + } + +} |
