diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1dc523fe3..c3e375f52 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,12 @@ Conventions for contributors:
### Added
+- **TitleBar drag regions — `.AutoRefreshDragRegions()` and `.IsDragRegion()`
+ (spec 059).** Windows App SDK bumped 2.0.1 → 2.1.3; custom `TitleBar` content
+ now auto-excludes interactive controls from the window drag region by default.
+ Override per element with `.IsDragRegion(false)` (force clickable) /
+ `.IsDragRegion(true)` (force draggable), and set `.AutoRefreshDragRegions()` to
+ re-derive regions when content changes across renders.
- **`DockFloatingWindowClosedEventArgs.Reason` — close-reason discriminator
for floating-window closes (spec 045 §5.3.5, issue #417).** A new
`required DockFloatingCloseReason Reason { get; init; }` on
diff --git a/Directory.Build.props b/Directory.Build.props
index 749bc182c..2c1ebf317 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -17,7 +17,7 @@
before any bootstrapper has run.
-->
- 2.0.1
+ 2.1.3
1.4.0
false
diff --git a/SKILL.md b/SKILL.md
index e876bd92b..2e181c34b 100644
--- a/SKILL.md
+++ b/SKILL.md
@@ -208,7 +208,7 @@ Workflow modes (Phase-2 ranker):
-
+
```
diff --git a/bootstrap.ps1 b/bootstrap.ps1
index fc131b4af..5a85195be 100644
--- a/bootstrap.ps1
+++ b/bootstrap.ps1
@@ -220,7 +220,7 @@ function Get-VsExtensionSkipReason {
# output stays a standalone deployable.
#
# Net effect: the user needs the WindowsAppRuntime 2.0 install matching
-# our WindowsAppSDKVersion=2.0.1 to run most things in this repo.
+# our WindowsAppSDKVersion=2.1.3 to run most things in this repo.
#
# So we prompt by default. `-InstallWinAppSdk` to force-install,
# `-InstallWinAppSdk:$false` to skip the prompt non-interactively.
diff --git a/docs/_pipeline/templates/index.md.dt b/docs/_pipeline/templates/index.md.dt
index 740e390f2..6cf1cf1ed 100644
--- a/docs/_pipeline/templates/index.md.dt
+++ b/docs/_pipeline/templates/index.md.dt
@@ -198,7 +198,7 @@ Create a console project, then edit the `.csproj`:
None
-
+
diff --git a/docs/_pipeline/templates/windows.md.dt b/docs/_pipeline/templates/windows.md.dt
index dc1f30138..00152e461 100644
--- a/docs/_pipeline/templates/windows.md.dt
+++ b/docs/_pipeline/templates/windows.md.dt
@@ -216,6 +216,21 @@ VStack(
TextBlock("Body"));
```
+`TitleBar(...)` accepts custom `Content` (and a trailing `RightHeader`). Interactive
+controls inside the content are excluded from the window drag region automatically
+(WinApp SDK ≥ 2.1.3). Override per element with `.IsDragRegion(false)` to force a
+visual clickable or `.IsDragRegion(true)` to force it draggable, and set
+`.AutoRefreshDragRegions()` on the title bar when the content changes across renders:
+
+```csharp
+(TitleBar("Gallery") with
+{
+ Content = HStack(8,
+ AutoSuggestBox("", _ => {}).Width(200),
+ Button("\uE713", OnSettings).IsDragRegion(false)),
+}).AutoRefreshDragRegions();
+```
+
Caveats:
- Setting `ExtendsContentIntoTitleBar = false` while still rendering a `TitleBar(...)`
diff --git a/docs/guide/README.md b/docs/guide/README.md
index 322751233..17442e141 100644
--- a/docs/guide/README.md
+++ b/docs/guide/README.md
@@ -224,7 +224,7 @@ Create a console project, then edit the `.csproj`:
None
-
+
diff --git a/docs/guide/index.md b/docs/guide/index.md
index 322751233..17442e141 100644
--- a/docs/guide/index.md
+++ b/docs/guide/index.md
@@ -224,7 +224,7 @@ Create a console project, then edit the `.csproj`:
None
-
+
diff --git a/docs/guide/windows.md b/docs/guide/windows.md
index b9bda96d9..ba6b834fa 100644
--- a/docs/guide/windows.md
+++ b/docs/guide/windows.md
@@ -208,6 +208,21 @@ VStack(
TextBlock("Body"));
```
+`TitleBar(...)` accepts custom `Content` (and a trailing `RightHeader`). Interactive
+controls inside the content are excluded from the window drag region automatically
+(WinApp SDK ≥ 2.1.3). Override per element with `.IsDragRegion(false)` to force a
+visual clickable or `.IsDragRegion(true)` to force it draggable, and set
+`.AutoRefreshDragRegions()` on the title bar when the content changes across renders:
+
+```csharp
+(TitleBar("Gallery") with
+{
+ Content = HStack(8,
+ AutoSuggestBox("", _ => {}).Width(200),
+ Button("\uE713", OnSettings).IsDragRegion(false)),
+}).AutoRefreshDragRegions();
+```
+
Caveats:
- Setting `ExtendsContentIntoTitleBar = false` while still rendering a `TitleBar(...)`
diff --git a/docs/specs/059-titlebar-drag-regions.md b/docs/specs/059-titlebar-drag-regions.md
new file mode 100644
index 000000000..8baec2cb9
--- /dev/null
+++ b/docs/specs/059-titlebar-drag-regions.md
@@ -0,0 +1,199 @@
+# TitleBar Drag Regions — Windows App SDK 2.1 Uptake
+
+## Status
+
+**Implemented — 2026-06-26.** Builds on [spec 036 — Window Model](036-window-design.md)
+and [spec 054 — Windowing Evolution](054-windowing-evolution.md) (modern title bar), and consumes
+the auto-mapping path from [spec 058 — Control Wrapper Generator](058-control-wrapper-generator.md).
+
+The triggering question was open-ended: \*"survey the Windows App SDK 2.x release notes and find new
+APIs Reactor could leverage."\* This spec captures that survey, adopts the one high-value uptake it
+surfaced (the `Microsoft.UI.Xaml.Controls.TitleBar` drag-region APIs added in 2.1.3), and records the
+rest of the survey as considered-but-deferred so the decision trail is durable.
+
+**Status of work:** the 2.0.1 → 2.1.3 bump, `AutoRefreshDragRegions`, and the `.IsDragRegion()` modifier
+are implemented and verified — the full `Reactor.Tests` suite is green (9711 passed / 0 failed), plus a
+real-WinUI selftest (`TitleBar_DragRegions`) confirming `AutoRefreshDragRegions` round-trips onto the
+control and `.IsDragRegion(false)` writes the attached property on a child. The §6 decisions were
+resolved per the recommendations.
+
+---
+
+## Table of contents
+
+- [§1 Motivation — the 2.x survey](#1-motivation--the-2x-survey)
+- [§2 Goals / non-goals](#2-goals--non-goals)
+- [§3 The SDK bump — 2.0.1 → 2.1.3](#3-the-sdk-bump--201--213)
+- [§4 API surface](#4-api-surface)
+- [§5 Considered but deferred](#5-considered-but-deferred)
+- [§6 Decisions (resolved)](#6-decisions-resolved-2026-06-26)
+- [§7 Implementation phases](#7-implementation-phases)
+- [§8 Testing](#8-testing)
+
+---
+
+## §1 Motivation — the 2.x survey
+
+Reactor pins **Windows App SDK 2.0.1** (`Directory.Build.props`, centralized via CPM; Win2D 1.4.0).
+The 2.x line has since shipped 2.1.3 (2026-05-21) and 2.2.0 (2026-06-09). Filtering the release notes
+to *XAML / UI* surface (the AI / ML / Video / Storage additions are out of scope for a UI framework),
+the new APIs vs. 2.0.1 are:
+
+| API (version) | Reactor relevance | Verdict |
+| --- | --- | --- |
+| **`TitleBar` drag regions** — `SetIsDragRegion` / `GetIsDragRegion`, `AutoRefreshDragRegions`, `RecomputeDragRegions()` (2.1.3) | `TitleBarElement` maps **directly** to `Microsoft.UI.Xaml.Controls.TitleBar` and exposes a user `Content` slot. The gallery sample already places an `AutoSuggestBox` + `Button` there — the exact mixed interactive / non-interactive case these APIs fix. | **Adopt** (this spec) |
+| `XamlBindingHelper.SetPropertyFromThickness` / `…CornerRadius` / `…Color` (2.2.0) | Boxing-free value-type DP sets. Reactor sets value props through source-generated strongly-typed CLR setters; no boxing-pool infra exists today. | Defer (§5) |
+| `Setter.ValueProperty` (2.2.0) | Exposes the `Setter.Value` DP; relevant only to programmatic `Style`/`Setter` construction. | Defer (§5) |
+| `ApplicationData.GetForUnpackaged()` (2.2.0) | First-class per-user app data for unpackaged apps — could simplify dock-layout / window-placement persistence. | Defer (§5) |
+
+The **only** addition that lands squarely on an existing Reactor surface is the title-bar drag-region
+family. It is also self-justifying: 2.1.3 changed the *default* drag-region behavior so the framework
+now recursively walks `TitleBar.Content` and excludes only **interactive** controls from the drag
+region (RuntimeCompatibilityChange `TitleBar_ContentDragRegions`). Today, Reactor hands the whole
+`TitleBar` to `Window.SetTitleBar`, so custom `Content` controls fight the drag region or need manual
+hole-punching. Just bumping the SDK inherits the better default; surfacing the two override APIs
+completes the story declaratively.
+
+## §2 Goals / non-goals
+
+**Goals**
+
+1. Adopt Windows App SDK **2.1.3** repo-wide, with the runtime / bootstrap / CI implications documented.
+2. Surface `TitleBar.AutoRefreshDragRegions` as a declarative `TitleBarElement` property + fluent modifier.
+3. Surface the `TitleBar.IsDragRegion` attached property as a cross-cutting `.IsDragRegion()` modifier
+ usable on any element placed in a title bar's content.
+4. Keep the API DIP-only / declarative (spec 036 / 054 conventions) — no imperative escape hatch unless
+ a declarative form can't express the scenario.
+
+**Non-goals**
+
+- Adopting 2.2.0 or any of its APIs (§5).
+- Reworking the `Window.SetTitleBar` registration path (`RegisterWindowTitleBar` in `Element.cs`) — the
+ new drag-region behavior layers on top of it unchanged.
+- A boxing-elimination pass over the reconciler's value-prop setters (§5).
+
+## §3 The SDK bump — 2.0.1 → 2.1.3
+
+**Decision:** bump `WindowsAppSDKVersion` to **2.1.3** — the *minimum* version that exposes the
+title-bar drag-region APIs. Rationale for "minimum, not latest":
+
+- 2.1.3 unlocks every API this spec adopts; 2.2.0 adds only the deferred surface (§5), so going further
+ widens the behavioral-change blast radius for no in-scope benefit.
+- 2.1.3 is **already the pinned runtime** in `tests/stress_perf/ci/Run-PerfBenchmark.ps1`
+ (`Microsoft.WindowsAppSDK.Runtime` `2.1.3`), so the perf leg and the framework converge on one version.
+
+**Runtime / packaging implications** (`WindowsAppSDKSelfContained=false` by default — the framework and
+its samples consume the *installed* runtime):
+
+| Concern | Finding |
+| --- | --- |
+| Side-by-side runtime family | All 2.x releases share **one** SbS framework keyed on the API-contract major (`Microsoft.WindowsAppRuntime.2.msix`), per the comment in `Run-PerfBenchmark.ps1`. So 2.0.1 → 2.1.3 is an **in-place servicing** bump, not a new SbS runtime. |
+| `bootstrap.ps1` | Installs `Microsoft.WindowsAppRuntime.2.0` (the major-2 winget id), which services forward to ≥ 2.1.3. The "matching `WindowsAppSDKVersion=2.0.1`" comment should be refreshed to 2.1.3; the winget id itself does not change. |
+| In-process test hosts | `Reactor.Tests`, `Reactor.AppTests.Host`, etc. set `WindowsAppSDKSelfContained=true`, so they **bundle** the matching 2.1.3 runtime and don't depend on a machine install — unit tests + selftests validate against the bumped version directly. |
+| Win2D | 1.4.0 is already paired with 2.x in-repo; staying within major 2 keeps that pairing valid. |
+| **Standalone scaffolding pins** | The `dotnet new` template (`tools/Templates/.../Company.ReactorApp1.csproj`), the CLI's local-scaffold csproj string (`Reactor.Cli/Program.cs`), and the `Microsoft.WindowsAppSDK` install snippets in `docs/guide/index.md`, `SKILL.md`, and `samples/WinFormsInterop/README.md` each **directly** pin the SDK and do **not** inherit the central `WindowsAppSDKVersion`. A stale `2.0.1` / `2.0.*` pin is an **NU1605 downgrade** against the framework's new `>= 2.1.3` dependency — the `Integration Tests` (template smoke) and `bootstrap.ps1` CI jobs scaffold + restore and fail on it. These must be bumped in lockstep (done: `2.1.3` / `2.1.*`). Future SDK bumps should grep `Microsoft.WindowsAppSDK" Version=` and update every literal pin. |
+| Inherited default-behavior change | `TitleBar_ContentDragRegions` (auto-exclude interactive controls from the drag region) applies to every `TitleBarElement.Content` for free. This is the headline win even before the override APIs. |
+
+**Validated:** with `WindowsAppSDKVersion=2.1.3`, `dotnet restore` + `dotnet build src/Reactor/Reactor.csproj`
+both succeed clean. The descriptor generator regenerates without new diagnostics.
+
+## §4 API surface
+
+### §4.1 `AutoRefreshDragRegions` (per-title-bar, declarative)
+
+Add a non-nullable `bool AutoRefreshDragRegions` to the `TitleBarElement` record. Because the title-bar
+wrapper runs in **descriptor-only auto-discovery** mode (spec 058 §15), the generator discovers the new
+`TitleBar.AutoRefreshDragRegions` dependency property and emits the \`.OneWay(e => e.AutoRefreshDragRegions,
+(c, v) => c.AutoRefreshDragRegions = v)\` mapping automatically; a non-nullable value-typed record prop is
+promoted to an unconditional write, so no `Customize` hand-wiring is needed. Default `false` matches the
+WinUI control default.
+
+````csharp
+// fluent modifier (ElementExtensions)
+public static TitleBarElement AutoRefreshDragRegions(this TitleBarElement el, bool value = true) =>
+ el with { AutoRefreshDragRegions = value };
+```
+
+When `true`, the control re-derives drag regions on every layout pass — the right fit for a reactive
+framework whose `Content` changes across renders.
+
+### §4.2 `.IsDragRegion(bool?)` (cross-cutting child modifier)
+
+`TitleBar.IsDragRegion` is a **static attached property** (`bool?` — `true` = draggable, `false` =
+clickable, unset = framework-decided). It can sit on any descendant of the title-bar content, so it is
+modeled as a cross-cutting modifier on `ElementModifiers`, exactly like `AutomationName` / `IsTabStop`:
+
+1. `ElementModifiers` gains `public bool? IsDragRegion { get; init; }`.
+2. A generic modifier:
+ ```csharp
+ public static T IsDragRegion(this T el, bool isDragRegion = true) where T : Element =>
+ Modify(el, new ElementModifiers { IsDragRegion = isDragRegion });
+ ```
+3. `Reconciler.ApplyModifiers` applies it on the realized `FrameworkElement`, mirroring the
+ `AutomationName` set/clear arms:
+ ```csharp
+ if (m.IsDragRegion.HasValue && m.IsDragRegion != oldM?.IsDragRegion)
+ WinUI.TitleBar.SetIsDragRegion(fe, m.IsDragRegion.Value);
+ else if (!m.IsDragRegion.HasValue && oldM?.IsDragRegion.HasValue == true)
+ fe.ClearValue(WinUI.TitleBar.IsDragRegionProperty);
+ ```
+
+Setting the attached DP on an element that is *not* inside a `TitleBar` is inert (an unread attached
+value), so the modifier is safe on any element and needs no title-bar-subtree gating. The unset state
+intentionally falls through to the new 2.1.3 default (auto-exclusion), so authors only reach for
+`.IsDragRegion(false)` to make a non-interactive visual clickable, or `.IsDragRegion(true)` to force a
+gap draggable.
+
+```csharp
+(TitleBar("Gallery") with
+{
+ Content = HStack(8,
+ AutoSuggestBox("", _ => {}).Width(200), // interactive → excluded by default
+ Button("\uE713", () => {}).IsDragRegion(false)) // explicit: keep clickable
+}).AutoRefreshDragRegions();
+```
+
+### §4.3 `RecomputeDragRegions()` — intentionally not surfaced
+
+The imperative `RecomputeDragRegions()` method is **not** wrapped. `AutoRefreshDragRegions` provides the
+same effect declaratively and continuously, which is the idiomatic Reactor fit; exposing an imperative
+"recompute now" method would invite call sites that fight the render loop. Revisit only if a concrete
+scenario needs a one-shot recompute that `AutoRefreshDragRegions` can't cover (§6, D3).
+
+## §5 Considered but deferred
+
+| Candidate | Why deferred |
+|---|---|
+| `XamlBindingHelper.SetPropertyFrom{Thickness,CornerRadius,Color}` (2.2.0) | Boxing-free value-type DP sets. Reactor writes value props via source-generated strongly-typed CLR setters (`c.Margin = …`), which box internally; capturing this win means threading explicit `DependencyProperty` references through the generator, fighting its strongly-typed model for a marginal GC improvement. No boxing-pool infra exists today to justify the inconsistency. Revisit only with reconcile-allocation evidence (the `perf-compare` harness). Requires 2.2.0. |
+| `Setter.ValueProperty` (2.2.0) | Useful only for boxing-free programmatic `Style`/`Setter` construction; tangential to Reactor's theming-token path. Requires 2.2.0. |
+| `ApplicationData.GetForUnpackaged()` (2.2.0) | Could replace bespoke file-path persistence for dock-layout / window-placement in unpackaged apps with a first-class `ApplicationData`. Worth a focused look, but orthogonal to this spec and gated on a 2.2.0 bump. |
+
+## §6 Decisions (resolved 2026-06-26)
+
+| # | Question | Resolution |
+|---|---|---|
+| D1 | Target version — **2.1.3** (minimal) vs. **2.2.0** (latest stable, picks up §5 candidates) | **2.1.3.** Minimal blast radius; aligns with the pinned perf runtime. |
+| D2 | `AutoRefreshDragRegions` default | **`false`** (match WinUI). Opt-in via `.AutoRefreshDragRegions()`. Avoids a per-layout recompute cost no author asked for. |
+| D3 | Expose `RecomputeDragRegions()`? | **No** (§4.3). |
+| D4 | Spec home — standalone **059** vs. a new section in **054** (windowing) | **Standalone**, because the SDK bump is repo-wide, not windowing-local. |
+| D5 | Forced consumer floor — the bump makes `Microsoft.UI.Reactor` advertise `Microsoft.WindowsAppSDK >= 2.1.3` (transitive, no `PrivateAssets`), so every consumer's floor rises. Mitigate (compile against 2.0.1 + reflection-guard the drag-region DPs so the feature lights up only on 2.1.3+, matching the `BackdropKind.Transparent` graceful-degradation precedent) vs. accept? | **Accept.** 2.0 → 2.1 is an **in-place servicing** bump within the *same* side-by-side 2.x runtime family (`Microsoft.WindowsAppRuntime.2.msix`), not a new SxS major — so the upgrade is low-friction for consumers. The reflection-guard alternative keeps the floor at 2.0.1 but adds string-based reflection + `[UnconditionalSuppressMessage]` to the AOT-strict core hot path (`ApplyModifiers`) and silently no-ops drag regions on 2.0 runtimes; the added complexity and the "silently does nothing" failure mode aren't worth avoiding a within-family servicing bump. Revisit only if a concrete consumer is pinned to 2.0.x and cannot service forward. |
+
+## §7 Implementation phases
+
+1. ✅ **SDK bump** — `Directory.Build.props` 2.0.1 → 2.1.3; refreshed the `bootstrap.ps1` version comment.
+2. ✅ **`AutoRefreshDragRegions`** — added the record property to `TitleBarElement`; generator auto-maps it
+ (`.OneWay(e => e.AutoRefreshDragRegions, …)`, confirmed in the generated descriptor); added the
+ `.AutoRefreshDragRegions()` fluent modifier.
+3. ✅ **`.IsDragRegion()`** — added `bool? IsDragRegion` to `ElementModifiers`, the generic modifier, and the
+ `ApplyModifiers` set/clear arm.
+4. ✅ **Sample** — extended `samples/ReactorGallery/.../TitleBarPage.cs` "TitleBar with Content".
+5. ✅ **Docs** — windowing skill + `windows.md.dt` guide template (recompiled `docs/guide/windows.md`).
+
+## §8 Testing
+
+| Tier | Coverage |
+|---|---|
+| Unit (`Reactor.Tests`) | `.IsDragRegion()` / `.AutoRefreshDragRegions()` produce the expected `ElementModifiers` / `TitleBarElement` records (modifier plumbing, default + override values). |
+| Selftest (`Reactor.AppTests.Host/SelfTest/Fixtures`) | Mount a real `TitleBar` with content; assert `TitleBar.GetIsDragRegion(child)` reflects `.IsDragRegion(false/true)` and clears on unset; assert `AutoRefreshDragRegions` round-trips on the control. |
+| Build / CI | Full `Reactor.slnx` build + unit tests + selftests on the bumped SDK (the standard PR gate). |
+````
\ No newline at end of file
diff --git a/plugins/reactor/skills/reactor-dsl/references/reactor.api.txt b/plugins/reactor/skills/reactor-dsl/references/reactor.api.txt
index f557370dc..fb9911c71 100644
--- a/plugins/reactor/skills/reactor-dsl/references/reactor.api.txt
+++ b/plugins/reactor/skills/reactor-dsl/references/reactor.api.txt
@@ -605,6 +605,7 @@ T.HierarchyLevel(int level) → T
T.HorizontalAlignment(HorizontalAlignment alignment) → T
T.Immediate() → T
T.InteractionStates(Func configure, Curve curve = null) → T
+T.IsDragRegion(bool? isDragRegion = true) → T
T.IsEnabled(bool enabled = true) → T
T.IsTabStop(bool isTabStop = true) → T
T.IsVisible(bool isVisible) → T
@@ -797,6 +798,7 @@ TextBoxElement.TextWrapping(TextWrapping wrapping = TextWrapping.Wrap) → TextB
TextBoxElement.UrlInput() → TextBoxElement
TimePickerElement.Set(Action configure) → TimePickerElement
TimePickerElement.TimeChanged(Action handler) → TimePickerElement
+TitleBarElement.AutoRefreshDragRegions(bool value = true) → TitleBarElement
TitleBarElement.BackButtonEnabled(bool enabled) → TitleBarElement
TitleBarElement.BackButtonVisible(bool visible) → TitleBarElement
TitleBarElement.BackRequested(Action handler) → TitleBarElement
@@ -2963,6 +2965,7 @@ Vector3? Translation { get; init; }
VerticalAlignment? VerticalAlignment { get; init; }
VisualModifiers Visual { get; init; }
XYFocusKeyboardNavigationMode? XYFocusKeyboardNavigation { get; init; }
+bool? IsDragRegion { get; init; }
bool? IsEnabled { get; init; }
bool? IsTabStop { get; init; }
bool? IsVisible { get; init; }
@@ -6832,6 +6835,7 @@ Action OnPaneToggleRequested { get; init; }
Element Content { get; init; }
Element RightHeader { get; init; }
IconData Icon { get; init; }
+bool AutoRefreshDragRegions { get; init; }
bool IsBackButtonEnabled { get; init; }
bool IsBackButtonVisible { get; init; }
bool IsPaneToggleButtonVisible { get; init; }
diff --git a/plugins/reactor/skills/reactor-windowing/SKILL.md b/plugins/reactor/skills/reactor-windowing/SKILL.md
index 5e4906493..35cf4436f 100644
--- a/plugins/reactor/skills/reactor-windowing/SKILL.md
+++ b/plugins/reactor/skills/reactor-windowing/SKILL.md
@@ -120,6 +120,22 @@ spec value is `null`. Explicit `true` or `false` wins.
> path) so it is safe. Prefer omitting `TitleBar(...)` when you want the system
> title bar.
+### Custom title-bar content and drag regions
+
+`TitleBar(...)` accepts custom `Content`. Interactive controls are excluded from
+the drag region automatically (WinApp SDK ≥ 2.1.3). Override per element with
+`.IsDragRegion(false)` (force clickable) or `.IsDragRegion(true)` (force draggable),
+and add `.AutoRefreshDragRegions()` when the content changes across renders:
+
+```csharp
+(TitleBar("Gallery") with
+{
+ Content = HStack(8,
+ AutoSuggestBox("", _ => {}).Width(200),
+ Button("\uE713", OnSettings).IsDragRegion(false)),
+}).AutoRefreshDragRegions();
+```
+
```csharp
VStack(...).Backdrop(BackdropKind.Mica);
new WindowSpec { Backdrop = BackdropChoice.Of(BackdropKind.DesktopAcrylic) };
diff --git a/samples/ReactorGallery/ControlPages/Navigation/TitleBarPage.cs b/samples/ReactorGallery/ControlPages/Navigation/TitleBarPage.cs
index 10c55d17a..9295e08ed 100644
--- a/samples/ReactorGallery/ControlPages/Navigation/TitleBarPage.cs
+++ b/samples/ReactorGallery/ControlPages/Navigation/TitleBarPage.cs
@@ -39,19 +39,19 @@ public override Element Render()
SampleCard("TitleBar with Content",
Border(
- TitleBar("Gallery") with
+ (TitleBar("Gallery") with
{
Content = HStack(8,
AutoSuggestBox("", _ => { }).Width(200),
- Button("\uE713", () => { }).Width(36).Height(36)
+ Button("\uE713", () => { }).Width(36).Height(36).IsDragRegion(false)
),
- }
+ }).AutoRefreshDragRegions()
).Background(Theme.LayerFill).CornerRadius(4).Height(48),
- @"TitleBar(""Gallery"") with {
+ @"(TitleBar(""Gallery"") with {
Content = HStack(8,
AutoSuggestBox("""", _ => {}).Width(200),
- Button(""⚙"", () => {}))
-}")
+ Button(""⚙"", () => {}).IsDragRegion(false))
+}).AutoRefreshDragRegions()")
).Margin(36, 24, 36, 36)
);
}
diff --git a/samples/WinFormsInterop/README.md b/samples/WinFormsInterop/README.md
index 43cf5f130..8ebfc9512 100644
--- a/samples/WinFormsInterop/README.md
+++ b/samples/WinFormsInterop/README.md
@@ -40,7 +40,7 @@ static class Program
true
None
-
+
```
diff --git a/skills/reactor.api.txt b/skills/reactor.api.txt
index f557370dc..fb9911c71 100644
--- a/skills/reactor.api.txt
+++ b/skills/reactor.api.txt
@@ -605,6 +605,7 @@ T.HierarchyLevel(int level) → T
T.HorizontalAlignment(HorizontalAlignment alignment) → T
T.Immediate() → T
T.InteractionStates(Func configure, Curve curve = null) → T
+T.IsDragRegion(bool? isDragRegion = true) → T
T.IsEnabled(bool enabled = true) → T
T.IsTabStop(bool isTabStop = true) → T
T.IsVisible(bool isVisible) → T
@@ -797,6 +798,7 @@ TextBoxElement.TextWrapping(TextWrapping wrapping = TextWrapping.Wrap) → TextB
TextBoxElement.UrlInput() → TextBoxElement
TimePickerElement.Set(Action configure) → TimePickerElement
TimePickerElement.TimeChanged(Action handler) → TimePickerElement
+TitleBarElement.AutoRefreshDragRegions(bool value = true) → TitleBarElement
TitleBarElement.BackButtonEnabled(bool enabled) → TitleBarElement
TitleBarElement.BackButtonVisible(bool visible) → TitleBarElement
TitleBarElement.BackRequested(Action handler) → TitleBarElement
@@ -2963,6 +2965,7 @@ Vector3? Translation { get; init; }
VerticalAlignment? VerticalAlignment { get; init; }
VisualModifiers Visual { get; init; }
XYFocusKeyboardNavigationMode? XYFocusKeyboardNavigation { get; init; }
+bool? IsDragRegion { get; init; }
bool? IsEnabled { get; init; }
bool? IsTabStop { get; init; }
bool? IsVisible { get; init; }
@@ -6832,6 +6835,7 @@ Action OnPaneToggleRequested { get; init; }
Element Content { get; init; }
Element RightHeader { get; init; }
IconData Icon { get; init; }
+bool AutoRefreshDragRegions { get; init; }
bool IsBackButtonEnabled { get; init; }
bool IsBackButtonVisible { get; init; }
bool IsPaneToggleButtonVisible { get; init; }
diff --git a/src/Reactor.Cli/Program.cs b/src/Reactor.Cli/Program.cs
index 829353d8a..423b189c6 100644
--- a/src/Reactor.Cli/Program.cs
+++ b/src/Reactor.Cli/Program.cs
@@ -314,7 +314,7 @@ string GenerateCsproj() =>
Trim="true" />
-
+
diff --git a/src/Reactor/Core/Element.cs b/src/Reactor/Core/Element.cs
index 92918ea50..c041d5307 100644
--- a/src/Reactor/Core/Element.cs
+++ b/src/Reactor/Core/Element.cs
@@ -1072,6 +1072,7 @@ internal static bool ModifiersEqual(ElementModifiers? a, ElementModifiers? b)
&& a.ContextFlyout is null && b.ContextFlyout is null
// Accessibility Tier 1
&& a.HeadingLevel == b.HeadingLevel
+ && a.IsDragRegion == b.IsDragRegion
&& a.IsTabStop == b.IsTabStop
&& a.TabIndex == b.TabIndex
&& a.AccessKey == b.AccessKey
@@ -1679,6 +1680,15 @@ public ElementTheme? RequestedTheme
init => Layout = Layout is null ? new LayoutModifiers { RequestedTheme = value } : Layout with { RequestedTheme = value };
}
+ ///
+ /// When set, writes the Microsoft.UI.Xaml.Controls.TitleBar.IsDragRegion
+ /// attached property on this element's control (WinApp SDK ≥ 2.1.3):
+ /// true = draggable, false = clickable. Unset leaves the title bar
+ /// to decide (interactive controls are auto-excluded from the drag region).
+ /// Inert on elements that are not inside a TitleBar. See spec 059.
+ ///
+ public bool? IsDragRegion { get; init; }
+
// ── Accessibility — Tier 1 (inline, commonly needed for WCAG AA) ─
public Microsoft.UI.Xaml.Automation.Peers.AutomationHeadingLevel? HeadingLevel { get; init; }
public bool? IsTabStop { get; init; }
@@ -1767,6 +1777,7 @@ public ElementModifiers Merge(ElementModifiers other)
DragSource = other.DragSource ?? DragSource,
DropTarget = other.DropTarget ?? DropTarget,
HeadingLevel = other.HeadingLevel ?? HeadingLevel,
+ IsDragRegion = other.IsDragRegion ?? IsDragRegion,
IsTabStop = other.IsTabStop ?? IsTabStop,
TabIndex = other.TabIndex ?? TabIndex,
AccessKey = other.AccessKey ?? AccessKey,
@@ -4172,7 +4183,7 @@ public partial record NavigationViewElement(
}
// Spec 058 §15 (P5.23) — Title/Subtitle/IsBackButtonVisible/IsBackButtonEnabled/
-// IsPaneToggleButtonVisible auto-map. Content+RightHeader (NamedSlots → overwrite d.Children),
+// IsPaneToggleButtonVisible/AutoRefreshDragRegions auto-map. Content+RightHeader (NamedSlots → overwrite d.Children),
// Icon (Icon→IconSource via IconResolver transform), the window.SetTitleBar registration
// (.Imperative) and the BackRequested/PaneToggleRequested events (Excluded) are bespoke.
// Replaces TitleBarDescriptor.
@@ -4190,6 +4201,13 @@ string Title
public Action? OnBackRequested { get; init; }
public bool IsPaneToggleButtonVisible { get; init; }
public Action? OnPaneToggleRequested { get; init; }
+ ///
+ /// When true, the WinUI TitleBar re-derives its drag regions on
+ /// every layout pass (WinApp SDK ≥ 2.1.3). Useful when
+ /// changes across renders. Default false (matches the control default;
+ /// interactive controls are still auto-excluded from the drag region). See spec 059.
+ ///
+ public bool AutoRefreshDragRegions { get; init; }
public Element? Content { get; init; }
public Element? RightHeader { get; init; }
///
diff --git a/src/Reactor/Core/ElementPool.cs b/src/Reactor/Core/ElementPool.cs
index 51447d993..f358264dd 100644
--- a/src/Reactor/Core/ElementPool.cs
+++ b/src/Reactor/Core/ElementPool.cs
@@ -248,6 +248,9 @@ internal static void CleanElement(FrameworkElement fe)
fe.ClearValue(UIElement.IsHitTestVisibleProperty);
if (fe is Control tabStopControl)
tabStopControl.ClearValue(Control.IsTabStopProperty);
+ // spec 059: clear the TitleBar.IsDragRegion attached prop so a pooled
+ // control marked .IsDragRegion(...) can't poison the next renter.
+ fe.ClearValue(WinUI.TitleBar.IsDragRegionProperty);
fe.ClearValue(Microsoft.UI.Xaml.Automation.AutomationProperties.IsRequiredForFormProperty);
fe.ClearValue(Microsoft.UI.Xaml.Automation.AutomationProperties.LiveSettingProperty);
fe.ClearValue(Microsoft.UI.Xaml.Automation.AutomationProperties.PositionInSetProperty);
diff --git a/src/Reactor/Core/Reconciler.cs b/src/Reactor/Core/Reconciler.cs
index fc2983b18..ed4513e95 100644
--- a/src/Reactor/Core/Reconciler.cs
+++ b/src/Reactor/Core/Reconciler.cs
@@ -3787,6 +3787,12 @@ internal void ApplyModifiers(FrameworkElement fe, ElementModifiers? oldM, Elemen
else if (m.AutomationId is null && oldM?.AutomationId is not null)
fe.ClearValue(Microsoft.UI.Xaml.Automation.AutomationProperties.AutomationIdProperty);
+ // TitleBar.IsDragRegion (attached; inert outside a TitleBar). WinApp SDK ≥ 2.1.3.
+ if (m.IsDragRegion.HasValue && m.IsDragRegion != oldM?.IsDragRegion)
+ WinUI.TitleBar.SetIsDragRegion(fe, m.IsDragRegion.Value);
+ else if (!m.IsDragRegion.HasValue && oldM?.IsDragRegion.HasValue == true)
+ fe.ClearValue(WinUI.TitleBar.IsDragRegionProperty);
+
// ElementSoundMode (on Control, not FrameworkElement)
if (m.ElementSoundMode.HasValue && m.ElementSoundMode != oldM?.ElementSoundMode && fe is WinUI.Control ctrl)
ctrl.ElementSoundMode = m.ElementSoundMode.Value;
diff --git a/src/Reactor/Elements/ElementExtensions.cs b/src/Reactor/Elements/ElementExtensions.cs
index cce7063f7..5bfb7ed39 100644
--- a/src/Reactor/Elements/ElementExtensions.cs
+++ b/src/Reactor/Elements/ElementExtensions.cs
@@ -1719,6 +1719,15 @@ public static TitleBarElement Icon(this TitleBarElement el, IconData icon) =>
public static TitleBarElement Icon(this TitleBarElement el, string imageUri) =>
el with { Icon = new ImageIconData(new Uri(imageUri)) };
+ ///
+ /// When true, the title bar re-derives its drag regions on every layout
+ /// pass — the right fit for content that changes across renders. WinApp SDK ≥ 2.1.3.
+ /// Interactive controls are auto-excluded from the drag region either way; use
+ /// on a child to override per element.
+ ///
+ public static TitleBarElement AutoRefreshDragRegions(this TitleBarElement el, bool value = true) =>
+ el with { AutoRefreshDragRegions = value };
+
///
/// Auto-syncs this TitleBar's back button with a NavigationHandle: sets
/// IsBackButtonVisible and IsBackButtonEnabled from CanGoBack,
@@ -2476,6 +2485,18 @@ public static T HeadingLevel(this T el, Microsoft.UI.Xaml.Automation.Peers.Au
public static T IsTabStop(this T el, bool isTabStop = true) where T : Element =>
Modify(el, new ElementModifiers { IsTabStop = isTabStop });
+ ///
+ /// Marks how this element participates in a window title bar's drag region
+ /// (Microsoft.UI.Xaml.Controls.TitleBar.IsDragRegion, WinApp SDK ≥ 2.1.3):
+ /// true = draggable, false = clickable, null = defer to the
+ /// title bar (interactive controls are auto-excluded from the drag region). Only
+ /// meaningful for an element inside a TitleBar's content; inert elsewhere.
+ /// The nullable parameter lets callers forward a bool? state without branching.
+ ///
+ /// Button("\uE713", OnSettings).IsDragRegion(false)
+ public static T IsDragRegion(this T el, bool? isDragRegion = true) where T : Element =>
+ Modify(el, new ElementModifiers { IsDragRegion = isDragRegion });
+
///
/// Sets Control.TabIndex — Tab order position. Lower values receive focus first.
///
diff --git a/src/Reactor/Reactor.csproj b/src/Reactor/Reactor.csproj
index 082b58a9f..7829fe5d4 100644
--- a/src/Reactor/Reactor.csproj
+++ b/src/Reactor/Reactor.csproj
@@ -237,6 +237,8 @@
Pack="true" PackagePath="agentkit/plugins/reactor/skills/reactor-recipes/references/" Visible="false" />
+
-
+