mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-07-02 08:28:55 +02:00
Compare commits
1 Commits
powerscrip
...
jearleycha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af71d4969c |
@@ -216,6 +216,7 @@
|
||||
<ItemsRepeater
|
||||
x:Name="ProvidersRepeater"
|
||||
x:Load="{x:Bind viewModel.Extensions.HasResults, Mode=OneWay}"
|
||||
GettingFocus="ProvidersRepeater_GettingFocus"
|
||||
ItemsSource="{x:Bind viewModel.Extensions.FilteredProviders, Mode=OneWay}"
|
||||
Layout="{StaticResource VerticalStackLayout}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
@@ -224,6 +225,7 @@
|
||||
Click="SettingsCard_Click"
|
||||
DataContext="{x:Bind}"
|
||||
Description="{x:Bind ExtensionSubtext, Mode=OneWay}"
|
||||
GotFocus="SettingsCard_GotFocus"
|
||||
Header="{x:Bind DisplayName, Mode=OneWay}"
|
||||
IsClickEnabled="True">
|
||||
<controls:SettingsCard.HeaderIcon>
|
||||
|
||||
@@ -19,6 +19,8 @@ public sealed partial class ExtensionsPage : Page
|
||||
private readonly TaskScheduler _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
|
||||
|
||||
private readonly SettingsViewModel? viewModel;
|
||||
private WeakReference<SettingsCard>? _lastFocusedCard;
|
||||
private bool _isRestoringFocus;
|
||||
|
||||
public ExtensionsPage()
|
||||
{
|
||||
@@ -58,4 +60,134 @@ public sealed partial class ExtensionsPage : Page
|
||||
Logger.LogError("Error when showing FallbackRankerDialog", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsCard_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Track when a SettingsCard gets focus (not its children like ToggleSwitch)
|
||||
if (sender is SettingsCard card && ReferenceEquals(e.OriginalSource, card))
|
||||
{
|
||||
_lastFocusedCard = new WeakReference<SettingsCard>(card);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProvidersRepeater_GettingFocus(UIElement sender, GettingFocusEventArgs args)
|
||||
{
|
||||
// Prevent recursive focus handling
|
||||
if (_isRestoringFocus)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Only intervene when focus is coming into the ItemsRepeater from outside
|
||||
if (args.OldFocusedElement != null && IsElementInsideRepeater(args.OldFocusedElement))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SettingsCard? targetCard = null;
|
||||
|
||||
// Try to restore focus to the last focused card
|
||||
if (_lastFocusedCard?.TryGetTarget(out var lastCard) == true && IsCardValid(lastCard))
|
||||
{
|
||||
targetCard = lastCard;
|
||||
}
|
||||
|
||||
// If no valid last focused card, focus the first one
|
||||
if (targetCard == null)
|
||||
{
|
||||
targetCard = GetFirstSettingsCard();
|
||||
}
|
||||
|
||||
// Use async focus restoration to avoid crashes during focus transition
|
||||
if (targetCard != null)
|
||||
{
|
||||
args.TryCancel();
|
||||
_ = RestoreFocusAsync(targetCard);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RestoreFocusAsync(SettingsCard card)
|
||||
{
|
||||
if (_isRestoringFocus)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_isRestoringFocus = true;
|
||||
|
||||
// Verify the card is still valid before focusing
|
||||
if (IsCardValid(card))
|
||||
{
|
||||
// Bring the element into view before focusing
|
||||
card.StartBringIntoView(new BringIntoViewOptions
|
||||
{
|
||||
AnimationDesired = true,
|
||||
VerticalAlignmentRatio = 0.5, // Center vertically
|
||||
});
|
||||
|
||||
_ = card.Focus(FocusState.Keyboard);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error restoring focus to SettingsCard", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isRestoringFocus = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsElementInsideRepeater(object element)
|
||||
{
|
||||
if (element is not DependencyObject depObj)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var parent = depObj;
|
||||
while (parent != null)
|
||||
{
|
||||
if (ReferenceEquals(parent, ProvidersRepeater))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
parent = Microsoft.UI.Xaml.Media.VisualTreeHelper.GetParent(parent);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsCardValid(SettingsCard card)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if the card is in the visual tree and can receive focus
|
||||
return IsElementInsideRepeater(card) && card.IsLoaded;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private SettingsCard? GetFirstSettingsCard()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ProvidersRepeater.TryGetElement(0) is FrameworkElement firstElement)
|
||||
{
|
||||
return firstElement as SettingsCard;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error getting first SettingsCard", ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user