Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Files.App/Data/Contracts/IShellPage.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using Microsoft.UI.Xaml.Navigation;

namespace Files.App.Data.Contracts
{
public interface IShellPage : ITabBarItemContent, IMultiPaneInfo, IDisposable, INotifyPropertyChanged
Expand All @@ -11,6 +13,10 @@ public interface IShellPage : ITabBarItemContent, IMultiPaneInfo, IDisposable, I

StorageHistoryHelpers StorageHistoryHelpers { get; }

IList<PageStackEntry> ForwardStack { get; }

IList<PageStackEntry> BackwardStack { get; }

IBaseLayoutPage SlimContentPage { get; }

Type CurrentPageType { get; }
Expand Down
20 changes: 20 additions & 0 deletions src/Files.App/Data/Models/ToolbarHistoryItemModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using Microsoft.UI.Xaml.Navigation;

namespace Files.App.Data.Models
{
internal sealed class ToolbarHistoryItemModel
{
public PageStackEntry PageStackEntry { get; }

public bool IsBackMode { get; }

public ToolbarHistoryItemModel(PageStackEntry pageStackEntry, bool isBackMode)
{
PageStackEntry = pageStackEntry;
IsBackMode = isBackMode;
}
}
}
30 changes: 30 additions & 0 deletions src/Files.App/Helpers/Navigation/NavigationHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Windows.Storage;
using Windows.Storage.Search;
using Windows.System;
using Microsoft.UI.Xaml.Media;

namespace Files.App.Helpers
{
Expand Down Expand Up @@ -130,6 +131,35 @@ private static async Task UpdateTabInfoAsync(TabBarItem tabItem, object navigati
}
}

public static async Task<ImageSource?> GetIconForPathAsync(string path)
{
ImageSource? imageSource;
if (string.IsNullOrEmpty(path) || path == "Home")
imageSource = new BitmapImage(new Uri(Constants.FluentIconsPaths.HomeIcon));
else if (WSLDistroManager.TryGetDistro(path, out WslDistroItem? wslDistro) && path.Equals(wslDistro.Path))
imageSource = new BitmapImage(wslDistro.Icon);
else
{
var normalizedPath = PathNormalization.NormalizePath(path);
var matchingCloudDrive = CloudDrivesManager.Drives.FirstOrDefault(x => normalizedPath.Equals(PathNormalization.NormalizePath(x.Path), StringComparison.OrdinalIgnoreCase));
imageSource = matchingCloudDrive?.Icon;

if (imageSource is null)
{
var result = await FileThumbnailHelper.GetIconAsync(
path,
Constants.ShellIconSizes.Small,
true,
IconOptions.ReturnIconOnly | IconOptions.UseCurrentScale);

if (result is not null)
imageSource = await result.ToBitmapAsync();
}
}

return imageSource;
}

public static async Task<(string tabLocationHeader, IconSource tabIcon, string toolTipText)> GetSelectedTabInfoAsync(string currentPath)
{
string? tabLocationHeader;
Expand Down
38 changes: 35 additions & 3 deletions src/Files.App/UserControls/AddressToolbar.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
xmlns:contract8Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,8)"
xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls"
xmlns:converters="using:Files.App.Converters"
xmlns:converters1="using:CommunityToolkit.WinUI.UI.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:extensions="using:CommunityToolkit.WinUI.UI"
xmlns:helpers="using:Files.App.Helpers"
xmlns:items="using:Files.App.Data.Items"
xmlns:keyboard="using:Files.App.UserControls.KeyboardShortcut"
xmlns:local="using:Files.App.UserControls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:triggers="using:CommunityToolkit.WinUI.UI.Triggers"
xmlns:uc="using:Files.App.UserControls"
xmlns:ucs="using:Files.App.UserControls.StatusCenter"
xmlns:vm="using:Files.App.ViewModels.UserControls"
x:Name="NavToolbar"
Height="50"
d:DesignHeight="50"
Expand Down Expand Up @@ -236,7 +237,7 @@

<!-- Page Navigation Actions -->
<StackPanel
Grid.Row="1"
Grid.Column="0"
Orientation="Horizontal"
Spacing="4">
<Button
Expand All @@ -251,6 +252,22 @@
Style="{StaticResource AddressToolbarButtonStyle}"
ToolTipService.ToolTip="{x:Bind ViewModel.Commands.NavigateBack.LabelWithHotKey, Mode=OneWay}">
<FontIcon FontSize="14" Glyph="{x:Bind ViewModel.Commands.NavigateBack.Glyph.BaseGlyph, Mode=OneWay}" />
<Button.ContextFlyout>
<MenuFlyout
x:Name="BackHistoryFlyout"
Opening="BackHistoryFlyout_Opening"
Placement="BottomEdgeAlignedLeft"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollMode="Auto">
<MenuFlyout.MenuFlyoutPresenterStyle>
<Style TargetType="MenuFlyoutPresenter">
<Setter Property="MaxHeight" Value="400" />
<!-- Workaround for https://github.com/files-community/Files/issues/13078 -->
<Setter Target="HighContrastAdjustment" Value="None" />
</Style>
</MenuFlyout.MenuFlyoutPresenterStyle>
</MenuFlyout>
</Button.ContextFlyout>
</Button>

<Button
Expand All @@ -265,6 +282,22 @@
Style="{StaticResource AddressToolbarButtonStyle}"
ToolTipService.ToolTip="{x:Bind ViewModel.Commands.NavigateForward.LabelWithHotKey, Mode=OneWay}">
<FontIcon FontSize="14" Glyph="{x:Bind ViewModel.Commands.NavigateForward.Glyph.BaseGlyph, Mode=OneWay}" />
<Button.ContextFlyout>
<MenuFlyout
x:Name="ForwardHistoryFlyout"
Opening="ForwardHistoryFlyout_Opening"
Placement="BottomEdgeAlignedLeft"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollMode="Auto">
<MenuFlyout.MenuFlyoutPresenterStyle>
<Style TargetType="MenuFlyoutPresenter">
<Setter Property="MaxHeight" Value="400" />
<!-- Workaround for https://github.com/files-community/Files/issues/13078 -->
<Setter Target="HighContrastAdjustment" Value="None" />
</Style>
</MenuFlyout.MenuFlyoutPresenterStyle>
</MenuFlyout>
</Button.ContextFlyout>
</Button>

<Button
Expand Down Expand Up @@ -344,7 +377,6 @@
x:Name="RightAlignedKeyboardShortcut"
Grid.Column="1"
HotKeys="{x:Bind HotKeys, Mode=OneWay}" />

</Grid>
</StackPanel>
</DataTemplate>
Expand Down
92 changes: 91 additions & 1 deletion src/Files.App/UserControls/AddressToolbar.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using System.Windows.Input;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Windows.System;
using Microsoft.UI.Xaml.Navigation;
using FocusManager = Microsoft.UI.Xaml.Input.FocusManager;

namespace Files.App.UserControls
{
public sealed partial class AddressToolbar : UserControl
{
private readonly IUserSettingsService userSettingsService = Ioc.Default.GetRequiredService<IUserSettingsService>();
private readonly ICommand historyItemClickedCommand;

public ICommandManager Commands { get; } = Ioc.Default.GetRequiredService<ICommandManager>();

// Using a DependencyProperty as the backing store for ShowOngoingTasks. This enables animation, styling, binding, etc...
Expand Down Expand Up @@ -55,7 +59,11 @@ public AddressToolbarViewModel? ViewModel

public StatusCenterViewModel? OngoingTasksViewModel { get; set; }

public AddressToolbar() => InitializeComponent();
public AddressToolbar()
{
InitializeComponent();
historyItemClickedCommand = new RelayCommand<ToolbarHistoryItemModel?>(HistoryItemClicked);
}

private void NavToolbar_Loading(FrameworkElement _, object e)
{
Expand Down Expand Up @@ -150,5 +158,87 @@ private void Button_AccessKeyInvoked(UIElement sender, AccessKeyInvokedEventArgs
if (VisualTreeHelper.GetOpenPopupsForXamlRoot(MainWindow.Instance.Content.XamlRoot).Any())
args.Handled = true;
}

private async void BackHistoryFlyout_Opening(object? sender, object e)
{
var shellPage = Ioc.Default.GetRequiredService<IContentPageContext>().ShellPage;
if (shellPage is null)
return;

await AddHistoryItemsAsync(shellPage.BackwardStack, BackHistoryFlyout.Items, true);
}

private async void ForwardHistoryFlyout_Opening(object? sender, object e)
{
var shellPage = Ioc.Default.GetRequiredService<IContentPageContext>().ShellPage;
if (shellPage is null)
return;

await AddHistoryItemsAsync(shellPage.ForwardStack, ForwardHistoryFlyout.Items, false);
}

private async Task AddHistoryItemsAsync(IEnumerable<PageStackEntry> items, IList<MenuFlyoutItemBase> destination, bool isBackMode)
{
// This may not seem performant, however it's the most viable trade-off to make.
// Instead of constantly keeping track of back/forward stack and performing lookups
// (which may degrade performance), we only add items in bulk when it's needed.
// There's also a high chance the user might not use the feature at all in which case
// the former approach would just waste extra performance gain

destination.Clear();
foreach (var item in items.Reverse())
{
if (item.Parameter is not NavigationArguments args || args.NavPathParam is null)
continue;

var imageSource = await NavigationHelpers.GetIconForPathAsync(args.NavPathParam);
var fileName = SystemIO.Path.GetFileName(args.NavPathParam);

// The fileName is empty if the path is (root) drive path
if (string.IsNullOrEmpty(fileName))
fileName = args.NavPathParam;

destination.Add(new MenuFlyoutItem()
{
Icon = new ImageIcon() { Source = imageSource },
Text = fileName,
Command = historyItemClickedCommand,
CommandParameter = new ToolbarHistoryItemModel(item, isBackMode)
});
}
}

private void HistoryItemClicked(ToolbarHistoryItemModel? itemModel)
{
if (itemModel is null)
return;

var shellPage = Ioc.Default.GetRequiredService<IContentPageContext>().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();
}
}
}
}
9 changes: 6 additions & 3 deletions src/Files.App/Views/Shells/BaseShellPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.UI.Xaml.Navigation;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -62,12 +61,16 @@ public abstract class BaseShellPage : Page, IShellPage, INotifyPropertyChanged

protected abstract Frame ItemDisplay { get; }

public abstract bool CanNavigateForward { get; }
public virtual bool CanNavigateForward => ItemDisplay.CanGoForward;

public abstract bool CanNavigateBackward { get; }
public virtual bool CanNavigateBackward => ItemDisplay.CanGoBack;

public bool IsColumnView => SlimContentPage is ColumnsLayoutPage;

public virtual IList<PageStackEntry> ForwardStack => ItemDisplay.ForwardStack;

public virtual IList<PageStackEntry> BackwardStack => ItemDisplay.BackStack;

public ShellViewModel ShellViewModel { get; protected set; }

public CurrentInstanceViewModel InstanceViewModel { get; }
Expand Down
7 changes: 0 additions & 7 deletions src/Files.App/Views/Shells/ModernShellPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media.Animation;
Expand All @@ -13,12 +12,6 @@ namespace Files.App.Views.Shells
{
public sealed partial class ModernShellPage : BaseShellPage
{
public override bool CanNavigateBackward
=> ItemDisplayFrame.CanGoBack;

public override bool CanNavigateForward
=> ItemDisplayFrame.CanGoForward;

protected override Frame ItemDisplay
=> ItemDisplayFrame;

Expand Down