Split out indicators
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
@@ -18,6 +18,11 @@ Item {
|
|||||||
property real fixedHeight: -1
|
property real fixedHeight: -1
|
||||||
property real textRotation: 0
|
property real textRotation: 0
|
||||||
property bool keepSpace: false
|
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 string tooltipText: ""
|
||||||
property var registeredBar: null
|
property var registeredBar: null
|
||||||
|
|
||||||
@@ -46,7 +51,7 @@ Item {
|
|||||||
readonly property real scaledVerticalPadding: Style.spaceReal(verticalPadding)
|
readonly property real scaledVerticalPadding: Style.spaceReal(verticalPadding)
|
||||||
|
|
||||||
visible: text !== "" || keepSpace
|
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))
|
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)
|
implicitHeight: fixedHeight > 0 ? fixedHeight : (vertical ? Math.max(12, label.implicitHeight + scaledVerticalPadding * 2) : barSize)
|
||||||
|
|
||||||
@@ -59,7 +64,7 @@ Item {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
anchors.horizontalCenterOffset: root.vertical ? 0 : -root.scaledRightExtraMargin / 2
|
anchors.horizontalCenterOffset: root.vertical ? 0 : -root.scaledRightExtraMargin / 2
|
||||||
text: root.text
|
text: root.text
|
||||||
color: root.active ? root.activeColor : root.foreground
|
color: root.active && root.useActiveColor ? root.activeColor : root.foreground
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
font.pixelSize: root.fontSize
|
font.pixelSize: root.fontSize
|
||||||
renderType: Text.NativeRendering
|
renderType: Text.NativeRendering
|
||||||
@@ -76,10 +81,23 @@ Item {
|
|||||||
id: mouseArea
|
id: mouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||||
|
enabled: root.interactive
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onEntered: if (root.bar) root.bar.showTooltip(root, root.tooltipText)
|
onEntered: {
|
||||||
onExited: if (root.bar) root.bar.hideTooltip(root)
|
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) }
|
onClicked: function(mouse) { root.triggerPress(mouse.button) }
|
||||||
onWheel: function(wheel) { root.wheelMoved(wheel.angleDelta.y) }
|
onWheel: function(wheel) { root.wheelMoved(wheel.angleDelta.y) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
module qs.Ui
|
module qs.Ui
|
||||||
|
|
||||||
|
BarIndicator 1.0 BarIndicator.qml
|
||||||
BarWidget 1.0 BarWidget.qml
|
BarWidget 1.0 BarWidget.qml
|
||||||
Button 1.0 Button.qml
|
Button 1.0 Button.qml
|
||||||
ButtonGroup 1.0 ButtonGroup.qml
|
ButtonGroup 1.0 ButtonGroup.qml
|
||||||
|
|||||||
+236
-119
@@ -56,18 +56,30 @@ Item {
|
|||||||
Behavior on background { ColorAnimation { duration: 420; easing.type: Easing.InOutCubic } }
|
Behavior on background { ColorAnimation { duration: 420; easing.type: Easing.InOutCubic } }
|
||||||
Behavior on urgent { ColorAnimation { duration: 420; easing.type: Easing.InOutCubic } }
|
Behavior on urgent { ColorAnimation { duration: 420; easing.type: Easing.InOutCubic } }
|
||||||
property bool updateAvailable: false
|
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 bool clockAlt: false
|
||||||
property var tooltipTarget: null
|
property var tooltipTarget: null
|
||||||
property string tooltipText: ""
|
property string tooltipText: ""
|
||||||
property bool tooltipShown: false
|
property bool tooltipShown: false
|
||||||
property var activePopout: null
|
property var activePopout: null
|
||||||
property var clickTargets: []
|
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) {
|
function registerClickTarget(target) {
|
||||||
if (!target || clickTargets.indexOf(target) !== -1) return
|
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
|
// 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
|
// 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,
|
// sections. The drawer's reserved space then sits next to the bar center,
|
||||||
@@ -160,7 +192,10 @@ Item {
|
|||||||
|
|
||||||
function entryId(entry) {
|
function entryId(entry) {
|
||||||
if (typeof entry === "string") return 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 ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +228,6 @@ Item {
|
|||||||
|
|
||||||
function canonicalWidgetId(name) {
|
function canonicalWidgetId(name) {
|
||||||
switch (String(name)) {
|
switch (String(name)) {
|
||||||
case "idle": return "idleInhibitor"
|
|
||||||
case "weatherFlyout": return "weather"
|
case "weatherFlyout": return "weather"
|
||||||
default: return String(name)
|
default: return String(name)
|
||||||
}
|
}
|
||||||
@@ -205,11 +239,7 @@ Item {
|
|||||||
case "workspaces": return workspacesModuleComponent
|
case "workspaces": return workspacesModuleComponent
|
||||||
case "clock": return clockModuleComponent
|
case "clock": return clockModuleComponent
|
||||||
case "update": return updateModuleComponent
|
case "update": return updateModuleComponent
|
||||||
case "voxtype": return voxtypeModuleComponent
|
case "indicators": return indicatorsModuleComponent
|
||||||
case "screenRecording":
|
|
||||||
case "screenrecording": return screenRecordingModuleComponent
|
|
||||||
case "notifications":
|
|
||||||
case "notificationSilencing": return notificationsModuleComponent
|
|
||||||
case "tray": return trayModuleComponent
|
case "tray": return trayModuleComponent
|
||||||
default: return null
|
default: return null
|
||||||
}
|
}
|
||||||
@@ -362,53 +392,11 @@ Item {
|
|||||||
tooltipShown = false
|
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() {
|
function refreshUpdate() {
|
||||||
runProcess(updateProc)
|
runProcess(updateProc)
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshScreenRecording() {
|
|
||||||
runProcess(screenRecordingProc)
|
|
||||||
}
|
|
||||||
|
|
||||||
function refreshIndicators() {
|
function refreshIndicators() {
|
||||||
refreshScreenRecording()
|
|
||||||
runProcess(notificationSilencingProc)
|
|
||||||
indicatorsRefreshRequested()
|
indicatorsRefreshRequested()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,6 +478,15 @@ Item {
|
|||||||
onTriggered: root.tooltipShown = true
|
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
|
// 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,
|
// directory because FileView can't observe a file that doesn't exist yet,
|
||||||
// and the flag is created/removed by `omarchy-toggle-bar`.
|
// and the flag is created/removed by `omarchy-toggle-bar`.
|
||||||
@@ -523,35 +520,6 @@ Item {
|
|||||||
onTriggered: root.refreshUpdate()
|
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 {
|
Timer {
|
||||||
interval: 2000
|
interval: 2000
|
||||||
running: true
|
running: true
|
||||||
@@ -568,7 +536,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function refreshScreenRecording(): void {
|
function refreshScreenRecording(): void {
|
||||||
root.refreshScreenRecording()
|
root.refreshIndicators()
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshIndicators(): void {
|
function refreshIndicators(): void {
|
||||||
@@ -725,9 +693,7 @@ Item {
|
|||||||
Component { id: workspacesModuleComponent; WorkspacesModule {} }
|
Component { id: workspacesModuleComponent; WorkspacesModule {} }
|
||||||
Component { id: clockModuleComponent; ClockModule {} }
|
Component { id: clockModuleComponent; ClockModule {} }
|
||||||
Component { id: updateModuleComponent; UpdateModule {} }
|
Component { id: updateModuleComponent; UpdateModule {} }
|
||||||
Component { id: voxtypeModuleComponent; VoxtypeModule {} }
|
Component { id: indicatorsModuleComponent; IndicatorsModule {} }
|
||||||
Component { id: screenRecordingModuleComponent; ScreenRecordingModule {} }
|
|
||||||
Component { id: notificationsModuleComponent; NotificationsModule {} }
|
|
||||||
Component { id: trayModuleComponent; TrayModule {} }
|
Component { id: trayModuleComponent; TrayModule {} }
|
||||||
|
|
||||||
function findCenterAnchorEntry() {
|
function findCenterAnchorEntry() {
|
||||||
@@ -925,6 +891,10 @@ Item {
|
|||||||
active: !slot.qmlCustom && !slot.registered
|
active: !slot.qmlCustom && !slot.registered
|
||||||
sourceComponent: slot.builtinComponent || (slot.commandCustom ? customCommandModuleComponent : emptyModuleComponent)
|
sourceComponent: slot.builtinComponent || (slot.commandCustom ? customCommandModuleComponent : emptyModuleComponent)
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
onLoaded: {
|
||||||
|
slot.injectProps()
|
||||||
|
Qt.callLater(slot.injectProps)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
@@ -932,7 +902,10 @@ Item {
|
|||||||
active: slot.registered
|
active: slot.registered
|
||||||
sourceComponent: slot.registered ? slot.registryComponent : null
|
sourceComponent: slot.registered ? slot.registryComponent : null
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onLoaded: slot.injectProps()
|
onLoaded: {
|
||||||
|
slot.injectProps()
|
||||||
|
Qt.callLater(slot.injectProps)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
@@ -940,13 +913,17 @@ Item {
|
|||||||
active: slot.qmlCustom
|
active: slot.qmlCustom
|
||||||
source: slot.qmlCustom ? root.customModuleSource(slot.entry) : ""
|
source: slot.qmlCustom ? root.customModuleSource(slot.entry) : ""
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onLoaded: slot.injectProps()
|
onLoaded: {
|
||||||
|
slot.injectProps()
|
||||||
|
Qt.callLater(slot.injectProps)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onActiveItemChanged: Qt.callLater(injectProps)
|
||||||
onModuleSettingsChanged: injectProps()
|
onModuleSettingsChanged: injectProps()
|
||||||
|
|
||||||
function injectProps() {
|
function injectProps() {
|
||||||
var target = registryLoader.item || qmlLoader.item
|
var target = activeItem
|
||||||
if (!target) return
|
if (!target) return
|
||||||
if ("bar" in target) target.bar = root
|
if ("bar" in target) target.bar = root
|
||||||
if ("moduleName" in target) target.moduleName = moduleName
|
if ("moduleName" in target) target.moduleName = moduleName
|
||||||
@@ -1080,10 +1057,174 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component IndicatorModule: ModuleButton {
|
component IndicatorsModule: Item {
|
||||||
fontSize: Style.font.caption
|
id: indicatorsRoot
|
||||||
horizontalMargin: 5
|
|
||||||
verticalPadding: 5
|
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 {
|
component OmarchyModule: ModuleButton {
|
||||||
@@ -1139,30 +1280,6 @@ Item {
|
|||||||
onPressed: function() { root.run("omarchy-launch-floating-terminal-with-presentation omarchy-update") }
|
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 {
|
component TrayModule: Item {
|
||||||
id: trayRoot
|
id: trayRoot
|
||||||
|
|
||||||
|
|||||||
@@ -67,9 +67,9 @@ Example `shell.json` (bar subtree only shown):
|
|||||||
|
|
||||||
### Built-in base modules (in `Bar.qml`)
|
### 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
|
## Orientation
|
||||||
|
|
||||||
|
|||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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() }
|
||||||
|
}
|
||||||
@@ -3,15 +3,10 @@ import Quickshell
|
|||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Ui
|
import qs.Ui
|
||||||
|
|
||||||
BarWidget {
|
BarIndicator {
|
||||||
id: root
|
id: root
|
||||||
moduleName: "idleInhibitor"
|
moduleName: "idleInhibitor"
|
||||||
|
|
||||||
|
|
||||||
property bool active: false
|
|
||||||
|
|
||||||
readonly property string icon: active ? "" : ""
|
|
||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
if (!statusProc.running) statusProc.running = true
|
if (!statusProc.running) statusProc.running = true
|
||||||
}
|
}
|
||||||
@@ -59,17 +54,9 @@ BarWidget {
|
|||||||
onTriggered: root.refresh()
|
onTriggered: root.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
visible: active
|
activeText: ""
|
||||||
implicitWidth: visible ? button.implicitWidth : 0
|
inactiveText: ""
|
||||||
implicitHeight: visible ? button.implicitHeight : 0
|
activeTooltipText: "No lock or screensaver"
|
||||||
|
inactiveTooltipText: "Idle enabled"
|
||||||
WidgetButton {
|
onPressed: function() { root.toggle() }
|
||||||
id: button
|
|
||||||
anchors.fill: parent
|
|
||||||
bar: root.bar
|
|
||||||
text: root.icon
|
|
||||||
active: false
|
|
||||||
tooltipText: root.active ? "No lock or screensaver" : ""
|
|
||||||
onPressed: function() { root.toggle() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,8 +134,7 @@ Item {
|
|||||||
left: [{ id: "omarchy" }, { id: "workspaces" }],
|
left: [{ id: "omarchy" }, { id: "workspaces" }],
|
||||||
center: [
|
center: [
|
||||||
{ id: "calendar", format: "dddd HH:mm", formatAlt: "dd MMMM 'W'ww yyyy", verticalFormat: "HH\n\u2014\nmm" },
|
{ id: "calendar", format: "dddd HH:mm", formatAlt: "dd MMMM 'W'ww yyyy", verticalFormat: "HH\n\u2014\nmm" },
|
||||||
{ id: "weather" }, { id: "update" }, { id: "voxtype" },
|
{ id: "weather" }, { id: "indicators", items: [ "dnd", "stayAwake", "screenrecording", "dictation" ] }, { id: "update" }
|
||||||
{ id: "screenRecording" }, { id: "idleInhibitor" }, { id: "notifications" }
|
|
||||||
],
|
],
|
||||||
right: [
|
right: [
|
||||||
{ id: "tray" }, { id: "bluetoothPanel" }, { id: "networkPanel" },
|
{ id: "tray" }, { id: "bluetoothPanel" }, { id: "networkPanel" },
|
||||||
@@ -298,9 +297,7 @@ Item {
|
|||||||
"workspaces": { name: "Workspaces", description: "Workspace number indicators", category: "Compositor" },
|
"workspaces": { name: "Workspaces", description: "Workspace number indicators", category: "Compositor" },
|
||||||
"clock": { name: "Clock", description: "Date / time text", category: "Time" },
|
"clock": { name: "Clock", description: "Date / time text", category: "Time" },
|
||||||
"update": { name: "Updates", description: "Indicates available system updates", category: "System" },
|
"update": { name: "Updates", description: "Indicates available system updates", category: "System" },
|
||||||
"voxtype": { name: "Voxtype", description: "Voxtype dictation state", category: "Status" },
|
"indicators": { name: "Indicators", description: "Manual state indicators", category: "Status" },
|
||||||
"screenRecording": { name: "Screen recording", description: "Active recording indicator", category: "Status" },
|
|
||||||
"notifications": { name: "DND", description: "Do-not-disturb indicator", category: "Status" },
|
|
||||||
"tray": { name: "System tray", description: "Status notifier items", category: "Status" }
|
"tray": { name: "System tray", description: "Status notifier items", category: "Status" }
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -322,7 +319,6 @@ Item {
|
|||||||
|
|
||||||
function canonicalWidgetId(id) {
|
function canonicalWidgetId(id) {
|
||||||
switch (String(id || "")) {
|
switch (String(id || "")) {
|
||||||
case "idle": return "idleInhibitor"
|
|
||||||
case "weatherFlyout": return "weather"
|
case "weatherFlyout": return "weather"
|
||||||
default: return String(id || "")
|
default: return String(id || "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,20 +27,17 @@
|
|||||||
{
|
{
|
||||||
"id": "weather"
|
"id": "weather"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "indicators",
|
||||||
|
"items": [
|
||||||
|
"dnd",
|
||||||
|
"stayAwake",
|
||||||
|
"screenrecording",
|
||||||
|
"dictation"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "update"
|
"id": "update"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "voxtype"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "screenRecording"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "idleInhibitor"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "notifications"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"right": [
|
"right": [
|
||||||
|
|||||||
Reference in New Issue
Block a user