diff --git a/shell/Ui/BarIndicator.qml b/shell/Ui/BarIndicator.qml new file mode 100644 index 00000000..c71a050e --- /dev/null +++ b/shell/Ui/BarIndicator.qml @@ -0,0 +1,52 @@ +import QtQuick +import qs.Commons + +WidgetButton { + id: root + + property string moduleName: "" + property var settings: ({}) + property string activeText: "" + property string inactiveText: activeText + property string activeTooltipText: "" + property string inactiveTooltipText: activeTooltipText + property string indicatorBlock: "single" + readonly property bool belongsInBlock: indicatorBlock === "active" ? active : (indicatorBlock === "inactive" ? !active : true) + readonly property bool inactiveRevealed: !active && !!bar && bar.revealInactiveIndicators + + function extractData(raw) { + var text = String(raw || "").trim() + if (text === "") return {} + + var lines = text.split("\n") + try { + return JSON.parse(lines[lines.length - 1]) + } catch (error) { + return { text: text } + } + } + + function syncIndicatorOpacity() { + root.opacity = !belongsInBlock ? 0 : (active ? 1 : (inactiveRevealed ? 0.45 : 0)) + } + + Component.onCompleted: syncIndicatorOpacity() + onActiveChanged: syncIndicatorOpacity() + onBarChanged: syncIndicatorOpacity() + onBelongsInBlockChanged: syncIndicatorOpacity() + onInactiveRevealedChanged: syncIndicatorOpacity() + onIndicatorBlockChanged: syncIndicatorOpacity() + + visible: belongsInBlock && (text !== "" || keepSpace) + text: active ? activeText : inactiveText + tooltipText: active ? activeTooltipText : inactiveTooltipText + keepSpace: true + dimmed: !active + concealed: !active && !inactiveRevealed + interactive: belongsInBlock && (active || indicatorBlock === "inactive" || inactiveRevealed) + useActiveColor: false + maintainIndicatorReveal: indicatorBlock === "inactive" + fontSize: Style.font.caption + horizontalMargin: 5 + verticalPadding: 5 +} diff --git a/shell/Ui/WidgetButton.qml b/shell/Ui/WidgetButton.qml index c0fec766..87611876 100644 --- a/shell/Ui/WidgetButton.qml +++ b/shell/Ui/WidgetButton.qml @@ -18,6 +18,11 @@ Item { property real fixedHeight: -1 property real textRotation: 0 property bool keepSpace: false + property bool dimmed: false + property bool concealed: false + property bool interactive: true + property bool useActiveColor: true + property bool maintainIndicatorReveal: false property string tooltipText: "" property var registeredBar: null @@ -46,7 +51,7 @@ Item { readonly property real scaledVerticalPadding: Style.spaceReal(verticalPadding) visible: text !== "" || keepSpace - opacity: text === "" ? 0 : 1 + opacity: text === "" || concealed ? 0 : (dimmed ? 0.45 : 1) implicitWidth: fixedWidth > 0 ? fixedWidth : (vertical ? barSize : Math.max(12, label.implicitWidth + scaledHorizontalMargin * 2 + scaledRightExtraMargin)) implicitHeight: fixedHeight > 0 ? fixedHeight : (vertical ? Math.max(12, label.implicitHeight + scaledVerticalPadding * 2) : barSize) @@ -59,7 +64,7 @@ Item { anchors.centerIn: parent anchors.horizontalCenterOffset: root.vertical ? 0 : -root.scaledRightExtraMargin / 2 text: root.text - color: root.active ? root.activeColor : root.foreground + color: root.active && root.useActiveColor ? root.activeColor : root.foreground font.family: root.fontFamily font.pixelSize: root.fontSize renderType: Text.NativeRendering @@ -76,10 +81,23 @@ Item { id: mouseArea anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton + enabled: root.interactive hoverEnabled: true cursorShape: Qt.PointingHandCursor - onEntered: if (root.bar) root.bar.showTooltip(root, root.tooltipText) - onExited: if (root.bar) root.bar.hideTooltip(root) + onEntered: { + if (root.bar) { + root.bar.showTooltip(root, root.tooltipText) + if (root.maintainIndicatorReveal && root.bar.setIndicatorItemHovered) + root.bar.setIndicatorItemHovered(true) + } + } + onExited: { + if (root.bar) { + root.bar.hideTooltip(root) + if (root.maintainIndicatorReveal && root.bar.setIndicatorItemHovered) + root.bar.setIndicatorItemHovered(false) + } + } onClicked: function(mouse) { root.triggerPress(mouse.button) } onWheel: function(wheel) { root.wheelMoved(wheel.angleDelta.y) } } diff --git a/shell/Ui/qmldir b/shell/Ui/qmldir index 7a10590e..63cf7782 100644 --- a/shell/Ui/qmldir +++ b/shell/Ui/qmldir @@ -1,5 +1,6 @@ module qs.Ui +BarIndicator 1.0 BarIndicator.qml BarWidget 1.0 BarWidget.qml Button 1.0 Button.qml ButtonGroup 1.0 ButtonGroup.qml diff --git a/shell/plugins/bar/Bar.qml b/shell/plugins/bar/Bar.qml index 6685ab9d..452dfa27 100644 --- a/shell/plugins/bar/Bar.qml +++ b/shell/plugins/bar/Bar.qml @@ -56,18 +56,30 @@ Item { Behavior on background { ColorAnimation { duration: 420; easing.type: Easing.InOutCubic } } Behavior on urgent { ColorAnimation { duration: 420; easing.type: Easing.InOutCubic } } property bool updateAvailable: false - property string voxtypeIcon: "" - property string voxtypeClass: "" - property string screenRecordingText: "" - property string screenRecordingTooltip: "" - property string notificationSilencingText: "" - property string notificationSilencingTooltip: "" property bool clockAlt: false property var tooltipTarget: null property string tooltipText: "" property bool tooltipShown: false property var activePopout: null property var clickTargets: [] + property bool indicatorAreaHovered: false + property bool indicatorItemHovered: false + readonly property bool revealInactiveIndicators: indicatorAreaHovered || indicatorItemHovered + + function setIndicatorAreaHovered(hovered) { + indicatorAreaHovered = hovered + if (hovered) indicatorHideTimer.stop() + else indicatorHideTimer.restart() + } + + function setIndicatorItemHovered(hovered) { + if (hovered) { + indicatorItemHovered = true + indicatorHideTimer.stop() + } else { + indicatorHideTimer.restart() + } + } function registerClickTarget(target) { if (!target || clickTargets.indexOf(target) !== -1) return @@ -112,6 +124,26 @@ Item { } } + function indicatorEntriesFromSettings(settings) { + var source = [] + if (settings.items && typeof settings.items.length === "number") source = settings.items + else if (settings.indicators && typeof settings.indicators.length === "number") source = settings.indicators + + var result = [] + for (var i = 0; i < source.length; i++) { + var item = source[i] + if (typeof item !== "string" && item !== null && typeof item === "object") { + try { + item = JSON.parse(JSON.stringify(item)) + } catch (error) { + } + } + var id = entryId(item) + if (id !== "") result.push(item) + } + return result + } + // The tray drawer reveals inward (away from the bar edge). Place it at the // section's inner edge: start of the right section, end of the left/center // sections. The drawer's reserved space then sits next to the bar center, @@ -160,7 +192,10 @@ Item { function entryId(entry) { if (typeof entry === "string") return entry - if (Util.isPlainObject(entry) && entry.id) return String(entry.id) + if (Util.isPlainObject(entry)) { + var id = entry["id"] + if (id !== undefined && id !== null && String(id) !== "") return String(id) + } return "" } @@ -193,7 +228,6 @@ Item { function canonicalWidgetId(name) { switch (String(name)) { - case "idle": return "idleInhibitor" case "weatherFlyout": return "weather" default: return String(name) } @@ -205,11 +239,7 @@ Item { case "workspaces": return workspacesModuleComponent case "clock": return clockModuleComponent case "update": return updateModuleComponent - case "voxtype": return voxtypeModuleComponent - case "screenRecording": - case "screenrecording": return screenRecordingModuleComponent - case "notifications": - case "notificationSilencing": return notificationsModuleComponent + case "indicators": return indicatorsModuleComponent case "tray": return trayModuleComponent default: return null } @@ -362,53 +392,11 @@ Item { tooltipShown = false } - function parseModuleJson(raw) { - var text = String(raw || "").trim() - if (!text) return {} - - var lines = text.split("\n") - try { - return JSON.parse(lines[lines.length - 1]) - } catch (error) { - return { text: text } - } - } - - function updateIndicator(name, raw) { - var data = parseModuleJson(raw) - var text = data.text || "" - var tooltip = data.tooltip || "" - - if (name === "screenRecording") { - screenRecordingText = text - screenRecordingTooltip = tooltip - } else if (name === "notifications") { - notificationSilencingText = text - notificationSilencingTooltip = tooltip - } - } - - function updateVoxtype(raw) { - var data = parseModuleJson(raw) - var state = String(data.alt || data.class || "idle") - - voxtypeClass = state - if (state === "recording") voxtypeIcon = "󰍬" - else if (state === "transcribing") voxtypeIcon = "󰔟" - else voxtypeIcon = "" - } - function refreshUpdate() { runProcess(updateProc) } - function refreshScreenRecording() { - runProcess(screenRecordingProc) - } - function refreshIndicators() { - refreshScreenRecording() - runProcess(notificationSilencingProc) indicatorsRefreshRequested() } @@ -490,6 +478,15 @@ Item { onTriggered: root.tooltipShown = true } + Timer { + id: indicatorHideTimer + interval: 120 + onTriggered: { + if (!root.indicatorAreaHovered) + root.indicatorItemHovered = false + } + } + // Presence of the `bar-off` flag = bar hidden. Watching the parent toggles // directory because FileView can't observe a file that doesn't exist yet, // and the flag is created/removed by `omarchy-toggle-bar`. @@ -523,35 +520,6 @@ Item { onTriggered: root.refreshUpdate() } - Process { - id: voxtypeProc - command: ["bash", "-lc", "omarchy-voxtype-status"] - running: true - stdout: SplitParser { - onRead: function(data) { - root.updateVoxtype(data) - } - } - } - - Process { - id: screenRecordingProc - command: ["bash", "-lc", Util.shellQuote(root.omarchyPath + "/shell/scripts/indicators/screen-recording.sh")] - stdout: StdioCollector { - waitForEnd: true - onStreamFinished: root.updateIndicator("screenRecording", text) - } - } - - Process { - id: notificationSilencingProc - command: ["bash", "-lc", Util.shellQuote(root.omarchyPath + "/shell/scripts/indicators/notification-silencing.sh")] - stdout: StdioCollector { - waitForEnd: true - onStreamFinished: root.updateIndicator("notifications", text) - } - } - Timer { interval: 2000 running: true @@ -568,7 +536,7 @@ Item { } function refreshScreenRecording(): void { - root.refreshScreenRecording() + root.refreshIndicators() } function refreshIndicators(): void { @@ -725,9 +693,7 @@ Item { Component { id: workspacesModuleComponent; WorkspacesModule {} } Component { id: clockModuleComponent; ClockModule {} } Component { id: updateModuleComponent; UpdateModule {} } - Component { id: voxtypeModuleComponent; VoxtypeModule {} } - Component { id: screenRecordingModuleComponent; ScreenRecordingModule {} } - Component { id: notificationsModuleComponent; NotificationsModule {} } + Component { id: indicatorsModuleComponent; IndicatorsModule {} } Component { id: trayModuleComponent; TrayModule {} } function findCenterAnchorEntry() { @@ -925,6 +891,10 @@ Item { active: !slot.qmlCustom && !slot.registered sourceComponent: slot.builtinComponent || (slot.commandCustom ? customCommandModuleComponent : emptyModuleComponent) anchors.fill: parent + onLoaded: { + slot.injectProps() + Qt.callLater(slot.injectProps) + } } Loader { @@ -932,7 +902,10 @@ Item { active: slot.registered sourceComponent: slot.registered ? slot.registryComponent : null anchors.fill: parent - onLoaded: slot.injectProps() + onLoaded: { + slot.injectProps() + Qt.callLater(slot.injectProps) + } } Loader { @@ -940,13 +913,17 @@ Item { active: slot.qmlCustom source: slot.qmlCustom ? root.customModuleSource(slot.entry) : "" anchors.fill: parent - onLoaded: slot.injectProps() + onLoaded: { + slot.injectProps() + Qt.callLater(slot.injectProps) + } } + onActiveItemChanged: Qt.callLater(injectProps) onModuleSettingsChanged: injectProps() function injectProps() { - var target = registryLoader.item || qmlLoader.item + var target = activeItem if (!target) return if ("bar" in target) target.bar = root if ("moduleName" in target) target.moduleName = moduleName @@ -1080,10 +1057,174 @@ Item { } } - component IndicatorModule: ModuleButton { - fontSize: Style.font.caption - horizontalMargin: 5 - verticalPadding: 5 + component IndicatorsModule: Item { + id: indicatorsRoot + + property var bar: root + property string moduleName: "indicators" + property var settings: ({}) + readonly property var indicatorEntries: root.indicatorEntriesFromSettings(settings) + + implicitWidth: root.vertical ? verticalIndicators.implicitWidth : horizontalIndicators.implicitWidth + implicitHeight: root.vertical ? verticalIndicators.implicitHeight : horizontalIndicators.implicitHeight + + Row { + id: horizontalIndicators + + visible: !root.vertical + spacing: 0 + + IndicatorBlock { + indicatorEntries: indicatorsRoot.indicatorEntries + indicatorBlock: "active" + horizontal: true + } + + Item { + id: inactiveHorizontalArea + + implicitWidth: inactiveHorizontalBlock.implicitWidth + implicitHeight: inactiveHorizontalBlock.implicitHeight + width: implicitWidth + height: implicitHeight + + IndicatorBlock { + id: inactiveHorizontalBlock + anchors.fill: parent + indicatorEntries: indicatorsRoot.indicatorEntries + indicatorBlock: "inactive" + horizontal: true + } + + HoverHandler { + onHoveredChanged: root.setIndicatorAreaHovered(hovered) + } + } + } + + Column { + id: verticalIndicators + + visible: root.vertical + spacing: 0 + + IndicatorBlock { + indicatorEntries: indicatorsRoot.indicatorEntries + indicatorBlock: "active" + horizontal: false + } + + Item { + id: inactiveVerticalArea + + implicitWidth: inactiveVerticalBlock.implicitWidth + implicitHeight: inactiveVerticalBlock.implicitHeight + width: implicitWidth + height: implicitHeight + + IndicatorBlock { + id: inactiveVerticalBlock + anchors.fill: parent + indicatorEntries: indicatorsRoot.indicatorEntries + indicatorBlock: "inactive" + horizontal: false + } + + HoverHandler { + onHoveredChanged: root.setIndicatorAreaHovered(hovered) + } + } + } + } + + component IndicatorBlock: Item { + id: indicatorBlockRoot + + property var indicatorEntries: [] + property string indicatorBlock: "active" + property bool horizontal: true + + implicitWidth: blockLoader.item ? blockLoader.item.childrenRect.width : 0 + implicitHeight: blockLoader.item ? blockLoader.item.childrenRect.height : 0 + width: implicitWidth + height: implicitHeight + + Loader { + id: blockLoader + + anchors.fill: parent + sourceComponent: indicatorBlockRoot.horizontal ? horizontalIndicatorBlock : verticalIndicatorBlock + } + + Component { + id: horizontalIndicatorBlock + + Row { + spacing: 0 + + Repeater { + model: indicatorBlockRoot.indicatorEntries + + IndicatorLoader { + required property var modelData + entry: modelData + indicatorBlock: indicatorBlockRoot.indicatorBlock + } + } + } + } + + Component { + id: verticalIndicatorBlock + + Column { + spacing: 0 + + Repeater { + model: indicatorBlockRoot.indicatorEntries + + IndicatorLoader { + required property var modelData + entry: modelData + indicatorBlock: indicatorBlockRoot.indicatorBlock + } + } + } + } + } + + component IndicatorLoader: Item { + id: indicatorSlot + + required property var entry + required property string indicatorBlock + readonly property string indicatorId: root.entryId(entry) + readonly property var indicatorSettings: root.entrySettings(entry) + + implicitWidth: indicatorSource.item && indicatorSource.item.visible ? indicatorSource.item.implicitWidth : 0 + implicitHeight: indicatorSource.item && indicatorSource.item.visible ? indicatorSource.item.implicitHeight : 0 + width: implicitWidth + height: implicitHeight + onIndicatorBlockChanged: injectProps() + onIndicatorSettingsChanged: injectProps() + + Loader { + id: indicatorSource + + anchors.fill: parent + source: indicatorSlot.indicatorId ? Qt.resolvedUrl("indicators/" + indicatorSlot.indicatorId + ".qml") : "" + onLoaded: indicatorSlot.injectProps() + onStatusChanged: if (status === Loader.Error) console.warn("Indicator loader error", indicatorSlot.indicatorId, source) + } + + function injectProps() { + var target = indicatorSource.item + if (!target) return + if ("bar" in target) target.bar = root + if ("moduleName" in target) target.moduleName = indicatorId + if ("settings" in target) target.settings = indicatorSettings + if ("indicatorBlock" in target) target.indicatorBlock = indicatorBlock + } } component OmarchyModule: ModuleButton { @@ -1139,30 +1280,6 @@ Item { onPressed: function() { root.run("omarchy-launch-floating-terminal-with-presentation omarchy-update") } } - component VoxtypeModule: ModuleButton { - text: root.voxtypeIcon - active: root.voxtypeClass === "recording" - tooltipText: root.voxtypeClass - onPressed: function(button) { - if (button === Qt.RightButton) root.run("omarchy-voxtype-config") - else root.run("omarchy-voxtype-model") - } - } - - component ScreenRecordingModule: IndicatorModule { - text: root.screenRecordingText - active: root.screenRecordingText !== "" - tooltipText: root.screenRecordingTooltip - onPressed: function() { root.run("omarchy-capture-screenrecording") } - } - - component NotificationsModule: IndicatorModule { - text: root.notificationSilencingText - active: root.notificationSilencingText !== "" - tooltipText: root.notificationSilencingTooltip - onPressed: function() { root.run("omarchy-toggle-notification-silencing") } - } - component TrayModule: Item { id: trayRoot diff --git a/shell/plugins/bar/README.md b/shell/plugins/bar/README.md index e7212b63..2af1a8d8 100644 --- a/shell/plugins/bar/README.md +++ b/shell/plugins/bar/README.md @@ -67,9 +67,9 @@ Example `shell.json` (bar subtree only shown): ### Built-in base modules (in `Bar.qml`) -`omarchy`, `workspaces`, `clock`, `update`, `voxtype`, `screenRecording`, `notifications`, `tray`. +`omarchy`, `workspaces`, `clock`, `update`, `indicators`, `tray`. -These remain available for compact status/launcher slots. Rich panels such as `powerPanel`, `networkPanel`, and `audioPanel` live in `widgets/` above. +The `indicators` module loads individual bar indicators from `indicators/`, ordered by its `items` array in `shell.json`. Rich panels such as `powerPanel`, `networkPanel`, and `audioPanel` live in `widgets/` above. ## Orientation diff --git a/shell/plugins/bar/indicators/dictation.qml b/shell/plugins/bar/indicators/dictation.qml new file mode 100644 index 00000000..9df740b8 --- /dev/null +++ b/shell/plugins/bar/indicators/dictation.qml @@ -0,0 +1,39 @@ +import QtQuick +import Quickshell.Io +import qs.Ui + +BarIndicator { + id: root + + property string state: "idle" + property string icon: "" + + active: state === "recording" + activeText: icon + inactiveText: "󰍬" + activeTooltipText: state + inactiveTooltipText: "Dictation" + + function update(raw) { + var data = extractData(raw) + + state = String(data.alt || data.class || "idle") + if (state === "recording") icon = "󰍬" + else if (state === "transcribing") icon = "󰔟" + else icon = "" + } + + Process { + command: ["bash", "-lc", "omarchy-voxtype-status"] + running: true + stdout: SplitParser { + onRead: function(data) { root.update(data) } + } + } + + onPressed: function(button) { + if (!root.bar) return + if (button === Qt.RightButton) root.bar.run("omarchy-voxtype-config") + else root.bar.run("omarchy-voxtype-model") + } +} diff --git a/shell/plugins/bar/indicators/dnd.qml b/shell/plugins/bar/indicators/dnd.qml new file mode 100644 index 00000000..ca3903f4 --- /dev/null +++ b/shell/plugins/bar/indicators/dnd.qml @@ -0,0 +1,51 @@ +import QtQuick +import Quickshell.Io +import qs.Commons +import qs.Ui + +BarIndicator { + id: root + + property string statusText: "" + property string statusTooltip: "" + + active: statusText !== "" + activeText: statusText + inactiveText: "󰂛" + activeTooltipText: statusTooltip + inactiveTooltipText: "Do Not Disturb" + + function refresh() { + if (!root.bar || statusProc.running) return + statusProc.command = ["bash", "-lc", Util.shellQuote(root.bar.omarchyPath + "/shell/scripts/indicators/notification-silencing.sh")] + statusProc.running = true + } + + function update(raw) { + var data = extractData(raw) + + statusText = data.text || "" + statusTooltip = data.tooltip || "" + } + + onBarChanged: refresh() + Component.onCompleted: refresh() + + Connections { + target: root.bar + ignoreUnknownSignals: true + function onIndicatorsRefreshRequested() { root.refresh() } + } + + Process { + id: statusProc + stdout: StdioCollector { + waitForEnd: true + onStreamFinished: root.update(text) + } + } + + onPressed: function() { + if (root.bar) root.bar.run("omarchy-toggle-notification-silencing") + } +} diff --git a/shell/plugins/bar/indicators/screenrecording.qml b/shell/plugins/bar/indicators/screenrecording.qml new file mode 100644 index 00000000..a3776ed9 --- /dev/null +++ b/shell/plugins/bar/indicators/screenrecording.qml @@ -0,0 +1,51 @@ +import QtQuick +import Quickshell.Io +import qs.Commons +import qs.Ui + +BarIndicator { + id: root + + property string statusText: "" + property string statusTooltip: "" + + active: statusText !== "" + activeText: statusText + inactiveText: "󰻂" + activeTooltipText: statusTooltip + inactiveTooltipText: "Screen recording" + + function refresh() { + if (!root.bar || statusProc.running) return + statusProc.command = ["bash", "-lc", Util.shellQuote(root.bar.omarchyPath + "/shell/scripts/indicators/screen-recording.sh")] + statusProc.running = true + } + + function update(raw) { + var data = extractData(raw) + + statusText = data.text || "" + statusTooltip = data.tooltip || "" + } + + onBarChanged: refresh() + Component.onCompleted: refresh() + + Connections { + target: root.bar + ignoreUnknownSignals: true + function onIndicatorsRefreshRequested() { root.refresh() } + } + + Process { + id: statusProc + stdout: StdioCollector { + waitForEnd: true + onStreamFinished: root.update(text) + } + } + + onPressed: function() { + if (root.bar) root.bar.run("omarchy-capture-screenrecording") + } +} diff --git a/shell/plugins/bar/indicators/stayAwake.qml b/shell/plugins/bar/indicators/stayAwake.qml new file mode 100644 index 00000000..d67da2f8 --- /dev/null +++ b/shell/plugins/bar/indicators/stayAwake.qml @@ -0,0 +1,56 @@ +import QtQuick +import Quickshell.Io +import qs.Ui + +BarIndicator { + id: root + + activeText: "󰅶" + inactiveText: "󰅶" + activeTooltipText: "No lock or screensaver" + inactiveTooltipText: "Idle enabled" + + function refresh() { + if (!statusProc.running) statusProc.running = true + } + + function toggle() { + if (root.bar) root.bar.run("omarchy-toggle-idle") + refreshTimer.restart() + } + + Component.onCompleted: refresh() + + Connections { + target: root.bar + ignoreUnknownSignals: true + function onIndicatorsRefreshRequested() { root.refresh() } + } + + Process { + id: statusProc + command: ["bash", "-lc", "omarchy-shell idle status 2>/dev/null"] + stdout: StdioCollector { + waitForEnd: true + onStreamFinished: { + var data = root.extractData(text) + root.active = data && data.enabled === false + } + } + } + + Timer { + id: refreshTimer + interval: 1500 + onTriggered: root.refresh() + } + + Timer { + interval: 5000 + running: true + repeat: true + onTriggered: root.refresh() + } + + onPressed: function() { root.toggle() } +} diff --git a/shell/plugins/bar/widgets/idleInhibitor.qml b/shell/plugins/bar/widgets/idleInhibitor.qml index 558d9e2c..53e095e8 100644 --- a/shell/plugins/bar/widgets/idleInhibitor.qml +++ b/shell/plugins/bar/widgets/idleInhibitor.qml @@ -3,15 +3,10 @@ import Quickshell import Quickshell.Io import qs.Ui -BarWidget { +BarIndicator { id: root moduleName: "idleInhibitor" - - property bool active: false - - readonly property string icon: active ? "󰅶" : "" - function refresh() { if (!statusProc.running) statusProc.running = true } @@ -59,17 +54,9 @@ BarWidget { onTriggered: root.refresh() } - visible: active - implicitWidth: visible ? button.implicitWidth : 0 - implicitHeight: visible ? button.implicitHeight : 0 - - WidgetButton { - id: button - anchors.fill: parent - bar: root.bar - text: root.icon - active: false - tooltipText: root.active ? "No lock or screensaver" : "" - onPressed: function() { root.toggle() } - } + activeText: "󰅶" + inactiveText: "󰅶" + activeTooltipText: "No lock or screensaver" + inactiveTooltipText: "Idle enabled" + onPressed: function() { root.toggle() } } diff --git a/shell/plugins/settings/SettingsPanel.qml b/shell/plugins/settings/SettingsPanel.qml index 30d1760f..39121f3b 100644 --- a/shell/plugins/settings/SettingsPanel.qml +++ b/shell/plugins/settings/SettingsPanel.qml @@ -134,8 +134,7 @@ Item { left: [{ id: "omarchy" }, { id: "workspaces" }], center: [ { id: "calendar", format: "dddd HH:mm", formatAlt: "dd MMMM 'W'ww yyyy", verticalFormat: "HH\n\u2014\nmm" }, - { id: "weather" }, { id: "update" }, { id: "voxtype" }, - { id: "screenRecording" }, { id: "idleInhibitor" }, { id: "notifications" } + { id: "weather" }, { id: "indicators", items: [ "dnd", "stayAwake", "screenrecording", "dictation" ] }, { id: "update" } ], right: [ { id: "tray" }, { id: "bluetoothPanel" }, { id: "networkPanel" }, @@ -298,9 +297,7 @@ Item { "workspaces": { name: "Workspaces", description: "Workspace number indicators", category: "Compositor" }, "clock": { name: "Clock", description: "Date / time text", category: "Time" }, "update": { name: "Updates", description: "Indicates available system updates", category: "System" }, - "voxtype": { name: "Voxtype", description: "Voxtype dictation state", category: "Status" }, - "screenRecording": { name: "Screen recording", description: "Active recording indicator", category: "Status" }, - "notifications": { name: "DND", description: "Do-not-disturb indicator", category: "Status" }, + "indicators": { name: "Indicators", description: "Manual state indicators", category: "Status" }, "tray": { name: "System tray", description: "Status notifier items", category: "Status" } }) @@ -322,7 +319,6 @@ Item { function canonicalWidgetId(id) { switch (String(id || "")) { - case "idle": return "idleInhibitor" case "weatherFlyout": return "weather" default: return String(id || "") } diff --git a/shell/shell-defaults.json b/shell/shell-defaults.json index d06612b5..0455eb22 100644 --- a/shell/shell-defaults.json +++ b/shell/shell-defaults.json @@ -27,20 +27,17 @@ { "id": "weather" }, + { + "id": "indicators", + "items": [ + "dnd", + "stayAwake", + "screenrecording", + "dictation" + ] + }, { "id": "update" - }, - { - "id": "voxtype" - }, - { - "id": "screenRecording" - }, - { - "id": "idleInhibitor" - }, - { - "id": "notifications" } ], "right": [