Skip to content

feat(pi): rebuild the touchscreen layout — graph-over-tabs dashboard, run-only Run page, one clean header - #204

Merged
gbradham merged 25 commits into
mainfrom
claude/pi-touchscreen-layout-5da409
Oct 21, 2026
Merged

gbradham merged 25 commits into
mainfrom
claude/pi-touchscreen-layout-5da409

Conversation

@gbradham

Copy link
Copy Markdown
Member

Summary

The Raspberry Pi touchscreen (Runner mode, 480×800 portrait) looked unfinished and was partly broken:

  • The desktop tab bar was drawn over the experiment name, which left a sliver of stray text at the top left.
  • There were two navigation bars.
  • Analyze appeared on the Pi.
  • The Dashboard tab showed empty side panels.
  • The graph could only be panned with a middle mouse button.
  • Text was clipped at 480 px.

This rebuilds the layout. Desktop mode is unchanged.

  • Header: a two-row header on every Pi page.

    • Row 1 has the experiment name and unsaved marker, the run-state pill (tap it to go to Run while a run is live), one dot per board, and a ⚙ menu: New, Open…, Save, Save As…, Connect boards…, Help, Switch to Desktop, Exit.
    • Row 2 has the tabs Experiment · Dashboard · Run. There is no Analyze tab.
    • A notice bar under the header replaces the status bar that used to appear under START/STOP. A critical notice is never replaced by a lesser one.
  • Dashboard: the node graph sits on the top half, with Nodes · Properties · Hardware · Camera · Control tabs under a draggable grip.

    • The graph starts on View only · tap to edit. While locked you can pan with one finger, zoom and fit. You cannot move, connect, delete or add nodes; Properties is read-only, and Hardware's add, edit and remove actions are off.
    • A running experiment forces the lock on.
    • Tapping a library node drops it in the middle of the graph.
    • The graph is fitted to the view when the dashboard opens.
    • Control is the old Manual tab (manual device controls and Functions).
  • Run: this page is only for running. It shows:

    • per-run fields, such as Dam ID; required ones block Start;
    • a READY / NOT READY / NEEDS DETAILS pill;
    • the big clock;
    • two columns of device cards;
    • camera video while the camera streams;
    • Start run when idle, or Pause/Resume and Stop while live.

    It reuses the desktop RunHeader, RunControlPanel and FieldTags through new touch= and columns= flags, so START gating, paused-time bookkeeping and the flow-duration snap are shared with desktop.

  • Experiment: section chips across the top, with full-width sections that fit 480 px.

  • Touch:

    • finger (kinetic) scrolling on every Pi panel and list;
    • 44 px touch targets in touch.qss;
    • a neutral button style, with green START and red STOP/REC;
    • it fits 800×480 landscape.
  • Connect boards… now actually connects: it opens the port dialog, then connects all boards when no run is live.

  • Switch to Desktop re-hosts the same widgets into the desktop editor, restoring the normal header and Analyze. It is refused mid-run.

  • Removed: RunnerShell, RunnerSetupPage, RunBanner and RunnerPanel, with their tests.

  • Docs: docs-site/runner/ is rewritten, with new screenshots.

Known follow-ups

  • On the Pi the critical UNSAFE STATE notice is capped at two lines. It keeps the hazard in its title, but the "verify every output device" instructions are cut off. Fix by shortening the runner copy or moving the instruction into the title.
  • The old run banner's one-tap STOP from other tabs is gone. Stopping from another tab is now: tap the run pill, then Stop.
  • The splitter grip is 14 px. With a notice and a streaming camera both up in landscape, the hint line under Start is cut.
  • Needs a check on real Pi hardware, especially finger scrolling and the press-and-hold to drag a node out of the library.

Test plan

  • Full suite: QT_QPA_PLATFORM=offscreen uv run --no-sync pytest -q -m "not slow" gives 6674 passed, 4 skipped, 7 deselected. That is 105 net new; 6569 passed on main@15ee496.
  • ruff check src tests plugins and black --check src tests plugins are clean.
  • Every Pi page rendered offscreen at 480×800 and 800×480, and compared against the approved mockup.
  • On a Pi with the touchscreen: boot to the kiosk, then
    • load an experiment;
    • Connect boards…;
    • fill the run fields;
    • Start, Pause, Resume and Stop;
    • unlock and edit a node, tap-add a node;
    • scroll the Experiment sections;
    • Switch to Desktop.

touch.qss is rewritten for the Pi's new layout. Buttons are neutral by
default; cyan marks only the selected thing (tabs, chips, the editing
lock), green is START and red is STOP / REC. Every tap target is 44 px.

- Header: CHROME rows with a 1 px divider under each, underlined shell
  tabs, the status strip rules ported from desktop.qss (pill states with
  a red REC tint, device dots and tinted chips), the gear without its
  menu arrow, 44 px menu items, and the notice bar's three levels.
- Dashboard: 48 px panel tabs with an accent underline, a borderless
  pane, a 14 px splitter grip, and the graph lock's two looks.
- Run: a 64 px monospace clock (green while live), 44 px run-field chips,
  READY / NOT READY / NEEDS DETAILS pills, SURFACE_1 device cards and a
  run-bar card.
- Experiment: 44 px section chips with an accent :checked tint, form
  labels centred on their fields, inputs on SURFACE_1, side-by-side
  - / + spin buttons, radio and check indicators.
- Dead rules are deleted: runnerRunBanner, readinessStrip/readinessRow,
  runnerHeader, runnerControls, and the .HeaderBar / .ControlPanel /
  .StatusIndicator / .VirtualKeyboard family that nothing uses. Every
  colour is a Deep Navy token or a tint of one.

Three small touch-only code changes:
- graph_view re-fits the lock button on reposition, because the window's
  sheet lands after the button was first sized and its text clipped;
- the notice bar repolishes its label, so the text takes a new level's
  colour;
- the touch RunControlPanel paints its stylesheet background and wraps
  its board line.

Renders (480x800, examples/box_pi_side.glider), docs/superpowers/renders/:
experiment.png, experiment_mice.png, experiment_zones.png,
experiment_vocabulary.png, experiment_plugins.png,
dashboard_nodes.png, dashboard_nodes_mid.png, dashboard_nodes_end.png,
dashboard_properties.png, dashboard_properties_node.png,
dashboard_hardware.png, dashboard_camera.png, dashboard_camera_mid.png,
dashboard_camera_end.png, dashboard_control.png,
dashboard_control_mid.png, dashboard_control_end.png,
dashboard_unlocked.png, dashboard_recording.png, run_idle.png,
run_recording.png, notice_critical.png, gear_menu.png
…n the Pi

The previous commit said every tap target was 44 px. That was not true:
the Lab Vocabulary rows were 28 px with 24x22 remove buttons, and the
12 px scroll-bar handles were the only way to scroll most panels by
finger. All of these changes are runner-only; desktop is unchanged.

- Vocabulary (touch mode): the remove button is a fixed 44x44, which
  makes each row 48 px. The row's size hint is taken before any
  stylesheet reaches it, so the size is set in code. The list is tall
  enough to show three rows. touch.qss gains a neutral base QToolButton
  rule, so the button no longer falls back to the native bevel.
- Finger scrolling: QScroller drag-scrolling on the scroll areas of the
  Nodes, Hardware (tree) and Camera tabs, and on the compact Experiment
  sections and the scroll areas inside them. The bars stay thin
  indicators. Zones is left out on purpose: its preview is drawn on by
  dragging, and a scroller would take every quick drag. Properties has
  no scroll area to give it.
- Graph fit: the Pi dashboard frames the flow on its first show and
  after New, Open or a load (pending if hidden). In touch mode the fit
  uses the band below the lock pill, so the pill never covers a node.

Still under 44 px: the 14 px splitter grip (by design), the Mice and
Zones table rows (the header's default section size), and the thin
scroll bars, now indicators only.

Renders checked (docs/superpowers/renders/): experiment_vocabulary.png,
dashboard_nodes.png, dashboard_unlocked.png.
…n the desktop switch

- Item views scroll by finger. A new FingerScroll grabs a QScroller on
  every QAbstractScrollArea under the panels it is given (combo-box popup
  lists excepted), not only QScrollArea/QTreeView. That brings in the
  Mice and Zones tables and the Lab Vocabulary lists. Item views scroll
  per pixel while grabbed, so a flick glides. The Zones section's own
  scroll area still gets no scroller (ruling R11): its preview is drawn
  on by dragging.
- Properties scrolls. In runner mode PropertiesHost holds the form in a
  vertical QScrollArea, which gets a scroller. A tall form (the example's
  WaitForInput, about 950 px) is now laid out whole instead of crushed
  into the tab. host.widget() is still the form, and the tab is still
  the host.
- Switch to Desktop releases every scroller the Pi grabbed, from the
  list kept when grabbing, and puts the item views' scroll modes back.
  A scroller left on a re-hosted panel swallowed the mouse moves that
  drag-to-add needs. The Experiment page stops grabbing for sections
  built later.
- Spin boxes take a 110 px floor. Qt's size hint counts only one of the
  two side-by-side buttons, and special text such as "No timeout" was
  clipped.

Still under 44 px: the 14 px splitter grip, and the Mice and Zones table
row heights (from the header's default section size).

Renders checked (docs/superpowers/renders/): dashboard_properties_wait.png,
dashboard_properties_wait_end.png, dashboard_control.png,
experiment_mice.png.
Rewrite the Runner docs around what shipped: the two-row header (name with
unsaved dot, board dots, run-state pill, gear menu, tabs), the sticky notice
bar, the Experiment / Dashboard / Run tabs, the graph lock and the five panel
tabs, the Run page (run fields, readiness pill, clock, device cards, camera,
Start / Pause / Stop), and Switch to Desktop (refused mid-run, one-way).

Replace runner.png with the Run tab render and add runner-dashboard.png.
Fix the old four-tab / run banner / Setup tab wording in the Runner index,
the home page, concepts, the ecosystem reference, troubleshooting, the
dashboard page and the subjects page.

mkdocs is not installed in the project venv, so the site was not built; the
relative links, anchors and images in docs-site/ were checked with a script.
A critical notice with a long unbreakable token widened the window (833 px
at 480) and a wrapped one grew without limit. The text now has a 1 px
minimum width; info and warning take one elided line with the whole text
in the tooltip, and critical wraps to two lines at most, the second elided.
The spec's icon is a level glyph before the text (ⓘ, ⚠), and a 44 px ✕ at
the right dismisses, as a tap anywhere still does. text() is unchanged.
The runner window needed 642 px at the least (444 before this branch), so
on an 800x480 screen held landscape, which the view manager runs as Runner,
Start was below the screen. The Pi dashboard took the graph's desktop 300 px
floor plus the panel tabs' tallest page, and the Run page 136 px for the
device cards' scroll area.

Runner only: the graph's floor drops to 120 px (Switch to Desktop restores
400x300), the panel tabs' to 140 px (a taller page scrolls, or the grip
gives it room), and the cards and the camera slot on the Run page give up
their room first. At 800x480 the window now needs 434 px, 479 with a
critical notice up, and every page fits.
The board dialog behind ⚙ › Connect boards… and the Hardware tab's button
only edits each board's port (Cancel / Save), so the Pi had no tap path to
connect-all while START waits for a connected board and the Run hint and
the docs send operators there. Both now go through one method that opens
the dialog and then connects every board, unless a run is live.
`glider --runner --file X` showed an empty graph: the core had loaded the
file, but only the desktop branch of _setup_ui opened it into the window.
Both branches now do, so the Pi dashboard's graph shows the flow.

A manual function in progress is on the Control tab, not the Manual tab;
the comments and the run_timer docstring that still named the run banner,
the Runner panel and the Manual tab now name what replaced them.
…the window

The lock-during-run test covers RUNNING, PAUSED, INITIALIZING and STOPPING,
and two window-level tests pin the Run page's pause/resume wiring and that
editing a run value refreshes the run info.
The header's order is name, "— edited", run pill on the left, board dots
and ⚙ on the right; the pill also reads Starting and Stopping and jumps to
Run only while a run is live. The Run pill's position and its INITIALIZING,
STOPPING and ERROR states, readiness with no boards configured, the notice
bar's glyph and ✕, and that Connect boards… now connects. Open comes before
Connect in the walkthrough, since the dialog needs a loaded experiment.
"⚙ ›" throughout, the ecosystem reference lists the whole menu, and a long
troubleshooting line is re-wrapped.
Setting a scroll mode on a header view that had not laid out its sections
segfaulted the macOS CI run while the Pi dashboard was being built, and a
drag on a header is a column resize, not a scroll. FingerScroll now skips
QHeaderView alongside combo-box popups; a test checks no header is grabbed.
touch.qss asks for -apple-system first, so Linux and Windows fall back to
wider faces and CI measured the Pi pages past 480 px. Make the widths
independent of the font rather than tuned to macOS metrics:

- Experiment section chips sit in a finger-scrolled strip that hugs its
  row, so their labels never set the page's minimum width.
- The touch tab bar's tabs take an Ignored width: the stretch alone splits
  the row in thirds.
- The status strip in touch mode can shrink a board name to its dot, marks
  unsaved work with a single dot, and labels a recording pill "● REC" (the
  mockup's word).
- The Metadata form puts labels above fields on the Pi, and the compact
  Zones editor stacks its two button rows.
- The field-tags elision test no longer assumes a label fits at one width.

Verified locally with touch.qss and the app font forced to Verdana and to
Courier New (wider than the macOS face, as on CI).
Windows CI still measured the Pi Run page at 528 px and the Zones section at
490 px. The run page's width came from the clock: 64 px of eight digits in
whatever monospace face the platform falls back to (none of DejaVu Sans
Mono, Liberation Mono, Menlo or SF Mono is on Windows). The clock now asks
for no width of its own and, given too little, paints its digits at the
largest size that fits. The Zones section's widest row was the three shape
radio buttons abreast; on the Pi they stack, like the button rows already do.

Tests: a 200 px clock no longer widens the page (750 px before the fix),
and the shape choices stack on the Pi but not on the desktop.
@gbradham
gbradham merged commit 63d7025 into main Oct 21, 2026
7 of 8 checks passed
@gbradham
gbradham deleted the claude/pi-touchscreen-layout-5da409 branch October 21, 2026 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant