fix: require live-tree membership for UIElement focusability by morning4coffe-dev · Pull Request #23645 · unoplatform/uno · GitHub
Skip to content

fix: require live-tree membership for UIElement focusability#23645

Draft
morning4coffe-dev wants to merge 1 commit into
masterfrom
dev/doti/focus-detached-element-parity
Draft

fix: require live-tree membership for UIElement focusability#23645
morning4coffe-dev wants to merge 1 commit into
masterfrom
dev/doti/focus-detached-element-parity

Conversation

@morning4coffe-dev

Copy link
Copy Markdown
Member

GitHub Issue: closes #

PR Type:

🐞 Bugfix

What changed? 🚀

TEMP, looking into implementing is active.

WinUI's CUIElement::IsFocusable requires IsActive() (element in the live visual tree), but Uno's port had that check commented out. This let Focus() and FindFirstFocusableElement succeed against detached elements, so a stale deferred focus call (e.g. from app code reacting to a view that already left the tree) could steal focus out of a TabFocusNavigation="Cycle" scope with no way back in.

Add IsActiveOrAttachedUnderActiveAncestor(), checked under CROSSRUNTIME in UIElement.IsFocusable and Hyperlink.IsFocusable. The ancestor walk (not a bare IsActiveInVisualTree check) is required because Uno's enhanced lifecycle activates freshly attached children one tick after they get a live parent, while WinUI enters them synchronously - a bare check regresses same-tick focus scenarios like a ComboBox focusing its item container right when the dropdown opens.

PR Checklist ✅

WinUI's CUIElement::IsFocusable requires IsActive() (element in the
live visual tree), but Uno's port had that check commented out. This
let Focus() and FindFirstFocusableElement succeed against detached
elements, so a stale deferred focus call (e.g. from app code reacting
to a view that already left the tree) could steal focus out of a
TabFocusNavigation="Cycle" scope with no way back in.

Add IsActiveOrAttachedUnderActiveAncestor(), checked under
__CROSSRUNTIME__ in UIElement.IsFocusable and Hyperlink.IsFocusable.
The ancestor walk (not a bare IsActiveInVisualTree check) is required
because Uno's enhanced lifecycle activates freshly attached children
one tick after they get a live parent, while WinUI enters them
synchronously - a bare check regresses same-tick focus scenarios like
a ComboBox focusing its item container right when the dropdown opens.
Copilot AI review requested due to automatic review settings July 4, 2026 12:20
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

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.

Pull request overview

This PR aligns Uno’s focusability behavior with WinUI by requiring live-tree membership (or being newly attached under an active ancestor) for an element to be considered focusable on crossruntime targets, preventing detached elements from stealing focus or being returned by focus-candidate searches.

Changes:

  • Gate UIElement.IsFocusable with a new crossruntime live-tree check (IsActiveOrAttachedUnderActiveAncestor()).
  • Apply the same live-tree gating to Hyperlink.IsFocusable() via the containing FrameworkElement.
  • Add runtime tests covering focus and focus-candidate behavior for detached subtrees, including a TabFocusNavigation=Cycle regression scenario.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/Uno.UI/UI/Xaml/UIElement.FocusMixins.cs Adds crossruntime live-tree/active-ancestor gating to IsFocusable and introduces IsActiveOrAttachedUnderActiveAncestor().
src/Uno.UI/UI/Xaml/Documents/Hyperlink.mux.cs Ensures hyperlinks respect the same crossruntime live-tree focusability rules via the containing element.
src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Input/Given_FocusManager_DetachedElements.cs Adds WinUI-parity runtime tests validating focus behavior for detached elements/subtrees and tab-cycling scope integrity.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Uno.UI/UI/Xaml/Documents/Hyperlink.mux.cs
@unodevops

Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-23645/wasm-skia-net9/index.html

@unodevops

Copy link
Copy Markdown
Contributor

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.

3 participants