Skip to content
22 changes: 8 additions & 14 deletions src/Files.App.Controls/Omnibar/Omnibar.Events.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using Microsoft.UI.Input;
using Microsoft.UI.Xaml.Input;
using Windows.System;
using Windows.UI.Core;

namespace Files.App.Controls
{
Expand All @@ -28,7 +26,7 @@ private void AutoSuggestBox_GettingFocus(UIElement sender, GettingFocusEventArgs

private void AutoSuggestBox_LosingFocus(UIElement sender, LosingFocusEventArgs args)
{
if (IsModeButtonPressed)
if (args.NewFocusedElement is Button && IsModeButtonPressed)
{
IsModeButtonPressed = false;
args.TryCancel();
Expand All @@ -47,12 +45,16 @@ private void AutoSuggestBox_GotFocus(object sender, RoutedEventArgs e)
private void AutoSuggestBox_LostFocus(object sender, RoutedEventArgs e)
{
// TextBox still has focus if the context menu for selected text is open
if (_textBox.ContextFlyout.IsOpen)
var element = Microsoft.UI.Xaml.Input.FocusManager.GetFocusedElement(this.XamlRoot);
if (element is FlyoutBase or Popup)
return;

GlobalHelper.WriteDebugStringForOmnibar("The TextBox lost the focus.");

IsFocused = false;

// Reset to the default mode when Omnibar loses focus
CurrentSelectedMode = Modes?.FirstOrDefault();
}

private async void AutoSuggestBox_KeyDown(object sender, KeyRoutedEventArgs e)
Expand Down Expand Up @@ -112,16 +114,6 @@ private async void AutoSuggestBox_KeyDown(object sender, KeyRoutedEventArgs e)
previouslyFocusedElement?.Focus(FocusState.Programmatic);
}
}
else if (e.Key == VirtualKey.Tab && !InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down))
{
GlobalHelper.WriteDebugStringForOmnibar("The TextBox accepted the Tab key.");

// Focus on inactive content when pressing Tab instead of moving to the next control in the tab order
e.Handled = true;
IsFocused = false;
await Task.Delay(15);
CurrentSelectedMode?.ContentOnInactive?.Focus(FocusState.Keyboard);
}
else
{
_textChangeReason = OmnibarTextChangeReason.UserInput;
Expand All @@ -141,6 +133,8 @@ private void AutoSuggestBox_TextChanged(object sender, TextChangedEventArgs e)
_textChangeReason = OmnibarTextChangeReason.UserInput;
_userInput = _textBox.Text;
}
else if (_textChangeReason is OmnibarTextChangeReason.ProgrammaticChange)
_textBox.SelectAll();

TextChanged?.Invoke(this, new(CurrentSelectedMode, _textChangeReason));

Expand Down
2 changes: 1 addition & 1 deletion src/Files.App.Controls/Omnibar/Omnibar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ protected void ChangeMode(OmnibarMode? oldMode, OmnibarMode newMode)
// Add the reposition transition to the all modes
mode.Transitions = [new RepositionThemeTransition()];
mode.UpdateLayout();
mode.IsTabStop = true;
mode.IsTabStop = false;
}

var index = _modesHostGrid.Children.IndexOf(newMode);
Expand Down
30 changes: 10 additions & 20 deletions src/Files.App.Controls/Omnibar/Omnibar.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -117,33 +117,28 @@
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Stretch" />

<Setter Property="IsTabStop" Value="True" />
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />

<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:OmnibarMode">
<Grid
x:Name="PART_RootGrid"
Height="{TemplateBinding Height}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Background="{TemplateBinding Background}"
TabFocusNavigation="Local">
Background="{TemplateBinding Background}">
<!-- Mode Button -->
<Border
<Button
x:Name="PART_ModeButton"
Width="{StaticResource OmnibarModeDefaultClickAreaWidth}"
Margin="1"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderThickness="0"
CornerRadius="{TemplateBinding CornerRadius}"
IsTabStop="True"
ToolTipService.ToolTip="{Binding ModeName, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
UseSystemFocusVisuals="{StaticResource UseSystemFocusVisuals}">
<Border.BackgroundTransition>
<BrushTransition Duration="0:0:0.083" />
</Border.BackgroundTransition>
ToolTipService.ToolTip="{Binding ModeName, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}">
<Button.Resources>
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{ThemeResource SubtleFillColorSecondary}" />
<SolidColorBrush x:Key="ButtonBackgroundPressed" Color="{ThemeResource SubtleFillColorTertiary}" />
</Button.Resources>

<Grid>
<ContentPresenter
Expand All @@ -160,7 +155,7 @@
Content="{Binding IconOnActive, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
Visibility="Collapsed" />
</Grid>
</Border>
</Button>

<ContentPresenter
x:Name="PART_InactiveContent"
Expand All @@ -174,14 +169,9 @@

<VisualStateGroup x:Name="PointerStates">
<VisualState x:Name="PointerNormal" />
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="PART_ModeButton.Background" Value="{ThemeResource SubtleFillColorSecondaryBrush}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver" />
<VisualState x:Name="PointerPressed">
<VisualState.Setters>
<Setter Target="PART_ModeButton.Background" Value="{ThemeResource SubtleFillColorTertiaryBrush}" />
<Setter Target="PART_ModeButtonInactiveIconPresenter.Visibility" Value="Collapsed" />
<Setter Target="PART_ModeButtonActiveIconPresenter.Visibility" Value="Visible" />
</VisualState.Setters>
Expand Down
16 changes: 10 additions & 6 deletions src/Files.App.Controls/Omnibar/OmnibarMode.Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ private void ModeButton_PointerReleased(object sender, PointerRoutedEventArgs e)
GlobalHelper.WriteDebugStringForOmnibar($"The mouse pointer has been unpressed from the UI area of this Mode ({this})");

VisualStateManager.GoToState(this, "PointerOver", true);

owner.IsModeButtonPressed = true;

// Change the current mode
owner.CurrentSelectedMode = this;
}

private void ModeButton_PointerExited(object sender, PointerRoutedEventArgs e)
Expand All @@ -48,5 +43,14 @@ private void ModeButton_PointerExited(object sender, PointerRoutedEventArgs e)

VisualStateManager.GoToState(this, "PointerNormal", true);
}

private void ModeButton_Click(object sender, RoutedEventArgs e)
{
if (_ownerRef is null || _ownerRef.TryGetTarget(out var owner) is false || owner.CurrentSelectedMode == this)
return;

owner.IsModeButtonPressed = true;
owner.CurrentSelectedMode = this;
}
}
}
}
5 changes: 3 additions & 2 deletions src/Files.App.Controls/Omnibar/OmnibarMode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public partial class OmnibarMode : ItemsControl

private WeakReference<Omnibar>? _ownerRef;

private Border _modeButton = null!;
private Button _modeButton = null!;

// Constructor

Expand All @@ -33,14 +33,15 @@ protected override void OnApplyTemplate()
{
base.OnApplyTemplate();

_modeButton = GetTemplateChild(TemplatePartName_ModeButton) as Border
_modeButton = GetTemplateChild(TemplatePartName_ModeButton) as Button
?? throw new MissingFieldException($"Could not find {TemplatePartName_ModeButton} in the given {nameof(OmnibarMode)}'s style.");

Loaded += OmnibarMode_Loaded;
_modeButton.PointerEntered += ModeButton_PointerEntered;
_modeButton.PointerPressed += ModeButton_PointerPressed;
_modeButton.PointerReleased += ModeButton_PointerReleased;
_modeButton.PointerExited += ModeButton_PointerExited;
_modeButton.Click += ModeButton_Click;

GlobalHelper.WriteDebugStringForOmnibar($"The template and the events of the Omnibar Mode ({this}) have been initialized.");
}
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/UserControls/NavigationToolbar.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@
x:Load="{x:Bind ViewModel.EnableOmnibar, Mode=OneWay}"
CurrentSelectedModeName="{x:Bind ViewModel.OmnibarCurrentSelectedModeName, Mode=TwoWay}"
IsFocused="{x:Bind ViewModel.IsOmnibarFocused, Mode=TwoWay}"
LostFocus="Omnibar_LostFocus"
ModeChanged="Omnibar_ModeChanged"
PreviewKeyDown="Omnibar_PreviewKeyDown"
QuerySubmitted="Omnibar_QuerySubmitted"
TextChanged="Omnibar_TextChanged">
Expand Down
20 changes: 15 additions & 5 deletions src/Files.App/UserControls/NavigationToolbar.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.UI.Xaml.Navigation;
using Windows.AI.Actions.Hosting;
using Windows.System;
using Windows.UI.Core;

namespace Files.App.UserControls
{
Expand Down Expand Up @@ -427,22 +428,31 @@ private void BreadcrumbBar_ItemDropDownFlyoutClosed(object sender, BreadcrumbBar
e.Flyout.Items.Clear();
}

private void Omnibar_LostFocus(object sender, RoutedEventArgs e)
private void Omnibar_ModeChanged(object sender, OmnibarModeChangedEventArgs e)
{
// Reset the command palette text when switching modes
if (Omnibar.CurrentSelectedMode == OmnibarCommandPaletteMode)
{
Omnibar.CurrentSelectedMode = OmnibarPathMode;
ViewModel.OmnibarCommandPaletteModeText = string.Empty;
}
}

private void Omnibar_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
private async void Omnibar_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key is VirtualKey.Escape)
{
Omnibar.IsFocused = false;
(MainPageViewModel.SelectedTabItem?.TabItemContent as Control)?.Focus(FocusState.Programmatic);
}
else if (e.Key is VirtualKey.Tab && Omnibar.IsFocused && !InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down))
{
var currentSelectedMode = Omnibar.CurrentSelectedMode;
Omnibar.IsFocused = false;
await Task.Delay(15);

if (currentSelectedMode == OmnibarPathMode)
BreadcrumbBar.Focus(FocusState.Keyboard);
else if (currentSelectedMode == OmnibarCommandPaletteMode)
OmnibarCommandPaletteMode.Focus(FocusState.Keyboard);
}
}

private void NavigationButtonOverflowFlyoutButton_LosingFocus(UIElement sender, LosingFocusEventArgs args)
Expand Down
Loading