Bind SUPER + CTRL + a number to the bar's right panels (#6702)

The letters name a panel; the numbers count them. One is the leftmost
panel in the right section, so the number matches the icon a user would
point at: a widget with no panel of its own is passed over, and so is one
that is hiding itself.

Counting rather than naming means the hotkeys follow the bar. Rearranging
the section, or adding a widget to it, renumbers the panels with no
binding to rewrite.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
David Heinemeier Hansson
2026-08-11 13:03:20 +02:00
committed by GitHub
co-authored by Claude Opus 5
parent efe805387e
commit 5edc3497fa
5 changed files with 72 additions and 0 deletions
+12
View File
@@ -102,6 +102,18 @@ o.bind("SUPER + CTRL + W", "Network", "omarchy-shell shell toggle omarchy.networ
o.bind("SUPER + CTRL + P", "Power", "omarchy-shell shell toggle omarchy.power")
o.bind("SUPER + CTRL + T", "Activity", { tui = "btop" })
-- The letters above name a panel; the numbers count them. 1 is the leftmost
-- panel in the bar's right section, and a widget with no panel of its own (the
-- tray) is not counted, so the number matches the icon a user would point at.
-- A bar with fewer panels than this leaves the tail of the range doing nothing.
for panel = 1, 9 do
o.bind(
"SUPER + CTRL + code:" .. tostring(panel + 9),
"Bar panel " .. panel,
"omarchy-shell -q shell togglePanelAt right " .. panel
)
end
o.bind("SUPER + CTRL + Z", "Zoom in", function()
local zoom = hl.get_config("cursor.zoom_factor") or 1
hl.config({ cursor = { zoom_factor = zoom + 1 } })
+15
View File
@@ -417,6 +417,21 @@ Item {
return slots
}
// The Nth panel in a bar region, counted the way the bar reads: layout order,
// and only the panels actually on screen. A widget with no panel (the tray)
// and one that is hiding itself are passed over, so the number lands on the
// Nth panel icon the user can see rather than the Nth layout entry.
// One-based, because it exists for hotkeys; anything else lands on no slot.
//
// Counting any bar surface is enough: every monitor lays its bar out from the
// one layout, and summoning the id routes through pickPanelSlot, which opens
// the focused monitor's copy whichever surface was counted.
function panelWidgetIdAt(region, index) {
var slots = panelNavigationSlots(String(region || ""), null)
var slot = slots[Math.round(Number(index)) - 1]
return slot ? String(slot.moduleName || "") : ""
}
function switchPanelFrom(owner, direction) {
if (!owner) return false
+13
View File
@@ -1011,6 +1011,19 @@ ShellRoot {
shell.toggle(id, payloadJson)
}
// A bar section's panels answer to their position as well as their id, so a
// hotkey can mean "the third panel in the right section" and keep meaning
// it after the bar is rearranged. Returns the id it acted on, or "unknown"
// when the section holds no panel at that position.
function togglePanelAt(section: string, index: string): string {
var id = shell.bar && typeof shell.bar.panelWidgetIdAt === "function"
? shell.bar.panelWidgetIdAt(section, index)
: ""
if (!id) return "unknown"
shell.toggle(id, "{}")
return id
}
function call(id: string, method: string, arg: string): string {
return shell.callIfLoaded(id, method, arg)
}
+17
View File
@@ -183,6 +183,23 @@ assert(
'bar tabs between panels within one bar surface'
)
// A positional hotkey means "the third panel in this section", so it counts the
// panels the bar actually draws. Reusing the tab-order walk is what keeps the
// count honest: a widget with no panel and a hidden one are already passed over
// there, and reading the layout config a second time would count both.
assert(
/function panelWidgetIdAt\(region, index\) \{[\s\S]*?panelNavigationSlots\(String\(region \|\| ""\), null\)/.test(barSource),
'bar counts positional panels off the drawn tab order'
)
assert(
/var slot = slots\[Math\.round\(Number\(index\)\) - 1\]/.test(barSource),
'positional panels are one-based, and anything off the end lands on no slot'
)
assert(
/function togglePanelAt\(section: string, index: string\): string \{[\s\S]*?shell\.bar\.panelWidgetIdAt\(section, index\)[\s\S]*?shell\.toggle\(id, "\{\}"\)/.test(shellSource),
'shell toggles a bar panel by its position over IPC'
)
const clockSlot = { id: 'clock' }
const traySlot = { id: 'tray' }
const horizontalTargets = [
@@ -175,6 +175,21 @@ if grep -Fq $'SUPER + CTRL + X Toggle dictation' <<<"$missing_voxtype_output"; t
fi
pass "missing Voxtype skips dictation bindings"
# The panel hotkeys claim a row of keys that workspace switching already uses
# under other modifiers, so the count matters as much as the bindings: a tenth
# claim on SUPER + CTRL + a number is a collision with one of these.
panels_home="$tmpdir/panels-home"
mkdir -p "$panels_home"
panels_output=$(run_omarchy_bindings "$panels_home")
for panel in 1 2 3 4 5 6 7 8 9; do
grep -Fqx "SUPER + CTRL + code:$((panel + 9))"$'\t'"Bar panel $panel" <<<"$panels_output" ||
fail "bar panel hotkeys count the right section" "$panel"
done
number_claims=$(cut -f1 <<<"$panels_output" | grep -cE '^SUPER \+ CTRL \+ code:1[0-9]$' || true)
(( number_claims == 9 )) ||
fail "only the bar panel hotkeys bind SUPER + CTRL + a number" "$number_claims"
pass "bar panel hotkeys bind SUPER + CTRL + a number without a collision"
migration=$(grep -rl 'Move stock Hyprland user overrides into package defaults' "$ROOT/migrations" | head -n 1 || true)
[[ -n $migration ]] || fail "Hyprland default config migration exists"