Metrics held sizes of single widgets (slider ticks, scroll bars, splitters,
menu rows, window buttons), so the core changed whenever a widget needed a
size, and its scaling list was written by hand and drifted from the fields.
Metrics now holds only the shared sizes plus elevation and glyph stroke; the
widget sizes move to their widgets in the components library. Scaling walks
one list of lengths, and a test fails when a new field is not on it.
Theme::Interaction gains the wheel step, wheel zoom factor and reveal margin,
next to the drag slop, click and long-press timing it already held.
Widget::assign(property, value, Affects) replaces the compare, assign and
invalidate lines every setter repeated.
Every popup filled a nine-field request, and two recipes were copied across
components. Ui::openDropdown places content beside its owner and closes it on
an outside press or Escape; Ui::openOverlay takes a kind (Dialog, Sheet,
Notice) that fixes placement and dismissal, so callers no longer compute
anchors from the viewport or pick contradictory flags. openPopup stays for the
rare custom popup; the tooltip uses the defaults instead of restating them.
Every container took the same three statements (set sizing, emplace a layout,
add each child), and later configuration needed the emplaced reference or a
downcast. Column, Row and Stack now hold their layout and take BoxOptions
(gap, padding, alignment, sizing with the common default) and their children
in one call; options can change at any time.
Widgets can own dynamic children: emplaceChild<T>() creates and adds one,
removing it destroys it, and clearOwnedChildren() drops all of them. Lists no
longer need a parallel vector of unique_ptrs to rebuild their rows.
Widget::platform() returns the window's services (clipboard, links, text
input, platform facts). Without a window every request does nothing and says
so, so callers no longer check ui() and windowHost() for null at every use.
Components re-implemented the same behaviours each a little differently; the
core now owns them once.
- Viewport: one widget for scrolling, panning and zooming, configured by a
ViewportPolicy (axes, wheel step, zoom range, pan buttons, inertia,
overscroll, bounds). Wheel on both axes with Shift and horizontal-only
strips, touchpad stretch, Control+wheel and pinch zoom around the pointer,
drag-to-pan through the router's drag recognizer with fling, reveal(rect,
margin) and edge auto-scroll for drags.
- SelectionModel<Id>: none/single/multiple, current item, click with Control
toggle and Shift range from an anchor, select all, retain on model change.
- ReorderSession: the lift-and-drop rule shared by lists and boards (lane under
the pointer, slot before the first item whose middle is not passed, the
lifted item following the grab point).
- Commands<Id>: actions with label, enabled and checked state and shortcuts;
menus read from it and key events run through it.
MagneticStep keeps its own tuning instead of a reference that could dangle.
Ui::dispatch takes the window's tp::InputEvent log (or a whole InputState) and
the router walks it in order. ui::Event is now a routing envelope around the
input event or a recognized gesture (click, long press, drag), with the
target, receiver, accept flag and the input state at that moment: pointer
position, modifiers, held buttons, pointer sample and click count. The mirror
vocabulary is gone: ui::PointerButton, ui::Modifiers, the three-bool button
set, the Event factories, InputRouting and the per-frame hooks.
Walking the log fixes what the snapshot translation lost:
- a press and release in one frame now deliver both and click;
- PointerLeft clears hover and the pointer position;
- wheel events keep their touchpad/wheel source and scroll end;
- Back and Forward buttons reach widgets;
- every event carries the modifiers held when it happened.
Drags are recognized by the router: a widget that accepts a drag for a button
gets Dragged Started/Moved/Finished once the press moves past the theme's
drag slop (4 px, 8 px for touch), captures the pointer while dragging, and
Escape or losing window focus cancels it. Long-press and multi-click timing
and distances move into Theme::Interaction.
Ui is constructed from UiOptions and takes the canvas text metrics it
measures with. The headless UiDriver and test probes are a WidgetsTesting
library built on the recording canvas, so other libraries link it instead of
compiling test sources by path. PopupStack indices are optional instead of -1.
Widgets paint on tp::Canvas and measure text through tp::TextMetrics. The
mirror of the canvas API is gone: Painter, ScopedTranslation, ScopedClip,
ImageId, LoadedImage, ImageLoader and the ui TextAlign/FontFace/TextStyle.
Axis is the canvas Axis.
Clipping now reaches the screen: Ui paints through Canvas::Clip, which the
backend applies, instead of an adapter that dropped it.
Core links only the libraries it uses (input layer, window types, canvas)
instead of the whole GraphicApplication, and no longer carries AppStorage;
WindowHost loses persistentAppStorage, storage location is the application's
concern.
The core library is now widget-free: just the widget/layout/painter/popup/theme
engine plus the two layout implementations. Every widget, the window adapter and
the gallery moved to the components library (WidgetsComponents), along with the
tests and test support that exercise them.
The full widget set (docking, kanban, markdown, media, files, views, session),
the node editor and the debug tools now live in a separate components library
(see the WidgetsComponents branch). This branch keeps:
- src/core, src/layouts, src/widgets (the simple proof-of-concept widgets) and
src/app (the minimal window adapter without the debug inspector/menu).
- A simple gallery in examples/ (SimpleGallery) instead of the full one.
- Tests covering only the core and simple widgets, plus the app adapter.
The tooltip bubble and InputRouting moved into core so it no longer depends on
the Label widget.
- Move to the GraphicApplication layout: src/{core,layouts,widgets,app,components},
one static library each (WidgetsCore/.../WidgetsComponents) under the Widgets umbrella.
- Split the widget set: the simple proof-of-concept widgets stay in widgets/; the
full widgets (docking, kanban, markdown, media, files, views, session) and the node
editor live in src/components/, grouped by category, with their tests alongside.
- The tooltip bubble moves into core (the tooltip controller owns one) so core no
longer depends on the Label widget; InputRouting moves into core too.
- DebugInspector/DebugTools move to components/debug; WidgetApplication is now a
minimal adapter without the inspector/debug menu.
- Tests split: core + simple-widget tests in tests/, component tests in
src/components/tests/, app adapter tests in tests/app/.