You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Improve Avalonia log UI and tab navigation shortcuts (#179)
* Improve the displaying of Avalonia Desktop app log messages.
Make the log text selectable, and add right click menu for copy.
Add copy all log button.
Improve automation capability with shortcuts for the different tabs at lower part of UI.
Copy file name to clipboardExpand all lines: doc/APPS_AVALONIA_AUTOMATION.md
+56-16Lines changed: 56 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -112,43 +112,83 @@ A non-exhaustive list of the most useful AutomationIds, grouped by view. All of
112
112
-`StatisticsView` (root)
113
113
-`C64InfoView` (root, keyboard mapping reference)
114
114
115
-
# Keyboard shortcuts (system menu contributions)
115
+
# Keyboard shortcuts
116
116
117
-
Some controls inside nested `UserControl`s do not traverse cleanly to the macOS AX tree (see "known gaps" below — the left-pane `C64MenuView` sections are the most visible example). To keep those operations reachable for agents and keyboard users, the active system's menu ViewModel implements `ISystemMenuContributor` ([`Core/SystemSetup/ISystemMenuContributor.cs`](../src/apps/Avalonia/Highbyte.DotNet6502.App.Avalonia.Core/SystemSetup/ISystemMenuContributor.cs)) and contributes:
118
-
- A `NativeMenu` that Avalonia installs on the **macOS system menu bar** (shown under a top-level header for the active system, e.g. `C64`). On macOS, `NativeMenu` items appear in the OS-level menu bar *outside* the app window — which is the desired UX. The macOS Accessibility API also exposes these items with their `Gesture` string, making shortcuts self-describing: an AI agent can discover them at runtime via `peekaboo menu list` without needing any prior documentation.
119
-
- A parallel list of `KeyBinding`s applied to the main window on **Windows / Linux**. `NativeMenu` on these platforms would render as in-window chrome, which is not desired, so `KeyBinding`s are used instead. The shortcuts fire regardless of which child control has focus, but they are invisible to accessibility tools — an automation agent needs to know them in advance (e.g. from this document).
117
+
The app exposes two layers of shortcuts:
120
118
121
-
`MainViewModel.ActiveMenuContributor` swaps when `SelectedSystemName` changes; `MainView.axaml.cs` applies the new menu / keybindings, and clears the previous one on teardown.
119
+
1.**General tab-navigation shortcuts** — always active, independent of which emulator system is running.
120
+
2.**System-specific shortcuts** — active only when a particular system is selected (e.g. C64). The active system's menu ViewModel implements `ISystemMenuContributor` ([`Core/SystemSetup/ISystemMenuContributor.cs`](../src/apps/Avalonia/Highbyte.DotNet6502.App.Avalonia.Core/SystemSetup/ISystemMenuContributor.cs)).
121
+
122
+
On macOS both layers appear in the OS-level **system menu bar** (outside the app window) — general shortcuts under a `View` top-level menu, system-specific shortcuts under the system name (e.g. `C64`). The macOS Accessibility API exposes these with their `Gesture` string, so an AI agent can discover them at runtime without prior documentation:
123
+
124
+
```sh
125
+
peekaboo menu list --app "DotNet 6502 Emulator"
126
+
```
127
+
128
+
On **Windows / Linux**, `NativeMenu` would render as in-window chrome (not desired), so `KeyBinding`s registered on the main window are used instead. They fire regardless of which child control has focus, but are invisible to accessibility tools — an agent needs to know them from this document.
129
+
130
+
## Tab navigation shortcuts (always active)
131
+
132
+
These shortcuts jump directly to a named tab regardless of tab order — reordering tabs in code does **not** break automation scripts.
On Windows / Linux, the same shortcuts are dispatched by the main window's key bindings; an automation harness simulates the key combo instead of clicking a menu.
144
-
145
168
# What is NOT surfaced (known gaps)
146
169
147
170
1.**Individual `TabItem` controls on macOS** — verified with `peekaboo see` after running the app. The `InformationTabControl` surfaces, but its `TabItem` children (`InformationTab`, `LogTab`, etc.) do not appear as distinct clickable elements in the AX tree, *despite* having explicit `AutomationProperties.AutomationId` + `Name`. The AX tree on macOS reports roles limited to `button`, `group`, `menu`, `other`, `slider` — no `AXTabGroup` / `AXTab`.
148
171
149
172
This is most likely an Avalonia `TabItemAutomationPeer` / macOS NSAccessibility bridge limitation, not a bug in this codebase. Worth filing an issue upstream in `avaloniaui/Avalonia`.
150
173
151
-
**Workaround**: click the tab by screen coordinates (see the peekaboo section below), or use keyboard navigation (`Ctrl+Tab` / arrow keys when the tab control is focused).
174
+
**Workaround**: use keyboard navigation — this is the **reliable** approach. Find the `InformationTabControl` element via `peekaboo see`, click it to give it focus, then press the right-arrow key once per tab step:
175
+
176
+
```sh
177
+
# Capture the AX tree and find InformationTabControl's elem_NN
The number of right-arrow presses depends on the **currently active tab**, not a fixed offset. If "Information" is active, pressing right twice reaches "Log". If another tab is already active, adjust accordingly.
190
+
191
+
**Avoid** clicking by screen coordinates for tabs: coordinates are window-size-dependent and scale across display densities. **Avoid**`peekaboo click "Log"`: text-query matching is global and can hit an element with the same label in another app or inside the tab's content area (e.g. Ghostty's "Log Out" menu).
152
192
153
193
2.**Collapsed/conditional content** only appears in the AX tree when its container is rendered. Examples:
154
194
-`C64MenuView` section contents (`DiskSectionContent`, `LoadSaveSectionContent`, `ConfigSectionContent`) — only visible when the section header is expanded.
-**Don't use `--no-auto-focus` from a terminal.** The terminal emulator (e.g. Ghostty) reclaims focus between commands, so a `--no-auto-focus` click lands on the terminal window at the same screen coordinates — `click` still reports "✅ Click successful" but against the wrong app. Let peekaboo's auto-focus bring the Avalonia window forward.
226
266
227
-
-**Text-query clicks can hit the wrong element.**`peekaboo click "Log"`may match a `TextBlock` labelled "Log" *inside* the tab content rather than the tab header, because the header is behind the Avalonia TabItem AX gap described above. When targeting tabs specifically, use coordinates.
267
+
-**Text-query clicks are unreliable for tabs — and can hit other apps.**`peekaboo click "Log"`searches globally across all visible AX elements. It can match a label *inside the tab content*, a menu item in another app (e.g. Ghostty's "Log Out" item), or any other element named "Log" that happens to be on screen. For tab navigation, always use the keyboard approach described in "Known Gaps" item 1 above.
228
268
229
269
-**Screenshot coordinates vs. screen coordinates.** The annotated screenshot from `peekaboo see --annotate` is scaled to roughly 0.75× the window-point size. To convert a pixel position in the screenshot to a click coordinate, scale by ~1.33× and add the window's screen offset (`peekaboo list` shows the window Position).
0 commit comments