Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9f0244a
Spec 047 §14 Phase 3 completion (1/N): PreMountedItems + TemplatedFli…
codemonkeychris May 28, 2026
bc7c722
spec047: add Semantic V1 descriptor
codemonkeychris May 28, 2026
3a2fba6
spec047: add GridView descriptor
codemonkeychris May 28, 2026
b2fb807
spec047: add ItemContainer descriptor
codemonkeychris May 28, 2026
6ddd0dd
spec047: add AnnounceRegion V1 descriptor
codemonkeychris May 28, 2026
14c03f0
spec047: add ItemsView descriptor
codemonkeychris May 28, 2026
78ce4a2
spec047: defer polymorphic and XAML interop descriptors
codemonkeychris May 28, 2026
d14b1dc
spec047: add AnimatedVisualPlayer V1 descriptor
codemonkeychris May 28, 2026
912f646
spec047: add AnnotatedScrollBar V1 descriptor
codemonkeychris May 28, 2026
fafcbdb
spec047: add MapControl V1 descriptor
codemonkeychris May 28, 2026
0d63958
spec047: add ParallaxView V1 descriptor
codemonkeychris May 28, 2026
fab387c
spec047: add RefreshContainer V1 descriptor
codemonkeychris May 28, 2026
74c9c37
spec047: add SwipeControl V1 descriptor
codemonkeychris May 28, 2026
d3e0413
spec047: add SemanticZoom V1 descriptor
codemonkeychris May 28, 2026
141fe82
spec047: add MediaPlayerElement V1 descriptor
codemonkeychris May 28, 2026
f3a28cb
spec047: add WebView2 V1 descriptor
codemonkeychris May 28, 2026
fd9c1d9
spec047: add TitleBar V1 descriptor
codemonkeychris May 28, 2026
526f26d
spec047: add NavigationView V1 descriptor
codemonkeychris May 28, 2026
cc9e042
spec047: fix DescSemanticZoom fixture's nested ListView registration
codemonkeychris May 28, 2026
9109395
spec047: §14 Phase 3 completion — IDecoratorElementHandler engine ext…
codemonkeychris May 28, 2026
ef7b8a3
spec047: add Icon V1 descriptor
codemonkeychris May 28, 2026
7eac37b
spec047: add XamlHost V1 descriptor
codemonkeychris May 28, 2026
a41a7c0
spec047: add XamlPage V1 descriptor
codemonkeychris May 28, 2026
16636c0
spec047: §14 Phase 3 completion — register Phase 3 descriptors in pro…
codemonkeychris May 28, 2026
0a8aac3
spec047: §14 Phase 3 completion — update spec + tracker for Phase 3 c…
codemonkeychris May 28, 2026
2f70c04
spec(047): record PR #440 + quantified V1 dispatch coverage (76/95 = …
codemonkeychris May 28, 2026
48b0515
spec(047): address PR #440 CR feedback (Copilot reviewer)
codemonkeychris May 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion docs/specs/047-extensible-control-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -1433,7 +1433,20 @@ ARM64 stable-AC re-capture on `LAPTOP-4MEP83VI` remains deferred for the §14 ra

**Phase 3 finish carry-forwards:** none remaining for the typed-items host families that were on Phase 3's scope (LazyVStack/HStack, ItemsRepeater<T>, ListView<T>, GridView<T>, TreeView, FlipView (simple), TabView, Pivot). The engine surface is complete. Production swap (Phase 4 cleanup) registers each descriptor in `RegisterV1BuiltInHandlers` and deletes the matching legacy `MountXxx` switch arm.

**Phase 3 deferred / not-attempted** (recorded for the Phase 3.5 / Phase 4 prelude — element types in the legacy `Reconciler.Mount` switch that have neither a Phase 1 V1 handler nor a Phase 3 descriptor; see `tasks/047-extensible-control-model-implementation.md` for the full enumeration). One genuine carve from close-out remains: `TemplatedFlipViewElement<T>` (the typed FlipView peer — needs a `PreMountedItems` ChildrenStrategy since FlipView lacks `ContainerContentChanging`). The rest of the deferred list — `GridViewElement` (plain), `ItemsViewElementBase`, dialog / overlay family (`ContentDialog`, `Flyout`, `Popup`, `MenuBar`, `MenuFlyout`, `CommandBar`, `CommandBarFlyout`), heavy / specialized controls (`WebView2`, `NavigationView`, `TitleBar`, `MediaPlayerElement`, `AnimatedVisualPlayer`, `MapControl`, `SemanticZoom`, `AnnotatedScrollBar`, `RefreshContainer`, `SwipeControl`, `ParallaxView`), interop / a11y (`SemanticElement`, `AnnounceRegion`, `XamlHost`, `XamlPage`, and the already-escape-hatched `IconElement`), and Reactor composition primitives (`Component`, `Func`, `Memo`, `ErrorBoundary`, `CommandHost`, `Validation.*`) — was never on the Phase 3 batch list. The composition primitives likely should NOT route through the V1 handler protocol (they sit above it); the rest are straightforward descriptor ports (or, where they'd surface engine gaps, would follow the close-out / finish pattern of landing the engine extension before the port).
**Phase 3 deferred / not-attempted** (recorded for the Phase 3.5 / Phase 4 prelude — element types in the legacy `Reconciler.Mount` switch that have neither a Phase 1 V1 handler nor a Phase 3 descriptor; see `tasks/047-extensible-control-model-implementation.md` for the full enumeration). **Updated in Phase 3 completion (PR #440, commit 16636c0d):** the engine gap is closed (`TemplatedFlipViewElement<T>` ported via the new `PreMountedItems` ChildrenStrategy + `TemplatedFlipViewDescriptor`), and every previously-deferred descriptor on the Phase 3 batch list is now both authored AND registered in `RegisterV1BuiltInHandlers`: untyped items hosts (`GridView`, `ItemsView`, `ItemContainer`), heavy / specialized controls (`WebView2`, `NavigationView`, `TitleBar`, `MediaPlayerElement`, `AnimatedVisualPlayer`, `MapControl`, `SemanticZoom`, `AnnotatedScrollBar`, `RefreshContainer`, `SwipeControl`, `ParallaxView`), polymorphic / a11y (`IconElement` via the new `IDecoratorElementHandler` engine extension, `SemanticElement`, `AnnounceRegion`). What still ships unregistered (intentional carve list, documented inline in `RegisterV1BuiltInHandlers`): the dialog / overlay family (`ContentDialog`, `Flyout`, `Popup`, `MenuBar`, `MenuFlyout`, `CommandBar`, `CommandBarFlyout`) — modal lifecycle needs decorator-style ports beyond the IDecoratorElementHandler shape; the stateful `NavigationHostElement` — `Reconciler.UnmountRecursive` intercepts before the V1 arm and needs a small refactor; `TabViewDescriptor` — bisect ratifies the documented gaps (spec 045 §2.4 drag pipeline, §2.2 pinnable headers, in-place CanUpdate, conditional `SelectedIndex` write, `TabStripHeader` / `TabStripFooter` slots) need engine work (post-children mount-hook + `ImperativeBridged` for named slots); the XAML interop bridges (`XamlHost`, `XamlPage`) — descriptors exist but `XamlInterop.Register` populates `_typeRegistry` at startup so V1 auto-registration would clash; and the Reactor composition primitives (`Component`, `Func`, `Memo`, `ErrorBoundary`, `CommandHost`, `Validation.*`) — these sit ABOVE the V1 handler protocol and Phase 4 cleanup keeps their legacy arms. The A|B parity bar is met for every registered element: 9134 xunit + 4410 selftest (V1 ON ≡ V1 OFF), 0 failures both flags.

**Quantified V1 dispatch coverage (post-PR #440):**

| Bucket | Arms | Notes |
|---|---:|---|
| Routed through V1 (Phase 1 handler or Phase 3 descriptor) | 75 | Production dispatch path when V1 ON |
| Reachable-but-deferred (overlays 7 + NavigationHost 1 + TabView 1 + GridView 1 + XamlHost/Page 2) | 12 | Follow-up PR closes these |
| Intentionally above V1 (Reactor composition primitives) | 8 | Permanent carve; Phase 4 keeps legacy arms |
| **Total switch arms** | **95** | — |

- **Coverage of V1-reachable surface:** 75 / 87 ≈ **86%** (excludes the 8 composition primitives that are permanently above the protocol).
- **Coverage of all element-type switch arms:** 75 / 95 ≈ **79%**.
- **Path to 100% reachable:** the next PR ports the 12 deferred (overlays, NavigationHost, TabView gap closure, GridView CCC virtualization, XamlHost/Page unification). Phase 4 cleanup follows.

**Phase 3 finish advisory perf** — Cloud PC x64 re-capture under `docs/specs/047/phase3-results/CPC-ander-YTZ3O-x64-advisory/2026-05-28-phase3-finish-3x5/` (n=15, 3 launches × 5 reps). V1 ON (descriptors) vs V1 OFF (today), against prior `2026-05-27-phase3-closeout-3x5/`:

Expand Down
175 changes: 134 additions & 41 deletions docs/specs/tasks/047-extensible-control-model-implementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -928,14 +928,11 @@ carve-outs:
- [x] **G3 typed lists — `TreeView`, `FlipView`, `TabView`, `Pivot`** —
closed by Phase 3 finish. **Note:** "FlipView" here is the
simple non-templated `FlipViewElement` (Element[] items). The
typed `TemplatedFlipViewElement<T>` peer stays carved exactly
as documented at Phase 3 close-out — FlipView lacks
`ContainerContentChanging` so the realization pipeline used by
`TemplatedListView<T>` / `TemplatedGridView<T>` doesn't apply;
a `PreMountedItems` strategy would land it but no fixture
currently demands it. The intermediate marker base
`TemplatedFlipViewElementBase` is reserved in the element
hierarchy for that future port.
typed `TemplatedFlipViewElement<T>` peer was ported in Phase 3
completion via the new `PreMountedItems<>` strategy + base-derived
`TemplatedFlipViewDescriptor` registered on
`TemplatedFlipViewElementBase` — see the Phase 3 completion entry
below.
- **TreeView** via new `TreeChildren<TElement, TControl>`
strategy (hierarchical, positional rebuild on Update,
recursive `ContentElement` mount).
Expand All @@ -959,43 +956,139 @@ Phase 3 descriptor — out of scope for the Phase 3 batch list, recorded
here for a future Phase 3.5 / Phase 4 prelude). Cross-referenced from
the audit at the end of `spec/047-phase3-finish`:

- **Genuine engine gap:** `TemplatedFlipViewElement<T>` — close-out
carve. Needs a `PreMountedItems` ChildrenStrategy. The intermediate
base `TemplatedFlipViewElementBase` already exists for the
base-derived registration.
- **Untyped items hosts not ported:** `GridViewElement` (the plain
Element[] variant — `ListViewElement` got a Phase 1 V1 handler,
GridView did not), `ItemsViewElementBase` (the higher-level
`ItemsView` wrapping its own ItemsRepeater), `ItemContainerElement`.
- **Genuine engine gap (CLOSED — Phase 3 completion):**
`TemplatedFlipViewElement<T>` — ported via the new
`PreMountedItems<TElement, TControl>` ChildrenStrategy and
`TemplatedFlipViewDescriptor`, registered base-derived against
`TemplatedFlipViewElementBase`. The strategy pre-mounts every item
up-front into `FlipView.Items` (no `ContainerContentChanging` to
drive realization) and positionally reconciles via
`Reconciler.ReconcileV1Child` on Update.
- **Untyped items hosts (CLOSED — Phase 3 completion, partial):**
`ItemsViewElementBase`, `ItemContainerElement` — ported as
standard descriptors and registered in `RegisterV1BuiltInHandlers`.
`GridViewElement` (plain Element[]) — descriptor authored
(`GridViewDescriptor`) but **carved during PR #440 CR**: the
descriptor's `ItemsHost<>` strategy pre-mounts every item into
`GridView.Items` (one container per item, no virtualization),
while the legacy `MountGridView` arm uses
`ItemsSource = Range(0..N) + ItemTemplate + ContainerContentChanging`
to realize containers lazily (matches Phase 1 `ListViewHandler`).
Closing this needs either a hand-coded `GridViewHandler` or a new
ChildrenStrategy variant wrapping CCC. Tracked alongside TabView /
overlays / NavigationHost gap-closure.
- **Heavy / specialized controls (CLOSED — Phase 3 completion):**
`WebView2Element`, `NavigationViewElement`, `TitleBarElement`,
`MediaPlayerElementElement`, `AnimatedVisualPlayerElement`,
`MapControlElement`, `SemanticZoomElement`,
`AnnotatedScrollBarElement`, `RefreshContainerElement`,
`SwipeControlElement`, `ParallaxViewElement` — all descriptors
authored and registered. (`NavigationHostElement` stays deferred —
see below.)
- **Polymorphic / a11y (CLOSED — Phase 3 completion):**
`IconElement` (decorator-style handler via the
`IDecoratorElementHandler` engine extension landed this phase),
`SemanticElement`, `AnnounceRegionElement` — all registered.

**Phase 3 completion — still deferred to the next PR (not regressions;
scoped carve list documented inline in `RegisterV1BuiltInHandlers`):**

- **Dialog / overlay family:** `ContentDialogElement`,
`FlyoutElement`, `PopupElement`, `MenuBarElement`,
`MenuFlyoutElement`, `CommandBarElement`,
`CommandBarFlyoutElement`. Button-family `Flyout` ships through the
`.OneWayBridged` setter on the button descriptors; the standalone
flyout elements are their own legacy paths.
- **Heavy / specialized controls:** `WebView2Element`,
`NavigationViewElement`, `NavigationHostElement`,
`TitleBarElement`, `MediaPlayerElementElement`,
`AnimatedVisualPlayerElement`, `MapControlElement`,
`SemanticZoomElement`, `AnnotatedScrollBarElement`,
`RefreshContainerElement`, `SwipeControlElement`,
`ParallaxViewElement`.
- **Polymorphic / interop / a11y:** `IconElement` (already documented
as escape-hatched — polymorphic mount), `SemanticElement`,
`AnnounceRegionElement`, `XamlHostElement`, `XamlPageElement`.
- **Reactor infrastructure (likely SHOULD stay out of V1 dispatch):**
`ComponentElement`, `FuncElement`, `MemoElement`,
`ErrorBoundaryElement`, `CommandHostElement`, `ModifiedElement`,
`CommandBarFlyoutElement`. Modal lifecycle (control-side-mounted,
not parent-tree-mounted) requires decorator-style ports beyond
the IDecoratorElementHandler shape used for `IconElement`.
- **Stateful host:** `NavigationHostElement`. Per-instance
route/cache/transition state is intercepted in
`Reconciler.UnmountRecursive` BEFORE the V1 dispatch arm; needs
a small refactor to internal-expose `MountNavigationHost` /
`UpdateNavigationHost` and duplicate cleanup logic in the V1
handler before it can route through V1.
- **`TabViewDescriptor` (descriptor exists, registration carved):**
Bisect (3× clean V1 ON full selftest with only TabViewDescriptor
carved, vs. 1–4 random docking-text-find failures per run when
registered: DockHooks / PixDoc / RoleAware / Composition /
FloatRoot) ratifies the descriptor's documented gaps as hot in
the docking suite — missing spec 045 §2.4 drag pipeline
(`OnTabDragStarting` / `OnTabDragCompleted`), §2.2 pinnable
headers (`BuildTabHeader` / `BuildPinButton` / in-place
`TryUpdatePinHeaderInPlace`), in-place CanUpdate for tab content
(preserves focus/state on re-renders), conditional `SelectedIndex`
write, and `TabStripHeader` / `TabStripFooter` Element slots.
Closing them requires engine work (post-children mount-hook so
`SelectionChanged` subscribes after children-add + an
`ImperativeBridged` shape for the named tab strip slots).
- **`GridViewDescriptor` (descriptor exists, registration carved
during PR #440 CR):** The descriptor's `ItemsHost<>` ChildrenStrategy
pre-mounts every item into `GridView.Items` (one container per item,
no virtualization). The legacy `MountGridView` arm uses
`ItemsSource = Range(0..N) + ItemTemplate + ContainerContentChanging`
to realize containers lazily — matching Phase 1
`ListViewHandler`. A|B tests pass either way (no fixture stresses
GridView scale), but production memory/lifecycle would silently
regress. Closing this needs either a hand-coded `GridViewHandler`
mirroring `ListViewHandler`'s CCC virtualization, or a new
ChildrenStrategy variant (e.g. `RecyclingItemsHost<>`) that wraps
the `ItemsSource` + `ContainerContentChanging` realization
contract.
- **Interop bridges:** `XamlHostElement`, `XamlPageElement`. V1
descriptors exist (`XamlHostDescriptor`, `XamlPageDescriptor`)
but stay unregistered because `XamlInterop.Register(reconciler)`
populates the external `_typeRegistry` at app startup; auto-
registering V1 would clash via `EnsureRegistrableElementType`.
Unification is Phase 4 follow-up.

**Reactor composition primitives (intentionally above the V1
protocol — Phase 4 cleanup keeps their legacy arms):**

- `ComponentElement`, `FuncElement`, `MemoElement`,
`ErrorBoundaryElement`, `CommandHostElement`,
`Validation.FormFieldElement` /
`ValidationVisualizerElement` / `ValidationRuleElement`. These are
Reactor composition primitives, not WinUI control wrappers — they
sit above the V1 handler protocol rather than being consumers of
it.

The "100% V1 dispatch" goal as scoped by §14's Phase 3 batches IS
met (every batch entry has a V1 handler or descriptor). The list
above is genuine post-Phase-3 scope, not a regression against the
shipped Phase 3 plan.
`ValidationVisualizerElement` / `ValidationRuleElement`. These
orchestrate child reconciliation rather than wrap a single WinUI
control, so the V1 handler protocol does not apply.
(`ModifiedElement` is intentionally NOT in this list — it's
unwrapped to its wrapped element BEFORE dispatch at the top of
`Reconciler.Mount`, so it never reaches the switch and does not
count as a carved arm.)

**Phase 3 completion status (PR #440 — landed-pending-merge):**
Every element type in the production codebase either (a) routes
through V1 dispatch (Phase 1 hand-coded handler OR Phase 3
descriptor registered in `RegisterV1BuiltInHandlers`), (b) is a
Reactor composition primitive intentionally kept above the V1
protocol, or (c) is in the explicit deferred carve list above with
a documented gap-closure path. The A|B parity bar — V1 ON ≡ V1 OFF
across the full xunit + selftest matrix — is met for every
registered element: 9134 xunit + 4410 selftest, 0 failures both
flags. Phase 4 cleanup can delete every legacy `MountXxx` /
`UpdateXxx` method that backs an element that has been registered
through V1; the legacy switch arms for the composition primitives
+ the deferred carve list must remain until their respective
follow-up PRs land.

**Quantified V1 dispatch coverage (post-PR #440):**

| Bucket | Arms | % of total |
|---|---:|---:|
| Routed through V1 (75 = 5 Phase 1 + 6 base-derived + 63 standard descriptors + 1 decorator) | 75 | 79% |
| Reachable-but-deferred (overlays 7, NavigationHost 1, TabView 1, GridView 1, XamlHost/Page 2) | 12 | 12.6% |
| Intentionally above V1 (composition primitives — permanent carve) | 8 | 8.4% |
| **Total `Reconciler.Mount.cs` switch arms** | **95** | **100%** |

- **Coverage of V1-reachable surface (excludes 8 composition primitives):** 75 / 87 ≈ **86%**.
- **Coverage of all switch arms:** 75 / 95 ≈ **79%**.
- **Path to 100% reachable:** the follow-up PR closes the 12 deferred:
1. Port the 7 overlay descriptors (ContentDialog, Flyout, Popup, MenuBar, MenuFlyout, CommandBar, CommandBarFlyout) — needs a decorator strategy variant for modal lifecycle beyond `IDecoratorElementHandler`.
2. Refactor `NavigationHostElement` cleanup path so V1 can own it (internal-expose `MountNavigationHost` / `UpdateNavigationHost`, duplicate cleanup logic in the V1 handler, remove the `UnmountRecursive` intercept).
3. Close `TabViewDescriptor` gaps (engine post-children mount-hook + `ImperativeBridged` for named slots + port `BuildTabHeader` / `BuildPinButton` / `TryUpdatePinHeaderInPlace` + drag pipeline trampolines + conditional `SelectedIndex` write + in-place `CanUpdate`).
4. Close `GridViewDescriptor` lifecycle gap — either author a Phase 1 hand-coded `GridViewHandler` mirroring `ListViewHandler`'s CCC virtualization, or introduce a `RecyclingItemsHost<>` ChildrenStrategy variant.
5. Unify `XamlInterop.Register` with V1 auto-registration so `XamlHostElement` / `XamlPageElement` descriptors can register without `EnsureRegistrableElementType` clash.

Phase 4 cleanup (deletion of legacy switch arms + `UseV1Protocol`
flag) is unblocked for the 75 routed arms today; the remaining 12
arms unblock as the follow-up PR lands each closure.

**Carry-forward known defects** (from Phase 1):

Expand Down
9 changes: 8 additions & 1 deletion src/Reactor/Core/Element.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3831,8 +3831,12 @@ public enum ItemsViewLayoutKind
/// shape: virtual hooks for factory creation, in-place update,
/// per-row reconcile, and event callback dispatch.
/// </summary>
public abstract record ItemsViewElementBase : Element
public abstract record ItemsViewElementBase : Element, global::Microsoft.UI.Reactor.Core.Internal.IKeyedItemSource
{
int global::Microsoft.UI.Reactor.Core.Internal.IItemViewSource.ItemCount => ItemCount;
Element global::Microsoft.UI.Reactor.Core.Internal.IItemViewSource.BuildItemView(int index) => BuildItemViewAt(index);
string global::Microsoft.UI.Reactor.Core.Internal.IKeyedItemSource.GetKeyAt(int index) => GetKeyAt(index);

public ItemsViewLayoutKind LayoutKind { get; init; } = ItemsViewLayoutKind.StackLayout;
public ItemsViewSelectionMode SelectionMode { get; init; } = ItemsViewSelectionMode.Single;
public bool IsItemInvokedEnabled { get; init; }
Expand All @@ -3844,6 +3848,7 @@ public abstract record ItemsViewElementBase : Element
public abstract bool TryUpdateFactory(IElementFactory existingFactory);
public abstract void RefreshRealizedItems(IElementFactory factory, WinUI.ItemsRepeater repeater);
internal abstract void AttachListStateToFactory(IElementFactory factory, Internal.ReactorListState listState);
internal abstract Element BuildItemViewAt(int index);
/// <summary>Dispatch an <c>ItemInvoked</c> event to the typed callback.</summary>
public abstract void InvokeItemInvoked(int index);
/// <summary>Dispatch a <c>SelectionChanged</c> snapshot to the typed callback.</summary>
Expand Down Expand Up @@ -3910,6 +3915,8 @@ internal override void PreflightFirstItem()
_ = GuardedViewBuilder(Items[0], 0);
}

internal override Element BuildItemViewAt(int index) => GuardedViewBuilder(Items[index], index);

public override IElementFactory CreateFactory(Reconciler reconciler, Action requestRerender, ElementPool? pool) =>
new ElementFactory<T>(Items, GuardedViewBuilder, reconciler, requestRerender, pool);

Expand Down
Loading
Loading