diff --git a/src/Files.App.CsWin32/ManualGuid.cs b/src/Files.App.CsWin32/ManualGuid.cs index 8ab59f21ee7f..db12aa36f36f 100644 --- a/src/Files.App.CsWin32/ManualGuid.cs +++ b/src/Files.App.CsWin32/ManualGuid.cs @@ -41,6 +41,9 @@ public static Guid* IID_IStorageProviderStatusUISourceFactory [GuidRVAGen.Guid("00021500-0000-0000-C000-000000000046")] public static partial Guid* IID_IQueryInfo { get; } + + [GuidRVAGen.Guid("000214F9-0000-0000-C000-000000000046")] + public static partial Guid* IID_IShellLinkW { get; } } public static unsafe partial class CLSID diff --git a/src/Files.App.CsWin32/NativeMethods.txt b/src/Files.App.CsWin32/NativeMethods.txt index aee2e82f99b3..ebbd177e25ea 100644 --- a/src/Files.App.CsWin32/NativeMethods.txt +++ b/src/Files.App.CsWin32/NativeMethods.txt @@ -226,3 +226,6 @@ _SICHINTF RoGetAgileReference IQueryInfo QITIPF_FLAGS +GetDiskFreeSpaceEx +GetDriveType +SLGP_FLAGS diff --git a/src/Files.App.Storage/Storables/WindowsStorage/IWindowsFile.cs b/src/Files.App.Storage/Storables/WindowsStorage/IWindowsFile.cs new file mode 100644 index 000000000000..43f30155f907 --- /dev/null +++ b/src/Files.App.Storage/Storables/WindowsStorage/IWindowsFile.cs @@ -0,0 +1,9 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Storage +{ + public interface IWindowsFile : IWindowsStorable, IChildFile + { + } +} diff --git a/src/Files.App.Storage/Storables/WindowsStorage/IWindowsFolder.cs b/src/Files.App.Storage/Storables/WindowsStorage/IWindowsFolder.cs new file mode 100644 index 000000000000..8bdc62b64a14 --- /dev/null +++ b/src/Files.App.Storage/Storables/WindowsStorage/IWindowsFolder.cs @@ -0,0 +1,9 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Storage +{ + public interface IWindowsFolder : IWindowsStorable, IChildFolder + { + } +} diff --git a/src/Files.App.Storage/Storables/WindowsStorage/IWindowsStorable.cs b/src/Files.App.Storage/Storables/WindowsStorage/IWindowsStorable.cs index 421d7a68dddd..4dd82c398487 100644 --- a/src/Files.App.Storage/Storables/WindowsStorage/IWindowsStorable.cs +++ b/src/Files.App.Storage/Storables/WindowsStorage/IWindowsStorable.cs @@ -6,7 +6,7 @@ namespace Files.App.Storage { - public interface IWindowsStorable : IDisposable + public interface IWindowsStorable : IStorableChild, IEquatable, IDisposable { ComPtr ThisPtr { get; } } diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsFile.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsFile.cs index 3ce56f786c2f..728552e06127 100644 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsFile.cs +++ b/src/Files.App.Storage/Storables/WindowsStorage/WindowsFile.cs @@ -8,7 +8,7 @@ namespace Files.App.Storage { [DebuggerDisplay("{" + nameof(ToString) + "()}")] - public sealed class WindowsFile : WindowsStorable, IChildFile + public sealed class WindowsFile : WindowsStorable, IWindowsFile { public WindowsFile(ComPtr nativeObject) { diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsFolder.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsFolder.cs index f4105687184f..e27ffc7a7f10 100644 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsFolder.cs +++ b/src/Files.App.Storage/Storables/WindowsStorage/WindowsFolder.cs @@ -10,7 +10,7 @@ namespace Files.App.Storage { [DebuggerDisplay("{" + nameof(ToString) + "()}")] - public sealed class WindowsFolder : WindowsStorable, IChildFolder + public sealed class WindowsFolder : WindowsStorable, IWindowsFolder { public WindowsFolder(ComPtr nativeObject) { diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorable.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorable.cs index 3fdc51e33389..6dd76debb3e0 100644 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorable.cs +++ b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorable.cs @@ -8,7 +8,7 @@ namespace Files.App.Storage { - public abstract class WindowsStorable : IWindowsStorable, IStorableChild, IEquatable + public abstract class WindowsStorable : IWindowsStorable { public ComPtr ThisPtr { get; protected set; } diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Shell.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Shell.cs index c09b2fc0d103..3cd1f402093f 100644 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Shell.cs +++ b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Shell.cs @@ -143,5 +143,19 @@ public unsafe static HRESULT TryGetShellTooltip(this IWindowsStorable storable, return HRESULT.S_OK; } + + public unsafe static HRESULT TryGetShellLink(this IWindowsStorable storable, out ComPtr pShellLink) + { + pShellLink = default; + + using ComPtr pShellLinkW = default; + HRESULT hr = storable.ThisPtr.Get()->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IShellLinkW, (void**)pShellLinkW.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + pShellLink = pShellLinkW; + + return HRESULT.S_OK; + } } } diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Storage.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Storage.cs index 151cad3fbf3c..252a70d6bf93 100644 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Storage.cs +++ b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Storage.cs @@ -3,6 +3,7 @@ using Windows.Win32; using Windows.Win32.Foundation; +using Windows.Win32.NetworkManagement.WNet; using Windows.Win32.Storage.FileSystem; using Windows.Win32.UI.Shell; @@ -84,5 +85,37 @@ public static bool TryShowFormatDriveDialog(HWND hWnd, uint driveLetterIndex, SH var result = PInvoke.SHFormatDrive(hWnd, driveLetterIndex, id, options); return result is 0xFFFF; } + + public static bool TryGetDriveTotalSpace(this IWindowsStorable storable, out ulong totalSize) + { + ulong ulTotalSize = 0UL; + bool res = PInvoke.GetDiskFreeSpaceEx(storable.GetDisplayName(), null, &ulTotalSize, null); + + totalSize = ulTotalSize; + + return res; + } + + public static bool TryGetDriveFreeSpace(this IWindowsStorable storable, out ulong freeSize) + { + ulong ulFreeSize = 0UL; + bool res = PInvoke.GetDiskFreeSpaceEx(storable.GetDisplayName(), null, null, &ulFreeSize); + + freeSize = ulFreeSize; + + return res; + } + + public static bool TryGetDriveType(this IWindowsStorable storable, out uint driveType) + { + driveType = PInvoke.GetDriveType(storable.GetDisplayName()); + + return driveType is 0U; // DRIVE_UNKNOWN + } + + public static bool TryDisconnectNetworkDrive(this IWindowsStorable storable) + { + return PInvoke.WNetCancelConnection2W(storable.GetDisplayName().TrimEnd('\\'), NET_CONNECT_FLAGS.CONNECT_UPDATE_PROFILE, true) is WIN32_ERROR.NO_ERROR; + } } } diff --git a/src/Files.App/Data/Items/WidgetDriveCardItem.cs b/src/Files.App/Data/Items/WidgetDriveCardItem.cs index c8d41ed31d4a..a596108a3849 100644 --- a/src/Files.App/Data/Items/WidgetDriveCardItem.cs +++ b/src/Files.App/Data/Items/WidgetDriveCardItem.cs @@ -2,50 +2,61 @@ // Licensed under the MIT License. using Microsoft.UI.Xaml.Media.Imaging; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; namespace Files.App.Data.Items { - public sealed partial class WidgetDriveCardItem : WidgetCardItem, IWidgetCardItem, IComparable + public sealed partial class WidgetDriveCardItem : WidgetCardItem, IWidgetCardItem, IDisposable { - private byte[] thumbnailData; + // Properties - public new DriveItem Item { get; private set; } + public required new IWindowsFolder Item { get; set; } - private BitmapImage thumbnail; - public BitmapImage Thumbnail - { - get => thumbnail; - set => SetProperty(ref thumbnail, value); - } + public required string Text { get; set; } + + public bool ShowStorageSense => UsedSize.GigaBytes / TotalSize.GigaBytes >= Constants.Widgets.Drives.LowStorageSpacePercentageThreshold; + + public bool ShowDriveUsage => TotalSize.GigaBytes > 0D; + + public ByteSizeLib.ByteSize TotalSize { get; set; } = default; + + public ByteSizeLib.ByteSize FreeSize { get; set; } = default; + + public ByteSizeLib.ByteSize UsedSize => ByteSizeLib.ByteSize.FromBytes(TotalSize.Bytes - FreeSize.Bytes); + + public string? UsageText => string.Format(Strings.DriveFreeSpaceAndCapacity.GetLocalizedResource(), FreeSize.ToSizeString(), TotalSize.ToSizeString()); + + public required SystemIO.DriveType DriveType { get; set; } - public WidgetDriveCardItem(DriveItem item) + private BitmapImage? _Thumbnail; + public BitmapImage? Thumbnail { get => _Thumbnail; set => SetProperty(ref _Thumbnail, value); } + + // Constructor + + public WidgetDriveCardItem() { - Item = item; - Path = item.Path; } + // Methods + public async Task LoadCardThumbnailAsync() { - var result = await FileThumbnailHelper.GetIconAsync( - Item.Path, - Constants.ShellIconSizes.Large, - true, - IconOptions.ReturnIconOnly | IconOptions.UseCurrentScale); - - if (result is null) - { - using var thumbnail = await DriveHelpers.GetThumbnailAsync(Item.Root); - result ??= await thumbnail.ToByteArrayAsync(); - } - - thumbnailData = result; - - var bitmapImage = await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => thumbnailData.ToBitmapAsync(), Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal); - if (bitmapImage is not null) - Thumbnail = bitmapImage; + if (string.IsNullOrEmpty(Path) || Item is not IWindowsStorable windowsStorable) + return; + + HRESULT hr = windowsStorable.TryGetThumbnail((int)(Constants.ShellIconSizes.Large * App.AppModel.AppWindowDPI), SIIGBF.SIIGBF_ICONONLY, out var rawThumbnailData); + if (hr.Failed || rawThumbnailData is null) + return; + + Thumbnail = await rawThumbnailData.ToBitmapAsync(); } - public int CompareTo(WidgetDriveCardItem? other) - => Item.Path.CompareTo(other?.Item?.Path); + // Disposer + + public void Dispose() + { + Item.Dispose(); + } } } diff --git a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml index a4024853e5c9..bae7144f8127 100644 --- a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml +++ b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml @@ -31,14 +31,14 @@ VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" - AutomationProperties.Name="{x:Bind Item.Text, Mode=OneWay}" + AutomationProperties.Name="{x:Bind Text, Mode=OneWay}" Click="Button_Click" CornerRadius="{StaticResource ControlCornerRadius}" DataContext="{x:Bind}" PointerPressed="Button_PointerPressed" RightTapped="Button_RightTapped" - Tag="{x:Bind Item.Path}" - ToolTipService.ToolTip="{x:Bind Item.Text, Mode=OneWay}"> + Tag="{x:Bind Path}" + ToolTipService.ToolTip="{x:Bind Text, Mode=OneWay}"> @@ -85,10 +85,10 @@ Grid.Column="1" VerticalAlignment="Stretch" VerticalContentAlignment="Stretch" - x:Load="{x:Bind Item.ShowDriveDetails, Mode=OneWay}" + x:Load="{x:Bind ShowDriveUsage, Mode=OneWay}" AutomationProperties.AccessibilityView="Raw" - Maximum="{x:Bind Item.MaxSpace.GigaBytes, Mode=OneWay}" - Value="{x:Bind Item.SpaceUsed.GigaBytes, Mode=OneWay}" /> + Maximum="{x:Bind TotalSize.GigaBytes, Mode=OneWay}" + Value="{x:Bind UsedSize.GigaBytes, Mode=OneWay}" /> diff --git a/src/Files.App/UserControls/Widgets/NetworkLocationsWidget.xaml b/src/Files.App/UserControls/Widgets/NetworkLocationsWidget.xaml index af6080a97546..a622fc711c48 100644 --- a/src/Files.App/UserControls/Widgets/NetworkLocationsWidget.xaml +++ b/src/Files.App/UserControls/Widgets/NetworkLocationsWidget.xaml @@ -58,14 +58,14 @@ VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" - AutomationProperties.Name="{x:Bind Item.Text, Mode=OneWay}" + AutomationProperties.Name="{x:Bind Text, Mode=OneWay}" Click="Button_Click" CornerRadius="{StaticResource ControlCornerRadius}" DataContext="{x:Bind}" PointerPressed="Button_PointerPressed" RightTapped="Button_RightTapped" - Tag="{x:Bind Item.Path}" - ToolTipService.ToolTip="{x:Bind Item.Text, Mode=OneWay}"> + Tag="{x:Bind Path}" + ToolTipService.ToolTip="{x:Bind Text, Mode=OneWay}"> @@ -111,10 +111,10 @@ Grid.Column="1" VerticalAlignment="Stretch" VerticalContentAlignment="Stretch" - x:Load="{x:Bind Item.ShowDriveDetails, Mode=OneWay}" + x:Load="{x:Bind ShowDriveUsage, Mode=OneWay}" AutomationProperties.AccessibilityView="Raw" - Maximum="{x:Bind Item.MaxSpace.GigaBytes, Mode=OneWay}" - Value="{x:Bind Item.SpaceUsed.GigaBytes, Mode=OneWay}" /> + Maximum="{x:Bind TotalSize.GigaBytes, Mode=OneWay}" + Value="{x:Bind UsedSize.GigaBytes, Mode=OneWay}" /> + ToolTipService.ToolTip="{x:Bind UsageText, Mode=OneWay}" /> diff --git a/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs index 5faf80c57720..e0430adb58e6 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/DrivesWidgetViewModel.cs @@ -7,6 +7,8 @@ using System.Windows.Input; using Windows.System; using Windows.UI.Core; +using Windows.Win32; +using Windows.Win32.UI.Shell; namespace Files.App.ViewModels.UserControls.Widgets { @@ -29,37 +31,50 @@ public sealed partial class DrivesWidgetViewModel : BaseWidgetViewModel, IWidget // Commands private ICommand EjectDeviceCommand { get; } = null!; - private ICommand DisconnectNetworkDriveCommand { get; } = null!; // Constructor public DrivesWidgetViewModel() { - Drives_CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - - DrivesViewModel.Drives.CollectionChanged += Drives_CollectionChanged; + Items.CollectionChanged += Items_CollectionChanged; + UserSettingsService.OnSettingChangedEvent += UserSettingsService_OnSettingChangedEvent; PinToSidebarCommand = new AsyncRelayCommand(ExecutePinToSidebarCommand); UnpinFromSidebarCommand = new AsyncRelayCommand(ExecuteUnpinFromSidebarCommand); EjectDeviceCommand = new RelayCommand(ExecuteEjectDeviceCommand); OpenPropertiesCommand = new RelayCommand(ExecuteOpenPropertiesCommand); - DisconnectNetworkDriveCommand = new RelayCommand(ExecuteDisconnectNetworkDriveCommand); - - UserSettingsService.OnSettingChangedEvent += UserSettingsService_OnSettingChangedEvent; } // Methods - private async void UserSettingsService_OnSettingChangedEvent(object? sender, SettingChangedEventArgs e) + public Task RefreshWidgetAsync() { - if (e.SettingName == nameof(UserSettingsService.FoldersSettingsService.SizeUnitFormat)) - await RefreshWidgetAsync(); - } + return MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => + { + foreach (var item in Items) + item.Dispose(); - public async Task RefreshWidgetAsync() - { - var updateTasks = Items.Select(item => item.Item.UpdatePropertiesAsync()); - await Task.WhenAll(updateTasks); + Items.Clear(); + + await foreach (IWindowsFolder folder in HomePageContext.HomeFolder.GetLogicalDrivesAsync(default)) + { + folder.TryGetDriveTotalSpace(out var totalSize); + folder.TryGetDriveFreeSpace(out var freeSize); + folder.TryGetDriveType(out var driveType); + + Items.Insert( + Items.Count, + new() + { + Item = folder, + Text = folder.GetDisplayName(SIGDN.SIGDN_PARENTRELATIVEFORUI), + Path = folder.GetDisplayName(SIGDN.SIGDN_FILESYSPATH), + TotalSize = ByteSizeLib.ByteSize.FromBytes(totalSize), + FreeSize = ByteSizeLib.ByteSize.FromBytes(freeSize), + DriveType = (SystemIO.DriveType)driveType, + }); + } + }); } public async Task NavigateToPath(string path) @@ -84,15 +99,8 @@ public async Task NavigateToPath(string 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; + if (item is not WidgetDriveCardItem driveItem) + return []; return new List() { @@ -129,7 +137,7 @@ public override List GetItemMenuItems(WidgetCard Text = Strings.Eject.GetLocalizedResource(), Command = EjectDeviceCommand, CommandParameter = item, - ShowItem = options?.ShowEjectDevice ?? false + ShowItem = driveItem.DriveType is SystemIO.DriveType.Removable or SystemIO.DriveType.CDRom }, new() { @@ -184,7 +192,7 @@ private void ExecuteEjectDeviceCommand(WidgetDriveCardItem? item) if (item is null) return; - DriveHelpers.EjectDeviceAsync(item.Item.Path); + DriveHelpers.EjectDeviceAsync(item.Path); } private void ExecuteOpenPropertiesCommand(WidgetDriveCardItem? item) @@ -204,43 +212,29 @@ private void ExecuteOpenPropertiesCommand(WidgetDriveCardItem? item) flyout!.Closed += flyoutClosed; } - private void ExecuteDisconnectNetworkDriveCommand(WidgetDriveCardItem? item) - { - if (item is null) - return; - - NetworkService.DisconnectNetworkDrive(item.Item); - } - // Event methods - private async void Drives_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + private async void Items_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { - await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => + if (e.Action is NotifyCollectionChangedAction.Add) { - foreach (DriveItem drive in DrivesViewModel.Drives.ToList().Cast()) - { - if (!Items.Any(x => x.Item == drive) && drive.Type is not DriveType.VirtualDrive and not DriveType.Network) - { - var cardItem = new WidgetDriveCardItem(drive); - Items.AddSorted(cardItem); - - await cardItem.LoadCardThumbnailAsync(); - } - } + foreach (WidgetDriveCardItem cardItem in e.NewItems!) + await cardItem.LoadCardThumbnailAsync(); + } + } - foreach (WidgetDriveCardItem driveCard in Items.ToList()) - { - if (!DrivesViewModel.Drives.Contains(driveCard.Item)) - Items.Remove(driveCard); - } - }); + private async void UserSettingsService_OnSettingChangedEvent(object? sender, SettingChangedEventArgs e) + { + if (e.SettingName == nameof(UserSettingsService.FoldersSettingsService.SizeUnitFormat)) + await RefreshWidgetAsync(); } // Disposer public void Dispose() { + foreach (var item in Items) + item.Dispose(); } } } diff --git a/src/Files.App/ViewModels/UserControls/Widgets/NetworkLocationsWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/NetworkLocationsWidgetViewModel.cs index eb0b71e66414..e767f136d2d8 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/NetworkLocationsWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/NetworkLocationsWidgetViewModel.cs @@ -4,10 +4,13 @@ using Microsoft.UI.Input; using Microsoft.UI.Xaml.Controls; using System.Collections.Specialized; +using System.Runtime.InteropServices; using System.Windows.Input; using Windows.System; using Windows.UI.Core; -using OwlCore.Storage; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.Shell; namespace Files.App.ViewModels.UserControls.Widgets { @@ -41,7 +44,6 @@ public bool IsNoNetworkLocations // Commands - private ICommand EjectDeviceCommand { get; } = null!; private ICommand MapNetworkDriveCommand { get; } = null!; private ICommand DisconnectNetworkDriveCommand { get; } = null!; @@ -49,15 +51,10 @@ public bool IsNoNetworkLocations public NetworkLocationsWidgetViewModel() { - Drives_CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - Shortcuts_CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - - DrivesViewModel.Drives.CollectionChanged += Drives_CollectionChanged; - NetworkService.Shortcuts.CollectionChanged += Shortcuts_CollectionChanged; + Items.CollectionChanged += Items_CollectionChanged; PinToSidebarCommand = new AsyncRelayCommand(ExecutePinToSidebarCommand); UnpinFromSidebarCommand = new AsyncRelayCommand(ExecuteUnpinFromSidebarCommand); - EjectDeviceCommand = new RelayCommand(ExecuteEjectDeviceCommand); OpenPropertiesCommand = new RelayCommand(ExecuteOpenPropertiesCommand); DisconnectNetworkDriveCommand = new RelayCommand(ExecuteDisconnectNetworkDriveCommand); MapNetworkDriveCommand = new AsyncRelayCommand(ExecuteMapNetworkDriveCommand); @@ -65,10 +62,49 @@ public NetworkLocationsWidgetViewModel() // Methods - public async Task RefreshWidgetAsync() + public Task RefreshWidgetAsync() { - var updateTasks = Items.Select(item => item.Item.UpdatePropertiesAsync()); - await Task.WhenAll(updateTasks); + return MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => + { + foreach (var item in Items) + item.Dispose(); + + Items.Clear(); + + await foreach (IWindowsFolder folder in HomePageContext.HomeFolder.GetNetworkLocationsAsync(default)) + { + folder.TryGetShellLink(out ComPtr pShellLink); + string linkTargetPath = string.Empty; + + if (pShellLink.IsNull) + { + folder.GetPropertyValue("System.Link.TargetParsingPath", out linkTargetPath); + } + else + { + unsafe + { + char* pszTargetPath = (char*)NativeMemory.Alloc(1024); + pShellLink.Get()->GetPath(pszTargetPath, 1024, null, (uint)SLGP_FLAGS.SLGP_RAWPATH); + linkTargetPath = new(pszTargetPath); + + NativeMemory.Free(pszTargetPath); + } + } + + Items.Insert( + Items.Count, + new() + { + Item = folder, + Text = folder.GetDisplayName(SIGDN.SIGDN_PARENTRELATIVEFORUI), + Path = linkTargetPath, + DriveType = SystemIO.DriveType.Network + }); + } + + IsNoNetworkLocations = !Items.Any(); + }); } public async Task NavigateToPath(string path) @@ -90,16 +126,6 @@ public async Task NavigateToPath(string 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 ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewTabFromHomeAction) @@ -130,13 +156,6 @@ public override List GetItemMenuItems(WidgetCard CommandParameter = item, ShowItem = isPinned }, - new() - { - Text = Strings.Eject.GetLocalizedResource(), - Command = EjectDeviceCommand, - CommandParameter = item, - ShowItem = options?.ShowEjectDevice ?? false - }, new ContextMenuFlyoutItemViewModelBuilder(CommandManager.FormatDriveFromHome).Build(), new() { @@ -181,24 +200,11 @@ public void DisableWidget() // Command methods - private void ExecuteEjectDeviceCommand(WidgetDriveCardItem? item) - { - if (item is null) - return; - - DriveHelpers.EjectDeviceAsync(item.Item.Path); - } - private Task ExecuteMapNetworkDriveCommand() { return NetworkService.OpenMapNetworkDriveDialogAsync(); } - private void ExecuteFormatDriveCommand(WidgetDriveCardItem? item) - { - Win32Helper.OpenFormatDriveDialog(item?.Path ?? string.Empty); - } - private void ExecuteOpenPropertiesCommand(WidgetDriveCardItem? item) { if (!HomePageContext.IsAnyItemRightClicked || item is null) @@ -221,52 +227,26 @@ private void ExecuteDisconnectNetworkDriveCommand(WidgetDriveCardItem? item) if (item is null) return; - NetworkService.DisconnectNetworkDrive(item.Item); + item.Item.TryDisconnectNetworkDrive(); } - private async Task UpdateItems(ObservableCollection source) - { - await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => - { - IsNoNetworkLocations = false; - - foreach (DriveItem drive in source.ToList().Cast()) - { - if (!Items.Any(x => x.Item == drive) && drive.Type is DriveType.Network) - { - var cardItem = new WidgetDriveCardItem(drive); - Items.AddSorted(cardItem); - - await cardItem.LoadCardThumbnailAsync(); - } - } - - foreach (WidgetDriveCardItem driveCard in Items.ToList()) - { - if (!DrivesViewModel.Drives.Contains(driveCard.Item) && !NetworkService.Shortcuts.Contains(driveCard.Item)) - Items.Remove(driveCard); - } - - IsNoNetworkLocations = !Items.Any(); - }); - } - // Event methods - private async void Drives_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) - { - await UpdateItems(DrivesViewModel.Drives); - } - - private async void Shortcuts_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + private async void Items_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { - await UpdateItems(NetworkService.Shortcuts); + if (e.Action is NotifyCollectionChangedAction.Add) + { + foreach (WidgetDriveCardItem cardItem in e.NewItems!) + await cardItem.LoadCardThumbnailAsync(); + } } // Disposer public void Dispose() { + foreach (var item in Items) + item.Dispose(); } } }