Files
omarchycn/default/quickshell/bar/widgets/keyboardLayout.qml
T
Ryan Hughes a901d4a71f Add 5 widgets inspired by Noctalia: activeWindow, nightLight, keyboardLayout, lockKeys, spacer
- activeWindow: shows the focused toplevel title from Quickshell.Wayland
  ToplevelManager. Left-click activates, middle-click closes, right-click
  killactive. Hidden in vertical bars to avoid title rotation noise.
- nightLight: toggles omarchy-toggle-nightlight; status detected via
  pgrep hyprsunset. Hides when hyprsunset isn't installed.
- keyboardLayout: shows current xkb layout short code, click cycles via
  hyprctl switchxkblayout. Refreshes on Hyprland activelayout events.
- lockKeys: caps/num/scroll lock indicators read from /sys/class/leds.
  Uses plain A/1/S letters; hides each indicator when off.
- spacer: configurable blank space (size) for layout tuning.

Defaults add activeWindow to the left section and nightLight to the
right section between audioPanel and brightness.
2026-05-14 02:21:47 -04:00

85 lines
1.9 KiB
QML

import QtQuick
import Quickshell
import Quickshell.Hyprland
import Quickshell.Io
import "../common" as Common
Item {
id: root
property QtObject bar: null
property string moduleName: "keyboardLayout"
property var settings: ({})
property string layoutLabel: ""
property string layoutFull: ""
function setting(name, fallback) {
var value = settings ? settings[name] : undefined
return value === undefined || value === null ? fallback : value
}
function refresh() {
if (!queryProc.running) queryProc.running = true
}
function cycleLayout() {
Hyprland.dispatch("switchxkblayout current next")
refreshTimer.restart()
}
Component.onCompleted: refresh()
Connections {
target: Hyprland
function onRawEvent(event) {
if (!event || !event.name) return
if (String(event.name).indexOf("activelayout") !== -1) root.refresh()
}
}
Process {
id: queryProc
command: ["bash", "-lc", "hyprctl -j devices 2>/dev/null | sed -n '/keyboards/,$p' | head -200"]
stdout: StdioCollector {
waitForEnd: true
onStreamFinished: {
var match = String(text || "").match(/"active_keymap":\s*"([^"]+)"/)
if (!match) return
var full = match[1]
root.layoutFull = full
var token = full.split(/\s+/)[0]
root.layoutLabel = token.substring(0, 3).toUpperCase()
}
}
}
Timer {
id: refreshTimer
interval: 600
onTriggered: root.refresh()
}
Timer {
interval: 10000
running: true
repeat: true
onTriggered: root.refresh()
}
visible: layoutLabel !== ""
implicitWidth: button.implicitWidth
implicitHeight: button.implicitHeight
Common.WidgetButton {
id: button
anchors.fill: parent
bar: root.bar
text: root.layoutLabel
fontSize: 10
horizontalMargin: 6
tooltipText: root.layoutFull
onPressed: function() { root.cycleLayout() }
}
}