From ecb3ceaf3a12f2e23af8460cb83fb2e3cfee1260 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Sat, 9 Mar 2024 02:09:22 +0900 Subject: [PATCH 01/14] Initial commit --- .../Data/Contexts/HomePage/HomePageContext.cs | 4 +- .../Contexts/HomePage/IHomePageContext.cs | 2 +- .../Data/Contracts/IWidgetViewModel.cs | 2 +- .../QuickAccessCardEventArgs.cs | 10 - .../Data/Items/WidgetContainerItem.cs | 9 +- .../Data/Items/WidgetFileTagCardItem.cs | 13 +- .../Data/Items/WidgetFileTagsContainerItem.cs | 10 +- src/Files.App/Strings/en-US/Resources.resw | 6 - .../UserControls/Widgets/DrivesWidget.xaml | 11 +- .../UserControls/Widgets/DrivesWidget.xaml.cs | 269 ++------------ .../UserControls/Widgets/FileTagsWidget.xaml | 27 +- .../Widgets/FileTagsWidget.xaml.cs | 226 +----------- .../Widgets/QuickAccessWidget.xaml | 10 +- .../Widgets/QuickAccessWidget.xaml.cs | 328 +---------------- .../Widgets/RecentFilesWidget.xaml | 20 +- .../Widgets/RecentFilesWidget.xaml.cs | 340 +----------------- src/Files.App/Utils/Widgets/WidgetsHelpers.cs | 34 +- src/Files.App/ViewModels/HomeViewModel.cs | 83 ++++- .../Widgets/BaseWidgetViewModel.cs | 53 +-- .../Widgets/DrivesWidgetViewModel.cs | 271 ++++++++++++++ .../Widgets/FileTagsWidgetViewModel.cs | 210 ++++++++++- .../Widgets/QuickAccessWidgetViewModel.cs | 316 ++++++++++++++++ .../Widgets/RecentFilesWidgetViewModel.cs | 340 ++++++++++++++++++ src/Files.App/Views/HomePage.xaml.cs | 208 +---------- .../Views/Shells/ModernShellPage.xaml.cs | 2 +- 25 files changed, 1354 insertions(+), 1450 deletions(-) diff --git a/src/Files.App/Data/Contexts/HomePage/HomePageContext.cs b/src/Files.App/Data/Contexts/HomePage/HomePageContext.cs index bf671697dd4c..e8d0085dcee9 100644 --- a/src/Files.App/Data/Contexts/HomePage/HomePageContext.cs +++ b/src/Files.App/Data/Contexts/HomePage/HomePageContext.cs @@ -7,7 +7,7 @@ namespace Files.App.Data.Contexts { - internal class HomePageContext : ObservableObject, IHomePageContext + public class HomePageContext : ObservableObject, IHomePageContext { private static readonly IImmutableList emptyTaggedItems = Enumerable.Empty().ToImmutableList(); @@ -29,7 +29,7 @@ public IReadOnlyList SelectedTaggedItems public HomePageContext() { BaseWidgetViewModel.RightClickedItemChanged += HomePageWidget_RightClickedItemChanged; - FileTagsWidget.SelectedTaggedItemsChanged += FileTagsWidget_SelectedTaggedItemsChanged; + FileTagsWidgetViewModel.SelectedTaggedItemsChanged += FileTagsWidget_SelectedTaggedItemsChanged; } private void FileTagsWidget_SelectedTaggedItemsChanged(object? sender, IEnumerable e) diff --git a/src/Files.App/Data/Contexts/HomePage/IHomePageContext.cs b/src/Files.App/Data/Contexts/HomePage/IHomePageContext.cs index ccf515e49ded..345e162ac20c 100644 --- a/src/Files.App/Data/Contexts/HomePage/IHomePageContext.cs +++ b/src/Files.App/Data/Contexts/HomePage/IHomePageContext.cs @@ -5,7 +5,7 @@ namespace Files.App.Data.Contexts { - internal interface IHomePageContext + public interface IHomePageContext { /// /// The last right clicked item diff --git a/src/Files.App/Data/Contracts/IWidgetViewModel.cs b/src/Files.App/Data/Contracts/IWidgetViewModel.cs index f890b1fb45dc..051c815dd474 100644 --- a/src/Files.App/Data/Contracts/IWidgetViewModel.cs +++ b/src/Files.App/Data/Contracts/IWidgetViewModel.cs @@ -17,7 +17,7 @@ public interface IWidgetViewModel : IDisposable bool ShowMenuFlyout { get; } - MenuFlyoutItem MenuFlyoutItem { get; } + MenuFlyoutItem? MenuFlyoutItem { get; } Task RefreshWidgetAsync(); } diff --git a/src/Files.App/Data/EventArguments/QuickAccessCardEventArgs.cs b/src/Files.App/Data/EventArguments/QuickAccessCardEventArgs.cs index 672221b4c0c2..086b7d533148 100644 --- a/src/Files.App/Data/EventArguments/QuickAccessCardEventArgs.cs +++ b/src/Files.App/Data/EventArguments/QuickAccessCardEventArgs.cs @@ -3,16 +3,6 @@ namespace Files.App.Data.EventArguments { - public class QuickAccessCardEventArgs : EventArgs - { - public LocationItem? Item { get; set; } - } - - public class QuickAccessCardInvokedEventArgs : EventArgs - { - public string? Path { get; set; } - } - public class ModifyQuickAccessEventArgs : EventArgs { public string[]? Paths { get; set; } diff --git a/src/Files.App/Data/Items/WidgetContainerItem.cs b/src/Files.App/Data/Items/WidgetContainerItem.cs index 116250ba829e..3ae262abc7c7 100644 --- a/src/Files.App/Data/Items/WidgetContainerItem.cs +++ b/src/Files.App/Data/Items/WidgetContainerItem.cs @@ -17,8 +17,12 @@ public class WidgetContainerItem : ObservableObject, IDisposable // Properties + public IWidgetViewModel _WidgetItemModel; public IWidgetViewModel WidgetItemModel - => WidgetControl as IWidgetViewModel; + { + get => _WidgetItemModel; + set => SetProperty(ref _WidgetItemModel, value); + } public string WidgetAutomationProperties => WidgetItemModel.AutomationProperties; @@ -48,11 +52,12 @@ public bool IsExpanded // Constructor - public WidgetContainerItem(object widgetControl, Action expanderValueChangedCallback, Func expanderValueRequestedCallback) + public WidgetContainerItem(object widgetControl, IWidgetViewModel widgetItemModel, Action expanderValueChangedCallback, Func expanderValueRequestedCallback) { _expanderValueChangedCallback = expanderValueChangedCallback; _expanderValueRequestedCallback = expanderValueRequestedCallback; + WidgetItemModel = widgetItemModel; WidgetControl = widgetControl; } diff --git a/src/Files.App/Data/Items/WidgetFileTagCardItem.cs b/src/Files.App/Data/Items/WidgetFileTagCardItem.cs index b81e9f9a6bd8..d7ca78e81796 100644 --- a/src/Files.App/Data/Items/WidgetFileTagCardItem.cs +++ b/src/Files.App/Data/Items/WidgetFileTagCardItem.cs @@ -9,14 +9,14 @@ namespace Files.App.Data.Items { public sealed partial class WidgetFileTagCardItem : WidgetCardItem { + // Dependency injections + + private IContentPageContext ContentPageContext { get; } = Ioc.Default.GetRequiredService(); + // Fields private readonly IStorable _associatedStorable; - // A workaround for lack of MVVM-compliant navigation support. - // This workaround must be kept until further refactor of navigation code is completed. - private readonly Func _openAction; - // Properties public bool IsFolder @@ -47,10 +47,9 @@ public override string Path public ICommand ClickCommand { get; } - public WidgetFileTagCardItem(IStorable associatedStorable, Func openAction, IImage? icon) + public WidgetFileTagCardItem(IStorable associatedStorable, IImage? icon) { _associatedStorable = associatedStorable; - _openAction = openAction; _Icon = icon; _Name = associatedStorable.Name; _Path = associatedStorable.TryGetPath(); @@ -61,7 +60,7 @@ public WidgetFileTagCardItem(IStorable associatedStorable, Func op private Task ClickAsync() { - return _openAction(_associatedStorable.Id); + return NavigationHelpers.OpenPath(_associatedStorable.Id, ContentPageContext.ShellPage!); } } } diff --git a/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs b/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs index e99674c30fe3..08928f4ff05e 100644 --- a/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs +++ b/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs @@ -13,11 +13,10 @@ public sealed partial class WidgetFileTagsContainerItem : ObservableObject, IAsy private readonly IFileTagsService FileTagsService = Ioc.Default.GetRequiredService(); private readonly IImageService ImageService = Ioc.Default.GetRequiredService(); private readonly ICommandManager Commands = Ioc.Default.GetRequiredService(); + private IContentPageContext ContentPageContext { get; } = Ioc.Default.GetRequiredService(); private readonly string _tagUid; - private readonly Func _openAction; - // Properties public ObservableCollection Tags { get; } @@ -46,10 +45,9 @@ public sealed partial class WidgetFileTagsContainerItem : ObservableObject, IAsy public ICommand ViewMoreCommand { get; } public ICommand OpenAllCommand { get; } - public WidgetFileTagsContainerItem(string tagUid, Func openAction) + public WidgetFileTagsContainerItem(string tagUid) { _tagUid = tagUid; - _openAction = openAction; Tags = new(); ViewMoreCommand = new AsyncRelayCommand(ViewMore); @@ -62,13 +60,13 @@ public async Task InitAsync(CancellationToken cancellationToken = default) await foreach (var item in FileTagsService.GetItemsForTagAsync(_tagUid, cancellationToken)) { var icon = await ImageService.GetIconAsync(item.Storable, cancellationToken); - Tags.Add(new(item.Storable, _openAction, icon)); + Tags.Add(new(item.Storable, icon)); } } private Task ViewMore(CancellationToken cancellationToken) { - return _openAction($"tag:{Name}"); + return NavigationHelpers.OpenPath($"tag:{Name}", ContentPageContext.ShellPage!); } private Task OpenAll(CancellationToken cancellationToken) diff --git a/src/Files.App/Strings/en-US/Resources.resw b/src/Files.App/Strings/en-US/Resources.resw index 27c3707e11cc..9864e3067e33 100644 --- a/src/Files.App/Strings/en-US/Resources.resw +++ b/src/Files.App/Strings/en-US/Resources.resw @@ -1698,12 +1698,6 @@ UserName - - Drives widget - - - Recent Files widget - No items found diff --git a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml index 74aa5f71355f..d9b305da6b92 100644 --- a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml +++ b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml @@ -1,22 +1,17 @@ - - + - + diff --git a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs index 129a962a4618..1070eba867f9 100644 --- a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs @@ -1,23 +1,19 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; -using System.Collections.Specialized; -using System.Runtime.CompilerServices; -using System.Windows.Input; -using Windows.System; -using Windows.UI.Core; namespace Files.App.UserControls.Widgets { /// /// Represents group of control displays a list of . /// - public sealed partial class DrivesWidget : BaseWidgetViewModel, IWidgetViewModel, INotifyPropertyChanged + public sealed partial class DrivesWidget : UserControl { +<<<<<<< HEAD +<<<<<<< HEAD private DrivesWidgetViewModel ViewModel { get; set; } public IUserSettingsService userSettingsService { get; } = Ioc.Default.GetRequiredService(); @@ -66,272 +62,53 @@ public IShellPage AppInstance }; public AsyncRelayCommand MapNetworkDriveCommand { get; } +======= + private DrivesWidgetViewModel ViewModel { get; set; } = new(); +>>>>>>> 70e2ff662 (Initial commit) +======= + public DrivesWidgetViewModel ViewModel { get; set; } = new(); +>>>>>>> 145267b14 (Fix) public DrivesWidget() { InitializeComponent(); - - Drives_CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - - drivesViewModel.Drives.CollectionChanged += Drives_CollectionChanged; - - FormatDriveCommand = new RelayCommand(FormatDrive); - EjectDeviceCommand = new AsyncRelayCommand(EjectDeviceAsync); - OpenInNewTabCommand = new AsyncRelayCommand(OpenInNewTabAsync); - OpenInNewWindowCommand = new AsyncRelayCommand(OpenInNewWindowAsync); - OpenInNewPaneCommand = new AsyncRelayCommand(OpenInNewPaneAsync); - OpenPropertiesCommand = new RelayCommand(OpenProperties); - PinToSidebarCommand = new AsyncRelayCommand(PinToSidebarAsync); - UnpinFromSidebarCommand = new AsyncRelayCommand(UnpinFromSidebarAsync); - MapNetworkDriveCommand = new AsyncRelayCommand(DoNetworkMapDriveAsync); - DisconnectNetworkDriveCommand = new RelayCommand(DisconnectNetworkDrive); - } - - private async void Drives_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) - { - await DispatcherQueue.EnqueueOrInvokeAsync(async () => - { - foreach (DriveItem drive in drivesViewModel.Drives.ToList()) - { - if (!ItemsAdded.Any(x => x.Item == drive) && drive.Type != DriveType.VirtualDrive) - { - var cardItem = new WidgetDriveCardItem(drive); - ItemsAdded.AddSorted(cardItem); - await cardItem.LoadCardThumbnailAsync(); // After add - } - } - - foreach (WidgetDriveCardItem driveCard in ItemsAdded.ToList()) - { - if (!drivesViewModel.Drives.Contains(driveCard.Item)) - ItemsAdded.Remove(driveCard); - } - }); - } - - public override List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) - { - var drive = ItemsAdded.Where(x => string.Equals(PathNormalization.NormalizePath(x.Path), PathNormalization.NormalizePath(item.Path), StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); - var options = drive?.Item.MenuOptions; - - return new List() - { - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenInNewTab".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconOpenInNewTab", - }, - Command = OpenInNewTabCommand, - CommandParameter = item, - ShowItem = userSettingsService.GeneralSettingsService.ShowOpenInNewTab - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenInNewWindow".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconOpenInNewWindow", - }, - Command = OpenInNewWindowCommand, - CommandParameter = item, - ShowItem = userSettingsService.GeneralSettingsService.ShowOpenInNewWindow - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenInNewPane".GetLocalizedResource(), - Command = OpenInNewPaneCommand, - CommandParameter = item, - ShowItem = userSettingsService.GeneralSettingsService.ShowOpenInNewPane - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "PinFolderToSidebar".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "Icons.Pin.16x16", - }, - Command = PinToSidebarCommand, - CommandParameter = item, - ShowItem = !isPinned - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "UnpinFolderFromSidebar".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "Icons.Unpin.16x16", - }, - Command = UnpinFromSidebarCommand, - CommandParameter = item, - ShowItem = isPinned - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "Eject".GetLocalizedResource(), - Command = EjectDeviceCommand, - CommandParameter = item, - ShowItem = options?.ShowEjectDevice ?? false - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "FormatDriveText".GetLocalizedResource(), - Command = FormatDriveCommand, - CommandParameter = item, - ShowItem = options?.ShowFormatDrive ?? false - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "Properties".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconProperties", - }, - Command = OpenPropertiesCommand, - CommandParameter = item - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "TurnOnBitLocker".GetLocalizedResource(), - Tag = "TurnOnBitLockerPlaceholder", - IsEnabled = false - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "ManageBitLocker".GetLocalizedResource(), - Tag = "ManageBitLockerPlaceholder", - IsEnabled = false - }, - new ContextMenuFlyoutItemViewModel() - { - ItemType = ContextMenuFlyoutItemType.Separator, - Tag = "OverflowSeparator", - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "Loading".GetLocalizedResource(), - Glyph = "\xE712", - Items = new List(), - ID = "ItemOverflow", - Tag = "ItemOverflow", - IsEnabled = false, - } - }.Where(x => x.ShowItem).ToList(); - } - - private Task DoNetworkMapDriveAsync() - { - return networkDrivesViewModel.OpenMapNetworkDriveDialogAsync(); - } - - private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - private async Task EjectDeviceAsync(WidgetDriveCardItem item) - { - var result = await DriveHelpers.EjectDeviceAsync(item.Item.Path); - await UIHelpers.ShowDeviceEjectResultAsync(item.Item.Type, result); - } - - private void FormatDrive(WidgetDriveCardItem? item) - { - Win32API.OpenFormatDriveDialog(item?.Path ?? string.Empty); - } - - private void OpenProperties(WidgetDriveCardItem item) - { - if (!HomePageContext.IsAnyItemRightClicked) - return; - - var flyout = HomePageContext.ItemContextFlyoutMenu; - EventHandler flyoutClosed = null!; - flyoutClosed = (s, e) => - { - flyout!.Closed -= flyoutClosed; - FilePropertiesHelpers.OpenPropertiesWindow(item.Item, associatedInstance); - }; - - flyout!.Closed += flyoutClosed; } private async void Button_Click(object sender, RoutedEventArgs e) { - string ClickedCard = (sender as Button).Tag.ToString(); - string NavigationPath = ClickedCard; // path to navigate - - if (await DriveHelpers.CheckEmptyDrive(NavigationPath)) + if (sender is not Button button) return; - var ctrlPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); - if (ctrlPressed) - { - await NavigationHelpers.OpenPathInNewTab(NavigationPath, false); - return; - } + var path = button.Tag.ToString() ?? string.Empty; - DrivesWidgetInvoked?.Invoke(this, new DrivesWidgetInvokedEventArgs() - { - Path = NavigationPath - }); + await ViewModel.NavigateToPath(path); } private async void Button_PointerPressed(object sender, PointerRoutedEventArgs e) { - if (!e.GetCurrentPoint(null).Properties.IsMiddleButtonPressed) // check middle click - return; - string navigationPath = (sender as Button).Tag.ToString(); - if (await DriveHelpers.CheckEmptyDrive(navigationPath)) + if (!e.GetCurrentPoint(null).Properties.IsMiddleButtonPressed || sender is not Button button) return; - await NavigationHelpers.OpenPathInNewTab(navigationPath, false); - } - public class DrivesWidgetInvokedEventArgs : EventArgs - { - public string Path { get; set; } - } + var path = button.Tag.ToString() ?? string.Empty; - private async Task OpenInNewPaneAsync(WidgetDriveCardItem item) - { - if (await DriveHelpers.CheckEmptyDrive(item.Item.Path)) + if (await DriveHelpers.CheckEmptyDrive(path)) return; - DrivesWidgetNewPaneInvoked?.Invoke(this, new DrivesWidgetInvokedEventArgs() - { - Path = item.Item.Path - }); - } - private void MenuFlyout_Opening(object sender, object e) - { - var pinToSidebarItem = (sender as MenuFlyout).Items.Single(x => x.Name == "PinToSidebar"); - pinToSidebarItem.Visibility = (pinToSidebarItem.DataContext as DriveItem).IsPinned ? Visibility.Collapsed : Visibility.Visible; - - var unpinFromSidebarItem = (sender as MenuFlyout).Items.Single(x => x.Name == "UnpinFromSidebar"); - unpinFromSidebarItem.Visibility = (unpinFromSidebarItem.DataContext as DriveItem).IsPinned ? Visibility.Visible : Visibility.Collapsed; - } - - private void DisconnectNetworkDrive(WidgetDriveCardItem item) - { - networkDrivesViewModel.DisconnectNetworkDrive(item.Item); + await NavigationHelpers.OpenPathInNewTab(path, false); } - private void GoToStorageSense_Click(object sender, RoutedEventArgs e) + private void Button_RightTapped(object sender, RightTappedRoutedEventArgs e) { - string clickedCard = (sender as Button).Tag.ToString(); - StorageSenseHelper.OpenStorageSenseAsync(clickedCard); + ViewModel.BuildItemContextMenu(sender, e); } - public async Task RefreshWidgetAsync() - { - var updateTasks = ItemsAdded.Select(item => item.Item.UpdatePropertiesAsync()); - await Task.WhenAll(updateTasks); - } - - public void Dispose() + private async void GoToStorageSense_Click(object sender, RoutedEventArgs e) { + if (sender is not Button button) + return; + string path = button.Tag.ToString() ?? string.Empty; + await StorageSenseHelper.OpenStorageSenseAsync(path); } } } diff --git a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml index 1b128f13099d..a165770b6170 100644 --- a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml +++ b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml @@ -1,15 +1,17 @@  ->>>>>> 70e2ff662 (Initial commit) DataContext="{x:Bind ViewModel, Mode=OneWay}" mc:Ignorable="d"> @@ -19,7 +21,7 @@ - + - - @@ -112,7 +112,7 @@ - - + + - + - + - - + + - + diff --git a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs index 4f793c9cffbd..c7db3b1d692a 100644 --- a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs @@ -1,14 +1,8 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.Helpers.ContextFlyouts; -using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; using Microsoft.UI.Xaml.Input; -using System.IO; -using System.Windows.Input; -using Windows.Storage; namespace Files.App.UserControls.Widgets { @@ -16,8 +10,9 @@ namespace Files.App.UserControls.Widgets /// Represents group of control displays a list of /// and its inner items with . /// - public sealed partial class FileTagsWidget : BaseWidgetViewModel, IWidgetViewModel + public sealed partial class FileTagsWidget : UserControl { +<<<<<<< HEAD public FileTagsWidgetViewModel ViewModel { get; set; } private readonly IUserSettingsService userSettingsService; @@ -41,229 +36,24 @@ public sealed partial class FileTagsWidget : BaseWidgetViewModel, IWidgetViewMod public MenuFlyoutItem? MenuFlyoutItem => null; private ICommand OpenInNewPaneCommand; +======= + public FileTagsWidgetViewModel ViewModel { get; set; } = new(); +>>>>>>> 70e2ff662 (Initial commit) public FileTagsWidget() { - userSettingsService = Ioc.Default.GetRequiredService(); - InitializeComponent(); - - // Second function is layered on top to ensure that OpenPath function is late initialized and a null reference is not passed-in - // See FileTagItemViewModel._openAction for more information - ViewModel = new(x => OpenAction!(x)); - OpenInNewTabCommand = new AsyncRelayCommand(OpenInNewTabAsync); - OpenInNewWindowCommand = new AsyncRelayCommand(OpenInNewWindowAsync); - OpenFileLocationCommand = new RelayCommand(OpenFileLocation); - OpenInNewPaneCommand = new RelayCommand(OpenInNewPane); - PinToSidebarCommand = new AsyncRelayCommand(PinToSidebarAsync); - UnpinFromSidebarCommand = new AsyncRelayCommand(UnpinFromSidebarAsync); - OpenPropertiesCommand = new RelayCommand(OpenProperties); - } - - private void OpenProperties(WidgetCardItem? item) - { - if (!HomePageContext.IsAnyItemRightClicked) - return; - - var flyout = HomePageContext.ItemContextFlyoutMenu; - EventHandler flyoutClosed = null!; - flyoutClosed = (s, e) => - { - flyout!.Closed -= flyoutClosed; - - ListedItem listedItem = new(null!) - { - ItemPath = (item.Item as WidgetFileTagCardItem)?.Path ?? string.Empty, - ItemNameRaw = (item.Item as WidgetFileTagCardItem)?.Name ?? string.Empty, - PrimaryItemAttribute = StorageItemTypes.Folder, - ItemType = "Folder".GetLocalizedResource(), - }; - FilePropertiesHelpers.OpenPropertiesWindow(listedItem, AppInstance); - }; - - flyout!.Closed += flyoutClosed; - } - - private void OpenInNewPane(WidgetCardItem? item) - { - FileTagsNewPaneInvoked?.Invoke(this, new QuickAccessCardInvokedEventArgs() - { - Path = item?.Path ?? string.Empty - }); } private void FileTagItem_ItemClick(object sender, ItemClickEventArgs e) { - if (e.ClickedItem is WidgetFileTagCardItem itemViewModel) - itemViewModel.ClickCommand.Execute(null); + if (e.ClickedItem is WidgetFileTagCardItem item) + item.ClickCommand.Execute(null); } private void AdaptiveGridView_RightTapped(object sender, RightTappedRoutedEventArgs e) { - // Ensure values are not null - if (e.OriginalSource is not FrameworkElement element || - element.DataContext is not WidgetFileTagCardItem item) - return; - - // Create a new Flyout - var itemContextMenuFlyout = new CommandBarFlyout() - { - Placement = FlyoutPlacementMode.Full - }; - - // Hook events - itemContextMenuFlyout.Opening += (sender, e) => App.LastOpenedFlyout = sender as CommandBarFlyout; - itemContextMenuFlyout.Closed += (sender, e) => OnRightClickedItemChanged(null, null); - - _flyoutItemPath = item.Path; - - // Notify of the change on right clicked item - OnRightClickedItemChanged(item, itemContextMenuFlyout); - - // Get items for the flyout - var menuItems = GetItemMenuItems(item, QuickAccessService.IsItemPinned(item.Path), item.IsFolder); - var (_, secondaryElements) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel(menuItems); - - // Set max width of the flyout - secondaryElements - .OfType() - .ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); - - // Add menu items to the secondary flyout - secondaryElements.ForEach(itemContextMenuFlyout.SecondaryCommands.Add); - - // Show the flyout - itemContextMenuFlyout.ShowAt(element, new() { Position = e.GetPosition(element) }); - - // Load shell menu items - _ = ShellContextFlyoutFactory.LoadShellMenuItemsAsync(_flyoutItemPath, itemContextMenuFlyout, null, true, true); - - e.Handled = true; - } - - public override List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) - { - return new List() - { - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenWith".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconOpenWith", - }, - Tag = "OpenWithPlaceholder", - ShowItem = !isFolder - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "SendTo".GetLocalizedResource(), - Tag = "SendToPlaceholder", - ShowItem = !isFolder && userSettingsService.GeneralSettingsService.ShowSendToMenu - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenInNewTab".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconOpenInNewTab", - }, - Command = OpenInNewTabCommand, - CommandParameter = item, - ShowItem = isFolder - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenInNewWindow".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconOpenInNewWindow", - }, - Command = OpenInNewWindowCommand, - CommandParameter = item, - ShowItem = isFolder - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenFileLocation".GetLocalizedResource(), - Glyph = "\uED25", - Command = OpenFileLocationCommand, - CommandParameter = item, - ShowItem = !isFolder - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenInNewPane".GetLocalizedResource(), - Command = OpenInNewPaneCommand, - CommandParameter = item, - ShowItem = userSettingsService.GeneralSettingsService.ShowOpenInNewPane && isFolder - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "PinFolderToSidebar".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "Icons.Pin.16x16", - }, - Command = PinToSidebarCommand, - CommandParameter = item, - ShowItem = !isPinned && isFolder - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "UnpinFolderFromSidebar".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "Icons.Unpin.16x16", - }, - Command = UnpinFromSidebarCommand, - CommandParameter = item, - ShowItem = isPinned && isFolder - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "Properties".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconProperties", - }, - Command = OpenPropertiesCommand, - CommandParameter = item, - ShowItem = isFolder - }, - new ContextMenuFlyoutItemViewModel() - { - ItemType = ContextMenuFlyoutItemType.Separator, - Tag = "OverflowSeparator", - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "Loading".GetLocalizedResource(), - Glyph = "\xE712", - Items = new List(), - ID = "ItemOverflow", - Tag = "ItemOverflow", - IsEnabled = false, - } - }.Where(x => x.ShowItem).ToList(); - } - - public void OpenFileLocation(WidgetCardItem? item) - { - FileTagsOpenLocationInvoked?.Invoke(this, new PathNavigationEventArgs() - { - ItemPath = Directory.GetParent(item?.Path ?? string.Empty)?.FullName ?? string.Empty, - ItemName = Path.GetFileName(item?.Path ?? string.Empty), - }); - } - - public Task RefreshWidgetAsync() - { - return Task.CompletedTask; - } - - public void Dispose() - { + ViewModel.BuildItemContextMenu(e.OriginalSource, e); } } } diff --git a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml index 40321096abf3..376e5c132750 100644 --- a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml +++ b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml @@ -1,20 +1,16 @@  - - + - + diff --git a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs index 71d98d0e3a5e..efc4466a01f8 100644 --- a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs @@ -4,20 +4,16 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; -using System.Collections.Specialized; -using System.IO; -using System.Runtime.CompilerServices; -using System.Windows.Input; -using Windows.System; -using Windows.UI.Core; namespace Files.App.UserControls.Widgets { /// /// Represents group of control displays a list of quick access folders with . /// - public sealed partial class QuickAccessWidget : BaseWidgetViewModel, IWidgetViewModel, INotifyPropertyChanged + public sealed partial class QuickAccessWidget : UserControl { +<<<<<<< HEAD +<<<<<<< HEAD private QuickAccessWidgetViewModel ViewModel { get; set; } public IUserSettingsService userSettingsService { get; } = Ioc.Default.GetRequiredService(); @@ -29,330 +25,38 @@ static QuickAccessWidget() { ItemsAdded.CollectionChanged += ItemsAdded_CollectionChanged; } +======= + private QuickAccessWidgetViewModel ViewModel { get; set; } = new(); +>>>>>>> 70e2ff662 (Initial commit) +======= + public QuickAccessWidgetViewModel ViewModel { get; set; } = new(); +>>>>>>> 145267b14 (Fix) public QuickAccessWidget() { InitializeComponent(); - - Loaded += QuickAccessWidget_Loaded; - Unloaded += QuickAccessWidget_Unloaded; - - OpenInNewTabCommand = new AsyncRelayCommand(OpenInNewTabAsync); - OpenInNewWindowCommand = new AsyncRelayCommand(OpenInNewWindowAsync); - OpenInNewPaneCommand = new RelayCommand(OpenInNewPane); - OpenPropertiesCommand = new RelayCommand(OpenProperties); - PinToSidebarCommand = new AsyncRelayCommand(PinToSidebarAsync); - UnpinFromSidebarCommand = new AsyncRelayCommand(UnpinFromSidebarAsync); - } - - public delegate void QuickAccessCardInvokedEventHandler(object sender, QuickAccessCardInvokedEventArgs e); - public delegate void QuickAccessCardNewPaneInvokedEventHandler(object sender, QuickAccessCardInvokedEventArgs e); - public delegate void QuickAccessCardPropertiesInvokedEventHandler(object sender, QuickAccessCardEventArgs e); - public event QuickAccessCardInvokedEventHandler CardInvoked; - public event QuickAccessCardNewPaneInvokedEventHandler CardNewPaneInvoked; - public event QuickAccessCardPropertiesInvokedEventHandler CardPropertiesInvoked; - public event EventHandler QuickAccessWidgetShowMultiPaneControlsInvoked; - public event PropertyChangedEventHandler PropertyChanged; - - public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowQuickAccessWidget; - public bool ShowMenuFlyout => false; - public MenuFlyoutItem? MenuFlyoutItem => null; - - public ICommand OpenPropertiesCommand; - public ICommand OpenInNewPaneCommand; - - public string WidgetName => nameof(QuickAccessWidget); - public string AutomationProperties => "QuickAccess".GetLocalizedResource(); - public string WidgetHeader => "QuickAccess".GetLocalizedResource(); - - public override List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) - { - return new List() - { - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenInNewTab".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconOpenInNewTab", - }, - Command = OpenInNewTabCommand, - CommandParameter = item, - ShowItem = userSettingsService.GeneralSettingsService.ShowOpenInNewTab - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenInNewWindow".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconOpenInNewWindow", - }, - Command = OpenInNewWindowCommand, - CommandParameter = item, - ShowItem = userSettingsService.GeneralSettingsService.ShowOpenInNewWindow - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenInNewPane".GetLocalizedResource(), - Command = OpenInNewPaneCommand, - CommandParameter = item, - ShowItem = userSettingsService.GeneralSettingsService.ShowOpenInNewPane - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "PinFolderToSidebar".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "Icons.Pin.16x16", - }, - Command = PinToSidebarCommand, - CommandParameter = item, - ShowItem = !isPinned - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "UnpinFolderFromSidebar".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "Icons.Unpin.16x16", - }, - Command = UnpinFromSidebarCommand, - CommandParameter = item, - ShowItem = isPinned - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "Properties".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconProperties", - }, - Command = OpenPropertiesCommand, - CommandParameter = item - }, - new ContextMenuFlyoutItemViewModel() - { - ItemType = ContextMenuFlyoutItemType.Separator, - Tag = "OverflowSeparator", - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "Loading".GetLocalizedResource(), - Glyph = "\xE712", - Items = new List(), - ID = "ItemOverflow", - Tag = "ItemOverflow", - IsEnabled = false, - } - }.Where(x => x.ShowItem).ToList(); - } - - private async void ModifyItemAsync(object? sender, ModifyQuickAccessEventArgs? e) - { - if (e is null) - return; - - await DispatcherQueue.EnqueueOrInvokeAsync(async () => - { - if (e.Reset) - { - // Find the intersection between the two lists and determine whether to remove or add - var originalItemsAdded = ItemsAdded.ToList(); - var itemsToRemove = originalItemsAdded.Where(x => !e.Paths.Contains(x.Path)); - var itemsToAdd = e.Paths.Where(x => !originalItemsAdded.Any(y => y.Path == x)); - - // Remove items - foreach (var itemToRemove in itemsToRemove) - ItemsAdded.Remove(itemToRemove); - - // Add items - foreach (var itemToAdd in itemsToAdd) - { - var interimItemsAdded = ItemsAdded.ToList(); - var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd); - var lastIndex = ItemsAdded.IndexOf(interimItemsAdded.FirstOrDefault(x => !x.IsPinned)); - var isPinned = (bool?)e.Items.Where(x => x.FilePath == itemToAdd).FirstOrDefault()?.Properties["System.Home.IsPinned"] ?? false; - if (interimItemsAdded.Any(x => x.Path == itemToAdd)) - continue; - - ItemsAdded.Insert(isPinned && lastIndex >= 0 ? Math.Min(lastIndex, ItemsAdded.Count) : ItemsAdded.Count, new WidgetFolderCardItem(item, Path.GetFileName(item.Text), isPinned) - { - Path = item.Path, - }); - } - - return; - } - if (e.Reorder) - { - // Remove pinned items - foreach (var itemToRemove in ItemsAdded.ToList().Where(x => x.IsPinned)) - ItemsAdded.Remove(itemToRemove); - - // Add pinned items in the new order - foreach (var itemToAdd in e.Paths) - { - var interimItemsAdded = ItemsAdded.ToList(); - var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd); - var lastIndex = ItemsAdded.IndexOf(interimItemsAdded.FirstOrDefault(x => !x.IsPinned)); - if (interimItemsAdded.Any(x => x.Path == itemToAdd)) - continue; - - ItemsAdded.Insert(lastIndex >= 0 ? Math.Min(lastIndex, ItemsAdded.Count) : ItemsAdded.Count, new WidgetFolderCardItem(item, Path.GetFileName(item.Text), true) - { - Path = item.Path, - }); - } - - return; - } - if (e.Add) - { - foreach (var itemToAdd in e.Paths) - { - var interimItemsAdded = ItemsAdded.ToList(); - var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd); - var lastIndex = ItemsAdded.IndexOf(interimItemsAdded.FirstOrDefault(x => !x.IsPinned)); - if (interimItemsAdded.Any(x => x.Path == itemToAdd)) - continue; - ItemsAdded.Insert(e.Pin && lastIndex >= 0 ? Math.Min(lastIndex, ItemsAdded.Count) : ItemsAdded.Count, new WidgetFolderCardItem(item, Path.GetFileName(item.Text), e.Pin) // Add just after the Recent Folders - { - Path = item.Path, - }); - } - } - else - foreach (var itemToRemove in ItemsAdded.ToList().Where(x => e.Paths.Contains(x.Path))) - ItemsAdded.Remove(itemToRemove); - }); - } - - private async void QuickAccessWidget_Loaded(object sender, RoutedEventArgs e) - { - Loaded -= QuickAccessWidget_Loaded; - - var itemsToAdd = await QuickAccessService.GetPinnedFoldersAsync(); - ModifyItemAsync(this, new ModifyQuickAccessEventArgs(itemsToAdd.ToArray(), false) - { - Reset = true - }); - - App.QuickAccessManager.UpdateQuickAccessWidget += ModifyItemAsync; - } - - private void QuickAccessWidget_Unloaded(object sender, RoutedEventArgs e) - { - Unloaded -= QuickAccessWidget_Unloaded; - App.QuickAccessManager.UpdateQuickAccessWidget -= ModifyItemAsync; - } - - private static async void ItemsAdded_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) - { - if (e.Action is NotifyCollectionChangedAction.Add) - { - foreach (WidgetFolderCardItem cardItem in e.NewItems!) - await cardItem.LoadCardThumbnailAsync(); - } - } - - private void MenuFlyout_Opening(object sender) - { - var pinToSidebarItem = (sender as MenuFlyout)?.Items.SingleOrDefault(x => x.Name == "PinToSidebar"); - if (pinToSidebarItem is not null) - pinToSidebarItem.Visibility = (pinToSidebarItem.DataContext as WidgetFolderCardItem)?.IsPinned ?? false ? Visibility.Collapsed : Visibility.Visible; - - var unpinFromSidebarItem = (sender as MenuFlyout)?.Items.SingleOrDefault(x => x.Name == "UnpinFromSidebar"); - if (unpinFromSidebarItem is not null) - unpinFromSidebarItem.Visibility = (unpinFromSidebarItem.DataContext as WidgetFolderCardItem)?.IsPinned ?? false ? Visibility.Visible : Visibility.Collapsed; - } - - private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - private void OpenInNewPane(WidgetFolderCardItem item) - { - CardNewPaneInvoked?.Invoke(this, new QuickAccessCardInvokedEventArgs { Path = item.Path }); } private async void Button_PointerPressed(object sender, PointerRoutedEventArgs e) { - if (e.GetCurrentPoint(null).Properties.IsMiddleButtonPressed) // check middle click - { - string navigationPath = ((Button)sender).Tag.ToString()!; - await NavigationHelpers.OpenPathInNewTab(navigationPath, false); - } - } - - private void OpenProperties(WidgetFolderCardItem item) - { - if (!HomePageContext.IsAnyItemRightClicked) + if (!e.GetCurrentPoint(null).Properties.IsMiddleButtonPressed || sender is not Button button) return; - var flyout = HomePageContext.ItemContextFlyoutMenu; - EventHandler flyoutClosed = null!; - - flyoutClosed = (s, e) => - { - flyout!.Closed -= flyoutClosed; - CardPropertiesInvoked?.Invoke(this, new QuickAccessCardEventArgs { Item = item.Item }); - }; - - flyout!.Closed += flyoutClosed; - } - - public override async Task PinToSidebarAsync(WidgetCardItem item) - { - await QuickAccessService.PinToSidebarAsync(item.Path); - - ModifyItemAsync(this, new ModifyQuickAccessEventArgs(new[] { item.Path }, false)); - - var items = (await QuickAccessService.GetPinnedFoldersAsync()) - .Where(link => !((bool?)link.Properties["System.Home.IsPinned"] ?? false)); - - var recentItem = items.Where(x => !ItemsAdded.ToList().Select(y => y.Path).Contains(x.FilePath)).FirstOrDefault(); - if (recentItem is not null) - { - ModifyItemAsync(this, new ModifyQuickAccessEventArgs(new[] { recentItem.FilePath }, true) - { - Pin = false - }); - } - } - - public override async Task UnpinFromSidebarAsync(WidgetCardItem item) - { - await QuickAccessService.UnpinFromSidebarAsync(item.Path); - - ModifyItemAsync(this, new ModifyQuickAccessEventArgs(new[] { item.Path }, false)); + string path = button.Tag.ToString()!; + await NavigationHelpers.OpenPathInNewTab(path, false); } private async void Button_Click(object sender, RoutedEventArgs e) { - string ClickedCard = (sender as Button).Tag.ToString(); - string NavigationPath = ClickedCard; // path to navigate - - if (string.IsNullOrEmpty(NavigationPath)) - return; - - var ctrlPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); - if (ctrlPressed) - { - await NavigationHelpers.OpenPathInNewTab(NavigationPath, false); + if (sender is not Button button || button.Tag.ToString() is not string path) return; - } - CardInvoked?.Invoke(this, new QuickAccessCardInvokedEventArgs { Path = NavigationPath }); - } - - public Task RefreshWidgetAsync() - { - return Task.CompletedTask; + await ViewModel.NavigateToPath(path); } - public void Dispose() + private void Button_RightTapped(object sender, RightTappedRoutedEventArgs e) { + ViewModel.BuildItemContextMenu(sender, e); } } } diff --git a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml index 99954af4b46b..0d822fca475f 100644 --- a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml +++ b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml @@ -1,23 +1,21 @@ - - + - @@ -93,4 +91,4 @@ - \ No newline at end of file + diff --git a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs index 6b82822e1cd4..9bddebcabf8d 100644 --- a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs @@ -1,25 +1,18 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.Helpers.ContextFlyouts; -using Microsoft.Extensions.Logging; -using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; using Microsoft.UI.Xaml.Input; -using System.Collections.Specialized; -using System.IO; -using System.Runtime.CompilerServices; -using Windows.Foundation.Metadata; -using Windows.System; namespace Files.App.UserControls.Widgets { /// /// Represents group of control displays a list of recent folders with . /// - public sealed partial class RecentFilesWidget : BaseWidgetViewModel, IWidgetViewModel, INotifyPropertyChanged + public sealed partial class RecentFilesWidget : UserControl { +<<<<<<< HEAD +<<<<<<< HEAD private RecentFilesWidgetViewModel ViewModel { get; set; } private IHomePageContext HomePageContext { get; } = Ioc.Default.GetRequiredService(); @@ -87,334 +80,29 @@ public IShellPage AppInstance } } } +======= + private RecentFilesWidgetViewModel ViewModel { get; set; } = new(); +>>>>>>> 70e2ff662 (Initial commit) +======= + public RecentFilesWidgetViewModel ViewModel { get; set; } = new(); +>>>>>>> 145267b14 (Fix) public RecentFilesWidget() { InitializeComponent(); - - refreshRecentsSemaphore = new SemaphoreSlim(1, 1); - refreshRecentsCTS = new CancellationTokenSource(); - - // recent files could have changed while widget wasn't loaded - _ = RefreshWidgetAsync(); - - App.RecentItemsManager.RecentFilesChanged += Manager_RecentFilesChanged; - - RemoveRecentItemCommand = new AsyncRelayCommand(RemoveRecentItemAsync); - ClearAllItemsCommand = new AsyncRelayCommand(ClearRecentItemsAsync); - OpenFileLocationCommand = new RelayCommand(OpenFileLocation); - OpenPropertiesCommand = new RelayCommand(OpenProperties); - } - - private void ListView_RightTapped(object sender, RightTappedRoutedEventArgs e) - { - // Ensure values are not null - if (e.OriginalSource is not FrameworkElement element || - element.DataContext is not RecentItem item) - return; - - // Create a new Flyout - var itemContextMenuFlyout = new CommandBarFlyout() - { - Placement = FlyoutPlacementMode.Full - }; - - // Hook events - itemContextMenuFlyout.Opening += (sender, e) => App.LastOpenedFlyout = sender as CommandBarFlyout; - itemContextMenuFlyout.Closed += (sender, e) => OnRightClickedItemChanged(null, null); - - _flyoutItemPath = item.Path; - - // Notify of the change on right clicked item - OnRightClickedItemChanged(item, itemContextMenuFlyout); - - // Get items for the flyout - var menuItems = GetItemMenuItems(item, QuickAccessService.IsItemPinned(item.Path)); - var (_, secondaryElements) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel(menuItems); - - // Set max width of the flyout - secondaryElements - .OfType() - .ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); - - // Add menu items to the secondary flyout - secondaryElements.ForEach(itemContextMenuFlyout.SecondaryCommands.Add); - - // Show the flyout - itemContextMenuFlyout.ShowAt(element, new() { Position = e.GetPosition(element) }); - - // Load shell menu items - _ = ShellContextFlyoutFactory.LoadShellMenuItemsAsync(_flyoutItemPath, itemContextMenuFlyout, null, true, true); - } - - public override List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) - { - return new List() - { - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenWith".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconOpenWith", - }, - Tag = "OpenWithPlaceholder", - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "SendTo".GetLocalizedResource(), - Tag = "SendToPlaceholder", - ShowItem = userSettingsService.GeneralSettingsService.ShowSendToMenu - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "RecentItemRemove/Text".GetLocalizedResource(), - Glyph = "\uE738", - Command = RemoveRecentItemCommand, - CommandParameter = item - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "RecentItemClearAll/Text".GetLocalizedResource(), - Glyph = "\uE74D", - Command = ClearAllItemsCommand - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "OpenFileLocation".GetLocalizedResource(), - Glyph = "\uED25", - Command = OpenFileLocationCommand, - CommandParameter = item - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "Properties".GetLocalizedResource(), - OpacityIcon = new OpacityIconModel() - { - OpacityIconStyle = "ColorIconProperties", - }, - Command = OpenPropertiesCommand, - CommandParameter = item - }, - new ContextMenuFlyoutItemViewModel() - { - ItemType = ContextMenuFlyoutItemType.Separator, - Tag = "OverflowSeparator", - }, - new ContextMenuFlyoutItemViewModel() - { - Text = "Loading".GetLocalizedResource(), - Glyph = "\xE712", - Items = new List(), - ID = "ItemOverflow", - Tag = "ItemOverflow", - IsEnabled = false, - } - }.Where(x => x.ShowItem).ToList(); - } - - public async Task RefreshWidgetAsync() - { - IsRecentFilesDisabledInWindows = App.RecentItemsManager.CheckIsRecentFilesEnabled() is false; - await App.RecentItemsManager.UpdateRecentFilesAsync(); - } - - private async void Manager_RecentFilesChanged(object sender, NotifyCollectionChangedEventArgs e) - { - await DispatcherQueue.EnqueueOrInvokeAsync(async () => - { - // e.Action can only be Reset right now; naively refresh everything for simplicity - await UpdateRecentsListAsync(e); - }); - } - - private void OpenFileLocation(RecentItem item) - { - RecentFilesOpenLocationInvoked?.Invoke(this, new PathNavigationEventArgs() - { - ItemPath = Directory.GetParent(item.RecentPath).FullName, // parent directory - ItemName = Path.GetFileName(item.RecentPath), // file name w extension - }); - } - - private void OpenProperties(RecentItem item) - { - var flyout = HomePageContext.ItemContextFlyoutMenu; - - if (item is null || flyout is null) - return; - - EventHandler flyoutClosed = null!; - flyoutClosed = async (s, e) => - { - flyout!.Closed -= flyoutClosed; - - BaseStorageFile file = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFileFromPathAsync(item.Path)); - if (file is null) - { - ContentDialog dialog = new() - { - Title = "CannotAccessPropertiesTitle".GetLocalizedResource(), - Content = "CannotAccessPropertiesContent".GetLocalizedResource(), - PrimaryButtonText = "Ok".GetLocalizedResource() - }; - - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - dialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - await dialog.TryShowAsync(); - } - else - { - var listedItem = await UniversalStorageEnumerator.AddFileAsync(file, null, default); - FilePropertiesHelpers.OpenPropertiesWindow(listedItem, associatedInstance); - } - }; - flyout!.Closed += flyoutClosed; } - private async Task UpdateRecentsListAsync(NotifyCollectionChangedEventArgs e) + private void RecentFilesView_ItemClick(object sender, ItemClickEventArgs e) { - try - { - await refreshRecentsSemaphore.WaitAsync(refreshRecentsCTS.Token); - } - catch (OperationCanceledException) - { + if (e.ClickedItem is not RecentItem item) return; - } - - try - { - // drop other waiting instances - refreshRecentsCTS.Cancel(); - refreshRecentsCTS = new CancellationTokenSource(); - - IsEmptyRecentsTextVisible = false; - - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - if (e.NewItems is not null) - { - var addedItem = e.NewItems.Cast().Single(); - AddItemToRecentList(addedItem, 0); - } - break; - - case NotifyCollectionChangedAction.Move: - if (e.OldItems is not null) - { - var movedItem = e.OldItems.Cast().Single(); - recentItemsCollection.RemoveAt(e.OldStartingIndex); - AddItemToRecentList(movedItem, 0); - } - break; - - case NotifyCollectionChangedAction.Remove: - if (e.OldItems is not null) - { - var removedItem = e.OldItems.Cast().Single(); - recentItemsCollection.RemoveAt(e.OldStartingIndex); - } - break; - - // case NotifyCollectionChangedAction.Reset: - default: - var recentFiles = App.RecentItemsManager.RecentFiles; // already sorted, add all in order - if (!recentFiles.SequenceEqual(recentItemsCollection)) - { - recentItemsCollection.Clear(); - foreach (var item in recentFiles) - { - AddItemToRecentList(item); - } - } - break; - } - - // update chevron if there aren't any items - if (recentItemsCollection.Count == 0 && !IsRecentFilesDisabledInWindows) - { - IsEmptyRecentsTextVisible = true; - } - } - catch (Exception ex) - { - App.Logger.LogInformation(ex, "Could not populate recent files"); - } - finally - { - refreshRecentsSemaphore.Release(); - } - } - - /// - /// Add the RecentItem to the ObservableCollection for the UI to render. - /// - /// The recent item to be added - private bool AddItemToRecentList(RecentItem recentItem, int index = -1) - { - if (!recentItemsCollection.Any(x => x.Equals(recentItem))) - { - recentItemsCollection.Insert(index < 0 ? recentItemsCollection.Count : Math.Min(index, recentItemsCollection.Count), recentItem); - _ = recentItem.LoadRecentItemIconAsync() - .ContinueWith(t => App.Logger.LogWarning(t.Exception, null), TaskContinuationOptions.OnlyOnFaulted); - return true; - } - return false; - } - - private void RecentsView_ItemClick(object sender, ItemClickEventArgs e) - { - var recentItem = e.ClickedItem as RecentItem; - RecentFileInvoked?.Invoke(this, new PathNavigationEventArgs() - { - ItemPath = recentItem.RecentPath, - }); - } - private async Task RemoveRecentItemAsync(RecentItem item) - { - await refreshRecentsSemaphore.WaitAsync(); - - try - { - await App.RecentItemsManager.UnpinFromRecentFiles(item); - } - finally - { - refreshRecentsSemaphore.Release(); - } + ViewModel.NavigateToPath(item.RecentPath); } - private async Task ClearRecentItemsAsync() - { - await refreshRecentsSemaphore.WaitAsync(); - try - { - recentItemsCollection.Clear(); - bool success = App.RecentItemsManager.ClearRecentItems(); - - if (success) - { - IsEmptyRecentsTextVisible = true; - } - } - finally - { - refreshRecentsSemaphore.Release(); - } - } - - private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - public void Dispose() + private void ListView_RightTapped(object sender, RightTappedRoutedEventArgs e) { - App.RecentItemsManager.RecentFilesChanged -= Manager_RecentFilesChanged; + ViewModel.BuildItemContextMenu(e.OriginalSource, e); } } } diff --git a/src/Files.App/Utils/Widgets/WidgetsHelpers.cs b/src/Files.App/Utils/Widgets/WidgetsHelpers.cs index 1ff02cec1011..0da8260d7687 100644 --- a/src/Files.App/Utils/Widgets/WidgetsHelpers.cs +++ b/src/Files.App/Utils/Widgets/WidgetsHelpers.cs @@ -1,55 +1,51 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.UserControls.Widgets; - namespace Files.App.Helpers { public static class WidgetsHelpers { - public static TWidget? TryGetWidget(IGeneralSettingsService generalSettingsService, HomeViewModel widgetsViewModel, out bool shouldReload, TWidget? defaultValue = default) where TWidget : IWidgetViewModel, new() + public static bool TryGetWidget(HomeViewModel widgetsViewModel) where TWidget : IWidgetViewModel, new() { bool canAddWidget = widgetsViewModel.CanAddWidget(typeof(TWidget).Name); - bool isWidgetSettingEnabled = TryGetIsWidgetSettingEnabled(generalSettingsService); + bool isWidgetSettingEnabled = TryGetIsWidgetSettingEnabled(); if (canAddWidget && isWidgetSettingEnabled) { - shouldReload = true; - return new TWidget(); + return true; } - else if (!canAddWidget && !isWidgetSettingEnabled) // The widgets exists but the setting has been disabled for it + // The widgets exists but the setting has been disabled for it + else if (!canAddWidget && !isWidgetSettingEnabled) { // Remove the widget widgetsViewModel.RemoveWidget(); - shouldReload = false; - return default; + return false; } else if (!isWidgetSettingEnabled) { - shouldReload = false; - return default; + return false; } - shouldReload = EqualityComparer.Default.Equals(defaultValue, default); - - return (defaultValue); + return true; } - public static bool TryGetIsWidgetSettingEnabled(IGeneralSettingsService generalSettingsService) where TWidget : IWidgetViewModel + public static bool TryGetIsWidgetSettingEnabled() where TWidget : IWidgetViewModel { - if (typeof(TWidget) == typeof(QuickAccessWidget)) + IGeneralSettingsService generalSettingsService = Ioc.Default.GetRequiredService(); + + if (typeof(TWidget) == typeof(QuickAccessWidgetViewModel)) { return generalSettingsService.ShowQuickAccessWidget; } - if (typeof(TWidget) == typeof(DrivesWidget)) + if (typeof(TWidget) == typeof(DrivesWidgetViewModel)) { return generalSettingsService.ShowDrivesWidget; } - if (typeof(TWidget) == typeof(FileTagsWidget)) + if (typeof(TWidget) == typeof(FileTagsWidgetViewModel)) { return generalSettingsService.ShowFileTagsWidget; } - if (typeof(TWidget) == typeof(RecentFilesWidget)) + if (typeof(TWidget) == typeof(RecentFilesWidgetViewModel)) { return generalSettingsService.ShowRecentFilesWidget; } diff --git a/src/Files.App/ViewModels/HomeViewModel.cs b/src/Files.App/ViewModels/HomeViewModel.cs index e4c3afb05c59..50b38c59604c 100644 --- a/src/Files.App/ViewModels/HomeViewModel.cs +++ b/src/Files.App/ViewModels/HomeViewModel.cs @@ -8,21 +8,81 @@ namespace Files.App.ViewModels { public class HomeViewModel : ObservableObject, IDisposable { - public ObservableCollection WidgetItems { get; } = new(); + // Dependency injections + + private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); + + // Properties + + public ObservableCollection WidgetItems { get; } = []; + + // Commands public ICommand HomePageLoadedCommand { get; } - public event EventHandler? HomePageLoadedInvoked; - public event EventHandler? WidgetListRefreshRequestedInvoked; + // Constructor public HomeViewModel() { HomePageLoadedCommand = new RelayCommand(ExecuteHomePageLoadedCommand); } - private void ExecuteHomePageLoadedCommand(RoutedEventArgs? e) + // Methods + + public void ReloadWidgets() { - HomePageLoadedInvoked?.Invoke(this, e!); + var reloadQuickAccessWidget = WidgetsHelpers.TryGetWidget(this); + var reloadDrivesWidget = WidgetsHelpers.TryGetWidget(this); + var reloadFileTagsWidget = WidgetsHelpers.TryGetWidget(this); + var reloadRecentFilesWidget = WidgetsHelpers.TryGetWidget(this); + + if (reloadQuickAccessWidget) + { + var quickAccessWidget = new QuickAccessWidget(); + + AddWidget( + new( + quickAccessWidget, + quickAccessWidget.ViewModel, + (value) => UserSettingsService.GeneralSettingsService.FoldersWidgetExpanded = value, + () => UserSettingsService.GeneralSettingsService.FoldersWidgetExpanded)); + } + + if (reloadDrivesWidget) + { + var drivesWidget = new DrivesWidget(); + + AddWidget( + new( + drivesWidget, + drivesWidget.ViewModel, + (value) => UserSettingsService.GeneralSettingsService.DrivesWidgetExpanded = value, + () => UserSettingsService.GeneralSettingsService.DrivesWidgetExpanded)); + } + + if (reloadFileTagsWidget) + { + var fileTagsWidget = new FileTagsWidget(); + + AddWidget( + new( + fileTagsWidget, + fileTagsWidget.ViewModel, + (value) => UserSettingsService.GeneralSettingsService.FileTagsWidgetExpanded = value, + () => UserSettingsService.GeneralSettingsService.FileTagsWidgetExpanded)); + } + + if (reloadRecentFilesWidget) + { + var recentFilesWidget = new RecentFilesWidget(); + + AddWidget( + new( + recentFilesWidget, + recentFilesWidget.ViewModel, + (value) => UserSettingsService.GeneralSettingsService.RecentFilesWidgetExpanded = value, + () => UserSettingsService.GeneralSettingsService.RecentFilesWidgetExpanded)); + } } public void RefreshWidgetList() @@ -30,12 +90,10 @@ public void RefreshWidgetList() for (int i = 0; i < WidgetItems.Count; i++) { if (!WidgetItems[i].WidgetItemModel.IsWidgetSettingEnabled) - { RemoveWidgetAt(i); - } } - WidgetListRefreshRequestedInvoked?.Invoke(this, EventArgs.Empty); + ReloadWidgets(); } public bool AddWidget(WidgetContainerItem widgetModel) @@ -102,12 +160,15 @@ public void RemoveWidgetAt(int index) RemoveWidgetAt(indexToRemove); } - public void ReorderWidget(WidgetContainerItem widgetModel, int place) + // Command methods + + private void ExecuteHomePageLoadedCommand(RoutedEventArgs? e) { - int widgetIndex = WidgetItems.IndexOf(widgetModel); - WidgetItems.Move(widgetIndex, place); + ReloadWidgets(); } + // Disposer + public void Dispose() { for (int i = 0; i < WidgetItems.Count; i++) diff --git a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs index 1ce7cbcf4f91..ab6485370e69 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs @@ -13,13 +13,18 @@ namespace Files.App.UserControls.Widgets /// /// Represents base ViewModel for widget ViewModels. /// - public abstract class BaseWidgetViewModel : UserControl + public abstract class BaseWidgetViewModel : ObservableObject { // Dependency injections - public IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); - public IQuickAccessService QuickAccessService { get; } = Ioc.Default.GetRequiredService(); - public IStorageService StorageService { get; } = Ioc.Default.GetRequiredService(); + protected IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); + protected IQuickAccessService QuickAccessService { get; } = Ioc.Default.GetRequiredService(); + protected IStorageService StorageService { get; } = Ioc.Default.GetRequiredService(); + protected IHomePageContext HomePageContext { get; } = Ioc.Default.GetRequiredService(); + protected IContentPageContext ContentPageContext { get; } = Ioc.Default.GetRequiredService(); + protected IFileTagsService FileTagsService { get; } = Ioc.Default.GetRequiredService(); + protected DrivesViewModel DrivesViewModel = Ioc.Default.GetRequiredService(); + protected NetworkDrivesViewModel NetworkDrivesViewModel = Ioc.Default.GetRequiredService(); // Fields @@ -27,29 +32,27 @@ public abstract class BaseWidgetViewModel : UserControl // Commands - protected ICommand? RemoveRecentItemCommand { get; set; } - protected ICommand? ClearAllItemsCommand { get; set; } - protected ICommand? OpenFileLocationCommand { get; set; } - protected ICommand? OpenInNewTabCommand { get; set; } - protected ICommand? OpenInNewWindowCommand { get; set; } - protected ICommand? OpenPropertiesCommand { get; set; } - protected ICommand? PinToSidebarCommand { get; set; } - protected ICommand? UnpinFromSidebarCommand { get; set; } + protected ICommand RemoveRecentItemCommand { get; set; } = null!; + protected ICommand ClearAllItemsCommand { get; set; } = null!; + protected ICommand OpenFileLocationCommand { get; set; } = null!; + protected ICommand OpenInNewTabCommand { get; set; } = null!; + protected ICommand OpenInNewWindowCommand { get; set; } = null!; + protected ICommand OpenPropertiesCommand { get; set; } = null!; + protected ICommand PinToSidebarCommand { get; set; } = null!; + protected ICommand UnpinFromSidebarCommand { get; set; } = null!; // Events public static event EventHandler? RightClickedItemChanged; - // Abstract methods + // Methods public abstract List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false); - // Event methods - - public void Button_RightTapped(object sender, RightTappedRoutedEventArgs e) + public void BuildItemContextMenu(object sender, RightTappedRoutedEventArgs e) { // Ensure values are not null - if (sender is not Button widgetCardItem || + if (sender is not FrameworkElement widgetCardItem || widgetCardItem.DataContext is not WidgetCardItem item) return; @@ -91,24 +94,24 @@ public void Button_RightTapped(object sender, RightTappedRoutedEventArgs e) // Command methods - public async Task OpenInNewTabAsync(WidgetCardItem item) + public async Task ExecuteOpenInNewTabCommand(WidgetCardItem? item) { - await NavigationHelpers.OpenPathInNewTab(item.Path, false); + await NavigationHelpers.OpenPathInNewTab(item?.Path ?? string.Empty, false); } - public async Task OpenInNewWindowAsync(WidgetCardItem item) + public async Task ExecuteOpenInNewWindowCommand(WidgetCardItem? item) { - await NavigationHelpers.OpenPathInNewWindowAsync(item.Path); + await NavigationHelpers.OpenPathInNewWindowAsync(item?.Path ?? string.Empty); } - public virtual async Task PinToSidebarAsync(WidgetCardItem item) + public virtual async Task ExecutePinToSidebarCommand(WidgetCardItem? item) { - await QuickAccessService.PinToSidebarAsync(item.Path); + await QuickAccessService.PinToSidebarAsync(item?.Path ?? string.Empty); } - public virtual async Task UnpinFromSidebarAsync(WidgetCardItem item) + public virtual async Task ExecuteUnpinFromSidebarCommand(WidgetCardItem? item) { - await QuickAccessService.UnpinFromSidebarAsync(item.Path); + await QuickAccessService.UnpinFromSidebarAsync(item?.Path ?? string.Empty); } protected void OnRightClickedItemChanged(WidgetCardItem? item, CommandBarFlyout? flyout) diff --git a/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs index 2947e1e74a01..df9ff9f76a51 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs @@ -1,13 +1,284 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. +<<<<<<< HEAD +======= +using Microsoft.UI.Input; +using Microsoft.UI.Xaml.Controls; +using System.Collections.Specialized; +using System.Windows.Input; +using Windows.System; +using Windows.UI.Core; + +>>>>>>> 70e2ff662 (Initial commit) namespace Files.App.ViewModels.UserControls.Widgets { /// /// Represents view model of . /// +<<<<<<< HEAD public class DrivesWidgetViewModel { public ObservableCollection Items { get; } = []; +======= + public class DrivesWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel + { + // Properties + + public ObservableCollection Items { get; } = []; + + public string WidgetName => nameof(DrivesWidget); + public string AutomationProperties => "Drives".GetLocalizedResource(); + public string WidgetHeader => "Drives".GetLocalizedResource(); + public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowDrivesWidget; + public bool ShowMenuFlyout => true; + public MenuFlyoutItem? MenuFlyoutItem => new() + { + Icon = new FontIcon() { Glyph = "\uE710" }, + Text = "DrivesWidgetOptionsFlyoutMapNetDriveMenuItem/Text".GetLocalizedResource(), + Command = MapNetworkDriveCommand + }; + + // Commands + + private ICommand FormatDriveCommand { get; } = null!; + private ICommand EjectDeviceCommand { get; } = null!; + private ICommand OpenInNewPaneCommand { get; } = null!; + private ICommand MapNetworkDriveCommand { get; } = null!; + private ICommand DisconnectNetworkDriveCommand { get; } = null!; + + // Constructor + + public DrivesWidgetViewModel() + { + Drives_CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + + DrivesViewModel.Drives.CollectionChanged += Drives_CollectionChanged; + + OpenInNewTabCommand = new AsyncRelayCommand(ExecuteOpenInNewTabCommand); + OpenInNewWindowCommand = new AsyncRelayCommand(ExecuteOpenInNewWindowCommand); + PinToSidebarCommand = new AsyncRelayCommand(ExecutePinToSidebarCommand); + UnpinFromSidebarCommand = new AsyncRelayCommand(ExecuteUnpinFromSidebarCommand); + FormatDriveCommand = new RelayCommand(ExecuteFormatDriveCommand); + EjectDeviceCommand = new AsyncRelayCommand(ExecuteEjectDeviceCommand); + OpenInNewPaneCommand = new AsyncRelayCommand(ExecuteOpenInNewPaneCommand); + OpenPropertiesCommand = new RelayCommand(ExecuteOpenPropertiesCommand); + DisconnectNetworkDriveCommand = new RelayCommand(ExecuteDisconnectNetworkDriveCommand); + MapNetworkDriveCommand = new AsyncRelayCommand(ExecuteMapNetworkDriveCommand); + } + + // Methods + + public async Task RefreshWidgetAsync() + { + var updateTasks = Items.Select(item => item.Item.UpdatePropertiesAsync()); + await Task.WhenAll(updateTasks); + } + + public async Task NavigateToPath(string path) + { + if (await DriveHelpers.CheckEmptyDrive(path)) + return; + + var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); + if (ctrlPressed) + { + await NavigationHelpers.OpenPathInNewTab(path, false); + return; + } + + ContentPageContext.ShellPage!.NavigateWithArguments( + ContentPageContext.ShellPage!.InstanceViewModel.FolderSettings.GetLayoutType(path), + new() { NavPathParam = path }); + } + + public override List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) + { + var drive = + Items.Where(x => + string.Equals( + PathNormalization.NormalizePath(x.Path!), + PathNormalization.NormalizePath(item.Path!), + StringComparison.OrdinalIgnoreCase)) + .FirstOrDefault(); + + var options = drive?.Item.MenuOptions; + + return new List() + { + new() + { + Text = "OpenInNewTab".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() { OpacityIconStyle = "ColorIconOpenInNewTab" }, + Command = OpenInNewTabCommand, + CommandParameter = item, + ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenInNewTab + }, + new() + { + Text = "OpenInNewWindow".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() { OpacityIconStyle = "ColorIconOpenInNewWindow" }, + Command = OpenInNewWindowCommand, + CommandParameter = item, + ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenInNewWindow + }, + new() + { + Text = "OpenInNewPane".GetLocalizedResource(), + Command = OpenInNewPaneCommand, + CommandParameter = item, + ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenInNewPane + }, + new() + { + Text = "PinFolderToSidebar".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() { OpacityIconStyle = "Icons.Pin.16x16" }, + Command = PinToSidebarCommand, + CommandParameter = item, + ShowItem = !isPinned + }, + new() + { + Text = "UnpinFolderFromSidebar".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() { OpacityIconStyle = "Icons.Unpin.16x16" }, + Command = UnpinFromSidebarCommand, + CommandParameter = item, + ShowItem = isPinned + }, + new() + { + Text = "Eject".GetLocalizedResource(), + Command = EjectDeviceCommand, + CommandParameter = item, + ShowItem = options?.ShowEjectDevice ?? false + }, + new() + { + Text = "FormatDriveText".GetLocalizedResource(), + Command = FormatDriveCommand, + CommandParameter = item, + ShowItem = options?.ShowFormatDrive ?? false + }, + new() + { + Text = "Properties".GetLocalizedResource(), + OpacityIcon = new OpacityIconModel() { OpacityIconStyle = "ColorIconProperties" }, + Command = OpenPropertiesCommand, + CommandParameter = item + }, + new() + { + Text = "TurnOnBitLocker".GetLocalizedResource(), + Tag = "TurnOnBitLockerPlaceholder", + IsEnabled = false + }, + new() + { + Text = "ManageBitLocker".GetLocalizedResource(), + Tag = "ManageBitLockerPlaceholder", + IsEnabled = false + }, + new() + { + ItemType = ContextMenuFlyoutItemType.Separator, + Tag = "OverflowSeparator", + }, + new() + { + Text = "Loading".GetLocalizedResource(), + Glyph = "\xE712", + Items = [], + ID = "ItemOverflow", + Tag = "ItemOverflow", + IsEnabled = false, + } + }.Where(x => x.ShowItem).ToList(); + } + + // Command methods + + private async Task ExecuteEjectDeviceCommand(WidgetDriveCardItem? item) + { + if (item is null) + return; + + var result = await DriveHelpers.EjectDeviceAsync(item.Item.Path); + await UIHelpers.ShowDeviceEjectResultAsync(item.Item.Type, result); + } + + private async Task ExecuteOpenInNewPaneCommand(WidgetDriveCardItem? item) + { + if (item is null || await DriveHelpers.CheckEmptyDrive(item.Item.Path)) + return; + + ContentPageContext.ShellPage!.PaneHolder?.OpenPathInNewPane(item.Item.Path); + } + + private Task ExecuteMapNetworkDriveCommand() + { + return NetworkDrivesViewModel.OpenMapNetworkDriveDialogAsync(); + } + + private void ExecuteFormatDriveCommand(WidgetDriveCardItem? item) + { + Win32API.OpenFormatDriveDialog(item?.Path ?? string.Empty); + } + + private void ExecuteOpenPropertiesCommand(WidgetDriveCardItem? item) + { + if (!HomePageContext.IsAnyItemRightClicked || item is null) + return; + + var flyout = HomePageContext.ItemContextFlyoutMenu; + + EventHandler flyoutClosed = null!; + flyoutClosed = (s, e) => + { + flyout!.Closed -= flyoutClosed; + FilePropertiesHelpers.OpenPropertiesWindow(item.Item, ContentPageContext.ShellPage!); + }; + + flyout!.Closed += flyoutClosed; + } + + private void ExecuteDisconnectNetworkDriveCommand(WidgetDriveCardItem? item) + { + if (item is null) + return; + + NetworkDrivesViewModel.DisconnectNetworkDrive(item.Item); + } + + // Event methods + + private async void Drives_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => + { + foreach (DriveItem drive in DrivesViewModel.Drives.ToList().Cast()) + { + if (!Items.Any(x => x.Item == drive) && drive.Type != DriveType.VirtualDrive) + { + var cardItem = new WidgetDriveCardItem(drive); + Items.AddSorted(cardItem); + + await cardItem.LoadCardThumbnailAsync(); + } + } + + foreach (WidgetDriveCardItem driveCard in Items.ToList()) + { + if (!DrivesViewModel.Drives.Contains(driveCard.Item)) + Items.Remove(driveCard); + } + }); + } + + // Disposer + + public void Dispose() + { + } +>>>>>>> 70e2ff662 (Initial commit) } } diff --git a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs index 54115d890003..7adf32490eca 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs @@ -1,50 +1,232 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Shared.Utils; +using Microsoft.UI.Xaml.Controls; +using System.IO; +using System.Windows.Input; +using Windows.Storage; namespace Files.App.ViewModels.UserControls.Widgets { /// /// Represents view model of . /// +<<<<<<< HEAD public sealed partial class FileTagsWidgetViewModel : ObservableObject, IAsyncInitialize +======= + public sealed partial class FileTagsWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel +>>>>>>> 70e2ff662 (Initial commit) { - // Dependency injections + // Properties - private IFileTagsService FileTagsService { get; } = Ioc.Default.GetRequiredService(); + public ObservableCollection Containers { get; } = []; +<<<<<<< HEAD +======= - // Fields + public string WidgetName => nameof(FileTagsWidget); + public string WidgetHeader => "FileTags".GetLocalizedResource(); + public string AutomationProperties => "FileTags".GetLocalizedResource(); + public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowFileTagsWidget; + public bool ShowMenuFlyout => false; + public MenuFlyoutItem? MenuFlyoutItem => null; - private readonly Func _openAction; + // Events - // Properties + public static event EventHandler>? SelectedTaggedItemsChanged; - public ObservableCollection Containers { get; } = []; + // Commands + + private ICommand OpenInNewPaneCommand { get; set; } = null!; +>>>>>>> 70e2ff662 (Initial commit) // Constructor - public FileTagsWidgetViewModel(Func openAction) + public FileTagsWidgetViewModel() { +<<<<<<< HEAD +<<<<<<< HEAD _openAction = openAction; +======= +======= +>>>>>>> 9de88d1c4 (Fix) + _ = InitializeWidget(); + + OpenInNewTabCommand = new AsyncRelayCommand(ExecuteOpenInNewTabCommand); + OpenInNewWindowCommand = new AsyncRelayCommand(ExecuteOpenInNewWindowCommand); + PinToSidebarCommand = new AsyncRelayCommand(ExecutePinToSidebarCommand); + UnpinFromSidebarCommand = new AsyncRelayCommand(ExecuteUnpinFromSidebarCommand); + OpenFileLocationCommand = new RelayCommand(ExecuteOpenFileLocationCommand); + OpenInNewPaneCommand = new RelayCommand(ExecuteOpenInNewPaneCommand); + OpenPropertiesCommand = new RelayCommand(ExecuteOpenPropertiesCommand); +<<<<<<< HEAD +>>>>>>> 70e2ff662 (Initial commit) +======= +>>>>>>> 9de88d1c4 (Fix) } // Methods - /// - public async Task InitAsync(CancellationToken cancellationToken = default) + public async Task InitializeWidget() { - await foreach (var item in FileTagsService.GetTagsAsync(cancellationToken)) + await foreach (var item in FileTagsService.GetTagsAsync()) { - var container = new WidgetFileTagsContainerItem(item.Uid, _openAction) + var container = new WidgetFileTagsContainerItem(item.Uid) { Name = item.Name, Color = item.Color }; - Containers.Add(container); - _ = container.InitAsync(cancellationToken); + Containers.Add(container); + _ = container.InitAsync(); } } + + public Task RefreshWidgetAsync() + { + return Task.CompletedTask; + } + + public override List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) + { + return new List() + { + new() + { + Text = "OpenWith".GetLocalizedResource(), + OpacityIcon = new() { OpacityIconStyle = "ColorIconOpenWith" }, + Tag = "OpenWithPlaceholder", + ShowItem = !isFolder + }, + new() + { + Text = "SendTo".GetLocalizedResource(), + Tag = "SendToPlaceholder", + ShowItem = !isFolder && UserSettingsService.GeneralSettingsService.ShowSendToMenu + }, + new() + { + Text = "OpenInNewTab".GetLocalizedResource(), + OpacityIcon = new() { OpacityIconStyle = "ColorIconOpenInNewTab" }, + Command = OpenInNewTabCommand, + CommandParameter = item, + ShowItem = isFolder + }, + new() + { + Text = "OpenInNewWindow".GetLocalizedResource(), + OpacityIcon = new() { OpacityIconStyle = "ColorIconOpenInNewWindow" }, + Command = OpenInNewWindowCommand, + CommandParameter = item, + ShowItem = isFolder + }, + new() + { + Text = "OpenFileLocation".GetLocalizedResource(), + Glyph = "\uED25", + Command = OpenFileLocationCommand, + CommandParameter = item, + ShowItem = !isFolder + }, + new() + { + Text = "OpenInNewPane".GetLocalizedResource(), + Command = OpenInNewPaneCommand, + CommandParameter = item, + ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenInNewPane && isFolder + }, + new() + { + Text = "PinFolderToSidebar".GetLocalizedResource(), + OpacityIcon = new() { OpacityIconStyle = "Icons.Pin.16x16" }, + Command = PinToSidebarCommand, + CommandParameter = item, + ShowItem = !isPinned && isFolder + }, + new() + { + Text = "UnpinFolderFromSidebar".GetLocalizedResource(), + OpacityIcon = new() { OpacityIconStyle = "Icons.Unpin.16x16" }, + Command = UnpinFromSidebarCommand, + CommandParameter = item, + ShowItem = isPinned && isFolder + }, + new() + { + Text = "Properties".GetLocalizedResource(), + OpacityIcon = new() { OpacityIconStyle = "ColorIconProperties" }, + Command = OpenPropertiesCommand, + CommandParameter = item, + ShowItem = isFolder + }, + new() + { + ItemType = ContextMenuFlyoutItemType.Separator, + Tag = "OverflowSeparator", + }, + new() + { + Text = "Loading".GetLocalizedResource(), + Glyph = "\xE712", + Items = [], + ID = "ItemOverflow", + Tag = "ItemOverflow", + IsEnabled = false, + } + }.Where(x => x.ShowItem).ToList(); + } + + // Command methods + + private void ExecuteOpenPropertiesCommand(WidgetCardItem? item) + { + if (!HomePageContext.IsAnyItemRightClicked || item is null) + return; + + var flyout = HomePageContext.ItemContextFlyoutMenu; + + EventHandler flyoutClosed = null!; + flyoutClosed = (s, e) => + { + flyout!.Closed -= flyoutClosed; + + ListedItem listedItem = new(null!) + { + ItemPath = (item.Item as WidgetFileTagCardItem)?.Path ?? string.Empty, + ItemNameRaw = (item.Item as WidgetFileTagCardItem)?.Name ?? string.Empty, + PrimaryItemAttribute = StorageItemTypes.Folder, + ItemType = "Folder".GetLocalizedResource(), + }; + + FilePropertiesHelpers.OpenPropertiesWindow(listedItem, ContentPageContext.ShellPage!); + }; + + flyout!.Closed += flyoutClosed; + } + + private void ExecuteOpenInNewPaneCommand(WidgetCardItem? item) + { + ContentPageContext.ShellPage!.PaneHolder?.OpenPathInNewPane(item?.Path ?? string.Empty); + } + + public void ExecuteOpenFileLocationCommand(WidgetCardItem? item) + { + var itemPath = Directory.GetParent(item?.Path ?? string.Empty)?.FullName ?? string.Empty; + var itemName = Path.GetFileName(item?.Path ?? string.Empty); + + ContentPageContext.ShellPage!.NavigateWithArguments( + ContentPageContext.ShellPage!.InstanceViewModel.FolderSettings.GetLayoutType(itemPath), + new NavigationArguments() + { + NavPathParam = itemPath, + SelectItems = new[] { itemName }, + AssociatedTabInstance = ContentPageContext.ShellPage! + }); + } + + // Disposer + + public void Dispose() + { + } } } diff --git a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs index 6c173d7889bf..26ff9c21750c 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs @@ -1,13 +1,329 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. +<<<<<<< HEAD +======= +using Microsoft.UI.Input; +using Microsoft.UI.Xaml.Controls; +using System.Collections.Specialized; +using System.IO; +using System.Windows.Input; +using Windows.Storage; +using Windows.System; +using Windows.UI.Core; + +>>>>>>> 70e2ff662 (Initial commit) namespace Files.App.ViewModels.UserControls.Widgets { /// /// Represents view model of . /// +<<<<<<< HEAD public class QuickAccessWidgetViewModel { public ObservableCollection Items { get; } = []; +======= + public class QuickAccessWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel + { + // Properties + + public ObservableCollection Items { get; } = []; + + public string WidgetName => nameof(QuickAccessWidget); + public string AutomationProperties => "QuickAccess".GetLocalizedResource(); + public string WidgetHeader => "QuickAccess".GetLocalizedResource(); + public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowQuickAccessWidget; + public bool ShowMenuFlyout => false; + public MenuFlyoutItem? MenuFlyoutItem => null; + + // Commands + + public ICommand OpenInNewPaneCommand { get; set; } = null!; + + // Constructor + + public QuickAccessWidgetViewModel() + { + _ = InitializeWidget(); + + Items.CollectionChanged += Items_CollectionChanged; + + OpenInNewTabCommand = new AsyncRelayCommand(ExecuteOpenInNewTabCommand); + OpenInNewWindowCommand = new AsyncRelayCommand(ExecuteOpenInNewWindowCommand); + OpenInNewPaneCommand = new RelayCommand(ExecuteOpenInNewPaneCommand); + OpenPropertiesCommand = new RelayCommand(ExecuteOpenPropertiesCommand); + PinToSidebarCommand = new AsyncRelayCommand(ExecutePinToSidebarCommand); + UnpinFromSidebarCommand = new AsyncRelayCommand(ExecuteUnpinFromSidebarCommand); + } + + // Methods + + public override List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) + { + return new List() + { + new() + { + Text = "OpenInNewTab".GetLocalizedResource(), + OpacityIcon = new() { OpacityIconStyle = "ColorIconOpenInNewTab" }, + Command = OpenInNewTabCommand, + CommandParameter = item, + ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenInNewTab + }, + new() + { + Text = "OpenInNewWindow".GetLocalizedResource(), + OpacityIcon = new() { OpacityIconStyle = "ColorIconOpenInNewWindow" }, + Command = OpenInNewWindowCommand, + CommandParameter = item, + ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenInNewWindow + }, + new() + { + Text = "OpenInNewPane".GetLocalizedResource(), + Command = OpenInNewPaneCommand, + CommandParameter = item, + ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenInNewPane + }, + new() + { + Text = "PinFolderToSidebar".GetLocalizedResource(), + OpacityIcon = new() { OpacityIconStyle = "Icons.Pin.16x16" }, + Command = PinToSidebarCommand, + CommandParameter = item, + ShowItem = !isPinned + }, + new() + { + Text = "UnpinFolderFromSidebar".GetLocalizedResource(), + OpacityIcon = new() { OpacityIconStyle = "Icons.Unpin.16x16" }, + Command = UnpinFromSidebarCommand, + CommandParameter = item, + ShowItem = isPinned + }, + new() + { + Text = "Properties".GetLocalizedResource(), + OpacityIcon = new() { OpacityIconStyle = "ColorIconProperties" }, + Command = OpenPropertiesCommand, + CommandParameter = item + }, + new() + { + ItemType = ContextMenuFlyoutItemType.Separator, + Tag = "OverflowSeparator", + }, + new() + { + Text = "Loading".GetLocalizedResource(), + Glyph = "\xE712", + Items = [], + ID = "ItemOverflow", + Tag = "ItemOverflow", + IsEnabled = false, + } + }.Where(x => x.ShowItem).ToList(); + } + + private async void ModifyItemAsync(object? sender, ModifyQuickAccessEventArgs? e) + { + if (e is null) + return; + + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => + { + if (e.Reset) + { + // Find the intersection between the two lists and determine whether to remove or add + var originalItems = Items.ToList(); + var itemsToRemove = originalItems.Where(x => !e.Paths.Contains(x.Path)); + var itemsToAdd = e.Paths.Where(x => !originalItems.Any(y => y.Path == x)); + + // Remove items + foreach (var itemToRemove in itemsToRemove) + Items.Remove(itemToRemove); + + // Add items + foreach (var itemToAdd in itemsToAdd) + { + var interimItems = Items.ToList(); + var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd); + var lastIndex = Items.IndexOf(interimItems.FirstOrDefault(x => !x.IsPinned)); + var isPinned = (bool?)e.Items.Where(x => x.FilePath == itemToAdd).FirstOrDefault()?.Properties["System.Home.IsPinned"] ?? false; + if (interimItems.Any(x => x.Path == itemToAdd)) + continue; + + Items.Insert(isPinned && lastIndex >= 0 ? Math.Min(lastIndex, Items.Count) : Items.Count, new WidgetFolderCardItem(item, Path.GetFileName(item.Text), isPinned) + { + Path = item.Path, + }); + } + + return; + } + if (e.Reorder) + { + // Remove pinned items + foreach (var itemToRemove in Items.ToList().Where(x => x.IsPinned)) + Items.Remove(itemToRemove); + + // Add pinned items in the new order + foreach (var itemToAdd in e.Paths) + { + var interimItems = Items.ToList(); + var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd); + var lastIndex = Items.IndexOf(interimItems.FirstOrDefault(x => !x.IsPinned)); + if (interimItems.Any(x => x.Path == itemToAdd)) + continue; + + Items.Insert(lastIndex >= 0 ? Math.Min(lastIndex, Items.Count) : Items.Count, new WidgetFolderCardItem(item, Path.GetFileName(item.Text), true) + { + Path = item.Path, + }); + } + + return; + } + if (e.Add) + { + foreach (var itemToAdd in e.Paths) + { + var interimItems = Items.ToList(); + var item = await App.QuickAccessManager.Model.CreateLocationItemFromPathAsync(itemToAdd); + var lastIndex = Items.IndexOf(interimItems.FirstOrDefault(x => !x.IsPinned)); + if (interimItems.Any(x => x.Path == itemToAdd)) + continue; + Items.Insert(e.Pin && lastIndex >= 0 ? Math.Min(lastIndex, Items.Count) : Items.Count, new WidgetFolderCardItem(item, Path.GetFileName(item.Text), e.Pin) // Add just after the Recent Folders + { + Path = item.Path, + }); + } + } + else + foreach (var itemToRemove in Items.ToList().Where(x => e.Paths.Contains(x.Path))) + Items.Remove(itemToRemove); + }); + } + + public Task RefreshWidgetAsync() + { + return Task.CompletedTask; + } + + public async Task NavigateToPath(string path) + { + var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); + if (ctrlPressed) + { + await NavigationHelpers.OpenPathInNewTab(path, false); + return; + } + + ContentPageContext.ShellPage!.NavigateWithArguments( + ContentPageContext.ShellPage!.InstanceViewModel.FolderSettings.GetLayoutType(path), + new() { NavPathParam = path }); + } + + // Event methods + + private async Task InitializeWidget() + { + var itemsToAdd = await QuickAccessService.GetPinnedFoldersAsync(); + ModifyItemAsync(this, new(itemsToAdd.ToArray(), false) { Reset = true }); + + App.QuickAccessManager.UpdateQuickAccessWidget += ModifyItemAsync; + } + + private async void Items_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action is NotifyCollectionChangedAction.Add) + { + foreach (WidgetFolderCardItem cardItem in e.NewItems!) + await cardItem.LoadCardThumbnailAsync(); + } + } + + // Command methods + + public override async Task ExecutePinToSidebarCommand(WidgetCardItem? item) + { + if (item is null || item.Path is null) + return; + + await QuickAccessService.PinToSidebarAsync(item.Path); + + ModifyItemAsync(this, new(new[] { item.Path }, false)); + + var items = (await QuickAccessService.GetPinnedFoldersAsync()) + .Where(link => !((bool?)link.Properties["System.Home.IsPinned"] ?? false)); + + var recentItem = items.Where(x => !Items.ToList().Select(y => y.Path).Contains(x.FilePath)).FirstOrDefault(); + if (recentItem is not null) + { + ModifyItemAsync(this, new(new[] { recentItem.FilePath }, true) { Pin = false }); + } + } + + public override async Task ExecuteUnpinFromSidebarCommand(WidgetCardItem? item) + { + if (item is null || item.Path is null) + return; + + await QuickAccessService.UnpinFromSidebarAsync(item.Path); + + ModifyItemAsync(this, new(new[] { item.Path }, false)); + } + + private void ExecuteOpenInNewPaneCommand(WidgetFolderCardItem? item) + { + if (item is null || item.Path is null) + return; + + ContentPageContext.ShellPage!.PaneHolder?.OpenPathInNewPane(item.Path); + } + + private void ExecuteOpenPropertiesCommand(WidgetFolderCardItem? item) + { + if (!HomePageContext.IsAnyItemRightClicked || item is null || item.Item is null) + return; + + var flyout = HomePageContext.ItemContextFlyoutMenu; + EventHandler flyoutClosed = null!; + + flyoutClosed = async (s, e) => + { + flyout!.Closed -= flyoutClosed; + + ListedItem listedItem = new(null!) + { + ItemPath = item.Item.Path, + ItemNameRaw = item.Item.Text, + PrimaryItemAttribute = StorageItemTypes.Folder, + ItemType = "Folder".GetLocalizedResource(), + }; + + if (!string.Equals(item.Item.Path, Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase)) + { + BaseStorageFolder matchingStorageFolder = await ContentPageContext.ShellPage!.FilesystemViewModel.GetFolderFromPathAsync(item.Item.Path); + if (matchingStorageFolder is not null) + { + var syncStatus = await ContentPageContext.ShellPage!.FilesystemViewModel.CheckCloudDriveSyncStatusAsync(matchingStorageFolder); + listedItem.SyncStatusUI = CloudDriveSyncStatusUI.FromCloudDriveSyncStatus(syncStatus); + } + } + + FilePropertiesHelpers.OpenPropertiesWindow(listedItem, ContentPageContext.ShellPage!); + }; + + flyout!.Closed += flyoutClosed; + } + + // Disposer + + public void Dispose() + { + App.QuickAccessManager.UpdateQuickAccessWidget -= ModifyItemAsync; + } +>>>>>>> 70e2ff662 (Initial commit) } } diff --git a/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs index 11257d0cba6d..4952bd002dbd 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs @@ -1,13 +1,353 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. +<<<<<<< HEAD +======= +using Microsoft.Extensions.Logging; +using Microsoft.UI.Xaml.Controls; +using System.Collections.Specialized; +using System.IO; +using Windows.Foundation.Metadata; + +>>>>>>> 70e2ff662 (Initial commit) namespace Files.App.ViewModels.UserControls.Widgets { /// /// Represents view model of . /// +<<<<<<< HEAD public class RecentFilesWidgetViewModel { public ObservableCollection Items { get; } = []; +======= + public class RecentFilesWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel + { + // Fields + + private readonly SemaphoreSlim _refreshRecentFilesSemaphore; + private CancellationTokenSource _refreshRecentFilesCTS; + + // Properties + + public ObservableCollection Items { get; } = []; + + public string WidgetName => nameof(RecentFilesWidget); + public string AutomationProperties => "RecentFiles".GetLocalizedResource(); + public string WidgetHeader => "RecentFiles".GetLocalizedResource(); + public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowRecentFilesWidget; + public bool ShowMenuFlyout => false; + public MenuFlyoutItem? MenuFlyoutItem => null; + + private bool _IsEmptyRecentFilesTextVisible; + public bool IsEmptyRecentFilesTextVisible + { + get => _IsEmptyRecentFilesTextVisible; + set => SetProperty(ref _IsEmptyRecentFilesTextVisible, value); + } + + private bool _IsRecentFilesDisabledInWindows; + public bool IsRecentFilesDisabledInWindows + { + get => _IsRecentFilesDisabledInWindows; + set => SetProperty(ref _IsRecentFilesDisabledInWindows, value); + } + + // Constructor + + public RecentFilesWidgetViewModel() + { + _refreshRecentFilesSemaphore = new SemaphoreSlim(1, 1); + _refreshRecentFilesCTS = new CancellationTokenSource(); + + // recent files could have changed while widget wasn't loaded + _ = RefreshWidgetAsync(); + + App.RecentItemsManager.RecentFilesChanged += Manager_RecentFilesChanged; + + RemoveRecentItemCommand = new AsyncRelayCommand(ExecuteRemoveRecentItemCommand); + ClearAllItemsCommand = new AsyncRelayCommand(ExecuteClearRecentItemsCommand); + OpenFileLocationCommand = new RelayCommand(ExecuteOpenFileLocationCommand); + OpenPropertiesCommand = new RelayCommand(ExecuteOpenPropertiesCommand); + } + + // Methods + + public async Task RefreshWidgetAsync() + { + IsRecentFilesDisabledInWindows = App.RecentItemsManager.CheckIsRecentFilesEnabled() is false; + await App.RecentItemsManager.UpdateRecentFilesAsync(); + } + + public override List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) + { + return new List() + { + new() + { + Text = "OpenWith".GetLocalizedResource(), + OpacityIcon = new() { OpacityIconStyle = "ColorIconOpenWith" }, + Tag = "OpenWithPlaceholder", + }, + new() + { + Text = "SendTo".GetLocalizedResource(), + Tag = "SendToPlaceholder", + ShowItem = UserSettingsService.GeneralSettingsService.ShowSendToMenu + }, + new() + { + Text = "RecentItemRemove/Text".GetLocalizedResource(), + Glyph = "\uE738", + Command = RemoveRecentItemCommand, + CommandParameter = item + }, + new() + { + Text = "RecentItemClearAll/Text".GetLocalizedResource(), + Glyph = "\uE74D", + Command = ClearAllItemsCommand + }, + new() + { + Text = "OpenFileLocation".GetLocalizedResource(), + Glyph = "\uED25", + Command = OpenFileLocationCommand, + CommandParameter = item + }, + new() + { + Text = "Properties".GetLocalizedResource(), + OpacityIcon = new() { OpacityIconStyle = "ColorIconProperties" }, + Command = OpenPropertiesCommand, + CommandParameter = item + }, + new() + { + ItemType = ContextMenuFlyoutItemType.Separator, + Tag = "OverflowSeparator", + }, + new() + { + Text = "Loading".GetLocalizedResource(), + Glyph = "\xE712", + Items = [], + ID = "ItemOverflow", + Tag = "ItemOverflow", + IsEnabled = false, + } + }.Where(x => x.ShowItem).ToList(); + } + + private async Task UpdateRecentFilesListAsync(NotifyCollectionChangedEventArgs e) + { + try + { + await _refreshRecentFilesSemaphore.WaitAsync(_refreshRecentFilesCTS.Token); + } + catch (OperationCanceledException) + { + return; + } + + try + { + // drop other waiting instances + _refreshRecentFilesCTS.Cancel(); + _refreshRecentFilesCTS = new CancellationTokenSource(); + + IsEmptyRecentFilesTextVisible = false; + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + if (e.NewItems is not null) + { + var addedItem = e.NewItems.Cast().Single(); + AddItemToRecentList(addedItem, 0); + } + break; + + case NotifyCollectionChangedAction.Move: + if (e.OldItems is not null) + { + var movedItem = e.OldItems.Cast().Single(); + Items.RemoveAt(e.OldStartingIndex); + AddItemToRecentList(movedItem, 0); + } + break; + + case NotifyCollectionChangedAction.Remove: + if (e.OldItems is not null) + { + var removedItem = e.OldItems.Cast().Single(); + Items.RemoveAt(e.OldStartingIndex); + } + break; + + // case NotifyCollectionChangedAction.Reset: + default: + var recentFiles = App.RecentItemsManager.RecentFiles; // already sorted, add all in order + if (!recentFiles.SequenceEqual(Items)) + { + Items.Clear(); + foreach (var item in recentFiles) + { + AddItemToRecentList(item); + } + } + break; + } + + // update chevron if there aren't any items + if (Items.Count == 0 && !IsRecentFilesDisabledInWindows) + { + IsEmptyRecentFilesTextVisible = true; + } + } + catch (Exception ex) + { + App.Logger.LogInformation(ex, "Could not populate recent files"); + } + finally + { + _refreshRecentFilesSemaphore.Release(); + } + } + + private bool AddItemToRecentList(RecentItem? recentItem, int index = -1) + { + if (recentItem is null) + return false; + + if (!Items.Any(x => x.Equals(recentItem))) + { + Items.Insert(index < 0 ? Items.Count : Math.Min(index, Items.Count), recentItem); + _ = recentItem.LoadRecentItemIconAsync() + .ContinueWith(t => App.Logger.LogWarning(t.Exception, null), TaskContinuationOptions.OnlyOnFaulted); + return true; + } + return false; + } + + public void NavigateToPath(string path) + { + try + { + var directoryName = Path.GetDirectoryName(path); + + _ = Win32Helpers.InvokeWin32ComponentAsync(path, ContentPageContext.ShellPage!, workingDirectory: directoryName ?? string.Empty); + } + catch (Exception) { } + } + + // Event methods + + private async void Manager_RecentFilesChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => + { + // e.Action can only be Reset right now; naively refresh everything for simplicity + await UpdateRecentFilesListAsync(e); + }); + } + + // Command methods + + private async Task ExecuteRemoveRecentItemCommand(RecentItem? item) + { + if (item is null) + return; + + await _refreshRecentFilesSemaphore.WaitAsync(); + + try + { + await App.RecentItemsManager.UnpinFromRecentFiles(item); + } + finally + { + _refreshRecentFilesSemaphore.Release(); + } + } + + private async Task ExecuteClearRecentItemsCommand() + { + await _refreshRecentFilesSemaphore.WaitAsync(); + try + { + Items.Clear(); + bool success = App.RecentItemsManager.ClearRecentItems(); + + if (success) + IsEmptyRecentFilesTextVisible = true; + } + finally + { + _refreshRecentFilesSemaphore.Release(); + } + } + + private void ExecuteOpenFileLocationCommand(RecentItem? item) + { + if (item is null) + return; + + var itemPath = Directory.GetParent(item.RecentPath)?.FullName ?? string.Empty; + var itemName = Path.GetFileName(item.RecentPath); + + ContentPageContext.ShellPage!.NavigateWithArguments( + ContentPageContext.ShellPage!.InstanceViewModel.FolderSettings.GetLayoutType(itemPath), + new NavigationArguments() + { + NavPathParam = itemPath, + SelectItems = new[] { itemName }, + AssociatedTabInstance = ContentPageContext.ShellPage! + }); + } + + private void ExecuteOpenPropertiesCommand(RecentItem? item) + { + var flyout = HomePageContext.ItemContextFlyoutMenu; + + if (item is null || flyout is null) + return; + + EventHandler flyoutClosed = null!; + flyoutClosed = async (s, e) => + { + flyout!.Closed -= flyoutClosed; + + BaseStorageFile file = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFileFromPathAsync(item.Path)); + if (file is null) + { + ContentDialog dialog = new() + { + Title = "CannotAccessPropertiesTitle".GetLocalizedResource(), + Content = "CannotAccessPropertiesContent".GetLocalizedResource(), + PrimaryButtonText = "Ok".GetLocalizedResource() + }; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + dialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + await dialog.TryShowAsync(); + } + else + { + var listedItem = await UniversalStorageEnumerator.AddFileAsync(file, null!, default); + FilePropertiesHelpers.OpenPropertiesWindow(listedItem, ContentPageContext.ShellPage!); + } + }; + + flyout!.Closed += flyoutClosed; + } + + // Disposer + + public void Dispose() + { + App.RecentItemsManager.RecentFilesChanged -= Manager_RecentFilesChanged; + } +>>>>>>> 70e2ff662 (Initial commit) } } diff --git a/src/Files.App/Views/HomePage.xaml.cs b/src/Files.App/Views/HomePage.xaml.cs index a6f3c1e90155..f293fc6ad918 100644 --- a/src/Files.App/Views/HomePage.xaml.cs +++ b/src/Files.App/Views/HomePage.xaml.cs @@ -1,13 +1,8 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.UserControls.Widgets; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Navigation; -using System.IO; -using System.Runtime.InteropServices; -using Windows.Foundation.Metadata; -using Windows.Storage; namespace Files.App.Views { @@ -15,32 +10,20 @@ public sealed partial class HomePage : Page, IDisposable { // Dependency injections - private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); - private HomeViewModel ViewModel { get; } = Ioc.Default.GetRequiredService(); + public HomeViewModel ViewModel { get; } = Ioc.Default.GetRequiredService(); // Properties private IShellPage AppInstance { get; set; } = null!; - public LayoutPreferencesManager FolderSettings - => AppInstance?.InstanceViewModel.FolderSettings!; - - private QuickAccessWidget? quickAccessWidget; - private DrivesWidget? drivesWidget; - private FileTagsWidget? fileTagsWidget; - private RecentFilesWidget? recentFilesWidget; - // Constructor public HomePage() { InitializeComponent(); - - ViewModel.HomePageLoadedInvoked += ViewModel_HomePageLoadedInvoked; - ViewModel.WidgetListRefreshRequestedInvoked += ViewModel_WidgetListRefreshRequestedInvoked; } - // Overridden methods + // Methods protected override async void OnNavigatedTo(NavigationEventArgs e) { @@ -92,198 +75,17 @@ protected override async void OnNavigatedTo(NavigationEventArgs e) base.OnNavigatedTo(e); } - protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) - { - base.OnNavigatingFrom(e); - - AppInstance.ToolbarViewModel.RefreshRequested -= ToolbarViewModel_RefreshRequested; - } - protected override void OnNavigatedFrom(NavigationEventArgs e) { Dispose(); - - base.OnNavigatedFrom(e); - } - - // Methods - - public void RefreshWidgetList() - { - ViewModel.RefreshWidgetList(); - } - - public void ReloadWidgets() - { - quickAccessWidget = WidgetsHelpers.TryGetWidget(UserSettingsService.GeneralSettingsService, ViewModel, out bool shouldReloadQuickAccessWidget, quickAccessWidget); - drivesWidget = WidgetsHelpers.TryGetWidget(UserSettingsService.GeneralSettingsService, ViewModel, out bool shouldReloadDrivesWidget, drivesWidget); - fileTagsWidget = WidgetsHelpers.TryGetWidget(UserSettingsService.GeneralSettingsService, ViewModel, out bool shouldReloadFileTags, fileTagsWidget); - recentFilesWidget = WidgetsHelpers.TryGetWidget(UserSettingsService.GeneralSettingsService, ViewModel, out bool shouldReloadRecentFiles, recentFilesWidget); - - // Reload QuickAccessWidget - if (shouldReloadQuickAccessWidget && quickAccessWidget is not null) - { - ViewModel.InsertWidget( - new( - quickAccessWidget, - (value) => UserSettingsService.GeneralSettingsService.FoldersWidgetExpanded = value, - () => UserSettingsService.GeneralSettingsService.FoldersWidgetExpanded), - 0); - - quickAccessWidget.CardInvoked -= QuickAccessWidget_CardInvoked; - quickAccessWidget.CardNewPaneInvoked -= WidgetCardNewPaneInvoked; - quickAccessWidget.CardPropertiesInvoked -= QuickAccessWidget_CardPropertiesInvoked; - quickAccessWidget.CardInvoked += QuickAccessWidget_CardInvoked; - quickAccessWidget.CardNewPaneInvoked += WidgetCardNewPaneInvoked; - quickAccessWidget.CardPropertiesInvoked += QuickAccessWidget_CardPropertiesInvoked; - } - - // Reload DrivesWidget - if (shouldReloadDrivesWidget && drivesWidget is not null) - { - ViewModel.InsertWidget(new(drivesWidget, (value) => UserSettingsService.GeneralSettingsService.DrivesWidgetExpanded = value, () => UserSettingsService.GeneralSettingsService.DrivesWidgetExpanded), 1); - - drivesWidget.AppInstance = AppInstance; - drivesWidget.DrivesWidgetInvoked -= DrivesWidget_DrivesWidgetInvoked; - drivesWidget.DrivesWidgetNewPaneInvoked -= DrivesWidget_DrivesWidgetNewPaneInvoked; - drivesWidget.DrivesWidgetInvoked += DrivesWidget_DrivesWidgetInvoked; - drivesWidget.DrivesWidgetNewPaneInvoked += DrivesWidget_DrivesWidgetNewPaneInvoked; - } - - // Reload FileTags - if (shouldReloadFileTags && fileTagsWidget is not null) - { - ViewModel.InsertWidget(new(fileTagsWidget, (value) => UserSettingsService.GeneralSettingsService.FileTagsWidgetExpanded = value, () => UserSettingsService.GeneralSettingsService.FileTagsWidgetExpanded), 2); - - fileTagsWidget.AppInstance = AppInstance; - fileTagsWidget.OpenAction = x => NavigationHelpers.OpenPath(x, AppInstance); - fileTagsWidget.FileTagsOpenLocationInvoked -= WidgetOpenLocationInvoked; - fileTagsWidget.FileTagsNewPaneInvoked -= WidgetCardNewPaneInvoked; - fileTagsWidget.FileTagsOpenLocationInvoked += WidgetOpenLocationInvoked; - fileTagsWidget.FileTagsNewPaneInvoked += WidgetCardNewPaneInvoked; - _ = fileTagsWidget.ViewModel.InitAsync(); - } - - // Reload RecentFilesWidget - if (shouldReloadRecentFiles && recentFilesWidget is not null) - { - ViewModel.InsertWidget(new(recentFilesWidget, (value) => UserSettingsService.GeneralSettingsService.RecentFilesWidgetExpanded = value, () => UserSettingsService.GeneralSettingsService.RecentFilesWidgetExpanded), 4); - - recentFilesWidget.AppInstance = AppInstance; - recentFilesWidget.RecentFilesOpenLocationInvoked -= WidgetOpenLocationInvoked; - recentFilesWidget.RecentFileInvoked -= RecentFilesWidget_RecentFileInvoked; - recentFilesWidget.RecentFilesOpenLocationInvoked += WidgetOpenLocationInvoked; - recentFilesWidget.RecentFileInvoked += RecentFilesWidget_RecentFileInvoked; - } - } - - // Event methods - - private void ViewModel_WidgetListRefreshRequestedInvoked(object? sender, EventArgs e) - { - ReloadWidgets(); - } - - private void ViewModel_HomePageLoadedInvoked(object? sender, Microsoft.UI.Xaml.RoutedEventArgs e) - { - ReloadWidgets(); - } - - private async void RecentFilesWidget_RecentFileInvoked(object sender, PathNavigationEventArgs e) - { - try - { - if (e.IsFile) - { - var directoryName = Path.GetDirectoryName(e.ItemPath); - await Win32Helpers.InvokeWin32ComponentAsync(e.ItemPath, AppInstance, workingDirectory: directoryName ?? string.Empty); - } - else - { - AppInstance.NavigateWithArguments( - FolderSettings.GetLayoutType(e.ItemPath), - new() - { - NavPathParam = e.ItemPath - }); - } - } - catch (UnauthorizedAccessException) - { - var dialog = DynamicDialogFactory.GetFor_ConsentDialog(); - - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - dialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - await dialog.TryShowAsync(); - } - catch (COMException) { } - catch (ArgumentException) { } - } - - private void WidgetOpenLocationInvoked(object sender, PathNavigationEventArgs e) - { - AppInstance.NavigateWithArguments(FolderSettings.GetLayoutType(e.ItemPath), new NavigationArguments() - { - NavPathParam = e.ItemPath, - SelectItems = new[] { e.ItemName }, - AssociatedTabInstance = AppInstance - }); - } - - private void QuickAccessWidget_CardInvoked(object sender, QuickAccessCardInvokedEventArgs e) - { - AppInstance.NavigateWithArguments(FolderSettings.GetLayoutType(e.Path), new NavigationArguments() - { - NavPathParam = e.Path - }); - } - - private void WidgetCardNewPaneInvoked(object sender, QuickAccessCardInvokedEventArgs e) - { - AppInstance.PaneHolder?.OpenPathInNewPane(e.Path); - } - - private async void QuickAccessWidget_CardPropertiesInvoked(object sender, QuickAccessCardEventArgs e) - { - ListedItem listedItem = new(null!) - { - ItemPath = e.Item.Path, - ItemNameRaw = e.Item.Text, - PrimaryItemAttribute = StorageItemTypes.Folder, - ItemType = "Folder".GetLocalizedResource(), - }; - - if (!string.Equals(e.Item.Path, Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase)) - { - BaseStorageFolder matchingStorageFolder = await AppInstance.FilesystemViewModel.GetFolderFromPathAsync(e.Item.Path); - if (matchingStorageFolder is not null) - { - var syncStatus = await AppInstance.FilesystemViewModel.CheckCloudDriveSyncStatusAsync(matchingStorageFolder); - listedItem.SyncStatusUI = CloudDriveSyncStatusUI.FromCloudDriveSyncStatus(syncStatus); - } - } - - FilePropertiesHelpers.OpenPropertiesWindow(listedItem, AppInstance); - } - - private void DrivesWidget_DrivesWidgetNewPaneInvoked(object sender, DrivesWidget.DrivesWidgetInvokedEventArgs e) - { - AppInstance.PaneHolder?.OpenPathInNewPane(e.Path); - } - - private void DrivesWidget_DrivesWidgetInvoked(object sender, DrivesWidget.DrivesWidgetInvokedEventArgs e) - { - AppInstance.NavigateWithArguments(FolderSettings.GetLayoutType(e.Path), new NavigationArguments() - { - NavPathParam = e.Path - }); } private async void ToolbarViewModel_RefreshRequested(object? sender, EventArgs e) { AppInstance.ToolbarViewModel.CanRefresh = false; + await Task.WhenAll(ViewModel.WidgetItems.Select(w => w.WidgetItemModel.RefreshWidgetAsync())); + AppInstance.ToolbarViewModel.CanRefresh = true; } @@ -291,8 +93,6 @@ private async void ToolbarViewModel_RefreshRequested(object? sender, EventArgs e public void Dispose() { - ViewModel.HomePageLoadedInvoked -= ViewModel_HomePageLoadedInvoked; - ViewModel.WidgetListRefreshRequestedInvoked -= ViewModel_WidgetListRefreshRequestedInvoked; AppInstance.ToolbarViewModel.RefreshRequested -= ToolbarViewModel_RefreshRequested; ViewModel?.Dispose(); } diff --git a/src/Files.App/Views/Shells/ModernShellPage.xaml.cs b/src/Files.App/Views/Shells/ModernShellPage.xaml.cs index 68f13b136f29..5a165a2a3f7a 100644 --- a/src/Files.App/Views/Shells/ModernShellPage.xaml.cs +++ b/src/Files.App/Views/Shells/ModernShellPage.xaml.cs @@ -75,7 +75,7 @@ public ModernShellPage() : base(new CurrentInstanceViewModel()) private void ModernShellPage_RefreshWidgetsRequested(object sender, EventArgs e) { if (ItemDisplayFrame?.Content is HomePage currentPage) - currentPage.RefreshWidgetList(); + currentPage.ViewModel.RefreshWidgetList(); } protected override void FolderSettings_LayoutPreferencesUpdateRequired(object sender, LayoutPreferenceEventArgs e) From 77572e950ebb590bbd10d44da9bf3cbbe15ec757 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Sun, 10 Mar 2024 12:02:19 +0900 Subject: [PATCH 02/14] Remove git indicators --- .../UserControls/Widgets/DrivesWidget.xaml.cs | 55 -------------- .../UserControls/Widgets/FileTagsWidget.xaml | 3 - .../Widgets/FileTagsWidget.xaml.cs | 26 ------- .../Widgets/QuickAccessWidget.xaml.cs | 18 ----- .../Widgets/RecentFilesWidget.xaml.cs | 74 ------------------- .../Widgets/DrivesWidgetViewModel.cs | 9 --- .../Widgets/FileTagsWidgetViewModel.cs | 17 ----- .../Widgets/QuickAccessWidgetViewModel.cs | 9 --- .../Widgets/RecentFilesWidgetViewModel.cs | 9 --- 9 files changed, 220 deletions(-) diff --git a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs index 1070eba867f9..1a6922931527 100644 --- a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs @@ -12,62 +12,7 @@ namespace Files.App.UserControls.Widgets /// public sealed partial class DrivesWidget : UserControl { -<<<<<<< HEAD -<<<<<<< HEAD - private DrivesWidgetViewModel ViewModel { get; set; } - - public IUserSettingsService userSettingsService { get; } = Ioc.Default.GetRequiredService(); - private IHomePageContext HomePageContext { get; } = Ioc.Default.GetRequiredService(); - private DrivesViewModel drivesViewModel = Ioc.Default.GetRequiredService(); - private NetworkDrivesViewModel networkDrivesViewModel = Ioc.Default.GetRequiredService(); - - public delegate void DrivesWidgetInvokedEventHandler(object sender, DrivesWidgetInvokedEventArgs e); - public event DrivesWidgetInvokedEventHandler DrivesWidgetInvoked; - public delegate void DrivesWidgetNewPaneInvokedEventHandler(object sender, DrivesWidgetInvokedEventArgs e); - public event DrivesWidgetNewPaneInvokedEventHandler DrivesWidgetNewPaneInvoked; - public event PropertyChangedEventHandler? PropertyChanged; - public static ObservableCollection ItemsAdded = new(); - - private IShellPage associatedInstance; - - public ICommand FormatDriveCommand; - public ICommand EjectDeviceCommand; - public ICommand DisconnectNetworkDriveCommand; - public ICommand GoToStorageSenseCommand; - public ICommand OpenInNewPaneCommand; - - public IShellPage AppInstance - { - get => associatedInstance; - set - { - if (value != associatedInstance) - { - associatedInstance = value; - NotifyPropertyChanged(nameof(AppInstance)); - } - } - } - - public string WidgetName => nameof(DrivesWidget); - public string AutomationProperties => "DrivesWidgetAutomationProperties/Name".GetLocalizedResource(); - public string WidgetHeader => "Drives".GetLocalizedResource(); - public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowDrivesWidget; - public bool ShowMenuFlyout => true; - public MenuFlyoutItem MenuFlyoutItem => new MenuFlyoutItem() - { - Icon = new FontIcon() { Glyph = "\uE710" }, - Text = "DrivesWidgetOptionsFlyoutMapNetDriveMenuItem/Text".GetLocalizedResource(), - Command = MapNetworkDriveCommand - }; - - public AsyncRelayCommand MapNetworkDriveCommand { get; } -======= - private DrivesWidgetViewModel ViewModel { get; set; } = new(); ->>>>>>> 70e2ff662 (Initial commit) -======= public DrivesWidgetViewModel ViewModel { get; set; } = new(); ->>>>>>> 145267b14 (Fix) public DrivesWidget() { diff --git a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml index a165770b6170..a9c77fca11b3 100644 --- a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml +++ b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml @@ -8,10 +8,7 @@ xmlns:dataitems="using:Files.App.Data.Items" xmlns:helpers="using:Files.App.Helpers" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" -<<<<<<< HEAD -======= xmlns:wctcontrols="using:CommunityToolkit.WinUI.UI.Controls" ->>>>>>> 70e2ff662 (Initial commit) DataContext="{x:Bind ViewModel, Mode=OneWay}" mc:Ignorable="d"> diff --git a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs index c7db3b1d692a..084a7a55b744 100644 --- a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs @@ -12,33 +12,7 @@ namespace Files.App.UserControls.Widgets /// public sealed partial class FileTagsWidget : UserControl { -<<<<<<< HEAD - public FileTagsWidgetViewModel ViewModel { get; set; } - - private readonly IUserSettingsService userSettingsService; - private IHomePageContext HomePageContext { get; } = Ioc.Default.GetRequiredService(); - - public IShellPage AppInstance; - - public Func? OpenAction { get; set; } - - public delegate void FileTagsOpenLocationInvokedEventHandler(object sender, PathNavigationEventArgs e); - public delegate void FileTagsNewPaneInvokedEventHandler(object sender, QuickAccessCardInvokedEventArgs e); - public static event EventHandler>? SelectedTaggedItemsChanged; - public event FileTagsOpenLocationInvokedEventHandler FileTagsOpenLocationInvoked; - public event FileTagsNewPaneInvokedEventHandler FileTagsNewPaneInvoked; - - public string WidgetName => nameof(FileTagsWidget); - public string WidgetHeader => "FileTags".GetLocalizedResource(); - public string AutomationProperties => "FileTags".GetLocalizedResource(); - public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowFileTagsWidget; - public bool ShowMenuFlyout => false; - public MenuFlyoutItem? MenuFlyoutItem => null; - - private ICommand OpenInNewPaneCommand; -======= public FileTagsWidgetViewModel ViewModel { get; set; } = new(); ->>>>>>> 70e2ff662 (Initial commit) public FileTagsWidget() { diff --git a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs index efc4466a01f8..07048dc3ccbe 100644 --- a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs @@ -12,25 +12,7 @@ namespace Files.App.UserControls.Widgets /// public sealed partial class QuickAccessWidget : UserControl { -<<<<<<< HEAD -<<<<<<< HEAD - private QuickAccessWidgetViewModel ViewModel { get; set; } - - public IUserSettingsService userSettingsService { get; } = Ioc.Default.GetRequiredService(); - private IHomePageContext HomePageContext { get; } = Ioc.Default.GetRequiredService(); - - public static ObservableCollection ItemsAdded = new(); - - static QuickAccessWidget() - { - ItemsAdded.CollectionChanged += ItemsAdded_CollectionChanged; - } -======= - private QuickAccessWidgetViewModel ViewModel { get; set; } = new(); ->>>>>>> 70e2ff662 (Initial commit) -======= public QuickAccessWidgetViewModel ViewModel { get; set; } = new(); ->>>>>>> 145267b14 (Fix) public QuickAccessWidget() { diff --git a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs index 9bddebcabf8d..4c98c3575b78 100644 --- a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs @@ -11,81 +11,7 @@ namespace Files.App.UserControls.Widgets /// public sealed partial class RecentFilesWidget : UserControl { -<<<<<<< HEAD -<<<<<<< HEAD - private RecentFilesWidgetViewModel ViewModel { get; set; } - - private IHomePageContext HomePageContext { get; } = Ioc.Default.GetRequiredService(); - - public delegate void RecentFilesOpenLocationInvokedEventHandler(object sender, PathNavigationEventArgs e); - public event RecentFilesOpenLocationInvokedEventHandler RecentFilesOpenLocationInvoked; - public delegate void RecentFileInvokedEventHandler(object sender, PathNavigationEventArgs e); - public event RecentFileInvokedEventHandler RecentFileInvoked; - public event PropertyChangedEventHandler PropertyChanged; - - private ObservableCollection recentItemsCollection = new ObservableCollection(); - - private SemaphoreSlim refreshRecentsSemaphore; - - private CancellationTokenSource refreshRecentsCTS; - - private readonly IUserSettingsService userSettingsService = Ioc.Default.GetRequiredService(); - - public string WidgetName => nameof(RecentFilesWidget); - public string AutomationProperties => "RecentFilesWidgetAutomationProperties/Name".GetLocalizedResource(); - public string WidgetHeader => "RecentFiles".GetLocalizedResource(); - public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowRecentFilesWidget; - public bool ShowMenuFlyout => false; - - public MenuFlyoutItem? MenuFlyoutItem => null; - - private bool isEmptyRecentsTextVisible = false; - public bool IsEmptyRecentsTextVisible - { - get => isEmptyRecentsTextVisible; - internal set - { - if (isEmptyRecentsTextVisible != value) - { - isEmptyRecentsTextVisible = value; - NotifyPropertyChanged(nameof(IsEmptyRecentsTextVisible)); - } - } - } - - private bool isRecentFilesDisabledInWindows = false; - public bool IsRecentFilesDisabledInWindows - { - get => isRecentFilesDisabledInWindows; - internal set - { - if (isRecentFilesDisabledInWindows != value) - { - isRecentFilesDisabledInWindows = value; - NotifyPropertyChanged(nameof(IsRecentFilesDisabledInWindows)); - } - } - } - - private IShellPage associatedInstance; - public IShellPage AppInstance - { - get => associatedInstance; - set - { - if (value != associatedInstance) - { - associatedInstance = value; - NotifyPropertyChanged(nameof(AppInstance)); - } - } - } -======= - private RecentFilesWidgetViewModel ViewModel { get; set; } = new(); ->>>>>>> 70e2ff662 (Initial commit) -======= public RecentFilesWidgetViewModel ViewModel { get; set; } = new(); ->>>>>>> 145267b14 (Fix) public RecentFilesWidget() { diff --git a/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs index df9ff9f76a51..db4142452e2c 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs @@ -1,8 +1,6 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -<<<<<<< HEAD -======= using Microsoft.UI.Input; using Microsoft.UI.Xaml.Controls; using System.Collections.Specialized; @@ -10,17 +8,11 @@ <<<<<<< HEAD using Windows.System; using Windows.UI.Core; ->>>>>>> 70e2ff662 (Initial commit) namespace Files.App.ViewModels.UserControls.Widgets { /// /// Represents view model of . /// -<<<<<<< HEAD - public class DrivesWidgetViewModel - { - public ObservableCollection Items { get; } = []; -======= public class DrivesWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel { // Properties @@ -279,6 +271,5 @@ private async void Drives_CollectionChanged(object? sender, NotifyCollectionChan public void Dispose() { } ->>>>>>> 70e2ff662 (Initial commit) } } diff --git a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs index 7adf32490eca..c5fdda35ca4e 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs @@ -11,17 +11,11 @@ namespace Files.App.ViewModels.UserControls.Widgets /// /// Represents view model of . /// -<<<<<<< HEAD - public sealed partial class FileTagsWidgetViewModel : ObservableObject, IAsyncInitialize -======= public sealed partial class FileTagsWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel ->>>>>>> 70e2ff662 (Initial commit) { // Properties public ObservableCollection Containers { get; } = []; -<<<<<<< HEAD -======= public string WidgetName => nameof(FileTagsWidget); public string WidgetHeader => "FileTags".GetLocalizedResource(); @@ -37,18 +31,11 @@ <<<<<<< HEAD // Commands private ICommand OpenInNewPaneCommand { get; set; } = null!; ->>>>>>> 70e2ff662 (Initial commit) // Constructor public FileTagsWidgetViewModel() { -<<<<<<< HEAD -<<<<<<< HEAD - _openAction = openAction; -======= -======= ->>>>>>> 9de88d1c4 (Fix) _ = InitializeWidget(); OpenInNewTabCommand = new AsyncRelayCommand(ExecuteOpenInNewTabCommand); @@ -58,10 +45,6 @@ >>>>>>> 9de88d1c4 (Fix) OpenFileLocationCommand = new RelayCommand(ExecuteOpenFileLocationCommand); OpenInNewPaneCommand = new RelayCommand(ExecuteOpenInNewPaneCommand); OpenPropertiesCommand = new RelayCommand(ExecuteOpenPropertiesCommand); -<<<<<<< HEAD ->>>>>>> 70e2ff662 (Initial commit) -======= ->>>>>>> 9de88d1c4 (Fix) } // Methods diff --git a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs index 26ff9c21750c..35870581b95d 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs @@ -1,8 +1,6 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -<<<<<<< HEAD -======= using Microsoft.UI.Input; using Microsoft.UI.Xaml.Controls; using System.Collections.Specialized; @@ -12,17 +10,11 @@ <<<<<<< HEAD using Windows.System; using Windows.UI.Core; ->>>>>>> 70e2ff662 (Initial commit) namespace Files.App.ViewModels.UserControls.Widgets { /// /// Represents view model of . /// -<<<<<<< HEAD - public class QuickAccessWidgetViewModel - { - public ObservableCollection Items { get; } = []; -======= public class QuickAccessWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel { // Properties @@ -324,6 +316,5 @@ public void Dispose() { App.QuickAccessManager.UpdateQuickAccessWidget -= ModifyItemAsync; } ->>>>>>> 70e2ff662 (Initial commit) } } diff --git a/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs index 4952bd002dbd..ddc44ebfa318 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs @@ -1,25 +1,17 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -<<<<<<< HEAD -======= using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml.Controls; using System.Collections.Specialized; using System.IO; using Windows.Foundation.Metadata; ->>>>>>> 70e2ff662 (Initial commit) namespace Files.App.ViewModels.UserControls.Widgets { /// /// Represents view model of . /// -<<<<<<< HEAD - public class RecentFilesWidgetViewModel - { - public ObservableCollection Items { get; } = []; -======= public class RecentFilesWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel { // Fields @@ -348,6 +340,5 @@ public void Dispose() { App.RecentItemsManager.RecentFilesChanged -= Manager_RecentFilesChanged; } ->>>>>>> 70e2ff662 (Initial commit) } } From 16e15d6f00ed1cefd34583f71f8a6858dc19f8b3 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Sun, 10 Mar 2024 12:11:20 +0900 Subject: [PATCH 03/14] Update --- .../UserControls/Widgets/DrivesWidget.xaml.cs | 15 +++++------ .../Widgets/QuickAccessWidget.xaml.cs | 8 +++--- .../Widgets/BaseWidgetViewModel.cs | 3 ++- .../Widgets/FileTagsWidgetViewModel.cs | 2 +- .../Widgets/QuickAccessWidgetViewModel.cs | 26 +++++++++---------- 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs index 1a6922931527..7ec6e2fa52c3 100644 --- a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs @@ -21,21 +21,20 @@ public DrivesWidget() private async void Button_Click(object sender, RoutedEventArgs e) { - if (sender is not Button button) + if (sender is not Button button || + button.Tag.ToString() is not string path) return; - var path = button.Tag.ToString() ?? string.Empty; - await ViewModel.NavigateToPath(path); } private async void Button_PointerPressed(object sender, PointerRoutedEventArgs e) { - if (!e.GetCurrentPoint(null).Properties.IsMiddleButtonPressed || sender is not Button button) + if (!e.GetCurrentPoint(null).Properties.IsMiddleButtonPressed || + sender is not Button button || + button.Tag.ToString() is not string path) return; - var path = button.Tag.ToString() ?? string.Empty; - if (await DriveHelpers.CheckEmptyDrive(path)) return; @@ -49,10 +48,10 @@ private void Button_RightTapped(object sender, RightTappedRoutedEventArgs e) private async void GoToStorageSense_Click(object sender, RoutedEventArgs e) { - if (sender is not Button button) + if (sender is not Button button || + button.Tag.ToString() is not string path) return; - string path = button.Tag.ToString() ?? string.Empty; await StorageSenseHelper.OpenStorageSenseAsync(path); } } diff --git a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs index 07048dc3ccbe..c986da67b4ed 100644 --- a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs @@ -21,16 +21,18 @@ public QuickAccessWidget() private async void Button_PointerPressed(object sender, PointerRoutedEventArgs e) { - if (!e.GetCurrentPoint(null).Properties.IsMiddleButtonPressed || sender is not Button button) + if (!e.GetCurrentPoint(null).Properties.IsMiddleButtonPressed || + sender is not Button button || + button.Tag.ToString() is not string path) return; - string path = button.Tag.ToString()!; await NavigationHelpers.OpenPathInNewTab(path, false); } private async void Button_Click(object sender, RoutedEventArgs e) { - if (sender is not Button button || button.Tag.ToString() is not string path) + if (sender is not Button button || + button.Tag.ToString() is not string path) return; await ViewModel.NavigateToPath(path); diff --git a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs index ab6485370e69..8848088548b5 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs @@ -53,7 +53,8 @@ public void BuildItemContextMenu(object sender, RightTappedRoutedEventArgs e) { // Ensure values are not null if (sender is not FrameworkElement widgetCardItem || - widgetCardItem.DataContext is not WidgetCardItem item) + widgetCardItem.DataContext is not WidgetCardItem item || + item.Path is null) return; // Create a new Flyout diff --git a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs index c5fdda35ca4e..9c1739d5ad6f 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs @@ -191,7 +191,7 @@ private void ExecuteOpenInNewPaneCommand(WidgetCardItem? item) ContentPageContext.ShellPage!.PaneHolder?.OpenPathInNewPane(item?.Path ?? string.Empty); } - public void ExecuteOpenFileLocationCommand(WidgetCardItem? item) + private void ExecuteOpenFileLocationCommand(WidgetCardItem? item) { var itemPath = Directory.GetParent(item?.Path ?? string.Empty)?.FullName ?? string.Empty; var itemName = Path.GetFileName(item?.Path ?? string.Empty); diff --git a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs index 35870581b95d..f75ca66b754c 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs @@ -50,6 +50,19 @@ public QuickAccessWidgetViewModel() // Methods + private async Task InitializeWidget() + { + var itemsToAdd = await QuickAccessService.GetPinnedFoldersAsync(); + ModifyItemAsync(this, new(itemsToAdd.ToArray(), false) { Reset = true }); + + App.QuickAccessManager.UpdateQuickAccessWidget += ModifyItemAsync; + } + + public Task RefreshWidgetAsync() + { + return Task.CompletedTask; + } + public override List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) { return new List() @@ -197,11 +210,6 @@ private async void ModifyItemAsync(object? sender, ModifyQuickAccessEventArgs? e }); } - public Task RefreshWidgetAsync() - { - return Task.CompletedTask; - } - public async Task NavigateToPath(string path) { var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); @@ -218,14 +226,6 @@ public async Task NavigateToPath(string path) // Event methods - private async Task InitializeWidget() - { - var itemsToAdd = await QuickAccessService.GetPinnedFoldersAsync(); - ModifyItemAsync(this, new(itemsToAdd.ToArray(), false) { Reset = true }); - - App.QuickAccessManager.UpdateQuickAccessWidget += ModifyItemAsync; - } - private async void Items_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { if (e.Action is NotifyCollectionChangedAction.Add) From 3fc55c6485ba1fe55ae3d33c91b9b5a8c085523b Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Sat, 23 Mar 2024 16:58:03 +0900 Subject: [PATCH 04/14] Fix --- .../ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs | 2 +- .../UserControls/Widgets/RecentFilesWidgetViewModel.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs index 437c9760a595..ad13a7a3c713 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs @@ -213,7 +213,7 @@ private Task ExecuteMapNetworkDriveCommand() private void ExecuteFormatDriveCommand(WidgetDriveCardItem? item) { - Win32API.OpenFormatDriveDialog(item?.Path ?? string.Empty); + Win32Helper.OpenFormatDriveDialog(item?.Path ?? string.Empty); } private void ExecuteOpenPropertiesCommand(WidgetDriveCardItem? item) diff --git a/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs index eec963be480e..b64763717085 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs @@ -227,7 +227,7 @@ public void NavigateToPath(string path) { var directoryName = Path.GetDirectoryName(path); - _ = Win32Helpers.InvokeWin32ComponentAsync(path, ContentPageContext.ShellPage!, workingDirectory: directoryName ?? string.Empty); + _ = Win32Helper.InvokeWin32ComponentAsync(path, ContentPageContext.ShellPage!, workingDirectory: directoryName ?? string.Empty); } catch (Exception) { } } From 1abbdd1f6225cd736a870afb1b2d610cbfff2ab4 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Mon, 15 Apr 2024 03:45:15 +0900 Subject: [PATCH 05/14] Req --- src/Files.App/GlobalUsings.cs | 2 +- .../Helpers/Application/AppLifecycleHelper.cs | 4 ++ .../UserControls/Widgets/DrivesWidget.xaml.cs | 2 +- .../Widgets/FileTagsWidget.xaml.cs | 2 +- .../Widgets/QuickAccessWidget.xaml.cs | 2 +- .../Widgets/RecentFilesWidget.xaml.cs | 2 +- .../Widgets/BaseWidgetViewModel.cs | 6 +- .../FileTagsContainerViewModel.cs | 61 ------------------- .../FileTagsWidget/FileTagsItemViewModel.cs | 55 ----------------- .../FileTagsWidget/FileTagsWidgetViewModel.cs | 54 ---------------- 10 files changed, 12 insertions(+), 178 deletions(-) delete mode 100644 src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs delete mode 100644 src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsItemViewModel.cs delete mode 100644 src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs diff --git a/src/Files.App/GlobalUsings.cs b/src/Files.App/GlobalUsings.cs index 994cd5a70fdb..1f411937c348 100644 --- a/src/Files.App/GlobalUsings.cs +++ b/src/Files.App/GlobalUsings.cs @@ -65,7 +65,7 @@ global using global::Files.App.ViewModels.Dialogs.AddItemDialog; global using global::Files.App.ViewModels.Dialogs.FileSystemDialog; global using global::Files.App.ViewModels.FileTags; -global using global::Files.App.ViewModels.Widgets; +global using global::Files.App.ViewModels.UserControls.Widgets; global using global::Files.App.Utils.CommandLine; // Files.Core.Storage diff --git a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs index dab4c5ef124c..994e99f3f04c 100644 --- a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs +++ b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs @@ -199,6 +199,10 @@ public static IHost ConfigureHost() .AddSingleton() .AddSingleton() .AddTransient() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() // Utilities .AddSingleton() .AddSingleton() diff --git a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs index 7c835a703d2d..7cfef19e8ddc 100644 --- a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs @@ -12,7 +12,7 @@ namespace Files.App.UserControls.Widgets /// public sealed partial class DrivesWidget : UserControl { - public DrivesWidgetViewModel ViewModel { get; set; } = new(); + public DrivesWidgetViewModel ViewModel { get; set; } = Ioc.Default.GetRequiredService(); public DrivesWidget() { diff --git a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs index 0225ca4278e3..32d5c313fed6 100644 --- a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs @@ -12,7 +12,7 @@ namespace Files.App.UserControls.Widgets /// public sealed partial class FileTagsWidget : UserControl { - public FileTagsWidgetViewModel ViewModel { get; set; } = new(); + public FileTagsWidgetViewModel ViewModel { get; set; } = Ioc.Default.GetRequiredService(); public FileTagsWidget() { diff --git a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs index c1342cbd091f..16ad970f4e46 100644 --- a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs @@ -12,7 +12,7 @@ namespace Files.App.UserControls.Widgets /// public sealed partial class QuickAccessWidget : UserControl { - public QuickAccessWidgetViewModel ViewModel { get; set; } = new(); + public QuickAccessWidgetViewModel ViewModel { get; set; } = Ioc.Default.GetRequiredService(); public QuickAccessWidget() { diff --git a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs index 4d3901fba822..0e48d8de31e2 100644 --- a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs @@ -11,7 +11,7 @@ namespace Files.App.UserControls.Widgets /// public sealed partial class RecentFilesWidget : UserControl { - public RecentFilesWidgetViewModel ViewModel { get; set; } = new(); + public RecentFilesWidgetViewModel ViewModel { get; set; } = Ioc.Default.GetRequiredService(); public RecentFilesWidget() { diff --git a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs index aba6e8ecefae..e56fbb598487 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs @@ -8,7 +8,7 @@ using Microsoft.UI.Xaml.Input; using System.Windows.Input; -namespace Files.App.UserControls.Widgets +namespace Files.App.ViewModels.UserControls.Widgets { /// /// Represents base ViewModel for widget ViewModels. @@ -23,8 +23,8 @@ public abstract class BaseWidgetViewModel : ObservableObject protected IHomePageContext HomePageContext { get; } = Ioc.Default.GetRequiredService(); protected IContentPageContext ContentPageContext { get; } = Ioc.Default.GetRequiredService(); protected IFileTagsService FileTagsService { get; } = Ioc.Default.GetRequiredService(); - protected DrivesViewModel DrivesViewModel = Ioc.Default.GetRequiredService(); - protected NetworkDrivesViewModel NetworkDrivesViewModel = Ioc.Default.GetRequiredService(); + protected DrivesViewModel DrivesViewModel { get; } = Ioc.Default.GetRequiredService(); + protected NetworkDrivesViewModel NetworkDrivesViewModel { get; } = Ioc.Default.GetRequiredService(); // Fields diff --git a/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs b/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs deleted file mode 100644 index 4959581f588f..000000000000 --- a/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Shared.Utils; - -namespace Files.App.ViewModels.Widgets.FileTagsWidget -{ - public sealed partial class FileTagsContainerViewModel : ObservableObject, IAsyncInitialize - { - private readonly IFileTagsService _fileTagsService; - - private readonly IImageService _imageService; - - private readonly string _tagUid; - - private readonly Func _openAction; - - public ObservableCollection Tags { get; } - - private string _Color; - public string Color - { - get => _Color; - set => SetProperty(ref _Color, value); - } - - private string _Name; - public string Name - { - get => _Name; - set => SetProperty(ref _Name, value); - } - - public FileTagsContainerViewModel(string tagUid, Func openAction) - { - _fileTagsService = Ioc.Default.GetRequiredService(); - _imageService = Ioc.Default.GetRequiredService(); - - _tagUid = tagUid; - _openAction = openAction; - Tags = []; - } - - /// - public async Task InitAsync(CancellationToken cancellationToken = default) - { - await foreach (var item in _fileTagsService.GetItemsForTagAsync(_tagUid, cancellationToken)) - { - var icon = await _imageService.GetIconAsync(item.Storable, cancellationToken); - - Tags.Add(new(item.Storable, _openAction, icon)); - } - } - - [RelayCommand] - private Task ViewMore(CancellationToken cancellationToken) - { - return _openAction($"tag:{Name}"); - } - } -} diff --git a/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsItemViewModel.cs b/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsItemViewModel.cs deleted file mode 100644 index ca05e1cb4e87..000000000000 --- a/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsItemViewModel.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Core.Storage; -using Files.Core.Storage.Extensions; -using Files.Shared.Helpers; -using Files.Shared.Utils; - -namespace Files.App.ViewModels.Widgets.FileTagsWidget -{ - public sealed partial class FileTagsItemViewModel : ObservableObject - { - private readonly IStorable _associatedStorable; - - // A workaround for lack of MVVM-compliant navigation support. - // This workaround must be kept until further refactor of navigation code is completed - private readonly Func _openAction; - - private IImage? _Icon; - public IImage? Icon - { - get => _Icon; - set => SetProperty(ref _Icon, value); - } - - private string _Name; - public string Name - { - get => _Name; - set => SetProperty(ref _Name, value); - } - - private string? _Path; - public string? Path - { - get => _Path; - set => SetProperty(ref _Path, value); - } - - public FileTagsItemViewModel(IStorable associatedStorable, Func openAction, IImage? icon) - { - _associatedStorable = associatedStorable; - _openAction = openAction; - _Icon = icon; - _Name = PathHelpers.FormatName(associatedStorable.Id); - _Path = associatedStorable.TryGetPath(); - } - - [RelayCommand] - private Task ClickAsync(CancellationToken cancellationToken) - { - return _openAction(_associatedStorable.Id); - } - } -} diff --git a/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs b/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs deleted file mode 100644 index 7a15073ed388..000000000000 --- a/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Shared.Utils; - -namespace Files.App.ViewModels.Widgets.FileTagsWidget -{ - public sealed partial class FileTagsWidgetViewModel : ObservableObject, IAsyncInitialize - { - private readonly Func _openAction; - - private readonly IFileTagsService _fileTagsService; - - private readonly IFileTagsSettingsService _fileTagsSettingsService; - - public ObservableCollection Containers { get; } - - public FileTagsWidgetViewModel(Func openAction) - { - // Dependency injection - _fileTagsService = Ioc.Default.GetRequiredService(); - _fileTagsSettingsService = Ioc.Default.GetRequiredService(); - - _openAction = openAction; - Containers = []; - - _fileTagsSettingsService.OnTagsUpdated += FileTagsSettingsService_OnTagsUpdated; - } - - private async void FileTagsSettingsService_OnTagsUpdated(object? _, EventArgs e) - { - Containers.Clear(); - - await InitAsync(); - } - - /// - public async Task InitAsync(CancellationToken cancellationToken = default) - { - await foreach (var item in _fileTagsService.GetTagsAsync(cancellationToken)) - { - var container = new FileTagsContainerViewModel(item.Uid, _openAction) - { - Name = item.Name, - Color = item.Color - }; - - Containers.Add(container); - - _ = container.InitAsync(cancellationToken); - } - } - } -} From 0de5e47222ba84c9c10e07ce092a26d026ec3cb8 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Fri, 19 Apr 2024 21:49:58 +0900 Subject: [PATCH 06/14] Fix --- .../ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs | 2 +- .../ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs index e56fbb598487..2e0fc3ed722c 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs @@ -24,7 +24,7 @@ public abstract class BaseWidgetViewModel : ObservableObject protected IContentPageContext ContentPageContext { get; } = Ioc.Default.GetRequiredService(); protected IFileTagsService FileTagsService { get; } = Ioc.Default.GetRequiredService(); protected DrivesViewModel DrivesViewModel { get; } = Ioc.Default.GetRequiredService(); - protected NetworkDrivesViewModel NetworkDrivesViewModel { get; } = Ioc.Default.GetRequiredService(); + protected INetworkDrivesService NetworkDrivesService { get; } = Ioc.Default.GetRequiredService(); // Fields diff --git a/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs index ad13a7a3c713..f1e4e4152197 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs @@ -208,7 +208,7 @@ private async Task ExecuteOpenInNewPaneCommand(WidgetDriveCardItem? item) private Task ExecuteMapNetworkDriveCommand() { - return NetworkDrivesViewModel.OpenMapNetworkDriveDialogAsync(); + return NetworkDrivesService.OpenMapNetworkDriveDialogAsync(); } private void ExecuteFormatDriveCommand(WidgetDriveCardItem? item) @@ -238,7 +238,7 @@ private void ExecuteDisconnectNetworkDriveCommand(WidgetDriveCardItem? item) if (item is null) return; - NetworkDrivesViewModel.DisconnectNetworkDrive(item.Item); + NetworkDrivesService.DisconnectNetworkDrive(item.Item); } // Event methods From 9ead11b7ff8325a8da8cf0d5afcdaf292619fc83 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Mon, 22 Apr 2024 23:58:24 +0900 Subject: [PATCH 07/14] Updated command constructors and UI elements in various files In `WidgetFileTagsContainerItem.cs`, updated `AsyncRelayCommand` constructors for `ViewMoreCommand` and `OpenAllCommand` to not require `CancellationToken` parameter. Also, updated related methods and simplified `SelectedTagChanged` event invocation. In `FileTagsWidget.xaml`, removed alignment properties from `Grid` element and added new `Setter` for `HorizontalAlignment` in `GridViewItem` style. Renamed `x:Name` property of `ListView` in `RecentFilesWidget.xaml` and updated event handlers accordingly. Also, renamed methods in `RecentFilesWidget.xaml.cs` to reflect this change. In `RecentItem.cs`, changed `Path` property to an override. Lastly, removed `SendTo` menu item from context menu in `FileTagsWidgetViewModel.cs` and `RecentFilesWidgetViewModel.cs`. --- .../Data/Items/WidgetFileTagsContainerItem.cs | 14 +++++++------- .../UserControls/Widgets/FileTagsWidget.xaml | 3 +-- .../UserControls/Widgets/RecentFilesWidget.xaml | 6 +++--- .../UserControls/Widgets/RecentFilesWidget.xaml.cs | 4 ++-- src/Files.App/Utils/RecentItem/RecentItem.cs | 2 +- .../Widgets/FileTagsWidgetViewModel.cs | 6 ------ .../Widgets/RecentFilesWidgetViewModel.cs | 6 ------ 7 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs b/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs index c146fcc5fe4c..acf5f31cea4d 100644 --- a/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs +++ b/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs @@ -50,28 +50,28 @@ public WidgetFileTagsContainerItem(string tagUid) _tagUid = tagUid; Tags = new(); - ViewMoreCommand = new AsyncRelayCommand(ViewMore); - OpenAllCommand = new AsyncRelayCommand(OpenAll); + ViewMoreCommand = new AsyncRelayCommand(ViewMore); + OpenAllCommand = new AsyncRelayCommand(OpenAll); } /// public async Task InitAsync(CancellationToken cancellationToken = default) { - await foreach (var item in FileTagsService.GetItemsForTagAsync(_tagUid, cancellationToken)) + await foreach (var item in FileTagsService.GetItemsForTagAsync(_tagUid)) { - var icon = await ImageService.GetIconAsync(item.Storable, cancellationToken); + var icon = await ImageService.GetIconAsync(item.Storable, default); Tags.Add(new(item.Storable, icon)); } } - private Task ViewMore(CancellationToken cancellationToken) + private Task ViewMore() { return NavigationHelpers.OpenPath($"tag:{Name}", ContentPageContext.ShellPage!); } - private Task OpenAll(CancellationToken cancellationToken) + private Task OpenAll() { - SelectedTagChanged?.Invoke(this, new SelectedTagChangedEventArgs(Tags.Select(tag => (tag.Path, tag.IsFolder)))); + SelectedTagChanged?.Invoke(this, new(Tags.Select(tag => (tag.Path, tag.IsFolder)))); return Commands.OpenAllTaggedItems.ExecuteAsync(); } diff --git a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml index ae80fa316e1d..191a8c8048f5 100644 --- a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml +++ b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml @@ -35,8 +35,6 @@ diff --git a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml index 459980e0c522..660abcc1feff 100644 --- a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml +++ b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml @@ -34,15 +34,15 @@ diff --git a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs index 0e48d8de31e2..4ae597f560e0 100644 --- a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs @@ -18,7 +18,7 @@ public RecentFilesWidget() InitializeComponent(); } - private void RecentFilesView_ItemClick(object sender, ItemClickEventArgs e) + private void RecentFilesListView_ItemClick(object sender, ItemClickEventArgs e) { if (e.ClickedItem is not RecentItem item) return; @@ -26,7 +26,7 @@ private void RecentFilesView_ItemClick(object sender, ItemClickEventArgs e) ViewModel.NavigateToPath(item.RecentPath); } - private void ListView_RightTapped(object sender, RightTappedRoutedEventArgs e) + private void RecentFilesListView_RightTapped(object sender, RightTappedRoutedEventArgs e) { ViewModel.BuildItemContextMenu(e.OriginalSource, e); } diff --git a/src/Files.App/Utils/RecentItem/RecentItem.cs b/src/Files.App/Utils/RecentItem/RecentItem.cs index e9491b1dd4e9..42ad32922cc4 100644 --- a/src/Files.App/Utils/RecentItem/RecentItem.cs +++ b/src/Files.App/Utils/RecentItem/RecentItem.cs @@ -18,7 +18,7 @@ public BitmapImage FileImg public string Name { get; set; } public DateTime LastModified { get; set; } public byte[] PIDL { get; set; } - public string Path { get => RecentPath; } + public override string Path => RecentPath; public RecentItem() { diff --git a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs index 01b5173c244e..1a307db6a01c 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs @@ -81,12 +81,6 @@ public override List GetItemMenuItems(WidgetCard ShowItem = !isFolder }, new() - { - Text = "SendTo".GetLocalizedResource(), - Tag = "SendToPlaceholder", - ShowItem = !isFolder && UserSettingsService.GeneralSettingsService.ShowSendToMenu - }, - new() { Text = "OpenInNewTab".GetLocalizedResource(), OpacityIcon = new() { OpacityIconStyle = "ColorIconOpenInNewTab" }, diff --git a/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs index b64763717085..df586021b38c 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs @@ -81,12 +81,6 @@ public override List GetItemMenuItems(WidgetCard Tag = "OpenWithPlaceholder", }, new() - { - Text = "SendTo".GetLocalizedResource(), - Tag = "SendToPlaceholder", - ShowItem = UserSettingsService.GeneralSettingsService.ShowSendToMenu - }, - new() { Text = "RecentItemRemove/Text".GetLocalizedResource(), Glyph = "\uE738", From 425871a404bf53265dfdd53d9aa70bfce3ed3e7e Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Tue, 23 Apr 2024 01:04:40 +0900 Subject: [PATCH 08/14] Refactor ViewModel classes and update BaseWidgetViewModel Moved `ListedTagViewModel` and `TagViewModel` classes to `Files.App.Data.Models` namespace. Added workaround for file tags `isFolder` in `BaseWidgetViewModel.cs` and updated `GetItemMenuItems` method call to include a new parameter. Enhanced `ListedTagViewModel` and `TagViewModel` classes with new properties and initialized them in constructors. Decorated `TagViewModel` properties with `JsonPropertyName` attributes for JSON representation. --- .../FileTags => Data/Models}/ListedTagViewModel.cs | 2 +- .../{ViewModels/FileTags => Data/Models}/TagViewModel.cs | 2 +- .../ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) rename src/Files.App/{ViewModels/FileTags => Data/Models}/ListedTagViewModel.cs (96%) rename src/Files.App/{ViewModels/FileTags => Data/Models}/TagViewModel.cs (93%) diff --git a/src/Files.App/ViewModels/FileTags/ListedTagViewModel.cs b/src/Files.App/Data/Models/ListedTagViewModel.cs similarity index 96% rename from src/Files.App/ViewModels/FileTags/ListedTagViewModel.cs rename to src/Files.App/Data/Models/ListedTagViewModel.cs index 3eccbe3ffe96..e5cf0f446246 100644 --- a/src/Files.App/ViewModels/FileTags/ListedTagViewModel.cs +++ b/src/Files.App/Data/Models/ListedTagViewModel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.App.ViewModels.FileTags +namespace Files.App.Data.Models { public sealed class ListedTagViewModel : ObservableObject { diff --git a/src/Files.App/ViewModels/FileTags/TagViewModel.cs b/src/Files.App/Data/Models/TagViewModel.cs similarity index 93% rename from src/Files.App/ViewModels/FileTags/TagViewModel.cs rename to src/Files.App/Data/Models/TagViewModel.cs index 31b66f90d0ad..6b50a087a1e5 100644 --- a/src/Files.App/ViewModels/FileTags/TagViewModel.cs +++ b/src/Files.App/Data/Models/TagViewModel.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; -namespace Files.App.ViewModels.FileTags +namespace Files.App.Data.Models { [Serializable] public sealed partial class TagViewModel : ObservableObject diff --git a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs index 2e0fc3ed722c..bbd548671a80 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs @@ -57,6 +57,10 @@ public void BuildItemContextMenu(object sender, RightTappedRoutedEventArgs e) item.Path is null) return; + // TODO: Add IsFolder to all WidgetCardItems + // NOTE: This is a workaround for file tags isFolder + var fileTagsCardItem = widgetCardItem.DataContext as WidgetFileTagCardItem; + // Create a new Flyout var itemContextMenuFlyout = new CommandBarFlyout() { @@ -73,7 +77,7 @@ public void BuildItemContextMenu(object sender, RightTappedRoutedEventArgs e) OnRightClickedItemChanged(item, itemContextMenuFlyout); // Get items for the flyout - var menuItems = GetItemMenuItems(item, QuickAccessService.IsItemPinned(item.Path)); + var menuItems = GetItemMenuItems(item, QuickAccessService.IsItemPinned(item.Path), fileTagsCardItem is not null && fileTagsCardItem.IsFolder); var (_, secondaryElements) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel(menuItems); // Set max width of the flyout From 8ff734eff1bd6bf831a878d43d66860042c8b913 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Tue, 23 Apr 2024 01:06:39 +0900 Subject: [PATCH 09/14] Revert back --- .../UserControls/Widgets/FileTagsWidgetViewModel.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs index 1a307db6a01c..01b5173c244e 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs @@ -81,6 +81,12 @@ public override List GetItemMenuItems(WidgetCard ShowItem = !isFolder }, new() + { + Text = "SendTo".GetLocalizedResource(), + Tag = "SendToPlaceholder", + ShowItem = !isFolder && UserSettingsService.GeneralSettingsService.ShowSendToMenu + }, + new() { Text = "OpenInNewTab".GetLocalizedResource(), OpacityIcon = new() { OpacityIconStyle = "ColorIconOpenInNewTab" }, From 05f4003322a1898028cdc9bcaff74c864d29696b Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Tue, 23 Apr 2024 01:09:03 +0900 Subject: [PATCH 10/14] Revert back 2 --- src/Files.App/UserControls/Widgets/FileTagsWidget.xaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml index 191a8c8048f5..ae80fa316e1d 100644 --- a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml +++ b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml @@ -35,6 +35,8 @@ From fc373a1540bea18693417b0e94178b3b39da70b5 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Tue, 23 Apr 2024 01:09:54 +0900 Subject: [PATCH 11/14] Fix reference issues --- src/Files.App/Data/Contracts/IFileTagsService.cs | 1 - src/Files.App/GlobalUsings.cs | 1 - src/Files.App/Services/Settings/FileTagsSettingsService.cs | 1 - src/Files.App/Services/Settings/IFileTagsSettingsService.cs | 2 -- src/Files.App/Views/Settings/TagsPage.xaml.cs | 1 - 5 files changed, 6 deletions(-) diff --git a/src/Files.App/Data/Contracts/IFileTagsService.cs b/src/Files.App/Data/Contracts/IFileTagsService.cs index 9fc0d6d14e52..266dc11462d1 100644 --- a/src/Files.App/Data/Contracts/IFileTagsService.cs +++ b/src/Files.App/Data/Contracts/IFileTagsService.cs @@ -1,7 +1,6 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.ViewModels.FileTags; using Files.Core.Storage.LocatableStorage; namespace Files.App.Data.Contracts diff --git a/src/Files.App/GlobalUsings.cs b/src/Files.App/GlobalUsings.cs index 1f411937c348..a6115752c87c 100644 --- a/src/Files.App/GlobalUsings.cs +++ b/src/Files.App/GlobalUsings.cs @@ -64,7 +64,6 @@ global using global::Files.App.ViewModels.Dialogs; global using global::Files.App.ViewModels.Dialogs.AddItemDialog; global using global::Files.App.ViewModels.Dialogs.FileSystemDialog; -global using global::Files.App.ViewModels.FileTags; global using global::Files.App.ViewModels.UserControls.Widgets; global using global::Files.App.Utils.CommandLine; diff --git a/src/Files.App/Services/Settings/FileTagsSettingsService.cs b/src/Files.App/Services/Settings/FileTagsSettingsService.cs index e49cf1035570..a9a3c5a8bbfd 100644 --- a/src/Files.App/Services/Settings/FileTagsSettingsService.cs +++ b/src/Files.App/Services/Settings/FileTagsSettingsService.cs @@ -7,7 +7,6 @@ using Files.App.Utils.Serialization; using Files.App.Utils.Serialization.Implementation; using Files.App.Services.Settings; -using Files.App.ViewModels.FileTags; using Microsoft.Extensions.Logging; using System.IO; using Windows.Storage; diff --git a/src/Files.App/Services/Settings/IFileTagsSettingsService.cs b/src/Files.App/Services/Settings/IFileTagsSettingsService.cs index 355838295230..263b093b86f1 100644 --- a/src/Files.App/Services/Settings/IFileTagsSettingsService.cs +++ b/src/Files.App/Services/Settings/IFileTagsSettingsService.cs @@ -1,8 +1,6 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.ViewModels.FileTags; - namespace Files.App.Services.Settings { public interface IFileTagsSettingsService : IBaseSettingsService diff --git a/src/Files.App/Views/Settings/TagsPage.xaml.cs b/src/Files.App/Views/Settings/TagsPage.xaml.cs index e1f117051319..55d9c49af679 100644 --- a/src/Files.App/Views/Settings/TagsPage.xaml.cs +++ b/src/Files.App/Views/Settings/TagsPage.xaml.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. See the LICENSE. using CommunityToolkit.WinUI.UI; -using Files.App.ViewModels.FileTags; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; From 85a014c56aef3e7f7eac20aa2ac28c79f72cc782 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Wed, 24 Apr 2024 00:40:30 +0900 Subject: [PATCH 12/14] Fix --- src/Files.App/Views/Layouts/DetailsLayoutPage.xaml | 4 ++-- src/Files.App/Views/Settings/TagsPage.xaml | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml index 79b7d41f4dd6..0ef3dd15d599 100644 --- a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml +++ b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml @@ -6,8 +6,8 @@ xmlns:behaviors="using:Files.App.Data.Behaviors" xmlns:converters="using:Files.App.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:datamodels="using:Files.App.Data.Models" xmlns:filesystem="using:Files.App.Utils" - xmlns:filetags="using:Files.App.ViewModels.FileTags" xmlns:helpers="using:Files.App.Helpers" xmlns:i="using:Microsoft.Xaml.Interactivity" xmlns:icore="using:Microsoft.Xaml.Interactions.Core" @@ -1101,7 +1101,7 @@ - + - + From 2762d16b8bbc5133fc25368250b119cd6cf1b388 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Sat, 27 Apr 2024 20:53:21 +0900 Subject: [PATCH 13/14] Fix --- .../ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs index bbd548671a80..2c13dccf129b 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/BaseWidgetViewModel.cs @@ -92,7 +92,7 @@ public void BuildItemContextMenu(object sender, RightTappedRoutedEventArgs e) itemContextMenuFlyout.ShowAt(widgetCardItem, new() { Position = e.GetPosition(widgetCardItem) }); // Load shell menu items - _ = ShellContextFlyoutFactory.LoadShellMenuItemsAsync(_flyoutItemPath, itemContextMenuFlyout); + _ = ShellContextFlyoutFactory.LoadShellMenuItemsAsync(_flyoutItemPath, itemContextMenuFlyout, null, true, true); e.Handled = true; } From 2c6b313125af5042657d70ae90049d7afb60a856 Mon Sep 17 00:00:00 2001 From: hishitetsu <66369541+hishitetsu@users.noreply.github.com> Date: Sun, 28 Apr 2024 23:59:45 +0900 Subject: [PATCH 14/14] Send to placeholder (#15) --- .../Data/Factories/ShellContextFlyoutHelper.cs | 12 ++++++++++-- .../UserControls/Widgets/FileTagsWidgetViewModel.cs | 12 ++++++------ .../Widgets/QuickAccessWidgetViewModel.cs | 6 ++++++ .../Widgets/RecentFilesWidgetViewModel.cs | 6 ++++++ 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs b/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs index ab9ccad22240..38df8e2dc4a2 100644 --- a/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs +++ b/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs @@ -341,10 +341,14 @@ async Task InvokeShellMenuItemAsync(ContextMenu contextMenu, object? tag) OpacityIconStyle = "ColorIconOpenWith", }; var (_, openWithItems) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel([openWithItem]); + var index = 0; var placeholder = itemContextMenuFlyout.SecondaryCommands.FirstOrDefault(x => Equals((x as AppBarButton)?.Tag, "OpenWithPlaceholder")) as AppBarButton; if (placeholder is not null) + { placeholder.Visibility = Visibility.Collapsed; - itemContextMenuFlyout.SecondaryCommands.Insert(0, openWithItems.FirstOrDefault()); + index = itemContextMenuFlyout.SecondaryCommands.IndexOf(placeholder); + } + itemContextMenuFlyout.SecondaryCommands.Insert(index, openWithItems.FirstOrDefault()); } // Add items to sendto dropdown @@ -353,10 +357,14 @@ async Task InvokeShellMenuItemAsync(ContextMenu contextMenu, object? tag) await sendToItem.LoadSubMenuAction(); var (_, sendToItems) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel([sendToItem]); + var index = 1; var placeholder = itemContextMenuFlyout.SecondaryCommands.FirstOrDefault(x => Equals((x as AppBarButton)?.Tag, "SendToPlaceholder")) as AppBarButton; if (placeholder is not null) + { placeholder.Visibility = Visibility.Collapsed; - itemContextMenuFlyout.SecondaryCommands.Insert(1, sendToItems.FirstOrDefault()); + index = itemContextMenuFlyout.SecondaryCommands.IndexOf(placeholder); + } + itemContextMenuFlyout.SecondaryCommands.Insert(index, sendToItems.FirstOrDefault()); } // Add items to shell submenu diff --git a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs index 01b5173c244e..2cba6f8ad70b 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs @@ -81,12 +81,6 @@ public override List GetItemMenuItems(WidgetCard ShowItem = !isFolder }, new() - { - Text = "SendTo".GetLocalizedResource(), - Tag = "SendToPlaceholder", - ShowItem = !isFolder && UserSettingsService.GeneralSettingsService.ShowSendToMenu - }, - new() { Text = "OpenInNewTab".GetLocalizedResource(), OpacityIcon = new() { OpacityIconStyle = "ColorIconOpenInNewTab" }, @@ -134,6 +128,12 @@ public override List GetItemMenuItems(WidgetCard ShowItem = isPinned && isFolder }, new() + { + Text = "SendTo".GetLocalizedResource(), + Tag = "SendToPlaceholder", + ShowItem = UserSettingsService.GeneralSettingsService.ShowSendToMenu + }, + new() { Text = "Properties".GetLocalizedResource(), OpacityIcon = new() { OpacityIconStyle = "ColorIconProperties" }, diff --git a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs index f6d6fef62771..5b4674e9ba3c 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs @@ -107,6 +107,12 @@ public override List GetItemMenuItems(WidgetCard ShowItem = isPinned }, new() + { + Text = "SendTo".GetLocalizedResource(), + Tag = "SendToPlaceholder", + ShowItem = UserSettingsService.GeneralSettingsService.ShowSendToMenu + }, + new() { Text = "Properties".GetLocalizedResource(), OpacityIcon = new() { OpacityIconStyle = "ColorIconProperties" }, diff --git a/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs index df586021b38c..d2cee3e2e0a4 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs @@ -101,6 +101,12 @@ public override List GetItemMenuItems(WidgetCard CommandParameter = item }, new() + { + Text = "SendTo".GetLocalizedResource(), + Tag = "SendToPlaceholder", + ShowItem = UserSettingsService.GeneralSettingsService.ShowSendToMenu + }, + new() { Text = "Properties".GetLocalizedResource(), OpacityIcon = new() { OpacityIconStyle = "ColorIconProperties" },