ui: the trigger glyph is a sine crossing its level, and every glyph is inset

The trigger's toolbar icon was a rising edge, which names only one of the four shape triggers the panel
offers. It is now a sine with a dashed level through it — the trigger's actual job, find the signal where
it crosses this — and the Signals tab's pill carries the *same* picture beside the channel list, so the
pill and the button that turns the trigger on read as the same idea. Both are one period of one-pixel
stairs, the way the plot rasterises its own traces.

Everything is inset two pixels now. The previous drafts ran their level and their bars flush to the cell
edge, which is what makes art look shaved wherever a button's padding or a crop takes a pixel: nothing in
either tab glyph, or in the trigger's, touches an edge.

Each shape was drawn and then read back as an ink map rather than trusted — the first sine came out as a
thin antialiased diagonal (a polygon through whole-pixel vertices has diagonal *edges*), which is why the
band is now per-column stairs; the level-through-the-centre arrangement was chosen over level-above-the-
centre after rendering both, since at these sizes a level nearer the crest merges with it into a blob. The
four glyphs measure 18%, 22%, 25% and 25% ink, inside the range the icons above them set.

Verified: `toolbar icons: trigger=udpscope-trigger-symbolic(ok)` and
`page icons: measure=udpscope-tab-measure-symbolic(ok) signals=udpscope-tab-signals-symbolic(ok)`;
`switcher: policy=narrow pills=2 labels=0 names=2 icon=32x32 size=308x36`;
`[side-tab-probe] … advanced=4`; `make test-ui` 14 files; zero `Gtk-CRITICAL` and no missing icon. The
icons' GResource blob is 12408 bytes — the SVG sources are embedded, comments included — still over the
C99 limit, so the clean-build warning count stays four.
This commit is contained in:
Martino Ferrari committed 2026-10-14 13:45:57 +02:00
1 parent dbe6377415
commit 75aa62cc3c
6 files changed
+70 -49

No files matched your search

+2 -1
View File
@@ -2394,7 +2394,8 @@ now — they are listed so they are not mistaken for anything else:
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 built
sidebar is a **Measure** tab (the cursors readout and the measurements) and a **Signals** sidebar is a **Measure** tab (the cursors readout and the measurements) and a **Signals**
tab (the trigger and the channels), **each a pill with its own glyph and no text** — the tab (the trigger and the channels), **each a pill with its own glyph and no text** — the
cursors and the ruler for Measure, the trigger and the channel list for Signals, drawn as a cursors and the ruler for Measure, a sine crossing its trigger level and the channel list for
Signals (the same picture the toolbar's trigger button carries), drawn as a
32x16 pair so both motifs are legible at a glance — with the words kept as each pill's tooltip and 32x16 pair so both motifs are legible at a glance — with the words kept as each pill's tooltip and
accessible name. The design spec had the switcher showing *titles* and neither page an accessible name. The design spec had the switcher showing *titles* and neither page an
`icon-name`; `narrow` alone does not do it (libadwaita still draws a label beside the icon, `icon-name`; `narrow` alone does not do it (libadwaita still draws a label beside the icon,
+6 -1
View File
@@ -66,7 +66,12 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
`udpscope-tab-signals-symbolic` is the trigger's edge above the channel list it acts on. They are not `udpscope-tab-signals-symbolic` is the trigger's edge above the channel list it acts on. They are not
reuses because a glyph the toolbar already carries means two things the moment a tab shows it too. Both reuses because a glyph the toolbar already carries means two things the moment a tab shows it too. Both
are **32x16** composites, one motif per half: the Measure pill is the two cursors beside the ruler, the are **32x16** composites, one motif per half: the Measure pill is the two cursors beside the ruler, the
Signals pill the trigger beside the channel list. Wider than tall on purpose — a 16px square cell carries Signals pill a sine with a dashed level through it beside the channel list. That picture is shared on
purpose: the trigger's *own* toolbar icon (`udpscope-trigger-symbolic`) is the same sine and the same
level, one period of one-pixel stairs, so the pill and the button that turns the trigger on are
recognisably about the same thing. The trigger glyph used to be a rising edge, and one edge names only
one of the four shape triggers the panel offers — a wave crossing a level names the trigger's job.
Wider than tall on purpose — a 16px square cell carries
one motif, and squeezing a pair into it is what turns both into a smear — and the pill has the width for one motif, and squeezing a pair into it is what turns both into a smear — and the pill has the width for
it, in a 300px column whose switcher has two pills in it. **GDK paints an icon into a square box whatever it, in a 300px column whose switcher has two pills in it. **GDK paints an icon into a square box whatever
the document's aspect** (measured: `gtk_icon_theme_lookup_icon()` at 32 reports `32x32`, and librsvg the document's aspect** (measured: `gtk_icon_theme_lookup_icon()` at 32 reports `32x32`, and librsvg
+1 -1
View File
@@ -16,7 +16,7 @@ xvfb-run -a env UDPSCOPE_EXIT_AFTER_MS=8000 UDPSCOPE_ENABLE_ALL_MS=1000 \
Two baselines every run is read against. A clean build emits **four** warnings and no more: two Two baselines every run is read against. A clean build emits **four** warnings and no more: two
`-Wmissing-field-initializers` on `MathNode::source` and `-Woverlength-strings` on **both** `-Wmissing-field-initializers` on `MathNode::source` and `-Woverlength-strings` on **both**
generated GResource `.c` files — the help pages' (**514660** bytes) and the icons', whose blob is generated GResource `.c` files — the help pages' (**514660** bytes) and the icons', whose blob is
**11661** against C99's 4095-byte string-literal limit with the seven action icons and the two app icons **12408** against C99's 4095-byte string-literal limit with the seven action icons and the two app icons
that remain (a note about a generated file, which GCC itself supports) -- re-measured when that remain (a note about a generated file, which GCC itself supports) -- re-measured when
the tab work changed the set (that icon left, the two tab glyphs arrived, and this file's the tab work changed the set (that icon left, the two tab glyphs arrived, and this file's
help page was rewritten), which did not move the count; re-measure by touching each `*.gresource.xml` help page was rewritten), which did not move the count; re-measure by touching each `*.gresource.xml`
@@ -1,26 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- SPDX-License-Identifier: MIT --> <!-- SPDX-License-Identifier: MIT -->
<!-- SPDX-FileCopyrightText: 2026 Martino Ferrari --> <!-- SPDX-FileCopyrightText: 2026 Martino Ferrari -->
<!-- The Measure tab, as two motifs side by side in a 32x16 cell: the two cursors on the left (a cap <!-- The Measure tab: the cursors and the ruler they read against. The caps and the dashed shafts
over a dashed shaft, the shape the toolbar's cursors button has) and the ruler they read against are the toolbar's cursors at the same 2px weight; the ruler's ticks hang inside its frame, which
on the right. The ruler is a closed frame with its ticks hanging inside it, which needs needs `fill-rule="evenodd"` — a tick inside the frame's hole is three crossings deep, so it fills
`fill-rule="evenodd"`: the ticks sit inside the frame's hole, three crossings deep, so they fill rather than punching a second hole. Both halves are inset two pixels and centred on the same
rather than punching a second hole. The cursors do not overlap the ruler, so one evenodd path line, so the pair reads as one instrument rather than as two icons that share a box. -->
carries both without either cancelling. Rendered at 32, i.e. 1:1, so the 2px bars stay crisp,
and every coordinate is a whole pixel of the cell. As with the Signals glyph, GDK's box is
square whatever the document's aspect: this pair is letterboxed, not stretched. -->
<svg height="16px" width="32px" viewBox="0 0 32 16" xmlns="http://www.w3.org/2000/svg"> <svg height="16px" width="32px" viewBox="0 0 32 16" xmlns="http://www.w3.org/2000/svg">
<path fill="#2e3436" fill-rule="evenodd" d="M1 1 H7 V4 H1 Z <path fill="#2e3436"
M3 5 H6 V8 H3 Z fill-rule="evenodd" d="M2 2 H7 V4 H2 Z
M3 9 H6 V12 H3 Z M10 2 H15 V4 H10 Z
M3 13 H6 V16 H3 Z M3 5 H5 V7 H3 Z
M9 1 H15 V4 H9 Z M3 9 H5 V11 H3 Z
M11 5 H14 V8 H11 Z M3 13 H5 V15 H3 Z
M11 9 H14 V12 H11 Z M10 5 H12 V7 H10 Z
M11 13 H14 V16 H11 Z M10 9 H12 V11 H10 Z
M17 4 H31 V12 H17 Z M10 13 H12 V15 H10 Z
M19 6 H29 V10 H19 Z M17 5 H30 V12 H17 Z
M21 6 H22 V9 H21 Z M19 7 H28 V10 H19 Z
M25 6 H26 V9 H25 Z M20 7 H21 V9 H20 Z
M28 6 H29 V8 H28 Z"/> M23 7 H24 V9 H23 Z
M26 7 H27 V9 H26 Z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1,21 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- SPDX-License-Identifier: MIT --> <!-- SPDX-License-Identifier: MIT -->
<!-- SPDX-FileCopyrightText: 2026 Martino Ferrari --> <!-- SPDX-FileCopyrightText: 2026 Martino Ferrari -->
<!-- The Signals tab, as two motifs side by side in a 32x16 cell: the trigger on the left (a rising <!-- The Signals tab: the toolbar's trigger picture — a sine with a dashed level through it — beside
edge crossing a dashed level, the shape the toolbar's trigger button has) and three rows of a the channel list. Sharing that picture is the point: the pill and the toolbar button that turns
colour chip and a name on the right, which is the channel list the tab mostly is. Wider than the trigger on should be recognisably about the same thing. Three channel rows on the right, on
tall because it has to hold *two* motifs: a 16px square cell can carry one, and squeezing the the left half's centre line, and everything inset two pixels, because art flush with the cell
pair into it is what turns them into a smear. The pill renders this at 32, i.e. 1:1, so the 2px edge is what a button or a crop clips. -->
bars stay crisp; a fractional scale would blur them, which is why every coordinate is a whole
pixel. GDK paints an icon into a *square* box whatever the document's aspect, so this pair is
letterboxed in a 32x32 box rather than stretched (measured: a 32x16 render inside a 32 request).
All subpaths are filled `path`s wound the same way. -->
<svg height="16px" width="32px" viewBox="0 0 32 16" xmlns="http://www.w3.org/2000/svg"> <svg height="16px" width="32px" viewBox="0 0 32 16" xmlns="http://www.w3.org/2000/svg">
<path fill="#2e3436" d="M4 2 H6 V12 H4 Z <path fill="#2e3436" d="M2 6 H3 V10 H2 Z
M6 2 H14 V4 H6 Z M3 4 H4 V8 H3 Z
M0 7 H2 V9 H0 Z M4 3 H5 V7 H4 Z
M8 7 H10 V9 H8 Z M5 2 H6 V6 H5 Z
M12 7 H14 V9 H12 Z M6 3 H7 V7 H6 Z
M7 4 H8 V8 H7 Z
M8 6 H9 V10 H8 Z
M9 8 H10 V12 H9 Z
M10 9 H11 V13 H10 Z
M11 10 H12 V14 H11 Z
M12 9 H13 V13 H12 Z
M13 8 H14 V12 H13 Z
M14 6 H15 V10 H14 Z
M2 7 H5 V9 H2 Z
M7 7 H10 V9 H7 Z
M12 7 H15 V9 H12 Z
M18 2 H21 V5 H18 Z M18 2 H21 V5 H18 Z
M23 3 H30 V5 H23 Z M23 3 H30 V5 H23 Z
M18 7 H21 V10 H18 Z M18 7 H21 V10 H18 Z

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -1,16 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- SPDX-License-Identifier: MIT --> <!-- SPDX-License-Identifier: MIT -->
<!-- SPDX-FileCopyrightText: 2026 Martino Ferrari --> <!-- SPDX-FileCopyrightText: 2026 Martino Ferrari -->
<!-- A rising edge crossing a dashed trigger level: what the trigger *is*. The dash <!-- The trigger: a sine and the dashed level it is looked for at, which is what a trigger *is* on a
rhythm is 2 on, 1 off (tight enough to read as one line rather than as a row of waveform that is not a step. It used to be a rising edge, and one edge names only one of the four
dots), with the one wide gap at x 5..9 that the edge rises through. Every shape triggers the panel offers — a wave crossing a level names the trigger's job instead. One
coordinate is a whole pixel of the 16px cell so nothing blurs at 1x. --> period, one-pixel stairs, the way the plot rasterises its own traces, with the level through the
centre so the crest and the trough stand clear and the crossings fall on the steep parts. Inset
two pixels, like the tabs' glyphs, so no art is flush with an edge a button might clip. -->
<svg height="16px" width="16px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> <svg height="16px" width="16px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path fill="#2e3436" d="M0 11 H8 V13 H0 Z <path fill="#2e3436" d="M2 7 H3 V10 H2 Z
M6 3 H8 V13 H6 Z M3 5 H4 V8 H3 Z
M6 3 H16 V5 H6 Z M4 3 H5 V6 H4 Z
M0 7 H2 V9 H0 Z M5 3 H6 V6 H5 Z
M3 7 H5 V9 H3 Z M6 4 H7 V7 H6 Z
M9 7 H11 V9 H9 Z M7 6 H8 V9 H7 Z
M12 7 H14 V9 H12 Z"/> M8 8 H9 V11 H8 Z
M9 10 H10 V13 H9 Z
M10 11 H11 V14 H10 Z
M11 11 H12 V14 H11 Z
M12 9 H13 V12 H12 Z
M13 7 H14 V10 H13 Z
M2 7 H5 V9 H2 Z
M7 7 H10 V9 H7 Z
M11 7 H14 V9 H11 Z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 857 B

After

Width:  |  Height:  |  Size: 1.5 KiB