Skip to content

Support picker methods in ReactorHostControl-hosted content - #1397

Merged
Alexandre Zollinger Chohfi (azchohfi) merged 8 commits into
mainfrom
azchohfi-picker-support-reactor-host-control
Oct 22, 2026
Merged

Alexandre Zollinger Chohfi (azchohfi) merged 8 commits into
mainfrom
azchohfi-picker-support-reactor-host-control

Conversation

@azchohfi

@azchohfi Alexandre Zollinger Chohfi (azchohfi) commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

PickFileAsync / PickMultipleFilesAsync / PickFolderAsync / PickMultipleFoldersAsync / PickSaveFileAsync now work in content hosted by ReactorHostControl and by a ReactorHost built over a raw Window. Before this change they threw "requires a component rendered in a ReactorWindow", and the docs pointed at a native-picker workaround.

The reconciler now carries a small internal owner resolver instead of a ReactorWindow:

Docs: the windows guide template drops the workaround caveat and the guide is recompiled. Also updated: both windowing skills, spec 054 §8.3, the 054 task doc, the CHANGELOG and the Pick*Async XML remarks (every throw condition).

Linked issue / spec

Fixes #1395. Part of spec 054 §8.3. Follow-up to #1394.

Test plan

  • Unit tests in tests/Reactor.Tests/PickerTests.cs. A fake IContentOwner checks that each status maps to its own error and never reaches the picker service. Available routes the id resolved at call time. RunCleanups and DetachFromHost give the "no longer mounted" error, and rendering again re-records the owner.
  • Selftest Pickers_HostControl: a control in a separate raw window. All 5 operations, plus the nested class, function, memo and virtualized-row components, route to that window's id; guards check it is not the harness or active-host window. Calls off the UI thread are rejected. Unloaded calls are rejected for all 5 operations. Moved into a second window, calls route to the new id. After Dispose(), calls get "no longer mounted".
  • Selftest Pickers_RawWindowHost: calls route to the raw window. They are rejected off the UI thread, and after the window closes they get "no longer mounted".
  • Selftest Pickers_EmbeddedTeardownAndFunctionRoots: a component cleanup calls a sibling's picker. That call is refused with the closed error in two cases: while a ReactorHostControl is disposed (still loaded), and while a raw window closes. A function root mounted directly on either host routes to its window.
  • Mutation checks:
    • Forcing the control's resolver to return Detached turns Pickers_HostControl red.
    • Removing the disposed/closed guards on both hosts, and the owner from both hosts' function-root BeginRender, turns all 8 teardown and function-root checks red. I confirmed the rebuilt dll was fresh before trusting this.
  • dotnet test tests/Reactor.Tests --filter-class "*PickerTests*": passed (48).
  • dotnet test tests/Reactor.Tests: passed (14,929 passed, 64 skipped, 0 failed).
  • dotnet run --project tests/Reactor.AppTests.Host --no-build -c Debug -p:Platform=x64 -- --self-test --filter "Pickers": 0 failures, on binaries whose timestamps I confirmed were fresh.
  • Full selftest run (-- --self-test): 2 failures, PDM_Stack_Swap_AllSurvivorsKeepIdentity and an AutoSuggestBoxCollectedStateTick timeout. That run overlapped with the full unit suite. Neither fixture touches pickers or owners, and both passed when re-run alone (PDM 3×).
  • dotnet restore Reactor.slnx then dotnet build Reactor.slnx --no-restore -c Release: compiled with no C# or IDE warnings or errors. This machine cannot reach nuget.org (TLS proxy), so I restored from the local package cache with deps: Windows NuGet updates #1393's package bumps reverted locally (not committed). For the same reason, the CLI's nested Reactor.SignaturesGen build fails with NU1900; CI is not affected.
  • dotnet run --project tools/Reactor.DocPipeline -- compile --topic windows --no-screenshots --skip-diagrams --skip-reference: only docs/guide/windows.md changed.

Risk / breaking changes

  • No public API change; the new interface is internal and implemented explicitly.
  • Behaviour change: a ReactorHost built directly over a Window can now open pickers, including the tray flyout host and test harnesses. Before, it threw. In the tray flyout, opening a picker deactivates the flyout, and light-dismiss closes it. The awaiting handler still receives the result.
  • The picker error messages changed. They are unreleased (from Migrate picker helpers to Microsoft.Windows.Storage.Pickers #1394).

Replace the ReactorWindow-typed picker owner with a small internal owner
resolver (IContentOwner, Reconciler.Owner) implemented by ReactorHost (the
Window it was constructed over) and ReactorHostControl (the window it is
loaded in, via XamlRoot.ContentIslandEnvironment.AppWindowId, resolved at
call time). Release on unmount/root retirement, the closed and off-thread
checks, and distinct errors are kept; a detached host control gets its own
error.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0095f9f3-4194-447b-bb62-a5a9914004c3
@azchohfi Alexandre Zollinger Chohfi (azchohfi) added the agent-preparing Agent is addressing feedback or completing required validation and CI label Oct 21, 2026
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

📦 Build metrics

Artifact sizes for f889fa2 vs the base branch (731911e).

Packages (compressed .nupkg)

Artifact base PR Δ
Microsoft.UI.Reactor.nupkg 1.90 MB 1.90 MB +1022 B (+0.05%) ⚠️
Microsoft.UI.Reactor.Advanced.nupkg 493.3 KB 493.3 KB +13 B (0.00%) ≈
Microsoft.UI.Reactor.Devtools.nupkg 288.0 KB 288.0 KB +10 B (0.00%) ≈

Assemblies in Microsoft.UI.Reactor

Artifact base PR Δ
Reactor.Analyzers.dll 410.5 KB 410.5 KB +0 B (0.00%) ≈
Reactor.dll 2.65 MB 2.65 MB +1.00 KB (+0.04%) ≈
Reactor.Localization.Generator.dll 16.0 KB 16.0 KB +0 B (0.00%) ≈
Reactor.Wrappers.Abstractions.dll 10.5 KB 10.5 KB +0 B (0.00%) ≈
Reactor.Wrappers.Generator.dll 99.5 KB 99.5 KB +0 B (0.00%) ≈

Assemblies in Microsoft.UI.Reactor.Advanced

Artifact base PR Δ
Reactor.Advanced.dll 1.00 MB 1.00 MB +0 B (0.00%) ≈

Assemblies in Microsoft.UI.Reactor.Devtools

Artifact base PR Δ
Microsoft.UI.Reactor.Devtools.dll 787.5 KB 787.5 KB +0 B (0.00%) ≈

✅ smaller / ⚠️ larger / ≈ within noise. Sizes come from a Release dotnet pack on the CI runner: packages are the compressed .nupkg download size, assemblies the uncompressed DLL inside it.
workflow run.

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

🧪 Merged coverage

Coverage for 4701016 vs the base branch (4f0bf1c) — unit + selftest merged.

Metric base PR Δ
Line 87.24% 87.24% 0.00 pp ≈
Branch 76.69% (977/1274) 76.69% (977/1274) 0.00 pp ≈

No coverage change beyond the noise floor. ✅

✅ higher / ⚠️ lower / ≈ within noise. Δ is in percentage points; coverage is unit + selftest merged (Debug x64) on the CI runner. Cobertura reports attached to the workflow run as artifacts.

Comment thread src/Reactor/Core/Reconciler.cs Resolved
Add EmbeddedTeardownAndFunctionRoots selftest: picker calls from cleanups
during ReactorHostControl dispose and raw-window close are refused, and
function roots mounted on either host record their owner. Document every
throw condition in the Pick*Async remarks, fix tense in spec 054, rename the
raw-host post-close checks to _Released_, and revert accidental thumbnail
churn.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0095f9f3-4194-447b-bb62-a5a9914004c3

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The owner propagation, lifecycle guards, regression coverage, and documentation consistently support the requested behavior, with no unresolved blocking findings.

No open issues 🎉

What changed in this PR

Extends Reactor’s picker helpers to embedded ReactorHostControl content and hosts built over raw WinUI windows, addressing #1395 without changing the public API.

Changes:

  • Introduces an internal owner resolver that determines the hosting window at call time and preserves distinct lifecycle and thread errors.
  • Adds unit tests and selftests for routing, reparenting, teardown, function roots, and off-thread rejection.
  • Updates the guide, windowing skills, specification, and changelog to document the expanded support.
File Description
tests/​Reactor.Tests/​PickerTests.cs Tests resolver states, routing, and owner release.
tests/​Reactor.AppTests.Host/​SelfTest/​SelfTestFixtureRegistry.cs Registers three new picker fixtures.
tests/​Reactor.AppTests.Host/​SelfTest/​Fixtures/​PickersFixtures.cs Covers embedded and raw-window picker lifecycles.
src/​Reactor/​Hosting/​ReactorHostControl.cs Resolves the current containing window with lifecycle guards.
src/​Reactor/​Hosting/​ReactorHost.cs Resolves picker ownership for raw and Reactor windows.
src/​Reactor/​Core/​RenderContext.cs Records the owner resolver and maps unavailable states to errors.
src/​Reactor/​Core/​Reconciler.Mount.cs Passes ownership to newly mounted components.
src/​Reactor/​Core/​Reconciler.cs Carries ownership through component updates.
src/​Reactor/​Core/​ContentOwner.cs Defines the internal owner contract and resolution states.
skills/​windowing.md Documents embedded picker support.
plugins/​reactor/​skills/​reactor-windowing/​SKILL.md Updates shipped windowing guidance.
docs/​specs/​tasks/​054-windowing-evolution-implementation.md Records implementation and validation of embedded owners.
docs/​specs/​054-windowing-evolution.md Specifies call-time owner resolution and failure states.
docs/​guide/​windows.md Replaces the workaround with supported hosting behavior.
docs/​_pipeline/​templates/​windows.md.dt Updates the source template for the windows guide.
CHANGELOG.md Announces embedded and raw-window picker support.

Review effort: Balanced

Use a using declaration for the ReactorHostControl in Pickers_HostControl.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0095f9f3-4194-447b-bb62-a5a9914004c3

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The owner propagation and lifecycle guards are consistent with the intended behavior, supported by focused regression coverage, with no unresolved blocking findings.

No open issues 🎉

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

Ownership propagation, lifecycle guards, regression coverage, and documentation align with the requested behavior, with no unresolved blocking issues.

No open issues 🎉

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The owner propagation, lifecycle guards, and regression coverage are consistent with the intended behavior, with no unresolved blocking findings.

No open issues 🎉

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

Owner propagation, lifecycle guards, regression coverage, and documentation are consistent, with no blocking issues identified.

No open issues 🎉

Review effort: Balanced

@azchohfi Alexandre Zollinger Chohfi (azchohfi) added ready-for-review Agent work and technical checks complete; awaiting review, not approval or merge and removed agent-preparing Agent is addressing feedback or completing required validation and CI labels Oct 21, 2026
Resolve against #1399's window-hook owner: Reconciler.OwningWindow reads
become Owner?.ReactorWindow (TitleBar mount/update/unmount), keep
Reconciler.HostWindow, and RenderContext.OwningReactorWindow reads the owner
resolver with #1399's no-fallback rule. Specs 036/054 describe the shared
owner.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0095f9f3-4194-447b-bb62-a5a9914004c3
@azchohfi
Alexandre Zollinger Chohfi (azchohfi) merged commit 5ba98bf into main Oct 22, 2026
45 checks passed
@azchohfi
Alexandre Zollinger Chohfi (azchohfi) deleted the azchohfi-picker-support-reactor-host-control branch October 22, 2026 17:06
@azchohfi Alexandre Zollinger Chohfi (azchohfi) removed the ready-for-review Agent work and technical checks complete; awaiting review, not approval or merge label Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support picker methods in ReactorHostControl-hosted content

2 participants