Skip to content

Commit da5cf3e

Browse files
0x5bfayaira2
andauthored
Code Quality: Improved Omnibar UX (#17145)
Co-authored-by: Yair <[email protected]>
1 parent 53806c1 commit da5cf3e

File tree

13 files changed

+208
-155
lines changed

13 files changed

+208
-155
lines changed

src/Files.App.Controls/Omnibar/Omnibar.Events.cs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,18 @@ private void Omnibar_SizeChanged(object sender, SizeChangedEventArgs e)
1414
_textBoxSuggestionsContainerBorder.Width = ActualWidth;
1515
}
1616

17-
private void AutoSuggestBox_GotFocus(object sender, RoutedEventArgs e)
17+
private void AutoSuggestBox_GettingFocus(UIElement sender, GettingFocusEventArgs args)
1818
{
19-
IsFocused = true;
19+
if (args.OldFocusedElement is null)
20+
return;
2021

21-
VisualStateManager.GoToState(CurrentSelectedMode, "Focused", true);
22-
VisualStateManager.GoToState(_textBox, "InputAreaVisible", true);
22+
_previouslyFocusedElement = new(args.OldFocusedElement as UIElement);
23+
}
2324

24-
TryToggleIsSuggestionsPopupOpen(true);
25+
private void AutoSuggestBox_GotFocus(object sender, RoutedEventArgs e)
26+
{
27+
IsFocused = true;
28+
_textBox.SelectAll();
2529
}
2630

2731
private void AutoSuggestBox_LostFocus(object sender, RoutedEventArgs e)
@@ -31,14 +35,6 @@ private void AutoSuggestBox_LostFocus(object sender, RoutedEventArgs e)
3135
return;
3236

3337
IsFocused = false;
34-
35-
if (CurrentSelectedMode?.ContentOnInactive is not null)
36-
{
37-
VisualStateManager.GoToState(CurrentSelectedMode, "CurrentUnfocused", true);
38-
VisualStateManager.GoToState(_textBox, "InputAreaCollapsed", true);
39-
}
40-
41-
TryToggleIsSuggestionsPopupOpen(false);
4238
}
4339

4440
private void AutoSuggestBox_KeyDown(object sender, KeyRoutedEventArgs e)
@@ -77,12 +73,20 @@ private void AutoSuggestBox_KeyDown(object sender, KeyRoutedEventArgs e)
7773
ChooseSuggestionItem(_textBoxSuggestionsListView.SelectedItem);
7874
}
7975
}
80-
else if (e.Key == VirtualKey.Escape && _textBoxSuggestionsPopup.IsOpen)
76+
else if (e.Key == VirtualKey.Escape)
8177
{
8278
e.Handled = true;
8379

84-
RevertTextToUserInput();
85-
_textBoxSuggestionsPopup.IsOpen = false;
80+
if (_textBoxSuggestionsPopup.IsOpen)
81+
{
82+
RevertTextToUserInput();
83+
_textBoxSuggestionsPopup.IsOpen = false;
84+
}
85+
else
86+
{
87+
_previouslyFocusedElement.TryGetTarget(out var previouslyFocusedElement);
88+
previouslyFocusedElement?.Focus(FocusState.Programmatic);
89+
}
8690
}
8791
else
8892
{

src/Files.App.Controls/Omnibar/Omnibar.Properties.cs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,50 @@ public partial class Omnibar
2222
[GeneratedDependencyProperty]
2323
public partial bool IsFocused { get; set; }
2424

25-
partial void OnCurrentSelectedModeChanged(OmnibarMode? newValue)
25+
partial void OnCurrentSelectedModePropertyChanged(DependencyPropertyChangedEventArgs e)
2626
{
27-
CurrentSelectedModeName = newValue?.ModeName;
27+
if (e.NewValue is not OmnibarMode newMode)
28+
return;
29+
30+
ChangeMode(e.OldValue as OmnibarMode, newMode);
31+
CurrentSelectedModeName = newMode.ModeName;
32+
}
33+
34+
partial void OnCurrentSelectedModeNameChanged(string? newValue)
35+
{
36+
if (string.IsNullOrEmpty(newValue) ||
37+
string.IsNullOrEmpty(CurrentSelectedMode?.Name) ||
38+
CurrentSelectedMode.Name.Equals(newValue) ||
39+
Modes is null)
40+
return;
41+
42+
var newMode = Modes.Where(x => x.Name?.Equals(newValue) ?? false).FirstOrDefault();
43+
if (newMode is null)
44+
return;
45+
46+
CurrentSelectedMode = newMode;
2847
}
2948

3049
partial void OnIsFocusedChanged(bool newValue)
3150
{
32-
//_textBox?.Focus(newValue ? FocusState.Programmatic : FocusState.Unfocused);
51+
if (CurrentSelectedMode is null || _textBox is null)
52+
return;
53+
54+
if (newValue)
55+
{
56+
VisualStateManager.GoToState(CurrentSelectedMode, "Focused", true);
57+
VisualStateManager.GoToState(_textBox, "InputAreaVisible", true);
58+
}
59+
else
60+
{
61+
if (CurrentSelectedMode?.ContentOnInactive is not null)
62+
{
63+
VisualStateManager.GoToState(CurrentSelectedMode, "CurrentUnfocused", true);
64+
VisualStateManager.GoToState(_textBox, "InputAreaCollapsed", true);
65+
}
66+
}
67+
68+
TryToggleIsSuggestionsPopupOpen(newValue);
3369
}
3470
}
3571
}

src/Files.App.Controls/Omnibar/Omnibar.cs

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public partial class Omnibar : Control
3131
private string _userInput = string.Empty;
3232
private OmnibarTextChangeReason _textChangeReason = OmnibarTextChangeReason.None;
3333

34+
private WeakReference<UIElement?> _previouslyFocusedElement = new(null);
35+
3436
// Events
3537

3638
public event TypedEventHandler<Omnibar, OmnibarQuerySubmittedEventArgs>? QuerySubmitted;
@@ -67,6 +69,7 @@ protected override void OnApplyTemplate()
6769
PopulateModes();
6870

6971
SizeChanged += Omnibar_SizeChanged;
72+
_textBox.GettingFocus += AutoSuggestBox_GettingFocus;
7073
_textBox.GotFocus += AutoSuggestBox_GotFocus;
7174
_textBox.LostFocus += AutoSuggestBox_LostFocus;
7275
_textBox.KeyDown += AutoSuggestBox_KeyDown;
@@ -104,27 +107,23 @@ public void PopulateModes()
104107
}
105108
}
106109

107-
public void ChangeMode(OmnibarMode modeToExpand, bool shouldFocus = false, bool useTransition = true)
110+
protected void ChangeMode(OmnibarMode? oldMode, OmnibarMode newMode)
108111
{
109-
if (_modesHostGrid is null || Modes is null)
112+
if (_modesHostGrid is null || Modes is null || CurrentSelectedMode is null)
110113
return;
111114

112115
foreach (var mode in Modes)
113116
{
114117
// Add the reposition transition to the all modes
115-
if (useTransition)
116-
{
117-
mode.Transitions = [new RepositionThemeTransition()];
118-
mode.UpdateLayout();
119-
}
120-
121-
mode.OnChangingCurrentMode(false);
118+
mode.Transitions = [new RepositionThemeTransition()];
119+
mode.UpdateLayout();
120+
mode.IsTabStop = true;
122121
}
123122

124-
var index = _modesHostGrid.Children.IndexOf(modeToExpand);
123+
var index = _modesHostGrid.Children.IndexOf(newMode);
125124

126-
if (CurrentSelectedMode is not null)
127-
VisualStateManager.GoToState(CurrentSelectedMode, "Unfocused", true);
125+
if (oldMode is not null)
126+
VisualStateManager.GoToState(oldMode, "Unfocused", true);
128127

129128
// Reset
130129
foreach (var column in _modesHostGrid.ColumnDefinitions)
@@ -134,8 +133,8 @@ public void ChangeMode(OmnibarMode modeToExpand, bool shouldFocus = false, bool
134133
_modesHostGrid.ColumnDefinitions[index].Width = new(1, GridUnitType.Star);
135134

136135
var itemCount = Modes.Count;
137-
var itemIndex = Modes.IndexOf(modeToExpand);
138-
var modeButtonWidth = modeToExpand.ActualWidth;
136+
var itemIndex = Modes.IndexOf(newMode);
137+
var modeButtonWidth = newMode.ActualWidth;
139138
var modeSeparatorWidth = itemCount is not 0 or 1 ? _modesHostGrid.Children[1] is FrameworkElement frameworkElement ? frameworkElement.ActualWidth : 0 : 0;
140139

141140
var leftPadding = (itemIndex + 1) * modeButtonWidth + modeSeparatorWidth * itemIndex;
@@ -144,51 +143,52 @@ public void ChangeMode(OmnibarMode modeToExpand, bool shouldFocus = false, bool
144143
// Set the correct AutoSuggestBox cursor position
145144
AutoSuggestBoxPadding = new(leftPadding, 0, rightPadding, 0);
146145

147-
CurrentSelectedMode = modeToExpand;
148-
149146
_textChangeReason = OmnibarTextChangeReason.ProgrammaticChange;
150-
ChangeTextBoxText(CurrentSelectedMode.Text ?? string.Empty);
151-
152-
// Move cursor of the TextBox to the tail
153-
_textBox.Select(_textBox.Text.Length, 0);
147+
ChangeTextBoxText(newMode.Text ?? string.Empty);
154148

155-
VisualStateManager.GoToState(CurrentSelectedMode, "Focused", true);
156-
CurrentSelectedMode.OnChangingCurrentMode(true);
157-
158-
if (IsFocused)
159-
{
160-
VisualStateManager.GoToState(CurrentSelectedMode, "Focused", true);
161-
VisualStateManager.GoToState(_textBox, "InputAreaVisible", true);
162-
}
163-
else if (CurrentSelectedMode?.ContentOnInactive is not null)
149+
VisualStateManager.GoToState(newMode, "Focused", true);
150+
newMode.IsTabStop = false;
151+
if (newMode.IsAutoFocusEnabled)
164152
{
165-
VisualStateManager.GoToState(CurrentSelectedMode, "CurrentUnfocused", true);
166-
VisualStateManager.GoToState(_textBox, "InputAreaCollapsed", true);
153+
_textBox.Focus(FocusState.Pointer);
167154
}
168155
else
169156
{
170-
VisualStateManager.GoToState(_textBox, "InputAreaVisible", true);
171-
}
172-
173-
if (shouldFocus)
174-
_textBox.Focus(FocusState.Keyboard);
157+
if (IsFocused)
158+
{
159+
VisualStateManager.GoToState(newMode, "Focused", true);
160+
VisualStateManager.GoToState(_textBox, "InputAreaVisible", true);
161+
}
162+
else if (newMode?.ContentOnInactive is not null)
163+
{
164+
VisualStateManager.GoToState(newMode, "CurrentUnfocused", true);
165+
VisualStateManager.GoToState(_textBox, "InputAreaCollapsed", true);
166+
}
167+
else
168+
{
169+
VisualStateManager.GoToState(_textBox, "InputAreaVisible", true);
170+
}
175171

176-
TryToggleIsSuggestionsPopupOpen(IsFocused && CurrentSelectedMode?.SuggestionItemsSource is not null);
172+
TryToggleIsSuggestionsPopupOpen(true);
173+
}
177174

178175
// Remove the reposition transition from the all modes
179-
if (useTransition)
176+
foreach (var mode in Modes)
180177
{
181-
foreach (var mode in Modes)
182-
{
183-
mode.Transitions.Clear();
184-
mode.UpdateLayout();
185-
}
178+
mode.Transitions.Clear();
179+
mode.UpdateLayout();
186180
}
187181
}
188182

183+
internal protected void FocusTextBox()
184+
{
185+
_textBox.Focus(FocusState.Keyboard);
186+
}
187+
189188
public bool TryToggleIsSuggestionsPopupOpen(bool wantToOpen)
190189
{
191-
if (wantToOpen && (!IsFocused || CurrentSelectedMode?.SuggestionItemsSource is null || (CurrentSelectedMode?.SuggestionItemsSource is IList collection && collection.Count is 0)))
190+
if (wantToOpen && (!IsFocused || CurrentSelectedMode?.SuggestionItemsSource is null || (CurrentSelectedMode?.SuggestionItemsSource is IList collection && collection.Count is 0)) ||
191+
_textBoxSuggestionsPopup is null)
192192
return false;
193193

194194
_textBoxSuggestionsPopup.IsOpen = wantToOpen;

src/Files.App.Controls/Omnibar/OmnibarMode.Events.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ private void ModeButton_PointerReleased(object sender, PointerRoutedEventArgs e)
3131
VisualStateManager.GoToState(this, "PointerOver", true);
3232

3333
// Change the current mode
34-
owner.ChangeMode(this);
34+
owner.CurrentSelectedMode = this;
3535
}
3636

3737
private void ModeButton_PointerExited(object sender, PointerRoutedEventArgs e)

src/Files.App.Controls/Omnibar/OmnibarMode.Properties.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ public partial class OmnibarMode
4343
[GeneratedDependencyProperty(DefaultValue = true)]
4444
public partial bool UpdateTextOnSelect { get; set; }
4545

46+
[GeneratedDependencyProperty]
47+
public partial bool IsAutoFocusEnabled { get; set; }
48+
4649
partial void OnTextChanged(string? newValue)
4750
{
4851
if (_ownerRef is null || _ownerRef.TryGetTarget(out var owner) is false)

src/Files.App.Controls/Omnibar/OmnibarMode.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ protected override void OnKeyUp(KeyRoutedEventArgs args)
5454
VisualStateManager.GoToState(this, "PointerPressed", true);
5555

5656
// Change the current mode
57-
owner.ChangeMode(this, true);
57+
owner.CurrentSelectedMode = this;
58+
owner.FocusTextBox();
5859

5960
VisualStateManager.GoToState(this, "PointerNormal", true);
6061
}
@@ -69,19 +70,14 @@ private void OmnibarMode_Loaded(object sender, RoutedEventArgs e)
6970
{
7071
// Set this mode as the current mode if it is the default mode
7172
if (IsDefault && _ownerRef is not null && _ownerRef.TryGetTarget(out var owner))
72-
owner.ChangeMode(this);
73+
owner.CurrentSelectedMode = this;
7374
}
7475

7576
public void SetOwner(Omnibar owner)
7677
{
7778
_ownerRef = new(owner);
7879
}
7980

80-
public void OnChangingCurrentMode(bool isCurrentMode)
81-
{
82-
_modeButton.IsTabStop = !isCurrentMode;
83-
}
84-
8581
public override string ToString()
8682
{
8783
return ModeName ?? string.Empty;

src/Files.App/Actions/Global/EditPathAction.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ namespace Files.App.Actions
55
{
66
internal sealed class EditPathAction : IAction
77
{
8-
private readonly IContentPageContext context;
8+
private readonly IContentPageContext context = Ioc.Default.GetRequiredService<IContentPageContext>();
9+
private readonly IGeneralSettingsService GeneralSettingsService = Ioc.Default.GetRequiredService<IGeneralSettingsService>();
910

1011
public string Label
1112
=> Strings.EditPath.GetLocalizedResource();
@@ -21,13 +22,18 @@ public HotKey SecondHotKey
2122

2223
public EditPathAction()
2324
{
24-
context = Ioc.Default.GetRequiredService<IContentPageContext>();
25+
2526
}
2627

2728
public Task ExecuteAsync(object? parameter = null)
2829
{
2930
if (context.ShellPage is not null)
30-
context.ShellPage.ToolbarViewModel.IsEditModeEnabled = true;
31+
{
32+
if (GeneralSettingsService.EnableOmnibar)
33+
context.ShellPage!.ToolbarViewModel.SwitchToPathMode();
34+
else
35+
context.ShellPage.ToolbarViewModel.IsEditModeEnabled = true;
36+
}
3137

3238
return Task.CompletedTask;
3339
}

src/Files.App/Actions/Global/SearchAction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public SearchAction()
3434

3535
public Task ExecuteAsync(object? parameter = null)
3636
{
37-
context.ShellPage!.ToolbarViewModel.SwitchSearchBoxVisibility();
37+
context.ShellPage!.ToolbarViewModel.SwitchToSearchMode();
3838

3939
return Task.CompletedTask;
4040
}

src/Files.App/Actions/Open/OpenCommandPaletteAction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public OpenCommandPaletteAction()
2323

2424
public Task ExecuteAsync(object? parameter = null)
2525
{
26-
_context.ShellPage?.ToolbarViewModel.OpenCommandPalette();
26+
_context.ShellPage?.ToolbarViewModel.SwitchToCommandPaletteMode();
2727

2828
return Task.CompletedTask;
2929
}

src/Files.App/Data/Contracts/IAddressToolbarViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public interface IAddressToolbarViewModel
4242

4343
public event EventHandler RefreshWidgetsRequested;
4444

45-
public void SwitchSearchBoxVisibility();
45+
public void SwitchToSearchMode();
4646

4747
public ISearchBoxViewModel SearchBox { get; }
4848
}

src/Files.App/UserControls/NavigationToolbar.xaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,9 @@
321321
Grid.Column="1"
322322
x:Load="{x:Bind ViewModel.EnableOmnibar, Mode=OneWay}"
323323
CurrentSelectedMode="{x:Bind ViewModel.OmnibarCurrentSelectedMode, Mode=TwoWay}"
324+
CurrentSelectedModeName="{x:Bind ViewModel.OmnibarCurrentSelectedModeName, Mode=TwoWay}"
324325
IsFocused="{x:Bind ViewModel.IsOmnibarFocused, Mode=TwoWay}"
325326
QuerySubmitted="Omnibar_QuerySubmitted"
326-
SuggestionChosen="Omnibar_SuggestionChosen"
327327
TextChanged="Omnibar_TextChanged">
328328

329329
<controls:OmnibarMode
@@ -367,6 +367,7 @@
367367
x:Name="OmnibarCommandPaletteMode"
368368
IconOnActive="{controls:ThemedIconMarkup Style={StaticResource App.ThemedIcons.Omnibar.Commands}, IsFilled=True}"
369369
IconOnInactive="{controls:ThemedIconMarkup Style={StaticResource App.ThemedIcons.Omnibar.Commands}, IconType=Outline}"
370+
IsAutoFocusEnabled="True"
370371
ModeName="{x:Bind Commands.OpenCommandPalette.LabelWithHotKey, Mode=OneWay}"
371372
PlaceholderText="{helpers:ResourceString Name=OmnibarCommandPaletteModeTextPlaceholder}"
372373
SuggestionItemsSource="{x:Bind ViewModel.OmnibarCommandPaletteModeSuggestionItems, Mode=OneWay}"
@@ -418,6 +419,7 @@
418419
x:Name="OmnibarSearchMode"
419420
IconOnActive="{controls:ThemedIconMarkup Style={StaticResource App.ThemedIcons.Omnibar.Search}, IsFilled=True}"
420421
IconOnInactive="{controls:ThemedIconMarkup Style={StaticResource App.ThemedIcons.Omnibar.Search}, IconType=Outline}"
422+
IsAutoFocusEnabled="True"
421423
ModeName="{x:Bind Commands.Search.LabelWithHotKey, Mode=OneWay}"
422424
PlaceholderText="{helpers:ResourceString Name=OmnibarSearchModeTextPlaceholder}" />
423425

0 commit comments

Comments
 (0)