From badfe3e5dc1c88c51df392980be5ab262a9cbcf8 Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Sat, 22 Jul 2023 23:27:51 +0200 Subject: [PATCH 01/83] WIP: Initial switching to new sidebar --- src/Files.App/App.xaml | 1 + src/Files.App/Files.App.csproj | 14 + .../ResourceDictionaries/SideBarItem.xaml | 138 +++++++ .../SideBar/SideBarHost.properties.cs | 47 +++ .../UserControls/SideBar/SideBarHost.xaml | 50 +++ .../UserControls/SideBar/SideBarHost.xaml.cs | 336 ++++++++++++++++++ .../UserControls/SideBar/SideBarItem.cs | 277 +++++++++++++++ .../SideBar/SideBarItem.properties.cs | 100 ++++++ .../SideBar/SideBarPane.properties.cs | 71 ++++ .../UserControls/SideBar/SideBarPane.xaml | 83 +++++ .../UserControls/SideBar/SideBarPane.xaml.cs | 124 +++++++ src/Files.App/Views/MainPage.xaml | 19 +- src/Files.App/Views/MainPage.xaml.cs | 8 +- 13 files changed, 1256 insertions(+), 12 deletions(-) create mode 100644 src/Files.App/ResourceDictionaries/SideBarItem.xaml create mode 100644 src/Files.App/UserControls/SideBar/SideBarHost.properties.cs create mode 100644 src/Files.App/UserControls/SideBar/SideBarHost.xaml create mode 100644 src/Files.App/UserControls/SideBar/SideBarHost.xaml.cs create mode 100644 src/Files.App/UserControls/SideBar/SideBarItem.cs create mode 100644 src/Files.App/UserControls/SideBar/SideBarItem.properties.cs create mode 100644 src/Files.App/UserControls/SideBar/SideBarPane.properties.cs create mode 100644 src/Files.App/UserControls/SideBar/SideBarPane.xaml create mode 100644 src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs diff --git a/src/Files.App/App.xaml b/src/Files.App/App.xaml index 30fc256cd8af..b4d817f2d1fe 100644 --- a/src/Files.App/App.xaml +++ b/src/Files.App/App.xaml @@ -29,6 +29,7 @@ + diff --git a/src/Files.App/Files.App.csproj b/src/Files.App/Files.App.csproj index 8f7a8c035981..bd869d728125 100644 --- a/src/Files.App/Files.App.csproj +++ b/src/Files.App/Files.App.csproj @@ -68,6 +68,11 @@ + + + + + @@ -130,4 +135,13 @@ + + + $(DefaultXamlRuntime) + + + MSBuild:Compile + + + diff --git a/src/Files.App/ResourceDictionaries/SideBarItem.xaml b/src/Files.App/ResourceDictionaries/SideBarItem.xaml new file mode 100644 index 000000000000..69c24cf2c6cf --- /dev/null +++ b/src/Files.App/ResourceDictionaries/SideBarItem.xaml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/UserControls/SideBar/SideBarHost.properties.cs b/src/Files.App/UserControls/SideBar/SideBarHost.properties.cs new file mode 100644 index 000000000000..abb61a01b24b --- /dev/null +++ b/src/Files.App/UserControls/SideBar/SideBarHost.properties.cs @@ -0,0 +1,47 @@ +using Microsoft.UI.Xaml; + +namespace Files.App.UserControls.SideBar +{ + public sealed partial class SideBarHost + { + public object Items + { + get { return (object)GetValue(ItemsProperty); } + set { SetValue(ItemsProperty, value); } + } + public static readonly DependencyProperty ItemsProperty = + DependencyProperty.Register("Items", typeof(object), typeof(SideBarHost), new PropertyMetadata(null)); + + public SideBarDisplayMode DisplayMode + { + get { return (SideBarDisplayMode)GetValue(DisplayModeProperty); } + set { SetValue(DisplayModeProperty, value); } + } + public event EventHandler? DisplayModeChanged; + public static readonly DependencyProperty DisplayModeProperty = + DependencyProperty.Register("DisplayMode", typeof(SideBarDisplayMode), typeof(SideBarPane), new PropertyMetadata(SideBarDisplayMode.Expanded)); + + public UIElement InnerContent + { + get { return (UIElement)GetValue(InnerContentProperty); } + set { SetValue(InnerContentProperty, value); } + } + public static readonly DependencyProperty InnerContentProperty = + DependencyProperty.Register("InnerContent", typeof(UIElement), typeof(SideBarHost), new PropertyMetadata(null)); + + public object SelectedItem + { + get { return (object)GetValue(SelectedItemProperty); } + set { SetValue(SelectedItemProperty, value); } + } + public static readonly DependencyProperty SelectedItemProperty = + DependencyProperty.Register("SelectedItem", typeof(object), typeof(SideBarHost), new PropertyMetadata(null)); + + public UIElement TabContent + { + get => (UIElement)GetValue(TabContentProperty); + set => SetValue(TabContentProperty, value); + } + public static readonly DependencyProperty TabContentProperty = DependencyProperty.Register(nameof(TabContent), typeof(UIElement), typeof(SideBarHost), new PropertyMetadata(null)); + } +} diff --git a/src/Files.App/UserControls/SideBar/SideBarHost.xaml b/src/Files.App/UserControls/SideBar/SideBarHost.xaml new file mode 100644 index 000000000000..8c11e453ea93 --- /dev/null +++ b/src/Files.App/UserControls/SideBar/SideBarHost.xaml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/UserControls/SideBar/SideBarHost.xaml.cs b/src/Files.App/UserControls/SideBar/SideBarHost.xaml.cs new file mode 100644 index 000000000000..fee2803d06fc --- /dev/null +++ b/src/Files.App/UserControls/SideBar/SideBarHost.xaml.cs @@ -0,0 +1,336 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using CommunityToolkit.WinUI.UI; +using Files.App.Helpers.ContextFlyouts; +using Files.App.ViewModels.Dialogs; +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.Markup; +using System.Runtime.CompilerServices; +using System.Windows.Input; +using Windows.ApplicationModel.DataTransfer; +using Windows.ApplicationModel.DataTransfer.DragDrop; +using Windows.System; +using Windows.UI.Core; +using DispatcherQueueTimer = Microsoft.UI.Dispatching.DispatcherQueueTimer; + +namespace Files.App.UserControls.SideBar +{ + [ContentProperty(Name = "InnerContent")] + public sealed partial class SideBarHost : UserControl, INotifyPropertyChanged + { + private readonly IUserSettingsService userSettingsService = Ioc.Default.GetRequiredService(); + private readonly ICommandManager commands = Ioc.Default.GetRequiredService(); + + public IQuickAccessService QuickAccessService { get; } = Ioc.Default.GetRequiredService(); + + public delegate void SidebarItemInvokedEventHandler(object sender, SidebarItemInvokedEventArgs e); + + public event SidebarItemInvokedEventHandler SidebarItemInvoked; + + public delegate void SidebarItemNewPaneInvokedEventHandler(object sender, SidebarItemNewPaneInvokedEventArgs e); + + public event SidebarItemNewPaneInvokedEventHandler SidebarItemNewPaneInvoked; + + public delegate void SidebarItemPropertiesInvokedEventHandler(object sender, SidebarItemPropertiesInvokedEventArgs e); + + public event SidebarItemPropertiesInvokedEventHandler SidebarItemPropertiesInvoked; + + public delegate void SidebarItemDroppedEventHandler(object sender, SidebarItemDroppedEventArgs e); + + public event SidebarItemDroppedEventHandler SidebarItemDropped; + + private INavigationControlItem rightClickedItem; + + private object? dragOverSection, dragOverItem = null; + + private bool isDropOnProcess = false; + + private double originalSize = 0; + + private bool lockFlag = false; + + public SidebarPinnedModel SidebarPinnedModel => App.QuickAccessManager.Model; + + // Using a DependencyProperty as the backing store for ViewModel. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ViewModelProperty = + DependencyProperty.Register(nameof(ViewModel), typeof(SidebarViewModel), typeof(SidebarControl), new PropertyMetadata(null)); + + public static readonly DependencyProperty SelectedSidebarItemProperty = DependencyProperty.Register(nameof(SelectedSidebarItem), typeof(INavigationControlItem), typeof(SidebarControl), new PropertyMetadata(null)); + + public INavigationControlItem SelectedSidebarItem + { + get => (INavigationControlItem)GetValue(SelectedSidebarItemProperty); + set + { + if (IsLoaded) + SetValue(SelectedSidebarItemProperty, value); + } + } + + public readonly ICommand CreateLibraryCommand = new AsyncRelayCommand(LibraryManager.ShowCreateNewLibraryDialog); + + public readonly ICommand RestoreLibrariesCommand = new AsyncRelayCommand(LibraryManager.ShowRestoreDefaultLibrariesDialog); + + private ICommand HideSectionCommand { get; } + + private ICommand PinItemCommand { get; } + + private ICommand UnpinItemCommand { get; } + + private ICommand OpenInNewTabCommand { get; } + + private ICommand OpenInNewWindowCommand { get; } + + private ICommand OpenInNewPaneCommand { get; } + + private ICommand EjectDeviceCommand { get; } + + private ICommand FormatDriveCommand { get; } + + private ICommand OpenPropertiesCommand { get; } + + private ICommand ReorderItemsCommand { get; } + + private bool IsInPointerPressed = false; + + private readonly DispatcherQueueTimer dragOverSectionTimer, dragOverItemTimer; + + public SideBarHost() + { + InitializeComponent(); + + dragOverSectionTimer = DispatcherQueue.CreateTimer(); + dragOverItemTimer = DispatcherQueue.CreateTimer(); + + HideSectionCommand = new RelayCommand(HideSection); + UnpinItemCommand = new RelayCommand(UnpinItem); + PinItemCommand = new RelayCommand(PinItem); + OpenInNewTabCommand = new AsyncRelayCommand(OpenInNewTab); + OpenInNewWindowCommand = new AsyncRelayCommand(OpenInNewWindow); + OpenInNewPaneCommand = new AsyncRelayCommand(OpenInNewPane); + EjectDeviceCommand = new AsyncRelayCommand(EjectDevice); + FormatDriveCommand = new RelayCommand(FormatDrive); + OpenPropertiesCommand = new RelayCommand(OpenProperties); + ReorderItemsCommand = new AsyncRelayCommand(ReorderItems); + } + + public SidebarViewModel ViewModel + { + get => (SidebarViewModel)GetValue(ViewModelProperty); + set => SetValue(ViewModelProperty, value); + } + + private bool canOpenInNewPane; + + public bool CanOpenInNewPane + { + get => canOpenInNewPane; + set + { + if (value != canOpenInNewPane) + { + canOpenInNewPane = value; + NotifyPropertyChanged(nameof(CanOpenInNewPane)); + } + } + } + + public event PropertyChangedEventHandler? PropertyChanged; + + private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + private async Task OpenInNewPane() + { + if (await DriveHelpers.CheckEmptyDrive(rightClickedItem.Path)) + return; + + SidebarItemNewPaneInvoked?.Invoke(this, new SidebarItemNewPaneInvokedEventArgs(rightClickedItem)); + } + + private async Task OpenInNewTab() + { + if (await DriveHelpers.CheckEmptyDrive(rightClickedItem.Path)) + return; + + await NavigationHelpers.OpenPathInNewTab(rightClickedItem.Path); + } + + private async Task OpenInNewWindow() + { + if (await DriveHelpers.CheckEmptyDrive(rightClickedItem.Path)) + return; + + await NavigationHelpers.OpenPathInNewWindowAsync(rightClickedItem.Path); + } + + private void PinItem() + { + if (rightClickedItem is DriveItem) + _ = QuickAccessService.PinToSidebar(new[] { rightClickedItem.Path }); + } + private void UnpinItem() + { + if (rightClickedItem.Section == SectionType.Favorites || rightClickedItem is DriveItem) + _ = QuickAccessService.UnpinFromSidebar(rightClickedItem.Path); + } + + private void HideSection() + { + switch (rightClickedItem.Section) + { + case SectionType.Favorites: + userSettingsService.GeneralSettingsService.ShowFavoritesSection = false; + break; + case SectionType.Library: + userSettingsService.GeneralSettingsService.ShowLibrarySection = false; + break; + case SectionType.CloudDrives: + userSettingsService.GeneralSettingsService.ShowCloudDrivesSection = false; + break; + case SectionType.Drives: + userSettingsService.GeneralSettingsService.ShowDrivesSection = false; + break; + case SectionType.Network: + userSettingsService.GeneralSettingsService.ShowNetworkDrivesSection = false; + break; + case SectionType.WSL: + userSettingsService.GeneralSettingsService.ShowWslSection = false; + break; + case SectionType.FileTag: + userSettingsService.GeneralSettingsService.ShowFileTagsSection = false; + break; + } + } + + private async Task ReorderItems() + { + var dialog = new ReorderSidebarItemsDialogViewModel(); + var dialogService = Ioc.Default.GetRequiredService(); + var result = await dialogService.ShowDialogAsync(dialog); + } + + private void OpenProperties(CommandBarFlyout menu) + { + EventHandler flyoutClosed = null!; + flyoutClosed = (s, e) => + { + menu.Closed -= flyoutClosed; + SidebarItemPropertiesInvoked?.Invoke(this, new SidebarItemPropertiesInvokedEventArgs(rightClickedItem)); + }; + menu.Closed += flyoutClosed; + } + + private async Task EjectDevice() + { + var result = await DriveHelpers.EjectDeviceAsync(rightClickedItem.Path); + await UIHelpers.ShowDeviceEjectResultAsync(result); + } + + private void FormatDrive() + { + Win32API.OpenFormatDriveDialog(rightClickedItem.Path); + } + + private async void SideBar_ItemInvoked(object sender, object item) + { + var navigationPath = item as string; + + if (await DriveHelpers.CheckEmptyDrive(navigationPath)) + return; + + var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); + if ((IsInPointerPressed || ctrlPressed) && navigationPath is not null) + { + await NavigationHelpers.OpenPathInNewTab(navigationPath); + return; + } + } + + private void SidebarNavView_Loaded(object sender, RoutedEventArgs e) + { + } + + private void SideBar_DisplayModeChanged(object sender, SideBarDisplayMode e) + { + if (e == SideBarDisplayMode.Minimal) + { + } + } + + private void SideBar_Loaded(object sender, RoutedEventArgs e) + { + (this.FindDescendant("TabContentBorder") as Border)!.Child = TabContent; + } + } + + public class SidebarItemDroppedEventArgs : EventArgs + { + public DataPackageView Package { get; set; } + public string ItemPath { get; set; } + public DataPackageOperation AcceptedOperation { get; set; } + public AsyncManualResetEvent SignalEvent { get; set; } + } + + public class SidebarItemInvokedEventArgs : EventArgs + { + public NavigationViewItemBase InvokedItemContainer { get; set; } + + public SidebarItemInvokedEventArgs(NavigationViewItemBase ItemContainer) + { + InvokedItemContainer = ItemContainer; + } + } + + public class SidebarItemPropertiesInvokedEventArgs : EventArgs + { + public object InvokedItemDataContext { get; set; } + + public SidebarItemPropertiesInvokedEventArgs(object invokedItemDataContext) + { + InvokedItemDataContext = invokedItemDataContext; + } + } + + public class SidebarItemNewPaneInvokedEventArgs : EventArgs + { + public object InvokedItemDataContext { get; set; } + + public SidebarItemNewPaneInvokedEventArgs(object invokedItemDataContext) + { + InvokedItemDataContext = invokedItemDataContext; + } + } + + public class NavItemDataTemplateSelector : DataTemplateSelector + { + public DataTemplate LocationNavItemTemplate { get; set; } + public DataTemplate DriveNavItemTemplate { get; set; } + public DataTemplate LinuxNavItemTemplate { get; set; } + public DataTemplate FileTagNavItemTemplate { get; set; } + public DataTemplate HeaderNavItemTemplate { get; set; } + + protected override DataTemplate? SelectTemplateCore(object item) + { + if (item is null || item is not INavigationControlItem navControlItem) + return null; + + return navControlItem.ItemType switch + { + NavigationControlItemType.Location => LocationNavItemTemplate, + NavigationControlItemType.Drive => DriveNavItemTemplate, + NavigationControlItemType.CloudDrive => DriveNavItemTemplate, + NavigationControlItemType.LinuxDistro => LinuxNavItemTemplate, + NavigationControlItemType.FileTag => FileTagNavItemTemplate, + _ => null + }; + } + } +} diff --git a/src/Files.App/UserControls/SideBar/SideBarItem.cs b/src/Files.App/UserControls/SideBar/SideBarItem.cs new file mode 100644 index 000000000000..2dbdfb6eb64c --- /dev/null +++ b/src/Files.App/UserControls/SideBar/SideBarItem.cs @@ -0,0 +1,277 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +using CommunityToolkit.WinUI.UI; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Windows.ApplicationModel.DataTransfer; + +namespace Files.App.UserControls.SideBar +{ + public sealed partial class SideBarItem : Control + { + + private bool isPointerOver = false; + + private object? selectedChildItem = null; + + public bool CollapsableChildren => DisplayMode != SideBarDisplayMode.Compact; + private bool HasChildSelection => selectedChildItem != null; + + public SideBarItem() + { + DefaultStyleKey = typeof(SideBarItem); + DataContext = this; + + PointerReleased += Item_PointerReleased; + KeyDown += (sender, args) => + { + args.Handled = true; + if (args.Key == Windows.System.VirtualKey.Enter) + { + Clicked(); + } + }; + CanDrag = true; + DragStarting += SideBarItem_DragStarting; + + Loaded += SideBarItem_Loaded; + } + + private void SideBarItem_DragStarting(UIElement sender, DragStartingEventArgs args) + { + args.Data.SetData(StandardDataFormats.Text, this.DataContext.ToString()); + } + + private void SetFlyoutOpen(bool isOpen = true) + { + if (Items is null) return; + + var flyoutOwner = (GetTemplateChild("ElementGrid") as FrameworkElement)!; + if (isOpen) + { + FlyoutBase.ShowAttachedFlyout(flyoutOwner); + } + else + { + FlyoutBase.GetAttachedFlyout(flyoutOwner).Hide(); + } + } + + private void SideBarItem_Loaded(object sender, RoutedEventArgs e) + { + HookupOwners(); + + if (GetTemplateChild("ElementGrid") is Grid grid) + { + grid.PointerEntered += ItemGrid_PointerEntered; + grid.PointerExited += ItemGrid_PointerExited; + grid.PointerCanceled += ItemGrid_PointerCanceled; + grid.PointerPressed += ItemGrid_PointerPressed; + grid.ContextRequested += ItemGrid_ContextRequested; + grid.DragLeave += ItemGrid_DragLeave; + grid.DragOver += ItemGrid_DragOver; + grid.Drop += ItemGrid_Drop; + grid.AllowDrop = true; + } + + if (GetTemplateChild("ChildrenPresenter") is ItemsRepeater repeater) + { + repeater.ElementPrepared += ChildrenPresenter_ElementPrepared; + } + if (GetTemplateChild("FlyoutChildrenPresenter") is ItemsRepeater flyoutRepeater) + { + flyoutRepeater.ElementPrepared += ChildrenPresenter_ElementPrepared; + } + + UpdateExpansionState(Items); + } + + private void ItemGrid_ContextRequested(UIElement sender, Microsoft.UI.Xaml.Input.ContextRequestedEventArgs args) + { + Owner.RaiseContextRequested(this); + } + + private void HookupOwners() + { + FrameworkElement resolvingTarget = this; + if (GetTemplateRoot(Parent) is FrameworkElement element) + { + resolvingTarget = element; + } + Owner = resolvingTarget.FindAscendant()!; + + Owner.RegisterPropertyChangedCallback(SideBarPane.DisplayModeProperty, (sender, args) => + { + DisplayMode = Owner.DisplayMode; + }); + Owner.RegisterPropertyChangedCallback(SideBarPane.SelectedItemProperty, (sender, args) => + { + ReevaluateSelection(); + }); + } + + private void ReevaluateSelection() + { + if (Items is null) + { + IsSelected = DataContext == Owner.SelectedItem; + } + else if (Items is IList list) + { + if (list.Contains(Owner.SelectedItem)) + { + selectedChildItem = Owner.SelectedItem; + } + else + { + selectedChildItem = null; + } + UpdateSelectionState(); + } + } + + private void ChildrenPresenter_ElementPrepared(ItemsRepeater sender, ItemsRepeaterElementPreparedEventArgs args) + { + if (Items is IList enumerable) + { + var newElement = enumerable[args.Index]; + if (newElement == selectedChildItem) + { + (args.Element as SideBarItem)!.IsSelected = true; + } + else + { + (args.Element as SideBarItem)!.IsSelected = false; + } + } + } + + private void Clicked() + { + if (CollapsableChildren) + { + IsExpanded = !IsExpanded; + } + else + { + SetFlyoutOpen(true); + } + Owner?.RaiseItemInvoked(this); + } + + private void SideBarDisplayModeChanged(SideBarDisplayMode displayMode) + { + switch (displayMode) + { + case SideBarDisplayMode.Expanded: + UpdateExpansionState(Items); + UpdateSelectionState(); + SetFlyoutOpen(false); + break; + case SideBarDisplayMode.Minimal: + UpdateExpansionState(Items); + SetFlyoutOpen(false); + break; + case SideBarDisplayMode.Compact: + UpdateExpansionState(null); + UpdateSelectionState(); + break; + } + } + + private void UpdateSelectionState() + { + VisualStateManager.GoToState(this, ShouldShowSelectionIndicator() ? "Selected" : "Unselected", true); + } + + private bool ShouldShowSelectionIndicator() + { + if (IsExpanded && CollapsableChildren) + { + return IsSelected; + } + else + { + return IsSelected || HasChildSelection; + } + } + + private void UpdateExpansionState(object? childContent) + { + if (childContent == null) + { + VisualStateManager.GoToState(this, "NoExpansion", true); + } + else + { + VisualStateManager.GoToState(this, IsExpanded ? "Expanded" : "Collapsed", true); + } + UpdateSelectionState(); + } + + private void ItemGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) + { + VisualStateManager.GoToState(this, "PointerOver", true); + isPointerOver = true; + } + + private void ItemGrid_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) + { + VisualStateManager.GoToState(this, "Normal", true); + isPointerOver = false; + } + + private void ItemGrid_PointerCanceled(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) + { + VisualStateManager.GoToState(this, "Normal", true); + } + + private void ItemGrid_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) + { + VisualStateManager.GoToState(this, "Pressed", true); + } + + private void Item_PointerReleased(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) + { + VisualStateManager.GoToState(this, isPointerOver ? "PointerOver" : "Normal", true); + e.Handled = true; + Clicked(); + } + + private void ItemGrid_DragOver(object sender, DragEventArgs e) + { + e.AcceptedOperation = DataPackageOperation.Move; + if (DragTargetAboveCenter(e)) + { + VisualStateManager.GoToState(this, "DragInsertAbove", true); + } + else + { + VisualStateManager.GoToState(this, "DragInsertBelow", true); + } + } + + private bool DragTargetAboveCenter(DragEventArgs args) + { + if (GetTemplateChild("ElementGrid") is Grid grid) + { + var position = args.GetPosition(grid); + return position.Y < grid.ActualHeight / 2; + } + return false; + } + + private void ItemGrid_DragLeave(object sender, DragEventArgs e) + { + VisualStateManager.GoToState(this, "NoDrag", true); + } + + private void ItemGrid_Drop(object sender, DragEventArgs e) + { + VisualStateManager.GoToState(this, "NoDrag", true); + Owner.RaiseItemDropped(this, e, DragTargetAboveCenter(e)); + } + } +} diff --git a/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs b/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs new file mode 100644 index 000000000000..4c40f8a4d734 --- /dev/null +++ b/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs @@ -0,0 +1,100 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.UserControls.SideBar +{ + public sealed partial class SideBarItem : Control + { + public SideBarPane Owner + { + get { return (SideBarPane)GetValue(OwnerProperty); } + set { SetValue(OwnerProperty, value); } + } + public static readonly DependencyProperty OwnerProperty = + DependencyProperty.Register("Owner", typeof(SideBarPane), typeof(SideBarItem), new PropertyMetadata(null)); + + public bool IsSelected + { + get { return (bool)GetValue(IsSelectedProperty); } + set { SetValue(IsSelectedProperty, value); } + } + public static readonly DependencyProperty IsSelectedProperty = + DependencyProperty.Register("IsSelected", typeof(bool), typeof(SideBarItem), new PropertyMetadata(false, OnPropertyChanged)); + + public bool IsExpanded + { + get { return (bool)GetValue(IsExpandedProperty); } + set { SetValue(IsExpandedProperty, value); } + } + public static readonly DependencyProperty IsExpandedProperty = + DependencyProperty.Register("IsExpanded", typeof(bool), typeof(SideBarItem), new PropertyMetadata(false, OnPropertyChanged)); + + public object Icon + { + get { return (object)GetValue(IconProperty); } + set { SetValue(IconProperty, value); } + } + public static readonly DependencyProperty IconProperty = + DependencyProperty.Register("Icon", typeof(object), typeof(SideBarItem), new PropertyMetadata(null)); + + public string Text + { + get { return (string)GetValue(TextProperty); } + set { SetValue(TextProperty, value); } + } + public static readonly DependencyProperty TextProperty = + DependencyProperty.Register("Text", typeof(string), typeof(SideBarItem), new PropertyMetadata(null)); + + public object Items + { + get { return (object)GetValue(ItemsProperty); } + set { SetValue(ItemsProperty, value); } + } + public static readonly DependencyProperty ItemsProperty = + DependencyProperty.Register("Items", typeof(object), typeof(SideBarItem), new PropertyMetadata(null)); + + public SideBarDisplayMode DisplayMode + { + get { return (SideBarDisplayMode)GetValue(DisplayModeProperty); } + set { SetValue(DisplayModeProperty, value); } + } + public static readonly DependencyProperty DisplayModeProperty = + DependencyProperty.Register("DisplayMode", typeof(SideBarDisplayMode), typeof(SideBarItem), new PropertyMetadata(SideBarDisplayMode.Expanded, OnPropertyChanged)); + + public static void SetTemplateRoot(DependencyObject target, FrameworkElement value) + { + target.SetValue(TemplateRootProperty, value); + } + public static FrameworkElement GetTemplateRoot(DependencyObject target) + { + return (FrameworkElement)target.GetValue(TemplateRootProperty); + } + public static readonly DependencyProperty TemplateRootProperty = + DependencyProperty.Register("TemplateRoot", typeof(FrameworkElement), typeof(FrameworkElement), new PropertyMetadata(null)); + + public static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + if (sender is not SideBarItem item) return; + if (e.Property == DisplayModeProperty) + { + item.SideBarDisplayModeChanged((SideBarDisplayMode)e.NewValue); + } + else if (e.Property == IsSelectedProperty) + { + item.UpdateSelectionState(); + } + else if (e.Property == IsExpandedProperty) + { + item.UpdateExpansionState(item.Items); + } + else if (e.Property == ItemsProperty) + { + item.UpdateExpansionState((UIElement)e.NewValue); + } + else if(e.Property == DataContextProperty) + { + item.ReevaluateSelection(); + } + } + } +} diff --git a/src/Files.App/UserControls/SideBar/SideBarPane.properties.cs b/src/Files.App/UserControls/SideBar/SideBarPane.properties.cs new file mode 100644 index 000000000000..ec8f15dc9a66 --- /dev/null +++ b/src/Files.App/UserControls/SideBar/SideBarPane.properties.cs @@ -0,0 +1,71 @@ +using Microsoft.UI.Xaml; + +namespace Files.App.UserControls.SideBar +{ + public enum SideBarDisplayMode + { + Minimal, + Compact, + Expanded + } + + public sealed partial class SideBarPane + { + public object Items + { + get { return (object)GetValue(ItemsProperty); } + set { SetValue(ItemsProperty, value); } + } + public static readonly DependencyProperty ItemsProperty = + DependencyProperty.Register("Items", typeof(object), typeof(SideBarPane), new PropertyMetadata(null)); + + public SideBarDisplayMode DisplayMode + { + get { return (SideBarDisplayMode)GetValue(DisplayModeProperty); } + set { SetValue(DisplayModeProperty, value); } + } + public event EventHandler? DisplayModeChanged; + public static readonly DependencyProperty DisplayModeProperty = + DependencyProperty.Register("DisplayMode", typeof(SideBarDisplayMode), typeof(SideBarPane), new PropertyMetadata(SideBarDisplayMode.Expanded, OnPropertyChanged)); + + public UIElement InnerContent + { + get { return (UIElement)GetValue(InnerContentProperty); } + set { SetValue(InnerContentProperty, value); } + } + public static readonly DependencyProperty InnerContentProperty = + DependencyProperty.Register("InnerContent", typeof(UIElement), typeof(SideBarPane), new PropertyMetadata(null)); + + public object SelectedItem + { + get { return (object)GetValue(SelectedItemProperty); } + set { SetValue(SelectedItemProperty, value); } + } + public static readonly DependencyProperty SelectedItemProperty = + DependencyProperty.Register("SelectedItem", typeof(object), typeof(SideBarPane), new PropertyMetadata(null)); + + public bool PaneExpanded + { + get { return (bool)GetValue(PaneExpandedProperty); } + set { SetValue(PaneExpandedProperty, value); } + } + public static readonly DependencyProperty PaneExpandedProperty = + DependencyProperty.Register("PaneExpanded", typeof(bool), typeof(SideBarPane), new PropertyMetadata(false, OnPropertyChanged)); + + + public static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + if (sender is not SideBarPane control) return; + + if (e.Property == DisplayModeProperty) + { + control.UpdateDisplayMode(); + control.DisplayModeChanged?.Invoke(control, (SideBarDisplayMode)e.NewValue); + } + else if (e.Property == PaneExpandedProperty) + { + control.UpdateMinimalMode(); + } + } + } +} diff --git a/src/Files.App/UserControls/SideBar/SideBarPane.xaml b/src/Files.App/UserControls/SideBar/SideBarPane.xaml new file mode 100644 index 000000000000..3f058ce70389 --- /dev/null +++ b/src/Files.App/UserControls/SideBar/SideBarPane.xaml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs b/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs new file mode 100644 index 000000000000..105313aa4601 --- /dev/null +++ b/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs @@ -0,0 +1,124 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Markup; +using System; +using Windows.ApplicationModel.DataTransfer; + +namespace Files.App.UserControls.SideBar +{ + [ContentProperty(Name = "InnerContent")] + public sealed partial class SideBarPane : UserControl + { + private const double COMPACT_MAX_WIDTH = 200; + + private double preManipulationSideBarWidth = 0; + + public record ItemDroppedEventArgs(object DropTarget, DataPackageView DroppedItem, bool InsertAbove) { } + + public event EventHandler? ItemDropped; + public event EventHandler? ItemInvoked; + public event EventHandler? ItemContextInvoked; + + public SideBarPane() + { + this.InitializeComponent(); + Loaded += SideBar_Loaded; + } + + private void SideBar_Loaded(object sender, RoutedEventArgs e) + { + if(DisplayMode != SideBarDisplayMode.Minimal) + { + UpdateDisplayModeForWidth(MenuItemsHost.ActualWidth); + } + else + { + UpdateDisplayMode(); + } + } + + public void RaiseContextRequested(SideBarItem item) + { + // Only leaves can be selected + ItemContextInvoked?.Invoke(item, item.DataContext); + } + + public void RaiseItemInvoked(SideBarItem item) + { + // Only leaves can be selected + if (item.Items is not null) return; + SelectedItem = item.DataContext; + ItemInvoked?.Invoke(item, SelectedItem); + } + + private void UpdateDisplayMode() + { + switch (DisplayMode) + { + case SideBarDisplayMode.Compact: + VisualStateManager.GoToState(this, "Compact", false); + return; + case SideBarDisplayMode.Expanded: + VisualStateManager.GoToState(this, "Expanded", false); + return; + case SideBarDisplayMode.Minimal: + PaneExpanded = false; + UpdateMinimalMode(); + return; + } + } + + private void UpdateMinimalMode() + { + if (DisplayMode != SideBarDisplayMode.Minimal) return; + + if (PaneExpanded) + { + VisualStateManager.GoToState(this, "MinimalExpanded", false); + } + else + { + VisualStateManager.GoToState(this, "MinimalCollapsed", false); + } + } + + internal void RaiseItemDropped(SideBarItem sideBarItem, DragEventArgs e, bool insertsAbove) + { + ItemDropped?.Invoke(this, new ItemDroppedEventArgs(sideBarItem.DataContext, e.DataView, insertsAbove)); + } + + private void GridSplitter_ManipulationStarted(object sender, Microsoft.UI.Xaml.Input.ManipulationStartedRoutedEventArgs e) + { + preManipulationSideBarWidth = MenuItemsHost.ActualWidth; + } + + private void GridSplitter_ManipulationDelta(object sender, Microsoft.UI.Xaml.Input.ManipulationDeltaRoutedEventArgs e) + { + var newWidth = preManipulationSideBarWidth + e.Cumulative.Translation.X; + UpdateDisplayModeForWidth(newWidth); + } + + private void UpdateDisplayModeForWidth(double newWidth) + { + if (newWidth < COMPACT_MAX_WIDTH) + { + DisplayMode = SideBarDisplayMode.Compact; + } + else if (newWidth > COMPACT_MAX_WIDTH) + { + DisplayMode = SideBarDisplayMode.Expanded; + DisplayColumn.Width = new GridLength(newWidth); + } + } + + private void PaneLightDismissLayer_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) + { + PaneExpanded = false; + } + + private void PaneLightDismissLayer_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e) + { + PaneExpanded = false; + } + } +} diff --git a/src/Files.App/Views/MainPage.xaml b/src/Files.App/Views/MainPage.xaml index 7936af8ba96e..c69c6200f7d3 100644 --- a/src/Files.App/Views/MainPage.xaml +++ b/src/Files.App/Views/MainPage.xaml @@ -9,6 +9,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:toolkit="using:CommunityToolkit.WinUI.UI.Controls" xmlns:uc="using:Files.App.UserControls" + xmlns:sidebar="using:Files.App.UserControls.SideBar" xmlns:usercontrols="using:Files.App.UserControls.MultitaskingControl" xmlns:viewmodels="using:Files.App.ViewModels" xmlns:wctconverters="using:CommunityToolkit.WinUI.UI.Converters" @@ -110,21 +111,23 @@ - + - + @@ -181,7 +184,7 @@ TabIndex="1" /> - + - + diff --git a/src/Files.App/Views/MainPage.xaml.cs b/src/Files.App/Views/MainPage.xaml.cs index e46c4895db6f..c6c7f51bdceb 100644 --- a/src/Files.App/Views/MainPage.xaml.cs +++ b/src/Files.App/Views/MainPage.xaml.cs @@ -197,10 +197,10 @@ protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.OnNavigatedTo(e); - SidebarControl.SidebarItemInvoked += SidebarControl_SidebarItemInvoked; - SidebarControl.SidebarItemPropertiesInvoked += SidebarControl_SidebarItemPropertiesInvoked; - SidebarControl.SidebarItemDropped += SidebarControl_SidebarItemDropped; - SidebarControl.SidebarItemNewPaneInvoked += SidebarControl_SidebarItemNewPaneInvoked; + //SidebarControl.SidebarItemInvoked += SidebarControl_SidebarItemInvoked; + //SidebarControl.SidebarItemPropertiesInvoked += SidebarControl_SidebarItemPropertiesInvoked; + //SidebarControl.SidebarItemDropped += SidebarControl_SidebarItemDropped; + //SidebarControl.SidebarItemNewPaneInvoked += SidebarControl_SidebarItemNewPaneInvoked; } protected override async void OnPreviewKeyDown(KeyRoutedEventArgs e) From 1bfef44e90990c6c56723ec56c3828f8c34eb6cd Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Sun, 23 Jul 2023 15:44:38 +0200 Subject: [PATCH 02/83] Get icons to work --- src/Files.App/Data/Items/DriveItem.cs | 15 +- src/Files.App/Data/Items/FileTagItem.cs | 17 +- .../Data/Items/INavigationControlItem.cs | 8 +- src/Files.App/Data/Items/LocationItem.cs | 12 +- src/Files.App/Data/Items/WslDistroItem.cs | 21 ++- .../ResourceDictionaries/SideBarItem.xaml | 37 ++-- .../UserControls/SideBar/SideBarHost.xaml | 1 + .../UserControls/SideBar/SideBarHost.xaml.cs | 168 +++++++++++++++++- .../UserControls/SideBar/SideBarItem.cs | 49 ++++- .../SideBar/SideBarItem.properties.cs | 34 ++-- .../SideBar/SideBarItem.resources.cs | 13 ++ .../UserControls/SideBar/SideBarPane.xaml | 16 +- .../UserControls/SideBar/SideBarPane.xaml.cs | 22 ++- .../UserControls/SidebarControl.xaml | 2 +- .../Utils/Global/WSLDistroManager.cs | 2 +- src/Files.App/ViewModels/MainPageViewModel.cs | 2 +- 16 files changed, 338 insertions(+), 81 deletions(-) create mode 100644 src/Files.App/UserControls/SideBar/SideBarItem.resources.cs diff --git a/src/Files.App/Data/Items/DriveItem.cs b/src/Files.App/Data/Items/DriveItem.cs index 413e851d6409..08b9f0fe4e75 100644 --- a/src/Files.App/Data/Items/DriveItem.cs +++ b/src/Files.App/Data/Items/DriveItem.cs @@ -6,7 +6,9 @@ using Files.Core.Storage.Enums; using Files.Core.Storage.LocatableStorage; using Files.Core.Storage.NestedStorage; +using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Imaging; using Windows.Storage; using Windows.Storage.Streams; @@ -20,7 +22,10 @@ public class DriveItem : ObservableObject, INavigationControlItem, ILocatableFol public BitmapImage Icon { get => icon; - set => SetProperty(ref icon, value); + set + { + SetProperty(ref icon, value, nameof(Icon)); + } } public byte[] IconData { get; set; } @@ -152,6 +157,13 @@ public bool ShowStorageSense public string Name => Root.DisplayName; + public BulkConcurrentObservableCollection? ChildItems => null; + + public IconSource? GenerateIconSource() => new ImageIconSource() + { + ImageSource = Icon + }; + public DriveItem() { ItemType = NavigationControlItemType.CloudDrive; @@ -261,7 +273,6 @@ public async Task LoadThumbnailAsync(bool isSidebar = false) } IconData ??= UIHelpers.GetSidebarIconResourceInfo(Constants.ImageRes.Folder).IconData; } - Icon ??= await IconData.ToBitmapAsync(); } diff --git a/src/Files.App/Data/Items/FileTagItem.cs b/src/Files.App/Data/Items/FileTagItem.cs index 7b18b3f76c81..66e0ab68ea88 100644 --- a/src/Files.App/Data/Items/FileTagItem.cs +++ b/src/Files.App/Data/Items/FileTagItem.cs @@ -1,11 +1,17 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. +using CommunityToolkit.WinUI.Helpers; +using Files.App.Converters; using Files.Core.ViewModels.FileTags; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Markup; +using Microsoft.UI.Xaml.Media; namespace Files.App.Data.Items { - public class FileTagItem : INavigationControlItem + public class FileTagItem : ObservableObject, INavigationControlItem { public string Text { get; set; } @@ -17,6 +23,7 @@ public string Path { path = value; ToolTipText = Text; + OnPropertyChanged("Icon"); } } @@ -33,5 +40,13 @@ public int CompareTo(INavigationControlItem other) => Text.CompareTo(other.Text); public TagViewModel FileTag { get; set; } + + public BulkConcurrentObservableCollection? ChildItems => null; + + public IconSource? GenerateIconSource() => new PathIconSource() + { + Data = (Geometry)XamlBindingHelper.ConvertValue(typeof(Geometry), (string)Application.Current.Resources["ColorIconFilledTag"]), + Foreground = new SolidColorBrush(FileTag.Color.ToColor()) + }; } } diff --git a/src/Files.App/Data/Items/INavigationControlItem.cs b/src/Files.App/Data/Items/INavigationControlItem.cs index 6535a5cc14c5..c784d8c2e2c1 100644 --- a/src/Files.App/Data/Items/INavigationControlItem.cs +++ b/src/Files.App/Data/Items/INavigationControlItem.cs @@ -1,9 +1,11 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. +using Microsoft.UI.Xaml.Controls; + namespace Files.App.Data.Items { - public interface INavigationControlItem : IComparable + public interface INavigationControlItem : IComparable, INotifyPropertyChanged { public string Text { get; } @@ -16,6 +18,10 @@ public interface INavigationControlItem : IComparable public NavigationControlItemType ItemType { get; } public ContextMenuOptions MenuOptions { get; } + + public BulkConcurrentObservableCollection? ChildItems { get; } + + public IconSource? GenerateIconSource(); } public enum NavigationControlItemType diff --git a/src/Files.App/Data/Items/LocationItem.cs b/src/Files.App/Data/Items/LocationItem.cs index e8bb1cce6524..6d52c86a8857 100644 --- a/src/Files.App/Data/Items/LocationItem.cs +++ b/src/Files.App/Data/Items/LocationItem.cs @@ -3,6 +3,7 @@ using CommunityToolkit.WinUI; using Files.App.Utils.Shell; +using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Imaging; using System.IO; @@ -14,11 +15,12 @@ public class LocationItem : ObservableObject, INavigationControlItem public BitmapImage Icon { get => icon; - set => SetProperty(ref icon, value); + set + { + SetProperty(ref icon, value, nameof(Icon)); + } } - //public Uri IconSource { get; set; } - public byte[] IconData { get; set; } public string Text { get; set; } = ""; @@ -48,6 +50,10 @@ public NavigationControlItemType ItemType public bool IsDefaultLocation { get; set; } public BulkConcurrentObservableCollection ChildItems { get; set; } + public IconSource? GenerateIconSource() => new ImageIconSource() + { + ImageSource = icon + }; public bool SelectsOnInvoked { get; set; } = true; diff --git a/src/Files.App/Data/Items/WslDistroItem.cs b/src/Files.App/Data/Items/WslDistroItem.cs index 2be4fd90d89e..593ca3bc2cd6 100644 --- a/src/Files.App/Data/Items/WslDistroItem.cs +++ b/src/Files.App/Data/Items/WslDistroItem.cs @@ -1,9 +1,11 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. +using Microsoft.UI.Xaml.Controls; + namespace Files.App.Data.Items { - public class WslDistroItem : INavigationControlItem + public class WslDistroItem : ObservableObject, INavigationControlItem { public string Text { get; set; } @@ -23,12 +25,27 @@ public string Path public NavigationControlItemType ItemType => NavigationControlItemType.LinuxDistro; - public Uri Logo { get; set; } + private Uri icon; + public Uri Icon + { + get => icon; + set + { + SetProperty(ref icon, value, nameof(Icon)); + } + } public SectionType Section { get; set; } public ContextMenuOptions MenuOptions { get; set; } + public BulkConcurrentObservableCollection? ChildItems => null; + public IconSource? GenerateIconSource() => new BitmapIconSource() + { + UriSource = icon, + ShowAsMonochrome = false, + }; + public int CompareTo(INavigationControlItem other) => Text.CompareTo(other.Text); } } diff --git a/src/Files.App/ResourceDictionaries/SideBarItem.xaml b/src/Files.App/ResourceDictionaries/SideBarItem.xaml index 69c24cf2c6cf..01fbea3ff438 100644 --- a/src/Files.App/ResourceDictionaries/SideBarItem.xaml +++ b/src/Files.App/ResourceDictionaries/SideBarItem.xaml @@ -2,9 +2,14 @@ + xmlns:local="using:Files.App.UserControls.SideBar" xmlns:items="using:Files.App.Data.Items"> + + + + - - - - - - - + - - - - - - + ItemsSource="{Binding ChildItems,Mode=OneWay}" + ItemTemplate="{StaticResource DefaultSideBarItemTemplate}" + local:SideBarItem.TemplateRoot="{Binding ElementName=RootPanel}" HorizontalAlignment="Stretch"/> diff --git a/src/Files.App/UserControls/SideBar/SideBarHost.xaml b/src/Files.App/UserControls/SideBar/SideBarHost.xaml index 8c11e453ea93..bfae144c947b 100644 --- a/src/Files.App/UserControls/SideBar/SideBarHost.xaml +++ b/src/Files.App/UserControls/SideBar/SideBarHost.xaml @@ -42,6 +42,7 @@ Grid.ColumnSpan="2" Loaded="SideBar_Loaded" ItemInvoked="SideBar_ItemInvoked" + ItemContextInvoked="SideBar_ItemContextInvoked" InnerContent="{x:Bind InnerContent}" DisplayMode="{x:Bind DisplayMode}" Items="{x:Bind ViewModel.SideBarItems, Mode=OneWay}" diff --git a/src/Files.App/UserControls/SideBar/SideBarHost.xaml.cs b/src/Files.App/UserControls/SideBar/SideBarHost.xaml.cs index fee2803d06fc..54dd2c467f80 100644 --- a/src/Files.App/UserControls/SideBar/SideBarHost.xaml.cs +++ b/src/Files.App/UserControls/SideBar/SideBarHost.xaml.cs @@ -8,12 +8,10 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Markup; using System.Runtime.CompilerServices; using System.Windows.Input; using Windows.ApplicationModel.DataTransfer; -using Windows.ApplicationModel.DataTransfer.DragDrop; using Windows.System; using Windows.UI.Core; using DispatcherQueueTimer = Microsoft.UI.Dispatching.DispatcherQueueTimer; @@ -258,6 +256,172 @@ private void SidebarNavView_Loaded(object sender, RoutedEventArgs e) { } + private List GetLocationItemMenuItems(INavigationControlItem item, CommandBarFlyout menu) + { + var options = item.MenuOptions; + + var favoriteModel = App.QuickAccessManager.Model; + var favoriteIndex = favoriteModel.IndexOfItem(item); + var favoriteCount = favoriteModel.FavoriteItems.Count; + + var isFavoriteItem = item.Section is SectionType.Favorites && favoriteIndex is not -1; + var showMoveItemUp = isFavoriteItem && favoriteIndex > 0; + var showMoveItemDown = isFavoriteItem && favoriteIndex < favoriteCount - 1; + + var isDriveItem = item is DriveItem; + var isDriveItemPinned = isDriveItem && ((DriveItem)item).IsPinned; + + return new List() + { + new ContextMenuFlyoutItemViewModel() + { + Text = "SideBarCreateNewLibrary/Text".GetLocalizedResource(), + Glyph = "\uE710", + Command = CreateLibraryCommand, + ShowItem = options.IsLibrariesHeader + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "SideBarRestoreLibraries/Text".GetLocalizedResource(), + Glyph = "\uE10E", + Command = RestoreLibrariesCommand, + ShowItem = options.IsLibrariesHeader + }, + new ContextMenuFlyoutItemViewModelBuilder(commands.EmptyRecycleBin) + { + IsVisible = options.ShowEmptyRecycleBin, + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(commands.RestoreAllRecycleBin) + { + IsVisible = options.ShowEmptyRecycleBin, + }.Build(), + new ContextMenuFlyoutItemViewModel() + { + Text = "OpenInNewTab".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() + { + OpacityIconStyle = "ColorIconOpenInNewTab", + }, + Command = OpenInNewTabCommand, + ShowItem = options.IsLocationItem && userSettingsService.GeneralSettingsService.ShowOpenInNewTab + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "OpenInNewWindow".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() + { + OpacityIconStyle = "ColorIconOpenInNewWindow", + }, + Command = OpenInNewWindowCommand, + ShowItem = options.IsLocationItem && userSettingsService.GeneralSettingsService.ShowOpenInNewTab + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "OpenInNewPane".GetLocalizedResource(), + Command = OpenInNewPaneCommand, + ShowItem = options.IsLocationItem && userSettingsService.GeneralSettingsService.ShowOpenInNewPane + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "PinToFavorites".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() + { + OpacityIconStyle = "ColorIconPinToFavorites", + }, + Command = PinItemCommand, + ShowItem = isDriveItem && !isDriveItemPinned + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "UnpinFromFavorites".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() + { + OpacityIconStyle = "ColorIconUnpinFromFavorites", + }, + Command = UnpinItemCommand, + ShowItem = options.ShowUnpinItem || isDriveItemPinned + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "ReorderSidebarItemsDialogText".GetLocalizedResource(), + Glyph = "\uE8D8", + Command = ReorderItemsCommand, + ShowItem = isFavoriteItem || item.Section is SectionType.Favorites + }, + new ContextMenuFlyoutItemViewModel() + { + Text = string.Format("SideBarHideSectionFromSideBar/Text".GetLocalizedResource(), rightClickedItem.Text), + Glyph = "\uE77A", + Command = HideSectionCommand, + ShowItem = options.ShowHideSection + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "SideBarEjectDevice/Text".GetLocalizedResource(), + Command = EjectDeviceCommand, + ShowItem = options.ShowEjectDevice + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "FormatDriveText".GetLocalizedResource(), + Command = FormatDriveCommand, + CommandParameter = item, + ShowItem = options.ShowFormatDrive + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "Properties".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() + { + OpacityIconStyle = "ColorIconProperties", + }, + Command = OpenPropertiesCommand, + CommandParameter = menu, + ShowItem = options.ShowProperties + }, + new ContextMenuFlyoutItemViewModel() + { + ItemType = ContextMenuFlyoutItemType.Separator, + Tag = "OverflowSeparator", + IsHidden = !options.ShowShellItems, + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "Loading".GetLocalizedResource(), + Glyph = "\xE712", + Items = new List(), + ID = "ItemOverflow", + Tag = "ItemOverflow", + IsEnabled = false, + IsHidden = !options.ShowShellItems, + } + }.Where(x => x.ShowItem).ToList(); + } + + private void SideBar_ItemContextInvoked(object sender, ItemContextInvokedArgs args) + { + if (args.Item is not INavigationControlItem item || sender is not FrameworkElement sidebarItem) + { + return; + } + rightClickedItem = item; + + var itemContextMenuFlyout = new CommandBarFlyout { Placement = FlyoutPlacementMode.Full }; + itemContextMenuFlyout.Opening += (sender, e) => App.LastOpenedFlyout = sender as CommandBarFlyout; + + var menuItems = GetLocationItemMenuItems(item, itemContextMenuFlyout); + var (_, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(menuItems); + + secondaryElements.OfType() + .ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); + + secondaryElements.ForEach(i => itemContextMenuFlyout.SecondaryCommands.Add(i)); + itemContextMenuFlyout.ShowAt(sidebarItem, new FlyoutShowOptions { Position = args.Position }); + + if (item.MenuOptions.ShowShellItems) + _ = ShellContextmenuHelper.LoadShellMenuItems(rightClickedItem.Path, itemContextMenuFlyout, item.MenuOptions); + } + private void SideBar_DisplayModeChanged(object sender, SideBarDisplayMode e) { if (e == SideBarDisplayMode.Minimal) diff --git a/src/Files.App/UserControls/SideBar/SideBarItem.cs b/src/Files.App/UserControls/SideBar/SideBarItem.cs index 2dbdfb6eb64c..ff0750d36e4a 100644 --- a/src/Files.App/UserControls/SideBar/SideBarItem.cs +++ b/src/Files.App/UserControls/SideBar/SideBarItem.cs @@ -1,3 +1,5 @@ + + // Copyright (c) Microsoft Corporation and Contributors. // Licensed under the MIT License. @@ -5,7 +7,10 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Media.Imaging; +using System.Drawing.Text; using Windows.ApplicationModel.DataTransfer; +using Windows.Foundation; namespace Files.App.UserControls.SideBar { @@ -22,7 +27,6 @@ public sealed partial class SideBarItem : Control public SideBarItem() { DefaultStyleKey = typeof(SideBarItem); - DataContext = this; PointerReleased += Item_PointerReleased; KeyDown += (sender, args) => @@ -46,7 +50,7 @@ private void SideBarItem_DragStarting(UIElement sender, DragStartingEventArgs ar private void SetFlyoutOpen(bool isOpen = true) { - if (Items is null) return; + if (Item?.ChildItems is null) return; var flyoutOwner = (GetTemplateChild("ElementGrid") as FrameworkElement)!; if (isOpen) @@ -62,6 +66,7 @@ private void SetFlyoutOpen(bool isOpen = true) private void SideBarItem_Loaded(object sender, RoutedEventArgs e) { HookupOwners(); + HookupIconChangeListener(null, Item); if (GetTemplateChild("ElementGrid") is Grid grid) { @@ -85,12 +90,13 @@ private void SideBarItem_Loaded(object sender, RoutedEventArgs e) flyoutRepeater.ElementPrepared += ChildrenPresenter_ElementPrepared; } - UpdateExpansionState(Items); + UpdateExpansionState(Item?.ChildItems); } private void ItemGrid_ContextRequested(UIElement sender, Microsoft.UI.Xaml.Input.ContextRequestedEventArgs args) { - Owner.RaiseContextRequested(this); + Owner.RaiseContextRequested(this, args.TryGetPosition(this, out var point) ? point : default); + args.Handled = true; } private void HookupOwners() @@ -112,13 +118,33 @@ private void HookupOwners() }); } + private void HookupIconChangeListener(INavigationControlItem? oldItem, INavigationControlItem? newItem) + { + if(oldItem != null) + { + oldItem.PropertyChanged -= ItemPropertyChangedHandler; + } + if(newItem != null) + { + newItem.PropertyChanged += ItemPropertyChangedHandler; + } + UpdateIcon(); + } + void ItemPropertyChangedHandler(object? sender, PropertyChangedEventArgs args) + { + if(args.PropertyName == "Icon") + { + UpdateIcon(); + } + } + private void ReevaluateSelection() { - if (Items is null) + if (Item?.ChildItems is null) { IsSelected = DataContext == Owner.SelectedItem; } - else if (Items is IList list) + else if (Item?.ChildItems is IList list) { if (list.Contains(Owner.SelectedItem)) { @@ -134,7 +160,7 @@ private void ReevaluateSelection() private void ChildrenPresenter_ElementPrepared(ItemsRepeater sender, ItemsRepeaterElementPreparedEventArgs args) { - if (Items is IList enumerable) + if (Item?.ChildItems is IList enumerable) { var newElement = enumerable[args.Index]; if (newElement == selectedChildItem) @@ -166,12 +192,12 @@ private void SideBarDisplayModeChanged(SideBarDisplayMode displayMode) switch (displayMode) { case SideBarDisplayMode.Expanded: - UpdateExpansionState(Items); + UpdateExpansionState(Item?.ChildItems); UpdateSelectionState(); SetFlyoutOpen(false); break; case SideBarDisplayMode.Minimal: - UpdateExpansionState(Items); + UpdateExpansionState(Item?.ChildItems); SetFlyoutOpen(false); break; case SideBarDisplayMode.Compact: @@ -186,6 +212,11 @@ private void UpdateSelectionState() VisualStateManager.GoToState(this, ShouldShowSelectionIndicator() ? "Selected" : "Unselected", true); } + private void UpdateIcon() + { + Icon = Item.GenerateIconSource()?.CreateIconElement(); + } + private bool ShouldShowSelectionIndicator() { if (IsExpanded && CollapsableChildren) diff --git a/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs b/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs index 4c40f8a4d734..9dbd88659f2c 100644 --- a/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs +++ b/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs @@ -29,29 +29,21 @@ public bool IsExpanded public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(SideBarItem), new PropertyMetadata(false, OnPropertyChanged)); - public object Icon + public INavigationControlItem? Item { - get { return (object)GetValue(IconProperty); } - set { SetValue(IconProperty, value); } + get { return (INavigationControlItem)GetValue(ItemProperty); } + set { SetValue(ItemProperty, value); } } - public static readonly DependencyProperty IconProperty = - DependencyProperty.Register("Icon", typeof(object), typeof(SideBarItem), new PropertyMetadata(null)); + public static readonly DependencyProperty ItemProperty = + DependencyProperty.Register("Item", typeof(INavigationControlItem), typeof(SideBarItem), new PropertyMetadata(null)); - public string Text + public FrameworkElement Icon { - get { return (string)GetValue(TextProperty); } - set { SetValue(TextProperty, value); } - } - public static readonly DependencyProperty TextProperty = - DependencyProperty.Register("Text", typeof(string), typeof(SideBarItem), new PropertyMetadata(null)); - - public object Items - { - get { return (object)GetValue(ItemsProperty); } - set { SetValue(ItemsProperty, value); } + get { return (FrameworkElement)GetValue(IconProperty); } + set { SetValue(IconProperty, value); } } - public static readonly DependencyProperty ItemsProperty = - DependencyProperty.Register("Items", typeof(object), typeof(SideBarItem), new PropertyMetadata(null)); + public static readonly DependencyProperty IconProperty = + DependencyProperty.Register("Icon", typeof(FrameworkElement), typeof(SideBarItem), new PropertyMetadata(null)); public SideBarDisplayMode DisplayMode { @@ -85,11 +77,11 @@ public static void OnPropertyChanged(DependencyObject sender, DependencyProperty } else if (e.Property == IsExpandedProperty) { - item.UpdateExpansionState(item.Items); + item.UpdateExpansionState(item.Item?.ChildItems); } - else if (e.Property == ItemsProperty) + else if(e.Property == ItemProperty) { - item.UpdateExpansionState((UIElement)e.NewValue); + item.HookupIconChangeListener(e.OldValue as INavigationControlItem, e.NewValue as INavigationControlItem); } else if(e.Property == DataContextProperty) { diff --git a/src/Files.App/UserControls/SideBar/SideBarItem.resources.cs b/src/Files.App/UserControls/SideBar/SideBarItem.resources.cs new file mode 100644 index 000000000000..d059c4a1e667 --- /dev/null +++ b/src/Files.App/UserControls/SideBar/SideBarItem.resources.cs @@ -0,0 +1,13 @@ +using Microsoft.UI.Xaml; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Files.App.UserControls.SideBar +{ + partial class SideBarItemResources : ResourceDictionary + { + } +} diff --git a/src/Files.App/UserControls/SideBar/SideBarPane.xaml b/src/Files.App/UserControls/SideBar/SideBarPane.xaml index 3f058ce70389..97eff60ea839 100644 --- a/src/Files.App/UserControls/SideBar/SideBarPane.xaml +++ b/src/Files.App/UserControls/SideBar/SideBarPane.xaml @@ -10,9 +10,10 @@ xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls" xmlns:local="using:Files.App.UserControls.SideBar" mc:Ignorable="d" - MinWidth="300" MinHeight="500"> + MinWidth="300" MinHeight="500" + Background="{ThemeResource App.Theme.Sidebar.BackgroundBrush}"> - + @@ -49,6 +50,7 @@ - - - - - - - + diff --git a/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs b/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs index 105313aa4601..ddf7c89efdf6 100644 --- a/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs +++ b/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs @@ -1,11 +1,17 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Markup; using System; using Windows.ApplicationModel.DataTransfer; +using Windows.Foundation; namespace Files.App.UserControls.SideBar { + public record ItemDroppedEventArgs(object DropTarget, DataPackageView DroppedItem, bool InsertAbove) { } + public record ItemContextInvokedArgs(object Item, Point Position) { } + + [ContentProperty(Name = "InnerContent")] public sealed partial class SideBarPane : UserControl { @@ -13,11 +19,11 @@ public sealed partial class SideBarPane : UserControl private double preManipulationSideBarWidth = 0; - public record ItemDroppedEventArgs(object DropTarget, DataPackageView DroppedItem, bool InsertAbove) { } - public event EventHandler? ItemDropped; + public event EventHandler? ItemInvoked; - public event EventHandler? ItemContextInvoked; + + public event EventHandler? ItemContextInvoked; public SideBarPane() { @@ -27,7 +33,7 @@ public SideBarPane() private void SideBar_Loaded(object sender, RoutedEventArgs e) { - if(DisplayMode != SideBarDisplayMode.Minimal) + if (DisplayMode != SideBarDisplayMode.Minimal) { UpdateDisplayModeForWidth(MenuItemsHost.ActualWidth); } @@ -37,16 +43,16 @@ private void SideBar_Loaded(object sender, RoutedEventArgs e) } } - public void RaiseContextRequested(SideBarItem item) + public void RaiseContextRequested(SideBarItem item, Point e) { // Only leaves can be selected - ItemContextInvoked?.Invoke(item, item.DataContext); + ItemContextInvoked?.Invoke(item, new ItemContextInvokedArgs(item.DataContext, e)); } - + public void RaiseItemInvoked(SideBarItem item) { // Only leaves can be selected - if (item.Items is not null) return; + if (item.Item?.ChildItems is not null) return; SelectedItem = item.DataContext; ItemInvoked?.Invoke(item, SelectedItem); } diff --git a/src/Files.App/UserControls/SidebarControl.xaml b/src/Files.App/UserControls/SidebarControl.xaml index fb9216d2e3a8..c8b6735627e1 100644 --- a/src/Files.App/UserControls/SidebarControl.xaml +++ b/src/Files.App/UserControls/SidebarControl.xaml @@ -278,7 +278,7 @@ - + diff --git a/src/Files.App/Utils/Global/WSLDistroManager.cs b/src/Files.App/Utils/Global/WSLDistroManager.cs index d289e1e86bd8..bdcb043de881 100644 --- a/src/Files.App/Utils/Global/WSLDistroManager.cs +++ b/src/Files.App/Utils/Global/WSLDistroManager.cs @@ -37,7 +37,7 @@ public async Task UpdateDrivesAsync() { Text = folder.DisplayName, Path = folder.Path, - Logo = logoURI, + Icon = logoURI, MenuOptions = new ContextMenuOptions { IsLocationItem = true }, }; diff --git a/src/Files.App/ViewModels/MainPageViewModel.cs b/src/Files.App/ViewModels/MainPageViewModel.cs index 5866f1116a09..67a7018c604c 100644 --- a/src/Files.App/ViewModels/MainPageViewModel.cs +++ b/src/Files.App/ViewModels/MainPageViewModel.cs @@ -225,7 +225,7 @@ public async Task UpdateTabInfo(TabItem tabItem, object navigationArg) else if (App.WSLDistroManager.TryGetDistro(currentPath, out WslDistroItem? wslDistro) && currentPath.Equals(wslDistro.Path)) { tabLocationHeader = wslDistro.Text; - iconSource.ImageSource = new BitmapImage(wslDistro.Logo); + iconSource.ImageSource = new BitmapImage(wslDistro.Icon); } else { From 540c3fa08397f685498038c794e51a5a6cd3aeaa Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Sun, 23 Jul 2023 20:42:04 +0200 Subject: [PATCH 03/83] Hookup selection/navigation --- .../ResourceDictionaries/SideBarItem.xaml | 7 +- .../SideBar/SideBarHost.commands.cs | 273 +++++++++++++ .../SideBar/SideBarHost.properties.cs | 29 +- .../UserControls/SideBar/SideBarHost.xaml | 5 +- .../UserControls/SideBar/SideBarHost.xaml.cs | 359 ++++-------------- .../UserControls/SideBar/SideBarItem.cs | 64 +++- .../SideBar/SideBarItem.properties.cs | 12 +- .../SideBar/SideBarPane.properties.cs | 6 +- .../UserControls/SideBar/SideBarPane.xaml | 23 +- .../UserControls/SideBar/SideBarPane.xaml.cs | 10 +- src/Files.App/Views/MainPage.xaml | 2 +- src/Files.App/Views/MainPage.xaml.cs | 40 +- 12 files changed, 463 insertions(+), 367 deletions(-) create mode 100644 src/Files.App/UserControls/SideBar/SideBarHost.commands.cs diff --git a/src/Files.App/ResourceDictionaries/SideBarItem.xaml b/src/Files.App/ResourceDictionaries/SideBarItem.xaml index 01fbea3ff438..fe87db8207db 100644 --- a/src/Files.App/ResourceDictionaries/SideBarItem.xaml +++ b/src/Files.App/ResourceDictionaries/SideBarItem.xaml @@ -73,6 +73,11 @@ + + + + + @@ -105,7 +110,7 @@ Width="16" Height="16" VerticalAlignment="Center" HorizontalAlignment="Center"/> - + diff --git a/src/Files.App/UserControls/SideBar/SideBarHost.commands.cs b/src/Files.App/UserControls/SideBar/SideBarHost.commands.cs new file mode 100644 index 000000000000..26fa0cfa7657 --- /dev/null +++ b/src/Files.App/UserControls/SideBar/SideBarHost.commands.cs @@ -0,0 +1,273 @@ +using Files.App.ViewModels.Dialogs; +using Microsoft.UI.Xaml.Controls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace Files.App.UserControls.SideBar +{ + partial class SideBarHost + { + public readonly ICommand CreateLibraryCommand = new AsyncRelayCommand(LibraryManager.ShowCreateNewLibraryDialog); + + public readonly ICommand RestoreLibrariesCommand = new AsyncRelayCommand(LibraryManager.ShowRestoreDefaultLibrariesDialog); + + private ICommand HideSectionCommand { get; } + + private ICommand PinItemCommand { get; } + + private ICommand UnpinItemCommand { get; } + + private ICommand OpenInNewTabCommand { get; } + + private ICommand OpenInNewWindowCommand { get; } + + private ICommand OpenInNewPaneCommand { get; } + + private ICommand EjectDeviceCommand { get; } + + private ICommand FormatDriveCommand { get; } + + private ICommand OpenPropertiesCommand { get; } + + private ICommand ReorderItemsCommand { get; } + + private async Task OpenInNewPane() + { + if (await DriveHelpers.CheckEmptyDrive(rightClickedItem.Path)) + return; + + SidebarItemNewPaneInvoked?.Invoke(this, new SidebarItemNewPaneInvokedEventArgs(rightClickedItem)); + } + + private async Task OpenInNewTab() + { + if (await DriveHelpers.CheckEmptyDrive(rightClickedItem.Path)) + return; + + await NavigationHelpers.OpenPathInNewTab(rightClickedItem.Path); + } + + private async Task OpenInNewWindow() + { + if (await DriveHelpers.CheckEmptyDrive(rightClickedItem.Path)) + return; + + await NavigationHelpers.OpenPathInNewWindowAsync(rightClickedItem.Path); + } + + private void PinItem() + { + if (rightClickedItem is DriveItem) + _ = QuickAccessService.PinToSidebar(new[] { rightClickedItem.Path }); + } + private void UnpinItem() + { + if (rightClickedItem.Section == SectionType.Favorites || rightClickedItem is DriveItem) + _ = QuickAccessService.UnpinFromSidebar(rightClickedItem.Path); + } + + private void HideSection() + { + switch (rightClickedItem.Section) + { + case SectionType.Favorites: + userSettingsService.GeneralSettingsService.ShowFavoritesSection = false; + break; + case SectionType.Library: + userSettingsService.GeneralSettingsService.ShowLibrarySection = false; + break; + case SectionType.CloudDrives: + userSettingsService.GeneralSettingsService.ShowCloudDrivesSection = false; + break; + case SectionType.Drives: + userSettingsService.GeneralSettingsService.ShowDrivesSection = false; + break; + case SectionType.Network: + userSettingsService.GeneralSettingsService.ShowNetworkDrivesSection = false; + break; + case SectionType.WSL: + userSettingsService.GeneralSettingsService.ShowWslSection = false; + break; + case SectionType.FileTag: + userSettingsService.GeneralSettingsService.ShowFileTagsSection = false; + break; + } + } + + private async Task ReorderItems() + { + var dialog = new ReorderSidebarItemsDialogViewModel(); + var dialogService = Ioc.Default.GetRequiredService(); + var result = await dialogService.ShowDialogAsync(dialog); + } + + private void OpenProperties(CommandBarFlyout menu) + { + EventHandler flyoutClosed = null!; + flyoutClosed = (s, e) => + { + menu.Closed -= flyoutClosed; + SidebarItemPropertiesInvoked?.Invoke(this, new SidebarItemPropertiesInvokedEventArgs(rightClickedItem)); + }; + menu.Closed += flyoutClosed; + } + + private async Task EjectDevice() + { + var result = await DriveHelpers.EjectDeviceAsync(rightClickedItem.Path); + await UIHelpers.ShowDeviceEjectResultAsync(result); + } + + private void FormatDrive() + { + Win32API.OpenFormatDriveDialog(rightClickedItem.Path); + } + + private List GetLocationItemMenuItems(INavigationControlItem item, CommandBarFlyout menu) + { + var options = item.MenuOptions; + + var favoriteModel = App.QuickAccessManager.Model; + var favoriteIndex = favoriteModel.IndexOfItem(item); + var favoriteCount = favoriteModel.FavoriteItems.Count; + + var isFavoriteItem = item.Section is SectionType.Favorites && favoriteIndex is not -1; + var showMoveItemUp = isFavoriteItem && favoriteIndex > 0; + var showMoveItemDown = isFavoriteItem && favoriteIndex < favoriteCount - 1; + + var isDriveItem = item is DriveItem; + var isDriveItemPinned = isDriveItem && ((DriveItem)item).IsPinned; + + return new List() + { + new ContextMenuFlyoutItemViewModel() + { + Text = "SideBarCreateNewLibrary/Text".GetLocalizedResource(), + Glyph = "\uE710", + Command = CreateLibraryCommand, + ShowItem = options.IsLibrariesHeader + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "SideBarRestoreLibraries/Text".GetLocalizedResource(), + Glyph = "\uE10E", + Command = RestoreLibrariesCommand, + ShowItem = options.IsLibrariesHeader + }, + new ContextMenuFlyoutItemViewModelBuilder(commands.EmptyRecycleBin) + { + IsVisible = options.ShowEmptyRecycleBin, + }.Build(), + new ContextMenuFlyoutItemViewModelBuilder(commands.RestoreAllRecycleBin) + { + IsVisible = options.ShowEmptyRecycleBin, + }.Build(), + new ContextMenuFlyoutItemViewModel() + { + Text = "OpenInNewTab".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() + { + OpacityIconStyle = "ColorIconOpenInNewTab", + }, + Command = OpenInNewTabCommand, + ShowItem = options.IsLocationItem && userSettingsService.GeneralSettingsService.ShowOpenInNewTab + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "OpenInNewWindow".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() + { + OpacityIconStyle = "ColorIconOpenInNewWindow", + }, + Command = OpenInNewWindowCommand, + ShowItem = options.IsLocationItem && userSettingsService.GeneralSettingsService.ShowOpenInNewTab + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "OpenInNewPane".GetLocalizedResource(), + Command = OpenInNewPaneCommand, + ShowItem = options.IsLocationItem && userSettingsService.GeneralSettingsService.ShowOpenInNewPane + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "PinToFavorites".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() + { + OpacityIconStyle = "ColorIconPinToFavorites", + }, + Command = PinItemCommand, + ShowItem = isDriveItem && !isDriveItemPinned + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "UnpinFromFavorites".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() + { + OpacityIconStyle = "ColorIconUnpinFromFavorites", + }, + Command = UnpinItemCommand, + ShowItem = options.ShowUnpinItem || isDriveItemPinned + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "ReorderSidebarItemsDialogText".GetLocalizedResource(), + Glyph = "\uE8D8", + Command = ReorderItemsCommand, + ShowItem = isFavoriteItem || item.Section is SectionType.Favorites + }, + new ContextMenuFlyoutItemViewModel() + { + Text = string.Format("SideBarHideSectionFromSideBar/Text".GetLocalizedResource(), rightClickedItem.Text), + Glyph = "\uE77A", + Command = HideSectionCommand, + ShowItem = options.ShowHideSection + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "SideBarEjectDevice/Text".GetLocalizedResource(), + Command = EjectDeviceCommand, + ShowItem = options.ShowEjectDevice + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "FormatDriveText".GetLocalizedResource(), + Command = FormatDriveCommand, + CommandParameter = item, + ShowItem = options.ShowFormatDrive + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "Properties".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() + { + OpacityIconStyle = "ColorIconProperties", + }, + Command = OpenPropertiesCommand, + CommandParameter = menu, + ShowItem = options.ShowProperties + }, + new ContextMenuFlyoutItemViewModel() + { + ItemType = ContextMenuFlyoutItemType.Separator, + Tag = "OverflowSeparator", + IsHidden = !options.ShowShellItems, + }, + new ContextMenuFlyoutItemViewModel() + { + Text = "Loading".GetLocalizedResource(), + Glyph = "\xE712", + Items = new List(), + ID = "ItemOverflow", + Tag = "ItemOverflow", + IsEnabled = false, + IsHidden = !options.ShowShellItems, + } + }.Where(x => x.ShowItem).ToList(); + } + + } +} diff --git a/src/Files.App/UserControls/SideBar/SideBarHost.properties.cs b/src/Files.App/UserControls/SideBar/SideBarHost.properties.cs index abb61a01b24b..5ec27fbfa767 100644 --- a/src/Files.App/UserControls/SideBar/SideBarHost.properties.cs +++ b/src/Files.App/UserControls/SideBar/SideBarHost.properties.cs @@ -29,19 +29,32 @@ public UIElement InnerContent public static readonly DependencyProperty InnerContentProperty = DependencyProperty.Register("InnerContent", typeof(UIElement), typeof(SideBarHost), new PropertyMetadata(null)); - public object SelectedItem - { - get { return (object)GetValue(SelectedItemProperty); } - set { SetValue(SelectedItemProperty, value); } - } - public static readonly DependencyProperty SelectedItemProperty = - DependencyProperty.Register("SelectedItem", typeof(object), typeof(SideBarHost), new PropertyMetadata(null)); - public UIElement TabContent { get => (UIElement)GetValue(TabContentProperty); set => SetValue(TabContentProperty, value); } public static readonly DependencyProperty TabContentProperty = DependencyProperty.Register(nameof(TabContent), typeof(UIElement), typeof(SideBarHost), new PropertyMetadata(null)); + + + public SidebarViewModel ViewModel + { + get => (SidebarViewModel)GetValue(ViewModelProperty); + set => SetValue(ViewModelProperty, value); + } + public static readonly DependencyProperty ViewModelProperty = + DependencyProperty.Register(nameof(ViewModel), typeof(SidebarViewModel), typeof(SidebarControl), new PropertyMetadata(null)); + + public INavigationControlItem SelectedItem + { + get => (INavigationControlItem)GetValue(SelectedItemProperty); + set + { + if (IsLoaded) + SetValue(SelectedItemProperty, value); + } + } + public static readonly DependencyProperty SelectedItemProperty = + DependencyProperty.Register(nameof(SelectedItem), typeof(INavigationControlItem), typeof(SidebarControl), new PropertyMetadata(null)); } } diff --git a/src/Files.App/UserControls/SideBar/SideBarHost.xaml b/src/Files.App/UserControls/SideBar/SideBarHost.xaml index bfae144c947b..0df4befe29c8 100644 --- a/src/Files.App/UserControls/SideBar/SideBarHost.xaml +++ b/src/Files.App/UserControls/SideBar/SideBarHost.xaml @@ -7,7 +7,8 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:sidebar="using:Files.App.UserControls.SideBar" HorizontalAlignment="Stretch" - Loaded="SidebarNavView_Loaded" + Loaded="SideBarHost_Loaded" + SizeChanged="SideBarHost_SizeChanged" mc:Ignorable="d"> @@ -41,9 +42,11 @@ diff --git a/src/Files.App/UserControls/SideBar/SideBarHost.xaml.cs b/src/Files.App/UserControls/SideBar/SideBarHost.xaml.cs index 54dd2c467f80..06be23b69bc0 100644 --- a/src/Files.App/UserControls/SideBar/SideBarHost.xaml.cs +++ b/src/Files.App/UserControls/SideBar/SideBarHost.xaml.cs @@ -54,50 +54,25 @@ public sealed partial class SideBarHost : UserControl, INotifyPropertyChanged public SidebarPinnedModel SidebarPinnedModel => App.QuickAccessManager.Model; - // Using a DependencyProperty as the backing store for ViewModel. This enables animation, styling, binding, etc... - public static readonly DependencyProperty ViewModelProperty = - DependencyProperty.Register(nameof(ViewModel), typeof(SidebarViewModel), typeof(SidebarControl), new PropertyMetadata(null)); + private bool IsInPointerPressed = false; - public static readonly DependencyProperty SelectedSidebarItemProperty = DependencyProperty.Register(nameof(SelectedSidebarItem), typeof(INavigationControlItem), typeof(SidebarControl), new PropertyMetadata(null)); + private readonly DispatcherQueueTimer dragOverSectionTimer, dragOverItemTimer; - public INavigationControlItem SelectedSidebarItem + private bool canOpenInNewPane; + + public bool CanOpenInNewPane { - get => (INavigationControlItem)GetValue(SelectedSidebarItemProperty); + get => canOpenInNewPane; set { - if (IsLoaded) - SetValue(SelectedSidebarItemProperty, value); + if (value != canOpenInNewPane) + { + canOpenInNewPane = value; + NotifyPropertyChanged(nameof(CanOpenInNewPane)); + } } } - public readonly ICommand CreateLibraryCommand = new AsyncRelayCommand(LibraryManager.ShowCreateNewLibraryDialog); - - public readonly ICommand RestoreLibrariesCommand = new AsyncRelayCommand(LibraryManager.ShowRestoreDefaultLibrariesDialog); - - private ICommand HideSectionCommand { get; } - - private ICommand PinItemCommand { get; } - - private ICommand UnpinItemCommand { get; } - - private ICommand OpenInNewTabCommand { get; } - - private ICommand OpenInNewWindowCommand { get; } - - private ICommand OpenInNewPaneCommand { get; } - - private ICommand EjectDeviceCommand { get; } - - private ICommand FormatDriveCommand { get; } - - private ICommand OpenPropertiesCommand { get; } - - private ICommand ReorderItemsCommand { get; } - - private bool IsInPointerPressed = false; - - private readonly DispatcherQueueTimer dragOverSectionTimer, dragOverItemTimer; - public SideBarHost() { InitializeComponent(); @@ -117,26 +92,6 @@ public SideBarHost() ReorderItemsCommand = new AsyncRelayCommand(ReorderItems); } - public SidebarViewModel ViewModel - { - get => (SidebarViewModel)GetValue(ViewModelProperty); - set => SetValue(ViewModelProperty, value); - } - - private bool canOpenInNewPane; - - public bool CanOpenInNewPane - { - get => canOpenInNewPane; - set - { - if (value != canOpenInNewPane) - { - canOpenInNewPane = value; - NotifyPropertyChanged(nameof(CanOpenInNewPane)); - } - } - } public event PropertyChangedEventHandler? PropertyChanged; @@ -145,100 +100,9 @@ private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - private async Task OpenInNewPane() - { - if (await DriveHelpers.CheckEmptyDrive(rightClickedItem.Path)) - return; - - SidebarItemNewPaneInvoked?.Invoke(this, new SidebarItemNewPaneInvokedEventArgs(rightClickedItem)); - } - - private async Task OpenInNewTab() - { - if (await DriveHelpers.CheckEmptyDrive(rightClickedItem.Path)) - return; - - await NavigationHelpers.OpenPathInNewTab(rightClickedItem.Path); - } - - private async Task OpenInNewWindow() - { - if (await DriveHelpers.CheckEmptyDrive(rightClickedItem.Path)) - return; - - await NavigationHelpers.OpenPathInNewWindowAsync(rightClickedItem.Path); - } - - private void PinItem() - { - if (rightClickedItem is DriveItem) - _ = QuickAccessService.PinToSidebar(new[] { rightClickedItem.Path }); - } - private void UnpinItem() - { - if (rightClickedItem.Section == SectionType.Favorites || rightClickedItem is DriveItem) - _ = QuickAccessService.UnpinFromSidebar(rightClickedItem.Path); - } - - private void HideSection() - { - switch (rightClickedItem.Section) - { - case SectionType.Favorites: - userSettingsService.GeneralSettingsService.ShowFavoritesSection = false; - break; - case SectionType.Library: - userSettingsService.GeneralSettingsService.ShowLibrarySection = false; - break; - case SectionType.CloudDrives: - userSettingsService.GeneralSettingsService.ShowCloudDrivesSection = false; - break; - case SectionType.Drives: - userSettingsService.GeneralSettingsService.ShowDrivesSection = false; - break; - case SectionType.Network: - userSettingsService.GeneralSettingsService.ShowNetworkDrivesSection = false; - break; - case SectionType.WSL: - userSettingsService.GeneralSettingsService.ShowWslSection = false; - break; - case SectionType.FileTag: - userSettingsService.GeneralSettingsService.ShowFileTagsSection = false; - break; - } - } - - private async Task ReorderItems() - { - var dialog = new ReorderSidebarItemsDialogViewModel(); - var dialogService = Ioc.Default.GetRequiredService(); - var result = await dialogService.ShowDialogAsync(dialog); - } - - private void OpenProperties(CommandBarFlyout menu) - { - EventHandler flyoutClosed = null!; - flyoutClosed = (s, e) => - { - menu.Closed -= flyoutClosed; - SidebarItemPropertiesInvoked?.Invoke(this, new SidebarItemPropertiesInvokedEventArgs(rightClickedItem)); - }; - menu.Closed += flyoutClosed; - } - - private async Task EjectDevice() - { - var result = await DriveHelpers.EjectDeviceAsync(rightClickedItem.Path); - await UIHelpers.ShowDeviceEjectResultAsync(result); - } - - private void FormatDrive() - { - Win32API.OpenFormatDriveDialog(rightClickedItem.Path); - } - private async void SideBar_ItemInvoked(object sender, object item) { + if (item is not INavigationControlItem navigationControlItem) return; var navigationPath = item as string; if (await DriveHelpers.CheckEmptyDrive(navigationPath)) @@ -250,154 +114,13 @@ private async void SideBar_ItemInvoked(object sender, object item) await NavigationHelpers.OpenPathInNewTab(navigationPath); return; } + SidebarItemInvoked?.Invoke(this, new SidebarItemInvokedEventArgs(navigationControlItem)); } - private void SidebarNavView_Loaded(object sender, RoutedEventArgs e) + private void SideBarHost_Loaded(object sender, RoutedEventArgs e) { } - private List GetLocationItemMenuItems(INavigationControlItem item, CommandBarFlyout menu) - { - var options = item.MenuOptions; - - var favoriteModel = App.QuickAccessManager.Model; - var favoriteIndex = favoriteModel.IndexOfItem(item); - var favoriteCount = favoriteModel.FavoriteItems.Count; - - var isFavoriteItem = item.Section is SectionType.Favorites && favoriteIndex is not -1; - var showMoveItemUp = isFavoriteItem && favoriteIndex > 0; - var showMoveItemDown = isFavoriteItem && favoriteIndex < favoriteCount - 1; - - var isDriveItem = item is DriveItem; - var isDriveItemPinned = isDriveItem && ((DriveItem)item).IsPinned; - - return new List() - { - new ContextMenuFlyoutItemViewModel() - { - Text = "SideBarCreateNewLibrary/Text".GetLocalizedResource(), - Glyph = "\uE710", - Command = CreateLibraryCommand, - ShowItem = options.IsLibrariesHeader - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "SideBarRestoreLibraries/Text".GetLocalizedResource(), - Glyph = "\uE10E", - Command = RestoreLibrariesCommand, - ShowItem = options.IsLibrariesHeader - }, - new ContextMenuFlyoutItemViewModelBuilder(commands.EmptyRecycleBin) - { - IsVisible = options.ShowEmptyRecycleBin, - }.Build(), - new ContextMenuFlyoutItemViewModelBuilder(commands.RestoreAllRecycleBin) - { - IsVisible = options.ShowEmptyRecycleBin, - }.Build(), - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenInNewTab".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconOpenInNewTab", - }, - Command = OpenInNewTabCommand, - ShowItem = options.IsLocationItem && userSettingsService.GeneralSettingsService.ShowOpenInNewTab - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenInNewWindow".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconOpenInNewWindow", - }, - Command = OpenInNewWindowCommand, - ShowItem = options.IsLocationItem && userSettingsService.GeneralSettingsService.ShowOpenInNewTab - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenInNewPane".GetLocalizedResource(), - Command = OpenInNewPaneCommand, - ShowItem = options.IsLocationItem && userSettingsService.GeneralSettingsService.ShowOpenInNewPane - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "PinToFavorites".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconPinToFavorites", - }, - Command = PinItemCommand, - ShowItem = isDriveItem && !isDriveItemPinned - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "UnpinFromFavorites".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconUnpinFromFavorites", - }, - Command = UnpinItemCommand, - ShowItem = options.ShowUnpinItem || isDriveItemPinned - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "ReorderSidebarItemsDialogText".GetLocalizedResource(), - Glyph = "\uE8D8", - Command = ReorderItemsCommand, - ShowItem = isFavoriteItem || item.Section is SectionType.Favorites - }, - new ContextMenuFlyoutItemViewModel() - { - Text = string.Format("SideBarHideSectionFromSideBar/Text".GetLocalizedResource(), rightClickedItem.Text), - Glyph = "\uE77A", - Command = HideSectionCommand, - ShowItem = options.ShowHideSection - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "SideBarEjectDevice/Text".GetLocalizedResource(), - Command = EjectDeviceCommand, - ShowItem = options.ShowEjectDevice - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "FormatDriveText".GetLocalizedResource(), - Command = FormatDriveCommand, - CommandParameter = item, - ShowItem = options.ShowFormatDrive - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "Properties".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconProperties", - }, - Command = OpenPropertiesCommand, - CommandParameter = menu, - ShowItem = options.ShowProperties - }, - new ContextMenuFlyoutItemViewModel() - { - ItemType = ContextMenuFlyoutItemType.Separator, - Tag = "OverflowSeparator", - IsHidden = !options.ShowShellItems, - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "Loading".GetLocalizedResource(), - Glyph = "\xE712", - Items = new List(), - ID = "ItemOverflow", - Tag = "ItemOverflow", - IsEnabled = false, - IsHidden = !options.ShowShellItems, - } - }.Where(x => x.ShowItem).ToList(); - } - private void SideBar_ItemContextInvoked(object sender, ItemContextInvokedArgs args) { if (args.Item is not INavigationControlItem item || sender is not FrameworkElement sidebarItem) @@ -422,6 +145,54 @@ private void SideBar_ItemContextInvoked(object sender, ItemContextInvokedArgs ar _ = ShellContextmenuHelper.LoadShellMenuItems(rightClickedItem.Path, itemContextMenuFlyout, item.MenuOptions); } + private void SideBarHost_SizeChanged(object sender, SizeChangedEventArgs args) + { + if (args.NewSize.Width < 500) + { + SideBar.DisplayMode = SideBarDisplayMode.Minimal; + } + else if (args.NewSize.Width < 1000) + { + SideBar.DisplayMode = SideBarDisplayMode.Compact; + } + else + { + SideBar.DisplayMode = SideBarDisplayMode.Expanded; + } + } + + private async void SideBar_ItemDropped(object sender, ItemDroppedEventArgs e) + { + if (e.DropTarget is not LocationItem locationItem) return; + + if (FilesystemHelpers.HasDraggedStorageItems(e.DroppedItem)) + { + var deferral = e.RawEvent.GetDeferral(); + if (string.IsNullOrEmpty(locationItem.Path) && isDropOnProcess && SectionType.Favorites.Equals(locationItem.Section)) // Pin to Favorites section + { + var storageItems = await FilesystemHelpers.GetDraggedStorageItems(e.DroppedItem); + foreach (var item in storageItems) + { + if (item.ItemType == FilesystemItemType.Directory && !SidebarPinnedModel.FavoriteItems.Contains(item.Path)) + QuickAccessService.PinToSidebar(item.Path); + } + } + else + { + var signal = new AsyncManualResetEvent(); + SidebarItemDropped?.Invoke(this, new SidebarItemDroppedEventArgs() + { + Package = e.DroppedItem, + ItemPath = locationItem.Path, + AcceptedOperation = e.RawEvent.AcceptedOperation, + SignalEvent = signal + }); + await signal.WaitAsync(); + } + deferral.Complete(); + } + } + private void SideBar_DisplayModeChanged(object sender, SideBarDisplayMode e) { if (e == SideBarDisplayMode.Minimal) @@ -445,11 +216,11 @@ public class SidebarItemDroppedEventArgs : EventArgs public class SidebarItemInvokedEventArgs : EventArgs { - public NavigationViewItemBase InvokedItemContainer { get; set; } + public INavigationControlItem InvokedItem { get; set; } - public SidebarItemInvokedEventArgs(NavigationViewItemBase ItemContainer) + public SidebarItemInvokedEventArgs(INavigationControlItem item) { - InvokedItemContainer = ItemContainer; + InvokedItem = item; } } diff --git a/src/Files.App/UserControls/SideBar/SideBarItem.cs b/src/Files.App/UserControls/SideBar/SideBarItem.cs index ff0750d36e4a..7c650c499cc1 100644 --- a/src/Files.App/UserControls/SideBar/SideBarItem.cs +++ b/src/Files.App/UserControls/SideBar/SideBarItem.cs @@ -21,6 +21,7 @@ public sealed partial class SideBarItem : Control private object? selectedChildItem = null; + public bool HasChildren => Item?.ChildItems is not null && Item.ChildItems.Count > 0; public bool CollapsableChildren => DisplayMode != SideBarDisplayMode.Compact; private bool HasChildSelection => selectedChildItem != null; @@ -90,7 +91,7 @@ private void SideBarItem_Loaded(object sender, RoutedEventArgs e) flyoutRepeater.ElementPrepared += ChildrenPresenter_ElementPrepared; } - UpdateExpansionState(Item?.ChildItems); + UpdateExpansionState(); } private void ItemGrid_ContextRequested(UIElement sender, Microsoft.UI.Xaml.Input.ContextRequestedEventArgs args) @@ -123,13 +124,24 @@ private void HookupIconChangeListener(INavigationControlItem? oldItem, INavigati if(oldItem != null) { oldItem.PropertyChanged -= ItemPropertyChangedHandler; + if (oldItem.ChildItems is not null) + oldItem.ChildItems.CollectionChanged -= ChildItems_CollectionChanged; } - if(newItem != null) + if (newItem != null) { newItem.PropertyChanged += ItemPropertyChangedHandler; + if(newItem.ChildItems is not null) + newItem.ChildItems.CollectionChanged += ChildItems_CollectionChanged; } UpdateIcon(); } + + private void ChildItems_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + ReevaluateSelection(); + UpdateExpansionState(); + } + void ItemPropertyChangedHandler(object? sender, PropertyChangedEventArgs args) { if(args.PropertyName == "Icon") @@ -140,7 +152,7 @@ void ItemPropertyChangedHandler(object? sender, PropertyChangedEventArgs args) private void ReevaluateSelection() { - if (Item?.ChildItems is null) + if (!HasChildren) { IsSelected = DataContext == Owner.SelectedItem; } @@ -176,13 +188,16 @@ private void ChildrenPresenter_ElementPrepared(ItemsRepeater sender, ItemsRepeat private void Clicked() { - if (CollapsableChildren) + if (HasChildren) { - IsExpanded = !IsExpanded; - } - else - { - SetFlyoutOpen(true); + if (CollapsableChildren) + { + IsExpanded = !IsExpanded; + } + else + { + SetFlyoutOpen(true); + } } Owner?.RaiseItemInvoked(this); } @@ -192,16 +207,16 @@ private void SideBarDisplayModeChanged(SideBarDisplayMode displayMode) switch (displayMode) { case SideBarDisplayMode.Expanded: - UpdateExpansionState(Item?.ChildItems); + UpdateExpansionState(); UpdateSelectionState(); SetFlyoutOpen(false); break; case SideBarDisplayMode.Minimal: - UpdateExpansionState(Item?.ChildItems); + UpdateExpansionState(); SetFlyoutOpen(false); break; case SideBarDisplayMode.Compact: - UpdateExpansionState(null); + UpdateExpansionState(); UpdateSelectionState(); break; } @@ -229,9 +244,9 @@ private bool ShouldShowSelectionIndicator() } } - private void UpdateExpansionState(object? childContent) + private void UpdateExpansionState() { - if (childContent == null) + if (!HasChildren) { VisualStateManager.GoToState(this, "NoExpansion", true); } @@ -273,14 +288,27 @@ private void Item_PointerReleased(object sender, Microsoft.UI.Xaml.Input.Pointer private void ItemGrid_DragOver(object sender, DragEventArgs e) { + if (HasChildren) + { + IsExpanded = true; + } + e.AcceptedOperation = DataPackageOperation.Move; - if (DragTargetAboveCenter(e)) + + if (UseReorderDrop) { - VisualStateManager.GoToState(this, "DragInsertAbove", true); + if (DragTargetAboveCenter(e)) + { + VisualStateManager.GoToState(this, "DragInsertAbove", true); + } + else + { + VisualStateManager.GoToState(this, "DragInsertBelow", true); + } } else { - VisualStateManager.GoToState(this, "DragInsertBelow", true); + VisualStateManager.GoToState(this, "DragOnTop", true); } } @@ -302,7 +330,7 @@ private void ItemGrid_DragLeave(object sender, DragEventArgs e) private void ItemGrid_Drop(object sender, DragEventArgs e) { VisualStateManager.GoToState(this, "NoDrag", true); - Owner.RaiseItemDropped(this, e, DragTargetAboveCenter(e)); + Owner.RaiseItemDropped(this, e, DragTargetAboveCenter(e), e); } } } diff --git a/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs b/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs index 9dbd88659f2c..7cb75a2d4cac 100644 --- a/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs +++ b/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs @@ -37,6 +37,14 @@ public INavigationControlItem? Item public static readonly DependencyProperty ItemProperty = DependencyProperty.Register("Item", typeof(INavigationControlItem), typeof(SideBarItem), new PropertyMetadata(null)); + public bool UseReorderDrop + { + get { return (bool)GetValue(UseReorderDropProperty); } + set { SetValue(UseReorderDropProperty, value); } + } + public static readonly DependencyProperty UseReorderDropProperty = + DependencyProperty.Register("UseReorderDrop", typeof(bool), typeof(SideBarItem), new PropertyMetadata(false)); + public FrameworkElement Icon { get { return (FrameworkElement)GetValue(IconProperty); } @@ -77,11 +85,13 @@ public static void OnPropertyChanged(DependencyObject sender, DependencyProperty } else if (e.Property == IsExpandedProperty) { - item.UpdateExpansionState(item.Item?.ChildItems); + item.UpdateExpansionState(); } else if(e.Property == ItemProperty) { item.HookupIconChangeListener(e.OldValue as INavigationControlItem, e.NewValue as INavigationControlItem); + item.UpdateExpansionState(); + item.ReevaluateSelection(); } else if(e.Property == DataContextProperty) { diff --git a/src/Files.App/UserControls/SideBar/SideBarPane.properties.cs b/src/Files.App/UserControls/SideBar/SideBarPane.properties.cs index ec8f15dc9a66..30ddae42eced 100644 --- a/src/Files.App/UserControls/SideBar/SideBarPane.properties.cs +++ b/src/Files.App/UserControls/SideBar/SideBarPane.properties.cs @@ -36,13 +36,13 @@ public UIElement InnerContent public static readonly DependencyProperty InnerContentProperty = DependencyProperty.Register("InnerContent", typeof(UIElement), typeof(SideBarPane), new PropertyMetadata(null)); - public object SelectedItem + public INavigationControlItem SelectedItem { - get { return (object)GetValue(SelectedItemProperty); } + get { return (INavigationControlItem)GetValue(SelectedItemProperty); } set { SetValue(SelectedItemProperty, value); } } public static readonly DependencyProperty SelectedItemProperty = - DependencyProperty.Register("SelectedItem", typeof(object), typeof(SideBarPane), new PropertyMetadata(null)); + DependencyProperty.Register("SelectedItem", typeof(INavigationControlItem), typeof(SideBarPane), new PropertyMetadata(null)); public bool PaneExpanded { diff --git a/src/Files.App/UserControls/SideBar/SideBarPane.xaml b/src/Files.App/UserControls/SideBar/SideBarPane.xaml index 97eff60ea839..840d848415ed 100644 --- a/src/Files.App/UserControls/SideBar/SideBarPane.xaml +++ b/src/Files.App/UserControls/SideBar/SideBarPane.xaml @@ -8,10 +8,9 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls" - xmlns:local="using:Files.App.UserControls.SideBar" mc:Ignorable="d" - MinWidth="300" MinHeight="500" - Background="{ThemeResource App.Theme.Sidebar.BackgroundBrush}"> + Background="Red" + MinWidth="300" MinHeight="500"> @@ -48,10 +47,12 @@ - + + + - - + + @@ -71,9 +73,10 @@ diff --git a/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs b/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs index ddf7c89efdf6..df55aa739e2f 100644 --- a/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs +++ b/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs @@ -8,7 +8,7 @@ namespace Files.App.UserControls.SideBar { - public record ItemDroppedEventArgs(object DropTarget, DataPackageView DroppedItem, bool InsertAbove) { } + public record ItemDroppedEventArgs(object DropTarget, DataPackageView DroppedItem, bool InsertAbove, DragEventArgs RawEvent) { } public record ItemContextInvokedArgs(object Item, Point Position) { } @@ -52,8 +52,8 @@ public void RaiseContextRequested(SideBarItem item, Point e) public void RaiseItemInvoked(SideBarItem item) { // Only leaves can be selected - if (item.Item?.ChildItems is not null) return; - SelectedItem = item.DataContext; + if (item.HasChildren) return; + SelectedItem = (item.DataContext as INavigationControlItem)!; ItemInvoked?.Invoke(item, SelectedItem); } @@ -88,9 +88,9 @@ private void UpdateMinimalMode() } } - internal void RaiseItemDropped(SideBarItem sideBarItem, DragEventArgs e, bool insertsAbove) + internal void RaiseItemDropped(SideBarItem sideBarItem, DragEventArgs e, bool insertsAbove, DragEventArgs rawEvent) { - ItemDropped?.Invoke(this, new ItemDroppedEventArgs(sideBarItem.DataContext, e.DataView, insertsAbove)); + ItemDropped?.Invoke(this, new ItemDroppedEventArgs(sideBarItem.DataContext, e.DataView, insertsAbove, rawEvent)); } private void GridSplitter_ManipulationStarted(object sender, Microsoft.UI.Xaml.Input.ManipulationStartedRoutedEventArgs e) diff --git a/src/Files.App/Views/MainPage.xaml b/src/Files.App/Views/MainPage.xaml index c69c6200f7d3..a0b2a17b1731 100644 --- a/src/Files.App/Views/MainPage.xaml +++ b/src/Files.App/Views/MainPage.xaml @@ -118,7 +118,7 @@ CanOpenInNewPane="{x:Bind SidebarAdaptiveViewModel.PaneHolder.IsMultiPaneEnabled, Mode=OneWay}" Loaded="SidebarControl_Loaded" DisplayMode="Expanded" - SelectedSidebarItem="{x:Bind SidebarAdaptiveViewModel.SidebarSelectedItem, Mode=TwoWay}" + SelectedItem="{x:Bind SidebarAdaptiveViewModel.SidebarSelectedItem, Mode=TwoWay}" ViewModel="{x:Bind SidebarAdaptiveViewModel}"> @@ -18,16 +18,18 @@ - - + + - - + + + + @@ -38,20 +40,30 @@ + + + + + + + + + + @@ -63,7 +75,7 @@ - + @@ -79,6 +91,16 @@ + + + + + + + + + + @@ -93,10 +115,13 @@ - + @@ -110,24 +135,31 @@ Width="16" Height="16" VerticalAlignment="Center" HorizontalAlignment="Center"/> - + + local:SideBarItem.TemplateRoot="{Binding ElementName=RootPanel}" HorizontalAlignment="Stretch"> + + + + + + - - Item?.ChildItems is not null && Item.ChildItems.Count > 0; - public bool CollapsableChildren => DisplayMode != SideBarDisplayMode.Compact; + public bool CollapseEnabled => DisplayMode != SideBarDisplayMode.Compact; private bool HasChildSelection => selectedChildItem != null; public SideBarItem() @@ -121,7 +121,7 @@ private void HookupOwners() private void HookupIconChangeListener(INavigationControlItem? oldItem, INavigationControlItem? newItem) { - if(oldItem != null) + if (oldItem != null) { oldItem.PropertyChanged -= ItemPropertyChangedHandler; if (oldItem.ChildItems is not null) @@ -130,7 +130,7 @@ private void HookupIconChangeListener(INavigationControlItem? oldItem, INavigati if (newItem != null) { newItem.PropertyChanged += ItemPropertyChangedHandler; - if(newItem.ChildItems is not null) + if (newItem.ChildItems is not null) newItem.ChildItems.CollectionChanged += ChildItems_CollectionChanged; } UpdateIcon(); @@ -144,7 +144,7 @@ private void ChildItems_CollectionChanged(object? sender, System.Collections.Spe void ItemPropertyChangedHandler(object? sender, PropertyChangedEventArgs args) { - if(args.PropertyName == "Icon") + if (args.PropertyName == "Icon") { UpdateIcon(); } @@ -190,7 +190,7 @@ private void Clicked() { if (HasChildren) { - if (CollapsableChildren) + if (CollapseEnabled) { IsExpanded = !IsExpanded; } @@ -220,6 +220,10 @@ private void SideBarDisplayModeChanged(SideBarDisplayMode displayMode) UpdateSelectionState(); break; } + if (!IsInFlyout) + { + VisualStateManager.GoToState(this, DisplayMode == SideBarDisplayMode.Compact ? "Compact" : "NonCompact", true); + } } private void UpdateSelectionState() @@ -234,7 +238,7 @@ private void UpdateIcon() private bool ShouldShowSelectionIndicator() { - if (IsExpanded && CollapsableChildren) + if (IsExpanded && CollapseEnabled) { return IsSelected; } @@ -246,7 +250,7 @@ private bool ShouldShowSelectionIndicator() private void UpdateExpansionState() { - if (!HasChildren) + if (!HasChildren || !CollapseEnabled) { VisualStateManager.GoToState(this, "NoExpansion", true); } @@ -265,13 +269,13 @@ private void ItemGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.Poin private void ItemGrid_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { - VisualStateManager.GoToState(this, "Normal", true); + VisualStateManager.GoToState(this, IsSelected ? "NormalSelected" : "Normal", true); isPointerOver = false; } private void ItemGrid_PointerCanceled(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { - VisualStateManager.GoToState(this, "Normal", true); + VisualStateManager.GoToState(this, IsSelected ? "NormalSelected" : "Normal", true); } private void ItemGrid_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) diff --git a/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs b/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs index 7cb75a2d4cac..4e15dff6ba3c 100644 --- a/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs +++ b/src/Files.App/UserControls/SideBar/SideBarItem.properties.cs @@ -29,6 +29,14 @@ public bool IsExpanded public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(SideBarItem), new PropertyMetadata(false, OnPropertyChanged)); + public bool IsInFlyout + { + get { return (bool)GetValue(IsInFlyoutProperty); } + set { SetValue(IsInFlyoutProperty, value); } + } + public static readonly DependencyProperty IsInFlyoutProperty = + DependencyProperty.Register("IsInFlyout", typeof(bool), typeof(SideBarItem), new PropertyMetadata(false)); + public INavigationControlItem? Item { get { return (INavigationControlItem)GetValue(ItemProperty); } diff --git a/src/Files.App/UserControls/SideBar/SideBarPane.xaml b/src/Files.App/UserControls/SideBar/SideBarPane.xaml index 840d848415ed..ed299d2881b7 100644 --- a/src/Files.App/UserControls/SideBar/SideBarPane.xaml +++ b/src/Files.App/UserControls/SideBar/SideBarPane.xaml @@ -24,8 +24,9 @@ - - + + + diff --git a/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs b/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs index df55aa739e2f..e0f7799e5a72 100644 --- a/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs +++ b/src/Files.App/UserControls/SideBar/SideBarPane.xaml.cs @@ -45,7 +45,6 @@ private void SideBar_Loaded(object sender, RoutedEventArgs e) public void RaiseContextRequested(SideBarItem item, Point e) { - // Only leaves can be selected ItemContextInvoked?.Invoke(item, new ItemContextInvokedArgs(item.DataContext, e)); } diff --git a/src/Files.App/UserControls/SideBar/SideBarItem.resources.cs b/src/Files.App/UserControls/SideBar/SideBarResources.cs similarity index 77% rename from src/Files.App/UserControls/SideBar/SideBarItem.resources.cs rename to src/Files.App/UserControls/SideBar/SideBarResources.cs index d059c4a1e667..3d91bb3f3ffe 100644 --- a/src/Files.App/UserControls/SideBar/SideBarItem.resources.cs +++ b/src/Files.App/UserControls/SideBar/SideBarResources.cs @@ -7,7 +7,7 @@ namespace Files.App.UserControls.SideBar { - partial class SideBarItemResources : ResourceDictionary + partial class SideBarResources : ResourceDictionary { } } From 4d91d3fd7e64ee4b3c10bd011638a045b4f1694c Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Mon, 24 Jul 2023 11:30:38 +0200 Subject: [PATCH 05/83] Implement minimal mode --- .../SideBarResources.xaml | 4 +-- .../SideBar/SideBarHost.properties.cs | 15 +++++++--- .../UserControls/SideBar/SideBarHost.xaml | 30 +++++++++++-------- .../UserControls/SideBar/SideBarHost.xaml.cs | 27 +++++++++-------- .../SideBar/SideBarPane.properties.cs | 12 ++++---- .../UserControls/SideBar/SideBarPane.xaml | 17 ++++++----- .../UserControls/SideBar/SideBarPane.xaml.cs | 8 ++--- .../UserControls/SidebarViewModel.cs | 14 ++++----- src/Files.App/Views/MainPage.xaml | 20 ++++--------- 9 files changed, 73 insertions(+), 74 deletions(-) diff --git a/src/Files.App/ResourceDictionaries/SideBarResources.xaml b/src/Files.App/ResourceDictionaries/SideBarResources.xaml index 9b3511e70cfd..b94933dc3197 100644 --- a/src/Files.App/ResourceDictionaries/SideBarResources.xaml +++ b/src/Files.App/ResourceDictionaries/SideBarResources.xaml @@ -28,7 +28,6 @@ - @@ -117,11 +116,10 @@ HorizontalAlignment="Stretch" BorderThickness="1" Margin="4,0" - BorderBrush="Transparent" Background="Transparent" Height="30" CornerRadius="{ThemeResource ControlCornerRadius}"> - + diff --git a/src/Files.App/UserControls/SideBar/SideBarHost.properties.cs b/src/Files.App/UserControls/SideBar/SideBarHost.properties.cs index 5ec27fbfa767..1461bac3b738 100644 --- a/src/Files.App/UserControls/SideBar/SideBarHost.properties.cs +++ b/src/Files.App/UserControls/SideBar/SideBarHost.properties.cs @@ -17,9 +17,8 @@ public SideBarDisplayMode DisplayMode get { return (SideBarDisplayMode)GetValue(DisplayModeProperty); } set { SetValue(DisplayModeProperty, value); } } - public event EventHandler? DisplayModeChanged; public static readonly DependencyProperty DisplayModeProperty = - DependencyProperty.Register("DisplayMode", typeof(SideBarDisplayMode), typeof(SideBarPane), new PropertyMetadata(SideBarDisplayMode.Expanded)); + DependencyProperty.Register("DisplayMode", typeof(SideBarDisplayMode), typeof(SideBarHost), new PropertyMetadata(SideBarDisplayMode.Expanded)); public UIElement InnerContent { @@ -34,8 +33,16 @@ public UIElement TabContent get => (UIElement)GetValue(TabContentProperty); set => SetValue(TabContentProperty, value); } - public static readonly DependencyProperty TabContentProperty = DependencyProperty.Register(nameof(TabContent), typeof(UIElement), typeof(SideBarHost), new PropertyMetadata(null)); + public static readonly DependencyProperty TabContentProperty = + DependencyProperty.Register(nameof(TabContent), typeof(UIElement), typeof(SideBarHost), new PropertyMetadata(null)); + public bool IsPaneOpen + { + get { return (bool)GetValue(IsPaneOpenProperty); } + set { SetValue(IsPaneOpenProperty, value); } + } + public static readonly DependencyProperty IsPaneOpenProperty = + DependencyProperty.Register("IsPaneOpen", typeof(bool), typeof(SideBarHost), new PropertyMetadata(false)); public SidebarViewModel ViewModel { @@ -43,7 +50,7 @@ public SidebarViewModel ViewModel set => SetValue(ViewModelProperty, value); } public static readonly DependencyProperty ViewModelProperty = - DependencyProperty.Register(nameof(ViewModel), typeof(SidebarViewModel), typeof(SidebarControl), new PropertyMetadata(null)); + DependencyProperty.Register(nameof(ViewModel), typeof(SidebarViewModel), typeof(SideBarHost), new PropertyMetadata(null)); public INavigationControlItem SelectedItem { diff --git a/src/Files.App/UserControls/SideBar/SideBarHost.xaml b/src/Files.App/UserControls/SideBar/SideBarHost.xaml index fee34d8f58e0..21cea8149e44 100644 --- a/src/Files.App/UserControls/SideBar/SideBarHost.xaml +++ b/src/Files.App/UserControls/SideBar/SideBarHost.xaml @@ -5,7 +5,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:sidebar="using:Files.App.UserControls.SideBar" + xmlns:sidebar="using:Files.App.UserControls.SideBar" xmlns:triggers="using:CommunityToolkit.WinUI.UI.Triggers" HorizontalAlignment="Stretch" Loaded="SideBarHost_Loaded" SizeChanged="SideBarHost_SizeChanged" @@ -17,16 +17,16 @@ - - - - - - + + + + + + @@ -34,20 +34,24 @@ - +