mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-09 20:57:22 +02:00
CmdPal: Auto-focus first content (#39414)
This lets us auto-focus the first input on a `IFormContent`, if there's only one `IContent` on the page. This dramatically improves the usability for forms, since we'll immediately put focus into them Closes #38436
This commit is contained in:
@@ -79,6 +79,9 @@ public partial class ContentPageViewModel : PageViewModel, ICommandBarContext
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var oneContent = newContent.Count == 1;
|
||||||
|
newContent.ForEach(c => c.OnlyControlOnPage = oneContent);
|
||||||
|
|
||||||
// Now, back to a UI thread to update the observable collection
|
// Now, back to a UI thread to update the observable collection
|
||||||
DoOnUiThread(
|
DoOnUiThread(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ namespace Microsoft.CmdPal.UI.ViewModels;
|
|||||||
public abstract partial class ContentViewModel(WeakReference<IPageContext> context) :
|
public abstract partial class ContentViewModel(WeakReference<IPageContext> context) :
|
||||||
ExtensionObjectViewModel(context)
|
ExtensionObjectViewModel(context)
|
||||||
{
|
{
|
||||||
|
public bool OnlyControlOnPage { get; internal set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
using AdaptiveCards.ObjectModel.WinUI3;
|
using AdaptiveCards.ObjectModel.WinUI3;
|
||||||
using AdaptiveCards.Rendering.WinUI3;
|
using AdaptiveCards.Rendering.WinUI3;
|
||||||
using Microsoft.CmdPal.UI.ViewModels;
|
using Microsoft.CmdPal.UI.ViewModels;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
|
||||||
namespace Microsoft.CmdPal.UI.Controls;
|
namespace Microsoft.CmdPal.UI.Controls;
|
||||||
|
|
||||||
@@ -96,11 +98,65 @@ public sealed partial class ContentFormControl : UserControl
|
|||||||
if (_renderedCard.FrameworkElement != null)
|
if (_renderedCard.FrameworkElement != null)
|
||||||
{
|
{
|
||||||
ContentGrid.Children.Add(_renderedCard.FrameworkElement);
|
ContentGrid.Children.Add(_renderedCard.FrameworkElement);
|
||||||
|
|
||||||
|
// Use the Loaded event to ensure we focus after the card is in the visual tree
|
||||||
|
_renderedCard.FrameworkElement.Loaded += OnFrameworkElementLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderedCard.Action += Rendered_Action;
|
_renderedCard.Action += Rendered_Action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnFrameworkElementLoaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// Unhook the event handler to avoid multiple registrations
|
||||||
|
if (sender is FrameworkElement element)
|
||||||
|
{
|
||||||
|
element.Loaded -= OnFrameworkElementLoaded;
|
||||||
|
|
||||||
|
if (!ViewModel?.OnlyControlOnPage ?? true)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus on the first focusable element asynchronously to ensure the visual tree is fully built
|
||||||
|
element.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () =>
|
||||||
|
{
|
||||||
|
var focusableElement = FindFirstFocusableElement(element);
|
||||||
|
focusableElement?.Focus(FocusState.Programmatic);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Control? FindFirstFocusableElement(DependencyObject parent)
|
||||||
|
{
|
||||||
|
var childCount = VisualTreeHelper.GetChildrenCount(parent);
|
||||||
|
|
||||||
|
// Process children first (depth-first search)
|
||||||
|
for (var i = 0; i < childCount; i++)
|
||||||
|
{
|
||||||
|
var child = VisualTreeHelper.GetChild(parent, i);
|
||||||
|
|
||||||
|
// If the child is a focusable control like TextBox, ComboBox, etc.
|
||||||
|
if (child is Control control &&
|
||||||
|
control.IsEnabled &&
|
||||||
|
control.IsTabStop &&
|
||||||
|
control.Visibility == Visibility.Visible &&
|
||||||
|
control.AllowFocusOnInteraction)
|
||||||
|
{
|
||||||
|
return control;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively check children
|
||||||
|
var result = FindFirstFocusableElement(child);
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void Rendered_Action(RenderedAdaptiveCard sender, AdaptiveActionEventArgs args) =>
|
private void Rendered_Action(RenderedAdaptiveCard sender, AdaptiveActionEventArgs args) =>
|
||||||
ViewModel?.HandleSubmit(args.Action, args.Inputs.AsJson());
|
ViewModel?.HandleSubmit(args.Action, args.Inputs.AsJson());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user