mirror of
https://github.com/arthur-pbty/arthur-os.git
synced 2026-08-01 20:28:16 +02:00
Extract panels out into their own top-level concern
This commit is contained in:
@@ -53,11 +53,11 @@ o.bind("SUPER + CTRL + ALT + T", "Show time", "omarchy-notification-time")
|
||||
o.bind("SUPER + CTRL + ALT + B", "Show battery remaining", "omarchy-notification-battery")
|
||||
o.bind("SUPER + CTRL + ALT + W", "Show weather", "omarchy-notification-weather")
|
||||
|
||||
o.bind("SUPER + CTRL + A", "Audio panel", "omarchy-shell audioPanel toggle")
|
||||
o.bind("SUPER + CTRL + B", "Bluetooth panel", "omarchy-shell bluetoothPanel toggle")
|
||||
o.bind("SUPER + CTRL + D", "Display panel", "omarchy-shell monitorPanel toggle")
|
||||
o.bind("SUPER + CTRL + W", "Network panel", "omarchy-shell networkPanel toggle")
|
||||
o.bind("SUPER + CTRL + P", "Power panel", "omarchy-shell powerPanel toggle")
|
||||
o.bind("SUPER + CTRL + A", "Audio panel", "omarchy-shell panels.audio toggle")
|
||||
o.bind("SUPER + CTRL + B", "Bluetooth panel", "omarchy-shell panels.bluetooth toggle")
|
||||
o.bind("SUPER + CTRL + D", "Display panel", "omarchy-shell panels.monitor toggle")
|
||||
o.bind("SUPER + CTRL + W", "Network panel", "omarchy-shell panels.network toggle")
|
||||
o.bind("SUPER + CTRL + P", "Power panel", "omarchy-shell panels.power toggle")
|
||||
o.bind("SUPER + CTRL + T", "Activity", { tui = "btop" })
|
||||
|
||||
o.bind("SUPER + CTRL + X", "Toggle dictation", "voxtype record toggle")
|
||||
|
||||
@@ -7,7 +7,7 @@ sudo sed -i 's/^#\?AutoEnable=.*/AutoEnable=false/' /etc/bluetooth/main.conf
|
||||
mkdir -p ~/.config/wireplumber/wireplumber.conf.d/
|
||||
cp "$OMARCHY_PATH/default/wireplumber/wireplumber.conf.d/bluetooth-a2dp-autoconnect.conf" ~/.config/wireplumber/wireplumber.conf.d/
|
||||
|
||||
# Quickshell.Bluetooth has no Agent API, so the omarchy-shell bluetoothPanel
|
||||
# Quickshell.Bluetooth has no Agent API, so the omarchy-shell panels.bluetooth
|
||||
# can't answer the auth prompts bluez issues during pair(). bt-agent registers
|
||||
# a NoInputNoOutput agent on the system bus so pair() actually completes.
|
||||
mkdir -p ~/.config/systemd/user/
|
||||
|
||||
@@ -8,7 +8,7 @@ notify_update() {
|
||||
notify_wifi() {
|
||||
(
|
||||
action=$(notify-send -a omarchy-action -u critical --hint=string:omarchy-glyph: "Click to Setup Wi-Fi" -A "default=Setup")
|
||||
[[ $action == "default" ]] && omarchy-shell networkPanel toggle
|
||||
[[ $action == "default" ]] && omarchy-shell panels.network toggle
|
||||
) >/dev/null 2>&1 &
|
||||
}
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ PanelWindow {
|
||||
readonly property var anchorWindow: anchorItem ? anchorItem.QsWindow.window : null
|
||||
readonly property string barPos: bar ? bar.position : "top"
|
||||
|
||||
function closePopout() {
|
||||
if (owner && "closePopout" in owner) owner.closePopout()
|
||||
function close() {
|
||||
if (owner && "close" in owner) owner.close()
|
||||
else root.open = false
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ PanelWindow {
|
||||
onExited: hoveringBar = false
|
||||
onClicked: function(mouse) {
|
||||
if (inBarRegion(mouse.x, mouse.y) && forwardBarClick(mouse.x, mouse.y, mouse.button)) return
|
||||
root.closePopout()
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
|
||||
// Base item for shell panels. Panels are not bar widgets, but the bar may host
|
||||
// or toggle them and injects the same ambient context while doing so. The base
|
||||
// owns the shared IPC-backed open/close lifecycle; panel implementations own
|
||||
// their button behavior, keyboard navigation, and content.
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property QtObject bar: null
|
||||
property string moduleName: ""
|
||||
property var settings: ({})
|
||||
property string ipcTarget: ""
|
||||
property bool manageIpc: true
|
||||
property alias controller: panelController
|
||||
|
||||
readonly property bool opened: panelController.open
|
||||
|
||||
function open() { panelController.show() }
|
||||
function close() { panelController.hide() }
|
||||
function toggle() { opened ? close() : open() }
|
||||
|
||||
PanelController {
|
||||
id: panelController
|
||||
}
|
||||
|
||||
property IpcHandler _ipc: manageIpc ? ipcComponent.createObject(root) : null
|
||||
|
||||
property Component ipcComponent: Component {
|
||||
IpcHandler {
|
||||
target: root.ipcTarget
|
||||
function open(): void { root.open() }
|
||||
function close(): void { root.close() }
|
||||
function show(): void { root.open() }
|
||||
function hide(): void { root.close() }
|
||||
function toggle(): void { root.toggle() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,16 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
// Owns the open/close lifecycle for a bar panel widget. Wraps the
|
||||
// repetitive popupOpen + closePopout + toggle/show/hide IpcHandler triplet
|
||||
// so each panel just declares one of these and binds its WidgetButton +
|
||||
// KeyboardPanel to the exposed `open` property.
|
||||
// Stores the open state for a shell panel. Panel owns the public lifecycle
|
||||
// methods and IPC wiring; this object only keeps state separate from the
|
||||
// panel implementation's own properties.
|
||||
//
|
||||
// Usage:
|
||||
// PanelController { id: ctrl; ipcTarget: "audioPanel" }
|
||||
//
|
||||
// WidgetButton { onPressed: ctrl.toggle() }
|
||||
// KeyboardPanel { open: ctrl.open; owner: ctrl; focusTarget: keyCatcher }
|
||||
//
|
||||
// The bar popout coordinator uses `owner` as a registry key, so each
|
||||
// PanelController instance doubles as that key. KeyboardPanel calls
|
||||
// `owner.closePopout()` when another panel grabs the slot.
|
||||
//
|
||||
// Set `manageIpc: false` when the panel needs to declare its own IpcHandler
|
||||
// for additional methods on the same target (monitorPanel adds brightness +
|
||||
// state). Quickshell only honors one IpcHandler per target, so the panel's
|
||||
// handler must then also delegate toggle/show/hide to this controller.
|
||||
// PanelController { id: panelController }
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
// IPC target name. The bar pairs this with the bar widget's filename so a
|
||||
// Hyprland keybind (`omarchy-shell <target> toggle`) summons the panel.
|
||||
property string ipcTarget: ""
|
||||
property bool manageIpc: true
|
||||
|
||||
property bool open: false
|
||||
|
||||
function toggle() { open = !open }
|
||||
function show() { if (!open) open = true }
|
||||
function hide() { open = false }
|
||||
function closePopout() { open = false }
|
||||
|
||||
property IpcHandler _ipc: manageIpc ? ipcComponent.createObject(root) : null
|
||||
|
||||
property Component ipcComponent: Component {
|
||||
IpcHandler {
|
||||
target: root.ipcTarget
|
||||
function toggle(): void { root.toggle() }
|
||||
function show(): void { root.show() }
|
||||
function hide(): void { root.hide() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import QtQuick
|
||||
// anchors.fill: parent
|
||||
// onMoveRequested: function(dx, dy) { root.moveCursor(dx, dy) }
|
||||
// onActivateRequested: root.activateCursor()
|
||||
// onCloseRequested: root.closePopout()
|
||||
// onCloseRequested: root.close()
|
||||
// onDeleteRequested: root.deleteSelected()
|
||||
// onTextKey: function(t) { if (t === "r") root.refresh() }
|
||||
//
|
||||
|
||||
@@ -55,8 +55,8 @@ PopupWindow {
|
||||
return Math.round(Math.min(desired, maxHeight))
|
||||
}
|
||||
|
||||
function closePopout() {
|
||||
if (owner && "closePopout" in owner) owner.closePopout()
|
||||
function close() {
|
||||
if (owner && "close" in owner) owner.close()
|
||||
else root.open = false
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ PopupWindow {
|
||||
HyprlandFocusGrab {
|
||||
active: root.open && root.triggerMode === "click"
|
||||
windows: root.anchorWindow ? [root, root.anchorWindow] : [root]
|
||||
onCleared: root.closePopout()
|
||||
onCleared: root.close()
|
||||
}
|
||||
|
||||
anchor {
|
||||
|
||||
@@ -8,6 +8,7 @@ CursorSurface 1.0 CursorSurface.qml
|
||||
Dropdown 1.0 Dropdown.qml
|
||||
KeyboardPanel 1.0 KeyboardPanel.qml
|
||||
NumberField 1.0 NumberField.qml
|
||||
Panel 1.0 Panel.qml
|
||||
PanelActionButton 1.0 PanelActionButton.qml
|
||||
PanelController 1.0 PanelController.qml
|
||||
PanelKeyCatcher 1.0 PanelKeyCatcher.qml
|
||||
|
||||
+13
-11
@@ -95,7 +95,7 @@ Item {
|
||||
|
||||
function requestPopout(owner) {
|
||||
if (activePopout === owner) return
|
||||
if (activePopout && "closePopout" in activePopout) activePopout.closePopout()
|
||||
if (activePopout && "close" in activePopout) activePopout.close()
|
||||
activePopout = owner
|
||||
}
|
||||
|
||||
@@ -277,16 +277,16 @@ Item {
|
||||
return source ? Util.fileUrl(source) : ""
|
||||
}
|
||||
|
||||
// First-party widgets are registered with the BarWidgetRegistry at startup.
|
||||
// Each entry maps a widget id to its display metadata; the QML source lives
|
||||
// at widgets/<id>.qml and is loaded asynchronously via Qt.createComponent.
|
||||
// First-party modules are registered with the BarWidgetRegistry at startup.
|
||||
// Each entry maps a module id to its display metadata; the QML source is
|
||||
// loaded asynchronously via Qt.createComponent.
|
||||
readonly property var firstPartyWidgetMetadata: ({
|
||||
"media": { displayName: "Media", description: "MPRIS now-playing with playback controls", category: "Media", allowMultiple: false },
|
||||
"audioPanel": { displayName: "Audio", description: "Volume slider, output picker, per-app mixer", category: "Audio", allowMultiple: false },
|
||||
"monitorPanel": { displayName: "Display", description: "Brightness slider and laptop display controls", category: "System", allowMultiple: false },
|
||||
"networkPanel": { displayName: "Network", description: "Wi-Fi list and connection state", category: "Network", allowMultiple: false },
|
||||
"powerPanel": { displayName: "Power", description: "Battery, power profile, and system stats", category: "System", allowMultiple: false },
|
||||
"bluetoothPanel": { displayName: "Bluetooth", description: "Bluetooth device list with connect/disconnect", category: "Network", allowMultiple: false },
|
||||
"audioPanel": { displayName: "Audio", description: "Volume slider, output picker, per-app mixer", category: "Audio", allowMultiple: false, sourceDir: "../panels", sourceName: "Audio" },
|
||||
"monitorPanel": { displayName: "Display", description: "Brightness slider and laptop display controls", category: "System", allowMultiple: false, sourceDir: "../panels", sourceName: "Monitor" },
|
||||
"networkPanel": { displayName: "Network", description: "Wi-Fi list and connection state", category: "Network", allowMultiple: false, sourceDir: "../panels", sourceName: "Network" },
|
||||
"powerPanel": { displayName: "Power", description: "Battery, power profile, and system stats", category: "System", allowMultiple: false, sourceDir: "../panels", sourceName: "Power" },
|
||||
"bluetoothPanel": { displayName: "Bluetooth", description: "Bluetooth device list with connect/disconnect", category: "Network", allowMultiple: false, sourceDir: "../panels", sourceName: "Bluetooth" },
|
||||
"calendar": { displayName: "Calendar", description: "Clock with month-grid popup", category: "Time", allowMultiple: false, settingsForm: "calendarSettings" },
|
||||
"notificationCenter": { displayName: "Notification center", description: "Recent notifications + DND", category: "Status", allowMultiple: false },
|
||||
"systemStats": { displayName: "System stats", description: "CPU icon — hover for graphs, click to open btop", category: "System", allowMultiple: false },
|
||||
@@ -316,8 +316,10 @@ Item {
|
||||
}
|
||||
|
||||
function registerOneFirstPartyWidget(id) {
|
||||
var url = Qt.resolvedUrl("widgets/" + id + ".qml")
|
||||
var meta = firstPartyWidgetMetadata[id] || {}
|
||||
var sourceDir = meta.sourceDir || "widgets"
|
||||
var sourceName = meta.sourceName || id
|
||||
var url = Qt.resolvedUrl(sourceDir + "/" + sourceName + ".qml")
|
||||
var enrichedMeta = {
|
||||
displayName: meta.displayName || id,
|
||||
description: meta.description || "",
|
||||
@@ -1285,7 +1287,7 @@ Item {
|
||||
|
||||
property bool expanded: false
|
||||
property bool managePopupOpen: false
|
||||
function closePopout() { managePopupOpen = false }
|
||||
function close() { managePopupOpen = false }
|
||||
|
||||
// Re-resolve the tray's own entry settings whenever the bar layout reloads.
|
||||
readonly property var trayEntry: {
|
||||
|
||||
+19
-10
@@ -7,7 +7,8 @@ the shell for its whole session.
|
||||
|
||||
- `manifest.json` declares the plugin (`id: omarchy.bar`, `kind: bar`) and points at `Bar.qml` as the entry point.
|
||||
- `Bar.qml` is Omarchy-owned bar engine code, loaded by the omarchy-shell host. Users should not edit it directly.
|
||||
- `widgets/` holds first-party widgets — modular, interactive components shipped with Omarchy.
|
||||
- `widgets/` holds first-party bar widgets.
|
||||
- `../panels/` holds first-party panels that the bar can toggle by id.
|
||||
- The bar receives its config from the host shell as a `barConfig` property; the host loads it from `~/.config/omarchy/shell.json` (or `shell-defaults.json` when the user has no file).
|
||||
- `omarchy-style-bar-position` updates only the user shell.json file.
|
||||
|
||||
@@ -49,15 +50,11 @@ Example `shell.json` (bar subtree only shown):
|
||||
|
||||
## Module catalogue
|
||||
|
||||
### First-party interactive widgets (in `widgets/`)
|
||||
### First-party interactive widgets
|
||||
|
||||
| Name | What it does | Interactions |
|
||||
|---|---|---|
|
||||
| `media` | MPRIS now-playing — scrolling track + artist, cover-art popup | left = play/pause · middle = next · scroll = prev/next · right = popup |
|
||||
| `audioPanel` | Volume icon + popup with master slider, output-device picker, per-app mixer | left = popup · right = mute · middle = popup · scroll = volume |
|
||||
| `networkPanel` | Wi-Fi/Ethernet icon + popup with Wi-Fi scan, signal, connect, DNS provider selection | left = popup · right = nmtui |
|
||||
| `powerPanel` | Battery/AC icon + popup with battery stats, power profiles, and system info | left = popup |
|
||||
| `bluetoothPanel` | Bluetooth icon + popup with device list, connect/disconnect, battery | left = popup · right = toggle radio · middle = bluetoothctl TUI |
|
||||
| `calendar` | Clock + popup with month-grid calendar | left = popup · right = tz selector |
|
||||
| `notificationCenter` | Bell with badge + popup with recent notifications, DND toggle | left = popup · right = toggle DND |
|
||||
| `systemStats` | Inline CPU + memory sparklines, popup with detail | left = popup · right = terminal |
|
||||
@@ -65,11 +62,21 @@ Example `shell.json` (bar subtree only shown):
|
||||
| `idleInhibitor` | Coffee-cup that toggles `omarchy-toggle-idle` | left = toggle |
|
||||
| `microphone` | Mic icon + scroll volume | left = mute toggle · middle = audio panel · scroll = source volume |
|
||||
|
||||
### First-party panels (in `../panels/`)
|
||||
|
||||
| Name | What it does | Interactions |
|
||||
|---|---|---|
|
||||
| `panels.audio` | Volume icon + popup with master slider, output-device picker, per-app mixer | left = popup · right = mute · middle = popup · scroll = volume |
|
||||
| `panels.network` | Wi-Fi/Ethernet icon + popup with Wi-Fi scan, signal, connect, DNS provider selection | left = popup · right = nmtui |
|
||||
| `panels.power` | Battery/AC icon + popup with battery stats, power profiles, and system info | left = popup |
|
||||
| `panels.bluetooth` | Bluetooth icon + popup with device list, connect/disconnect, battery | left = popup · right = toggle radio · middle = bluetoothctl TUI |
|
||||
| `panels.monitor` | Brightness and laptop display controls | left = popup |
|
||||
|
||||
### Built-in base modules (in `Bar.qml`)
|
||||
|
||||
`omarchy`, `workspaces`, `clock`, `update`, `indicators`, `tray`.
|
||||
|
||||
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.
|
||||
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 `../panels/` above.
|
||||
|
||||
## Orientation
|
||||
|
||||
@@ -162,9 +169,11 @@ Widgets receive `bar` (the shell root), `moduleName` (string), and `settings` (o
|
||||
- `bar.showTooltip(target, text)` / `bar.hideTooltip(target)` — shared tooltip popup
|
||||
- `bar.requestPopout(owner)` / `bar.releasePopout(owner)` — one-popup-at-a-time coordinator
|
||||
|
||||
First-party widgets live in `widgets/<name>.qml` and are picked up by the
|
||||
shell's `BarWidgetRegistry` at startup; reference one by `id` in any
|
||||
layout list.
|
||||
First-party bar widgets live in `widgets/<name>.qml`; first-party panels
|
||||
live in `../panels/<name>.qml` and expose IPC targets such as
|
||||
`panels.audio`. The compatibility bar layout ids remain `audioPanel`,
|
||||
`networkPanel`, and so on, and are picked up by the shell's
|
||||
`BarWidgetRegistry` at startup; reference one by `id` in any layout list.
|
||||
|
||||
Third-party widgets ship as separate plugins under
|
||||
`~/.config/omarchy/plugins/<plugin-id>/` with their own `manifest.json`
|
||||
|
||||
@@ -13,7 +13,7 @@ BarWidget {
|
||||
property date viewMonth: new Date()
|
||||
property bool popupOpen: false
|
||||
|
||||
function closePopout() { popupOpen = false }
|
||||
function close() { popupOpen = false }
|
||||
|
||||
function setting(name, fallback) {
|
||||
var value = settings ? settings[name] : undefined
|
||||
|
||||
@@ -33,7 +33,7 @@ BarWidget {
|
||||
|
||||
property bool popupOpen: false
|
||||
|
||||
function closePopout() { popupOpen = false }
|
||||
function close() { popupOpen = false }
|
||||
property real maxLabelWidth: 180
|
||||
|
||||
visible: hasMedia
|
||||
|
||||
@@ -42,7 +42,7 @@ BarWidget {
|
||||
active: root.inUse
|
||||
tooltipText: root.muted ? "Microphone muted" : (root.inUse ? "Microphone in use" : "Microphone live")
|
||||
onPressed: function(b) {
|
||||
if (b === Qt.MiddleButton) root.bar.run("omarchy-shell audioPanel toggle")
|
||||
if (b === Qt.MiddleButton) root.bar.run("omarchy-shell panels.audio toggle")
|
||||
else root.toggleMute()
|
||||
}
|
||||
onWheelMoved: function(delta) {
|
||||
|
||||
@@ -10,10 +10,10 @@ BarWidget {
|
||||
|
||||
|
||||
property bool popupOpen: false
|
||||
function closePopout() { popupOpen = false }
|
||||
function close() { popupOpen = false }
|
||||
|
||||
// Always default to the pending tab when there's anything unseen, no
|
||||
// matter how the popup was opened (click, keybind/IPC, or the closePopout
|
||||
// matter how the popup was opened (click, keybind/IPC, or the close
|
||||
// path). Keeps the spec from drifting based on the user's last manual
|
||||
// tab selection.
|
||||
onPopupOpenChanged: {
|
||||
|
||||
@@ -19,7 +19,7 @@ BarWidget {
|
||||
|
||||
property bool popupOpen: false
|
||||
|
||||
function closePopout() { popupOpen = false }
|
||||
function close() { popupOpen = false }
|
||||
|
||||
readonly property int historyLimit: 30
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ BarWidget {
|
||||
|
||||
|
||||
property bool popupOpen: false
|
||||
function closePopout() { popupOpen = false }
|
||||
function close() { popupOpen = false }
|
||||
|
||||
function showPopup() {
|
||||
root.popupOpen = !root.popupOpen
|
||||
|
||||
@@ -407,7 +407,7 @@ Item {
|
||||
color: Qt.darker(root.foreground, 1.4)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
text: "Single cursor. Most reusable panel primitives expose hasCursor: bool and emit hovered(bool); composed rows (including sliders) wrap their content in CursorSurface. The panel root owns cursorActive + focusSection + selectedIndex; each element binds hasCursor: root.cursorActive && root.focusSection === 'X' && root.selectedIndex === N, and onHovered flips cursorActive on while updating the same state. No initial highlight, then one highlight on screen once the keyboard or mouse enters. See plugins/bar/widgets/audioPanel.qml for the canonical recipe."
|
||||
text: "Single cursor. Most reusable panel primitives expose hasCursor: bool and emit hovered(bool); composed rows (including sliders) wrap their content in CursorSurface. The panel root owns cursorActive + focusSection + selectedIndex; each element binds hasCursor: root.cursorActive && root.focusSection === 'X' && root.selectedIndex === N, and onHovered flips cursorActive on while updating the same state. No initial highlight, then one highlight on screen once the keyboard or mouse enters. See plugins/panels/Audio.qml for the canonical recipe."
|
||||
}
|
||||
Text {
|
||||
width: parent.width
|
||||
|
||||
@@ -7,14 +7,10 @@ import Quickshell.Services.Pipewire
|
||||
import qs.Ui
|
||||
import qs.Commons
|
||||
|
||||
BarWidget {
|
||||
Panel {
|
||||
id: root
|
||||
moduleName: "audioPanel"
|
||||
|
||||
|
||||
PanelController { id: ctrl; ipcTarget: "audioPanel" }
|
||||
readonly property bool popupOpen: ctrl.open
|
||||
function closePopout() { ctrl.hide() }
|
||||
ipcTarget: "panels.audio"
|
||||
|
||||
readonly property var sink: Pipewire.defaultAudioSink
|
||||
readonly property var source: Pipewire.defaultAudioSource
|
||||
@@ -214,8 +210,8 @@ BarWidget {
|
||||
}
|
||||
}
|
||||
|
||||
onPopupOpenChanged: {
|
||||
if (popupOpen) {
|
||||
onOpenedChanged: {
|
||||
if (opened) {
|
||||
focusSection = "output"
|
||||
selectedIndex = -1 // first keyboard cursor reveal starts on the output slider
|
||||
cursorActive = false
|
||||
@@ -546,7 +542,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
|
||||
Timer {
|
||||
interval: 5000
|
||||
running: root.popupOpen
|
||||
running: root.opened
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: if (!sinkAvailabilityProc.running) sinkAvailabilityProc.running = true
|
||||
@@ -560,7 +556,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
fontSize: Style.font.body
|
||||
onPressed: function(b) {
|
||||
if (b === Qt.RightButton) root.toggleOutputMute()
|
||||
else ctrl.toggle()
|
||||
else root.toggle()
|
||||
}
|
||||
|
||||
onWheelMoved: function(delta) {
|
||||
@@ -574,7 +570,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
anchorItem: button
|
||||
owner: ctrl
|
||||
bar: root.bar
|
||||
open: ctrl.open
|
||||
open: root.opened
|
||||
focusTarget: keyCatcher
|
||||
contentWidth: panel.fittedContentWidth(Style.space(370))
|
||||
contentHeight: panel.fittedContentHeight(panelColumn.implicitHeight, Style.space(560))
|
||||
@@ -588,7 +584,7 @@ for block in re.split(r"(?m)^Sink #", sys.stdin.read())[1:]:
|
||||
else if (dx !== 0) root.adjustVolume(dx * 0.05)
|
||||
}
|
||||
onActivateRequested: if (root.cursorActive) root.activateCursor()
|
||||
onCloseRequested: root.closePopout()
|
||||
onCloseRequested: root.close()
|
||||
onTextKey: function(t) {
|
||||
// 'm' mutes whatever the cursor is on: focused section's slider
|
||||
// for output/input, the focused stream for streams.
|
||||
@@ -6,13 +6,10 @@ import Quickshell.Bluetooth
|
||||
import qs.Ui
|
||||
import qs.Commons
|
||||
|
||||
BarWidget {
|
||||
Panel {
|
||||
id: root
|
||||
moduleName: "bluetoothPanel"
|
||||
|
||||
|
||||
PanelController { id: ctrl; ipcTarget: "bluetoothPanel" }
|
||||
readonly property bool popupOpen: ctrl.open
|
||||
ipcTarget: "panels.bluetooth"
|
||||
|
||||
// Address -> true while we are waiting for a click-initiated pair to land
|
||||
// so we can chain trust + connect at root scope. Doing this in the row's
|
||||
@@ -20,8 +17,6 @@ BarWidget {
|
||||
// moment `paired` flips, before the row's handler reliably fires.
|
||||
property var pendingPairAddresses: ({})
|
||||
|
||||
function closePopout() { ctrl.hide() }
|
||||
|
||||
readonly property var adapter: Bluetooth.defaultAdapter
|
||||
readonly property var devices: Bluetooth.devices ? Bluetooth.devices.values : []
|
||||
|
||||
@@ -219,8 +214,8 @@ BarWidget {
|
||||
else if (dev.forget) dev.forget()
|
||||
}
|
||||
|
||||
onPopupOpenChanged: {
|
||||
if (popupOpen) {
|
||||
onOpenedChanged: {
|
||||
if (opened) {
|
||||
if (adapter && adapter.enabled && !adapter.discovering) adapter.discovering = true
|
||||
if (knownDevices.length > 0) { focusSection = "known"; selectedIndex = 0 }
|
||||
else { focusSection = "header"; selectedIndex = 1 }
|
||||
@@ -303,7 +298,7 @@ BarWidget {
|
||||
Connections {
|
||||
target: root.adapter || null
|
||||
function onEnabledChanged() {
|
||||
if (root.popupOpen && root.adapter && root.adapter.enabled && !root.adapter.discovering)
|
||||
if (root.opened && root.adapter && root.adapter.enabled && !root.adapter.discovering)
|
||||
root.adapter.discovering = true
|
||||
}
|
||||
}
|
||||
@@ -341,7 +336,7 @@ BarWidget {
|
||||
onPressed: function(b) {
|
||||
if (b === Qt.RightButton && root.adapter) root.adapter.enabled = !root.adapter.enabled
|
||||
else if (b === Qt.MiddleButton) root.bar.run("omarchy-launch-bluetooth")
|
||||
else ctrl.toggle()
|
||||
else root.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,7 +345,7 @@ BarWidget {
|
||||
anchorItem: button
|
||||
owner: ctrl
|
||||
bar: root.bar
|
||||
open: ctrl.open
|
||||
open: root.opened
|
||||
focusTarget: keyCatcher
|
||||
contentWidth: panel.fittedContentWidth(Style.space(320))
|
||||
contentHeight: panel.fittedContentHeight(column.implicitHeight)
|
||||
@@ -364,7 +359,7 @@ BarWidget {
|
||||
else if (dx !== 0) root.moveCursorH(dx)
|
||||
}
|
||||
onActivateRequested: if (root.cursorActive) root.activateCursor()
|
||||
onCloseRequested: root.closePopout()
|
||||
onCloseRequested: root.close()
|
||||
onDeleteRequested: if (root.cursorActive) root.deleteSelected()
|
||||
|
||||
Column {
|
||||
@@ -5,15 +5,14 @@ import Quickshell.Io
|
||||
import qs.Ui
|
||||
import qs.Commons
|
||||
|
||||
BarWidget {
|
||||
Panel {
|
||||
id: root
|
||||
moduleName: "monitorPanel"
|
||||
|
||||
ipcTarget: "panels.monitor"
|
||||
manageIpc: false
|
||||
|
||||
// manageIpc: false so this panel can own the single IpcHandler the target
|
||||
// permits — needed for the brightness + state methods below.
|
||||
PanelController { id: ctrl; ipcTarget: "monitorPanel"; manageIpc: false }
|
||||
readonly property bool popupOpen: ctrl.open
|
||||
property int brightnessPercent: 0
|
||||
property int pendingBrightnessPercent: 0
|
||||
property bool brightnessSetQueued: false
|
||||
@@ -29,7 +28,7 @@ BarWidget {
|
||||
|
||||
// Cursor model shared by keyboard and mouse. Sections:
|
||||
// "brightness" - single slider row, selectedIndex = -1 sentinel
|
||||
// (mirrors audioPanel's slider rows). Only present if a
|
||||
// (mirrors Audio's slider rows). Only present if a
|
||||
// controllable backlight was detected.
|
||||
// "scale" - 6 Button scale presets; treated as a single
|
||||
// horizontal row from j/k's perspective. h/l moves
|
||||
@@ -170,10 +169,8 @@ BarWidget {
|
||||
flick.contentY = bottom + margin - flick.height
|
||||
}
|
||||
|
||||
function closePopout() { ctrl.hide() }
|
||||
|
||||
IpcHandler {
|
||||
target: "monitorPanel"
|
||||
target: "panels.monitor"
|
||||
|
||||
function brightness(percent: string): string {
|
||||
var value = Number(percent)
|
||||
@@ -191,9 +188,11 @@ BarWidget {
|
||||
})
|
||||
}
|
||||
|
||||
function toggle(): void { ctrl.toggle() }
|
||||
function show(): void { ctrl.show() }
|
||||
function hide(): void { ctrl.hide() }
|
||||
function open(): void { root.open() }
|
||||
function close(): void { root.close() }
|
||||
function toggle(): void { root.toggle() }
|
||||
function show(): void { root.open() }
|
||||
function hide(): void { root.close() }
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
@@ -260,8 +259,8 @@ BarWidget {
|
||||
// KeyboardPanel takes Exclusive focus at map-time, so SUPER-bound IPC
|
||||
// summons land with j/k ready to navigate. Keep a default landing point,
|
||||
// but don't paint the cursor until hover or the first navigation key.
|
||||
onPopupOpenChanged: {
|
||||
if (popupOpen) {
|
||||
onOpenedChanged: {
|
||||
if (opened) {
|
||||
refresh()
|
||||
if (brightnessAvailable) {
|
||||
focusSection = "brightness"
|
||||
@@ -343,7 +342,7 @@ BarWidget {
|
||||
bar: root.bar
|
||||
text: root.displays.length > 1 ? "" : ""
|
||||
fontSize: Style.font.subtitle
|
||||
onPressed: function(b) { ctrl.toggle() }
|
||||
onPressed: function(b) { root.toggle() }
|
||||
onWheelMoved: function(delta) {
|
||||
if (root.brightnessAvailable) root.setBrightness(root.brightnessPercent + (delta > 0 ? 5 : -5))
|
||||
}
|
||||
@@ -354,7 +353,7 @@ BarWidget {
|
||||
anchorItem: button
|
||||
owner: ctrl
|
||||
bar: root.bar
|
||||
open: ctrl.open
|
||||
open: root.opened
|
||||
focusTarget: keyCatcher
|
||||
contentWidth: panel.fittedContentWidth(Style.space(320))
|
||||
contentHeight: panel.fittedContentHeight(panelColumn.implicitHeight, Style.space(560))
|
||||
@@ -371,7 +370,7 @@ BarWidget {
|
||||
}
|
||||
}
|
||||
onActivateRequested: if (root.cursorActive) root.activateCursor()
|
||||
onCloseRequested: root.closePopout()
|
||||
onCloseRequested: root.close()
|
||||
|
||||
ScrollView {
|
||||
id: scrollArea
|
||||
@@ -5,16 +5,14 @@ import Quickshell.Io
|
||||
import qs.Ui
|
||||
import qs.Commons
|
||||
|
||||
BarWidget {
|
||||
Panel {
|
||||
id: root
|
||||
moduleName: "networkPanel"
|
||||
ipcTarget: "panels.network"
|
||||
|
||||
|
||||
PanelController { id: ctrl; ipcTarget: "networkPanel" }
|
||||
readonly property bool popupOpen: ctrl.open
|
||||
// Centralized close so callers can't forget to drop the passphrase prompt.
|
||||
function closePopout() {
|
||||
ctrl.hide()
|
||||
function close() {
|
||||
root.controller.hide()
|
||||
passwordSsid = ""
|
||||
}
|
||||
|
||||
@@ -96,11 +94,11 @@ BarWidget {
|
||||
readonly property color selectedFill: bar ? Style.selectedFillFor(bar.foreground, Color.accent) : "transparent"
|
||||
|
||||
// The panel below is its own layer-shell with Exclusive keyboard focus,
|
||||
// so Hyprland grants focus when the surface is mapped (popupOpen flips
|
||||
// so Hyprland grants focus when the surface is mapped (opened flips
|
||||
// to true). That's what makes the SUPER+CTRL+W keybind actually work
|
||||
// — OnDemand only grants focus on click/hover.
|
||||
onPopupOpenChanged: {
|
||||
if (popupOpen) {
|
||||
onOpenedChanged: {
|
||||
if (opened) {
|
||||
refresh(true)
|
||||
selectedIndex = wifiNetworks.length > 0 ? 0 : -1
|
||||
focusSection = wifiNetworks.length > 0 ? "wifi" : "dns"
|
||||
@@ -115,7 +113,7 @@ BarWidget {
|
||||
// The KeyboardPanel's focusTarget covers initial popup-open; this handles
|
||||
// the inline-editor case where focus was handed off to a child.
|
||||
onPasswordSsidChanged: {
|
||||
if (passwordSsid === "" && popupOpen) {
|
||||
if (passwordSsid === "" && opened) {
|
||||
Qt.callLater(function() { if (keyCatcher) keyCatcher.forceActiveFocus() })
|
||||
}
|
||||
}
|
||||
@@ -129,7 +127,7 @@ BarWidget {
|
||||
if (focusSection === "wifi") focusSection = "dns"
|
||||
} else if (selectedIndex >= wifiNetworks.length) {
|
||||
selectedIndex = wifiNetworks.length - 1
|
||||
} else if (selectedIndex < 0 && popupOpen) {
|
||||
} else if (selectedIndex < 0 && opened) {
|
||||
selectedIndex = 0
|
||||
}
|
||||
}
|
||||
@@ -385,14 +383,14 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
if (provider === "Custom") {
|
||||
var launcher = Util.shellQuote(root.bar.omarchyPath + "/bin/omarchy-launch-floating-terminal-with-presentation")
|
||||
root.bar.run(launcher + " " + Util.shellQuote(root.dnsCommand(provider)))
|
||||
root.closePopout()
|
||||
root.close()
|
||||
return
|
||||
}
|
||||
|
||||
root.pendingDnsProvider = provider
|
||||
actionProc.command = ["bash", "-lc", root.dnsCommand(provider)]
|
||||
actionProc.running = true
|
||||
root.closePopout()
|
||||
root.close()
|
||||
}
|
||||
|
||||
function isProtected(security) {
|
||||
@@ -597,7 +595,7 @@ fi
|
||||
id: detailsPoll
|
||||
interval: 1500
|
||||
repeat: true
|
||||
running: root.popupOpen
|
||||
running: root.opened
|
||||
onTriggered: if (!detailsProc.running) detailsProc.running = true
|
||||
}
|
||||
|
||||
@@ -631,8 +629,8 @@ fi
|
||||
rightExtraMargin: 2
|
||||
|
||||
onPressed: function(b) {
|
||||
if (ctrl.open) root.closePopout()
|
||||
else { ctrl.show(); root.refresh() }
|
||||
if (root.opened) root.close()
|
||||
else { root.open(); root.refresh() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -647,7 +645,7 @@ fi
|
||||
anchorItem: button
|
||||
owner: ctrl
|
||||
bar: root.bar
|
||||
open: ctrl.open
|
||||
open: root.opened
|
||||
focusTarget: keyCatcher
|
||||
contentWidth: panel.fittedContentWidth(Style.space(340))
|
||||
contentHeight: panel.fittedContentHeight(column.implicitHeight)
|
||||
@@ -699,7 +697,7 @@ fi
|
||||
else root.activateSelected()
|
||||
}
|
||||
}
|
||||
onCloseRequested: root.closePopout()
|
||||
onCloseRequested: root.close()
|
||||
onDeleteRequested: {
|
||||
if (root.cursorActive && root.focusSection === "wifi") root.forgetSelected()
|
||||
}
|
||||
@@ -5,13 +5,10 @@ import Quickshell.Services.UPower
|
||||
import qs.Commons
|
||||
import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
Panel {
|
||||
id: root
|
||||
moduleName: "powerPanel"
|
||||
|
||||
|
||||
PanelController { id: ctrl; ipcTarget: "powerPanel" }
|
||||
readonly property bool popupOpen: ctrl.open
|
||||
ipcTarget: "panels.power"
|
||||
property var batteryInfo: ({})
|
||||
property var systemInfo: ({})
|
||||
property var profiles: []
|
||||
@@ -19,8 +16,6 @@ BarWidget {
|
||||
property int profileIndex: 0
|
||||
property bool cursorActive: false
|
||||
|
||||
function closePopout() { ctrl.hide() }
|
||||
|
||||
function selectProfileByDelta(delta) {
|
||||
if (profiles.length === 0) { profileIndex = 0; return }
|
||||
profileIndex = Math.max(0, Math.min(profiles.length - 1, profileIndex + delta))
|
||||
@@ -95,7 +90,7 @@ BarWidget {
|
||||
profiles = list
|
||||
activeProfile = active
|
||||
if (profileIndex >= profiles.length) profileIndex = Math.max(0, profiles.length - 1)
|
||||
if (popupOpen && activeProfile !== "") {
|
||||
if (opened && activeProfile !== "") {
|
||||
var idx = profiles.indexOf(activeProfile)
|
||||
if (idx >= 0) profileIndex = idx
|
||||
}
|
||||
@@ -107,8 +102,8 @@ BarWidget {
|
||||
actionProc.running = true
|
||||
}
|
||||
|
||||
onPopupOpenChanged: {
|
||||
if (popupOpen) {
|
||||
onOpenedChanged: {
|
||||
if (opened) {
|
||||
refresh()
|
||||
var idx = profiles.indexOf(activeProfile)
|
||||
profileIndex = idx >= 0 ? idx : 0
|
||||
@@ -164,7 +159,7 @@ printf 'time\t%s\n' "$($OMARCHY_PATH/bin/omarchy-battery-remaining-time 2>/dev/n
|
||||
rightExtraMargin: 2
|
||||
active: UPower.displayDevice && UPower.displayDevice.percentage <= 0.2 && UPower.onBattery
|
||||
tooltipText: ""
|
||||
onPressed: function(b) { ctrl.toggle() }
|
||||
onPressed: function(b) { root.toggle() }
|
||||
}
|
||||
|
||||
KeyboardPanel {
|
||||
@@ -172,7 +167,7 @@ printf 'time\t%s\n' "$($OMARCHY_PATH/bin/omarchy-battery-remaining-time 2>/dev/n
|
||||
anchorItem: button
|
||||
owner: ctrl
|
||||
bar: root.bar
|
||||
open: ctrl.open
|
||||
open: root.opened
|
||||
focusTarget: keyCatcher
|
||||
contentWidth: panel.fittedContentWidth(Style.space(340))
|
||||
contentHeight: panel.fittedContentHeight(column.implicitHeight)
|
||||
@@ -186,7 +181,7 @@ printf 'time\t%s\n' "$($OMARCHY_PATH/bin/omarchy-battery-remaining-time 2>/dev/n
|
||||
else if (dy !== 0) root.selectProfileByDelta(dy)
|
||||
}
|
||||
onActivateRequested: if (root.cursorActive) root.activateSelectedProfile()
|
||||
onCloseRequested: root.closePopout()
|
||||
onCloseRequested: root.close()
|
||||
|
||||
Column {
|
||||
id: column
|
||||
Reference in New Issue
Block a user