Widgets: fix every component-review finding in one refactor #19

Open
agent wants to merge 10 commits from WidgetsRefactor into master
Collaborator

Widgets refactor: every finding of the components review, in one branch

This implements the findings of the Widgets components architecture review
(reports/widgets-components-review.md): one mechanism per behaviour, adopted by every component and by the
applications on master that use them. 35 commits, each building on its own; 133 files, +6.6k / −3.3k lines.

Result per finding

Finding What changed Where it is
F1 Scrolling ScrollView is now a core Viewport with one axis. Its own offset animation, 8 px touch-drag state machine, fling, two stretches and layout are gone. Fixed: a horizontal ScrollView now takes horizontal wheel and trackpad scrolling; Shift+wheel scrolls sideways in every scroller; there is one reveal (TextArea caret, TabView tabs); the carousel tells touchpad from wheel by the event's scroll source instead of testing for a whole-number delta; the Kanban horizontal-wheel workaround is deleted. Viewport gained per-edge stretch, glide, revealOffset, overlay hooks and the averaged release velocity, all with tests. a57c681, bd4b442, 717abb8
F2 Drags Slider, ResizeHandle, SeekBar, ColorWheel/ColorSquare (one shared pick helper), FloatingPanel, DockPanel and its corner grip, text selection, MenuBar window drag, ReorderableList and Kanban all use router drags. Every drag starts at the theme slop (4 px mouse, 8 px touch) and Escape cancels it. Router: drag phases go only to the widget that accepted the drag; a drag whose widget is hidden is cancelled; a capture drops an unstarted ancestor drag; Ui::claimDrag hands a long press over as a drag. 375d039, f8cb63b, 0f67bfa, f1a9b3a, 8d491ed
F3 Reorder ReorderableList and Kanban use one ReorderSession slot rule (a neighbour makes room when the lifted edge passes its middle; tests in core), one elevation shadow, Viewport::autoScrollAt and a landing glide. The list gained auto-scroll and the glide it never had. ListLayout's slot logic, CardLayout and StripLayout are gone. f1a9b3a, 8d491ed, dbc628a
F4 Lists New ListNavigator (core), ListRow and ListView (model, reused rows, SelectionModel, Up/Down/Home/End/Page/Enter/Space/type-ahead, empty state). On top of them: search results, menus and dropdowns (they had no keyboard handling; now Up/Down/Home/End/type-ahead, Enter, Right/Left for submenus, and focus is returned when the menu closes), settings rows, the file browser, the LibraryViewer track list and the theme editor's colour list. 55e4010, c3cf458, e6ae34b, 284e343, db2b70b, f0d9b6c, 8aed818
F5 Text editing Not in scope; the review rated it a separate refactor. —
F6 Node views NodeEditor and TreeView share link geometry, nearest-port search, marquee rect and look (graph::, with tests), and SelectionModel. Not done: a common GraphCanvas base on the Viewport camera (see the scope notes below). cc88ae1
F7 Titled panels New TitledPanel (header with leading and trailing slots, a body that can scroll, a footer, a corner slot, one content API). Dialog, DockPanel, FloatingPanel and StackPage are titled panels now. DockPanel::PanelLayout, Collapsible::SectionLayout, HeaderLayout and CollapsibleRevealLayout are replaced by stock layouts plus BoxLayout::setReversed. 02a86a7
F8 Segmented choices SegmentedControl<Value> on SelectionModel, with arrow keys. RadioGroup keeps its choice in the same model. The TabView tab strip and the MarkdownEditor mode buttons use it. f100ede, 2461839
F9 Property editors PropertyList: rows bound once to a getter and setter (value, slider, checkbox, choice, text, custom). refresh() pulls values without writing them back. Used by the LibraryViewer track info (it was one \n-joined string) and the theme editor's metrics. 8aed818
F10 Glyphs Core Glyph set plus paintGlyph. The set gains Volume, Muted and ResizeCorner, plus a turn for the opening chevron. Transport buttons, the check mark, the list grip, the dropdown chevron, number-field steps, the add-colour cell, the menu icon, the dock grip and the collapsible chevron all use it. No inline glyph drawing is left in the components. b0dfeb0, bd3cad6, 375d039, b727e0d
F11 Visuals InteractionVisual and one elevation shadow. Buttons, toggles, window buttons, the scroll bar, ResizeHandle, the dock grip, Kanban cards, colour swatches and the seek handle fade the same way. Four private withAlpha copies became Palette::faded. b0dfeb0, bd3cad6, fcd5e78
F12 Commands MenuEntry::command(commands, id) takes the label, shortcut, enabled and checked state from Commands<Id>. NodeEditor's command switch is now a Commands registry with availability. Found by the new test: a menu behind a plain popup button never refreshed its entries' state; every menu now refreshes when it opens. a3e49fe
Smell 18: owned children MarkdownView, Breadcrumb, colour swatches, dialog buttons, settings sections and rows, menu items, separators and titles are owned by their parent (emplaceChild, new emplaceChildAt). 22722f7, 2b277e6, 284e343, a784d34
Custom layouts GridLayout gains per-column widths (MarkdownView tables, PropertyList); RowLayout gains overflow (MenuBar). Of 17 custom layouts, 11 remain; seven of them are thin subclasses of stock layouts or are layouts the review said to keep (see below). 0f85def, 22722f7, 0f67bfa, cee845c
Gallery New Forms page (ListView, PropertyList, SegmentedControl, a Commands menu) and Panels page (TitledPanel, scrolling body, Dialog), each with tests. 242a6c7

Applications updated in this branch

  • LibraryViewer: the track list is a ListView (keyboard, the selection survives filtering, empty state), and
    the track info is a PropertyList.
  • 3DEditor and Sketch3D: ported onto the refactored Widgets (they did not build on master).
  • Theme editor example: colour list and metric sliders, as above.

Verification

  • Full tree build (Debug, tests and examples on): 0 failing targets. Master has 3 (3DEditor Entry,
    Sketch3D Entry, SketchGUI).
  • Tests passing, master → this branch: Widgets core 134 → 156, Widgets components 789 → 851,
    LibraryViewer 16 → 18, Sketch3D 2 (it did not build on master).
    All other suites in the tree pass (Allocators, Callstack, Connection, Containers, DataAnalysis,
    GraphicApplication 108, Math, Objects, RasterRender, RayTracer, Strings, TextEditor, Threading).
  • No test file lost a test. Tests that asserted the old behaviour were changed, and each change is named in its
    commit:
    • the carousel wheel test asserted the F1 defect (a horizontal scroller passing horizontal scrolling on);
    • the slider test checked pointer capture on press;
    • the StackPage test checked that the title is detached (it is now hidden);
    • the Kanban slot-outline test now compares against the lifted column's width.

Scope notes and decisions to check

  • Not verified visually. There is no display on the build machine and the gallery has no screenshot path.
    The look was checked through recorded draw commands in tests, not by eye. Please run the gallery and look at
    Lists, Reorder, Kanban, Forms, Panels and the menus.
  • F6 GraphCanvas base not done. Geometry and selection are shared. The two cameras are not: the tree's
    magnetic zoom steps and full view and the editor's free zoom behave differently on purpose, and moving them onto
    one Viewport would change behaviour the current tests do not cover. Kept as follow-up work.
  • TabView CarouselLayout kept. Moving it onto the Viewport needs a paged inertia mode built from
    MagneticStep. Its own wheel heuristic was fixed (scroll source); the layout itself was not replaced.
  • Remaining custom layouts. These stay:
    • SplitLayout, DockLayout and TreeViewLayout, which the review said to keep;
    • KanbanBoard::BoardLayout, which places the lifted card and full view (application-specific geometry);
    • ReorderableList::ListLayout, which places the lifted item;
    • Collapsible::RevealLayout, which is the animated height window;
    • thin subclasses of stock layouts: MenuBarLayout (centre widget), TitledLayout (corner slot), LandingLayout
      (starts the landing glide), AreaLayout (re-reveals the caret).
  • Explicit ownership kept where the lifetime differs from "child of one parent":
    • SegmentedControl buttons (made by a factory the caller supplies);
    • Kanban column views (lifted out while dragged);
    • PageStack pages (alive while they slide out).
  • API changes (all callers on master updated):
    • ScrollView gains revealRange and revealOffsetFor;
    • SearchField::rows() → results() / row(i);
    • FileBrowser::rows() → listed(); FileRow and scroll → list;
    • LibraryWidget::visibleTracks() returns tracks, and tracks() is the list;
    • Dialog::buttons(), Menu::items(), MenuBar::titles() and SettingsPage::rows() return plain pointers;
    • TransportButton::Glyph → core Glyph;
    • StackPage::setContent takes a pointer like every titled panel.
  • Not on master and not touched: Sketch2D (PR #17) and Taskboard (PR #18), which have their own copies (F12's
    CommandRegistry and BoardCommand, their lists). They can adopt these components after this branch merges.
    Objects' osg uses ImGui and is not affected. DocumentFolderPanel has no user on master, so it was not added.
## Widgets refactor: every finding of the components review, in one branch This implements the findings of the Widgets components architecture review (`reports/widgets-components-review.md`): one mechanism per behaviour, adopted by every component and by the applications on master that use them. 35 commits, each building on its own; 133 files, +6.6k / −3.3k lines. ### Result per finding | Finding | What changed | Where it is | |---|---|---| | **F1 Scrolling** | `ScrollView` is now a core `Viewport` with one axis. Its own offset animation, 8 px touch-drag state machine, fling, two stretches and layout are gone. Fixed: a horizontal ScrollView now takes horizontal wheel and trackpad scrolling; Shift+wheel scrolls sideways in every scroller; there is one `reveal` (TextArea caret, TabView tabs); the carousel tells touchpad from wheel by the event's scroll source instead of testing for a whole-number delta; the Kanban horizontal-wheel workaround is deleted. Viewport gained per-edge stretch, glide, `revealOffset`, overlay hooks and the averaged release velocity, all with tests. | a57c681, bd4b442, 717abb8 | | **F2 Drags** | Slider, ResizeHandle, SeekBar, ColorWheel/ColorSquare (one shared pick helper), FloatingPanel, DockPanel and its corner grip, text selection, MenuBar window drag, ReorderableList and Kanban all use router drags. Every drag starts at the theme slop (4 px mouse, 8 px touch) and Escape cancels it. Router: drag phases go only to the widget that accepted the drag; a drag whose widget is hidden is cancelled; a capture drops an unstarted ancestor drag; `Ui::claimDrag` hands a long press over as a drag. | 375d039, f8cb63b, 0f67bfa, f1a9b3a, 8d491ed | | **F3 Reorder** | `ReorderableList` and Kanban use one `ReorderSession` slot rule (a neighbour makes room when the lifted edge passes its middle; tests in core), one elevation shadow, `Viewport::autoScrollAt` and a landing glide. The list gained auto-scroll and the glide it never had. `ListLayout`'s slot logic, `CardLayout` and `StripLayout` are gone. | f1a9b3a, 8d491ed, dbc628a | | **F4 Lists** | New `ListNavigator` (core), `ListRow` and `ListView` (model, reused rows, SelectionModel, Up/Down/Home/End/Page/Enter/Space/type-ahead, empty state). On top of them: search results, menus and dropdowns (they had **no** keyboard handling; now Up/Down/Home/End/type-ahead, Enter, Right/Left for submenus, and focus is returned when the menu closes), settings rows, the file browser, the LibraryViewer track list and the theme editor's colour list. | 55e4010, c3cf458, e6ae34b, 284e343, db2b70b, f0d9b6c, 8aed818 | | **F5 Text editing** | Not in scope; the review rated it a separate refactor. | — | | **F6 Node views** | NodeEditor and TreeView share link geometry, nearest-port search, marquee rect and look (`graph::`, with tests), and `SelectionModel`. **Not done:** a common `GraphCanvas` base on the `Viewport` camera (see the scope notes below). | cc88ae1 | | **F7 Titled panels** | New `TitledPanel` (header with leading and trailing slots, a body that can scroll, a footer, a corner slot, one content API). Dialog, DockPanel, FloatingPanel and StackPage are titled panels now. `DockPanel::PanelLayout`, `Collapsible::SectionLayout`, `HeaderLayout` and `CollapsibleRevealLayout` are replaced by stock layouts plus `BoxLayout::setReversed`. | 02a86a7 | | **F8 Segmented choices** | `SegmentedControl<Value>` on SelectionModel, with arrow keys. RadioGroup keeps its choice in the same model. The TabView tab strip and the MarkdownEditor mode buttons use it. | f100ede, 2461839 | | **F9 Property editors** | `PropertyList`: rows bound once to a getter and setter (value, slider, checkbox, choice, text, custom). `refresh()` pulls values without writing them back. Used by the LibraryViewer track info (it was one `\n`-joined string) and the theme editor's metrics. | 8aed818 | | **F10 Glyphs** | Core `Glyph` set plus `paintGlyph`. The set gains Volume, Muted and ResizeCorner, plus a turn for the opening chevron. Transport buttons, the check mark, the list grip, the dropdown chevron, number-field steps, the add-colour cell, the menu icon, the dock grip and the collapsible chevron all use it. No inline glyph drawing is left in the components. | b0dfeb0, bd3cad6, 375d039, b727e0d | | **F11 Visuals** | `InteractionVisual` and one elevation shadow. Buttons, toggles, window buttons, the scroll bar, ResizeHandle, the dock grip, Kanban cards, colour swatches and the seek handle fade the same way. Four private `withAlpha` copies became `Palette::faded`. | b0dfeb0, bd3cad6, fcd5e78 | | **F12 Commands** | `MenuEntry::command(commands, id)` takes the label, shortcut, enabled and checked state from `Commands<Id>`. NodeEditor's command switch is now a `Commands` registry with availability. Found by the new test: a menu behind a plain popup button never refreshed its entries' state; every menu now refreshes when it opens. | a3e49fe | | **Smell 18: owned children** | MarkdownView, Breadcrumb, colour swatches, dialog buttons, settings sections and rows, menu items, separators and titles are owned by their parent (`emplaceChild`, new `emplaceChildAt`). | 22722f7, 2b277e6, 284e343, a784d34 | | **Custom layouts** | `GridLayout` gains per-column widths (MarkdownView tables, PropertyList); `RowLayout` gains overflow (MenuBar). Of 17 custom layouts, 11 remain; seven of them are thin subclasses of stock layouts or are layouts the review said to keep (see below). | 0f85def, 22722f7, 0f67bfa, cee845c | | **Gallery** | New **Forms** page (ListView, PropertyList, SegmentedControl, a Commands menu) and **Panels** page (TitledPanel, scrolling body, Dialog), each with tests. | 242a6c7 | ### Applications updated in this branch - **LibraryViewer:** the track list is a ListView (keyboard, the selection survives filtering, empty state), and the track info is a PropertyList. - **3DEditor and Sketch3D:** ported onto the refactored Widgets (they did not build on master). - **Theme editor example:** colour list and metric sliders, as above. ### Verification - Full tree build (Debug, tests and examples on): **0 failing targets.** Master has 3 (3DEditor Entry, Sketch3D Entry, SketchGUI). - Tests passing, master → this branch: Widgets core 134 → **156**, Widgets components 789 → **851**, LibraryViewer 16 → **18**, Sketch3D 2 (it did not build on master). All other suites in the tree pass (Allocators, Callstack, Connection, Containers, DataAnalysis, GraphicApplication 108, Math, Objects, RasterRender, RayTracer, Strings, TextEditor, Threading). - No test file lost a test. Tests that asserted the old behaviour were changed, and each change is named in its commit: - the carousel wheel test asserted the F1 defect (a horizontal scroller passing horizontal scrolling on); - the slider test checked pointer capture on press; - the StackPage test checked that the title is detached (it is now hidden); - the Kanban slot-outline test now compares against the lifted column's width. ### Scope notes and decisions to check - **Not verified visually.** There is no display on the build machine and the gallery has no screenshot path. The look was checked through recorded draw commands in tests, not by eye. Please run the gallery and look at Lists, Reorder, Kanban, Forms, Panels and the menus. - **F6 GraphCanvas base not done.** Geometry and selection are shared. The two cameras are not: the tree's magnetic zoom steps and full view and the editor's free zoom behave differently on purpose, and moving them onto one Viewport would change behaviour the current tests do not cover. Kept as follow-up work. - **TabView `CarouselLayout` kept.** Moving it onto the Viewport needs a paged inertia mode built from MagneticStep. Its own wheel heuristic was fixed (scroll source); the layout itself was not replaced. - **Remaining custom layouts.** These stay: - SplitLayout, DockLayout and TreeViewLayout, which the review said to keep; - KanbanBoard::BoardLayout, which places the lifted card and full view (application-specific geometry); - ReorderableList::ListLayout, which places the lifted item; - Collapsible::RevealLayout, which is the animated height window; - thin subclasses of stock layouts: MenuBarLayout (centre widget), TitledLayout (corner slot), LandingLayout (starts the landing glide), AreaLayout (re-reveals the caret). - **Explicit ownership kept** where the lifetime differs from "child of one parent": - SegmentedControl buttons (made by a factory the caller supplies); - Kanban column views (lifted out while dragged); - PageStack pages (alive while they slide out). - **API changes** (all callers on master updated): - `ScrollView` gains `revealRange` and `revealOffsetFor`; - `SearchField::rows()` → `results()` / `row(i)`; - `FileBrowser::rows()` → `listed()`; `FileRow` and `scroll` → `list`; - `LibraryWidget::visibleTracks()` returns tracks, and `tracks()` is the list; - `Dialog::buttons()`, `Menu::items()`, `MenuBar::titles()` and `SettingsPage::rows()` return plain pointers; - `TransportButton::Glyph` → core `Glyph`; - `StackPage::setContent` takes a pointer like every titled panel. - **Not on master and not touched:** Sketch2D (PR #17) and Taskboard (PR #18), which have their own copies (F12's CommandRegistry and BoardCommand, their lists). They can adopt these components after this branch merges. Objects' `osg` uses ImGui and is not affected. `DocumentFolderPanel` has no user on master, so it was not added.
agent added 8 commits 2026-11-01 10:54:58 +03:00
Both were ImGui or half-ported on master and did not build there. 3DEditor
gets an EditorWidget (orbit/pan/zoom and click-select through router drags on
a Viewport) with a docked controls panel; Sketch3D gets a SketchGUI with a
router-drag viewport (Shift move, Alt rotate, Control zoom, pressure draws)
and a dock area with color wheel, sliders and menus.
- core glyph set (plus Volume/Muted/ResizeCorner and a turn) with paintGlyph,
  InteractionVisual (hover/press/reveal) and the elevation shadow, adopted by
  buttons, toggles, panels and window controls;
- GridLayout per-column widths; MarkdownView tables on it and its blocks owned;
- RowLayout overflow; MenuBar fits its titles through it and moves its window
  through a router drag;
- SegmentedControl and RadioGroup on SelectionModel; the tab strip and the
  MarkdownEditor modes are segmented controls.
The core Viewport gains per-edge stretch, animated glide, revealOffset, overlay
hooks and the averaged release velocity. ScrollView drops its own offset
animation, 8px drag state machine, fling, two stretches and layout. Horizontal
wheel, trackpad and Shift+wheel scroll sideways in every scroller (Kanban loses
its workaround); reveal uses the theme margin in TextArea and TabView; the
carousel tells touchpad from wheel by the scroll source. The router delivers a
drag only to the widget that accepted it, cancels a drag whose widget is
hidden, and drops an unstarted ancestor drag on capture.
Slider, ResizeHandle, SeekBar, the color pickers, FloatingPanel, DockPanel and
its grip, text selection, ReorderableList and Kanban all use router drags (one
slop, Escape cancels; Ui::claimDrag hands a long press over). ReorderableList
and Kanban share ReorderSession (one slot rule and auto-scroll through the
Viewport, one elevation, a landing glide) and stock layouts. Cards, swatches
and the seek handle fade through InteractionVisual; breadcrumbs, swatches and
dialog buttons are owned children; four withAlpha copies become Palette::faded.
Lists share navigation (arrows, Home/End, Page, type-ahead), one row with
leading/title/subtitle/trailing slots, selection and an empty state. Search
results, the file browser, settings rows, the LibraryViewer track list and the
theme color list use them. Menus and dropdowns gain full keyboard navigation
(they had none): Up/Down/Home/End/type-ahead, Enter, Right/Left for submenus,
focus restored on close. MenuEntry::command reads a Commands registry, which
NodeEditor now runs through; menus refresh whenever they open.
One header (leading/trailing slots) + scrollable body + footer + corner slot,
with one content API. PropertyList (value/slider/checkbox/choice/text rows
bound to getter/setter) drives the LibraryViewer track info and theme metrics.
Dialog, DockPanel, FloatingPanel and StackPage are titled panels; the private
panel layouts are gone.
The node editor and tree view now share link geometry (one S-curve with a
LinkShape), nearest-port search, the marquee rect and look, and keep their
selection in a SelectionModel; the tree also already had box selection. The
last hand-drawn glyphs use core glyphs: number-field steps, the add-colour
cell, the menu icon, the dock grip and the collapsible chevron (which turns).
Menus own their items, separators and titles; TextArea is a column. The
gallery gains Forms and Panels pages for the new shared components.
Widgets: drop two debug prints from the menu icon test
Some checks are pending
Windows / build (pull_request) Waiting to run
CMake on a single platform / build (pull_request) Waiting to run
CMake / build (pull_request) Waiting to run
a85b8db8c3
ilusha added 2 commits 2026-11-01 11:22:18 +03:00
A one-line prompt (rename a layer, save a sketch, name a skill) is a dialog
with a text field, a confirm button and a cancel. PromptDialog focuses and
selects the field when it opens; Enter submits (confirm), Escape or Cancel
cancel without running the callback.

The field needed a way to tell "Enter submitted it" from "it committed because
it lost focus": a click on Cancel moves focus and would otherwise also confirm.
TextEditor::setOnSubmitted fires only when Enter submits a single-line editor,
while setOnCommitted keeps firing on both Enter and focus loss.
Widgets: DocumentFolderPanel — a folder of markdown documents
Some checks are pending
Windows / build (pull_request) Waiting to run
CMake on a single platform / build (pull_request) Waiting to run
CMake / build (pull_request) Waiting to run
12ddb9532d
The shared half of a skills library and a task workspace: a FileBrowser over
a root directory of markdown files, an editor page that opens a file on
double-click, loads it and writes edits back to disk, a status line for
read/save failures, and a back-to-library navigation. The library title, an
optional hint above the browser, the editor mode and the document title are
configurable; onDocumentOpened reports each open. Tests cover open/load,
write-back through editing, and the unreadable-file status.
Some checks are pending
Windows / build (pull_request) Waiting to run
CMake on a single platform / build (pull_request) Waiting to run
CMake / build (pull_request) Waiting to run
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin WidgetsRefactor:WidgetsRefactor
git checkout WidgetsRefactor

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git checkout master
git merge --no-ff WidgetsRefactor
git checkout WidgetsRefactor
git rebase master
git checkout master
git merge --ff-only WidgetsRefactor
git checkout WidgetsRefactor
git rebase master
git checkout master
git merge --no-ff WidgetsRefactor
git checkout master
git merge --squash WidgetsRefactor
git checkout master
git merge --ff-only WidgetsRefactor
git checkout master
git merge WidgetsRefactor
git push origin master
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: ilusha/Modules#19
No description provided.