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
@@ -46,13 +46,13 @@ The detail lives in `docs/claude/`. Read the files that touch the area you are e
|
|||||||
|
|
||||||
| File | What it covers |
|
| File | What it covers |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| [architecture.md](docs/claude/architecture.md) | The two threads and what they share; the one `BufferStore` and the four display modes (`Live`, `Capturing`, `Held`, `Waiting`); the display clock and `stream_at_x0()`; the layers to keep straight (`SignalInfo` → `ChannelBuffer` → `SlotSeries` → `RowSet`); the source plugins. |
|
| [architecture.md](docs/claude/architecture.md) | The two threads and what they share; the one `BufferStore` and the four display modes (`Live`, `Capturing`, `Held`, `Waiting`); the display clock and `stream_at_x0()`; the layers to keep straight (`SignalInfo` → `ChannelBuffer` → `SlotSeries` → `RowSet`); the source plugins; the window's chrome (slide-over, badge bar, dock) and when each is built. |
|
||||||
|
|
||||||
### Invariants worth knowing before editing
|
### Invariants worth knowing before editing
|
||||||
|
|
||||||
| File | What it covers |
|
| File | What it covers |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| [invariants-plot-and-layout.md](docs/claude/invariants-plot-and-layout.md) | What `plot::Figure`/`plot::Plot` can and cannot be asked to do, what a row's contents belong to, the layout editor's overlay children, and the rule that **layout must not depend on content**. |
|
| [invariants-plot-and-layout.md](docs/claude/invariants-plot-and-layout.md) | What `plot::Figure`/`plot::Plot` can and cannot be asked to do, what a row's contents belong to, the layout editor's and the slide-over's overlay children (with the pick rule), the badge bar's fixed height, and the rule that **layout must not depend on content**. |
|
||||||
| [invariants-readouts-and-cursors.md](docs/claude/invariants-readouts-and-cursors.md) | A readout quotes a stored sample and never a value between two; how the cursors are read back from the plots; the one formatter both panes share. |
|
| [invariants-readouts-and-cursors.md](docs/claude/invariants-readouts-and-cursors.md) | A readout quotes a stored sample and never a value between two; how the cursors are read back from the plots; the one formatter both panes share. |
|
||||||
| [invariants-data-path.md](docs/claude/invariants-data-path.md) | What rebuilds the buffer and drops history, why decimation at ingest is an **average** and never a stride, and the two rules the `f64` time ring and its cached first stamp impose. |
|
| [invariants-data-path.md](docs/claude/invariants-data-path.md) | What rebuilds the buffer and drops history, why decimation at ingest is an **average** and never a stride, and the two rules the `f64` time ring and its cached first stamp impose. |
|
||||||
| [invariants-rendering.md](docs/claude/invariants-rendering.md) | How the renderer maps a visible range onto samples (never the `dx()` grid), what `gather()` emits and relative to which origin, why every series needs a real `MinMaxPyramid`, and why a dense trace is drawn one pixel column at a time. |
|
| [invariants-rendering.md](docs/claude/invariants-rendering.md) | How the renderer maps a visible range onto samples (never the `dx()` grid), what `gather()` emits and relative to which origin, why every series needs a real `MinMaxPyramid`, and why a dense trace is drawn one pixel column at a time. |
|
||||||
@@ -61,4 +61,4 @@ The detail lives in `docs/claude/`. Read the files that touch the area you are e
|
|||||||
| [invariants-measurements.md](docs/claude/invariants-measurements.md) | Why the pane is refreshed on a cadence, why neither pass may read its window raw, how a crossing is counted with hysteresis, and which mean over a split is which. |
|
| [invariants-measurements.md](docs/claude/invariants-measurements.md) | Why the pane is refreshed on a cadence, why neither pass may read its window raw, how a crossing is counted with hysteresis, and which mean over a split is which. |
|
||||||
| [invariants-math-channels.md](docs/claude/invariants-math-channels.md) | A math channel *is* a channel; the engine runs on the producer thread, so a regression there costs packets and not frames; the node editor's own rules. |
|
| [invariants-math-channels.md](docs/claude/invariants-math-channels.md) | A math channel *is* a channel; the engine runs on the producer thread, so a regression there costs packets and not frames; the node editor's own rules. |
|
||||||
| [invariants-session-and-export.md](docs/claude/invariants-session-and-export.md) | What a session file persists and in what order, when a save is refused, and when an export is collected. |
|
| [invariants-session-and-export.md](docs/claude/invariants-session-and-export.md) | What a session file persists and in what order, when a save is refused, and when an export is collected. |
|
||||||
| [invariants-gtk-and-chrome.md](docs/claude/invariants-gtk-and-chrome.md) | The GTK signal-signature and dialog-lifetime traps this app has already fallen into, the status bar's two tiers, the bundled icons and their authoring rules, and how the Mallard manual is opened. |
|
| [invariants-gtk-and-chrome.md](docs/claude/invariants-gtk-and-chrome.md) | The GTK signal-signature and dialog-lifetime traps this app has already fallen into, the status bar's two tiers, the bundled icons and their authoring rules, how the slide-over's click-away coexists with the plot's gestures, how the guided tour opens a page, and how the Mallard manual is opened. |
|
||||||
@@ -73,7 +73,7 @@ switches trigger mode on, waits for an event, then leaves trigger mode the way t
|
|||||||
"Go live" button does, closes the window, and prints on stdout the status line in
|
"Go live" button does, closes the window, and prints on stdout the status line in
|
||||||
both states, the frame and per-channel sample counts, each plot's painted-frame
|
both states, the frame and per-channel sample counts, each plot's painted-frame
|
||||||
count, which channel each trace slot holds, and the allocated size and reveal
|
count, which channel each trace slot holds, and the allocated size and reveal
|
||||||
state of both side panels.
|
state of the dock and the events pane.
|
||||||
|
|
||||||
Every size it prints is read from a value the frame tick captured while the window was alive, not
|
Every size it prints is read from a value the frame tick captured while the window was alive, not
|
||||||
from the widget: the dump runs from the destroy handler, where the widget pointer is already freed
|
from the widget: the dump runs from the destroy handler, where the widget pointer is already freed
|
||||||
@@ -96,9 +96,24 @@ src/plot_host.* the one plot::Figure, its rows' traces and cursors, the x lin
|
|||||||
src/export.* the visible range as a table, and the .npy / .csv / .mf3 writers
|
src/export.* the visible range as a table, and the .npy / .csv / .mf3 writers
|
||||||
src/layout_store.* the per-source session layout file (TOML), written and read back
|
src/layout_store.* the per-source session layout file (TOML), written and read back
|
||||||
src/event_series.hpp an immutable captured event as a plot::data::Series
|
src/event_series.hpp an immutable captured event as a plot::data::Series
|
||||||
src/ui.* the GTK4 window: toolbar, trigger panel, signal panel, plot box
|
src/ui.* the GTK4 window: toolbar, trigger form, signal list, plot box, the dock
|
||||||
|
src/ui_slideover.cpp the slide-over panel over the plot's right edge, one page at a time
|
||||||
|
src/ui_badges.cpp the badge bar under the plot: the Trigger and Source badges
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**The window.** The plot is the window. Under it sits a fixed-height **badge bar** (36 px) whose
|
||||||
|
right-hand end carries two words-only badges: **Trigger** (`off`, `armed`, `capturing`, `triggered`
|
||||||
|
or `disarmed`) and **Source** (the kind's name and `connected`/`disconnected`; a read-out, not a button,
|
||||||
|
for a source with no controls of its own, and for a file). A badge opens the **slide-over**: a 360 px
|
||||||
|
panel laid over the plot's right edge, opaque, with its own title, a *Close* button and one scroller per
|
||||||
|
page. The Trigger badge opens the trigger form; the Source badge opens the source kind's own panels
|
||||||
|
(the E-1608's *Analog out* and *Digital I/O*, behind a segmented switch at the top of the panel). It
|
||||||
|
closes with a second click on its badge, with `Esc`, with *Close*, or with a click on the plot; it never
|
||||||
|
resizes the plot. The right-hand **dock** (the existing sidebar, 320 px) holds what is left of the old
|
||||||
|
tabs in one scroller: the signal list, the cursors and the measurements, shown from the start and
|
||||||
|
insensitive until the stream has signals. It is temporary -- see "Review findings not (yet)
|
||||||
|
implemented" for what the redesign still has to do with it.
|
||||||
|
|
||||||
Threading: one producer thread owns the data source (the UDPStreamer client is the built-in
|
Threading: one producer thread owns the data source (the UDPStreamer client is the built-in
|
||||||
one) and calls `poll()`; the GTK main thread owns every widget and the GL contexts.
|
one) and calls `poll()`; the GTK main thread owns every widget and the GL contexts.
|
||||||
They share `ScopeModel` under one mutex, which the producer takes once per DATA
|
They share `ScopeModel` under one mutex, which the producer takes once per DATA
|
||||||
@@ -1006,7 +1021,9 @@ offset = 0.5
|
|||||||
```
|
```
|
||||||
|
|
||||||
It also stores `measure_stats` (the measurements statistics toggle, a boolean, absent means
|
It also stores `measure_stats` (the measurements statistics toggle, a boolean, absent means
|
||||||
`false`). It is written (400 ms after the last change, and flushed on close) whenever the
|
`false`) and `dock` (whether the dock is shown, a boolean, absent means `true`; it replaced the
|
||||||
|
sidebar's `side_tab`, and a file that still carries `side_tab` loads, the key being ignored). The
|
||||||
|
open slide-over page is not stored: a panel that opens by itself at start-up would be a surprise. It is written (400 ms after the last change, and flushed on close) whenever the
|
||||||
window, the layout, a drop or a calibration changes, and applied once, when the
|
window, the layout, a drop or a calibration changes, and applied once, when the
|
||||||
first CONFIG names the signals -- signal *names* are the identity, because a
|
first CONFIG names the signals -- signal *names* are the identity, because a
|
||||||
CONFIG renumbers and an enable/disable rebuilds the channel list. Signals the
|
CONFIG renumbers and an enable/disable rebuilds the channel list. Signals the
|
||||||
@@ -1151,18 +1168,23 @@ up, the same delay the first-run auto-start uses, the pending request starts the
|
|||||||
chooser's rows show **no accelerators** -- the window prints `F1`/`Ctrl+T` because the window binds
|
chooser's rows show **no accelerators** -- the window prints `F1`/`Ctrl+T` because the window binds
|
||||||
them, and this dialog binds none.
|
them, and this dialog binds none.
|
||||||
|
|
||||||
**The guided tour is five steps on a `GtkOverlay`** inserted above the window's content: the plot,
|
**The guided tour is six steps on a `GtkOverlay`** inserted above the window's content: the plot,
|
||||||
the side panel, the trigger, the signals and the status line. There is no popover anywhere in it and
|
the badge bar, the trigger form, the dock, the signals and the status line. There is no popover anywhere in it and
|
||||||
therefore no pointer grab to lose: what makes the tour modal is four dim rectangles -- above, below
|
therefore no pointer grab to lose: what makes the tour modal is four dim rectangles -- above, below
|
||||||
and either side of the target -- that swallow the clicks that land on them. A stop declares the
|
and either side of the target -- that swallow the clicks that land on them. A stop declares the
|
||||||
**page it stands on** (the trigger and the signals steps live on the sidebar's *Signals* page, named
|
**slide-over page it stands on** (the trigger step names `trigger`, in `TourStop::page`): the tour opens
|
||||||
in `TourStop::page`), and the tour selects that page as the step is entered -- on the frame *after*
|
that page as the step is entered, and a stop with no page *closes* the panel, so the card never points
|
||||||
the switch, since a stack allocates a new page's widgets a frame late -- so the spotlight lands on a
|
at the status line over an open panel. It does not spotlight the form until the panel has finished
|
||||||
widget the user had left behind on the other tab instead of on nothing. `gtk_widget_get_mapped()`
|
sliding in (about 120 ms, during which the surface is dimmed and the card waits in the corner), and the
|
||||||
decides which steps exist at all: a stop on the *wrong page* is kept, because selecting its page
|
hole is the target's bounds clipped by every scroller above it -- on the documented 756x470 window the
|
||||||
reveals it, while one whose *pane is collapsed* is dropped, because the stack is unmapped with it
|
trigger form is taller than the panel, and the spotlight covers the part that is on screen, not the
|
||||||
(a hidden page keeps its own *visible* flag true -- the widget's own flag, which is all
|
badge bar under it. Skip, Escape and the last step close a panel the tour opened (an idle dismissal
|
||||||
`is_visible()` walks -- so the old test walked over surfaces that were not on screen). The chooser's
|
leaves one the user opened alone). `gtk_widget_get_mapped()`
|
||||||
|
decides which steps exist at all: a stop on a slide-over page is always kept, because opening the page
|
||||||
|
reveals it, while one whose *pane is collapsed* (the dock, in a window too narrow to put it beside the
|
||||||
|
plot) is dropped, because it is unmapped with its pane (a hidden page keeps its own *visible* flag
|
||||||
|
true -- the widget's own flag, which is all `is_visible()` walks -- so the old test walked over
|
||||||
|
surfaces that were not on screen); a narrow window therefore walks four steps. The chooser's
|
||||||
tour works the same way over its own two pages: its tabs step shows the *Waveforms* page and
|
tour works the same way over its own two pages: its tabs step shows the *Waveforms* page and
|
||||||
stepping off it -- in either direction -- brings the *Sources* page back.
|
stepping off it -- in either direction -- brings the *Sources* page back.
|
||||||
|
|
||||||
@@ -2780,12 +2802,13 @@ reaches every widget). The full key table:
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `a` | arm / disarm the trigger |
|
| `a` | arm / disarm the trigger |
|
||||||
| `Space` | pause / resume |
|
| `Space` | pause / resume |
|
||||||
| `t` | show / hide the trigger panel |
|
| `t` | enable / disable trigger mode |
|
||||||
| `c` | show / hide cursors |
|
| `c` | enable / disable the cursors |
|
||||||
| `m` | show / hide measurements |
|
| `m` | go to Measure (scroll the dock to the cursors and the measurements) |
|
||||||
| `g` | show / hide math channels |
|
| `g` | go to Signals (scroll the dock back to the signal list) |
|
||||||
| `l` | show / hide the layout editor |
|
| `l` | show / hide the layout editor |
|
||||||
| `s` | show / hide the side panel |
|
| `s` | show / hide the dock |
|
||||||
|
| `Esc` | close the slide-over panel (the guided tour takes it first) |
|
||||||
| `f` | fit to data (the plot's own key, not an application accelerator) |
|
| `f` | fit to data (the plot's own key, not an application accelerator) |
|
||||||
| `Ctrl+E` | export visible range |
|
| `Ctrl+E` | export visible range |
|
||||||
| `F1` | open the manual (`help:org.udpscope.app`) in the desktop help viewer |
|
| `F1` | open the manual (`help:org.udpscope.app`) in the desktop help viewer |
|
||||||
@@ -2897,32 +2920,42 @@ now — they are listed so they are not mistaken for anything else:
|
|||||||
progress. What the spec was protecting still holds — a *held* event is bound to its
|
progress. What the spec was protecting still holds — a *held* event is bound to its
|
||||||
snapshot alone, and the axis is the event's own window from the edge onwards rather
|
snapshot alone, and the axis is the event's own window from the edge onwards rather
|
||||||
than a sliding live view.
|
than a sliding live view.
|
||||||
- **The sidebar is two tabs, and a math channel is a row of the signal list.** `SPECS.md`
|
- **The sidebar is one dock, setup is a slide-over, and a math channel is a row of the signal list.**
|
||||||
names the panes as separate collapsible sections — trigger, cursors, measurements, math,
|
`SPECS.md` names the panes as separate collapsible sections -- trigger, cursors, measurements, math,
|
||||||
signal list — in one scrolling column; with four of them open in a 320 px sidebar every
|
signal list -- in one scrolling column; with four of them open in a 320 px sidebar every
|
||||||
section was short of room, and finding one meant scrolling past the others. The built
|
section was short of room, and finding one meant scrolling past the others. The sidebar was then two
|
||||||
sidebar is a **Measure** tab (the cursors readout and the measurements) and a **Signals**
|
text tabs (a **Measure** tab and a **Signals** tab, with a source kind's own tabs beside them), and four
|
||||||
tab (the trigger and the channels), **each a text tab** (the sidebar was icon-only for a while; glyphs for "cursors and ruler" and
|
tabs did not fit 320 px. It is **one dock** now, with no tab switcher: the signal list, the cursors and the
|
||||||
"trigger and channel list" were not understandable enough, so the words won). A source kind's own tabs
|
measurements in one scroller, shown from the start and insensitive until the stream has signals; `m` and
|
||||||
(the E-1608's **Analog out** and **Digital I/O**) sit beside them as text too. Both panes also have a **locked** mode, which is what a returning user gets: the
|
`g` scroll it to the cursors and back to the signal list. The **trigger form** and a **source's own
|
||||||
|
controls** are pages of the slide-over, reached from the badge bar. Both panes also have a **locked** mode, which is what a returning user gets: the
|
||||||
trigger pane shows one line saying what it is set to, the signal list shows only the signals that are
|
trigger pane shows one line saying what it is set to, the signal list shows only the signals that are
|
||||||
on, and neither offers a control that changes the configuration — each heading has an `[edit]` button
|
on, and neither offers a control that changes the configuration -- each heading has an `[edit]` button
|
||||||
to switch to the full pane, and back. Dragging a signal onto a plot still works while locked — filling
|
to switch to the full pane, and back. Dragging a signal onto a plot still works while locked -- filling
|
||||||
plots is what the list is for, not configuration — and a locked row can be dragged from anywhere on it,
|
plots is what the list is for, not configuration -- and a locked row can be dragged from anywhere on it,
|
||||||
the colour chip included. In a window too narrow to show both, where the sidebar is *overlaid on* the
|
the colour chip included. In a window too narrow to show both, where the dock is *overlaid on* the
|
||||||
plot, the sidebar steps aside for the length of the drag so there is something under the pointer to drop
|
plot, the dock steps aside for the length of the drag so there is something under the pointer to drop
|
||||||
onto. A **first run** (no session file for that source) comes up
|
onto. A **first run** (no session file for that source) comes up
|
||||||
unlocked, because configuring the host is what a first run is for. Each pane's heading also carries its **own switch**, the same control as the
|
unlocked, because configuring the host is what a first run is for. Each pane's heading also carries its **own switch**, the same control as the
|
||||||
header bar's button — clicking either moves both — because a pane that is *disabled* while its
|
header bar's button -- clicking either moves both -- because a pane that is *disabled* while its
|
||||||
feature is off is a pane whose controls you would otherwise have to leave to turn it back on. The
|
feature is off is a pane whose controls you would otherwise have to leave to turn it back on. With the math
|
||||||
sidebar is one **dock** now, with no tab switcher: the signal list, the cursors and the measurements
|
list folded into the signal list under a `MATH` divider whose `+` creates a channel -- a derived channel already *is* a channel in
|
||||||
in one scroller, shown from the start and insensitive until the stream has signals; `m` and `g`
|
|
||||||
scroll it to the cursors and back to the signal list. With the math
|
|
||||||
list folded into the signal list under a `MATH` divider whose `+` creates a channel — a derived channel already *is* a channel in
|
|
||||||
the model, so the two lists were the same list drawn twice. No
|
the model, so the two lists were the same list drawn twice. No
|
||||||
toggle hides a pane any more: cursors-off **disables** its readout and leaves it in place, and
|
toggle hides a pane any more: cursors-off **disables** its readout and leaves it in place, and
|
||||||
trigger-off leaves the trigger panel fully editable — a trigger is configured *before* it is
|
trigger-off leaves the trigger form fully editable -- a trigger is configured *before* it is
|
||||||
switched on, and the locked view says `stream (trigger off)` instead of the settings — and the measurements' and math panes' toolbar toggles are gone with the panes.
|
switched on, and the locked view says `stream (trigger off)` instead of the settings -- and the measurements' and math panes' toolbar toggles are gone with the panes.
|
||||||
|
- **The badge bar / slide-over / dock redesign is in progress; this is its first step, and it deviates
|
||||||
|
from `docs/superpowers/specs/2026-11-02-ui-badges-dock-design.md` in three known ways.**
|
||||||
|
(1) The dock keeps the sidebar's **320 px**, not the spec's ~280: the panels it holds were built for
|
||||||
|
320, and the width changes when they become compact readouts (step 3). (2) The session file gains the
|
||||||
|
dock's *shown* state only, not a width -- the dock's width is fixed, so there is nothing to persist
|
||||||
|
-- and does not store the open slide-over page. (3) The **Trigger badge** shows `off`, `armed` and
|
||||||
|
`triggered` as the spec says, and two more words, `capturing` (a post-trigger window is filling) and
|
||||||
|
`disarmed` (trigger on, not armed), so the label never claims a state the engine is not in. Still to
|
||||||
|
come, in the spec's order: the signal badges and drag (2), the compact readouts (3), the events
|
||||||
|
section in the dock, the deletion of the left pane and the Events toggle, and `USER-GUIDE.md` and the
|
||||||
|
manual's figures (4). Until then the events pane, the header's Events toggle and the manual still
|
||||||
|
describe the previous layout.
|
||||||
- **A plot holds at most eight signals** (`RowSet::kSlotsPerRow`), because a `Plot`
|
- **A plot holds at most eight signals** (`RowSet::kSlotsPerRow`), because a `Plot`
|
||||||
cannot give up a trace and the slots are therefore created up front.
|
cannot give up a trace and the slots are therefore created up front.
|
||||||
- **One toggle drives both cursor orientations.** The vertical markers and the
|
- **One toggle drives both cursor orientations.** The vertical markers and the
|
||||||
@@ -3016,6 +3049,10 @@ silence timeout and then resumes, which is the EMI case end to end.
|
|||||||
rather than rebuilds, and why the frame tick reads the markers back: a parked
|
rather than rebuilds, and why the frame tick reads the markers back: a parked
|
||||||
cursor's snapped position is a real number, so nothing may be inferred from it. The
|
cursor's snapped position is a real number, so nothing may be inferred from it. The
|
||||||
trigger marker is separate: its own cursor, independent of the cursor toggle.
|
trigger marker is separate: its own cursor, independent of the cursor toggle.
|
||||||
|
- **The slide-over shows a form taller than the plot by scrolling it.** The panel is as tall as the
|
||||||
|
plot and never taller, and each page has a scroller of its own, so on the documented 756x470 window
|
||||||
|
the trigger form (about 474 px) shows 253 px of itself. The tour's spotlight is clipped to that, and at
|
||||||
|
that size the card of the trigger step overlaps the panel's edge by about 18 px (`clear=0`).
|
||||||
- **Verified headless only.** Acquisition, partitioning, the trigger state
|
- **Verified headless only.** Acquisition, partitioning, the trigger state
|
||||||
machine and its integration with the buffers, and rendering under Xvfb
|
machine and its integration with the buffers, and rendering under Xvfb
|
||||||
(llvmpipe) were exercised; the interactive paths (drag and drop, the dialogs,
|
(llvmpipe) were exercised; the interactive paths (drag and drop, the dialogs,
|
||||||
|
|||||||
@@ -91,6 +91,39 @@ What feeds `Acquisition` is a `DataSource`, one of the registered *kinds*.
|
|||||||
every handler tests `Form::alive`, which the groups' `destroy` clears, because the chooser may have
|
every handler tests `Form::alive`, which the groups' `destroy` clears, because the chooser may have
|
||||||
destroyed the sibling widgets the handlers reach by raw pointer.
|
destroyed the sibling widgets the handlers reach by raw pointer.
|
||||||
|
|
||||||
|
### The window's chrome: plot, slide-over, badge bar, dock
|
||||||
|
|
||||||
|
The main window is a plot with three things around it, each a unit of `src/ui_*.cpp` and none of them
|
||||||
|
touching the data path.
|
||||||
|
|
||||||
|
- **`SlideOver`** (`src/ui_slideover.cpp`) is one `GtkRevealer` holding a `GtkStack`, added as the second
|
||||||
|
child placed *over* the plot (after the layout editor's buttons) in `GtkOverlay#plot_overlay`. A page is
|
||||||
|
a `slideover_add_page(app, id, title, widget)`: the widget is wrapped in a `GtkScrolledWindow` of its
|
||||||
|
own and registered under its id (`trigger`, `source-<id>`; `signal:<name>`, `signals`, `measure`,
|
||||||
|
`cursors` and `events` are reserved for later steps). One page is open at a time and `App::slideover.page`
|
||||||
|
is its id, empty when closed. The open page is **not** a session value. Esc, *Close*, a second click on the
|
||||||
|
badge and a click on the plot close it; the click-away is a capture-phase observer (see
|
||||||
|
[invariants-gtk-and-chrome.md](invariants-gtk-and-chrome.md)). With two or more `source-*` pages, an
|
||||||
|
`AdwToggleGroup` under the header is the segmented switch between them.
|
||||||
|
- **`BadgeBar`** (`src/ui_badges.cpp`) is the fixed-height row between the plot and the status line
|
||||||
|
(`main-window.ui` `badge_bar`, two slots: signals, left, empty until step 2; system, right, holding the
|
||||||
|
**Trigger** and **Source** badges). A badge is a flat button whose `page` is the slide-over page it
|
||||||
|
toggles; the Source badge's page is the first `source-*` page, and a kind with none (UDPS, Flat UDP, a
|
||||||
|
file) leaves it a read-out with `can_target` off.
|
||||||
|
- **The dock** is the `AdwOverlaySplitView` sidebar (`side_box` > `dock_scroller` > `dock_page`): the
|
||||||
|
signal list, the cursors and the measurements in one scroller. It holds no tab switcher and no
|
||||||
|
revealer, and it does not hold a source kind's panels (those are slide-over pages).
|
||||||
|
|
||||||
|
**When each is built and when it is read.** `build_slideover()` runs in `on_activate` right after the tour
|
||||||
|
overlay is built (the tour's overlay wraps the whole window, so its dim rectangles cover the slide-over
|
||||||
|
too); the trigger form is registered as
|
||||||
|
a page by `build_trigger_panel`; `build_source_panels()` registers the source pages; and
|
||||||
|
`build_badge_bar()` runs *after* all of them, because the Source badge needs to know the first page.
|
||||||
|
Per-tick state (the Trigger word, the Source name and `connected`, the open marks) is written by
|
||||||
|
`sync_badge_bar()`, called from `tick()` and from `slideover_open`/`slideover_close`; it writes a label
|
||||||
|
only when its text changed. Nothing in the badge bar or the slide-over may be read from the producer
|
||||||
|
thread.
|
||||||
|
|
||||||
### Data path
|
### Data path
|
||||||
|
|
||||||
`ScopeModel` holds **one `BufferStore`** (`live()`): one shared time-base ring plus one
|
`ScopeModel` holds **one `BufferStore`** (`live()`): one shared time-base ring plus one
|
||||||
|
|||||||
@@ -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 --
|
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
|
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 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
|
- **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
|
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"`,
|
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
|
`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
|
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
|
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
|
schedules a save. `dock:` in the dump reads the result.
|
||||||
`AdwViewSwitcherButton`'s placeholder `[x]` image went with the switcher, and `dock:` in the dump
|
|
||||||
replaces the `switcher:` line.
|
|
||||||
- **An *overlaid* sidebar covers the plot, so a drag out of it has nowhere to land.** `collapsed` in an
|
- **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
|
`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
|
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
|
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
|
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.
|
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
|
- **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
|
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,
|
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
|
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
|
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.
|
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
|
- **A stop declares the page it stands on, and the tour selects that page as the stop is entered --
|
||||||
stop whose target lives on another page of a stack -- the window's `signals` (and `trigger`) stops
|
by a hook in the main window and by a stack in the chooser.** The main window has no page stack any
|
||||||
on the sidebar's `signals` page, the chooser's "Two kinds of input" stop on its `waveforms` page --
|
more: its pages are the slide-over's. A `TourStop::page` there is a slide-over page id (`trigger`), and
|
||||||
names that page in `TourStop::page`, and `show_step()` selects it from `TourView::page_stack`
|
`TourView::on_page(page_user, page)` opens it -- or, for a stop whose `page` is null, **closes** the
|
||||||
before anything is measured. The page switch is **not** followed by a one-shot measure: `show_step()`
|
panel, so the card never points at the status line over an open panel. `tour_end()` calls
|
||||||
selects the page and `tour_update()`, which already runs every frame, fills the spotlight in on the
|
`on_page(nullptr)` *only if a walk was running* (so Skip, Esc and the last Next close a panel the tour
|
||||||
frame *after*, when the new page has been laid out. A stack allocates the new page's widgets on the
|
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
|
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.
|
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
|
- **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
|
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
|
`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
|
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
|
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
|
its target is **mapped**; a stop that names a page of a view with an `on_page` hook is **always**
|
||||||
wrong page leaves the stack mapped, so the stop is kept and the page switch reveals it; a collapsed
|
kept (the hook opens the page whatever the dock is doing, since the slide-over does not depend on it);
|
||||||
pane leaves the stack unmapped too, so no page can reveal it and the stop is dropped. The filter
|
and in a view with a `page_stack` a stop that names a page is kept when the stack is mapped -- a wrong
|
||||||
lives in `tour_begin()`, not in each window's `start_tour()`, both so the two windows cannot drift
|
page leaves the stack mapped, so the page switch reveals it, while a collapsed pane leaves it unmapped
|
||||||
into two rules and because the rule needs the view's own `page_stack`.
|
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
|
- **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
|
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
|
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
|
- **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
|
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
|
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
|
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 crop
|
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
|
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
|
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.
|
larger screen gives different rectangles for the same panels.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Invariants: the plot, the figure and the layout
|
# Invariants: the plot, the figure and the layout
|
||||||
|
|
||||||
How `plot::Figure`/`plot::Plot` may and may not be driven, what a row's state belongs to, the layout editor's overlay, and the rule that layout must not depend on content.
|
How `plot::Figure`/`plot::Plot` may and may not be driven, what a row's state belongs to, the layout editor's and the slide-over's overlay children, and the rule that layout must not depend on content.
|
||||||
|
|
||||||
Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to learn; read the ones touching the area you are editing before you edit it.
|
Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to learn; read the ones touching the area you are editing before you edit it.
|
||||||
|
|
||||||
@@ -97,6 +97,31 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
|||||||
an equal division. `sync_layout_editor()` runs every frame on purpose:
|
an equal division. `sync_layout_editor()` runs every frame on purpose:
|
||||||
`gtk_widget_set_margin_*()` returns early on an unchanged value, so a settled layout costs
|
`gtk_widget_set_margin_*()` returns early on an unchanged value, so a settled layout costs
|
||||||
the comparisons and nothing else, and a resize needs no notification.
|
the comparisons and nothing else, and a resize needs no notification.
|
||||||
|
- **The slide-over is the second thing placed over the plot, and it is one opaque widget, not a
|
||||||
|
transparent area.** The same pick rule as the editor's buttons applies, harder: the panel is a
|
||||||
|
`GtkRevealer` aligned to the end edge (`halign=END`, `valign=FILL`) at `kSlideOverWidth` = 360 px, so
|
||||||
|
its allocation *is* the part of the plot it covers and everything to its left is still the plot's --
|
||||||
|
pan, zoom, drop and hover are untouched. A full-area child with a panel drawn in it would swallow all
|
||||||
|
of them. Closed, the revealer is `can_target(FALSE)` (and `gtk_overlay_set_clip_overlay()` keeps its
|
||||||
|
slide-out inside the plot), so the frames of a slide-out do not eat input either. `[slideover]` reads
|
||||||
|
it back: `outside_hits_plot=1 inside_hits_panel=1`. Three consequences:
|
||||||
|
the panel **never resizes the plot** (it is an overlay child; `plot_unchanged=1`), its height is the
|
||||||
|
plot's and never the page's (each page has a `GtkScrolledWindow` with a `NEVER` horizontal policy that
|
||||||
|
does not propagate its child's height), and content that is taller than the plot -- the trigger form
|
||||||
|
on a 470 px window -- scrolls inside the panel instead of growing it. The widgets that go in a page
|
||||||
|
are reparented into it; their widths must fit 360 minus the border (the trigger form measures 267
|
||||||
|
minimum, the E-1608's panels 197).
|
||||||
|
- **The badge bar has a fixed height and sits outside the overlay.** `badge_bar` is 36 px
|
||||||
|
(`height-request`) between `plot_overlay` and the status line, so the plot above it never moves when a
|
||||||
|
badge's text changes (`bar_h` = `bar_h_after` in `[badges]`). This is "layout must not depend on
|
||||||
|
content" for the new row: a badge's state is a word from a short fixed set and the Source badge's name
|
||||||
|
is a kind's display name, so what changes with the stream is the row's *width* (the bar has room to
|
||||||
|
spare on the right) and never its height. The badge labels are not ellipsized: a name long enough to
|
||||||
|
need it would be a defect of the kind's `display_name()`. Rows that appear by trigger condition appear
|
||||||
|
inside the slide-over's scroller, never in the bar.
|
||||||
|
- **The dock's width is fixed too** (`side_column` `width-request` 300, the split view's 320 px floor
|
||||||
|
and cap), and it holds one scroller with the signal list, the cursors and the measurements. A
|
||||||
|
section's visibility is never what makes the dock taller; the scroller is what absorbs the extra.
|
||||||
- **Trace slots are identified by signal name, not channel index.** Enabling a signal
|
- **Trace slots are identified by signal name, not channel index.** Enabling a signal
|
||||||
or a new CONFIG renumbers the channel list, so an index would silently re-point a
|
or a new CONFIG renumbers the channel list, so an index would silently re-point a
|
||||||
trace at another signal.
|
trace at another signal.
|
||||||
|
|||||||
@@ -30,10 +30,14 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
|||||||
not freeze a colour that is meant to follow the signal list), **the math channels with their
|
not freeze a colour that is meant to follow the signal list), **the math channels with their
|
||||||
graphs**, **the measurements pane**, and **`measure_stats`** — the measurements statistics
|
graphs**, **the measurements pane**, and **`measure_stats`** — the measurements statistics
|
||||||
toggle; written as a bare top-level boolean (`measure_stats = true/false`), defaults to `false`
|
toggle; written as a bare top-level boolean (`measure_stats = true/false`), defaults to `false`
|
||||||
when absent, and **`dock`** -- the Dock button's state (the user's last word on it, `sidebar_wanted`),
|
when absent, and **`dock`** -- `LayoutState::dock_shown`, the Dock button's state (the user's last word on it,
|
||||||
a bare top-level boolean written where the sidebar's `side_tab` used to be, default `true`. The parser
|
`App::sidebar_wanted`): a bare top-level boolean written where `side_tab` used to be, default `true`.
|
||||||
ignores unknown keys, so a file written before the tabs became a dock (`side_tab = "measure"`) still loads
|
It is the **only** thing the dock persists -- its 320 px width is fixed, so there is nothing to store --
|
||||||
and simply opens with the dock shown; nothing needs to read the old key. Signal identity
|
and **the open slide-over page is deliberately not persisted** (a panel that opens by itself at start-up
|
||||||
|
is a surprise, and a page id from another source kind may not exist). The parser ignores unknown keys
|
||||||
|
(its key chain has no erroring final `else`), so a file written before the tabs became a dock
|
||||||
|
(`side_tab = "measure"`, or `side_tab = "source-ao"`) still loads and opens with the dock shown;
|
||||||
|
nothing reads the old key, and `[dock-session]` asserts it. Signal identity
|
||||||
is the *name* throughout (a CONFIG renumbers channels), 'enabled' is read off the channels the
|
is the *name* throughout (a CONFIG renumbers channels), 'enabled' is read off the channels the
|
||||||
model built -- an enabled signal is exactly one that produced a channel -- and `apply_layout()`
|
model built -- an enabled signal is exactly one that produced a channel -- and `apply_layout()`
|
||||||
installs the graphs **before** assigning the rows, since a row may hold a math channel's name and
|
installs the graphs **before** assigning the rows, since a row may hold a math channel's name and
|
||||||
|
|||||||
@@ -15,6 +15,17 @@ xvfb-run -a env UDPSCOPE_EXIT_AFTER_MS=8000 UDPSCOPE_ENABLE_ALL_MS=1000 \
|
|||||||
|
|
||||||
Two baselines every run is read against. The UDPS runs must be **unchanged by the source-plugin work**:
|
Two baselines every run is read against. The UDPS runs must be **unchanged by the source-plugin work**:
|
||||||
a UDPS self-check, number-normalised (`sed 's/[0-9.]\+/N/g'`), diffs empty against one taken before it.
|
a UDPS self-check, number-normalised (`sed 's/[0-9.]\+/N/g'`), diffs empty against one taken before it.
|
||||||
|
For the badge bar / slide-over / dock redesign the same diff is taken **against the commit before that
|
||||||
|
series (`c572ec2`)**, from a fresh `XDG_CONFIG_HOME` on each side (a stored mock session changes the
|
||||||
|
trigger lines), and it is "unchanged except" exactly these lines: `panels:` loses `switcher=` (the
|
||||||
|
trigger form is read through the slide-over, so its width and height change, and the `compact arm
|
||||||
|
probe` width with them); `switcher:` becomes `dock: shown= sensitive= w= scroller_pages=`;
|
||||||
|
`panel controls:` prints `dock=` for `switcher=`; `help figures:` loses `side_switcher=`, replaces
|
||||||
|
`side_stack=` with `badge_bar=` and `dock=`, moves the `trigger_*` rectangles into the slide-over and
|
||||||
|
shortens `plot=` by the 36 px bar; and the `tour started: steps=` count. A self-check taken **before**
|
||||||
|
`c572ec2` is no longer the baseline for those lines. The rest is the mock's own jitter (the sign of a
|
||||||
|
`square y=[...]` or a `trig engine:` figure, the position of one `trigger: edge` line), which the
|
||||||
|
normalisation does not remove -- anything else is a finding.
|
||||||
A clean build emits **one** warning and no more: a `-Woverlength-strings` on the icons'
|
A clean build emits **one** warning and no more: a `-Woverlength-strings` on the icons'
|
||||||
generated GResource `.c`, whose blob is **9248** against C99's 4095-byte string-literal limit with
|
generated GResource `.c`, whose blob is **9248** against C99's 4095-byte string-literal limit with
|
||||||
the five action icons and the two app icons that remain (a note about a generated file, which GCC
|
the five action icons and the two app icons that remain (a note about a generated file, which GCC
|
||||||
|
|||||||
Reference in new issue
Block a user