Files
udpscope/docs/superpowers/specs/2026-11-02-ui-badges-dock-design.md

10 KiB

Badge bar, readouts dock and slide-over panel -- design

Date: 2026-11-02. Status: approved in conversation, awaiting written-spec review.

Goal

Replace the sidebar's tab switcher and the separate events pane with a layout built the way bench scopes build theirs: setup is reached by the object it configures (a badge under the plot), results are always on screen (a compact dock), and anything bigger than a readout opens in one slide-over panel. The sidebar tabs and the left events pane go away.

Why (the problems, as found in the code)

  • Unreadable tab titles. side_view is pinned at 320 px (min-sidebar-width = max-sidebar-width in main-window.ui), so four text tabs ("Measure / Signals / Analog out / Digital I/O") ellipsize, and a source can add more.
  • Hard to find things. The Signals tab stacks a trigger form of up to 15 rows (plus two expanders) on top of the signal list. Cursors and measurements are in Measure, source controls are in their own tabs, the header is icon-only, and the trigger/cursor toggles live apart from their panels.
  • The events pane is a different application. A GtkPaned + GtkRevealer on the left with its own bold title and header, a dragged width that is not saved, thumbnails the rest of the UI does not have, and a header toggle that is hidden until events exist.

State of the art this follows

  • Tektronix 5/6 Series: a badge bar under the waveform (a badge per channel/math waveform, plus Horizontal, Trigger, Acquisition badges); a tap opens only that item's settings; results (measurements, cursors, search) are a separate bar that does not cover the waveform.
  • Scopy and PicoScope 7: a channel list at the edge and a settings panel that slides in for the selected item (PicoScope lets it overlay or squeeze the trace).
  • Saleae Logic 2: one side panel at a time; decoded results also in a list beside the waveform.
  • Keysight InfiniiVision: dialogs that can be undocked so cursor and measurement values show together.

Layout

┌ header: [window 2 s ▾] [Trigger] [Pause] [Cursors]        [Dock] [☰] ┐
├──────────────────────────────────────────────┬────────────────────┤
│  plot rows                      ┌───────────┐ │ READOUTS           │
│                                 │ slide-over│ │  cursors, measures │
│                                 │ settings  │ ├────────────────────┤
│                                 └───────────┘ │ EVENTS (n)         │
├───────────────────────────────────────────────┴────────────────────┤
│ [●sine][●square][+]  …                        [Trigger ▸][Source ▸]│
├ status line ───────────────────────────────────────────────────────┤

Header bar

Keeps the time-window control, the Trigger, Pause and Cursors toggles and the overflow menu. The Events toggle is removed (Events is always in the dock). The sidebar toggle becomes the Dock toggle, so a narrow window can still hide the dock. There is no "Window" badge: the header's control is the timebase, and a second one would duplicate it.

Right dock ("Readouts" and "Events")

  • Fixed width, about 280 px; always on screen, shown but insensitive until the stream has signals ("disable, do not hide"). It stays an AdwOverlaySplitView sidebar, which already gives it its own fixed width, the hide toggle and the overlay behaviour on a narrow window (invariants-plot-and-layout.md).
  • Readouts (top): the active cursor values and measurement values, read-only, one compact line per value, through the one formatter both panes share (invariants-readouts-and-cursors.md). An "Edit" affordance opens the slide-over's measure or cursors page; clicking a measurement row opens that measurement's editor.
  • Events (bottom): compact rows -- title, time, a small marker; no thumbnails. Insensitive outside trigger mode. A "Manage" affordance opens the slide-over's events page.

Badge bar

A fixed-height row between the plot and the status line.

  • Signals (left): one badge per enabled signal, up to 6; beyond that a single "N signals" badge. A badge shows a colour dot and the name, is a drag handle (reorder, or drop on a plot row, which replaces today's drag from the signal list), and opens that signal's page (colour, calibration, row, math). A trailing + opens the signals page (the full list, to enable more; the math editor entry).
  • System (right): Trigger shows its state (off / armed / triggered) and opens the trigger form; Source shows the connection state and the source name and opens that source's pages (SourcePanel pages, behind a small segmented switch inside the panel when there are several, as E-1608 has Analog out and Digital I/O).

Slide-over panel

  • Overlays the plot's right edge (about 360 px wide), never the dock; one page at a time; Esc or a click outside closes it. The plot is not resized by it.
  • Pages, by id: trigger, signal:<name>, signals, measure, cursors, events, source-<id>. Each page has its own scroller, so a tall page does not leave a shorter one scrolled (the reason the sidebar had a scroller per page).
  • Content: the trigger form (conditional rows, Advanced, Search the buffer), signal and calibration settings, measurement add/edit/remove and statistics, cursor options, event actions (Live, Compare, Accumulate), the math editor entry, and source pages.

Status line

Unchanged.

Components

New files, because ui.cpp is already very large.

Unit File Responsibility
BadgeBar ui_badges.cpp Builds signal / Trigger / Source badges from the model; emits "open page X"; owns badge drag.
SlideOver ui_slideover.cpp A GtkOverlay child of the plot area: revealer + page stack keyed by id; Esc/click-away; open/close API.
ReadoutsDock ui_dock.cpp The compact readouts and the compact events list, reading the same model the panes read today.

Reuse, not rewrite:

  • The trigger form, signal list and calibration, measurement editor and cursor options are reparented into slide-over pages.
  • SourcePanel{id, title, widget} is unchanged, so the E-1608 and Flat UDP panels need no change.
  • Only the events list and actions of the old pane move; the left GtkPaned/GtkRevealer and its divider sync are deleted.

Invariants kept

  • Layout must not depend on content (invariants-plot-and-layout.md): the dock has a fixed width, the badge bar a fixed height, and the slide-over overlays instead of resizing the plot. Rows that appear by trigger condition appear inside the slide-over's scroller.
  • A GtkOverlay child is picked wherever its allocation is: the slide-over is one opaque widget aligned to the end edge by margin and width, never a full-area transparent child, so pan, drop and hover on the rest of the plot are untouched.
  • The header's Trigger/Cursors/Pause toggles keep their two-way mirroring with the panels.

State and session file

  • The session file drops side_tab and gains the dock's shown state and width. An old file's side_tab is ignored (an unknown name is already harmless to select_side_tab).
  • The open slide-over page is not saved.
  • The tab ids measure, signals and source-<id> stop being a session format; they remain page ids.

What must be rewritten around it

  • Tour and context help: steps and help-figure surfaces that point at side_switcher, side_stack and side_box (ui_tour.cpp, ui_context_help.cpp) are re-pointed at the badge bar, slide-over and dock, and the tour text is rewritten.
  • Probes: the switcher: policy=... pills=... labels=... images=... pages=... line and side_tab_probe become probes that count badges, dock rows and the open slide-over page; the events divider probe goes away with the divider.
  • Keyboard: SideTabMeasure / SideTabSignals become "open the measure / signals page".
  • Docs: README.md, USER-GUIDE.md, docs/claude/* (architecture, verification, probe-readings, the plot-and-layout and gtk-and-chrome invariants), and the Mallard manual's figures.

Rollout

Four steps, each its own plan, each leaving the app building, passing the probes and usable.

  1. Shell. The plot overlay, the badge bar with Trigger and Source, the slide-over holding the existing trigger form and source pages. The sidebar tabs are removed; the existing Measure and Signals panels move into a temporary dock so nothing is lost.
  2. Signal badges. Per-signal badges, the + and "N signals" overflow, drag to reorder and to move to a row.
  3. Compact readouts. Read-only cursors and measurements in the dock; their editors in the slide-over.
  4. Events. The compact events section in the dock, the left pane and divider deleted, then the tour / help / docs sweep.

Risks

  • Overlay above the GL plot. The panel must composite and take input correctly above the plotter's GL area. The layout editor already places overlay children over the plot, so this is expected to work; step 1 confirms it before anything else is built on it.
  • Reparenting the trigger form. Its two-way mirroring with the header toggle, sync_trigger_rows and the locked summary view must keep working from a hidden-by-default page. Step 1 verifies it with the existing trigger probes.
  • Many signals. The badge bar's six-badge limit and horizontal behaviour are the one place the E-1608 (up to about 17 signals) stresses the design; step 2 includes a run on it.

Out of scope

User-arranged or undockable panels, saved layouts of the panels themselves, a touch-specific mode, and any change to the plot, trigger, measurement or source engines. Per-source tab names do not change.