Commit 7a370d6
spec(047): close V1-protocol descriptor regressions (default stays V1 OFF) (#443)
* spec(047): Engine A1 — post-children mount hook for V1 handlers
Add an optional AfterChildrenMount hook to the V1 handler surface, invoked
by V1HandlerAdapter.Mount after the children strategy (including inline
items-binder strategies) has mounted every child. DescriptorHandler forwards
to a new ControlDescriptor.AfterChildrenMount delegate.
Lets handlers subscribe events that must wire strictly after children-add
(e.g. TabView.SelectionChanged, which WinUI fires spuriously during the
first tab add when subscribed during the prop-apply phase). Additive and
inert until a descriptor populates the callback (Batch T).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* TabItemsHost: in-place reconcile instead of full rebuild on update
The V1 TabItemsHost children strategy (backing PivotDescriptor, which is
registered/live under V1, and the carved TabViewDescriptor) rebuilt every
container on each Update: it unmounted+remounted all TabViewItem/PivotItem
containers and their content subtrees. That re-triggers the tab-strip
entrance animation and steals focus from descendant controls on any render
that touches the host element.
Switch the Update path to an index-keyed in-place positional reconcile,
mirroring the legacy UpdateTabView/UpdatePivot arms:
- keep existing containers for shared indices; reconcile each container's
Content via ReconcileV1Child and only reassign when the realized control
reference actually changes (avoids WinUI detach/reattach dropping queued
setState);
- refresh container metadata (header/icon/closable) through a new optional
UpdateContainer callback supplied per descriptor;
- unmount+remove excess tail; mount+append surplus new items;
- fall back to full rebuild only on engine-invariant break / count drift.
Extend the Desc_Pivot and Desc_TabView selftests with same-shape update
assertions proving container AND content-subtree instance identity is
preserved (would fail on the old rebuild path).
Spec 047 §14 Phase 3 prelude.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Spec 047 §14: route NavigationHost through V1 dispatch
Close the NavigationHost dispatch carve (Path B delegate handler). Expose
MountNavigationHost/UpdateNavigationHost as internal and add
NavigationHostHandler delegating to them; register it in
RegisterV1BuiltInHandlers.
Unmount stays on the flag-independent UnmountRecursive intercept (fires
before the V1 unmount arm), so teardown is byte-identical V1 ON =/= V1 OFF.
Validated: core build green; Navigation_NavHost* selftests (16 asserts)
pass under both V1 ON and V1 OFF.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Spec 047 §14: route GridView through V1 dispatch
Close the GridView dispatch carve with a hand-coded GridViewHandler (Path B
delegate) mirroring ListViewHandler. Expose MountGridView/UpdateGridView as
internal and delegate to them; register the handler in
RegisterV1BuiltInHandlers. The GridViewDescriptor stays unregistered (its
ItemsHost<> strategy pre-mounts every item with no virtualization, which
would regress the recycle contract).
Validated: core build green; all GridView selftests (RareControl, KLR,
SelectionEvt, TemplatedListHL, CoreCov, Desc) pass under both V1 ON and
V1 OFF (0 failures).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Close V1 overlay dispatch carves (spec 047 §14 prelude)
Route the seven overlay elements (ContentDialog, Flyout, MenuBar,
CommandBar, MenuFlyout, Popup, CommandBarFlyout) through V1 handler
dispatch via decorator-style Path B handlers that delegate to the
existing internal MountXxx/UpdateXxx engine bodies and return
ContinueDefaultTraversal on unmount.
Because ContinueDefaultTraversal makes the engine fall through to the
same UnmountRecursive type-based recursion that runs when the V1 flag
is OFF, mount/update/unmount are byte-identical V1 ON ≡ V1 OFF.
Made the 14 overlay Mount*/Update* methods internal, added
OverlayDecoratorHandlers.cs, registered all 7 in
RegisterV1BuiltInHandlers, and updated the carve xmldoc.
Validated A|B: overlay selftests (ContentDialog/Flyout/CommandBar/
MenuBar/Popup families) pass with identical counts and 0 failures
under both V1 ON and V1 OFF.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Close V1 TabView dispatch carve via Path B delegate (spec 047 §14 prelude)
Route TabViewElement through V1 handler dispatch via TabViewHandler, a
Path B delegate that calls the COMPLETE legacy MountTabView/UpdateTabView
bodies (drag pipeline, pinnable headers, strip header/footer, in-place
tab-content reconcile, conditional SelectedIndex). This is distinct from
the still-unregistered TabViewDescriptor + TabItemsHost port, which leaves
those features on the legacy arm; the delegate runs the full feature set
because it IS the legacy code, so it has none of the descriptor's gaps.
UpdateTabView returns null (pure in-place reconcile), so the void-Update
IElementHandler shape preserves identity. Made MountTabView/UpdateTabView
internal. Unmount is byte-identical V1 ON =/= V1 OFF: a WinUI.TabView is an
ItemsControl that both unmount paths pool without child recursion (no
Panel/Border/ContentControl branch matches), and CollectSelf reproduces the
V1-OFF fall-through. Mount/update are unchanged from the previously-carved
V1-ON path; registering the handler only makes the parity-safe unmount arm
fire.
Validated A|B: TabView (5) + Pivot (3) selftests pass identically under V1
ON and V1 OFF.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Close V1 Button ContentElement gap via Path B delegate (spec 047 §14 prelude)
ButtonDescriptor only expressed the string-Label fast path and dropped
ButtonElement.ContentElement, so element-content buttons (e.g. PropertyGrid
expand buttons) rendered blank under V1 ON. Replace the registered descriptor
with a delegate ButtonHandler that runs the complete legacy MountButton/
UpdateButton bodies (enabled/disabled-focusable, Click trampoline, setters AND
ContentElement). Decorator shape returns ContinueDefaultTraversal so unmount
recurses into ContentControl content in both paths, matching V1 OFF cleanup of
an element child. ButtonDescriptor retained for its isolated selftests + perf
bench. A|B validated: PropertyGrid/Button/Categor selftests 0 failures under
V1 ON and V1 OFF.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix V1 panel keyed-reconcile gap via Path B delegate handlers
The V1 Panel<> children strategy (V1HandlerAdapter) reconciles children by
index with no keyed reconcile, losing WinUI control identity on keyed
reorder/reverse/swap/remove-middle. Legacy Update{Flex,Stack,Grid,Canvas,
RelativePanel,WrapGrid} run ReconcileChildren -> ChildReconciler (spec-042
keyed LIS) and re-apply attached props, so V1 ON diverged from V1 OFF on
~17 identity-preservation selftests.
Route all 6 panels through IDecoratorElementHandler delegates that invoke the
complete legacy Mount*/Update* bodies (identical to V1 OFF). ContinueDefaultTraversal
on unmount so teardown falls through to the same WinUI.Panel child-recursion
V1 OFF uses. Update returns the legacy result when non-null (RelativePanel may
substitute the control on count change) else keeps the existing control.
Descriptor types retained for isolated selftests + perf bench.
Made the 6 Mount*/Update* methods internal for delegate access.
A|B validated (V1 ON == V1 OFF, 0 failures): FlexColumn, Keyed*, MultiCycle,
Reconciler_KeyedList, Stack, Grid, Canvas, RelativePanel, WrapGrid. Also fixes
as side effects: AAF_MoveSpring + AAF_FlexColumnMove (animation) and
ConditionalRendering_Toggle_HiddenAgain.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix V1 Lazy*Stack ScrollViewer-wrapper gap via derived-type delegate
The V1 LazyStackDescriptor port used ItemsRepeater directly as TControl (a
descriptor's RentControl returns a single control, with no place for a
wrapping ScrollViewer). Legacy MountLazyStack wraps the ItemsRepeater in a
ScrollViewer with orientation-appropriate scrollbars + ScrollViewerSetters.
Under V1 ON the ScrollViewer was therefore absent, failing every
FindControl<ScrollViewer> fixture (LazyVStack/HStack_ScrollViewer,
LazyHStack_HScrollEnabled, ScrollPop/Back, HookPaging_Scroll*) and the
component-cleanup-on-unmount gate (EFR_LazyStackUnmount) — no ScrollViewer
for the engine to recurse through on teardown.
Add RegisterDecoratorHandlerForDerivedTypes<TBase> (the derived-type analogue
of RegisterDecoratorHandler) and route the Lazy*Stack family through a Path B
LazyStackHandler decorator that runs the complete legacy MountLazyStack/
UpdateLazyStack bodies. ContinueDefaultTraversal on unmount so the engine
recurses ScrollViewer -> ItemsRepeater -> realized rows (running each row
component's UseEffect cleanup), identical to V1 OFF. Descriptor retained for
isolated selftests.
Made MountLazyStack/UpdateLazyStack internal for delegate access.
A|B validated (V1 ON == V1 OFF, 0 failures): Lazy, Scroll, HookPaging,
DataGridScroll. EFR_LazyStackUnmount_AtLeastOneCleanup now after=5 (was 0).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix 3 V1-ON regressions via Path B delegate handlers
Convert CheckBox, Expander, and templated list (ListView/GridView/FlipView)
V1 descriptors to decorator handlers that delegate Mount/Update to the legacy
engine bodies, restoring byte-identical parity with V1 OFF.
- CheckBoxHandler: legacy UpdateCheckBox only suppresses echo when the target
differs from the current value, fixing ConditionalRendering toggle swallow.
- ExpanderHandler: restores ContentTransitions + late callback subscription.
- TemplatedListHandler: registered on common base TemplatedListElementBase so
typed ListView<T>/GridView<T>/FlipView<T> route through legacy
MountTemplatedList + control-typed Update*, restoring ApplyMoveAnimations
(fixes AAF_MoveSpring/FlexColumnMove implicit-offset attach).
Made the corresponding legacy Mount*/Update* methods internal. A|B validated
under real V1 ON vs OFF: 0 failures, equal check counts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>1 parent 4fa02f7 commit 7a370d6
22 files changed
Lines changed: 980 additions & 155 deletions
File tree
- src/Reactor/Core
- V1Protocol
- Descriptor
- Descriptors
- Handlers
- tests/Reactor.AppTests.Host/SelfTest/Fixtures
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
301 | 301 | | |
302 | 302 | | |
303 | 303 | | |
304 | | - | |
| 304 | + | |
305 | 305 | | |
306 | 306 | | |
307 | 307 | | |
| |||
675 | 675 | | |
676 | 676 | | |
677 | 677 | | |
678 | | - | |
| 678 | + | |
679 | 679 | | |
680 | 680 | | |
681 | 681 | | |
| |||
1116 | 1116 | | |
1117 | 1117 | | |
1118 | 1118 | | |
1119 | | - | |
| 1119 | + | |
1120 | 1120 | | |
1121 | 1121 | | |
1122 | 1122 | | |
| |||
1140 | 1140 | | |
1141 | 1141 | | |
1142 | 1142 | | |
1143 | | - | |
| 1143 | + | |
1144 | 1144 | | |
1145 | 1145 | | |
1146 | 1146 | | |
| |||
1160 | 1160 | | |
1161 | 1161 | | |
1162 | 1162 | | |
1163 | | - | |
| 1163 | + | |
1164 | 1164 | | |
1165 | 1165 | | |
1166 | 1166 | | |
| |||
1268 | 1268 | | |
1269 | 1269 | | |
1270 | 1270 | | |
1271 | | - | |
| 1271 | + | |
1272 | 1272 | | |
1273 | 1273 | | |
1274 | 1274 | | |
| |||
1325 | 1325 | | |
1326 | 1326 | | |
1327 | 1327 | | |
1328 | | - | |
| 1328 | + | |
1329 | 1329 | | |
1330 | 1330 | | |
1331 | 1331 | | |
| |||
1345 | 1345 | | |
1346 | 1346 | | |
1347 | 1347 | | |
1348 | | - | |
| 1348 | + | |
1349 | 1349 | | |
1350 | 1350 | | |
1351 | 1351 | | |
| |||
1369 | 1369 | | |
1370 | 1370 | | |
1371 | 1371 | | |
1372 | | - | |
| 1372 | + | |
1373 | 1373 | | |
1374 | 1374 | | |
1375 | 1375 | | |
| |||
1507 | 1507 | | |
1508 | 1508 | | |
1509 | 1509 | | |
1510 | | - | |
| 1510 | + | |
1511 | 1511 | | |
1512 | 1512 | | |
1513 | 1513 | | |
| |||
1803 | 1803 | | |
1804 | 1804 | | |
1805 | 1805 | | |
1806 | | - | |
| 1806 | + | |
1807 | 1807 | | |
1808 | 1808 | | |
1809 | 1809 | | |
| |||
1986 | 1986 | | |
1987 | 1987 | | |
1988 | 1988 | | |
1989 | | - | |
| 1989 | + | |
1990 | 1990 | | |
1991 | 1991 | | |
1992 | 1992 | | |
| |||
2295 | 2295 | | |
2296 | 2296 | | |
2297 | 2297 | | |
2298 | | - | |
| 2298 | + | |
2299 | 2299 | | |
2300 | 2300 | | |
2301 | 2301 | | |
| |||
2350 | 2350 | | |
2351 | 2351 | | |
2352 | 2352 | | |
2353 | | - | |
| 2353 | + | |
2354 | 2354 | | |
2355 | 2355 | | |
2356 | 2356 | | |
| |||
2411 | 2411 | | |
2412 | 2412 | | |
2413 | 2413 | | |
2414 | | - | |
| 2414 | + | |
2415 | 2415 | | |
2416 | 2416 | | |
2417 | 2417 | | |
| |||
2488 | 2488 | | |
2489 | 2489 | | |
2490 | 2490 | | |
2491 | | - | |
| 2491 | + | |
2492 | 2492 | | |
2493 | 2493 | | |
2494 | 2494 | | |
| |||
2543 | 2543 | | |
2544 | 2544 | | |
2545 | 2545 | | |
2546 | | - | |
| 2546 | + | |
2547 | 2547 | | |
2548 | 2548 | | |
2549 | 2549 | | |
| |||
3177 | 3177 | | |
3178 | 3178 | | |
3179 | 3179 | | |
3180 | | - | |
| 3180 | + | |
3181 | 3181 | | |
3182 | 3182 | | |
3183 | 3183 | | |
| |||
3462 | 3462 | | |
3463 | 3463 | | |
3464 | 3464 | | |
3465 | | - | |
| 3465 | + | |
3466 | 3466 | | |
3467 | 3467 | | |
3468 | 3468 | | |
| |||
3680 | 3680 | | |
3681 | 3681 | | |
3682 | 3682 | | |
3683 | | - | |
| 3683 | + | |
3684 | 3684 | | |
3685 | 3685 | | |
3686 | 3686 | | |
| |||
3719 | 3719 | | |
3720 | 3720 | | |
3721 | 3721 | | |
3722 | | - | |
| 3722 | + | |
3723 | 3723 | | |
3724 | 3724 | | |
3725 | 3725 | | |
| |||
0 commit comments