Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ T.Immediate<T>() → T
T.InteractionStates<T>(Func<InteractionStatesBuilder, InteractionStatesBuilder> configure, Curve curve = null) → T
T.IsDragRegion<T>(bool? isDragRegion = true) → T
T.IsEnabled<T>(bool enabled = true) → T
T.IsHitTestVisible<T>(bool isHitTestVisible = true) → T
T.IsTabStop<T>(bool isTabStop = true) → T
T.IsVisible<T>(bool isVisible) → T
T.ItemContainerTransitions<T>(Transition[] transitions) → T
Expand Down Expand Up @@ -2979,6 +2980,7 @@ VisualModifiers Visual { get; init; }
XYFocusKeyboardNavigationMode? XYFocusKeyboardNavigation { get; init; }
bool? IsDragRegion { get; init; }
bool? IsEnabled { get; init; }
bool? IsHitTestVisible { get; init; }
bool? IsTabStop { get; init; }
bool? IsVisible { get; init; }
double? FontSize { get; init; }
Expand Down
2 changes: 2 additions & 0 deletions skills/reactor.api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ T.Immediate<T>() → T
T.InteractionStates<T>(Func<InteractionStatesBuilder, InteractionStatesBuilder> configure, Curve curve = null) → T
T.IsDragRegion<T>(bool? isDragRegion = true) → T
T.IsEnabled<T>(bool enabled = true) → T
T.IsHitTestVisible<T>(bool isHitTestVisible = true) → T
T.IsTabStop<T>(bool isTabStop = true) → T
T.IsVisible<T>(bool isVisible) → T
T.ItemContainerTransitions<T>(Transition[] transitions) → T
Expand Down Expand Up @@ -2979,6 +2980,7 @@ VisualModifiers Visual { get; init; }
XYFocusKeyboardNavigationMode? XYFocusKeyboardNavigation { get; init; }
bool? IsDragRegion { get; init; }
bool? IsEnabled { get; init; }
bool? IsHitTestVisible { get; init; }
bool? IsTabStop { get; init; }
bool? IsVisible { get; init; }
double? FontSize { get; init; }
Expand Down
3 changes: 3 additions & 0 deletions src/Reactor/Core/Element.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,7 @@ internal static bool ModifiersEqual(ElementModifiers? a, ElementModifiers? b)
&& a.HeadingLevel == b.HeadingLevel
&& a.IsDragRegion == b.IsDragRegion
&& a.IsTabStop == b.IsTabStop
&& a.IsHitTestVisible == b.IsHitTestVisible
&& a.TabIndex == b.TabIndex
&& a.AccessKey == b.AccessKey
&& ReferenceEquals(a.XYFocusUpRef, b.XYFocusUpRef)
Expand Down Expand Up @@ -1997,6 +1998,7 @@ public ElementTheme? RequestedTheme
// ── Accessibility — Tier 1 (inline, commonly needed for WCAG AA) ─
public Microsoft.UI.Xaml.Automation.Peers.AutomationHeadingLevel? HeadingLevel { get; init; }
public bool? IsTabStop { get; init; }
public bool? IsHitTestVisible { get; init; }
public int? TabIndex { get; init; }
public string? AccessKey { get; init; }
public Microsoft.UI.Xaml.Input.XYFocusKeyboardNavigationMode? XYFocusKeyboardNavigation { get; init; }
Expand Down Expand Up @@ -2084,6 +2086,7 @@ public ElementModifiers Merge(ElementModifiers other)
HeadingLevel = other.HeadingLevel ?? HeadingLevel,
IsDragRegion = other.IsDragRegion ?? IsDragRegion,
IsTabStop = other.IsTabStop ?? IsTabStop,
IsHitTestVisible = other.IsHitTestVisible ?? IsHitTestVisible,
TabIndex = other.TabIndex ?? TabIndex,
AccessKey = other.AccessKey ?? AccessKey,
XYFocusKeyboardNavigation = other.XYFocusKeyboardNavigation ?? XYFocusKeyboardNavigation,
Expand Down
3 changes: 3 additions & 0 deletions src/Reactor/Core/Reconciler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3832,6 +3832,9 @@ internal void ApplyModifiers(FrameworkElement fe, ElementModifiers? oldM, Elemen
if (m.IsTabStop.HasValue && m.IsTabStop != oldM?.IsTabStop)
fe.IsTabStop = m.IsTabStop.Value;

if (m.IsHitTestVisible.HasValue && m.IsHitTestVisible != oldM?.IsHitTestVisible)
fe.IsHitTestVisible = m.IsHitTestVisible.Value;
Comment on lines +3835 to +3836

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This only writes when HasValue, so it never resets. If an element sets .IsHitTestVisible(false) and a later render drops the modifier, the update path hands ApplyModifiers an empty ElementModifiers (Reconciler.Update.cs:239-240) and this write is skipped — the control stays click-through. The IsDragRegion/AutomationName handlers above have a clear-on-unset branch; this one (like IsTabStop) doesn't. Pooled reuse is fine since ElementPool already clears the DP, but an in-place set→unset on a retained control isn't.

Suggested change
if (m.IsHitTestVisible.HasValue && m.IsHitTestVisible != oldM?.IsHitTestVisible)
fe.IsHitTestVisible = m.IsHitTestVisible.Value;
if (m.IsHitTestVisible.HasValue && m.IsHitTestVisible != oldM?.IsHitTestVisible)
fe.IsHitTestVisible = m.IsHitTestVisible.Value;
else if (!m.IsHitTestVisible.HasValue && oldM?.IsHitTestVisible.HasValue == true)
fe.ClearValue(UIElement.IsHitTestVisibleProperty);

Would also be worth a selftest fixture covering the set→unset round-trip against a live control — the current unit test only asserts the modifier record, not the reconciler write.


if (m.TabIndex.HasValue && m.TabIndex != oldM?.TabIndex && fe is WinUI.Control tabIdxCtrl)
tabIdxCtrl.TabIndex = m.TabIndex.Value;

Expand Down
10 changes: 10 additions & 0 deletions src/Reactor/Elements/ElementExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2517,6 +2517,16 @@ public static T HeadingLevel<T>(this T el, Microsoft.UI.Xaml.Automation.Peers.Au
public static T IsTabStop<T>(this T el, bool isTabStop = true) where T : Element =>
Modify(el, new ElementModifiers { IsTabStop = isTabStop });

/// <summary>
/// Sets UIElement.IsHitTestVisible — whether the element (and its subtree) can be
/// the target of pointer input. Set <c>false</c> for decorative or overlay layers
/// that should let pointer events pass through to whatever is underneath.
/// Works on any element type (Panel, Control, etc.) in WinUI 3.
/// </summary>
/// <example>Border(content).IsHitTestVisible(false)</example>
public static T IsHitTestVisible<T>(this T el, bool isHitTestVisible = true) where T : Element =>
Modify(el, new ElementModifiers { IsHitTestVisible = isHitTestVisible });

/// <summary>
/// Marks how this element participates in a window title bar's drag region
/// (<c>Microsoft.UI.Xaml.Controls.TitleBar.IsDragRegion</c>, WinApp SDK ≥ 2.1.3):
Expand Down
9 changes: 9 additions & 0 deletions tests/Reactor.Tests/ElementExtensionsCoverageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,15 @@ public void IsEnabled_Sets_Modifier()
Assert.True(enabled.Modifiers!.IsEnabled);
}

[Fact]
public void IsHitTestVisible_Sets_Modifier()
{
var el = Button("Y").IsHitTestVisible(false);
Assert.False(el.Modifiers!.IsHitTestVisible);
var hitTestable = Button("Y").IsHitTestVisible();
Assert.True(hitTestable.Modifiers!.IsHitTestVisible);
}

[Fact]
[Obsolete("Tests the deprecated Disabled shim")]
public void Disabled_Shim_Inverts_To_IsEnabled()
Expand Down
Loading