docs: fix five accuracy findings in the libadwaita and icon documentation

- CLAUDE.md: the libadwaita dependency paragraph now names both AdwDialog
  widgets (AdwShortcutsDialog and AdwAboutDialog), explains the shared
  on_dialog_closed() handler keyed by pointer-to-pointer, and covers
  both show_about() and show_shortcuts() in the AdwDialog sentence.
- CLAUDE.md: the icon-authoring invariant's explanation of how Icon= in
  the desktop entry resolves now names the installed copies under
  $XDG_DATA_HOME/icons/hicolor/scalable/apps and .../symbolic/apps,
  not the GResource (a desktop shell cannot read a compiled-in resource).
- CLAUDE.md: [help] probe example byte count corrected from 1381 to 1379
  (measured: wc -c src/help/01-overview.txt = 1379).
- README.md + udpscope.gresource.xml: "file, line and column" corrected
  to "file and line number (plus a caret under the offending character)"
  to match xmllint's actual output; also removes -- from the XML comment
  where it was illegal.
- README.md: desktop-file-validate described as a hard failure "when the
  tool is on PATH", matching cmake/install_desktop_entry.cmake lines 44-54.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino FerrariandClaude Sonnet 4.6 committed 2026-10-04 21:13:22 +02:00
1 parent e2be597698
commit 02c53bb7a0
3 files changed
+26 -17

No files matched your search

+22 -13
View File
@@ -44,16 +44,21 @@ form for a new host. Every probe below passes `--server`, so none of them sees i
vendored C99 source pair compiled into a static lib. vendored C99 source pair compiled into a static lib.
**libadwaita ≥ 1.8 is a system dependency** (`pkg_check_modules(ADWAITA ... libadwaita-1>=1.8)`), **libadwaita ≥ 1.8 is a system dependency** (`pkg_check_modules(ADWAITA ... libadwaita-1>=1.8)`),
for exactly one widget: the shortcut overview is an `AdwShortcutsDialog`. GTK4's own for two widgets: the shortcut overview (`AdwShortcutsDialog`) and the About dialog
`GtkShortcutsWindow` is deprecated as of 4.18 and that is its replacement. Two consequences (`AdwAboutDialog`). GTK4's own `GtkShortcutsWindow` is deprecated as of 4.18 and
worth knowing before editing. `adw_init()` is called — in `run_ui()` and, since the chooser `AdwShortcutsDialog` is its replacement; `GtkAboutDialog` still exists but
runs first and must be styled the same, in `choose_source()` in place of its `gtk_init()` `adw_about_window_new()` is deprecated in favour of `AdwAboutDialog`, which shows the
(both are idempotent) — so **the whole app is drawn with libadwaita's stylesheet**, not application icon as the page's own header. Two consequences worth knowing before editing.
GTK's: a metric change there reaches every widget, and `layout over N frames` / `adw_init()` is called — in `run_ui()` and, since the chooser runs first and must be styled
`axes.w changes` is what says whether the layout still settles. And an `AdwDialog` is the same, in `choose_source()` in place of its `gtk_init()` (both are idempotent) — so
**not** a `GtkWindow`: `show_shortcuts()` keeps an `AdwDialog*`, clears it on `::closed` **the whole app is drawn with libadwaita's stylesheet**, not GTK's: a metric change there
(there is no `::close-request`) and closes it with `adw_dialog_close()`. Presenting it on reaches every widget, and `layout over N frames` / `axes.w changes` is what says whether
this app's plain `GtkApplicationWindow` is supported and warning-free; it was checked. the layout still settles. And an `AdwDialog` is **not** a `GtkWindow`: both `show_about()`
and `show_shortcuts()` keep an `AdwDialog*`, cleared on `::closed` (there is no
`::close-request`) and closed with `adw_dialog_close()`. They share one `on_dialog_closed()`
handler keyed by a pointer-to-pointer (`gpointer slot` cast to `AdwDialog**`), so one
handler clears either pointer without knowing which dialog it is. Presenting either on this
app's plain `GtkApplicationWindow` is supported and warning-free; it was checked.
`deps/plotter` is upstream's own repository and this app's figure support was added `deps/plotter` is upstream's own repository and this app's figure support was added
there rather than worked around here: `Figure::set_min_y_gutter()` (a floor under the there rather than worked around here: `Figure::set_min_y_gutter()` (a floor under the
@@ -614,7 +619,7 @@ Two lines of the self-check output are assertions in disguise:
so the panes the tick rewrites are measured directly. The `[width-probe]` lines are the so the panes the tick rewrites are measured directly. The `[width-probe]` lines are the
per-change detail behind the `axes.w changes` count, and a step over 100 px dumps the widget per-change detail behind the `axes.w changes` count, and a step over 100 px dumps the widget
tree's wide subtrees — that is what the documented, intermittent ~320 px step wants read. tree's wide subtrees — that is what the documented, intermittent ~320 px step wants read.
- `[help] pages=14 parsed=14 shortest='Overview' 1381 B; shortcuts: app=12 plot=13; about version=… commit=… built=… settings=… icon=org.udpscope.app; closed ok` (stderr, `UDPSCOPE_PROBE_HELP_MS`) — **`parsed` must equal `pages`** and no page may be empty: a missing resource or markup Pango rejects renders a one-line stand-in instead of failing, so the window *looks* fine and only this count notices. `settings=` must be a real path (the same one the About dialog shows). **`icon=` must read `org.udpscope.app`** — it is read back through `adw_about_dialog_get_application_icon()` while the dialog is open, so the dialog is asserted to *carry* the icon rather than merely to open (a `set_application_icon()` lost in an edit leaves a dialog that opens, looks almost right, and shows a generic placeholder). `closed ok` with **no `Gtk-CRITICAL`** is as much a crash test of three windows built, presented and closed in one run as it is a state check, and a stronger one than it was: the three are now closed by **two different calls**, so a mismatched pair crashes or leaks here. One of the three is a `GtkWindow` (the help window, `gtk_window_close()`); the About dialog and the shortcut overview are `AdwDialog`s and close with `adw_dialog_close()`, cleared on `::closed`. `shortest=` is the page title — a page whose body was never compiled into the binary is absent rather than empty, and a title alone is the stand-in's shape. - `[help] pages=14 parsed=14 shortest='Overview' 1379 B; shortcuts: app=12 plot=13; about version=… commit=… built=… settings=… icon=org.udpscope.app; closed ok` (stderr, `UDPSCOPE_PROBE_HELP_MS`) — **`parsed` must equal `pages`** and no page may be empty: a missing resource or markup Pango rejects renders a one-line stand-in instead of failing, so the window *looks* fine and only this count notices. `settings=` must be a real path (the same one the About dialog shows). **`icon=` must read `org.udpscope.app`** — it is read back through `adw_about_dialog_get_application_icon()` while the dialog is open, so the dialog is asserted to *carry* the icon rather than merely to open (a `set_application_icon()` lost in an edit leaves a dialog that opens, looks almost right, and shows a generic placeholder). `closed ok` with **no `Gtk-CRITICAL`** is as much a crash test of three windows built, presented and closed in one run as it is a state check, and a stronger one than it was: the three are now closed by **two different calls**, so a mismatched pair crashes or leaks here. One of the three is a `GtkWindow` (the help window, `gtk_window_close()`); the About dialog and the shortcut overview are `AdwDialog`s and close with `adw_dialog_close()`, cleared on `::closed`. `shortest=` is the page title — a page whose body was never compiled into the binary is absent rather than empty, and a title alone is the stand-in's shape.
- `[keys] a: armed 0->1 | space: paused 0->1 | t: trigger panel: 0->1 | … | editable focused: 'm' refused=1` (stderr, `UDPSCOPE_PROBE_KEYS_MS`) — **every `X->Y` pair must differ** and **`refused` must be 1**. The refusal is the whole reason the controller is in the bubble phase: every bare-letter accel is also a character, so an accel that fires while a `GtkEditable` has focus makes the window entry unusable. A pair reading `0->0` is an action whose dispatch does not reach the handler a click reaches. Note the format is `<key>: <widget-description>: <before>-><after>` — the name passed to `before_after()` includes the widget's description, so `t: trigger panel: 0->1` is the correct form (not `t: trigger panel 0->1` without the colon before the values). - `[keys] a: armed 0->1 | space: paused 0->1 | t: trigger panel: 0->1 | … | editable focused: 'm' refused=1` (stderr, `UDPSCOPE_PROBE_KEYS_MS`) — **every `X->Y` pair must differ** and **`refused` must be 1**. The refusal is the whole reason the controller is in the bubble phase: every bare-letter accel is also a character, so an accel that fires while a `GtkEditable` has focus makes the window entry unusable. A pair reading `0->0` is an action whose dispatch does not reach the handler a click reaches. Note the format is `<key>: <widget-description>: <before>-><after>` — the name passed to `before_after()` includes the widget's description, so `t: trigger panel: 0->1` is the correct form (not `t: trigger panel 0->1` without the colon before the values).
- `[zoomkeys] ctrl+plus x=[..]->[..] y_held=1 | alt+plus y=[..]->[..] x_held=1` (stderr, `UDPSCOPE_PROBE_ZOOMKEYS_MS`) — **both `_held` must be 1 and both zooms must still happen.** Run it inside a pinned state, where a frame cannot re-frame the other axis for its own reasons: `UDPSCOPE_PROBE_PAUSE_MS=1500 UDPSCOPE_PROBE_ZOOMKEYS_MS=2200`. A `_held=0` is the modifier not reaching the plotter's controller; an unchanged range on the *zoomed* axis is the opposite failure, the key doing nothing. Ctrl restricts to x and Alt to y; Ctrl+Alt zooms neither (no special case needed — neither flag alone fires). - `[zoomkeys] ctrl+plus x=[..]->[..] y_held=1 | alt+plus y=[..]->[..] x_held=1` (stderr, `UDPSCOPE_PROBE_ZOOMKEYS_MS`) — **both `_held` must be 1 and both zooms must still happen.** Run it inside a pinned state, where a frame cannot re-frame the other axis for its own reasons: `UDPSCOPE_PROBE_PAUSE_MS=1500 UDPSCOPE_PROBE_ZOOMKEYS_MS=2200`. A `_held=0` is the modifier not reaching the plotter's controller; an unchanged range on the *zoomed* axis is the opposite failure, the key doing nothing. Ctrl restricts to x and Alt to y; Ctrl+Alt zooms neither (no special case needed — neither flag alone fires).
- `status bar: glance=[● live · 1.09 MS/s · 2 s window] max_glance_chars=53 detail_fields=11 popover_rebuilds=0` (exit dump) — **`max_glance_chars` must stay small** (under about 60) and **`popover_rebuilds` must be 0**. The status row is directly above the plot, so this is the "layout must not depend on content" rule read from the bar's own side: the state field is one word from a fixed set, the rate field is `%.3g` (so `10 kS/s`, never `10.0 kS/s`), and the only field trigger mode adds is the elapsed time, which steps units rather than growing digits. `detail_fields` is the popover's label count, a literal, so adding a label without thinking about layout shows up here. The glance rate format matters: `%.3g` drops the trailing zero that a `%.4g` or a fixed-precision format would keep — a rate that reads differently in the dump and in the help page is a format disagreement, and `14-status.txt`'s example must match it. - `status bar: glance=[● live · 1.09 MS/s · 2 s window] max_glance_chars=53 detail_fields=11 popover_rebuilds=0` (exit dump) — **`max_glance_chars` must stay small** (under about 60) and **`popover_rebuilds` must be 0**. The status row is directly above the plot, so this is the "layout must not depend on content" rule read from the bar's own side: the state field is one word from a fixed set, the rate field is `%.3g` (so `10 kS/s`, never `10.0 kS/s`), and the only field trigger mode adds is the elapsed time, which steps units rather than growing digits. `detail_fields` is the popover's label count, a literal, so adding a label without thinking about layout shows up here. The glance rate format matters: `%.3g` drops the trailing zero that a `%.4g` or a fixed-precision format would keep — a rate that reads differently in the dump and in the help page is a format disagreement, and `14-status.txt`'s example must match it.
@@ -1304,8 +1309,12 @@ far the largest file.
measurements and math. `src/icons/scalable/actions/udpscope-{trigger,cursors,measure,math}-symbolic.svg` measurements and math. `src/icons/scalable/actions/udpscope-{trigger,cursors,measure,math}-symbolic.svg`
are the four action icons; beside them in `src/icons/scalable/apps/` are the two are the four action icons; beside them in `src/icons/scalable/apps/` are the two
**application** icons, `org.udpscope.app.svg` (128px colour artwork) and **application** icons, `org.udpscope.app.svg` (128px colour artwork) and
`org.udpscope.app-symbolic.svg` (16px), whose `apps` context is where an icon theme keeps an `org.udpscope.app-symbolic.svg` (16px). The `apps` context is where an icon theme keeps an
application's own icon and is what makes `Icon=org.udpscope.app` in the desktop entry resolve. application's own icon and is where the **in-process** window icon resolves from; it is also
the layout that `cmake/install_desktop_entry.cmake` mirrors when it copies the same two files
into `$XDG_DATA_HOME/icons/hicolor/scalable/apps` and `.../symbolic/apps` — those installed
copies are what makes `Icon=org.udpscope.app` in the desktop entry resolve (a desktop shell
cannot read a GResource compiled into the binary).
All six are bundled by `src/icons/udpscope.gresource.xml` (CMake runs All six are bundled by `src/icons/udpscope.gresource.xml` (CMake runs
`glib-compile-resources`) and reached through **one** `glib-compile-resources`) and reached through **one**
`gtk_icon_theme_add_resource_path()` in `run_ui()`, right after `adw_init()` — not at the top `gtk_icon_theme_add_resource_path()` in `run_ui()`, right after `adw_init()` — not at the top
+2 -2
View File
@@ -287,7 +287,7 @@ the four action icons are: udpscope is run straight out of its build tree and ha
The one consequence worth knowing is that a **malformed** SVG cannot be caught at runtime -- the The one consequence worth knowing is that a **malformed** SVG cannot be caught at runtime -- the
icon *name* still resolves, so GTK reports the icon as present and draws nothing -- which is why icon *name* still resolves, so GTK reports the icon as present and draws nothing -- which is why
every `<file>` in the manifest carries `preprocess="xml-stripblanks"` and the build fails with a every `<file>` in the manifest carries `preprocess="xml-stripblanks"` and the build fails with a
file, line and column instead. file and line number (plus a caret under the offending character) instead.
**The About dialog is an `AdwAboutDialog`** rather than a `GtkAboutDialog`, and that migration is **The About dialog is an `AdwAboutDialog`** rather than a `GtkAboutDialog`, and that migration is
what the app has an icon *for*: the dialog shows it in its own header, which `GtkAboutDialog` what the app has an icon *for*: the dialog shows it in its own header, which `GtkAboutDialog`
@@ -313,7 +313,7 @@ cmake --build build --target desktop-entry
``` ```
That writes `org.udpscope.app.desktop` and both app icons under `$XDG_DATA_HOME` (default That writes `org.udpscope.app.desktop` and both app icons under `$XDG_DATA_HOME` (default
`~/.local/share`), validates the entry with `desktop-file-validate` as a hard failure, refreshes `~/.local/share`), validates the entry with `desktop-file-validate` as a hard failure when the tool is on `PATH`, refreshes
`update-desktop-database` and `gtk4-update-icon-cache` if they are on `PATH`, and prints the three `update-desktop-database` and `gtk4-update-icon-cache` if they are on `PATH`, and prints the three
paths it wrote. Those three lines **are** the uninstall instructions: there is no uninstall paths it wrote. Those three lines **are** the uninstall instructions: there is no uninstall
target, because three `rm`s the build already told you about are less to maintain than a target target, because three `rm`s the build already told you about are less to maintain than a target
+2 -2
View File
@@ -12,8 +12,8 @@
such a file outright, but the icon *name* still resolves, so `gtk_icon_theme_has_icon()` such a file outright, but the icon *name* still resolves, so `gtk_icon_theme_has_icon()`
returns true and the icon simply draws nothing. No runtime GTK call can tell the two apart returns true and the icon simply draws nothing. No runtime GTK call can tell the two apart
(`lookup_icon()` never returns NULL and always reports the size it was asked for), so the (`lookup_icon()` never returns NULL and always reports the size it was asked for), so the
check belongs here, where a double hyphen inside a comment fails the build with a file, check belongs here, where a malformed SVG fails the build with a file and line number
line and column instead. (plus a caret under the offending character) instead.
--> -->
<gresources> <gresources>
<gresource prefix="/org/udpscope/icons"> <gresource prefix="/org/udpscope/icons">