From 1cff7a8c7ab1f1b822c2622baa2316c89f020fd7 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Sun, 6 Apr 2025 16:54:34 +0900 Subject: [PATCH 1/5] Init --- .../MenuFlyoutItemHeader.cs | 18 ++ .../Omnibar/Omnibar.Events.cs | 7 +- .../Omnibar/Omnibar.Properties.cs | 11 + src/Files.App.Controls/Omnibar/Omnibar.cs | 21 +- src/Files.App.Controls/Omnibar/Omnibar.xaml | 1 - .../Omnibar/OmnibarMode.Properties.cs | 8 + .../Data/Items/NavigationBarSuggestionItem.cs | 1 + .../Data/Models/BreadcrumbBarItemModel.cs | 7 + .../Models/OmnibarPathModeSuggestionModel.cs | 7 + src/Files.App/Strings/en-US/Resources.resw | 61 +++--- .../UserControls/NavigationToolbar.xaml | 122 +++++++++-- .../UserControls/NavigationToolbar.xaml.cs | 116 +++++++---- .../UserControls/PathBreadcrumb.xaml.cs | 10 +- .../NavigationToolbarViewModel.cs | 195 +++++++++++++++--- src/Files.App/Views/Shells/BaseShellPage.cs | 9 +- 15 files changed, 463 insertions(+), 131 deletions(-) create mode 100644 src/Files.App.Controls/MenuFlyoutItemHeader/MenuFlyoutItemHeader.cs create mode 100644 src/Files.App/Data/Models/BreadcrumbBarItemModel.cs create mode 100644 src/Files.App/Data/Models/OmnibarPathModeSuggestionModel.cs diff --git a/src/Files.App.Controls/MenuFlyoutItemHeader/MenuFlyoutItemHeader.cs b/src/Files.App.Controls/MenuFlyoutItemHeader/MenuFlyoutItemHeader.cs new file mode 100644 index 000000000000..850764385a16 --- /dev/null +++ b/src/Files.App.Controls/MenuFlyoutItemHeader/MenuFlyoutItemHeader.cs @@ -0,0 +1,18 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using CommunityToolkit.WinUI; + +namespace Files.App.Controls +{ + public partial class MenuFlyoutItemHeader : MenuFlyoutItem + { + [GeneratedDependencyProperty] + public partial bool Header { get; set; } + + public MenuFlyoutItemHeader() + { + //this.InitializeComponent(); + } + } +} diff --git a/src/Files.App.Controls/Omnibar/Omnibar.Events.cs b/src/Files.App.Controls/Omnibar/Omnibar.Events.cs index 06161def1f95..ebd4f5bcb457 100644 --- a/src/Files.App.Controls/Omnibar/Omnibar.Events.cs +++ b/src/Files.App.Controls/Omnibar/Omnibar.Events.cs @@ -16,7 +16,7 @@ private void Omnibar_SizeChanged(object sender, SizeChangedEventArgs e) private void AutoSuggestBox_GotFocus(object sender, RoutedEventArgs e) { - _isFocused = true; + IsFocused = true; VisualStateManager.GoToState(CurrentSelectedMode, "Focused", true); VisualStateManager.GoToState(_textBox, "InputAreaVisible", true); @@ -30,7 +30,7 @@ private void AutoSuggestBox_LostFocus(object sender, RoutedEventArgs e) if (_textBox.ContextFlyout.IsOpen) return; - _isFocused = false; + IsFocused = false; if (CurrentSelectedMode?.ContentOnInactive is not null) { @@ -92,7 +92,8 @@ private void AutoSuggestBox_KeyDown(object sender, KeyRoutedEventArgs e) private void AutoSuggestBox_TextChanged(object sender, TextChangedEventArgs e) { - CurrentSelectedMode!.Text = _textBox.Text; + if (string.Compare(_textBox.Text, CurrentSelectedMode!.Text, StringComparison.OrdinalIgnoreCase) is not 0) + CurrentSelectedMode!.Text = _textBox.Text; // UpdateSuggestionListView(); diff --git a/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs b/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs index e530e55e69fd..417c6bdd20a6 100644 --- a/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs +++ b/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs @@ -13,7 +13,18 @@ public partial class Omnibar [GeneratedDependencyProperty] public partial OmnibarMode? CurrentSelectedMode { get; set; } + [GeneratedDependencyProperty] + public partial string? CurrentSelectedModeName { get; set; } + [GeneratedDependencyProperty] public partial Thickness AutoSuggestBoxPadding { get; set; } + + [GeneratedDependencyProperty] + public partial bool IsFocused { get; set; } + + partial void OnCurrentSelectedModeChanged(OmnibarMode? newValue) + { + CurrentSelectedModeName = newValue?.ModeName; + } } } diff --git a/src/Files.App.Controls/Omnibar/Omnibar.cs b/src/Files.App.Controls/Omnibar/Omnibar.cs index 9f3d5fae3710..50990709f5c9 100644 --- a/src/Files.App.Controls/Omnibar/Omnibar.cs +++ b/src/Files.App.Controls/Omnibar/Omnibar.cs @@ -28,7 +28,6 @@ public partial class Omnibar : Control private Border _textBoxSuggestionsContainerBorder = null!; private ListView _textBoxSuggestionsListView = null!; - private bool _isFocused; private string _userInput = string.Empty; private OmnibarTextChangeReason _textChangeReason = OmnibarTextChangeReason.None; @@ -148,7 +147,7 @@ public void ChangeMode(OmnibarMode modeToExpand, bool shouldFocus = false, bool CurrentSelectedMode = modeToExpand; _textChangeReason = OmnibarTextChangeReason.ProgrammaticChange; - _textBox.Text = CurrentSelectedMode.Text ?? string.Empty; + ChangeTextBoxText(CurrentSelectedMode.Text ?? string.Empty); // Move cursor of the TextBox to the tail _textBox.Select(_textBox.Text.Length, 0); @@ -156,7 +155,7 @@ public void ChangeMode(OmnibarMode modeToExpand, bool shouldFocus = false, bool VisualStateManager.GoToState(CurrentSelectedMode, "Focused", true); CurrentSelectedMode.OnChangingCurrentMode(true); - if (_isFocused) + if (IsFocused) { VisualStateManager.GoToState(CurrentSelectedMode, "Focused", true); VisualStateManager.GoToState(_textBox, "InputAreaVisible", true); @@ -174,7 +173,7 @@ public void ChangeMode(OmnibarMode modeToExpand, bool shouldFocus = false, bool if (shouldFocus) _textBox.Focus(FocusState.Keyboard); - TryToggleIsSuggestionsPopupOpen(_isFocused && CurrentSelectedMode?.SuggestionItemsSource is not null); + TryToggleIsSuggestionsPopupOpen(IsFocused && CurrentSelectedMode?.SuggestionItemsSource is not null); // Remove the reposition transition from the all modes if (useTransition) @@ -189,7 +188,7 @@ public void ChangeMode(OmnibarMode modeToExpand, bool shouldFocus = false, bool public bool TryToggleIsSuggestionsPopupOpen(bool wantToOpen) { - if (wantToOpen && (!_isFocused || CurrentSelectedMode?.SuggestionItemsSource is null)) + if (wantToOpen && (!IsFocused || CurrentSelectedMode?.SuggestionItemsSource is null)) return false; _textBoxSuggestionsPopup.IsOpen = wantToOpen; @@ -205,10 +204,15 @@ public void ChooseSuggestionItem(object obj) if (CurrentSelectedMode.UpdateTextOnSelect) { _textChangeReason = OmnibarTextChangeReason.SuggestionChosen; - _textBox.Text = GetObjectText(obj); + ChangeTextBoxText(GetObjectText(obj)); } SuggestionChosen?.Invoke(this, new(CurrentSelectedMode, obj)); + } + + internal protected void ChangeTextBoxText(string text) + { + _textBox.Text = text; // Move the cursor to the end of the TextBox _textBox?.Select(_textBox.Text.Length, 0); @@ -245,10 +249,7 @@ private void RevertTextToUserInput() _textBoxSuggestionsListView.SelectedIndex = -1; _textChangeReason = OmnibarTextChangeReason.ProgrammaticChange; - _textBox.Text = _userInput ?? ""; - - // Move the cursor to the end of the TextBox - _textBox?.Select(_textBox.Text.Length, 0); + ChangeTextBoxText(_userInput ?? ""); } } } diff --git a/src/Files.App.Controls/Omnibar/Omnibar.xaml b/src/Files.App.Controls/Omnibar/Omnibar.xaml index 439ec2674897..64eabed99947 100644 --- a/src/Files.App.Controls/Omnibar/Omnibar.xaml +++ b/src/Files.App.Controls/Omnibar/Omnibar.xaml @@ -133,7 +133,6 @@ Height="{TemplateBinding Height}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Background="{TemplateBinding Background}" - CornerRadius="{TemplateBinding CornerRadius}" TabFocusNavigation="Local"> Splitting size - - Do not split - CD @@ -2198,15 +2195,6 @@ Edit settings file - - Open settings file in your default editor - - - Release Notes - - - Open Release Notes - Creating a shortcut in this location requires administrator privileges @@ -3508,7 +3496,7 @@ Cloning {0} from "{1}" to "{2}" Shown in a StatusCenter card. - + Canceled installing {0} fonts Shown in a StatusCenter card. @@ -3540,7 +3528,7 @@ Installing {0} font(s) from "{1}" Shown in a StatusCenter card. - + Canceled copying {0} item(s) to "{1}" Shown in a StatusCenter card. @@ -3700,9 +3688,6 @@ Failed to set the background wallpaper - - Failed to open the settings file - Delete Git branch @@ -4122,18 +4107,6 @@ Add to shelf Tooltip that displays when dragging items to the Shelf Pane - - Enter a hash to compare - Placeholder that appears in the compare hash text box - - - Matches {0} - Appears when two compared hashes match, e.g. "Matches SHA256" - - - No matches found - Appears when two compared hashes don't match - Path or alias @@ -4177,6 +4150,33 @@ Cannot clone repo Cannot clone repo dialog title + + Do not split + + + Open settings file in your default editor + + + Release Notes + + + Open Release Notes + + + Failed to open the settings file + + + Enter a hash to compare + Placeholder that appears in the compare hash text box + + + Matches {0} + Appears when two compared hashes match, e.g. "Matches SHA256" + + + No matches found + Appears when two compared hashes don't match + Compare a file Button that appears in file hash properties that allows the user to compare two files @@ -4193,4 +4193,7 @@ Enable Omnibar + + Enter a path to navigate to... + \ No newline at end of file diff --git a/src/Files.App/UserControls/NavigationToolbar.xaml b/src/Files.App/UserControls/NavigationToolbar.xaml index 1473c10abaa2..081edb6f6c12 100644 --- a/src/Files.App/UserControls/NavigationToolbar.xaml +++ b/src/Files.App/UserControls/NavigationToolbar.xaml @@ -9,6 +9,8 @@ xmlns:converters="using:Files.App.Converters" xmlns:converters1="using:CommunityToolkit.WinUI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:dataitems="using:Files.App.Data.Items" + xmlns:datamodels="using:Files.App.Data.Models" xmlns:helpers="using:Files.App.Helpers" xmlns:items="using:Files.App.Data.Items" xmlns:keyboard="using:Files.App.UserControls.KeyboardShortcut" @@ -211,6 +213,7 @@ + @@ -314,23 +317,11 @@ LostFocus="SearchRegion_LostFocus" SearchBoxViewModel="{x:Bind ViewModel.SearchBoxViewModel, Mode=OneWay}" Visibility="{x:Bind converters:MultiBooleanConverter.OrConvertToVisibility(ShowSearchBox, ViewModel.IsSearchBoxVisible), Mode=OneWay}" /> - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (HistoryItemClicked); } + // Methods + private void NavToolbar_Loading(FrameworkElement _, object e) { Loading -= NavToolbar_Loading; @@ -104,7 +99,7 @@ private void VisiblePath_LostFocus(object _, RoutedEventArgs e) if (App.AppModel.IsMainWindowClosed) return; - var element = FocusManager.GetFocusedElement(MainWindow.Instance.Content.XamlRoot); + var element = Microsoft.UI.Xaml.Input.FocusManager.GetFocusedElement(MainWindow.Instance.Content.XamlRoot); if (element is FlyoutBase or AppBarButton or Popup) return; @@ -195,9 +190,9 @@ private async Task AddHistoryItemsAsync(IEnumerable items, IList var flyoutItem = new MenuFlyoutItem { - Icon = new FontIcon { Glyph = "\uE8B7" }, // Use font icon as placeholder + Icon = new FontIcon() { Glyph = "\uE8B7" }, // Placeholder icon Text = fileName, - Command = historyItemClickedCommand, + Command = new RelayCommand(HistoryItemClicked), CommandParameter = new ToolbarHistoryItemModel(item, isBackMode) }; @@ -205,58 +200,91 @@ private async Task AddHistoryItemsAsync(IEnumerable items, IList // Start loading the thumbnail in the background _ = LoadFlyoutItemIconAsync(flyoutItem, args.NavPathParam); + + async Task LoadFlyoutItemIconAsync(MenuFlyoutItem flyoutItem, string path) + { + var imageSource = await NavigationHelpers.GetIconForPathAsync(path); + + if (imageSource is not null) + flyoutItem.Icon = new ImageIcon() { Source = imageSource }; + } + + void HistoryItemClicked(ToolbarHistoryItemModel? itemModel) + { + if (itemModel is null) + return; + + var shellPage = Ioc.Default.GetRequiredService().ShellPage; + if (shellPage is null) + return; + + if (itemModel.IsBackMode) + { + // Remove all entries after the target entry in the BackwardStack + while (shellPage.BackwardStack.Last() != itemModel.PageStackEntry) + { + shellPage.BackwardStack.RemoveAt(shellPage.BackwardStack.Count - 1); + } + + // Navigate back + shellPage.Back_Click(); + } + else + { + // Remove all entries before the target entry in the ForwardStack + while (shellPage.ForwardStack.First() != itemModel.PageStackEntry) + { + shellPage.ForwardStack.RemoveAt(0); + } + + // Navigate forward + shellPage.Forward_Click(); + } + } } } - private async Task LoadFlyoutItemIconAsync(MenuFlyoutItem flyoutItem, string path) + private void ClickablePath_GettingFocus(UIElement sender, GettingFocusEventArgs args) { - var imageSource = await NavigationHelpers.GetIconForPathAsync(path); + if (args.InputDevice != FocusInputDeviceKind.Keyboard) + return; - if (imageSource is not null) - flyoutItem.Icon = new ImageIcon { Source = imageSource }; + var previousControl = args.OldFocusedElement as FrameworkElement; + if (previousControl?.Name == nameof(HomeButton) || previousControl?.Name == nameof(Refresh)) + ViewModel.IsEditModeEnabled = true; } - private void HistoryItemClicked(ToolbarHistoryItemModel? itemModel) + private void Omnibar_SuggestionChosen(Controls.Omnibar sender, Controls.OmnibarSuggestionChosenEventArgs args) { - if (itemModel is null) - return; - var shellPage = Ioc.Default.GetRequiredService().ShellPage; - if (shellPage is null) - return; + } - if (itemModel.IsBackMode) + private async void BreadcrumbBar_ItemClicked(Controls.BreadcrumbBar sender, Controls.BreadcrumbBarItemClickedEventArgs args) + { + if (args.IsRootItem) { - // Remove all entries after the target entry in the BackwardStack - while (shellPage.BackwardStack.Last() != itemModel.PageStackEntry) - { - shellPage.BackwardStack.RemoveAt(shellPage.BackwardStack.Count - 1); - } - - // Navigate back - shellPage.Back_Click(); + // TODO: Go to Home + return; } - else - { - // Remove all entries before the target entry in the ForwardStack - while (shellPage.ForwardStack.First() != itemModel.PageStackEntry) - { - shellPage.ForwardStack.RemoveAt(0); - } - // Navigate forward - shellPage.Forward_Click(); - } + await ViewModel.HandleBreadcrumbBarItemClicked(ViewModel.PathComponents[args.Index].Path); } - private void ClickablePath_GettingFocus(UIElement sender, GettingFocusEventArgs args) + private async void BreadcrumbBar_ItemDropDownFlyoutOpening(object sender, Controls.BreadcrumbBarItemDropDownFlyoutEventArgs e) { - if (args.InputDevice != FocusInputDeviceKind.Keyboard) + if (e.IsRootItem) + { + // TODO: Populate a different flyout for the root item return; + } - var previousControl = args.OldFocusedElement as FrameworkElement; - if (previousControl?.Name == nameof(HomeButton) || previousControl?.Name == nameof(Refresh)) - ViewModel.IsEditModeEnabled = true; + await ViewModel.SetPathBoxDropDownFlyoutAsync(e.Flyout, ViewModel.PathComponents[e.Index]); + } + + private void BreadcrumbBar_ItemDropDownFlyoutClosed(object sender, Controls.BreadcrumbBarItemDropDownFlyoutEventArgs e) + { + // Clear the flyout items to save memory + e.Flyout.Items.Clear(); } } } diff --git a/src/Files.App/UserControls/PathBreadcrumb.xaml.cs b/src/Files.App/UserControls/PathBreadcrumb.xaml.cs index bb08408b5eaa..bff7c3600101 100644 --- a/src/Files.App/UserControls/PathBreadcrumb.xaml.cs +++ b/src/Files.App/UserControls/PathBreadcrumb.xaml.cs @@ -50,7 +50,15 @@ private async void PathBoxItem_Drop(object sender, DragEventArgs e) private async void PathBoxItem_Tapped(object sender, TappedRoutedEventArgs e) { - await ViewModel.PathBoxItem_Tapped(sender, e); + if (sender is not TextBlock textBlock || + textBlock.DataContext is not PathBoxItem item || + item.Path is not { } path) + return; + + // TODO: Implement middle click retrieving. + await ViewModel.HandleBreadcrumbBarItemClicked(path); + + e.Handled = true; } private void PathBoxItem_PointerPressed(object sender, PointerRoutedEventArgs e) diff --git a/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs index 05e632463d26..b01a840cbd95 100644 --- a/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs @@ -3,6 +3,7 @@ using CommunityToolkit.WinUI; using Files.App.Actions; +using Files.App.Controls; using Files.Shared.Helpers; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; @@ -22,6 +23,10 @@ public sealed partial class NavigationToolbarViewModel : ObservableObject, IAddr private const int MaxSuggestionsCount = 10; + public const string OmnibarPathModeName = "Path"; + public const string OmnibarPaletteModeName = "Palette"; + public const string OmnibarSearchModeName = "Search"; + // Dependency injections private readonly IUserSettingsService UserSettingsService = Ioc.Default.GetRequiredService(); @@ -30,6 +35,7 @@ public sealed partial class NavigationToolbarViewModel : ObservableObject, IAddr private readonly DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService(); private readonly IUpdateService UpdateService = Ioc.Default.GetRequiredService(); private readonly ICommandManager Commands = Ioc.Default.GetRequiredService(); + private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); // Fields @@ -63,6 +69,8 @@ public sealed partial class NavigationToolbarViewModel : ObservableObject, IAddr public ObservableCollection NavigationBarSuggestions { get; } = []; + internal ObservableCollection PathModeSuggestionItems { get; } = []; + public bool IsSingleItemOverride { get; set; } public bool SearchHasFocus { get; private set; } @@ -198,17 +206,56 @@ public bool IsSearchBoxVisible } } - private string? _PathText; - public string? PathText + private string? _LegacySharedPathPaletteText; + [Obsolete("Remove once Omnibar goes out of experimental.")] + public string? LegacySharedPathPaletteText { - get => _PathText; + get => _LegacySharedPathPaletteText; set { - _PathText = value; - OnPropertyChanged(nameof(PathText)); + if (SetProperty(ref _LegacySharedPathPaletteText, value)) + OnPropertyChanged(nameof(OmnibarPathModeText)); } } + private bool _IsOmnibarFocused; + public bool IsOmnibarFocused + { + get => _IsOmnibarFocused; + set + { + if (SetProperty(ref _IsOmnibarFocused, value)) + { + if (value) + { + switch(OmnibarCurrentSelectedModeName) + { + case OmnibarPathModeName: + OmnibarPathModeText = + string.IsNullOrEmpty(ContentPageContext.ShellPage?.ShellViewModel?.WorkingDirectory) + ? Constants.UserEnvironmentPaths.HomePath + : ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory; + _ = PopulateOmnibarSuggestionsForPathMode(); + break; + case OmnibarPaletteModeName: + break; + case OmnibarSearchModeName: + break; + default: + throw new ArgumentOutOfRangeException(""); + } + + } + } + } + } + + private string _OmnibarCurrentSelectedModeName; + public string OmnibarCurrentSelectedModeName { get => _OmnibarCurrentSelectedModeName; set => SetProperty(ref _OmnibarCurrentSelectedModeName, value); } + + private string _OmnibarPathModeText; + public string OmnibarPathModeText { get => _OmnibarPathModeText; set => SetProperty(ref _OmnibarPathModeText, value); } + private CurrentInstanceViewModel _InstanceViewModel; public CurrentInstanceViewModel InstanceViewModel { @@ -226,6 +273,7 @@ public CurrentInstanceViewModel InstanceViewModel } } + [Obsolete("Remove once Omnibar goes out of experimental.")] public bool IsEditModeEnabled { get => ManualEntryBoxLoaded; @@ -548,28 +596,24 @@ public void PathBoxItem_PointerPressed(object sender, PointerRoutedEventArgs e) _pointerRoutedEventArgs = ptrPt.Properties.IsMiddleButtonPressed ? e : null; } - public async Task PathBoxItem_Tapped(object sender, TappedRoutedEventArgs e) + public async Task HandleBreadcrumbBarItemClicked(string path, bool? isMiddleButtonPressed = null) { - var itemTappedPath = ((sender as TextBlock)?.DataContext as PathBoxItem)?.Path; - if (itemTappedPath is null) - return; - - if (_pointerRoutedEventArgs is not null) + isMiddleButtonPressed ??= _pointerRoutedEventArgs is not null; + if (isMiddleButtonPressed is true) { - await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => - { - await NavigationHelpers.AddNewTabByPathAsync(typeof(ShellPanesPage), itemTappedPath, true); - }, DispatcherQueuePriority.Low); - e.Handled = true; + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync( + async () => + { + await NavigationHelpers.AddNewTabByPathAsync(typeof(ShellPanesPage), path, true); + }, + DispatcherQueuePriority.Low); + _pointerRoutedEventArgs = null; return; } - ToolbarPathItemInvoked?.Invoke(this, new PathNavigationEventArgs() - { - ItemPath = itemTappedPath - }); + ToolbarPathItemInvoked?.Invoke(this, new() { ItemPath = path }); } public void PathBoxItem_PreviewKeyDown(object sender, KeyRoutedEventArgs e) @@ -603,7 +647,7 @@ public void PathBoxItem_PreviewKeyDown(object sender, KeyRoutedEventArgs e) public void OpenCommandPalette() { - PathText = ">"; + LegacySharedPathPaletteText = ">"; IsCommandPaletteOpen = true; ManualEntryBoxLoaded = true; ClickablePathLoaded = false; @@ -677,12 +721,12 @@ public void SearchRegion_LostFocus(object sender, RoutedEventArgs e) private void SearchRegion_Escaped(object? sender, ISearchBoxViewModel _SearchBox) => CloseSearchBox(true); - public async Task SetPathBoxDropDownFlyoutAsync(MenuFlyout flyout, PathBoxItem pathItem, IShellPage shellPage) + public async Task SetPathBoxDropDownFlyoutAsync(MenuFlyout flyout, PathBoxItem pathItem) { var nextPathItemTitle = PathComponents[PathComponents.IndexOf(pathItem) + 1].Title; IList? childFolders = null; - StorageFolderWithPath folder = await shellPage.ShellViewModel.GetFolderWithPathFromPathAsync(pathItem.Path); + StorageFolderWithPath folder = await ContentPageContext.ShellPage.ShellViewModel.GetFolderWithPathFromPathAsync(pathItem.Path); if (folder is not null) childFolders = (await FilesystemTasks.Wrap(() => folder.GetFoldersWithPathAsync(string.Empty))).Result; @@ -723,7 +767,7 @@ public async Task SetPathBoxDropDownFlyoutAsync(MenuFlyout flyout, PathBoxItem p flyoutItem.Click += (sender, args) => { // Navigate to the directory - shellPage.NavigateToPath(childFolder.Path); + ContentPageContext.ShellPage.NavigateToPath(childFolder.Path); }; } @@ -885,7 +929,108 @@ private static async Task LaunchApplicationFromPath(string currentInput, s ); } - public async Task SetAddressBarSuggestionsAsync(AutoSuggestBox sender, IShellPage shellpage) + public async Task PopulateOmnibarSuggestionsForPathMode() + { + var result = await SafetyExtensions.IgnoreExceptions(async () => + { + List? newSuggestions = []; + var pathText = OmnibarPathModeText; + + // If the current input is special, populate navigation history instead. + if (string.IsNullOrWhiteSpace(pathText) || + pathText is "Home" or "ReleaseNotes" or "Settings") + { + // Load previously entered path + if (UserSettingsService.GeneralSettingsService.PathHistoryList is { } pathHistoryList) + { + newSuggestions.AddRange(pathHistoryList.Select(x => new OmnibarPathModeSuggestionModel(x, x))); + } + } + else + { + var isFtp = FtpHelpers.IsFtpPath(pathText); + pathText = NormalizePathInput(pathText, isFtp); + var expandedPath = StorageFileExtensions.GetResolvedPath(pathText, isFtp); + var folderPath = PathNormalization.GetParentDir(expandedPath) ?? expandedPath; + StorageFolderWithPath folder = await ContentPageContext.ShellPage.ShellViewModel.GetFolderWithPathFromPathAsync(folderPath); + if (folder is null) + return false; + + var currPath = await folder.GetFoldersWithPathAsync(Path.GetFileName(expandedPath), MaxSuggestionsCount); + if (currPath.Count >= MaxSuggestionsCount) + { + newSuggestions.AddRange(currPath.Select(x => new OmnibarPathModeSuggestionModel(x.Path, x.Item.DisplayName))); + } + else if (currPath.Any()) + { + var subPath = await currPath.First().GetFoldersWithPathAsync((uint)(MaxSuggestionsCount - currPath.Count)); + newSuggestions.AddRange(currPath.Select(x => new OmnibarPathModeSuggestionModel(x.Path, x.Item.DisplayName))); + newSuggestions.AddRange(subPath.Select(x => new OmnibarPathModeSuggestionModel(x.Path, PathNormalization.Combine(currPath.First().Item.DisplayName, x.Item.DisplayName)))); + } + + // If there are no suggestions, show "No suggestions" + if (newSuggestions.Count is 0) + { + AddNoResultsItem(); + } + + // Check whether at least one item is in common between the old and the new suggestions + // since Omnibar suggestions popup becoming empty causes flickering + if (!PathModeSuggestionItems.IntersectBy(newSuggestions, x => x.DisplayName).Any()) + { + // No items in common, update the list in-place + for (int index = 0; index < newSuggestions.Count; index++) + { + if (index < PathModeSuggestionItems.Count) + { + PathModeSuggestionItems[index] = newSuggestions[index]; + } + else + { + PathModeSuggestionItems.Add(newSuggestions[index]); + } + } + + while (PathModeSuggestionItems.Count > newSuggestions.Count) + PathModeSuggestionItems.RemoveAt(PathModeSuggestionItems.Count - 1); + } + else + { + // At least an element in common, show animation + foreach (var s in PathModeSuggestionItems.ExceptBy(newSuggestions, x => x.DisplayName).ToList()) + PathModeSuggestionItems.Remove(s); + + for (int index = 0; index < newSuggestions.Count; index++) + { + if (PathModeSuggestionItems.Count > index && PathModeSuggestionItems[index].DisplayName == newSuggestions[index].DisplayName) + { + PathModeSuggestionItems[index] = newSuggestions[index]; + } + else + PathModeSuggestionItems.Insert(index, newSuggestions[index]); + } + } + } + + return true; + }); + + if (!result) + { + AddNoResultsItem(); + } + + void AddNoResultsItem() + { + PathModeSuggestionItems.Clear(); + PathModeSuggestionItems.Add(new( + ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory, + Strings.NavigationToolbarVisiblePathNoResults.GetLocalizedResource())); + } + } + + [Obsolete("Remove once Omnibar goes out of experimental.")] + public async Task SetLegacyAddressBarSuggestionsAsync(AutoSuggestBox sender, IShellPage shellpage) { if (sender.Text is not null && shellpage.ShellViewModel is not null) { diff --git a/src/Files.App/Views/Shells/BaseShellPage.cs b/src/Files.App/Views/Shells/BaseShellPage.cs index 76000bad73c3..fafb7a5ca682 100644 --- a/src/Files.App/Views/Shells/BaseShellPage.cs +++ b/src/Files.App/Views/Shells/BaseShellPage.cs @@ -417,12 +417,12 @@ protected async void ShellPage_PathBoxItemDropped(object sender, PathBoxItemDrop protected async void ShellPage_AddressBarTextEntered(object sender, AddressBarTextEnteredEventArgs e) { - await ToolbarViewModel.SetAddressBarSuggestionsAsync(e.AddressBarTextField, this); + await ToolbarViewModel.SetLegacyAddressBarSuggestionsAsync(e.AddressBarTextField, this); } protected async void ShellPage_ToolbarPathItemLoaded(object sender, ToolbarPathItemLoadedEventArgs e) { - await ToolbarViewModel.SetPathBoxDropDownFlyoutAsync(e.OpenedFlyout, e.Item, this); + await ToolbarViewModel.SetPathBoxDropDownFlyoutAsync(e.OpenedFlyout, e.Item); } protected async void ShellPage_ToolbarFlyoutOpening(object sender, ToolbarFlyoutOpeningEventArgs e) @@ -430,7 +430,7 @@ protected async void ShellPage_ToolbarFlyoutOpening(object sender, ToolbarFlyout var pathBoxItem = ((Button)e.OpeningFlyout.Target).DataContext as PathBoxItem; if (pathBoxItem is not null) - await ToolbarViewModel.SetPathBoxDropDownFlyoutAsync(e.OpeningFlyout, pathBoxItem, this); + await ToolbarViewModel.SetPathBoxDropDownFlyoutAsync(e.OpeningFlyout, pathBoxItem); } protected async void NavigationToolbar_QuerySubmitted(object sender, ToolbarQuerySubmittedEventArgs e) @@ -442,9 +442,10 @@ protected void NavigationToolbar_EditModeEnabled(object sender, EventArgs e) { ToolbarViewModel.ManualEntryBoxLoaded = true; ToolbarViewModel.ClickablePathLoaded = false; - ToolbarViewModel.PathText = string.IsNullOrEmpty(ShellViewModel?.WorkingDirectory) + ToolbarViewModel.OmnibarPathModeText = string.IsNullOrEmpty(ShellViewModel?.WorkingDirectory) ? Constants.UserEnvironmentPaths.HomePath : ShellViewModel.WorkingDirectory; + ToolbarViewModel.LegacySharedPathPaletteText = ToolbarViewModel.OmnibarPathModeText; } protected async void DrivesManager_PropertyChanged(object sender, PropertyChangedEventArgs e) From ad8dfc33136aa96695d147cfc8932d8c84b25add Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Tue, 8 Apr 2025 23:39:47 +0900 Subject: [PATCH 2/5] Update --- .../UserControls/NavigationToolbar.xaml.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Files.App/UserControls/NavigationToolbar.xaml.cs b/src/Files.App/UserControls/NavigationToolbar.xaml.cs index 5b52b7f5b0f6..2729ffe51d52 100644 --- a/src/Files.App/UserControls/NavigationToolbar.xaml.cs +++ b/src/Files.App/UserControls/NavigationToolbar.xaml.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using CommunityToolkit.WinUI; +using Files.App.Controls; using Microsoft.UI.Input; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -9,7 +10,6 @@ using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Navigation; -using System.Windows.Input; using Windows.System; namespace Files.App.UserControls @@ -270,11 +270,22 @@ private async void BreadcrumbBar_ItemClicked(Controls.BreadcrumbBar sender, Cont await ViewModel.HandleBreadcrumbBarItemClicked(ViewModel.PathComponents[args.Index].Path); } - private async void BreadcrumbBar_ItemDropDownFlyoutOpening(object sender, Controls.BreadcrumbBarItemDropDownFlyoutEventArgs e) + private async void BreadcrumbBar_ItemDropDownFlyoutOpening(object sender, BreadcrumbBarItemDropDownFlyoutEventArgs e) { if (e.IsRootItem) { // TODO: Populate a different flyout for the root item + e.Flyout.Items.Add(new MenuFlyoutHeaderItem() { Text = "Quick access" }); + e.Flyout.Items.Add(new MenuFlyoutItem() { Text = "Desktop" }); + e.Flyout.Items.Add(new MenuFlyoutItem() { Text = "Download" }); + e.Flyout.Items.Add(new MenuFlyoutItem() { Text = "Documents" }); + e.Flyout.Items.Add(new MenuFlyoutItem() { Text = "Pictures" }); + e.Flyout.Items.Add(new MenuFlyoutItem() { Text = "Music" }); + e.Flyout.Items.Add(new MenuFlyoutItem() { Text = "Videos" }); + e.Flyout.Items.Add(new MenuFlyoutItem() { Text = "Recycle bin" }); + e.Flyout.Items.Add(new MenuFlyoutHeaderItem() { Text = "Drives" }); + e.Flyout.Items.Add(new MenuFlyoutItem() { Text = "Local Disk (C:)" }); + e.Flyout.Items.Add(new MenuFlyoutItem() { Text = "Local Disk (D:)" }); return; } From e1ee482a28dda8d7dc54f48467f69d20193173e1 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Thu, 10 Apr 2025 11:35:31 +0900 Subject: [PATCH 3/5] Click on a suggestion --- src/Files.App.Controls/Omnibar/Omnibar.cs | 2 +- .../Omnibar/OmnibarMode.Properties.cs | 5 ++++- .../Data/Models/OmnibarPathModeSuggestionModel.cs | 15 ++++++++++++++- src/Files.App/UserControls/NavigationToolbar.xaml | 4 ++-- .../UserControls/NavigationToolbar.xaml.cs | 10 ++++++---- src/Files.App/UserControls/PathBreadcrumb.xaml.cs | 2 +- .../UserControls/NavigationToolbarViewModel.cs | 2 +- 7 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/Files.App.Controls/Omnibar/Omnibar.cs b/src/Files.App.Controls/Omnibar/Omnibar.cs index 50990709f5c9..fe8c2e20b2da 100644 --- a/src/Files.App.Controls/Omnibar/Omnibar.cs +++ b/src/Files.App.Controls/Omnibar/Omnibar.cs @@ -237,7 +237,7 @@ private string GetObjectText(object obj) return obj is string text ? text : obj is IOmnibarTextMemberPathProvider textMemberPathProvider - ? textMemberPathProvider.GetTextMemberPath(CurrentSelectedMode.DisplayMemberPath ?? string.Empty) + ? textMemberPathProvider.GetTextMemberPath(CurrentSelectedMode.TextMemberPath ?? string.Empty) : obj.ToString() ?? string.Empty; } diff --git a/src/Files.App.Controls/Omnibar/OmnibarMode.Properties.cs b/src/Files.App.Controls/Omnibar/OmnibarMode.Properties.cs index 5842f3c99854..9a7a234e4472 100644 --- a/src/Files.App.Controls/Omnibar/OmnibarMode.Properties.cs +++ b/src/Files.App.Controls/Omnibar/OmnibarMode.Properties.cs @@ -35,7 +35,10 @@ public partial class OmnibarMode public partial DataTemplate? SuggestionItemTemplate { get; set; } [GeneratedDependencyProperty] - public partial string? DisplayMemberPath { get; set; } + /// + /// Implement in to get the text member path from the suggestion item correctly. + /// + public partial string? TextMemberPath { get; set; } [GeneratedDependencyProperty(DefaultValue = true)] public partial bool UpdateTextOnSelect { get; set; } diff --git a/src/Files.App/Data/Models/OmnibarPathModeSuggestionModel.cs b/src/Files.App/Data/Models/OmnibarPathModeSuggestionModel.cs index c4d37251fcec..4142d00f0941 100644 --- a/src/Files.App/Data/Models/OmnibarPathModeSuggestionModel.cs +++ b/src/Files.App/Data/Models/OmnibarPathModeSuggestionModel.cs @@ -1,7 +1,20 @@ // Copyright (c) Files Community // Licensed under the MIT License. +using Files.App.Controls; + namespace Files.App.Data.Models { - internal record OmnibarPathModeSuggestionModel(string Path, string DisplayName); + internal record OmnibarPathModeSuggestionModel(string Path, string DisplayName) : IOmnibarTextMemberPathProvider + { + public string GetTextMemberPath(string textMemberPath) + { + return textMemberPath switch + { + nameof(Path) => Path, + nameof(DisplayName) => DisplayName, + _ => string.Empty + }; + } + } } diff --git a/src/Files.App/UserControls/NavigationToolbar.xaml b/src/Files.App/UserControls/NavigationToolbar.xaml index 081edb6f6c12..378674378abf 100644 --- a/src/Files.App/UserControls/NavigationToolbar.xaml +++ b/src/Files.App/UserControls/NavigationToolbar.xaml @@ -345,14 +345,14 @@ SuggestionChosen="Omnibar_SuggestionChosen"> + Text="{x:Bind ViewModel.OmnibarPathModeText, Mode=OneWay}" + TextMemberPath="Path"> Date: Thu, 17 Apr 2025 00:34:04 +0900 Subject: [PATCH 4/5] Handled query submitted event --- .../UserControls/NavigationToolbar.xaml | 6 +- .../UserControls/NavigationToolbar.xaml.cs | 10 ++ .../NavigationToolbarViewModel.cs | 107 +++++++++++++++++- 3 files changed, 118 insertions(+), 5 deletions(-) diff --git a/src/Files.App/UserControls/NavigationToolbar.xaml b/src/Files.App/UserControls/NavigationToolbar.xaml index 378674378abf..48359e2f94bf 100644 --- a/src/Files.App/UserControls/NavigationToolbar.xaml +++ b/src/Files.App/UserControls/NavigationToolbar.xaml @@ -342,7 +342,9 @@ x:Load="{x:Bind ViewModel.EnableOmnibar, Mode=OneWay}" CurrentSelectedModeName="{x:Bind ViewModel.OmnibarCurrentSelectedModeName, Mode=TwoWay}" IsFocused="{x:Bind ViewModel.IsOmnibarFocused, Mode=TwoWay}" - SuggestionChosen="Omnibar_SuggestionChosen"> + QuerySubmitted="Omnibar_QuerySubmitted" + SuggestionChosen="Omnibar_SuggestionChosen" + TextChanged="Omnibar_TextChanged"> @@ -616,6 +616,106 @@ await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync( ToolbarPathItemInvoked?.Invoke(this, new() { ItemPath = path }); } + public async Task HandleItemNavigationAsync(string path) + { + if (ContentPageContext.ShellPage is null || PathComponents.LastOrDefault()?.Path is not { } currentPath) + return; + + var isFtp = FtpHelpers.IsFtpPath(path); + var normalizedInput = NormalizePathInput(path, isFtp); + if (currentPath.Equals(normalizedInput, StringComparison.OrdinalIgnoreCase) || + string.IsNullOrWhiteSpace(normalizedInput)) + return; + + if (normalizedInput.Equals(ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory) && + ContentPageContext.ShellPage.CurrentPageType != typeof(HomePage)) + return; + + if (normalizedInput.Equals("Home", StringComparison.OrdinalIgnoreCase) || + normalizedInput.Equals(Strings.Home.GetLocalizedResource(), StringComparison.OrdinalIgnoreCase)) + { + SavePathToHistory("Home"); + ContentPageContext.ShellPage.NavigateHome(); + } + else if (normalizedInput.Equals("ReleaseNotes", StringComparison.OrdinalIgnoreCase) || + normalizedInput.Equals(Strings.ReleaseNotes.GetLocalizedResource(), StringComparison.OrdinalIgnoreCase)) + { + SavePathToHistory("ReleaseNotes"); + ContentPageContext.ShellPage.NavigateToReleaseNotes(); + } + else if (normalizedInput.Equals("Settings", StringComparison.OrdinalIgnoreCase) || + normalizedInput.Equals(Strings.Settings.GetLocalizedResource(), StringComparison.OrdinalIgnoreCase)) + { + //SavePathToHistory("Settings"); + //ContentPageContext.ShellPage.NavigateToSettings(); + } + else + { + normalizedInput = StorageFileExtensions.GetResolvedPath(normalizedInput, isFtp); + if (currentPath.Equals(normalizedInput, StringComparison.OrdinalIgnoreCase)) + return; + + var item = await FilesystemTasks.Wrap(() => DriveHelpers.GetRootFromPathAsync(normalizedInput)); + + var resFolder = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderWithPathFromPathAsync(normalizedInput, item)); + if (resFolder || FolderHelpers.CheckFolderAccessWithWin32(normalizedInput)) + { + var matchingDrive = drivesViewModel.Drives.Cast().FirstOrDefault(x => PathNormalization.NormalizePath(normalizedInput).StartsWith(PathNormalization.NormalizePath(x.Path), StringComparison.Ordinal)); + if (matchingDrive is not null && matchingDrive.Type == Data.Items.DriveType.CDRom && matchingDrive.MaxSpace == ByteSizeLib.ByteSize.FromBytes(0)) + { + bool ejectButton = await DialogDisplayHelper.ShowDialogAsync(Strings.InsertDiscDialog_Title.GetLocalizedResource(), string.Format(Strings.InsertDiscDialog_Text.GetLocalizedResource(), matchingDrive.Path), Strings.InsertDiscDialog_OpenDriveButton.GetLocalizedResource(), Strings.Close.GetLocalizedResource()); + if (ejectButton) + DriveHelpers.EjectDeviceAsync(matchingDrive.Path); + return; + } + + var pathToNavigate = resFolder.Result?.Path ?? normalizedInput; + SavePathToHistory(pathToNavigate); + ContentPageContext.ShellPage.NavigateToPath(pathToNavigate); + } + else if (isFtp) + { + SavePathToHistory(normalizedInput); + ContentPageContext.ShellPage.NavigateToPath(normalizedInput); + } + else // Not a folder or inaccessible + { + var resFile = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFileWithPathFromPathAsync(normalizedInput, item)); + if (resFile) + { + var pathToInvoke = resFile.Result.Path; + await Win32Helper.InvokeWin32ComponentAsync(pathToInvoke, ContentPageContext.ShellPage); + } + else // Not a file or not accessible + { + var workingDir = + string.IsNullOrEmpty(ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory) || + ContentPageContext.ShellPage.CurrentPageType == typeof(HomePage) + ? Constants.UserEnvironmentPaths.HomePath + : ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory; + + if (await LaunchApplicationFromPath(OmnibarPathModeText, workingDir)) + return; + + try + { + if (!await Windows.System.Launcher.LaunchUriAsync(new Uri(OmnibarPathModeText))) + await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidItemDialogTitle.GetLocalizedResource(), + string.Format(Strings.InvalidItemDialogContent.GetLocalizedResource(), Environment.NewLine, resFolder.ErrorCode.ToString())); + } + catch (Exception ex) when (ex is UriFormatException || ex is ArgumentException) + { + await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidItemDialogTitle.GetLocalizedResource(), + string.Format(Strings.InvalidItemDialogContent.GetLocalizedResource(), Environment.NewLine, resFolder.ErrorCode.ToString())); + } + } + } + } + + PathControlDisplayText = ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory; + IsOmnibarFocused = false; + } + public void PathBoxItem_PreviewKeyDown(object sender, KeyRoutedEventArgs e) { switch (e.Key) @@ -799,6 +899,7 @@ private static string NormalizePathInput(string currentInput, bool isFtp) return currentInput; } + [Obsolete("Remove once Omnibar goes out of experimental.")] public async Task CheckPathInputAsync(string currentInput, string currentSelectedPath, IShellPage shellPage) { if (currentInput.StartsWith('>')) From 84ad3aae0239e600f27d1c1c288d6285d035541a Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Thu, 17 Apr 2025 13:16:16 +0900 Subject: [PATCH 5/5] Fixed an issue --- .../Omnibar/Omnibar.Properties.cs | 5 ++ .../UserControls/NavigationToolbar.xaml.cs | 7 +- .../NavigationToolbarViewModel.cs | 66 +++++++++---------- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs b/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs index 417c6bdd20a6..d4d8ff449cab 100644 --- a/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs +++ b/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs @@ -26,5 +26,10 @@ partial void OnCurrentSelectedModeChanged(OmnibarMode? newValue) { CurrentSelectedModeName = newValue?.ModeName; } + + partial void OnIsFocusedChanged(bool newValue) + { + //_textBox?.Focus(newValue ? FocusState.Programmatic : FocusState.Unfocused); + } } } diff --git a/src/Files.App/UserControls/NavigationToolbar.xaml.cs b/src/Files.App/UserControls/NavigationToolbar.xaml.cs index 85ada83f19f2..0d6d4cb0a295 100644 --- a/src/Files.App/UserControls/NavigationToolbar.xaml.cs +++ b/src/Files.App/UserControls/NavigationToolbar.xaml.cs @@ -261,8 +261,9 @@ private async void Omnibar_QuerySubmitted(Omnibar sender, OmnibarQuerySubmittedE private async void Omnibar_SuggestionChosen(Omnibar sender, OmnibarSuggestionChosenEventArgs args) { - if (args.SelectedItem is OmnibarPathModeSuggestionModel item) - await ViewModel.HandleFolderNavigationAsync(item.Path); + if (args.SelectedItem is OmnibarPathModeSuggestionModel item && + !string.IsNullOrEmpty(item.Path)) + await ViewModel.HandleItemNavigationAsync(item.Path); } private async void Omnibar_TextChanged(Omnibar sender, OmnibarTextChangedEventArgs args) @@ -274,7 +275,7 @@ private async void BreadcrumbBar_ItemClicked(Controls.BreadcrumbBar sender, Cont { if (args.IsRootItem) { - // TODO: Go to Home + await ViewModel.HandleItemNavigationAsync("Home"); return; } diff --git a/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs index 0cce2c60ea48..197318ec6eee 100644 --- a/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs @@ -1068,48 +1068,48 @@ public async Task PopulateOmnibarSuggestionsForPathMode() newSuggestions.AddRange(currPath.Select(x => new OmnibarPathModeSuggestionModel(x.Path, x.Item.DisplayName))); newSuggestions.AddRange(subPath.Select(x => new OmnibarPathModeSuggestionModel(x.Path, PathNormalization.Combine(currPath.First().Item.DisplayName, x.Item.DisplayName)))); } + } - // If there are no suggestions, show "No suggestions" - if (newSuggestions.Count is 0) - { - AddNoResultsItem(); - } + // If there are no suggestions, show "No suggestions" + if (newSuggestions.Count is 0) + { + AddNoResultsItem(); + } - // Check whether at least one item is in common between the old and the new suggestions - // since Omnibar suggestions popup becoming empty causes flickering - if (!PathModeSuggestionItems.IntersectBy(newSuggestions, x => x.DisplayName).Any()) + // Check whether at least one item is in common between the old and the new suggestions + // since the suggestions popup becoming empty causes flickering + if (!PathModeSuggestionItems.IntersectBy(newSuggestions, x => x.DisplayName).Any()) + { + // No items in common, update the list in-place + for (int index = 0; index < newSuggestions.Count; index++) { - // No items in common, update the list in-place - for (int index = 0; index < newSuggestions.Count; index++) + if (index < PathModeSuggestionItems.Count) { - if (index < PathModeSuggestionItems.Count) - { - PathModeSuggestionItems[index] = newSuggestions[index]; - } - else - { - PathModeSuggestionItems.Add(newSuggestions[index]); - } + PathModeSuggestionItems[index] = newSuggestions[index]; + } + else + { + PathModeSuggestionItems.Add(newSuggestions[index]); } - - while (PathModeSuggestionItems.Count > newSuggestions.Count) - PathModeSuggestionItems.RemoveAt(PathModeSuggestionItems.Count - 1); } - else - { - // At least an element in common, show animation - foreach (var s in PathModeSuggestionItems.ExceptBy(newSuggestions, x => x.DisplayName).ToList()) - PathModeSuggestionItems.Remove(s); - for (int index = 0; index < newSuggestions.Count; index++) + while (PathModeSuggestionItems.Count > newSuggestions.Count) + PathModeSuggestionItems.RemoveAt(PathModeSuggestionItems.Count - 1); + } + else + { + // At least an element in common, show animation + foreach (var s in PathModeSuggestionItems.ExceptBy(newSuggestions, x => x.DisplayName).ToList()) + PathModeSuggestionItems.Remove(s); + + for (int index = 0; index < newSuggestions.Count; index++) + { + if (PathModeSuggestionItems.Count > index && PathModeSuggestionItems[index].DisplayName == newSuggestions[index].DisplayName) { - if (PathModeSuggestionItems.Count > index && PathModeSuggestionItems[index].DisplayName == newSuggestions[index].DisplayName) - { - PathModeSuggestionItems[index] = newSuggestions[index]; - } - else - PathModeSuggestionItems.Insert(index, newSuggestions[index]); + PathModeSuggestionItems[index] = newSuggestions[index]; } + else + PathModeSuggestionItems.Insert(index, newSuggestions[index]); } }