-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Add omarchy keyboard layout bar widget #6358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
heyssh
wants to merge
21
commits into
basecamp:quattro
Choose a base branch
from
heyssh:add/omarchy-keyboard-layout
base: quattro
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
242627e
Add omarchy keyboard layout bar plugin
heyssh 40228ae
Add Setup toggle to show/hide the keyboard layout bar icon
heyssh dc7c924
Merge branch 'quattro' into add/omarchy-keyboard-layout
heyssh 3fd646b
Potential fix for pull request finding
heyssh 8ac3a1e
Update omarchy-keyboard-layout
heyssh fb89c8f
Update Model.js
heyssh e8dbee1
Improve panel and model files and add more descriptive comments
heyssh fa9f789
Remove Both Shift preset, add keybinding to open panel when enabled
heyssh 97d6ddb
fix(panel): Enter in add-language search now adds highlighted row, no…
heyssh 422c956
Rename "Keyboard" label to "Keyboard layout"
heyssh 1dd80e4
Update omarchy-menu.jsonc
heyssh 3a4e2d6
Update keyboard layout binding
heyssh 062ce31
Update "keyboard layout" label to "keyboard layout while enabled"
heyssh ea0a9ae
Update Panel.qml
heyssh b3c1d8b
Potential fix for pull request finding
heyssh 01008bf
feat(osd): improve notification display for toggle actions
heyssh 07746fb
default/hypr: sync input.lua with keyboard panel
heyssh 29fe044
fix(keyboard-panel): allow Up from any switcher pill to reach languages
heyssh 2354816
Merge branch 'quattro' into add/omarchy-keyboard-layout
heyssh 92b66d9
fix(keyboard-panel): stop repeated Enter from adding extra languages
heyssh f7aa9df
improve keyboard panel
heyssh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| #!/bin/bash | ||
|
|
||
| # omarchy:summary=Get, switch, or configure keyboard layouts | ||
| # omarchy:group=keyboard | ||
| # omarchy:args=[status|available|next|set|add|remove|switcher] [value] | ||
| # omarchy:examples=omarchy keyboard layout add de | ||
|
|
||
| set -e | ||
|
|
||
| STATE_DIR="$HOME/.local/state/omarchy/settings" | ||
| STATE_FILE="$STATE_DIR/keyboard.json" | ||
| XKB_LIST="/usr/share/X11/xkb/rules/base.lst" | ||
|
|
||
| usage() { | ||
| echo "Usage: omarchy-keyboard-layout [status|available|next|set <code>|add <code>|remove <code>|switcher <preset>]" >&2 | ||
| exit 1 | ||
| } | ||
|
|
||
| # Looks up the human label xkb ships for a layout code, e.g. "ir" -> "Persian". | ||
| # This is the same file base.lst that `localectl list-x11-keymap-layouts` reads. | ||
| layout_label() { | ||
| local code=$1 | ||
| awk -v code="$code" ' | ||
| /^! layout/ { insection=1; next } | ||
| /^!/ { insection=0 } | ||
| insection && $1 == code { | ||
| line = $0 | ||
| sub(/^[ \t]+/, "", line) | ||
| sub(/^[^ \t]+[ \t]+/, "", line) | ||
| print line | ||
| exit | ||
| } | ||
| ' "$XKB_LIST" | ||
| } | ||
|
|
||
| # Every layout code + label xkb knows about, as a JSON array, sorted by label. | ||
| # This feeds the panel's "Add language" search list. | ||
| available_layouts_json() { | ||
| awk ' | ||
| /^! layout/ { insection=1; next } | ||
| /^!/ { insection=0 } | ||
| insection && NF && $1 != "custom" { | ||
| rest = $0 | ||
| sub(/^[ \t]+/, "", rest) | ||
| code = $1 | ||
| sub(/^[^ \t]+[ \t]+/, "", rest) | ||
| printf "%s\t%s\n", code, rest | ||
| } | ||
| ' "$XKB_LIST" | sort -t $'\t' -k2 | jq -R -s ' | ||
| split("\n") | map(select(length > 0) | split("\t") | {code: .[0], label: .[1]}) | ||
| ' | ||
| } | ||
|
|
||
| ensure_state() { | ||
| mkdir -p "$STATE_DIR" | ||
| [[ -f $STATE_FILE ]] && return | ||
|
|
||
| local primary label | ||
| primary=$(awk -F= '/^XKBLAYOUT/{gsub(/"/, "", $2); print $2}' /etc/vconsole.conf 2>/dev/null || true) | ||
| primary=${primary:-us} | ||
| label=$(layout_label "$primary") | ||
|
|
||
| jq -n --arg code "$primary" --arg label "${label:-$primary}" \ | ||
| '{layouts: [{code: $code, label: $label}], switcher: "alt_shift"}' > "$STATE_FILE" | ||
| } | ||
|
|
||
| main_keyboard() { | ||
| hyprctl devices -j | jq -r ' | ||
| [.keyboards[] | select(.name | test("virtual|fcitx|ibus|video-bus|buttons"; "i") | not)] as $real | ||
| | (($real[] | select(.main == true) | .name) // ($real[0].name // empty)) | ||
| ' | ||
| } | ||
|
|
||
| # hyprctl reports the active layout as a human description ("English (US)", | ||
| # "Persian"), not the xkb code we store, so match it against the labels we | ||
| # already saved for the configured layouts rather than re-deriving a code | ||
| # from free text. | ||
| active_index() { | ||
| local device keymap | ||
| device=$(main_keyboard) | ||
| [[ -n $device ]] || { echo 0; return; } | ||
|
|
||
| keymap=$(hyprctl devices -j | jq -r --arg dev "$device" \ | ||
| '.keyboards[] | select(.name == $dev) | .active_keymap // ""') | ||
|
|
||
| jq -r --arg keymap "$keymap" ' | ||
| ([.layouts[].label] | map(. as $l | (($keymap|ascii_downcase) | contains($l|ascii_downcase))) | ||
| | index(true)) // 0 | ||
| ' "$STATE_FILE" | ||
| } | ||
|
|
||
| status_json() { | ||
| local idx | ||
| idx=$(active_index) | ||
| jq --argjson idx "$idx" '. + {active: (.layouts[$idx].code // .layouts[0].code)}' "$STATE_FILE" | ||
| } | ||
|
|
||
| reload_hyprland() { | ||
| hyprctl reload >/dev/null 2>&1 || true | ||
| } | ||
|
|
||
| write_state() { | ||
| local filter=$1; shift | ||
| local tmp | ||
| tmp=$(mktemp) | ||
| jq "$@" "$filter" "$STATE_FILE" > "$tmp" | ||
| mv "$tmp" "$STATE_FILE" | ||
| } | ||
|
|
||
| action=${1:-status} | ||
| ensure_state | ||
|
|
||
| case "$action" in | ||
| status) | ||
| status_json | ||
| ;; | ||
|
|
||
| available) | ||
| available_layouts_json | ||
| ;; | ||
|
|
||
| next) | ||
| device=$(main_keyboard) | ||
| [[ -n $device ]] || { echo "No keyboard device found" >&2; exit 1; } | ||
| hyprctl switchxkblayout "$device" next >/dev/null | ||
| status_json | ||
| ;; | ||
|
|
||
| set) | ||
| code=${2:-} | ||
| [[ -n $code ]] || usage | ||
| device=$(main_keyboard) | ||
| [[ -n $device ]] || { echo "No keyboard device found" >&2; exit 1; } | ||
| index=$(jq -r --arg code "$code" '[.layouts[].code] | index($code) // empty' "$STATE_FILE") | ||
| [[ -n $index ]] || { echo "Layout '$code' is not configured" >&2; exit 1; } | ||
| hyprctl switchxkblayout "$device" "$index" >/dev/null | ||
| status_json | ||
| ;; | ||
|
|
||
| add) | ||
| code=${2:-} | ||
| [[ -n $code ]] || usage | ||
| already=$(jq -r --arg code "$code" '[.layouts[].code] | index($code) // empty' "$STATE_FILE") | ||
| if [[ -z $already ]]; then | ||
| label=$(layout_label "$code") | ||
| [[ -n $label ]] || { echo "Unknown layout '$code'" >&2; exit 1; } | ||
| write_state '.layouts += [{code: $code, label: $label}]' --arg code "$code" --arg label "$label" | ||
| reload_hyprland | ||
| fi | ||
| status_json | ||
| ;; | ||
|
|
||
| remove) | ||
| code=${2:-} | ||
| [[ -n $code ]] || usage | ||
| count=$(jq '.layouts | length' "$STATE_FILE") | ||
| primary_code=$(jq -r '.layouts[0].code // empty' "$STATE_FILE") | ||
| if [[ $count -gt 1 && $code != "$primary_code" ]]; then | ||
| write_state '.layouts |= map(select(.code != $code))' --arg code "$code" | ||
| reload_hyprland | ||
| fi | ||
| status_json | ||
| ;; | ||
|
|
||
| switcher) | ||
| preset=${2:-} | ||
| case "$preset" in | ||
| alt_shift|ctrl_shift|right_alt|both_shift) ;; | ||
| *) usage ;; | ||
| esac | ||
| write_state '.switcher = $s' --arg s "$preset" | ||
| reload_hyprland | ||
| status_json | ||
| ;; | ||
|
|
||
| *) | ||
| usage | ||
| ;; | ||
| esac |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,57 +1,103 @@ | ||
| -- Keep only your personal input overrides here. Uncommented settings below | ||
| -- replace Omarchy's defaults. | ||
|
|
||
| -- Keyboard layout and options. | ||
| -- See https://wiki.hypr.land/Configuring/Basics/Variables/#input | ||
| -- hl.config({ | ||
| -- input = { | ||
| -- -- Use multiple keyboard layouts and switch between them with Left Alt + Right Alt. | ||
| -- kb_layout = "us,dk,eu", | ||
| -- kb_options = "compose:caps,shift:both_capslock,grp:alts_toggle", | ||
| -- | ||
| -- -- Use a specific keyboard variant if needed (e.g. intl for international keyboards). | ||
| -- kb_variant = "intl", | ||
| -- | ||
| -- -- Change speed of keyboard repeat. | ||
| -- repeat_rate = 40, | ||
| -- repeat_delay = 250, | ||
| -- | ||
| -- -- Start with numlock on by default. | ||
| -- numlock_by_default = true, | ||
| -- | ||
| -- -- Increase sensitivity for mouse/trackpad (default: 0). | ||
| -- sensitivity = 0.35, | ||
| -- | ||
| -- -- Turn off mouse acceleration (default: adaptive). | ||
| -- accel_profile = "flat", | ||
| -- | ||
| -- touchpad = { | ||
| -- -- Use natural (inverse) scrolling. | ||
| -- natural_scroll = true, | ||
| -- | ||
| -- -- Use two-finger clicks for right-click instead of lower-right corner. | ||
| -- clickfinger_behavior = true, | ||
| -- | ||
| -- -- Control the speed of your scrolling. | ||
| -- scroll_factor = 0.4, | ||
| -- | ||
| -- -- Enable the touchpad while typing. | ||
| -- disable_while_typing = false, | ||
| -- | ||
| -- -- Left-click-and-drag with three fingers. | ||
| -- drag_3fg = 1, | ||
| -- }, | ||
| -- }, | ||
| -- }) | ||
|
|
||
| -- App-specific touchpad scroll speeds. | ||
| -- o.window("(Alacritty|kitty|foot)", { scroll_touchpad = 1.5 }) | ||
| -- o.window("com.mitchellh.ghostty", { scroll_touchpad = 0.2 }) | ||
|
|
||
| -- Enable touchpad gestures for changing workspaces. | ||
| -- See https://wiki.hypr.land/Configuring/Advanced-and-Cool/Gestures/ | ||
| -- hl.gesture({ fingers = 3, direction = "horizontal", action = "workspace" }) | ||
|
|
||
| -- Enable touchpad gestures for moving focus (helpful on scrolling layout). | ||
| -- hl.gesture({ fingers = 3, direction = "left", action = function() hl.dispatch(hl.dsp.focus({ direction = "l" })) end }) | ||
| -- hl.gesture({ fingers = 3, direction = "right", action = function() hl.dispatch(hl.dsp.focus({ direction = "r" })) end }) | ||
| -- https://wiki.hypr.land/Configuring/Basics/Variables/#input | ||
|
|
||
| local function read_vconsole() | ||
| local values = {} | ||
| local file = io.open("/etc/vconsole.conf", "r") | ||
| if not file then | ||
| return values | ||
| end | ||
|
|
||
| for line in file:lines() do | ||
| local key, value = line:match("^%s*([%w_]+)%s*=%s*(.-)%s*$") | ||
| if key and value then | ||
| value = value:gsub("%s+#.*$", "") | ||
| value = value:gsub('^"(.*)"$', "%1") | ||
| value = value:gsub("^'(.*)'$", "%1") | ||
| values[key] = value | ||
| end | ||
| end | ||
|
|
||
| file:close() | ||
| return values | ||
| end | ||
|
|
||
| -- The keyboard panel (bin/omarchy-keyboard-layout) owns this file: it's the | ||
| -- list of layouts the user picked via "Add language" plus their chosen | ||
| -- switch shortcut. jq is already a hard dependency of the Omarchy CLI | ||
| -- scripts, so shelling out to it here is simpler and less error-prone than | ||
| -- hand-rolling JSON parsing in Lua. | ||
| local STATE_FILE = os.getenv("HOME") .. "/.local/state/omarchy/settings/keyboard.json" | ||
|
|
||
|
|
||
| local function popen_trim(cmd) | ||
| local handle = io.popen(cmd) | ||
| if not handle then | ||
| return "" | ||
| end | ||
| local result = handle:read("*a") or "" | ||
| handle:close() | ||
| return (result:gsub("^%s+", ""):gsub("%s+$", "")) | ||
| end | ||
|
|
||
| local function state_field(jq_filter, fallback) | ||
| local cmd = string.format("jq -r %s %q 2>/dev/null", jq_filter, STATE_FILE) | ||
| local value = popen_trim(cmd) | ||
| if value == "" or value == "null" then | ||
| return fallback | ||
| end | ||
| return value | ||
| end | ||
|
|
||
| local vconsole = read_vconsole() | ||
|
|
||
| local kb_layout = state_field("'[.layouts[].code] | join(\",\")'", vconsole.XKBLAYOUT or "us") | ||
| local switcher = state_field("'.switcher // \"alt_shift\"'", "alt_shift") | ||
|
|
||
| -- The four switch-shortcut presets offered by the keyboard panel's pill | ||
| -- row, mapped to the matching xkb grp option. | ||
| local SWITCHER_OPTIONS = { | ||
| alt_shift = "grp:alt_shift_toggle", | ||
| ctrl_shift = "grp:ctrl_shift_toggle", | ||
| right_alt = "grp:toggle", | ||
| both_shift = "grp:shifts_toggle", | ||
| } | ||
|
|
||
| local grp_option = SWITCHER_OPTIONS[switcher] or SWITCHER_OPTIONS.alt_shift | ||
|
|
||
| -- Compose used to be bound to "compose:caps", which hijacks Caps Lock | ||
| -- entirely and turns it into the Compose key -- Caps Lock then never | ||
| -- toggles capitalization again, it just shows the small compose-pending | ||
| -- dot while waiting for the next key. Bind Compose to the Menu key | ||
| -- instead: it's unused by every switcher preset above, so Caps Lock keeps | ||
| -- working normally and Compose sequences (e.g. Menu, ', e -> é) still work. | ||
| local kb_options = "compose:menu," .. grp_option | ||
|
|
||
|
|
||
| hl.config({ | ||
| input = { | ||
| kb_layout = kb_layout, | ||
| kb_variant = "", | ||
|
|
||
| kb_model = "", | ||
| kb_options = kb_options, | ||
| kb_rules = "", | ||
| follow_mouse = 1, | ||
| sensitivity = 0, | ||
|
|
||
| repeat_rate = 40, | ||
| repeat_delay = 250, | ||
| numlock_by_default = true, | ||
|
|
||
| touchpad = { | ||
| natural_scroll = false, | ||
| clickfinger_behavior = true, | ||
| scroll_factor = 0.4, | ||
| }, | ||
| }, | ||
|
|
||
| misc = { | ||
| key_press_enables_dpms = true, | ||
| mouse_move_enables_dpms = true, | ||
| }, | ||
| }) | ||
|
|
||
| -- Scroll nicely in the terminal. | ||
| o.window("(Alacritty|kitty|foot)", { scroll_touchpad = 1.5 }) | ||
| o.window("com.mitchellh.ghostty", { scroll_touchpad = 0.2 }) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.