From ada8ff3d0af7cbd37f0206b0ed28c4b3c821fec9 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Sat, 9 Sep 2023 22:47:31 +0900 Subject: [PATCH 1/3] Move viewmodel classes --- src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs | 2 +- src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs | 1 + src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs | 2 +- src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs | 2 +- src/Files.App/UserControls/Widgets/WidgetsListControl.xaml.cs | 2 +- src/Files.App/Utils/Widgets/WidgetsHelpers.cs | 2 +- src/Files.App/ViewModels/HomeViewModel.cs | 2 +- .../{ => UserControls}/Widgets/FileTagsContainerViewModel.cs | 0 .../{ => UserControls}/Widgets/FileTagsItemViewModel.cs | 0 .../{ => UserControls}/Widgets/FileTagsWidgetViewModel.cs | 3 ++- .../{ => UserControls}/Widgets/ICustomWidgetItemModel.cs | 2 +- .../ViewModels/{ => UserControls}/Widgets/IWidgetItemModel.cs | 2 +- .../Widgets/WidgetsListControlItemViewModel.cs | 4 ++-- .../{ => UserControls}/Widgets/WidgetsListControlViewModel.cs | 4 ++-- 14 files changed, 15 insertions(+), 13 deletions(-) rename src/Files.App/ViewModels/{ => UserControls}/Widgets/FileTagsContainerViewModel.cs (100%) rename src/Files.App/ViewModels/{ => UserControls}/Widgets/FileTagsItemViewModel.cs (100%) rename src/Files.App/ViewModels/{ => UserControls}/Widgets/FileTagsWidgetViewModel.cs (92%) rename src/Files.App/ViewModels/{ => UserControls}/Widgets/ICustomWidgetItemModel.cs (84%) rename src/Files.App/ViewModels/{ => UserControls}/Widgets/IWidgetItemModel.cs (89%) rename src/Files.App/ViewModels/{ => UserControls}/Widgets/WidgetsListControlItemViewModel.cs (96%) rename src/Files.App/ViewModels/{ => UserControls}/Widgets/WidgetsListControlViewModel.cs (94%) diff --git a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs index 60a18fd95fa4..e63b1d42e59e 100644 --- a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs @@ -6,7 +6,7 @@ using CommunityToolkit.WinUI; using Files.App.Data.Items; using Files.App.Utils.Shell; -using Files.App.ViewModels.Widgets; +using Files.App.ViewModels.UserControls.Widgets; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; diff --git a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs index 5397503b3566..faee812f3b14 100644 --- a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.Helpers.ContextFlyouts; +using Files.App.ViewModels.UserControls.Widgets; using Files.App.ViewModels.Widgets; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs index 13fdd9c52eb9..5e733ff8aa0f 100644 --- a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs @@ -1,6 +1,6 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.ViewModels.Widgets; +using Files.App.ViewModels.UserControls.Widgets; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; diff --git a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs index 72a86be3110e..1b6995a488b6 100644 --- a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.Helpers.ContextFlyouts; -using Files.App.ViewModels.Widgets; +using Files.App.ViewModels.UserControls.Widgets; using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/UserControls/Widgets/WidgetsListControl.xaml.cs b/src/Files.App/UserControls/Widgets/WidgetsListControl.xaml.cs index d9c1795616f0..f02f608d33ef 100644 --- a/src/Files.App/UserControls/Widgets/WidgetsListControl.xaml.cs +++ b/src/Files.App/UserControls/Widgets/WidgetsListControl.xaml.cs @@ -1,7 +1,7 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.ViewModels.Widgets; +using Files.App.ViewModels.UserControls.Widgets; using Microsoft.UI.Xaml.Controls; using System; diff --git a/src/Files.App/Utils/Widgets/WidgetsHelpers.cs b/src/Files.App/Utils/Widgets/WidgetsHelpers.cs index afb7a6ba44f5..ac90049c6795 100644 --- a/src/Files.App/Utils/Widgets/WidgetsHelpers.cs +++ b/src/Files.App/Utils/Widgets/WidgetsHelpers.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.UserControls.Widgets; -using Files.App.ViewModels.Widgets; +using Files.App.ViewModels.UserControls.Widgets; using Files.Core.Services.Settings; using System.Collections.Generic; diff --git a/src/Files.App/ViewModels/HomeViewModel.cs b/src/Files.App/ViewModels/HomeViewModel.cs index fcfa77d42deb..651a3122393c 100644 --- a/src/Files.App/ViewModels/HomeViewModel.cs +++ b/src/Files.App/ViewModels/HomeViewModel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.ViewModels.Widgets; +using Files.App.ViewModels.UserControls.Widgets; using Microsoft.UI.Xaml; using System.Windows.Input; diff --git a/src/Files.App/ViewModels/Widgets/FileTagsContainerViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsContainerViewModel.cs similarity index 100% rename from src/Files.App/ViewModels/Widgets/FileTagsContainerViewModel.cs rename to src/Files.App/ViewModels/UserControls/Widgets/FileTagsContainerViewModel.cs diff --git a/src/Files.App/ViewModels/Widgets/FileTagsItemViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsItemViewModel.cs similarity index 100% rename from src/Files.App/ViewModels/Widgets/FileTagsItemViewModel.cs rename to src/Files.App/ViewModels/UserControls/Widgets/FileTagsItemViewModel.cs diff --git a/src/Files.App/ViewModels/Widgets/FileTagsWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs similarity index 92% rename from src/Files.App/ViewModels/Widgets/FileTagsWidgetViewModel.cs rename to src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs index 8c3044885e2d..2518a10a3035 100644 --- a/src/Files.App/ViewModels/Widgets/FileTagsWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs @@ -1,10 +1,11 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.ViewModels.Widgets; using Files.Core.Services; using Files.Shared.Utils; -namespace Files.App.ViewModels.Widgets +namespace Files.App.ViewModels.UserControls.Widgets { public sealed partial class FileTagsWidgetViewModel : ObservableObject, IAsyncInitialize { diff --git a/src/Files.App/ViewModels/Widgets/ICustomWidgetItemModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/ICustomWidgetItemModel.cs similarity index 84% rename from src/Files.App/ViewModels/Widgets/ICustomWidgetItemModel.cs rename to src/Files.App/ViewModels/UserControls/Widgets/ICustomWidgetItemModel.cs index f1ac4141c3ba..9c2d40e30841 100644 --- a/src/Files.App/ViewModels/Widgets/ICustomWidgetItemModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/ICustomWidgetItemModel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.App.ViewModels.Widgets +namespace Files.App.ViewModels.UserControls.Widgets { /// /// This interface is used to mark widgets that are not standard to Files i.e. custom widgets diff --git a/src/Files.App/ViewModels/Widgets/IWidgetItemModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/IWidgetItemModel.cs similarity index 89% rename from src/Files.App/ViewModels/Widgets/IWidgetItemModel.cs rename to src/Files.App/ViewModels/UserControls/Widgets/IWidgetItemModel.cs index c3a54995ffc6..52e750712635 100644 --- a/src/Files.App/ViewModels/Widgets/IWidgetItemModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/IWidgetItemModel.cs @@ -3,7 +3,7 @@ using Microsoft.UI.Xaml.Controls; -namespace Files.App.ViewModels.Widgets +namespace Files.App.ViewModels.UserControls.Widgets { public interface IWidgetItemModel : IDisposable { diff --git a/src/Files.App/ViewModels/Widgets/WidgetsListControlItemViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/WidgetsListControlItemViewModel.cs similarity index 96% rename from src/Files.App/ViewModels/Widgets/WidgetsListControlItemViewModel.cs rename to src/Files.App/ViewModels/UserControls/Widgets/WidgetsListControlItemViewModel.cs index d459c5917967..91f0a8fb611e 100644 --- a/src/Files.App/ViewModels/Widgets/WidgetsListControlItemViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/WidgetsListControlItemViewModel.cs @@ -3,7 +3,7 @@ using Microsoft.UI.Xaml.Controls; -namespace Files.App.ViewModels.Widgets +namespace Files.App.ViewModels.UserControls.Widgets { public class WidgetsListControlItemViewModel : ObservableObject, IDisposable { @@ -49,7 +49,7 @@ public bool ShowMenuFlyout { get => WidgetItemModel.ShowMenuFlyout; } - + public MenuFlyoutItem MenuFlyoutItem { get => WidgetItemModel.MenuFlyoutItem; diff --git a/src/Files.App/ViewModels/Widgets/WidgetsListControlViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/WidgetsListControlViewModel.cs similarity index 94% rename from src/Files.App/ViewModels/Widgets/WidgetsListControlViewModel.cs rename to src/Files.App/ViewModels/UserControls/Widgets/WidgetsListControlViewModel.cs index 78b0ff18c0d8..3459f19531cb 100644 --- a/src/Files.App/ViewModels/Widgets/WidgetsListControlViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/WidgetsListControlViewModel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.App.ViewModels.Widgets +namespace Files.App.ViewModels.UserControls.Widgets { public class WidgetsListControlViewModel : ObservableObject, IDisposable { @@ -55,7 +55,7 @@ public bool InsertWidget(WidgetsListControlItemViewModel widgetModel, int atInde public bool CanAddWidget(string widgetName) { - return !(Widgets.Any((item) => item.WidgetItemModel.WidgetName == widgetName)); + return !Widgets.Any((item) => item.WidgetItemModel.WidgetName == widgetName); } public void RemoveWidgetAt(int index) From 7f1b2ae667fb53c467f8f6914e984409ef09a717 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Sun, 10 Sep 2023 20:23:46 +0900 Subject: [PATCH 2/3] Refactor started --- src/Files.App/App.xaml.cs | 1 + .../Data/Contexts/Tags/TagsContext.cs | 1 - .../EventArguments/QuickAccessEventArgs.cs | 48 ++ .../Data/Items/Widgets/DriveCardItem.cs | 46 ++ .../Widgets/FileTagsContainerViewModel.cs | 2 +- .../Items}/Widgets/FileTagsItemViewModel.cs | 2 +- .../Data/Items/Widgets/FolderCardItem.cs | 58 ++ .../Items}/Widgets/IWidgetCardItem.cs | 2 +- .../Items}/Widgets/IWidgetItemModel.cs | 2 +- .../Items}/Widgets/WidgetCardItem.cs | 2 +- .../WidgetsListControlItemViewModel.cs | 17 +- .../UserControls/Widgets/DrivesWidget.xaml | 23 +- .../UserControls/Widgets/DrivesWidget.xaml.cs | 49 +- .../UserControls/Widgets/FileTagsWidget.xaml | 29 +- .../Widgets/FileTagsWidget.xaml.cs | 6 +- .../Widgets/QuickAccessWidget.xaml | 25 +- .../Widgets/QuickAccessWidget.xaml.cs | 82 +-- .../Widgets/RecentFilesWidget.xaml | 3 +- .../Widgets/RecentFilesWidget.xaml.cs | 4 +- .../Widgets/WidgetsListControl.xaml | 510 ------------------ .../Widgets/WidgetsListControl.xaml.cs | 26 - src/Files.App/Utils/Widgets/WidgetsHelpers.cs | 24 +- src/Files.App/ViewModels/HomeViewModel.cs | 294 +++++++++- .../Widgets/FileTagsWidgetViewModel.cs | 1 - .../UserControls/Widgets/HomePageWidget.cs | 0 .../Widgets/ICustomWidgetItemModel.cs | 12 - .../Widgets/WidgetsListControlViewModel.cs | 105 ---- src/Files.App/Views/HomePage.xaml | 503 ++++++++++++++++- src/Files.App/Views/HomePage.xaml.cs | 271 ++-------- 29 files changed, 1053 insertions(+), 1095 deletions(-) create mode 100644 src/Files.App/Data/EventArguments/QuickAccessEventArgs.cs create mode 100644 src/Files.App/Data/Items/Widgets/DriveCardItem.cs rename src/Files.App/{ViewModels/UserControls => Data/Items}/Widgets/FileTagsContainerViewModel.cs (98%) rename src/Files.App/{ViewModels/UserControls => Data/Items}/Widgets/FileTagsItemViewModel.cs (96%) create mode 100644 src/Files.App/Data/Items/Widgets/FolderCardItem.cs rename src/Files.App/{UserControls => Data/Items}/Widgets/IWidgetCardItem.cs (88%) rename src/Files.App/{ViewModels/UserControls => Data/Items}/Widgets/IWidgetItemModel.cs (89%) rename src/Files.App/{UserControls => Data/Items}/Widgets/WidgetCardItem.cs (87%) rename src/Files.App/{ViewModels/UserControls => Data/Items}/Widgets/WidgetsListControlItemViewModel.cs (96%) delete mode 100644 src/Files.App/UserControls/Widgets/WidgetsListControl.xaml delete mode 100644 src/Files.App/UserControls/Widgets/WidgetsListControl.xaml.cs rename src/Files.App/{ => ViewModels}/UserControls/Widgets/HomePageWidget.cs (100%) delete mode 100644 src/Files.App/ViewModels/UserControls/Widgets/ICustomWidgetItemModel.cs delete mode 100644 src/Files.App/ViewModels/UserControls/Widgets/WidgetsListControlViewModel.cs diff --git a/src/Files.App/App.xaml.cs b/src/Files.App/App.xaml.cs index 6aabab857d22..8bacc4fe85a0 100644 --- a/src/Files.App/App.xaml.cs +++ b/src/Files.App/App.xaml.cs @@ -150,6 +150,7 @@ private IHost ConfigureHost() .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() ).Build(); } diff --git a/src/Files.App/Data/Contexts/Tags/TagsContext.cs b/src/Files.App/Data/Contexts/Tags/TagsContext.cs index 6564e22a4e16..8cb69203fb3c 100644 --- a/src/Files.App/Data/Contexts/Tags/TagsContext.cs +++ b/src/Files.App/Data/Contexts/Tags/TagsContext.cs @@ -1,7 +1,6 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.ViewModels.Widgets; using System.Collections.Immutable; namespace Files.App.Data.Contexts diff --git a/src/Files.App/Data/EventArguments/QuickAccessEventArgs.cs b/src/Files.App/Data/EventArguments/QuickAccessEventArgs.cs new file mode 100644 index 000000000000..a34137d9a316 --- /dev/null +++ b/src/Files.App/Data/EventArguments/QuickAccessEventArgs.cs @@ -0,0 +1,48 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media.Imaging; +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.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; } + public ShellFileItem[] Items { get; set; } + public bool Add; + public bool Pin = true; + public bool Reset = false; + + public ModifyQuickAccessEventArgs(string[] paths, bool add) + { + Paths = paths; + Add = add; + } + + public ModifyQuickAccessEventArgs(ShellFileItem[] items, bool add) + { + Paths = items.Select(x => x.FilePath).ToArray(); + Items = items; + Add = add; + } + } +} diff --git a/src/Files.App/Data/Items/Widgets/DriveCardItem.cs b/src/Files.App/Data/Items/Widgets/DriveCardItem.cs new file mode 100644 index 000000000000..fd4cdda13928 --- /dev/null +++ b/src/Files.App/Data/Items/Widgets/DriveCardItem.cs @@ -0,0 +1,46 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI.Xaml.Media.Imaging; + +namespace Files.App.Data.Items +{ + public class DriveCardItem : WidgetCardItem, IWidgetCardItem, IComparable + { + private BitmapImage thumbnail; + private byte[] thumbnailData; + + public new DriveItem Item { get; private set; } + public bool HasThumbnail => thumbnail is not null && thumbnailData is not null; + public BitmapImage Thumbnail + { + get => thumbnail; + set => SetProperty(ref thumbnail, value); + } + public DriveCardItem(DriveItem item) + { + Item = item; + Path = item.Path; + } + + public async Task LoadCardThumbnailAsync() + { + // Try load thumbnail using ListView mode + if (thumbnailData is null || thumbnailData.Length == 0) + thumbnailData = await FileThumbnailHelper.LoadIconFromPathAsync(Item.Path, Convert.ToUInt32(Constants.Widgets.WidgetIconSize), Windows.Storage.FileProperties.ThumbnailMode.SingleItem, Windows.Storage.FileProperties.ThumbnailOptions.ResizeThumbnail); + + // Thumbnail is still null, use DriveItem icon (loaded using SingleItem mode) + if (thumbnailData is null || thumbnailData.Length == 0) + { + await Item.LoadThumbnailAsync(); + thumbnailData = Item.IconData; + } + + // Thumbnail data is valid, set the item icon + if (thumbnailData is not null && thumbnailData.Length > 0) + Thumbnail = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => thumbnailData.ToBitmapAsync(Constants.Widgets.WidgetIconSize)); + } + + public int CompareTo(DriveCardItem? other) => Item.Path.CompareTo(other?.Item?.Path); + } +} diff --git a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsContainerViewModel.cs b/src/Files.App/Data/Items/Widgets/FileTagsContainerViewModel.cs similarity index 98% rename from src/Files.App/ViewModels/UserControls/Widgets/FileTagsContainerViewModel.cs rename to src/Files.App/Data/Items/Widgets/FileTagsContainerViewModel.cs index bf2ca509ba3e..fe82b1b70da7 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsContainerViewModel.cs +++ b/src/Files.App/Data/Items/Widgets/FileTagsContainerViewModel.cs @@ -4,7 +4,7 @@ using Files.Core.Storage; using Files.Shared.Utils; -namespace Files.App.ViewModels.Widgets +namespace Files.App.Data.Items { public sealed partial class FileTagsContainerViewModel : ObservableObject, IAsyncInitialize { diff --git a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsItemViewModel.cs b/src/Files.App/Data/Items/Widgets/FileTagsItemViewModel.cs similarity index 96% rename from src/Files.App/ViewModels/UserControls/Widgets/FileTagsItemViewModel.cs rename to src/Files.App/Data/Items/Widgets/FileTagsItemViewModel.cs index 0c3f681efcd4..99a98f927d89 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsItemViewModel.cs +++ b/src/Files.App/Data/Items/Widgets/FileTagsItemViewModel.cs @@ -5,7 +5,7 @@ using Files.Core.Storage; using Files.Core.Storage.Extensions; -namespace Files.App.ViewModels.Widgets +namespace Files.App.Data.Items { public sealed partial class FileTagsItemViewModel : WidgetCardItem { diff --git a/src/Files.App/Data/Items/Widgets/FolderCardItem.cs b/src/Files.App/Data/Items/Widgets/FolderCardItem.cs new file mode 100644 index 000000000000..aab2684215da --- /dev/null +++ b/src/Files.App/Data/Items/Widgets/FolderCardItem.cs @@ -0,0 +1,58 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media.Imaging; +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.Data.Items +{ + public class FolderCardItem : WidgetCardItem, IWidgetCardItem + { + private BitmapImage thumbnail; + private byte[] thumbnailData; + + public string AutomationProperties { get; set; } + public bool HasPath => !string.IsNullOrEmpty(Path); + public bool HasThumbnail => thumbnail is not null && thumbnailData is not null; + public BitmapImage Thumbnail + { + get => thumbnail; + set => SetProperty(ref thumbnail, value); + } + public LocationItem Item { get; private set; } + public string Text { get; set; } + public bool IsPinned { get; set; } + + public FolderCardItem(LocationItem item, string text, bool isPinned) + { + if (!string.IsNullOrWhiteSpace(text)) + { + Text = text; + AutomationProperties = Text; + } + IsPinned = isPinned; + Item = item; + Path = item.Path; + } + + public async Task LoadCardThumbnailAsync() + { + if (thumbnailData is null || thumbnailData.Length == 0) + { + thumbnailData = await FileThumbnailHelper.LoadIconFromPathAsync(Path, Convert.ToUInt32(Constants.Widgets.WidgetIconSize), Windows.Storage.FileProperties.ThumbnailMode.SingleItem, Windows.Storage.FileProperties.ThumbnailOptions.ResizeThumbnail); + } + if (thumbnailData is not null && thumbnailData.Length > 0) + { + Thumbnail = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => thumbnailData.ToBitmapAsync(Constants.Widgets.WidgetIconSize)); + } + } + } +} diff --git a/src/Files.App/UserControls/Widgets/IWidgetCardItem.cs b/src/Files.App/Data/Items/Widgets/IWidgetCardItem.cs similarity index 88% rename from src/Files.App/UserControls/Widgets/IWidgetCardItem.cs rename to src/Files.App/Data/Items/Widgets/IWidgetCardItem.cs index bb6ab0c2abb3..7eabab0cd184 100644 --- a/src/Files.App/UserControls/Widgets/IWidgetCardItem.cs +++ b/src/Files.App/Data/Items/Widgets/IWidgetCardItem.cs @@ -4,7 +4,7 @@ using Microsoft.UI.Xaml.Media.Imaging; using System.Threading.Tasks; -namespace Files.App.UserControls.Widgets +namespace Files.App.Data.Items { public interface IWidgetCardItem { diff --git a/src/Files.App/ViewModels/UserControls/Widgets/IWidgetItemModel.cs b/src/Files.App/Data/Items/Widgets/IWidgetItemModel.cs similarity index 89% rename from src/Files.App/ViewModels/UserControls/Widgets/IWidgetItemModel.cs rename to src/Files.App/Data/Items/Widgets/IWidgetItemModel.cs index 52e750712635..904c3a54b1fa 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/IWidgetItemModel.cs +++ b/src/Files.App/Data/Items/Widgets/IWidgetItemModel.cs @@ -3,7 +3,7 @@ using Microsoft.UI.Xaml.Controls; -namespace Files.App.ViewModels.UserControls.Widgets +namespace Files.App.Data.Items { public interface IWidgetItemModel : IDisposable { diff --git a/src/Files.App/UserControls/Widgets/WidgetCardItem.cs b/src/Files.App/Data/Items/Widgets/WidgetCardItem.cs similarity index 87% rename from src/Files.App/UserControls/Widgets/WidgetCardItem.cs rename to src/Files.App/Data/Items/Widgets/WidgetCardItem.cs index 6bb626189856..7703e34eecb6 100644 --- a/src/Files.App/UserControls/Widgets/WidgetCardItem.cs +++ b/src/Files.App/Data/Items/Widgets/WidgetCardItem.cs @@ -3,7 +3,7 @@ using CommunityToolkit.Mvvm.ComponentModel; -namespace Files.App.UserControls.Widgets +namespace Files.App.Data.Items { public abstract class WidgetCardItem : ObservableObject { diff --git a/src/Files.App/ViewModels/UserControls/Widgets/WidgetsListControlItemViewModel.cs b/src/Files.App/Data/Items/Widgets/WidgetsListControlItemViewModel.cs similarity index 96% rename from src/Files.App/ViewModels/UserControls/Widgets/WidgetsListControlItemViewModel.cs rename to src/Files.App/Data/Items/Widgets/WidgetsListControlItemViewModel.cs index 91f0a8fb611e..75cc7f54bf07 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/WidgetsListControlItemViewModel.cs +++ b/src/Files.App/Data/Items/Widgets/WidgetsListControlItemViewModel.cs @@ -3,12 +3,11 @@ using Microsoft.UI.Xaml.Controls; -namespace Files.App.ViewModels.UserControls.Widgets +namespace Files.App.Data.Items { public class WidgetsListControlItemViewModel : ObservableObject, IDisposable { private readonly Action _expanderValueChangedCallback; - private readonly Func _expanderValueRequestedCallback; private object _WidgetControl; @@ -18,13 +17,6 @@ public object WidgetControl set => SetProperty(ref _WidgetControl, value); } - public WidgetsListControlItemViewModel(object widgetControl, Action expanderValueChangedCallback, Func expanderValueRequestedCallback) - { - WidgetControl = widgetControl; - _expanderValueChangedCallback = expanderValueChangedCallback; - _expanderValueRequestedCallback = expanderValueRequestedCallback; - } - public bool IsExpanded { get => _expanderValueRequestedCallback?.Invoke() ?? true; @@ -55,6 +47,13 @@ public MenuFlyoutItem MenuFlyoutItem get => WidgetItemModel.MenuFlyoutItem; } + public WidgetsListControlItemViewModel(object widgetControl, Action expanderValueChangedCallback, Func expanderValueRequestedCallback) + { + WidgetControl = widgetControl; + _expanderValueChangedCallback = expanderValueChangedCallback; + _expanderValueRequestedCallback = expanderValueRequestedCallback; + } + public void Dispose() { (WidgetControl as IDisposable)?.Dispose(); diff --git a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml index 086bbf5a898d..ae29a37dd8bc 100644 --- a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml +++ b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml @@ -4,6 +4,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:dataitems="using:Files.App.Data.Items" xmlns:helpers="using:Files.App.Helpers" xmlns:local="using:Files.App.UserControls.Widgets" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" @@ -16,18 +17,8 @@ VerticalAlignment="Top" ItemsSource="{x:Bind local:DrivesWidget.ItemsAdded, Mode=OneWay}"> - - - - - + - - - - - - - - - diff --git a/src/Files.App/UserControls/Widgets/WidgetsListControl.xaml.cs b/src/Files.App/UserControls/Widgets/WidgetsListControl.xaml.cs deleted file mode 100644 index f02f608d33ef..000000000000 --- a/src/Files.App/UserControls/Widgets/WidgetsListControl.xaml.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2023 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.ViewModels.UserControls.Widgets; -using Microsoft.UI.Xaml.Controls; -using System; - -namespace Files.App.UserControls.Widgets -{ - public sealed partial class WidgetsListControl : UserControl, IDisposable - { - public WidgetsListControlViewModel ViewModel { get; set; } - - public WidgetsListControl() - { - InitializeComponent(); - - ViewModel = new(); - } - - public void Dispose() - { - ViewModel?.Dispose(); - } - } -} diff --git a/src/Files.App/Utils/Widgets/WidgetsHelpers.cs b/src/Files.App/Utils/Widgets/WidgetsHelpers.cs index ac90049c6795..de128aab8a38 100644 --- a/src/Files.App/Utils/Widgets/WidgetsHelpers.cs +++ b/src/Files.App/Utils/Widgets/WidgetsHelpers.cs @@ -2,15 +2,15 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.UserControls.Widgets; -using Files.App.ViewModels.UserControls.Widgets; -using Files.Core.Services.Settings; -using System.Collections.Generic; namespace Files.App.Helpers { + /// + /// Provides static helper to handle Widgets. + /// public static class WidgetsHelpers { - public static TWidget? TryGetWidget(IGeneralSettingsService generalSettingsService, WidgetsListControlViewModel widgetsViewModel, out bool shouldReload, TWidget? defaultValue = default) where TWidget : IWidgetItemModel, new() + public static TWidget? TryGetWidget(IGeneralSettingsService generalSettingsService, HomeViewModel widgetsViewModel, out bool shouldReload, TWidget? defaultValue = default) where TWidget : IWidgetItemModel, new() { bool canAddWidget = widgetsViewModel.CanAddWidget(typeof(TWidget).Name); bool isWidgetSettingEnabled = TryGetIsWidgetSettingEnabled(generalSettingsService); @@ -41,24 +41,18 @@ public static class WidgetsHelpers public static bool TryGetIsWidgetSettingEnabled(IGeneralSettingsService generalSettingsService) where TWidget : IWidgetItemModel { if (typeof(TWidget) == typeof(QuickAccessWidget)) - { return generalSettingsService.ShowQuickAccessWidget; - } + if (typeof(TWidget) == typeof(DrivesWidget)) - { return generalSettingsService.ShowDrivesWidget; - } + if (typeof(TWidget) == typeof(FileTagsWidget)) - { return generalSettingsService.ShowFileTagsWidget; - } + if (typeof(TWidget) == typeof(RecentFilesWidget)) - { return generalSettingsService.ShowRecentFilesWidget; - } - // A custom widget it is - TWidget implements ICustomWidgetItemModel - return typeof(ICustomWidgetItemModel).IsAssignableFrom(typeof(TWidget)); // Return true for custom widgets - they're always enabled + return false; } } -} \ No newline at end of file +} diff --git a/src/Files.App/ViewModels/HomeViewModel.cs b/src/Files.App/ViewModels/HomeViewModel.cs index 651a3122393c..fd4e8d074f63 100644 --- a/src/Files.App/ViewModels/HomeViewModel.cs +++ b/src/Files.App/ViewModels/HomeViewModel.cs @@ -1,34 +1,216 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.ViewModels.UserControls.Widgets; using Microsoft.UI.Xaml; using System.Windows.Input; +using Files.App.Dialogs; +using Files.App.UserControls.Widgets; +using System.IO; +using System.Runtime.InteropServices; +using Windows.Storage; namespace Files.App.ViewModels { + /// + /// Represents ViewModel for . + /// public class HomeViewModel : ObservableObject, IDisposable { - private readonly WidgetsListControlViewModel widgetsViewModel; + private readonly IUserSettingsService _userSettingsService = Ioc.Default.GetRequiredService(); - private IShellPage associatedInstance; + private QuickAccessWidget quickAccessWidget; + private DrivesWidget drivesWidget; + private FileTagsWidget fileTagsWidget; + private RecentFilesWidget recentFilesWidget; + + public IShellPage AppInstance { get; set; } + + public FolderSettingsViewModel FolderSettings + => AppInstance?.InstanceViewModel.FolderSettings; + + public ObservableCollection Widgets { get; private set; } = new(); public event EventHandler YourHomeLoadedInvoked; public ICommand YourHomeLoadedCommand { get; private set; } - public HomeViewModel(WidgetsListControlViewModel widgetsViewModel, IShellPage associatedInstance) + public HomeViewModel() { - this.widgetsViewModel = widgetsViewModel; - this.associatedInstance = associatedInstance; + YourHomeLoadedInvoked += ViewModel_YourHomeLoadedInvoked; + WidgetListRefreshRequestedInvoked += ViewModel_WidgetListRefreshRequestedInvoked; + + // Hook events + AppInstance.ToolbarViewModel.RefreshRequested -= ToolbarViewModel_RefreshRequested; + AppInstance.ToolbarViewModel.RefreshRequested += ToolbarViewModel_RefreshRequested; // Create commands YourHomeLoadedCommand = new RelayCommand(YourHomeLoaded); } - public void ChangeAppInstance(IShellPage associatedInstance) + private void ViewModel_WidgetListRefreshRequestedInvoked(object? sender, EventArgs e) + { + ReloadWidgets(); + } + + public void ReloadWidgets() + { + quickAccessWidget = WidgetsHelpers.TryGetWidget(_userSettingsService.GeneralSettingsService, this, out bool shouldReloadQuickAccessWidget, quickAccessWidget); + drivesWidget = WidgetsHelpers.TryGetWidget(_userSettingsService.GeneralSettingsService, this, out bool shouldReloadDrivesWidget, drivesWidget); + fileTagsWidget = WidgetsHelpers.TryGetWidget(_userSettingsService.GeneralSettingsService, this, out bool shouldReloadFileTags, fileTagsWidget); + recentFilesWidget = WidgetsHelpers.TryGetWidget(_userSettingsService.GeneralSettingsService, this, out bool shouldReloadRecentFiles, recentFilesWidget); + + // Reload QuickAccessWidget + if (shouldReloadQuickAccessWidget && quickAccessWidget is not null) + { + 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) + { + 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) + { + 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) + { + 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; + } + } + + private void ViewModel_YourHomeLoadedInvoked(object? sender, RoutedEventArgs e) + { + // NOTE: We must change the AppInstance because only now it has loaded and not null + ChangeAppInstance(AppInstance); + + 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); + } + else + { + AppInstance.NavigateWithArguments( + FolderSettings.GetLayoutType(e.ItemPath), + new() + { + NavPathParam = e.ItemPath + }); + } + } + catch (UnauthorizedAccessException) + { + DynamicDialog dialog = DynamicDialogFactory.GetFor_ConsentDialog(); + 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(), + }; + + 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) { - this.associatedInstance = associatedInstance; + AppInstance.ToolbarViewModel.CanRefresh = false; + await Task.WhenAll(Widgets.Select(w => w.WidgetItemModel.RefreshWidget())); + AppInstance.ToolbarViewModel.CanRefresh = true; + } + + public void ChangeAppInstance(IShellPage AppInstance) + { + this.AppInstance = AppInstance; } private void YourHomeLoaded(RoutedEventArgs e) @@ -36,9 +218,103 @@ private void YourHomeLoaded(RoutedEventArgs e) YourHomeLoadedInvoked?.Invoke(this, e); } + public event EventHandler WidgetListRefreshRequestedInvoked; + + public void RefreshWidgetList() + { + for (int i = 0; i < Widgets.Count; i++) + { + if (!Widgets[i].WidgetItemModel.IsWidgetSettingEnabled) + { + RemoveWidgetAt(i); + } + } + + WidgetListRefreshRequestedInvoked?.Invoke(this, EventArgs.Empty); + } + + public bool AddWidget(WidgetsListControlItemViewModel widgetModel) + { + return InsertWidget(widgetModel, Widgets.Count + 1); + } + + public bool InsertWidget(WidgetsListControlItemViewModel widgetModel, int atIndex) + { + // The widget must not be null and must implement IWidgetItemModel + if (widgetModel.WidgetItemModel is not IWidgetItemModel widgetItemModel) + { + return false; + } + + // Don't add existing ones! + if (!CanAddWidget(widgetItemModel.WidgetName)) + { + return false; + } + + if (atIndex > Widgets.Count) + { + Widgets.Add(widgetModel); + } + else + { + Widgets.Insert(atIndex, widgetModel); + } + + return true; + } + + public bool CanAddWidget(string widgetName) + { + return !Widgets.Any((item) => item.WidgetItemModel.WidgetName == widgetName); + } + + public void RemoveWidgetAt(int index) + { + if (index < 0) + { + return; + } + + Widgets[index].Dispose(); + Widgets.RemoveAt(index); + } + + public void RemoveWidget() where TWidget : IWidgetItemModel + { + int indexToRemove = -1; + + for (int i = 0; i < Widgets.Count; i++) + { + if (typeof(TWidget).IsAssignableFrom(Widgets[i].WidgetControl.GetType())) + { + // Found matching types + indexToRemove = i; + break; + } + } + + RemoveWidgetAt(indexToRemove); + } + + public void ReorderWidget(WidgetsListControlItemViewModel widgetModel, int place) + { + int widgetIndex = Widgets.IndexOf(widgetModel); + Widgets.Move(widgetIndex, place); + } + public void Dispose() { - widgetsViewModel?.Dispose(); + YourHomeLoadedInvoked -= ViewModel_YourHomeLoadedInvoked; + WidgetListRefreshRequestedInvoked -= ViewModel_WidgetListRefreshRequestedInvoked; + AppInstance.ToolbarViewModel.RefreshRequested -= ToolbarViewModel_RefreshRequested; + + for (int i = 0; i < Widgets.Count; i++) + { + Widgets[i].Dispose(); + } + + Widgets.Clear(); } } } diff --git a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs index 2518a10a3035..9b1f801f31c8 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/FileTagsWidgetViewModel.cs @@ -1,7 +1,6 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.ViewModels.Widgets; using Files.Core.Services; using Files.Shared.Utils; diff --git a/src/Files.App/UserControls/Widgets/HomePageWidget.cs b/src/Files.App/ViewModels/UserControls/Widgets/HomePageWidget.cs similarity index 100% rename from src/Files.App/UserControls/Widgets/HomePageWidget.cs rename to src/Files.App/ViewModels/UserControls/Widgets/HomePageWidget.cs diff --git a/src/Files.App/ViewModels/UserControls/Widgets/ICustomWidgetItemModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/ICustomWidgetItemModel.cs deleted file mode 100644 index 9c2d40e30841..000000000000 --- a/src/Files.App/ViewModels/UserControls/Widgets/ICustomWidgetItemModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2023 Files Community -// Licensed under the MIT License. See the LICENSE. - -namespace Files.App.ViewModels.UserControls.Widgets -{ - /// - /// This interface is used to mark widgets that are not standard to Files i.e. custom widgets - /// - public interface ICustomWidgetItemModel : IWidgetItemModel - { - } -} diff --git a/src/Files.App/ViewModels/UserControls/Widgets/WidgetsListControlViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/WidgetsListControlViewModel.cs deleted file mode 100644 index 3459f19531cb..000000000000 --- a/src/Files.App/ViewModels/UserControls/Widgets/WidgetsListControlViewModel.cs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) 2023 Files Community -// Licensed under the MIT License. See the LICENSE. - -namespace Files.App.ViewModels.UserControls.Widgets -{ - public class WidgetsListControlViewModel : ObservableObject, IDisposable - { - public event EventHandler WidgetListRefreshRequestedInvoked; - - public ObservableCollection Widgets { get; private set; } = new ObservableCollection(); - - public void RefreshWidgetList() - { - for (int i = 0; i < Widgets.Count; i++) - { - if (!Widgets[i].WidgetItemModel.IsWidgetSettingEnabled) - { - RemoveWidgetAt(i); - } - } - - WidgetListRefreshRequestedInvoked?.Invoke(this, EventArgs.Empty); - } - - public bool AddWidget(WidgetsListControlItemViewModel widgetModel) - { - return InsertWidget(widgetModel, Widgets.Count + 1); - } - - public bool InsertWidget(WidgetsListControlItemViewModel widgetModel, int atIndex) - { - // The widget must not be null and must implement IWidgetItemModel - if (widgetModel.WidgetItemModel is not IWidgetItemModel widgetItemModel) - { - return false; - } - - // Don't add existing ones! - if (!CanAddWidget(widgetItemModel.WidgetName)) - { - return false; - } - - if (atIndex > Widgets.Count) - { - Widgets.Add(widgetModel); - } - else - { - Widgets.Insert(atIndex, widgetModel); - } - - return true; - } - - public bool CanAddWidget(string widgetName) - { - return !Widgets.Any((item) => item.WidgetItemModel.WidgetName == widgetName); - } - - public void RemoveWidgetAt(int index) - { - if (index < 0) - { - return; - } - - Widgets[index].Dispose(); - Widgets.RemoveAt(index); - } - - public void RemoveWidget() where TWidget : IWidgetItemModel - { - int indexToRemove = -1; - - for (int i = 0; i < Widgets.Count; i++) - { - if (typeof(TWidget).IsAssignableFrom(Widgets[i].WidgetControl.GetType())) - { - // Found matching types - indexToRemove = i; - break; - } - } - - RemoveWidgetAt(indexToRemove); - } - - public void ReorderWidget(WidgetsListControlItemViewModel widgetModel, int place) - { - int widgetIndex = Widgets.IndexOf(widgetModel); - Widgets.Move(widgetIndex, place); - } - - public void Dispose() - { - for (int i = 0; i < Widgets.Count; i++) - { - Widgets[i].Dispose(); - } - - Widgets.Clear(); - } - } -} diff --git a/src/Files.App/Views/HomePage.xaml b/src/Files.App/Views/HomePage.xaml index ffa403c692ad..b7a52cb2b33d 100644 --- a/src/Files.App/Views/HomePage.xaml +++ b/src/Files.App/Views/HomePage.xaml @@ -3,6 +3,7 @@ x:Class="Files.App.Views.HomePage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:i="using:Microsoft.Xaml.Interactivity" xmlns:icore="using:Microsoft.Xaml.Interactions.Core" @@ -17,12 +18,504 @@ - + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/Views/HomePage.xaml.cs b/src/Files.App/Views/HomePage.xaml.cs index c6a994859c6b..2b7462bdb858 100644 --- a/src/Files.App/Views/HomePage.xaml.cs +++ b/src/Files.App/Views/HomePage.xaml.cs @@ -1,264 +1,77 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.Dialogs; -using Files.App.UserControls.Widgets; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Navigation; -using System.IO; -using System.Runtime.InteropServices; -using Windows.Storage; namespace Files.App.Views { + /// + /// Represents for Files App Home page. + /// public sealed partial class HomePage : Page, IDisposable { - private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); + private IShellPage? _appInstance; - private IShellPage AppInstance = null; - - public FolderSettingsViewModel FolderSettings => AppInstance?.InstanceViewModel.FolderSettings; - - private QuickAccessWidget quickAccessWidget; - private DrivesWidget drivesWidget; - private FileTagsWidget fileTagsWidget; - private RecentFilesWidget recentFilesWidget; - - public HomeViewModel ViewModel { get; set; } + public HomeViewModel ViewModel; public HomePage() { InitializeComponent(); - ViewModel = new(Widgets.ViewModel, AppInstance); - - ViewModel.YourHomeLoadedInvoked += ViewModel_YourHomeLoadedInvoked; - Widgets.ViewModel.WidgetListRefreshRequestedInvoked += ViewModel_WidgetListRefreshRequestedInvoked; - } - - protected override void OnNavigatedFrom(NavigationEventArgs e) - { - Dispose(); - - base.OnNavigatedFrom(e); - } - - public void RefreshWidgetList() => Widgets.ViewModel.RefreshWidgetList(); - - private void ViewModel_WidgetListRefreshRequestedInvoked(object? sender, EventArgs e) - { - ReloadWidgets(); - } - - public void ReloadWidgets() - { - quickAccessWidget = WidgetsHelpers.TryGetWidget(UserSettingsService.GeneralSettingsService, Widgets.ViewModel, out bool shouldReloadQuickAccessWidget, quickAccessWidget); - drivesWidget = WidgetsHelpers.TryGetWidget(UserSettingsService.GeneralSettingsService, Widgets.ViewModel, out bool shouldReloadDrivesWidget, drivesWidget); - fileTagsWidget = WidgetsHelpers.TryGetWidget(UserSettingsService.GeneralSettingsService, Widgets.ViewModel, out bool shouldReloadFileTags, fileTagsWidget); - recentFilesWidget = WidgetsHelpers.TryGetWidget(UserSettingsService.GeneralSettingsService, Widgets.ViewModel, out bool shouldReloadRecentFiles, recentFilesWidget); - - // Reload QuickAccessWidget - if (shouldReloadQuickAccessWidget && quickAccessWidget is not null) - { - Widgets.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) - { - Widgets.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) - { - Widgets.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) - { - Widgets.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; - } - } - - private void ViewModel_YourHomeLoadedInvoked(object? sender, Microsoft.UI.Xaml.RoutedEventArgs e) - { - // NOTE: We must change the associatedInstance because only now it has loaded and not null - ViewModel.ChangeAppInstance(AppInstance); - - 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); - } - else - { - AppInstance.NavigateWithArguments( - FolderSettings.GetLayoutType(e.ItemPath), - new() - { - NavPathParam = e.ItemPath - }); - } - } - catch (UnauthorizedAccessException) - { - DynamicDialog dialog = DynamicDialogFactory.GetFor_ConsentDialog(); - 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(), - }; - - 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 - }); + ViewModel = Ioc.Default.GetRequiredService(); } protected override async void OnNavigatedTo(NavigationEventArgs eventArgs) { - var parameters = eventArgs.Parameter as NavigationArguments; - - AppInstance = parameters.AssociatedTabInstance; - AppInstance.InstanceViewModel.IsPageTypeNotHome = false; - AppInstance.InstanceViewModel.IsPageTypeSearchResults = false; - AppInstance.InstanceViewModel.IsPageTypeMtpDevice = false; - AppInstance.InstanceViewModel.IsPageTypeRecycleBin = false; - AppInstance.InstanceViewModel.IsPageTypeCloudDrive = false; - AppInstance.InstanceViewModel.IsPageTypeFtp = false; - AppInstance.InstanceViewModel.IsPageTypeZipFolder = false; - AppInstance.InstanceViewModel.IsPageTypeLibrary = false; - AppInstance.InstanceViewModel.GitRepositoryPath = null; - AppInstance.ToolbarViewModel.CanRefresh = true; - AppInstance.ToolbarViewModel.CanGoBack = AppInstance.CanNavigateBackward; - AppInstance.ToolbarViewModel.CanGoForward = AppInstance.CanNavigateForward; - AppInstance.ToolbarViewModel.CanNavigateToParent = false; + if (eventArgs.Parameter is not NavigationArguments parameters || parameters.AssociatedTabInstance is null) + return; + + // Set current shell page instance + ViewModel.AppInstance = parameters.AssociatedTabInstance; + + // Set page type + _appInstance = parameters.AssociatedTabInstance; + _appInstance.InstanceViewModel.IsPageTypeNotHome = false; + _appInstance.InstanceViewModel.IsPageTypeSearchResults = false; + _appInstance.InstanceViewModel.IsPageTypeMtpDevice = false; + _appInstance.InstanceViewModel.IsPageTypeRecycleBin = false; + _appInstance.InstanceViewModel.IsPageTypeCloudDrive = false; + _appInstance.InstanceViewModel.IsPageTypeFtp = false; + _appInstance.InstanceViewModel.IsPageTypeZipFolder = false; + _appInstance.InstanceViewModel.IsPageTypeLibrary = false; + _appInstance.InstanceViewModel.GitRepositoryPath = null; + _appInstance.ToolbarViewModel.CanRefresh = true; + _appInstance.ToolbarViewModel.CanGoBack = _appInstance.CanNavigateBackward; + _appInstance.ToolbarViewModel.CanGoForward = _appInstance.CanNavigateForward; + _appInstance.ToolbarViewModel.CanNavigateToParent = false; + + // Set the working directory empty + await _appInstance.FilesystemViewModel.SetWorkingDirectoryAsync("Home"); + + // Set git info empty + _appInstance.SlimContentPage?.DirectoryPropertiesViewModel.UpdateGitInfo(false, string.Empty, Array.Empty()); + + // Clear breadcrumbs + _appInstance.ToolbarViewModel.PathComponents.Clear(); - AppInstance.ToolbarViewModel.RefreshRequested -= ToolbarViewModel_RefreshRequested; - AppInstance.ToolbarViewModel.RefreshRequested += ToolbarViewModel_RefreshRequested; - - // Set path of working directory empty - await AppInstance.FilesystemViewModel.SetWorkingDirectoryAsync("Home"); - - AppInstance.SlimContentPage?.DirectoryPropertiesViewModel.UpdateGitInfo(false, string.Empty, Array.Empty()); - - // Clear the path UI and replace with Favorites - AppInstance.ToolbarViewModel.PathComponents.Clear(); - string componentLabel = parameters.NavPathParam == "Home" ? "Home".GetLocalizedResource() : parameters.NavPathParam; - string tag = parameters.NavPathParam; var item = new PathBoxItem() { - Title = componentLabel, - Path = tag, + Title = parameters.NavPathParam == "Home" ? "Home".GetLocalizedResource() : parameters.NavPathParam, + Path = parameters.NavPathParam, }; - AppInstance.ToolbarViewModel.PathComponents.Add(item); - base.OnNavigatedTo(eventArgs); + // Set breadcrumbs + _appInstance.ToolbarViewModel.PathComponents.Add(item); } - protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) - { - base.OnNavigatingFrom(e); - - AppInstance.ToolbarViewModel.RefreshRequested -= ToolbarViewModel_RefreshRequested; - } - - private async void ToolbarViewModel_RefreshRequested(object? sender, EventArgs e) + protected override void OnNavigatedFrom(NavigationEventArgs e) { - AppInstance.ToolbarViewModel.CanRefresh = false; - await Task.WhenAll(Widgets.ViewModel.Widgets.Select(w => w.WidgetItemModel.RefreshWidget())); - AppInstance.ToolbarViewModel.CanRefresh = true; + Dispose(); } public void Dispose() { - ViewModel.YourHomeLoadedInvoked -= ViewModel_YourHomeLoadedInvoked; - Widgets.ViewModel.WidgetListRefreshRequestedInvoked -= ViewModel_WidgetListRefreshRequestedInvoked; - AppInstance.ToolbarViewModel.RefreshRequested -= ToolbarViewModel_RefreshRequested; ViewModel?.Dispose(); } } From 5d32a87451995d4c3245f62fb5c0b5924a9620c3 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Mon, 11 Sep 2023 20:51:20 +0900 Subject: [PATCH 3/3] Fix build issues --- src/Files.App/UserControls/Widgets/FileTagsWidget.xaml | 2 +- src/Files.App/Views/HomePage.xaml | 3 ++- src/Files.App/Views/Shells/ModernShellPage.xaml.cs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml index 0ec0f90e46f8..9e1a4dbb86fb 100644 --- a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml +++ b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml @@ -121,7 +121,7 @@ - + - +