Skip to content

Commit 15b70a5

Browse files
committed
Added populating suggestions for Path mode in Omnibar
1 parent 404e3bd commit 15b70a5

File tree

6 files changed

+153
-3
lines changed

6 files changed

+153
-3
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,18 @@ public partial class Omnibar
1313
[GeneratedDependencyProperty]
1414
public partial OmnibarMode? CurrentSelectedMode { get; set; }
1515

16+
[GeneratedDependencyProperty]
17+
public partial string? CurrentSelectedModeName { get; set; }
18+
1619
[GeneratedDependencyProperty]
1720
public partial Thickness AutoSuggestBoxPadding { get; set; }
1821

1922
[GeneratedDependencyProperty]
2023
public partial bool IsFocused { get; set; }
24+
25+
partial void OnCurrentSelectedModeChanged(OmnibarMode? newValue)
26+
{
27+
CurrentSelectedModeName = newValue?.ModeName;
28+
}
2129
}
2230
}

src/Files.App/Data/Items/NavigationBarSuggestionItem.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace Files.App.Data.Items
55
{
6+
[Obsolete("Remove once Omnibar goes out of experimental.")]
67
public sealed partial class NavigationBarSuggestionItem : ObservableObject
78
{
89
private string? _Text;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
namespace Files.App.Data.Models
5+
{
6+
internal record OmnibarPathModeSuggestionModel(string Path, string DisplayName);
7+
}

src/Files.App/UserControls/NavigationToolbar.xaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
xmlns:converters1="using:CommunityToolkit.WinUI.Converters"
1111
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
1212
xmlns:dataitems="using:Files.App.Data.Items"
13+
xmlns:datamodels="using:Files.App.Data.Models"
1314
xmlns:helpers="using:Files.App.Helpers"
1415
xmlns:items="using:Files.App.Data.Items"
1516
xmlns:keyboard="using:Files.App.UserControls.KeyboardShortcut"
@@ -339,14 +340,16 @@
339340
x:Name="Omnibar"
340341
Grid.Column="1"
341342
x:Load="{x:Bind ViewModel.EnableOmnibar, Mode=OneWay}"
343+
CurrentSelectedModeName="{x:Bind ViewModel.OmnibarCurrentSelectedModeName, Mode=TwoWay}"
342344
IsFocused="{x:Bind ViewModel.IsOmnibarFocused, Mode=TwoWay}">
343345

344346
<controls:OmnibarMode
345347
IconOnActive="{controls:ThemedIconMarkup Style={StaticResource App.ThemedIcons.Omnibar.Path}, IsFilled=True}"
346348
IconOnInactive="{controls:ThemedIconMarkup Style={StaticResource App.ThemedIcons.Omnibar.Path}, IconType=Outline}"
347349
IsDefault="True"
348-
ModeName="{helpers:ResourceString Name=Path}"
350+
ModeName="Path"
349351
PlaceholderText="{helpers:ResourceString Name=OmnibarPathModeTextPlaceholder}"
352+
SuggestionItemsSource="{x:Bind ViewModel.PathModeSuggestionItems, Mode=OneWay}"
350353
Text="{x:Bind ViewModel.OmnibarPathModeText, Mode=OneWay}">
351354
<controls:OmnibarMode.ContentOnInactive>
352355
<controls:BreadcrumbBar x:Name="NewBreadcrumbBar" ItemsSource="{x:Bind ViewModel.PathComponents, Mode=OneWay}">
@@ -363,6 +366,11 @@
363366
</controls:BreadcrumbBar.ItemTemplate>
364367
</controls:BreadcrumbBar>
365368
</controls:OmnibarMode.ContentOnInactive>
369+
<controls:OmnibarMode.SuggestionItemTemplate>
370+
<DataTemplate x:DataType="datamodels:OmnibarPathModeSuggestionModel">
371+
<TextBlock Text="{x:Bind DisplayName, Mode=OneWay}" />
372+
</DataTemplate>
373+
</controls:OmnibarMode.SuggestionItemTemplate>
366374
</controls:OmnibarMode>
367375

368376
<controls:OmnibarMode

src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33

44
using CommunityToolkit.WinUI;
5+
using Files.App.Controls;
56
using Files.Shared.Helpers;
67
using Microsoft.UI.Dispatching;
78
using 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
{

src/Files.App/Views/Shells/BaseShellPage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ protected async void ShellPage_PathBoxItemDropped(object sender, PathBoxItemDrop
417417

418418
protected async void ShellPage_AddressBarTextEntered(object sender, AddressBarTextEnteredEventArgs e)
419419
{
420-
await ToolbarViewModel.SetAddressBarSuggestionsAsync(e.AddressBarTextField, this);
420+
await ToolbarViewModel.SetLegacyAddressBarSuggestionsAsync(e.AddressBarTextField, this);
421421
}
422422

423423
protected async void ShellPage_ToolbarPathItemLoaded(object sender, ToolbarPathItemLoadedEventArgs e)

0 commit comments

Comments
 (0)