22// Licensed under the MIT License.
33
44using CommunityToolkit . WinUI ;
5+ using Files . App . Controls ;
56using Files . Shared . Helpers ;
67using Microsoft . UI . Dispatching ;
78using Microsoft . UI . Xaml ;
@@ -21,6 +22,10 @@ public sealed partial class NavigationToolbarViewModel : ObservableObject, IAddr
2122
2223 private const int MaxSuggestionsCount = 10 ;
2324
25+ public const string OmnibarPathModeName = "Path" ;
26+ public const string OmnibarPaletteModeName = "Palette" ;
27+ public const string OmnibarSearchModeName = "Search" ;
28+
2429 // Dependency injections
2530
2631 private readonly IUserSettingsService UserSettingsService = Ioc . Default . GetRequiredService < IUserSettingsService > ( ) ;
@@ -29,6 +34,7 @@ public sealed partial class NavigationToolbarViewModel : ObservableObject, IAddr
2934 private readonly DrivesViewModel drivesViewModel = Ioc . Default . GetRequiredService < DrivesViewModel > ( ) ;
3035 private readonly IUpdateService UpdateService = Ioc . Default . GetRequiredService < IUpdateService > ( ) ;
3136 private readonly ICommandManager Commands = Ioc . Default . GetRequiredService < ICommandManager > ( ) ;
37+ private readonly IContentPageContext ContentPageContext = Ioc . Default . GetRequiredService < IContentPageContext > ( ) ;
3238
3339 // Fields
3440
@@ -62,6 +68,8 @@ public sealed partial class NavigationToolbarViewModel : ObservableObject, IAddr
6268
6369 public ObservableCollection < NavigationBarSuggestionItem > NavigationBarSuggestions { get ; } = [ ] ;
6470
71+ internal ObservableCollection < OmnibarPathModeSuggestionModel > PathModeSuggestionItems { get ; } = [ ] ;
72+
6573 public bool IsSingleItemOverride { get ; set ; }
6674
6775 public bool SearchHasFocus { get ; private set ; }
@@ -205,11 +213,28 @@ public bool IsOmnibarFocused
205213 if ( value )
206214 {
207215 EditModeEnabled ? . Invoke ( this , EventArgs . Empty ) ;
216+
217+ switch ( OmnibarCurrentSelectedModeName )
218+ {
219+ case OmnibarPathModeName :
220+ _ = PopulateOmnibarSuggestionsForPathMode ( ) ;
221+ break ;
222+ case OmnibarPaletteModeName :
223+ break ;
224+ case OmnibarSearchModeName :
225+ break ;
226+ default :
227+ throw new ArgumentOutOfRangeException ( "" ) ;
228+ }
229+
208230 }
209231 }
210232 }
211233 }
212234
235+ private string _OmnibarCurrentSelectedModeName ;
236+ public string OmnibarCurrentSelectedModeName { get => _OmnibarCurrentSelectedModeName ; set => SetProperty ( ref _OmnibarCurrentSelectedModeName , value ) ; }
237+
213238 private string _OmnibarPathModeText ;
214239 public string OmnibarPathModeText { get => _OmnibarPathModeText ; set => SetProperty ( ref _OmnibarPathModeText , value ) ; }
215240
@@ -892,7 +917,108 @@ private static async Task<bool> LaunchApplicationFromPath(string currentInput, s
892917 ) ;
893918 }
894919
895- public async Task SetAddressBarSuggestionsAsync ( AutoSuggestBox sender , IShellPage shellpage )
920+ public async Task PopulateOmnibarSuggestionsForPathMode ( )
921+ {
922+ var result = await SafetyExtensions . IgnoreExceptions ( async ( ) =>
923+ {
924+ List < OmnibarPathModeSuggestionModel > ? newSuggestions = [ ] ;
925+ var pathText = OmnibarPathModeText ;
926+
927+ // If the current input is special, populate navigation history instead.
928+ if ( string . IsNullOrWhiteSpace ( pathText ) ||
929+ pathText is "Home" or "ReleaseNotes" or "Settings" )
930+ {
931+ // Load previously entered path
932+ if ( UserSettingsService . GeneralSettingsService . PathHistoryList is { } pathHistoryList )
933+ {
934+ newSuggestions . AddRange ( pathHistoryList . Select ( x => new OmnibarPathModeSuggestionModel ( x , x ) ) ) ;
935+ }
936+ }
937+ else
938+ {
939+ var isFtp = FtpHelpers . IsFtpPath ( pathText ) ;
940+ pathText = NormalizePathInput ( pathText , isFtp ) ;
941+ var expandedPath = StorageFileExtensions . GetResolvedPath ( pathText , isFtp ) ;
942+ var folderPath = PathNormalization . GetParentDir ( expandedPath ) ?? expandedPath ;
943+ StorageFolderWithPath folder = await ContentPageContext . ShellPage . ShellViewModel . GetFolderWithPathFromPathAsync ( folderPath ) ;
944+ if ( folder is null )
945+ return false ;
946+
947+ var currPath = await folder . GetFoldersWithPathAsync ( Path . GetFileName ( expandedPath ) , MaxSuggestionsCount ) ;
948+ if ( currPath . Count >= MaxSuggestionsCount )
949+ {
950+ newSuggestions . AddRange ( currPath . Select ( x => new OmnibarPathModeSuggestionModel ( x . Path , x . Item . DisplayName ) ) ) ;
951+ }
952+ else if ( currPath . Any ( ) )
953+ {
954+ var subPath = await currPath . First ( ) . GetFoldersWithPathAsync ( ( uint ) ( MaxSuggestionsCount - currPath . Count ) ) ;
955+ newSuggestions . AddRange ( currPath . Select ( x => new OmnibarPathModeSuggestionModel ( x . Path , x . Item . DisplayName ) ) ) ;
956+ newSuggestions . AddRange ( subPath . Select ( x => new OmnibarPathModeSuggestionModel ( x . Path , PathNormalization . Combine ( currPath . First ( ) . Item . DisplayName , x . Item . DisplayName ) ) ) ) ;
957+ }
958+
959+ // If there are no suggestions, show "No suggestions"
960+ if ( newSuggestions . Count is 0 )
961+ {
962+ AddNoResultsItem ( ) ;
963+ }
964+
965+ // Check whether at least one item is in common between the old and the new suggestions
966+ // since Omnibar suggestions popup becoming empty causes flickering
967+ if ( ! PathModeSuggestionItems . IntersectBy ( newSuggestions , x => x . DisplayName ) . Any ( ) )
968+ {
969+ // No items in common, update the list in-place
970+ for ( int index = 0 ; index < newSuggestions . Count ; index ++ )
971+ {
972+ if ( index < PathModeSuggestionItems . Count )
973+ {
974+ PathModeSuggestionItems [ index ] = newSuggestions [ index ] ;
975+ }
976+ else
977+ {
978+ PathModeSuggestionItems . Add ( newSuggestions [ index ] ) ;
979+ }
980+ }
981+
982+ while ( PathModeSuggestionItems . Count > newSuggestions . Count )
983+ PathModeSuggestionItems . RemoveAt ( PathModeSuggestionItems . Count - 1 ) ;
984+ }
985+ else
986+ {
987+ // At least an element in common, show animation
988+ foreach ( var s in PathModeSuggestionItems . ExceptBy ( newSuggestions , x => x . DisplayName ) . ToList ( ) )
989+ PathModeSuggestionItems . Remove ( s ) ;
990+
991+ for ( int index = 0 ; index < newSuggestions . Count ; index ++ )
992+ {
993+ if ( PathModeSuggestionItems . Count > index && PathModeSuggestionItems [ index ] . DisplayName == newSuggestions [ index ] . DisplayName )
994+ {
995+ PathModeSuggestionItems [ index ] = newSuggestions [ index ] ;
996+ }
997+ else
998+ PathModeSuggestionItems . Insert ( index , newSuggestions [ index ] ) ;
999+ }
1000+ }
1001+ }
1002+
1003+ return true ;
1004+ } ) ;
1005+
1006+ if ( ! result )
1007+ {
1008+ AddNoResultsItem ( ) ;
1009+ }
1010+
1011+ void AddNoResultsItem ( )
1012+ {
1013+ PathModeSuggestionItems . Clear ( ) ;
1014+ PathModeSuggestionItems . Add ( new (
1015+ ContentPageContext . ShellPage . ShellViewModel . WorkingDirectory ,
1016+ Strings . NavigationToolbarVisiblePathNoResults . GetLocalizedResource ( ) ) ) ;
1017+ }
1018+ }
1019+
1020+ [ Obsolete ( "Remove once Omnibar goes out of experimental." ) ]
1021+ public async Task SetLegacyAddressBarSuggestionsAsync ( AutoSuggestBox sender , IShellPage shellpage )
8961022 {
8971023 if ( sender . Text is not null && shellpage . ShellViewModel is not null )
8981024 {
0 commit comments