|
1 | 1 | // Copyright (c) 2024 Files Community |
2 | 2 | // Licensed under the MIT License. See the LICENSE. |
3 | 3 |
|
| 4 | +using System.Windows.Input; |
4 | 5 | using Microsoft.UI.Input; |
5 | 6 | using Microsoft.UI.Xaml; |
6 | 7 | using Microsoft.UI.Xaml.Controls; |
7 | 8 | using Microsoft.UI.Xaml.Controls.Primitives; |
8 | 9 | using Microsoft.UI.Xaml.Input; |
9 | 10 | using Microsoft.UI.Xaml.Media; |
10 | 11 | using Windows.System; |
| 12 | +using Microsoft.UI.Xaml.Navigation; |
11 | 13 | using FocusManager = Microsoft.UI.Xaml.Input.FocusManager; |
12 | 14 |
|
13 | 15 | namespace Files.App.UserControls |
14 | 16 | { |
15 | 17 | public sealed partial class AddressToolbar : UserControl |
16 | 18 | { |
17 | 19 | private readonly IUserSettingsService userSettingsService = Ioc.Default.GetRequiredService<IUserSettingsService>(); |
| 20 | + private readonly ICommand historyItemClickedCommand; |
18 | 21 | private readonly MainPageViewModel MainPageViewModel = Ioc.Default.GetRequiredService<MainPageViewModel>(); |
| 22 | + |
19 | 23 | public ICommandManager Commands { get; } = Ioc.Default.GetRequiredService<ICommandManager>(); |
20 | 24 |
|
21 | 25 | public static readonly DependencyProperty IsSidebarPaneOpenToggleButtonVisibleProperty = |
@@ -64,7 +68,11 @@ public AddressToolbarViewModel? ViewModel |
64 | 68 |
|
65 | 69 | public StatusCenterViewModel? OngoingTasksViewModel { get; set; } |
66 | 70 |
|
67 | | - public AddressToolbar() => InitializeComponent(); |
| 71 | + public AddressToolbar() |
| 72 | + { |
| 73 | + InitializeComponent(); |
| 74 | + historyItemClickedCommand = new RelayCommand<ToolbarHistoryItemModel?>(HistoryItemClicked); |
| 75 | + } |
68 | 76 |
|
69 | 77 | private void NavToolbar_Loading(FrameworkElement _, object e) |
70 | 78 | { |
@@ -159,5 +167,87 @@ private void Button_AccessKeyInvoked(UIElement sender, AccessKeyInvokedEventArgs |
159 | 167 | if (VisualTreeHelper.GetOpenPopupsForXamlRoot(MainWindow.Instance.Content.XamlRoot).Any()) |
160 | 168 | args.Handled = true; |
161 | 169 | } |
| 170 | + |
| 171 | + private async void BackHistoryFlyout_Opening(object? sender, object e) |
| 172 | + { |
| 173 | + var shellPage = Ioc.Default.GetRequiredService<IContentPageContext>().ShellPage; |
| 174 | + if (shellPage is null) |
| 175 | + return; |
| 176 | + |
| 177 | + await AddHistoryItemsAsync(shellPage.BackwardStack, BackHistoryFlyout.Items, true); |
| 178 | + } |
| 179 | + |
| 180 | + private async void ForwardHistoryFlyout_Opening(object? sender, object e) |
| 181 | + { |
| 182 | + var shellPage = Ioc.Default.GetRequiredService<IContentPageContext>().ShellPage; |
| 183 | + if (shellPage is null) |
| 184 | + return; |
| 185 | + |
| 186 | + await AddHistoryItemsAsync(shellPage.ForwardStack, ForwardHistoryFlyout.Items, false); |
| 187 | + } |
| 188 | + |
| 189 | + private async Task AddHistoryItemsAsync(IEnumerable<PageStackEntry> items, IList<MenuFlyoutItemBase> destination, bool isBackMode) |
| 190 | + { |
| 191 | + // This may not seem performant, however it's the most viable trade-off to make. |
| 192 | + // Instead of constantly keeping track of back/forward stack and performing lookups |
| 193 | + // (which may degrade performance), we only add items in bulk when it's needed. |
| 194 | + // There's also a high chance the user might not use the feature at all in which case |
| 195 | + // the former approach would just waste extra performance gain |
| 196 | + |
| 197 | + destination.Clear(); |
| 198 | + foreach (var item in items.Reverse()) |
| 199 | + { |
| 200 | + if (item.Parameter is not NavigationArguments args || args.NavPathParam is null) |
| 201 | + continue; |
| 202 | + |
| 203 | + var imageSource = await NavigationHelpers.GetIconForPathAsync(args.NavPathParam); |
| 204 | + var fileName = SystemIO.Path.GetFileName(args.NavPathParam); |
| 205 | + |
| 206 | + // The fileName is empty if the path is (root) drive path |
| 207 | + if (string.IsNullOrEmpty(fileName)) |
| 208 | + fileName = args.NavPathParam; |
| 209 | + |
| 210 | + destination.Add(new MenuFlyoutItem() |
| 211 | + { |
| 212 | + Icon = new ImageIcon() { Source = imageSource }, |
| 213 | + Text = fileName, |
| 214 | + Command = historyItemClickedCommand, |
| 215 | + CommandParameter = new ToolbarHistoryItemModel(item, isBackMode) |
| 216 | + }); |
| 217 | + } |
| 218 | + } |
| 219 | + |
| 220 | + private void HistoryItemClicked(ToolbarHistoryItemModel? itemModel) |
| 221 | + { |
| 222 | + if (itemModel is null) |
| 223 | + return; |
| 224 | + |
| 225 | + var shellPage = Ioc.Default.GetRequiredService<IContentPageContext>().ShellPage; |
| 226 | + if (shellPage is null) |
| 227 | + return; |
| 228 | + |
| 229 | + if (itemModel.IsBackMode) |
| 230 | + { |
| 231 | + // Remove all entries after the target entry in the BackwardStack |
| 232 | + while (shellPage.BackwardStack.Last() != itemModel.PageStackEntry) |
| 233 | + { |
| 234 | + shellPage.BackwardStack.RemoveAt(shellPage.BackwardStack.Count - 1); |
| 235 | + } |
| 236 | + |
| 237 | + // Navigate back |
| 238 | + shellPage.Back_Click(); |
| 239 | + } |
| 240 | + else |
| 241 | + { |
| 242 | + // Remove all entries before the target entry in the ForwardStack |
| 243 | + while (shellPage.ForwardStack.First() != itemModel.PageStackEntry) |
| 244 | + { |
| 245 | + shellPage.ForwardStack.RemoveAt(0); |
| 246 | + } |
| 247 | + |
| 248 | + // Navigate forward |
| 249 | + shellPage.Forward_Click(); |
| 250 | + } |
| 251 | + } |
162 | 252 | } |
163 | 253 | } |
0 commit comments