ui: the signal list is the signals slide-over page, opened by a + badge or g
This commit is contained in:
1 parent
e36f2a202b
commit
0dd235e41c
11 files changed
+98
-75
No files matched your search
@@ -2805,7 +2805,7 @@ reaches every widget). The full key table:
|
||||
| `t` | enable / disable trigger mode |
|
||||
| `c` | enable / disable the cursors |
|
||||
| `m` | go to Measure (scroll the dock to the cursors and the measurements) |
|
||||
| `g` | go to Signals (scroll the dock back to the signal list) |
|
||||
| `g` | go to Signals (open or close the signal list, the `signals` slide-over page, also reached by the `+` badge) |
|
||||
| `l` | show / hide the layout editor |
|
||||
| `s` | show / hide the dock |
|
||||
| `Esc` | close the slide-over panel (the guided tour takes it first) |
|
||||
@@ -2925,9 +2925,9 @@ now — they are listed so they are not mistaken for anything else:
|
||||
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 sidebar was then two
|
||||
text tabs (a **Measure** tab and a **Signals** tab, with a source kind's own tabs beside them), and four
|
||||
tabs did not fit 320 px. It is **one dock** now, with no tab switcher: the signal list, the cursors and the
|
||||
measurements 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. The **trigger form** and a **source's own
|
||||
tabs did not fit 320 px. It is **one dock** now, with no tab switcher: the cursors and the
|
||||
measurements in one scroller, shown from the start and insensitive until the stream has signals; `m`
|
||||
scrolls it to the cursors; the **signal list** is the `signals` slide-over page (`g`, or the `+` badge). 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
|
||||
on, and neither offers a control that changes the configuration -- each heading has an `[edit]` button
|
||||
|
||||
@@ -229,8 +229,8 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
replaced an `AdwViewSwitcher` over an `AdwViewStack`, whose four text tabs did not fit 320 px (and a source
|
||||
can add more); a source kind's own panels are slide-over pages, not dock sections. The reason is the one
|
||||
this trap teaches: sections competing for one 320 px column each left every one of them short of room, and
|
||||
a scroller gives each its natural height. `m` and `g` scroll the dock to the cursors and to the signal list
|
||||
(`dock_show_section()`). A *feature's* on/off is no longer a visibility at all -- the
|
||||
a scroller gives each its natural height. `m` scrolls the dock to the cursors (`dock_show_section()`); the signal list is
|
||||
not a dock section but the `signals` slide-over page, which `g` and the `+` badge open. A *feature's* on/off is no longer a visibility at all -- the
|
||||
**cursors'** toggle sets its pane's **body** insensitive -- the readout has nothing to show with the
|
||||
instruments off -- and the *body* and not the pane, because the pane's heading carries its own switch
|
||||
(see the next bullet). The **trigger's body is deliberately never disabled**: a trigger is configured
|
||||
@@ -643,7 +643,8 @@ The `Help` submenu, the contextual help's "?" and the guided tour, and the traps
|
||||
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
|
||||
the main window's tour is five steps there (`[toursurface] collapsed: ... steps=5`: the signals stop names
|
||||
the `signals` slide-over page, which does not depend on the dock either). 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
|
||||
|
||||
@@ -969,8 +969,8 @@ two also catch a row whose accelerator went missing. `settings=` must be a real
|
||||
slide); the `on_page` hook is what opens the page for the trigger stop and closes the panel for every
|
||||
stop that names none. The lines:
|
||||
|
||||
- `[toursurface] before: slideover=none signal_list_mapped=1 running=1` -- the state a first run starts
|
||||
in (panel closed, dock on screen); the auto-started walk is dismissed.
|
||||
- `[toursurface] before: slideover=none signal_list_mapped=0 running=1` -- the state a first run starts
|
||||
in (panel closed, dock on screen; the signal list is a slide-over page, so unmapped); the auto-started walk is dismissed.
|
||||
- `[toursurface] on trigger: slideover=trigger trigger_body_mapped=1 side_box_mapped=1` -- a frame after
|
||||
the page was opened: the dock is still on screen under the panel (they are not alternatives).
|
||||
- `[toursurface] tour: visible=1 steps=6 step=0 trigger_stop=1 signals_stop=1` -- the plan: **six
|
||||
@@ -978,8 +978,8 @@ two also catch a row whose accelerator went missing. `settings=` must be a real
|
||||
- `[toursurface] step=N/6 title='…' slideover=<page> target_mapped=D` -- one per stop, in order: *The
|
||||
plot*, *The badges*, *The trigger*, *The dock*, *The signals*, *The status line*. **Step 1 reads
|
||||
`slideover=none` although the probe started with the panel open** (a stop with no page closes it),
|
||||
**step 3 reads `slideover=trigger`, and step 4 reads `slideover=none` again**; every `target_mapped`
|
||||
is 1.
|
||||
**step 3 reads `slideover=trigger`, step 4 reads `slideover=none` again, and step 5 (the signals)
|
||||
reads `slideover=signals`**; every `target_mapped` is 1.
|
||||
- `[toursurface] stop=N/6 visible=1 slideover=… spot=[…] raw=[…] target=[…] contains=1 inside=I card=[…] clear=C` -- the
|
||||
`tour_geometry_line()` the helpui probe uses, one per stop, read a frame after the stop was entered
|
||||
(the spotlight is withheld until the panel has finished sliding in, `page_settled`): **`contains=1` on
|
||||
@@ -993,13 +993,13 @@ two also catch a row whose accelerator went missing. `settings=` must be a real
|
||||
- `[toursurface] restarted at trigger: running=1 slideover=trigger` then
|
||||
`[toursurface] skipped: running=0 slideover=none` -- the walk parked on the trigger stop and dismissed
|
||||
(the Skip/Esc route): **`slideover=none` after the dismissal** is the tour not leaving a panel behind.
|
||||
- `[toursurface] collapsed: side_box_mapped=0 steps=4 trigger_stop=1 signals_stop=0 side_stop=0` -- the
|
||||
- `[toursurface] collapsed: side_box_mapped=0 steps=5 trigger_stop=1 signals_stop=1 side_stop=0` -- the
|
||||
filter with the dock put away (`sidebar_wanted` off; `update_side_visibility()` re-asserts it, so this
|
||||
is how a probe puts the dock away for good): the dock's two stops are dropped, **and the trigger stop is
|
||||
kept** because the slide-over does not depend on the dock -- four stops remain (plot, badges, trigger,
|
||||
status).
|
||||
- `[keys] a: armed 0->1 | space: paused 0->1 | t: trigger panel: 0->1 | c: cursors: 0->1 | m: dock 0->243 (cursors_y=243 reach=2256 at_cursors=1) | g: dock 243->0 at_top=1 | … | esc: opened=1 handled=1 closed=1 idle_handled=0 | editable focused: 'm' refused=1` (stderr, `UDPSCOPE_PROBE_KEYS_MS`) — **every `X->Y` pair must differ** (`g` returns, so its pair reads `243->0`), **`refused` must be 1**, and the `esc:` reading must be `opened=1 handled=1 closed=1 idle_handled=0`.
|
||||
The `m`/`g` readings are the dock scroller's vertical adjustment before and after (the probe pads the dock to 3000 px so there is something to scroll, and undoes it at the end): **`m` must land on `cursors_y`, the cursors panel's y in the dock (`at_cursors=1`), and `g` must return to 0 (`at_top=1`)**, which is what says those two keys scroll to a section rather than toggling a pane; a `reach` of 0 would mean the dock fit its viewport and the probe's padding was lost; `t`/`c` read the toolbar toggle's own state, because those two enable a feature and the button is where that state is visible. **The `esc:` reading goes through `on_app_key_pressed()` with `GDK_KEY_Escape`**, the handler the window's key controller calls, and not through `slideover_escape()`: the probe opens the `trigger` slide-over page (`opened=1`), sends the key (`handled=1`: it was consumed, and `closed=1`: the page is gone), then sends it again with nothing open (`idle_handled=0`: Escape is left alone when there is nothing for it to close, so a dialog or popover under it still gets it). `handled=0` or `closed=0` is the slide-over's Escape never reaching the panel from the window's key path -- a handler that returns before the slide-over test, or a controller in the wrong phase -- which the `[slideover] esc_closes=1` reading (a direct call to `slideover_escape()`) cannot see. **Limit of the `m`/`g` readings:** the padding is 3000 px at the end of the page, so `reach` is non-zero on any window shorter than that and the key has somewhere to scroll, but it does not push the cursors panel out of the viewport -- on a window whose dock viewport is taller than `cursors_y` (a tall window) the section is already in view at offset 0 and `m` still scrolls to it; `at_cursors=1` is the key landing the section's top at the viewport's top, which is the reading, not "the section was hidden before". 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).
|
||||
is how a probe puts the dock away for good): the dock's own stop is dropped, **and the trigger and
|
||||
signals stops are kept** because the slide-over does not depend on the dock -- five stops remain (plot,
|
||||
badges, trigger, signals, status).
|
||||
- `[keys] a: armed 0->1 | space: paused 0->1 | t: trigger panel: 0->1 | c: cursors: 0->1 | m: dock 0->243 (cursors_y=243 reach=2256 at_cursors=1) | g: page=1 dock_still=1 closed=1 | … | esc: opened=1 handled=1 closed=1 idle_handled=0 | editable focused: 'm' refused=1` (stderr, `UDPSCOPE_PROBE_KEYS_MS`) — **every `X->Y` pair must differ** (`g` is not a pair: it opens the `signals` slide-over page and a second press closes it), **`refused` must be 1**, and the `esc:` reading must be `opened=1 handled=1 closed=1 idle_handled=0`.
|
||||
The `m`/`g` readings are the dock scroller's vertical adjustment before and after (the probe pads the dock to 3000 px so there is something to scroll, and undoes it at the end): **`m` must land on `cursors_y`, the cursors panel's y in the dock (`at_cursors=1`), and `g` must read `page=1 dock_still=1 closed=1`** (the `signals` page opened, the dock's scroll untouched, a second press closing it), which is what says `m` scrolls to a section and `g` opens the list's page rather than toggling a pane; a `reach` of 0 would mean the dock fit its viewport and the probe's padding was lost; `t`/`c` read the toolbar toggle's own state, because those two enable a feature and the button is where that state is visible. **The `esc:` reading goes through `on_app_key_pressed()` with `GDK_KEY_Escape`**, the handler the window's key controller calls, and not through `slideover_escape()`: the probe opens the `trigger` slide-over page (`opened=1`), sends the key (`handled=1`: it was consumed, and `closed=1`: the page is gone), then sends it again with nothing open (`idle_handled=0`: Escape is left alone when there is nothing for it to close, so a dialog or popover under it still gets it). `handled=0` or `closed=0` is the slide-over's Escape never reaching the panel from the window's key path -- a handler that returns before the slide-over test, or a controller in the wrong phase -- which the `[slideover] esc_closes=1` reading (a direct call to `slideover_escape()`) cannot see. **Limit of the `m`/`g` readings:** the padding is 3000 px at the end of the page, so `reach` is non-zero on any window shorter than that and the key has somewhere to scroll, but it does not push the cursors panel out of the viewport -- on a window whose dock viewport is taller than `cursors_y` (a tall window) the section is already in view at offset 0 and `m` still scrolls to it; `at_cursors=1` is the key landing the section's top at the viewport's top, which is the reading, not "the section was hidden before". 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).
|
||||
- `[color] '<name>' at row R slot S: trace=#RRGGBB chip_agrees=1 (want 1)`, then `[color] override: trace=#123456 chip_agrees=1`, `[color] session: entries=1 rgb=#123456` and `[color] default: trace=#RRGGBB chip_agrees=1` (stderr, `UDPSCOPE_PROBE_COLOR_MS`) — **all three `chip_agrees` must be 1**, the override line's `trace` must be `#123456`, the session line must read `entries=1 rgb=#123456`, and the default line's `trace` must be back to the first line's. Run it with `UDPSCOPE_ENABLE_ALL_MS` ahead of it, or there is no bound trace and the probe prints `no bound trace to recolour` instead of asserting anything; on the standard mock the first line reads `'sine' at row 0 slot 0: trace=#4FA3FF`. Two things make this an assertion rather than a tautology. The colours are read out of **`plot::Plot::trace().color`** — what the renderer will stroke, reached through `Row::plot` *after* `relayout_areas()`, since a new colour is a new figure — and out of the chip's own `<span foreground="…">` markup, not by asking `RowSet::signal_color()` twice, which would pass against a chip that never reached the trace. And the override is `#123456`, deliberately in no palette slot: an override equal to the entry it replaces passes against an implementation that ignores overrides entirely. The session leg round-trips `collect_layout()` → `save_layout()` → `load_layout()` on a temp path and deletes it, because a probe must not leave a colour in the real session file for a later run to inherit.
|
||||
- `[reseq-probe] reseq=N late=N stale=N overflows=N holes=N hole_slots=N backfilled=N
|
||||
gaps_refused=N open_holes=N bands=N bands_repaired=N nan_slots=N scanned=N trigger_in_hole=D`
|
||||
@@ -1819,14 +1819,14 @@ and `regained` reads 400 ms later.
|
||||
## `[side-tab-probe]`
|
||||
|
||||
```
|
||||
[side-tab-probe] section=signals accums=4 baseline recorded (measure at scroll 2732 of 2732, outside_at_top=1)
|
||||
[side-tab-probe] section=signals rows=4 accums=4 advanced=4 min_n=18
|
||||
[side-tab-probe] section=cursors accums=4 baseline recorded (measure at scroll 2732 of 2732, outside_at_top=1)
|
||||
[side-tab-probe] section=cursors rows=4 accums=4 advanced=4 min_n=18
|
||||
```
|
||||
|
||||
`UDPSCOPE_PROBE_SIDE_TAB_MS` (the variable and the tag keep their pre-dock names; the function is `dock_section_probe`) is the one reading for the rule that **the statistics accumulator keeps running
|
||||
while the Measure section is out of view**. It scrolls the dock *to* the Measure section (`dock_show_section("measure")`), forces one pass so there is a baseline, then scrolls back to Signals and reads again two seconds later. To make the scroll observable the probe pads `dock_page` **and the signal list** to 3000 px each (probe only, both undone in the report), and it re-arms once after 400 ms so the first reading has the layout.
|
||||
while the Measure section is out of view**. It scrolls the dock *to* the measurements (`dock_show_section("measure")` lands on the cursors panel, the dock's top, so the probe then moves to the measurements' heading itself), forces one pass so there is a baseline, then scrolls back to the top (the cursors) and reads again two seconds later. To make the scroll observable the probe pads `dock_page` **and the cursors panel** to 3000 px each (probe only, both undone in the report), and it re-arms once after 400 ms so the first reading has the layout.
|
||||
|
||||
- **`outside_at_top=1` is the precondition**: with the dock at offset 0, the Measure section (the cursors panel, where `dock_show_section("measure")` lands) starts at or below the viewport's bottom edge, so the "away from the section" state really has the measurements out of view and `advanced=4` says something. `outside_at_top=0` means the probe's own set-up failed and the run proves nothing -- before the signal list was padded it read `0` on the documented window, where the section's top is 243 px down and the dock's viewport is taller than that. **The limit is the 3000 px:** the guarantee holds for a dock viewport shorter than the padding (any real window); a viewport taller than the signal list's padding, about 3000 logical px, would read `0` again.
|
||||
- **`outside_at_top=1` is the precondition**: with the dock at offset 0, the Measure section (the measurements' heading, below the padded cursors panel) starts at or below the viewport's bottom edge, so the "away from the section" state really has the measurements out of view and `advanced=4` says something. `outside_at_top=0` means the probe's own set-up failed and the run proves nothing -- unpadded, the section's top sits a short way down and the dock's viewport is taller than that. **The limit is the 3000 px:** the guarantee holds for a dock viewport shorter than the padding (any real window); a viewport taller than the cursors panel's padding, about 3000 logical px, would read `0` again.
|
||||
|
||||
- **`advanced=4` is the assertion**: every accumulator gained passes while its section was out of view, and
|
||||
`min_n` (the smallest `n` seen) must be non-zero too. **`advanced=0` is the defect**: something gated the
|
||||
|
||||
@@ -424,8 +424,8 @@ precedent, not an exception.
|
||||
phases: `phase=0`, `phase=1` (after pause) and `phase=2` (after Reset). Needs
|
||||
`UDPSCOPE_ENABLE_ALL_MS` ahead of it and `UDPSCOPE_PROBE_MEASURE_MS` to add measurements first.
|
||||
- `UDPSCOPE_PROBE_SIDE_TAB_MS=3000` with `UDPSCOPE_EXIT_AFTER_MS=11000` — the measurements in the
|
||||
dock's Measure section, a forced pass, then the dock scrolled back to the signal list (the Measure
|
||||
section out of view) and read again two seconds later. The probe pads the dock page and the signal list to
|
||||
dock's Measure section, a forced pass, then the dock scrolled back to its top (the
|
||||
measurements out of view) and read again two seconds later. The probe pads the dock page and the cursors panel to
|
||||
3000 px each so the Measure section really starts below the viewport (`outside_at_top=1`) on any real window. **`advanced=4`
|
||||
is the assertion** (every accumulator gained passes while its section was out of view); `advanced=0` is the
|
||||
defect where the accumulation is gated on the visible section, which would leave a gap in min/max/avg for
|
||||
@@ -656,7 +656,7 @@ as distinct from the fault injection below, which exercises ingest:
|
||||
(and a dismissal on the trigger stop) leave none. It then puts the dock *away* and starts the tour again:
|
||||
the dock's stops are dropped while the trigger stop, which does not depend on the dock, stays.
|
||||
`[toursurface] stop=3/6 … slideover=trigger … contains=1` and
|
||||
`[toursurface] collapsed: side_box_mapped=0 steps=4 trigger_stop=1 signals_stop=0` are the assertions; its
|
||||
`[toursurface] collapsed: side_box_mapped=0 steps=5 trigger_stop=1 signals_stop=1` are the assertions; its
|
||||
lines are in [probe-readings.md](probe-readings.md). Run it alone with
|
||||
`UDPSCOPE_EXIT_AFTER_MS=9000 UDPSCOPE_EXIT_DUMP=1` against a live source.
|
||||
- `UDPSCOPE_PROBE_DEFAULTS_MS` — opens **Calibrate** and reads every spin button in it and in the
|
||||
|
||||
+11
-16
@@ -2864,19 +2864,13 @@ void on_right_click(GtkGestureClick* gesture, int, double x, double y, gpointer
|
||||
open_row_menu(*app, row, x, y, widget);
|
||||
}
|
||||
|
||||
/// Scrolls the dock to a section. The dock is one scroller (`App::dock_scroller`) holding the signal list,
|
||||
/// the cursors and the measurements top to bottom, so "measure" is the cursors panel's offset in the
|
||||
/// page, and "signals" the top. Nothing is hidden or removed: both stay in the page. An unknown name is a
|
||||
/// no-op, so a key bound to a section a later version drops cannot blank anything.
|
||||
/// Scrolls the dock to a section. The dock is one scroller (`App::dock_scroller`) holding the cursors and
|
||||
/// the measurements top to bottom, so "measure" is the cursors panel's offset in the page. Nothing is
|
||||
/// hidden or removed: both stay in the page. An unknown name (the signal list is a slide-over page now,
|
||||
/// not a dock section) is a no-op, so a key bound to a section a later version drops cannot blank anything.
|
||||
void dock_show_section(App& app, const char* section) {
|
||||
if (app.dock_scroller == nullptr || app.dock_page == nullptr || section == nullptr) return;
|
||||
GtkAdjustment* adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(app.dock_scroller));
|
||||
if (std::strcmp(section, "signals") == 0) {
|
||||
// The list is the first section: its own offset in the page is the page's margin, and "go to Signals"
|
||||
// means the top of the dock.
|
||||
gtk_adjustment_set_value(adj, 0.0);
|
||||
return;
|
||||
}
|
||||
if (std::strcmp(section, "measure") != 0 || app.cursors_panel == nullptr) return;
|
||||
graphene_point_t zero = GRAPHENE_POINT_INIT(0.f, 0.f);
|
||||
graphene_point_t at;
|
||||
@@ -4679,7 +4673,7 @@ void run_app_action(App& app, AppAction action) {
|
||||
toggle(app.cursors_toggle);
|
||||
break;
|
||||
case AppAction::ShowMeasure: dock_show_section(app, "measure"); break;
|
||||
case AppAction::ShowSignals: dock_show_section(app, "signals"); break;
|
||||
case AppAction::ShowSignals: slideover_toggle(app, "signals"); break;
|
||||
case AppAction::LayoutEditor:
|
||||
toggle(app.layout_toggle);
|
||||
break;
|
||||
@@ -5153,9 +5147,9 @@ void on_activate(GtkApplication* application, gpointer user) {
|
||||
gtk_popover_set_has_arrow(GTK_POPOVER(app->status_popover), TRUE);
|
||||
}
|
||||
|
||||
// The right-hand side is the dock: one scroller, the signal list first (it is what is dragged onto the
|
||||
// plot), then the cursors and the measurements. Every section is always there -- "go to Measure" scrolls
|
||||
// to it instead of switching a tab, so the measurements are as live as the list above them.
|
||||
// The right-hand side is the dock: one scroller, the cursors then the measurements. Every section is
|
||||
// always there -- "go to Measure" scrolls to it instead of switching a tab, so the measurements are as
|
||||
// live as the cursors above them. The signal list is a slide-over page (reached by the `+` badge).
|
||||
// `side` and `dock_page`/`dock_scroller` are resolved above with the rest of the skeleton and checked
|
||||
// by the null guard; assign the member here.
|
||||
app->side_box = side;
|
||||
@@ -5169,9 +5163,10 @@ void on_activate(GtkApplication* application, gpointer user) {
|
||||
// would spread its rows (or draw its frame) over empty space under the last one.
|
||||
gtk_widget_set_valign(app->trigger_panel, GTK_ALIGN_START);
|
||||
slideover_add_page(*app, "trigger", "Trigger", app->trigger_panel);
|
||||
// The signal list is the `signals` page, reached by the `+` badge or `g`; the panel is built here, after
|
||||
// `build_slideover`, and registered once.
|
||||
app->signal_panel = build_signal_panel(*app);
|
||||
gtk_box_append(GTK_BOX(app->dock_page), app->signal_panel);
|
||||
gtk_box_append(GTK_BOX(app->dock_page), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL));
|
||||
slideover_add_page(*app, "signals", "Signals", app->signal_panel);
|
||||
gtk_box_append(GTK_BOX(app->dock_page), app->cursors_panel);
|
||||
gtk_box_append(GTK_BOX(app->dock_page), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL));
|
||||
gtk_box_append(GTK_BOX(app->dock_page), measures_panel);
|
||||
|
||||
+3
-2
@@ -156,6 +156,7 @@ struct BadgeBar {
|
||||
GtkWidget* system_slot = nullptr; ///< right: Trigger, Source
|
||||
Badge trigger;
|
||||
Badge source; ///< opens the first source page; a read-out when the source has none
|
||||
Badge plus; ///< the "+" in the signals slot: opens the `signals` page
|
||||
};
|
||||
|
||||
struct App {
|
||||
@@ -253,8 +254,8 @@ struct App {
|
||||
GtkWidget* trigger_panel = nullptr;
|
||||
GtkWidget* signal_panel = nullptr;
|
||||
GtkWidget* side_box = nullptr;
|
||||
/// The dock: the sidebar's one scroller and the box inside it, which holds the signal list, the
|
||||
/// cursors and the measurements (`dock_show_section()` scrolls it). A feature's own on/off never
|
||||
/// The dock: the sidebar's one scroller and the box inside it, which holds the cursors and
|
||||
/// the measurements (`dock_show_section()` scrolls it). A feature's own on/off never
|
||||
/// removes a section (it sets its panel's sensitivity instead).
|
||||
GtkWidget* dock_page = nullptr;
|
||||
GtkWidget* dock_scroller = nullptr;
|
||||
|
||||
@@ -86,6 +86,9 @@ void build_badge_bar(App& app) {
|
||||
gtk_widget_set_can_target(bb.source.button, FALSE);
|
||||
gtk_widget_set_focusable(bb.source.button, FALSE);
|
||||
}
|
||||
// The full signal list. Insensitive until the stream has declared signals ("disable, do not hide").
|
||||
make_badge(app, bb.plus, bb.signals_slot, "+", "signals");
|
||||
gtk_widget_set_tooltip_text(bb.plus.button, "all signals: switch more on, add a math channel");
|
||||
sync_badge_bar(app);
|
||||
}
|
||||
|
||||
@@ -100,6 +103,10 @@ void sync_badge_bar(App& app) {
|
||||
set_text_if_changed(bb.trigger.state, trigger_badge_state(enabled, armed, pending, has_event));
|
||||
set_open(bb.trigger.button, slideover_page(app) == bb.trigger.page);
|
||||
|
||||
if (bb.plus.button != nullptr) {
|
||||
gtk_widget_set_sensitive(bb.plus.button, !app.model->signals().empty());
|
||||
set_open(bb.plus.button, slideover_page(app) == "signals");
|
||||
}
|
||||
if (bb.source.button == nullptr) return;
|
||||
// The source's name and link state. A file has neither a link nor a kind worth naming.
|
||||
const bool file_mode = app.replaying || !app.model->config().open_waveform.empty();
|
||||
|
||||
@@ -92,7 +92,7 @@ constexpr HelpHint kHelpHints[] = {
|
||||
{"signals", "The signals",
|
||||
"Every signal the source declares, each with the switch that acquires it. Nothing is buffered "
|
||||
"until it is switched on, and only acquired signals appear in the drop targets and the "
|
||||
"measurement editor. A signal that is dragged onto a plot is drawn there.",
|
||||
"measurement editor. A signal's badge, dragged onto a plot, draws it there.",
|
||||
"G", &App::signals_head},
|
||||
{"events", "The event history",
|
||||
"Every triggered acquisition the cache keeps, with a picture of each. Open one to read it as a "
|
||||
@@ -117,8 +117,8 @@ constexpr HelpHint kHelpHints[] = {
|
||||
"", &App::trigger_search},
|
||||
{"signals_config", "Configuring signals",
|
||||
"The list is locked until the pencil above opens the per-signal controls: the calibration that "
|
||||
"maps raw counts to a unit, the colour a trace is drawn in, and which plot a signal is dragged "
|
||||
"onto. A signal nothing measures can be switched off to stop acquiring it.",
|
||||
"maps raw counts to a unit, the colour a trace is drawn in, and which plot a signal is drawn "
|
||||
"on. A signal nothing measures can be switched off to stop acquiring it.",
|
||||
"", &App::signals_note},
|
||||
{"window", "The time window",
|
||||
"How much time is on screen, counted back from the newest sample. Type a span, change its unit, "
|
||||
|
||||
+42
-24
@@ -93,17 +93,14 @@ int on_render_probe(GtkGLArea*, GdkGLContext*, gpointer user) {
|
||||
return FALSE; // same reason the adapter does: never stop the emission
|
||||
}
|
||||
|
||||
gboolean exit_probe_step3(gpointer user) {
|
||||
gboolean exit_probe_step3b(gpointer user) {
|
||||
App* a = static_cast<App*>(user);
|
||||
a->dbg_mode_live = a->status_detail;
|
||||
GtkWidget* window = a->window;
|
||||
a->window = nullptr; // stop the tick before the widgets go
|
||||
a->dbg_side_w = gtk_widget_get_width(a->side_box);
|
||||
a->dbg_side_h = gtk_widget_get_height(a->side_box);
|
||||
// The signal list is the `signals` page, opened by step 3 300 ms ago: its allocation is only real once
|
||||
// the page is open and the reveal has run.
|
||||
a->dbg_signal_w = gtk_widget_get_width(a->signal_panel);
|
||||
a->dbg_signal_h = gtk_widget_get_height(a->signal_panel);
|
||||
a->dbg_trigger_w = gtk_widget_get_width(a->trigger_panel);
|
||||
a->dbg_trigger_h = gtk_widget_get_height(a->trigger_panel);
|
||||
// How many rows of the signal list carry a drag source: dragging a signal onto a plot is how a plot is
|
||||
// filled, so it has to survive both pane modes -- the locked list drops the tick, the menu and the `+`,
|
||||
// and this is the reading that says it did not drop the drag with them. One row is the `MATH` divider,
|
||||
@@ -211,11 +208,25 @@ gboolean exit_probe_step3(gpointer user) {
|
||||
}
|
||||
// `dbg_window_text` is kept by `sync_window_display()`, the only place that knows the span the
|
||||
// window control is showing: there is no label to read it back from any more.
|
||||
slideover_close(*a); // opened by step 1 for the allocations read above
|
||||
slideover_close(*a); // opened by step 3 for the signal list's allocations read above
|
||||
if (window != nullptr) gtk_window_destroy(GTK_WINDOW(window));
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
/// Reads the sizes that need the `trigger` page open (step 1 opened it), then swaps it for the `signals`
|
||||
/// page -- the slide-over holds one page -- and leaves step 3b the 300 ms the reveal needs.
|
||||
gboolean exit_probe_step3(gpointer user) {
|
||||
App* a = static_cast<App*>(user);
|
||||
a->dbg_mode_live = a->status_detail;
|
||||
a->dbg_side_w = gtk_widget_get_width(a->side_box);
|
||||
a->dbg_side_h = gtk_widget_get_height(a->side_box);
|
||||
a->dbg_trigger_w = gtk_widget_get_width(a->trigger_panel);
|
||||
a->dbg_trigger_h = gtk_widget_get_height(a->trigger_panel);
|
||||
slideover_open(*a, "signals");
|
||||
g_timeout_add(300, exit_probe_step3b, a);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
/// The frame after the pane was locked: read the compact arm control's *allocation*, then leave the
|
||||
/// configuration mode and trigger mode as step 2 used to, and hand the chain back its 400 ms. The button
|
||||
/// is created hidden and this is the first tick it is shown, so its width in the same tick would always
|
||||
@@ -1794,15 +1805,16 @@ gboolean stats_probe(gpointer user) {
|
||||
}
|
||||
|
||||
/// Which section the dock's scroller is showing at its top: "measure" once it has been scrolled to the
|
||||
/// cursors panel or past it, "signals" above that. Read from the adjustment, so it is the state the user
|
||||
/// would see and not the last call made.
|
||||
/// measurements' heading or past it, "cursors" above that (the cursors panel is the dock's first section;
|
||||
/// the signal list is a slide-over page). Read from the adjustment, so it is the state the user would see
|
||||
/// and not the last call made.
|
||||
static const char* dock_section_in_view(App& a) {
|
||||
if (a.dock_scroller == nullptr || a.cursors_panel == nullptr || a.dock_page == nullptr) return "none";
|
||||
if (a.dock_scroller == nullptr || a.measures_head == nullptr || a.dock_page == nullptr) return "none";
|
||||
graphene_point_t zero = GRAPHENE_POINT_INIT(0.f, 0.f);
|
||||
graphene_point_t at;
|
||||
if (!gtk_widget_compute_point(a.cursors_panel, a.dock_page, &zero, &at)) return "none";
|
||||
if (!gtk_widget_compute_point(a.measures_head, a.dock_page, &zero, &at)) return "none";
|
||||
GtkAdjustment* adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(a.dock_scroller));
|
||||
return gtk_adjustment_get_value(adj) > 0.5 * at.y ? "measure" : "signals";
|
||||
return gtk_adjustment_get_value(adj) > 0.5 * at.y ? "measure" : "cursors";
|
||||
}
|
||||
|
||||
/// Phase two of the dock-section probe: the accumulators must have advanced while the Measure section was
|
||||
@@ -1825,7 +1837,7 @@ gboolean dock_section_probe_report(gpointer user) {
|
||||
}
|
||||
}
|
||||
if (a->dock_page != nullptr) gtk_widget_set_size_request(a->dock_page, -1, -1);
|
||||
if (a->signal_panel != nullptr) gtk_widget_set_size_request(a->signal_panel, -1, -1);
|
||||
if (a->cursors_panel != nullptr) gtk_widget_set_size_request(a->cursors_panel, -1, -1);
|
||||
std::fprintf(stderr,
|
||||
"[side-tab-probe] section=%s rows=%zu accums=%zu advanced=%" G_GUINT64_FORMAT
|
||||
" min_n=%" G_GUINT64_FORMAT "\n",
|
||||
@@ -1847,10 +1859,11 @@ gboolean dock_section_probe(gpointer user) {
|
||||
if (!padded && a->dock_page != nullptr) {
|
||||
padded = true;
|
||||
gtk_widget_set_size_request(a->dock_page, -1, 3000);
|
||||
// ... and the signal list taller than any window too, which is what pushes the Measure section below
|
||||
// the viewport at offset 0 (padding the page's end only makes it scrollable, and on a window taller than
|
||||
// the section's y the section would stay in view). Undone in the report.
|
||||
if (a->signal_panel != nullptr) gtk_widget_set_size_request(a->signal_panel, -1, 3000);
|
||||
// ... and the cursors panel (the dock's first section, above the measurements) taller than any window
|
||||
// too, which is what pushes the measurements below the viewport at offset 0 (padding the page's end only
|
||||
// makes it scrollable, and on a window taller than the section's y the section would stay in view).
|
||||
// Undone in the report.
|
||||
if (a->cursors_panel != nullptr) gtk_widget_set_size_request(a->cursors_panel, -1, 3000);
|
||||
g_timeout_add(400, dock_section_probe, a);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
@@ -1892,16 +1905,16 @@ gboolean dock_section_probe(gpointer user) {
|
||||
gtk_check_button_set_active(GTK_CHECK_BUTTON(a->measures_stats_check), TRUE);
|
||||
}
|
||||
GtkAdjustment* dock_adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(a->dock_scroller));
|
||||
// From the top, is the measure section (the cursors panel, where `dock_show_section("measure")` lands)
|
||||
// below the viewport? Only then is scrolling to it a move *into* view and back out a move *away*; a
|
||||
// From the top, is the measure section (the measurements' heading) below the viewport? Only then is scrolling to it a move *into* view and back out a move *away*; a
|
||||
// section already in view at offset 0 would make the whole probe a reading of nothing.
|
||||
gtk_adjustment_set_value(dock_adj, 0.0);
|
||||
graphene_point_t zero = GRAPHENE_POINT_INIT(0.f, 0.f);
|
||||
graphene_point_t measure_at = GRAPHENE_POINT_INIT(-1.f, -1.f);
|
||||
const bool located =
|
||||
a->cursors_panel != nullptr && gtk_widget_compute_point(a->cursors_panel, a->dock_page, &zero, &measure_at);
|
||||
a->measures_head != nullptr && gtk_widget_compute_point(a->measures_head, a->dock_page, &zero, &measure_at);
|
||||
const bool outside_at_top = located && static_cast<double>(measure_at.y) >= gtk_adjustment_get_page_size(dock_adj);
|
||||
dock_show_section(*a, "measure");
|
||||
dock_show_section(*a, "measure"); // lands on the cursors panel, the dock's top: a no-op scroll
|
||||
if (located) gtk_adjustment_set_value(dock_adj, measure_at.y); // ... so go to the measurements themselves
|
||||
// Where the dock went: `scrolled` is its offset and `reach` the most it can scroll. A reach of 0 would
|
||||
// mean the content fits and "away from the section" is not a state this run can reach.
|
||||
const double scrolled = gtk_adjustment_get_value(dock_adj);
|
||||
@@ -1911,7 +1924,7 @@ gboolean dock_section_probe(gpointer user) {
|
||||
for (const MeasureStatsAccum& s : a->measure_stats) g_dock_section_n_before.push_back(s.n);
|
||||
// Away from the section the measurements are in: they are scrolled out of the viewport (the widgets
|
||||
// stay mapped, only clipped), and the tick keeps calling `update_measures()` regardless.
|
||||
dock_show_section(*a, "signals");
|
||||
gtk_adjustment_set_value(dock_adj, 0.0);
|
||||
std::fprintf(stderr,
|
||||
"[side-tab-probe] section=%s accums=%zu baseline recorded (measure at scroll %.0f of %.0f, "
|
||||
"outside_at_top=%d)\n",
|
||||
@@ -6427,12 +6440,17 @@ gboolean keys_probe(gpointer user) {
|
||||
line += buf;
|
||||
}
|
||||
{
|
||||
// `g` is the signal list's slide-over page now: it opens it, the dock does not move, and a second `g`
|
||||
// closes it again.
|
||||
const double before = dock_value();
|
||||
run_app_action(*a, AppAction::ShowSignals);
|
||||
const bool opened = slideover_page(*a) == "signals";
|
||||
const double after = dock_value();
|
||||
run_app_action(*a, AppAction::ShowSignals);
|
||||
const bool closed = slideover_page(*a).empty();
|
||||
char buf[96];
|
||||
std::snprintf(buf, sizeof(buf), " g: dock %.0f->%.0f at_top=%d |", before, after,
|
||||
std::fabs(after) < 1.0 ? 1 : 0);
|
||||
std::snprintf(buf, sizeof(buf), " g: page=%d dock_still=%d closed=%d |", opened ? 1 : 0,
|
||||
std::fabs(after - before) < 1.0 ? 1 : 0, closed ? 1 : 0);
|
||||
line += buf;
|
||||
}
|
||||
before_after("l: layout", a->layout_toggle, AppAction::LayoutEditor);
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2026 Martino Ferrari
|
||||
//
|
||||
// The signal list: one row per signal with its colour chip and acquire tick, the panel around it,\n// and the drop that puts a signal on a row.
|
||||
// The signal list: one row per signal with its colour chip and acquire tick, the panel around it,
|
||||
// and the drop that puts a signal on a row.
|
||||
|
||||
#include "ui_app.hpp"
|
||||
|
||||
|
||||
+5
-5
@@ -454,12 +454,12 @@ constexpr TourStep kTourSteps[] = {
|
||||
"the view then freezes on the first event that matches, however rare it is.",
|
||||
&App::trigger_body},
|
||||
{"side", nullptr, "The dock",
|
||||
"The dock beside the plot: the list of signals, then the cursors and the measurements. The "
|
||||
"buttons in the bar above show and hide it.",
|
||||
"The dock beside the plot: the cursors, then the measurements. The buttons in the bar above "
|
||||
"show and hide it.",
|
||||
&App::side_box},
|
||||
{"signals", nullptr, "The signals",
|
||||
"Every signal the source declares, each with the switch that acquires it. A signal has to be "
|
||||
"switched on before it can be plotted, measured or searched.",
|
||||
{"signals", "signals", "The signals",
|
||||
"Every signal the source declares, each with the switch that acquires it. The + badge opens "
|
||||
"this list; a signal has to be switched on before it can be plotted, measured or searched.",
|
||||
&App::signal_list},
|
||||
{"status", nullptr, "The status line",
|
||||
"The stream's own report, once a second: the display mode, the rate arriving, the window on "
|
||||
|
||||
Reference in new issue
Block a user