Add fully configurable keyboard shortcuts throughout Gramps - #2463
Add fully configurable keyboard shortcuts throughout Gramps#2463dsblank wants to merge 7 commits into
Conversation
Users can now view, search, rebind, reset, and save keyboard shortcuts for essentially every command in Gramps through a new Keyboard Shortcuts tab in Preferences: the main menu and toolbar, every List/Navigation view, the Note editor's formatting toolbar, Tools and Reports menu entries, and every "Add"/"Private"/"Date editor" style button inside the Person, Event, Family, and other object editor dialogs. Every shortcut, including ones belonging to a view or dialog never opened this session, appears in the list and is editable, since each source (main window, per-view, per-dialog) registers its actions statically rather than only when instantiated. Rebinding a shortcut applies immediately for anything currently open, warns about and offers to resolve conflicts with other actions bound to the same key, and persists to a per-user file. A keyboard shortcut "theme" system lets users save their current bindings under a name and load a saved set later; a bundled "Default-PC" theme captures Gramps' own shipped non-mac defaults as an editable starting point, while "Reset All to Defaults" remains the authoritative, always platform-correct way back to Gramps' actual shipped bindings. Also fixes a long-standing surprise where Ctrl+C in the main window silently opened the Clipboard window instead of behaving like a normal shortcut: that behavior was previously a hardcoded key-press handler living outside the action system entirely, invisible to any customization. It is now a regular, rebindable "Copy to Clipboard" action like everything else. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds global, customizable "Accept Dialog (OK)" / "Cancel Dialog" shortcuts that are wired to a dialog's real OK/Cancel button even though plain Gtk.Dialog windows don't get GtkApplication's automatic accelerator routing. Glade tooltip lookup now uses the correct tooltip-text property name (was tooltip_text) and takes only the first line, since some tooltips continue with further explanation. Hardcoded "_OK"/"_Cancel" button mnemonics are removed from the glade files now that these actions are driven by the shortcut system. Fixes a "WindowManger" typo in the WindowManager action group name, and excludes that group from the shortcut editor entirely, since its actions are generated per open-window instance and are not stable, customizable commands. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The shortcut editor's Load/Save buttons treated a "theme" as a one-shot import/export: loading flattened it into an anonymous per-user override file, and nothing remembered which theme, if any, was active. Preferences now tracks the active theme by name via a new interface.keybinding-theme config key, and every edit -- rebind, clear, or reset -- is saved directly into that theme's own file instead of the old gramps.accel override. Editing the bundled "Default" theme forks a copy into the user's keybinding_themes directory the first time it's saved, since user themes already take precedence over same-named bundled ones. Selecting a different theme in the "Theme:" combo now switches to it immediately, so the separate "Load"/"Switch Theme" button is gone -- every edit is already persisted, so there is never unsaved state to protect. "Save..." is replaced by "New Theme...", which forks the current bindings under a new name and makes it active. "Reset to Default" and "Reset All to Defaults" always read Gramps' live, hardcoded accelerator defaults rather than a theme's own saved values -- renamed to "Reset to Factory Default" / "Reset All to Factory Defaults" to make that distinction clear. Renamed the bundled "Default-PC" theme to "Default": GTK already renders <Primary> as the platform's own modifier (e.g. Cmd on macOS), so the single bundled file isn't PC-specific, and the "-PC" suffix wrongly implied a separate Mac variant that doesn't exist. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Backwards-incompatibility issues in feature/custom-keybindings
Across ~42 glade dialogs (editperson.glade, editevent.glade, editname.glade, dialog.glade, etc.), the OK/Cancel button labels were changed from _OK/_Cancel to plain OK/Cancel, stripping the GTK mnemonic markup. In its place, managedwindow.py:519-570 manually wires a global app.dialog-ok / app.dialog-cancel accelerator (default o / c, user-configurable) onto a Gtk.AccelGroup added to each dialog window, and calls .clicked() on whatever widget owns that response id. Net effect for the default theme: Alt+O and Alt+C still work exactly as before. But since GTK's "hold Alt → underline the mnemonic letter" behavior is driven by the mnemonic markup in the label, and that markup is gone, holding Alt no longer visually underlines the OK/Cancel button (your observation). In dialogs where OK/Cancel were the only mnemonic widgets (e.g. editurl.glade, editname.glade), holding Alt now visually does nothing at all, which reads as "mnemonics are broken" even though other dialogs (editperson, editevent, which still have _Given name:-style field mnemonics) will still show underlines elsewhere. This is a deliberate tradeoff, not an oversight — it's what makes OK/Cancel globally reassignable via the new shortcuts editor instead of being fixed per-dialog GTK mnemonics — but it's an unannounced visual/UX regression and undocumented in the code or tests.
Changing _OK/_Cancel → OK/Cancel changes the translatable msgid in every one of those glade files. Existing translations for _OK/_Cancel won't match the new strings, so those buttons will silently fall back to untranslated English in every locale until translators catch up — a temporary but real regression for non-English users right after merge.
This renames a pre-existing (already-shipped) ActionGroup name. The only path by which this could actually break something for an existing user is the diagnostic-only dump_all_accels() → load_accels() round trip (never exposed via UI, dev-only). Low risk, but worth a mention in the commit message since it's a rename of a shipped identifier, not new code.
Consistent with your cb_ callback-naming convention, but it's a rename of a method that (in principle) an out-of-tree view subclass or addon could have overridden to customize Ctrl+C behavior. I found no such override in-tree or in the parts of addons-source I can see from here, so this is low-risk but technically a public-ish API break.
Default.accel is a static, checked-in snapshot of the current defaults, and it is loaded automatically for every user via theme_path("Default") in grampsgui.py (not just used as a reference). If a future commit changes a hard-coded default accelerator in Python without regenerating this file, the stale entry in Default.accel will silently override the new default for all users, since it's merged in over default_accels at startup. Not a problem today, but a landmine for later PRs unless there's a lint/test step keeping the two in sync (I didn't find one). Nothing here touches the database schema or on-disk family tree data — this is all GUI-layer, so no upgrade.py/schema migration concerns. |
Restores the "_OK"/"_Cancel" mnemonics stripped from ~42 editor-dialog
glade files by the previous commit. Removing them had also removed
the GTK-native accelerator binding on those buttons, which is what
exposes a keybinding to assistive technology via ATK -- so screen
readers lost any indication that Alt+O/Alt+C activate those buttons,
not just the visual Alt-hold underline.
ManagedWindow now only intervenes when a user has actually customized
"Accept Dialog (OK)"/"Cancel Dialog" away from its default: it strips
the (now stale) mnemonic from that button's label, binds the real key
via Gtk.Widget.add_accelerator() -- a genuine widget-level GTK
accelerator, so it still shows up in the button's ATK keybinding --
and shows the current shortcut as a tooltip, since an arbitrary
user-chosen key cannot be represented as an underlined letter in a
translated label. When left at its default, the glade-defined
mnemonic is untouched and does all the work, same as before this
whole keyboard-shortcuts feature existed.
Also switches the keyboard shortcut theme file format from a single
Python-dict-literal blob (`*.accel`) to JSON Lines (`*.jsonl`), one
`{"id", "label", "category", "accel"}` object per line:
- Each entry now carries its own human-readable label and category,
so a theme file is self-documenting when opened directly in a text
editor, rather than a bare {action_id: accel} mapping that requires
cross-referencing the shortcuts editor to make sense of.
- A malformed line (or one with a disallowed accelerator) is logged
and skipped rather than aborting the whole file, so a single typo in
a hand-edited theme no longer costs every other binding in it.
- Startup now tolerates a theme file that can't be loaded at all
(missing, unreadable, or otherwise malformed) instead of crashing
before the main window ever appears, which a hand-edited file could
previously trigger via an uncaught SyntaxError.
- The bundled "Default" theme is converted to the new format, with
labels/categories filled in wherever mechanically derivable from
glade files and static action registrations.
Fixes two check_accel() bugs surfaced by validating theme entries on
load: it rejected Left/Right/Up/Down whenever used with any modifier,
which would have flagged Gramps' own default "Go Back"/"Go Forward"
bindings (<Alt>Left/<Alt>Right) as invalid; and it could not resolve
the virtual <Primary> modifier without a live display connection,
causing false rejections under this project's headless test
convention (GDK_BACKEND=-).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Boots the real UIManager/ViewManager stack under a real X display (not the full GrampsApplication, which would auto-open the Family Tree manager dialog) against an isolated, throwaway profile, visits every view once so each view's own menu/toolbar actions register, then writes every known shortcut with its label and category to gramps/gui/keybinding_themes/Default.jsonl. This is the only way to get accurate labels for actions that aren't part of a view's static get_shortcut_specs(), since those are only known once the real menu XML is built against a live display. Re-running it against the current codebase raises coverage from 104 entries (69 labeled, from a mechanical, display-less conversion) to 189 entries (187 labeled), since visiting every view surfaces each view's own ordinary menu actions that the static registration alone does not. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Switching views tears down the previous view's action groups and menu XML (ViewManager.__disconnect_previous_page), so UIManager.list_actions() -- and therefore the shortcut editor and save_accels() -- only ever reported a label/category for whichever view happens to be on screen right now. Any other view's own menu action (e.g. a Geography view's "Print..." command) would show blank the rest of the time, and permanently blank in a one-shot dump like scripts/regenerate_default_theme.py, which showed up there as win.F2/win.PrintView losing their label the moment a later view became active. UIManager now keeps a small label_cache, populated the same way default_accels already is: the first time an action's label/category is resolved while its group is live, list_actions() remembers it, and falls back to that cached value (or the same id-derived text get_action_label() already falls back to for a live-but-unlabeled action) for any action currently known but not live. Also excludes the WindowManager group from the new fallback path, matching the existing exclusion for currently-live groups, since its per-open- window ids were otherwise leaking back in. scripts/regenerate_default_theme.py now calls list_actions() once per visited view, seeding the cache while each view is still live, rather than only once at the very end. Regenerated gramps/gui/keybinding_themes/Default.jsonl: all 189 entries are now labeled and categorized (previously 2 were blank), and win.PrintView recovered its real menu label "Print..." instead of the id-derived fallback. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Hopefully in the right thread this time..., Since a "Help -> Keybindings" menu item already exists (which links to the Wiki page: https://gramps-project.org/wiki/index.php?title=Gramps_6.0_Wiki_Manual_-_Keybindings from the gramps/gramps/gui/grampsgui.py Line 273 in a32b463 |
Reaching a given tab in a primary-object editor (Person, Family, Event, etc.) previously required repeatedly tabbing through every preceding widget or clicking with the mouse; the only keyboard alternative was stepping one tab at a time with Alt+Left/Alt+Right. This adds direct, positional tab-jump shortcuts, registered like the existing dialog-ok/ dialog-cancel actions so they show up in the Keyboard Shortcuts editor and can be rebound or themed. Since it's wired once in EditPrimary's shared _setup_notebook_tabs, it applies to every editor automatically. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Summary
Makes keyboard shortcuts fully user-configurable across Gramps, via a new
Keyboard Shortcuts tab in Preferences. Previously, shortcuts were
scattered across several different, non-overlapping mechanisms (
Gio.SimpleActionaccelerators for the main menu/toolbar/views, static
<accelerator>tags ineditor-dialog
.gladefiles, and even a hardcodedkey-press-eventhandlerfor Ctrl+C) with no way for a user to see or change any of them. This PR
unifies all of them under one system.
What you can do now
in the app — main menu/toolbar, every List/Navigation view (People, Events,
Places, …), the Note editor's rich-text toolbar, Tools/Reports menu
entries, and every "Add"/"Private"/"Date editor" style button inside the
Person/Event/Family/etc. editor dialogs.
Gtk.CellRendererAccel),done — applies live for anything currently open, with a conflict warning
if the combo is already in use elsewhere.
Defaults (everything) — always correct for your platform, since it
reads live defaults rather than a snapshot, regardless of what your
active theme currently has saved.
editor), and every rebind/reset is saved straight into it — no separate
unsaved state to track. Picking a different entry in the combo switches
to it immediately. New Theme… forks your current bindings under a
new name and makes that the active theme. A bundled Default theme
ships as an editable starting point; editing it forks a personal copy
automatically the first time you save.
straight to that numbered tab — no more tabbing through every widget
just to reach, say, the Events list before adding a new one.
In plain terms (why 189 shortcuts, and what's not on the list)
For anyone reading this who isn't a developer:
Almost every command in Gramps now has a keyboard shortcut you can change,
found in Preferences → Keyboard Shortcuts. That includes the main menu and
toolbar, every view's own commands (People, Events, Places, etc.), the Note
editor's formatting buttons, and the small icon buttons inside editing
windows (like the "Private" lock icon, or the button that opens the date
picker). Right now that adds up to 189 shortcuts you can search, rebind, or
reset — with Gramps warning you if a new choice is already used somewhere
else.
What's not on that list: the underlined letters you see when you hold Alt
in an editing window (like the underlined "T" in "Type" or "N" in
"Nickname"). Those aren't separate commands — they're just a quick way to
jump straight to a specific box on the screen, built directly into that
box's label. Because they're tied to the label's own wording (and that
wording is translated differently in every language), there's no separate
"shortcut" underneath them to reassign. They'll keep working exactly as
they always have; they just won't show up in the Keyboard Shortcuts list.
How editor-dialog shortcuts become overridable
Since GtkBuilder wires
<accelerator>tags up entirely on its own with zeroPython hooks, there's no
Gio.SimpleActionto rebind. Converting ~24 editordialogs to the action-based system would have been a much larger, riskier
change (each dialog would need to become "application-aware" early, and
button wiring uses two different existing patterns). Instead,
Glade.__init__now rewrites the relevant<accelerator>XML attributes atload time based on a saved override, right alongside the existing macOS
Control→Meta remap it already did. Verified by round-tripping every
accelerator-bearing
.gladefile (with real widget types registered)through a real
Gtk.Builderunder Xvfb — all 26 load correctly.One consequence: conflict detection for these is scoped to the same dialog
only (different dialogs are never open at once, so the same key reused
across two unrelated dialogs isn't a real conflict) — confirmed empirically
during development, where global conflict-checking was flagging the same
key as "in use" across five unrelated dialogs.
Also fixed: Ctrl+C
Ctrl+C in the main window silently opened the Clipboard window — not
because it was bound to that action (
Ctrl+Bis), but becauseNavigationView.key_press_handlerhardcoded a rawGdk.KEY_ccheck thatcalled
copy_to_clipboard()directly, entirely outside the action/acceleratorsystem. It's now a normal
win.CopyToClipboardaction with the same defaultbinding, so it's visible and rebindable like everything else.
Notable findings during development
.gladefiles, nowsurfaced as warnings at startup instead of being silently invisible:
editpersonref.gladehas two different, simultaneously-visible buttons(
select,add_del) both hardcoded to Ctrl+S;importprogen.gladehastwo Private toggles both hardcoded to Ctrl+P. Not fixed in this PR.
Gtk.accelerator_parse/accelerator_namesilently drop virtual modifierslike
<Primary>without a real display connection, which would havequietly corrupted accelerator strings under this project's headless
(
GDK_BACKEND=-) test convention._normalize_accel()detects that caseand passes the value through unchanged instead.
Out of scope
Ctrl+C/V/X/A/Zinside aGtkEntry) —structurally unreachable from the action system.
Tag menu, bookmark-jump submenu, gramplet popups) and MRU
navigation-history jump slots — session/content-dependent, not fixed
named commands.
field, e.g. Alt+T for "_Type:") — see "In plain terms" above for why.
Update (2026-07-13)
shortcuts (default
Alt+O/Alt+C, registered viaregister_static_shortcuts) that work in any editor dialog. PlainGtk.Dialogwindows don't getGtkApplication's automaticaccelerator-to-action routing even once associated with the app via
set_application(), soManagedWindownow wires its ownGtk.AccelGroupand, on activation, clicks the dialog's actualOK/Cancel button (looked up via
get_widget_for_response) — mosteditors wire their save/cancel logic to the button's own
clickedsignal rather than the dialog's
responsesignal, so emittingresponse()directly would have been a no-op for them._OK/_Cancelmnemonics from editor.gladefiles now that these are driven by the shortcut above instead.
tooltip_text, but GTK's actual glade property istooltip-text, sotooltip-derived shortcut labels were silently never found. Also now
takes only the first line of a tooltip, since a few tooltips continue
with further explanation on subsequent lines.
"WindowManger"typo in the internal action-group name(
gramps/gui/managedwindow.py).WindowManageraction group from the shortcuteditor. Its actions are generated one-per-open-window for the
"Windows" switcher list (
wm-<window_id>, wherewindow_idis oftena Python
id()of a class/instance) — not a fixed command, so theaction only exists while that specific window happens to be open, and
its id isn't stable across app restarts. It was showing up in the
editor as a confusing, ever-changing "Preferences..." row (etc.)
alongside the real, static Application → Preferences... shortcut.
A new
_DYNAMIC_GROUP_NAMESset inuimanager.pynow excludes it(and is the place to add any future per-instance group).
matching how VS Code/JetBrains handle keymap profiles, instead of the
previous one-shot Load/Save import-export:
interface.keybinding-themeconfig key tracks which theme isactive; every rebind, clear, or reset now saves directly into that
theme's own file (
gramps/gui/uimanager.py's newtheme_dirs()/theme_path()helpers, shared between the editor and app startup)instead of a separate, anonymous
gramps.acceloverride file.user's
keybinding_themesdirectory the first time it's saved, sinceuser themes already take precedence over same-named bundled ones.
separate "Load"/"Switch Theme" button is gone, since every edit is
already persisted and there's never unsaved state to protect.
"Save…" is replaced by New Theme…, which forks the current
bindings under a new name and makes it active.
Reset to Factory Default / Reset All to Factory Defaults to
make explicit that they always read Gramps' live, hardcoded
accelerator defaults, not a theme's own saved values.
renders
<Primary>as the platform's own modifier (e.g. Cmd onmacOS), so the single bundled file was never actually PC-specific,
and the "-PC" suffix wrongly implied a separate Mac variant that
doesn't exist.
Update (2026-07-13, accessibility + theme file format)
Follow-up review raised an accessibility concern with the OK/Cancel change
above: removing
_OK/_Cancelalso removed the GTK-native acceleratorbinding on those buttons, which is what exposes a keybinding to assistive
technology through ATK — so a screen reader lost any indication that
Alt+O/Alt+C activate those buttons, not just the visual Alt-hold underline
for sighted keyboard users.
_OK/_Cancelmnemonics are restored in all ~42 editor-dialog gladefiles.
ManagedWindownow only intervenes when a user has actuallycustomized "Accept Dialog (OK)"/"Cancel Dialog" away from its default: it
strips the (now stale) mnemonic from that button's label, binds the real
key via
Gtk.Widget.add_accelerator()— a genuine widget-level GTKaccelerator, so it still shows up in the button's ATK keybinding, unlike
the previous bare
Gtk.AccelGroupcallback — and shows the currentshortcut as a tooltip, since an arbitrary user-chosen key (e.g.
F9)can't be represented as an underlined letter in a translated label at
all. Left at its default, the glade-defined mnemonic does all the work
untouched, exactly as before this whole feature existed.
Also switched the keyboard shortcut theme file format from a single
Python-dict-literal blob (
*.accel) to JSON Lines (*.jsonl), one{"id", "label", "category", "accel"}object per line:file is self-documenting when opened directly in a text editor, instead
of a bare
{action_id: accel}mapping that requires cross-referencingthe shortcuts editor to make sense of — these files live in a normal,
user-writable config directory, so hand-editing them was always possible
even though nothing documented it.
skipped rather than aborting the whole file, so a single typo in a
hand-edited theme no longer costs every other binding in it.
unreadable, or otherwise malformed) instead of crashing before the main
window ever appears, which a hand-edited file could previously trigger
via an uncaught
SyntaxErrorinGrampsApplication.do_startup().and categories initially filled in wherever mechanically derivable from
glade files and static action registrations (69 of 104 entries) —
superseded below by a script that generates it from a real, running
session instead.
Validating theme entries on load this way surfaced two pre-existing
check_accel()bugs, both now fixed: it rejectedLeft/Right/Up/Downwhenever used with any modifier, which would have flagged Gramps' own
default "Go Back"/"Go Forward" bindings (
<Alt>Left/<Alt>Right) asinvalid the moment the bundled theme was loaded; and it could not resolve
the virtual
<Primary>modifier without a live display connection, causingfalse rejections under this project's headless test convention
(
GDK_BACKEND=-).Update (2026-07-13, regeneration script)
The mechanical, display-less conversion of the bundled "Default" theme
mentioned above could only resolve labels for actions known statically at
startup (69 of 104 entries) — most ordinary per-view menu/toolbar actions
(e.g.
win.NewTag,win.SourceAdd) are only known once that view's realmenu XML has actually been built against a live display, which a headless
conversion can't do.
Added
scripts/regenerate_default_theme.pyto close that gap: it bootsthe real
UIManager/ViewManagerstack under a real X display (not thefull
GrampsApplication, since that would auto-open the Family Treemanager dialog) against an isolated, throwaway
GRAMPSHOME— so it neverreads or writes a developer's real settings, and never loads an existing
accel/theme file, only Gramps' true hard-coded defaults — visits every
registered view once so each view's own actions register, then calls
save_accels(only_changed=False)and copies the result overgramps/gui/keybinding_themes/Default.jsonl.Re-run it after adding, renaming, or removing a keyboard shortcut anywhere
in Gramps, then review the diff before committing. Running it against the
current tree raised coverage from 104 entries (69 labeled) to 189
entries (187 labeled) — the 2 that still come back unlabeled reflect a
real, pre-existing gap in Gramps' own menu labeling for those two actions
(
win.F2,win.PrintView), not a script limitation.save_accels()'sonly_changed=Falsebranch writes entriessorted()by id, so re-runningthe script is deterministic — confirmed by diffing two consecutive runs —
and a future re-run's diff will only ever show genuinely added, removed,
or changed shortcuts, never spurious reordering.
Update (2026-07-15, direct tab-jump shortcuts)
A reviewer pointed out that reaching a given tab in a primary-object editor
(Person, Family, Event, …) with only the keyboard meant tabbing through
every preceding widget — e.g. 12
Tabpresses just to reach the Eventslist before
Insertcould add anything. That's not specific to Events:the Person editor alone has 10 addable tabs (Events, Names, Source
Citations, Attributes, Addresses, Notes, Gallery, Internet, Associations,
LDS), all
ButtonTab-derived and already supportingInsert/Delete/Enter. A one-off "new event" shortcut wouldn't generalize; what wasmissing was a fast way to reach a tab in the first place.
Added
Alt+1..Alt+9to jump directly to notebook tab N, registered asstatic shortcuts (
app.dialog-goto-tab-1..9) right alongsidedialog-ok/dialog-cancel, so they show up in Preferences → KeyboardShortcuts and are fully rebindable/themeable. They're wired once in
EditPrimary._setup_notebook_tabs(shared by every primary-object editor),using the same per-dialog
Gtk.AccelGrouppattern as the existingdialog-ok/dialog-cancelwiring, since a bareGtk.Dialoggets noautomatic accel routing from
GtkApplication. Editors with fewer than 9tabs simply get fewer bound keys; the existing
Alt+Left/Alt+Rightsequential stepping is untouched. Net effect: "open Person, add an Event"
goes from ~13 keystrokes to 2 (
Alt+1,Insert).Testing
gramps/gui/test/uimanager_test.py(53 tests) — accel default tracking,label resolution, set/clear/reset, save/load round-trip in the new JSONL
format (including malformed-line, disallowed-accel, and unreadable-file
handling), static registration for never-instantiated actions, glade-scope
conflict detection, the modified-nav-key
check_accel()fix, andexclusion of the
WindowManagergroup fromlist_actions().gramps/gui/test/glade_test.py(15 tests) — XML scanning, modifierconversion (pure string ops, no display dependency), override
application, multiline-tooltip handling, and a smoke test parsing
every
.gladefile underGLADE_DIR.gramps/gui/test/managedwindow_test.py(4 tests, new) — the OK/Cancelaccelerator wiring: default binding leaves the mnemonic/tooltip untouched,
a customized binding strips the mnemonic and sets a tooltip, a cleared
binding strips the mnemonic with no tooltip, and non-dialog windows are a
no-op.
mypyandblack --checkclean across the full tree.scripts/regenerate_default_theme.pytwice in a row underxvfb-run, confirmed the two runs produce byte-identical output, andconfirmed the resulting
Default.jsonlloads with zero rejected entries..gladefile loaded through a realGtk.Builderunder Xvfb after rewriting, confirming the overridemechanism doesn't corrupt dialog layout.
GrampsApplication+ViewManager,isolated
GRAMPSHOME): opened Preferences → Keyboard Shortcuts andscreenshotted it; rebound a shortcut and confirmed it's written into
the active theme's file; used New Theme to fork a second theme and
confirmed the combo/config switch to it; switched back via the combo
and confirmed the (customized) bindings reloaded; used Reset to
Factory Default and confirmed it restores the true hardcoded default
rather than the theme's saved value, and that the reset itself is
then persisted back into the active theme's file.
confirmed
Alt+2/Alt+1/Alt+9jump to the corresponding tab; landedon Events via
Alt+1and confirmedInsertopens the Event ReferenceEditor with no mouse involved; opened the Note editor (2 tabs) and
confirmed
Alt+1/Alt+2work whileAlt+3..Alt+9are silent no-ops;rebound
Go to Tab 1toCtrl+1in Preferences and confirmed the newbinding takes effect immediately and the old
Alt+1no longer does.🤖 Generated with Claude Code