diff --git a/src/Files.App.Controls/Omnibar/EventArgs.cs b/src/Files.App.Controls/Omnibar/EventArgs.cs index 6f0d5ca518ba..e702c0d8d3cb 100644 --- a/src/Files.App.Controls/Omnibar/EventArgs.cs +++ b/src/Files.App.Controls/Omnibar/EventArgs.cs @@ -8,4 +8,6 @@ public record class OmnibarQuerySubmittedEventArgs(OmnibarMode Mode, object? Ite public record class OmnibarSuggestionChosenEventArgs(OmnibarMode Mode, object SelectedItem); public record class OmnibarTextChangedEventArgs(OmnibarMode Mode, OmnibarTextChangeReason Reason); + + public record class OmnibarModeChangedEventArgs(OmnibarMode? OldMode, OmnibarMode NewMode); } diff --git a/src/Files.App.Controls/Omnibar/IOmnibarTextMemberPathProvider.cs b/src/Files.App.Controls/Omnibar/IOmnibarTextMemberPathProvider.cs index 41f798da7e7f..d38f0d3aeb8c 100644 --- a/src/Files.App.Controls/Omnibar/IOmnibarTextMemberPathProvider.cs +++ b/src/Files.App.Controls/Omnibar/IOmnibarTextMemberPathProvider.cs @@ -4,7 +4,7 @@ namespace Files.App.Controls { /// - /// An interface that provides a way to get the text member path of . + /// An interface that provides a way to get the text member path of . /// /// /// An alternative to this interface is to use an powered by CsWinRT. diff --git a/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs b/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs index fb3cc94db2a5..cd156dc4cea2 100644 --- a/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs +++ b/src/Files.App.Controls/Omnibar/Omnibar.Properties.cs @@ -28,7 +28,7 @@ partial void OnCurrentSelectedModePropertyChanged(DependencyPropertyChangedEvent return; ChangeMode(e.OldValue as OmnibarMode, newMode); - CurrentSelectedModeName = newMode.ModeName; + CurrentSelectedModeName = newMode.Name; } partial void OnCurrentSelectedModeNameChanged(string? newValue) diff --git a/src/Files.App.Controls/Omnibar/Omnibar.cs b/src/Files.App.Controls/Omnibar/Omnibar.cs index 6ab43cea4e14..e13c04274558 100644 --- a/src/Files.App.Controls/Omnibar/Omnibar.cs +++ b/src/Files.App.Controls/Omnibar/Omnibar.cs @@ -41,6 +41,7 @@ public partial class Omnibar : Control public event TypedEventHandler? QuerySubmitted; public event TypedEventHandler? SuggestionChosen; public event TypedEventHandler? TextChanged; + public event TypedEventHandler? ModeChanged; // Constructor @@ -155,9 +156,11 @@ protected void ChangeMode(OmnibarMode? oldMode, OmnibarMode newMode) VisualStateManager.GoToState(newMode, "Focused", true); newMode.IsTabStop = false; + ModeChanged?.Invoke(this, new(oldMode, newMode!)); + _textBox.PlaceholderText = newMode.PlaceholderText ?? string.Empty; - _textBoxSuggestionsListView.ItemTemplate = newMode.SuggestionItemTemplate; - _textBoxSuggestionsListView.ItemsSource = newMode.SuggestionItemsSource; + _textBoxSuggestionsListView.ItemTemplate = newMode.ItemTemplate; + _textBoxSuggestionsListView.ItemsSource = newMode.ItemsSource; if (newMode.IsAutoFocusEnabled) { @@ -179,10 +182,10 @@ protected void ChangeMode(OmnibarMode? oldMode, OmnibarMode newMode) { VisualStateManager.GoToState(_textBox, "InputAreaVisible", true); } - - TryToggleIsSuggestionsPopupOpen(true); } + TryToggleIsSuggestionsPopupOpen(true); + // Remove the reposition transition from the all modes foreach (var mode in Modes) { @@ -196,12 +199,17 @@ internal protected void FocusTextBox() _textBox.Focus(FocusState.Keyboard); } - public bool TryToggleIsSuggestionsPopupOpen(bool wantToOpen) + internal protected bool TryToggleIsSuggestionsPopupOpen(bool wantToOpen) { - if (wantToOpen && (!IsFocused || CurrentSelectedMode?.SuggestionItemsSource is null || (CurrentSelectedMode?.SuggestionItemsSource is IList collection && collection.Count is 0)) || - _textBoxSuggestionsPopup is null) + if (_textBoxSuggestionsPopup is null) return false; + if (wantToOpen && (!IsFocused || CurrentSelectedMode?.ItemsSource is null || (CurrentSelectedMode?.ItemsSource is IList collection && collection.Count is 0))) + { + _textBoxSuggestionsPopup.IsOpen = false; + return false; + } + _textBoxSuggestionsPopup.IsOpen = wantToOpen; return false; diff --git a/src/Files.App.Controls/Omnibar/Omnibar.xaml b/src/Files.App.Controls/Omnibar/Omnibar.xaml index 26c90b65eb0f..c08f2fc5de71 100644 --- a/src/Files.App.Controls/Omnibar/Omnibar.xaml +++ b/src/Files.App.Controls/Omnibar/Omnibar.xaml @@ -145,11 +145,21 @@ - + + + + + - + + @@ -181,13 +192,16 @@ - + + + - + + diff --git a/src/Files.App.Controls/Omnibar/OmnibarMode.Properties.cs b/src/Files.App.Controls/Omnibar/OmnibarMode.Properties.cs index 6245ebbca02f..3130d95d305c 100644 --- a/src/Files.App.Controls/Omnibar/OmnibarMode.Properties.cs +++ b/src/Files.App.Controls/Omnibar/OmnibarMode.Properties.cs @@ -28,15 +28,9 @@ public partial class OmnibarMode [GeneratedDependencyProperty] public partial FrameworkElement? IconOnInactive { get; set; } - [GeneratedDependencyProperty] - public partial object? SuggestionItemsSource { get; set; } - - [GeneratedDependencyProperty] - public partial DataTemplate? SuggestionItemTemplate { get; set; } - [GeneratedDependencyProperty] /// - /// Implement in to get the text member path from the suggestion item correctly. + /// Implement in to get the text member path from the suggestion item correctly. /// public partial string? TextMemberPath { get; set; } diff --git a/src/Files.App.Controls/Omnibar/OmnibarMode.cs b/src/Files.App.Controls/Omnibar/OmnibarMode.cs index b4d5927383a9..1a3b49e975d9 100644 --- a/src/Files.App.Controls/Omnibar/OmnibarMode.cs +++ b/src/Files.App.Controls/Omnibar/OmnibarMode.cs @@ -6,7 +6,7 @@ namespace Files.App.Controls { [DebuggerDisplay("{" + nameof(ToString) + "(),nq}")] - public partial class OmnibarMode : Control + public partial class OmnibarMode : ItemsControl { // Constants @@ -66,6 +66,14 @@ protected override void OnKeyUp(KeyRoutedEventArgs args) } } + protected override void OnItemsChanged(object e) + { + base.OnItemsChanged(e); + + if (_ownerRef is not null && _ownerRef.TryGetTarget(out var owner)) + owner.TryToggleIsSuggestionsPopupOpen(true); + } + private void OmnibarMode_Loaded(object sender, RoutedEventArgs e) { // Set this mode as the current mode if it is the default mode diff --git a/src/Files.App/UserControls/NavigationToolbar.xaml b/src/Files.App/UserControls/NavigationToolbar.xaml index ef9e511e009c..eec566ce5c64 100644 --- a/src/Files.App/UserControls/NavigationToolbar.xaml +++ b/src/Files.App/UserControls/NavigationToolbar.xaml @@ -321,7 +321,6 @@ x:Name="Omnibar" Grid.Column="1" x:Load="{x:Bind ViewModel.EnableOmnibar, Mode=OneWay}" - CurrentSelectedMode="{x:Bind ViewModel.OmnibarCurrentSelectedMode, Mode=TwoWay}" CurrentSelectedModeName="{x:Bind ViewModel.OmnibarCurrentSelectedModeName, Mode=TwoWay}" IsFocused="{x:Bind ViewModel.IsOmnibarFocused, Mode=TwoWay}" LostFocus="Omnibar_LostFocus" @@ -334,9 +333,9 @@ IconOnActive="{controls:ThemedIconMarkup Style={StaticResource App.ThemedIcons.Omnibar.Path}, IsFilled=True}" IconOnInactive="{controls:ThemedIconMarkup Style={StaticResource App.ThemedIcons.Omnibar.Path}, IconType=Outline}" IsDefault="True" + ItemsSource="{x:Bind ViewModel.PathModeSuggestionItems, Mode=OneWay}" ModeName="{x:Bind Commands.EditPath.LabelWithHotKey, Mode=OneWay}" PlaceholderText="{helpers:ResourceString Name=OmnibarPathModeTextPlaceholder}" - SuggestionItemsSource="{x:Bind ViewModel.PathModeSuggestionItems, Mode=OneWay}" Text="{x:Bind ViewModel.PathText, Mode=TwoWay}" TextMemberPath="Path"> @@ -365,11 +364,11 @@ - + - + - + @@ -422,7 +421,7 @@ HotKeys="{x:Bind HotKeys}" /> - + (); private readonly ICommandManager Commands = Ioc.Default.GetRequiredService(); private readonly StatusCenterViewModel OngoingTasksViewModel = Ioc.Default.GetRequiredService(); + private readonly IContentPageContext ContentPageContext = Ioc.Default.GetRequiredService(); // Properties @@ -413,9 +414,9 @@ private void BreadcrumbBar_ItemDropDownFlyoutClosed(object sender, BreadcrumbBar private void Omnibar_LostFocus(object sender, RoutedEventArgs e) { - if (ViewModel.OmnibarCurrentSelectedMode == OmnibarCommandPaletteMode) + if (Omnibar.CurrentSelectedMode == OmnibarCommandPaletteMode) { - ViewModel.OmnibarCurrentSelectedMode = OmnibarPathMode; + Omnibar.CurrentSelectedMode = OmnibarPathMode; ViewModel.OmnibarCommandPaletteModeText = string.Empty; } } diff --git a/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs index 8332c2aa3a04..216f0835c966 100644 --- a/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs @@ -4,6 +4,7 @@ using CommunityToolkit.WinUI; using Files.App.Controls; using Files.Shared.Helpers; +using Microsoft.Extensions.Logging; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -242,7 +243,7 @@ public bool IsOmnibarFocused if (value) { - switch (OmnibarCurrentSelectedMode.Name) + switch (OmnibarCurrentSelectedModeName) { case OmnibarPathModeName: PathText = @@ -263,11 +264,34 @@ public bool IsOmnibarFocused } } - private OmnibarMode _OmnibarCurrentSelectedMode; - public OmnibarMode OmnibarCurrentSelectedMode { get => _OmnibarCurrentSelectedMode; set => SetProperty(ref _OmnibarCurrentSelectedMode, value); } - - private string _OmnibarCurrentSelectedModeName; - public string OmnibarCurrentSelectedModeName { get => _OmnibarCurrentSelectedModeName; set => SetProperty(ref _OmnibarCurrentSelectedModeName, value); } + private string _OmnibarCurrentSelectedModeName = OmnibarPathModeName; + public string OmnibarCurrentSelectedModeName + { + get => _OmnibarCurrentSelectedModeName; + set + { + if (SetProperty(ref _OmnibarCurrentSelectedModeName, value) && IsOmnibarFocused) + { + switch (value) + { + case OmnibarPathModeName: + PathText = + string.IsNullOrEmpty(ContentPageContext.ShellPage?.ShellViewModel?.WorkingDirectory) + ? Constants.UserEnvironmentPaths.HomePath + : ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory; + _ = PopulateOmnibarSuggestionsForPathMode(); + break; + case OmnibarPaletteModeName: + PopulateOmnibarSuggestionsForCommandPaletteMode(); + break; + case OmnibarSearchModeName: + break; + default: + break; + } + } + } + } private CurrentInstanceViewModel _InstanceViewModel; public CurrentInstanceViewModel InstanceViewModel @@ -738,7 +762,6 @@ await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidItemDialogTitle.GetLoca } PathControlDisplayText = ContentPageContext.ShellPage.ShellViewModel.WorkingDirectory; - IsOmnibarFocused = false; } public void PathBoxItem_PreviewKeyDown(object sender, KeyRoutedEventArgs e) @@ -1077,6 +1100,8 @@ private static async Task LaunchApplicationFromPath(string currentInput, s public async Task PopulateOmnibarSuggestionsForPathMode() { + PathModeSuggestionItems.Clear(); + var result = await SafetyExtensions.IgnoreExceptions((Func>)(async () => { List? newSuggestions = []; @@ -1117,9 +1142,7 @@ public async Task PopulateOmnibarSuggestionsForPathMode() // If there are no suggestions, show "No suggestions" if (newSuggestions.Count is 0) - { - AddNoResultsItem(); - } + return false; // Check whether at least one item is in common between the old and the new suggestions // since the suggestions popup becoming empty causes flickering diff --git a/tests/Files.App.UITests/Views/OmnibarPage.xaml b/tests/Files.App.UITests/Views/OmnibarPage.xaml index 26a4a1604835..4ca5f82a5a0b 100644 --- a/tests/Files.App.UITests/Views/OmnibarPage.xaml +++ b/tests/Files.App.UITests/Views/OmnibarPage.xaml @@ -50,12 +50,12 @@ + SuggestionItemsSource="{x:Bind Omnibar1_PaletteSuggestions, Mode=OneWay}" + TextMemberPath="{x:Bind Omnibar1_TextMemberPathForPaletteMode}">