docs: the badge bar, the slide-over and the dock (step 1 of the redesign)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
a1672acc70
commit
0c94caf866
7 files changed
+203
-70
No files matched your search
@@ -60,11 +60,6 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
user; `udpscope-measure-symbolic` **stayed**, because the Preferences window's Plot page carries it --
|
||||
an icon with a live user is not an orphan, whatever the toggle that introduced it did -- and it is
|
||||
the Preferences window's icon.
|
||||
|
||||
The sidebar has **no tabs any more** (it is one dock): two tab glyphs (`udpscope-tab-measure-symbolic`,
|
||||
`udpscope-tab-signals-symbolic`, each a 32x16 pair) were drawn for them and judged not understandable
|
||||
enough, so the tabs were text, and both SVGs were deleted. The *trigger's* own icon was redrawn to the
|
||||
Signals glyph's picture at that time and kept it.
|
||||
- **A shortcut table row's `accel` is GTK accelerator syntax, and a pointer gesture has
|
||||
none.** `src/shortcuts.cpp`'s strings are handed to `AdwShortcutsItem`, which parses them
|
||||
with `gtk_accelerator_parse()` — so they are `"space"`, `"<Control>e"`, `"<Control>slash"`,
|
||||
@@ -249,9 +244,7 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
`update_side_visibility()` and the tick's per-frame block set the column's visibility from the user's wish
|
||||
alone (`sidebar_wanted`) and the dock's *sensitivity* from `have_signals`; the toolbar's Dock button stays
|
||||
sensitive throughout. The wish is persisted (`dock = true|false` in the session file) and a change of it
|
||||
schedules a save. There is no tab icon to keep clear any more: the `keep_tabs_icon_free()` workaround for
|
||||
`AdwViewSwitcherButton`'s placeholder `[x]` image went with the switcher, and `dock:` in the dump
|
||||
replaces the `switcher:` line.
|
||||
schedules a save. `dock:` in the dump reads the result.
|
||||
- **An *overlaid* sidebar covers the plot, so a drag out of it has nowhere to land.** `collapsed` in an
|
||||
`AdwOverlaySplitView` means the sidebar sits *on* the plot rather than beside it: the pointer is over the
|
||||
list, never over a drop target, and a signal can be dragged but not dropped. `row_drag_sidebar()` hides
|
||||
@@ -594,6 +587,21 @@ The `Help` submenu, the contextual help's "?" and the guided tour, and the traps
|
||||
grab to leak. The probe asserts it as a reading -- `controllers=` against the count taken before the
|
||||
tour opened -- rather than as a claim, because "no lingering grab" is exactly the sort of thing a
|
||||
later change can add back without noticing.
|
||||
- **Click-away coexists with the plot's gestures because the observer never claims the sequence.** The
|
||||
slide-over closes on a press anywhere outside it, and the plot underneath already owns pan, zoom, drop and
|
||||
right-click. A `GtkGestureClick` added to `plot_overlay` in `GTK_PHASE_CAPTURE` sees the press before the
|
||||
plot's own controllers and **does not call `gtk_gesture_set_state()`**, so the sequence stays unclaimed and
|
||||
the plot's gestures run exactly as before; claiming it (or putting a full-area target over the plot) would
|
||||
swallow the very pan the click was part of. The observer decides inside/outside by
|
||||
`gtk_widget_compute_point()` into the revealer plus `gtk_widget_contains()`, never by pick. The press
|
||||
that closes the panel still reaches the plot, which is accepted: it is one click and not a drag the user
|
||||
started on purpose. `[slideover]` reads `click_inside_closes=0 click_outside_closes=1`.
|
||||
- **`AdwToggleGroup` is the segmented switch** (libadwaita >= 1.7; the build asks for 1.8). It replaces the
|
||||
`AdwViewSwitcher` for a source with several panels (the E-1608: Analog out | Digital I/O). Its toggles are
|
||||
named by the slide-over page ids, and the group's `notify::active-name` is the *input*; writing it
|
||||
ourselves (`slideover_open` on a source page) goes through `SlideOver::syncing`, or the program's own
|
||||
write comes back as a click and re-opens the page it just opened. It is hidden unless a source page is
|
||||
open and there are two or more, which `[badges] switch_visible=` reads.
|
||||
- **The tour is one implementation over two windows.** `ui_impl::TourView` holds the overlay children
|
||||
and the walk's place in it (`tour_build`/`tour_begin`/`tour_advance`/`tour_end`/`tour_update`), and
|
||||
what differs between the windows is only the *stops*: a `TourStop` names a widget the owner resolves,
|
||||
@@ -602,15 +610,26 @@ The `Help` submenu, the contextual help's "?" and the guided tour, and the traps
|
||||
the view's `on_done`; the window's passes null, the chooser's sets `Config::tour_pending`, which is
|
||||
how the two halves are one tour. `App` carries its own `TourView` (`app.tour`), so the machinery is
|
||||
compiled once and the probe reads either window's state by the same fields.
|
||||
- **A stop declares the page it stands on, and the tour selects that page as the stop is entered.** A
|
||||
stop whose target lives on another page of a stack -- the window's `signals` (and `trigger`) stops
|
||||
on the sidebar's `signals` page, the chooser's "Two kinds of input" stop on its `waveforms` page --
|
||||
names that page in `TourStop::page`, and `show_step()` selects it from `TourView::page_stack`
|
||||
before anything is measured. The page switch is **not** followed by a one-shot measure: `show_step()`
|
||||
selects the page and `tour_update()`, which already runs every frame, fills the spotlight in on the
|
||||
frame *after*, when the new page has been laid out. A stack allocates the new page's widgets on the
|
||||
- **A stop declares the page it stands on, and the tour selects that page as the stop is entered --
|
||||
by a hook in the main window and by a stack in the chooser.** The main window has no page stack any
|
||||
more: its pages are the slide-over's. A `TourStop::page` there is a slide-over page id (`trigger`), and
|
||||
`TourView::on_page(page_user, page)` opens it -- or, for a stop whose `page` is null, **closes** the
|
||||
panel, so the card never points at the status line over an open panel. `tour_end()` calls
|
||||
`on_page(nullptr)` *only if a walk was running* (so Skip, Esc and the last Next close a panel the tour
|
||||
opened, while dismissing an idle tour does not close one the user opened). The chooser has no hook and
|
||||
keeps `TourView::page_stack` (an `AdwViewStack`, selected by name). `select_view_page()` is the single
|
||||
entry for both, and `show_step()` calls it before anything is measured.
|
||||
Two rules follow from a panel that *slides*. The page switch is **not** followed by a one-shot measure:
|
||||
`tour_update()`, which already runs every frame, fills the spotlight in on a later one, and for a stop
|
||||
with a page it waits for `TourView::page_settled` (the slide-over's `gtk_revealer_get_child_revealed()`)
|
||||
-- until then the target is moving, so the surface is dimmed with an empty hole. And **the hole is the
|
||||
target's bounds clipped by every `GtkScrolledWindow` above it** (`tour_target_bounds()`): a form taller
|
||||
than the panel's scroller overruns it, and an unclipped hole spotlights the badge bar and the status
|
||||
line under the panel. A target scrolled out entirely is a *measured, empty* rectangle (the function
|
||||
returns true), not a failure -- `tour_update()` zeroes the hole on it, because taking "false" for "not
|
||||
measured yet" keeps the previous frame's hole standing. A stack allocates a new page's widgets on the
|
||||
frame after the switch, so a measurement taken in the switch's own tick reads zero (the allocation
|
||||
bullet in [invariants-trigger-and-pinning.md](invariants-trigger-and-pinning.md)) -- and reading the
|
||||
bullet in [invariants-trigger-and-pinning.md](invariants-trigger-and-pinning.md)) -- reading the
|
||||
page *name* back is fine immediately, because that is state and not geometry.
|
||||
- **The stop filter runs on `get_mapped()`, not `is_visible()`, and that is what separates a wrong
|
||||
page from a collapsed pane.** GTK 4.24's `gtk_widget_is_visible()` walks only the widgets' own
|
||||
@@ -619,11 +638,14 @@ The `Help` submenu, the contextual help's "?" and the guided tour, and the traps
|
||||
`gtk_widget_set_child_visible()`, so a widget on another page, *and* one in a collapsed pane, both
|
||||
read `is_visible() == TRUE` (verified against 4.24.1) -- the old filter kept a stop it meant to
|
||||
drop and left the spotlight on a target with no allocation. `stop_is_walkable()` keeps a stop when
|
||||
its target is **mapped**, or when it names a page and the stack that page belongs to is mapped: a
|
||||
wrong page leaves the stack mapped, so the stop is kept and the page switch reveals it; a collapsed
|
||||
pane leaves the stack unmapped too, so no page can reveal it and the stop is dropped. The filter
|
||||
lives in `tour_begin()`, not in each window's `start_tour()`, both so the two windows cannot drift
|
||||
into two rules and because the rule needs the view's own `page_stack`.
|
||||
its target is **mapped**; a stop that names a page of a view with an `on_page` hook is **always**
|
||||
kept (the hook opens the page whatever the dock is doing, since the slide-over does not depend on it);
|
||||
and in a view with a `page_stack` a stop that names a page is kept when the stack is mapped -- a wrong
|
||||
page leaves the stack mapped, so the page switch reveals it, while a collapsed pane leaves it unmapped
|
||||
too and the stop is dropped. So the dock's stop is dropped in a window narrow enough to overlay it, and
|
||||
the main window's tour is four steps there (`[toursurface] collapsed: ... steps=4`). The filter
|
||||
lives in `tour_begin()`, not in each window's `start_tour()`, so the two windows cannot drift
|
||||
into two rules.
|
||||
- **The chooser's tour needs an overlay over its *whole* window, and `AdwWindow`'s content is
|
||||
replaced, not wrapped.** Two of the chooser's stops name header-bar buttons, and an overlay inside
|
||||
the `AdwToolbarView`'s `content` would give them negative coordinates and clamp the hole to the top
|
||||
@@ -657,8 +679,9 @@ The `Help` submenu, the contextual help's "?" and the guided tour, and the traps
|
||||
- **The `help figures:` line is captured on the tick, like every other size the dump prints.** It names
|
||||
ten surfaces' window-coordinate rectangles for the help's annotated figures, and the dump runs after
|
||||
the window is destroyed -- a `gtk_widget_compute_bounds()` there is the "no `gtk_widget_*` call in the
|
||||
dump" rule again. `is_visible()`, not `get_visible()`: a widget under a hidden parent -- the trigger
|
||||
summary while the pane is in configuration mode -- keeps its own flag true and reads 0x0, and a crop
|
||||
dump" rule again. `is_visible()` *and* `get_mapped()`, not `get_visible()`: a widget under a hidden parent -- the trigger
|
||||
summary while the pane is in configuration mode -- keeps its own flag true and reads 0x0 (and a widget on a closed slide-over page, `trigger_*`
|
||||
while the panel is shut, would read a bogus `630,46,0,0` without the mapped test), and a crop
|
||||
box drawn from that is a box over the wrong thing. Its numbers depend on the *window's* size, which
|
||||
under the documented `xvfb-run` is the window's 756x894 minimum (a 640x480 screen), so a run on a
|
||||
larger screen gives different rectangles for the same panels.
|
||||
Reference in new issue
Block a user