diff --git a/src/Files.App.Controls/Omnibar/Omnibar.Events.cs b/src/Files.App.Controls/Omnibar/Omnibar.Events.cs
index c3065f4780de..5c2ce55253f0 100644
--- a/src/Files.App.Controls/Omnibar/Omnibar.Events.cs
+++ b/src/Files.App.Controls/Omnibar/Omnibar.Events.cs
@@ -134,13 +134,13 @@ private void AutoSuggestBox_TextChanged(object sender, TextChangedEventArgs e)
if (string.Compare(_textBox.Text, CurrentSelectedMode!.Text, StringComparison.OrdinalIgnoreCase) is not 0)
CurrentSelectedMode!.Text = _textBox.Text;
- // UpdateSuggestionListView();
-
if (_textChangeReason is OmnibarTextChangeReason.ProgrammaticChange)
_textBox.SelectAll();
else
{
_userInput = _textBox.Text;
+ if (_userInput.Length == 0)
+ _textChangeReason = OmnibarTextChangeReason.UserInput;
}
TextChanged?.Invoke(this, new(CurrentSelectedMode, _textChangeReason));
diff --git a/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs b/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs
index f5eb7895a255..ef5096114b6b 100644
--- a/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs
+++ b/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs
@@ -3,97 +3,60 @@
using Files.App.Controls;
using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Media;
namespace Files.App.Data.Items
{
[Obsolete("Remove once Omnibar goes out of experimental.")]
- public sealed partial class NavigationBarSuggestionItem : ObservableObject, IOmnibarTextMemberPathProvider
+ public sealed partial class NavigationBarSuggestionItem : IOmnibarTextMemberPathProvider
{
- private ImageSource? _ActionIconSource;
- public ImageSource? ActionIconSource { get => _ActionIconSource; set => SetProperty(ref _ActionIconSource, value); }
+ public IRichCommand? Command { get; }
- private Style? _ThemedIconStyle;
- public Style? ThemedIconStyle { get => _ThemedIconStyle; set => SetProperty(ref _ThemedIconStyle, value); }
+ public Style? ThemedIconStyle { get; }
- private string? _Glyph;
- public string? Glyph { get => _Glyph; set => SetProperty(ref _Glyph, value); }
+ public string? Glyph { get; }
- private string? _Text;
- public string? Text { get => _Text; set => SetProperty(ref _Text, value); }
+ public string Text { get; }
- private string? _PrimaryDisplay;
- public string? PrimaryDisplay
- {
- get => _PrimaryDisplay;
- set
- {
- if (SetProperty(ref _PrimaryDisplay, value))
- UpdatePrimaryDisplay();
- }
- }
+ public string? PrimaryDisplayPreMatched { get; }
- private string? _SearchText;
- public string? SearchText
- {
- get => _SearchText;
- set
- {
- if (SetProperty(ref _SearchText, value))
- UpdatePrimaryDisplay();
- }
- }
+ public string? PrimaryDisplayMatched { get; }
- private string? _PrimaryDisplayPreMatched;
- public string? PrimaryDisplayPreMatched
- {
- get => _PrimaryDisplayPreMatched;
- private set => SetProperty(ref _PrimaryDisplayPreMatched, value);
- }
+ public string? PrimaryDisplayPostMatched { get; }
- private string? _PrimaryDisplayMatched;
- public string? PrimaryDisplayMatched
- {
- get => _PrimaryDisplayMatched;
- private set => SetProperty(ref _PrimaryDisplayMatched, value);
- }
+ public HotKeyCollection HotKeys { get; }
- private string? _PrimaryDisplayPostMatched;
- public string? PrimaryDisplayPostMatched
+ public NavigationBarSuggestionItem(string? searchText, IRichCommand? command)
{
- get => _PrimaryDisplayPostMatched;
- private set => SetProperty(ref _PrimaryDisplayPostMatched, value);
- }
+ Command = command;
- private HotKeyCollection _HotKeys = new();
- public HotKeyCollection HotKeys
- {
- get => _HotKeys;
- set => SetProperty(ref _HotKeys, value);
- }
+ if (command is null)
+ {
+ Text = string.Format(Strings.NoCommandsFound.GetLocalizedResource(), searchText);
+ }
+ else
+ {
+ ThemedIconStyle = command.ThemedIconStyle;
+ Glyph = command.Glyph.BaseGlyph;
+ Text = command.Description;
+ HotKeys = command.HotKeys;
+ }
- private void UpdatePrimaryDisplay()
- {
- if (SearchText is null || PrimaryDisplay is null)
+ if (searchText is null)
{
- PrimaryDisplayPreMatched = null;
- PrimaryDisplayMatched = PrimaryDisplay;
- PrimaryDisplayPostMatched = null;
+ PrimaryDisplayMatched = Text;
}
else
{
- var index = PrimaryDisplay.IndexOf(SearchText, StringComparison.OrdinalIgnoreCase);
+ var index = Text.IndexOf(searchText, StringComparison.OrdinalIgnoreCase);
if (index < 0)
{
- PrimaryDisplayPreMatched = PrimaryDisplay;
- PrimaryDisplayMatched = null;
- PrimaryDisplayPostMatched = null;
+ PrimaryDisplayPreMatched = Text;
}
else
{
- PrimaryDisplayPreMatched = PrimaryDisplay.Substring(0, index);
- PrimaryDisplayMatched = PrimaryDisplay.Substring(index, SearchText.Length);
- PrimaryDisplayPostMatched = PrimaryDisplay.Substring(index + SearchText.Length);
+ PrimaryDisplayPreMatched = Text.Substring(0, index);
+ PrimaryDisplayMatched = Text.Substring(index, searchText.Length);
+ PrimaryDisplayPostMatched = Text.Substring(index + searchText.Length);
}
}
}
@@ -103,8 +66,6 @@ public string GetTextMemberPath(string textMemberPath)
return textMemberPath switch
{
nameof(Text) => Text,
- nameof(PrimaryDisplay) => PrimaryDisplay,
- nameof(SearchText) => SearchText,
_ => string.Empty
};
}
diff --git a/src/Files.App/UserControls/NavigationToolbar.xaml b/src/Files.App/UserControls/NavigationToolbar.xaml
index 3ec89274a576..fa070de7be8d 100644
--- a/src/Files.App/UserControls/NavigationToolbar.xaml
+++ b/src/Files.App/UserControls/NavigationToolbar.xaml
@@ -309,11 +309,10 @@
-
+ Visibility="{x:Bind Glyph, Converter={StaticResource NullToVisibilityCollapsedConverter}, Mode=OneTime}">
+
-
-
+
@@ -324,7 +323,7 @@
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap">
-
+
@@ -332,7 +331,7 @@
x:Name="RightAlignedKeyboardShortcut"
Grid.Column="2"
VerticalAlignment="Center"
- HotKeys="{x:Bind HotKeys, Mode=OneWay}" />
+ HotKeys="{x:Bind HotKeys, Mode=OneTime}" />
diff --git a/src/Files.App/UserControls/NavigationToolbar.xaml.cs b/src/Files.App/UserControls/NavigationToolbar.xaml.cs
index 0311b57e320a..fc8ec5ad6da5 100644
--- a/src/Files.App/UserControls/NavigationToolbar.xaml.cs
+++ b/src/Files.App/UserControls/NavigationToolbar.xaml.cs
@@ -183,25 +183,24 @@ private async void Omnibar_QuerySubmitted(Omnibar sender, OmnibarQuerySubmittedE
// Command palette mode
else if (mode == OmnibarCommandPaletteMode)
{
- var item = args.Item as NavigationBarSuggestionItem;
+ IRichCommand? command = null;
- // Try invoking built-in command
- foreach (var command in Commands)
- {
- if (command == Commands.None)
- continue;
-
- if (!string.Equals(command.Description, item?.Text, StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(command.Description, args.Text, StringComparison.OrdinalIgnoreCase))
- continue;
+ if (args.Item is NavigationBarSuggestionItem item)
+ command = item.Command;
+ else
+ // Maybe the user typed the full command description without selecting a suggestion, so search for it
+ foreach (var c in Commands)
+ if (c != Commands.None && string.Equals(c.Description, args.Text, StringComparison.OrdinalIgnoreCase))
+ {
+ command = c;
+ break;
+ }
+ if (command is not null)
await command.ExecuteAsync();
- ContentPageContext.ShellPage!.PaneHolder.FocusActivePane();
- return;
- }
-
- await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidCommand.GetLocalizedResource(),
- string.Format(Strings.InvalidCommandContent.GetLocalizedResource(), args.Text));
+ else
+ await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidCommand.GetLocalizedResource(),
+ string.Format(Strings.InvalidCommandContent.GetLocalizedResource(), args.Text));
ContentPageContext.ShellPage!.PaneHolder.FocusActivePane();
return;
diff --git a/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs
index 901f23a0f0c1..df3372307651 100644
--- a/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs
+++ b/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs
@@ -5,7 +5,6 @@
using Files.App.Controls;
using Files.App.ViewModels.Settings;
using Files.Shared.Helpers;
-using Microsoft.Extensions.Logging;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -1030,12 +1029,12 @@ public async Task PopulateOmnibarSuggestionsForPathMode()
void AddNoResultsItem()
{
PathModeSuggestionItems.Clear();
-
+
// Use null-safe access to avoid NullReferenceException during app lifecycle transitions
var workingDirectory = string.IsNullOrEmpty(ContentPageContext.ShellPage?.ShellViewModel?.WorkingDirectory)
? Constants.UserEnvironmentPaths.HomePath
: ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory;
-
+
PathModeSuggestionItems.Add(new(
workingDirectory,
Strings.NavigationToolbarVisiblePathNoResults.GetLocalizedResource()));
@@ -1044,97 +1043,54 @@ void AddNoResultsItem()
public async Task PopulateOmnibarSuggestionsForCommandPaletteMode()
{
- var (suggestionsToProcess, commandsToProcess) = await Task.Run(() =>
- {
- var suggestions = new List();
+ string? omnibarCommandPaletteModeText = OmnibarCommandPaletteModeText;
- var commandsData = Commands
- .Where(command => command.IsAccessibleGlobally
- && (command.Description.Contains(OmnibarCommandPaletteModeText, StringComparison.OrdinalIgnoreCase)
- || command.Code.ToString().Contains(OmnibarCommandPaletteModeText, StringComparison.OrdinalIgnoreCase)))
- .Where(command => command.Description != Commands.OpenCommandPalette.Description.ToString())
- .ToList();
-
- return (suggestions, commandsData);
+ var commandsToProcess = await Task.Run(() =>
+ {
+ if (string.IsNullOrEmpty(OmnibarCommandPaletteModeText))
+ return Commands.Where(static command => command.IsAccessibleGlobally && command.Code != CommandCodes.OpenCommandPalette).ToList();
+ else
+ return Commands
+ .Where(command => command.IsAccessibleGlobally
+ && (command.Description.Contains(OmnibarCommandPaletteModeText, StringComparison.OrdinalIgnoreCase)
+ || command.Code.ToString().Contains(OmnibarCommandPaletteModeText, StringComparison.OrdinalIgnoreCase))
+ && command.Code != CommandCodes.OpenCommandPalette)
+ .ToList();
});
- var newSuggestions = new List(suggestionsToProcess);
- int processedCount = 0;
+ if (OmnibarCommandPaletteModeText != omnibarCommandPaletteModeText)
+ // We're still processing old input, user has typed in the mean time.
+ return;
- foreach (var command in commandsToProcess)
- {
- if (!command.IsExecutable)
- {
- processedCount++;
- // To allow UI updates
- if (processedCount % 3 == 0)
- await Task.Yield();
- continue;
- }
+ int count = 0;
- var newItem = new NavigationBarSuggestionItem
+ foreach (var command in commandsToProcess)
+ {
+ if (command.IsExecutable)
{
- ThemedIconStyle = command.Glyph.ToThemedIconStyle(),
- Glyph = command.Glyph.BaseGlyph,
- Text = command.Description,
- PrimaryDisplay = command.Description,
- HotKeys = command.HotKeys,
- SearchText = OmnibarCommandPaletteModeText,
- };
+ var newSuggestion = new NavigationBarSuggestionItem(OmnibarCommandPaletteModeText, command);
- newSuggestions.Add(newItem);
- processedCount++;
+ if (count < OmnibarCommandPaletteModeSuggestionItems.Count)
+ OmnibarCommandPaletteModeSuggestionItems[count] = newSuggestion;
+ else
+ OmnibarCommandPaletteModeSuggestionItems.Add(newSuggestion);
- // To allow UI updates
- if (processedCount % 3 == 0)
- await Task.Yield();
- }
+ count++;
- UpdateCommandPaletteSuggestions(newSuggestions);
- }
-
- private void UpdateCommandPaletteSuggestions(List newSuggestions)
- {
- if (newSuggestions.Count == 0)
- {
- newSuggestions.Add(new NavigationBarSuggestionItem()
- {
- PrimaryDisplay = string.Format(Strings.NoCommandsFound.GetLocalizedResource(), OmnibarCommandPaletteModeText),
- SearchText = OmnibarCommandPaletteModeText,
- });
+ // To allow UI updates
+ if (count % 4 == 0)
+ await Task.Yield();
+ }
}
- if (!OmnibarCommandPaletteModeSuggestionItems.IntersectBy(newSuggestions, x => x.PrimaryDisplay).Any())
+ if (count == 0)
{
- for (int index = 0; index < newSuggestions.Count; index++)
- {
- if (index < OmnibarCommandPaletteModeSuggestionItems.Count)
- OmnibarCommandPaletteModeSuggestionItems[index] = newSuggestions[index];
- else
- OmnibarCommandPaletteModeSuggestionItems.Add(newSuggestions[index]);
- }
-
- while (OmnibarCommandPaletteModeSuggestionItems.Count > newSuggestions.Count)
- OmnibarCommandPaletteModeSuggestionItems.RemoveAt(OmnibarCommandPaletteModeSuggestionItems.Count - 1);
+ OmnibarCommandPaletteModeSuggestionItems.Clear();
+ OmnibarCommandPaletteModeSuggestionItems.Add(new NavigationBarSuggestionItem(OmnibarCommandPaletteModeText, null));
}
else
- {
- foreach (var s in OmnibarCommandPaletteModeSuggestionItems.ExceptBy(newSuggestions, x => x.PrimaryDisplay).ToList())
- OmnibarCommandPaletteModeSuggestionItems.Remove(s);
-
- for (int index = 0; index < newSuggestions.Count; index++)
- {
- if (OmnibarCommandPaletteModeSuggestionItems.Count > index
- && OmnibarCommandPaletteModeSuggestionItems[index].PrimaryDisplay == newSuggestions[index].PrimaryDisplay)
- {
- OmnibarCommandPaletteModeSuggestionItems[index] = newSuggestions[index];
- }
- else
- {
- OmnibarCommandPaletteModeSuggestionItems.Insert(index, newSuggestions[index]);
- }
- }
- }
+ while (OmnibarCommandPaletteModeSuggestionItems.Count > count)
+ OmnibarCommandPaletteModeSuggestionItems.RemoveAt(OmnibarCommandPaletteModeSuggestionItems.Count - 1);
}
public async Task PopulateOmnibarSuggestionsForSearchMode()