/
githubmirror
/
Files
Обзор
Документация
Войти
/
githubmirror
/
Files
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs
325 строк
11 KB
0x5bfa
Code Quality: Replace raw ABI pointers with ComObject instances for safer disposal (#18684)
16 июл 2026, 18:32
Не верифицирован
16 июл 2026, 18:32
93dfb04
Код
Авторство
О чём код?
// Copyright (c) Files Community // Licensed under the MIT License. using Microsoft.UI.Input; using Microsoft.UI.Xaml.Controls; using System.Collections.Specialized; using Windows.Storage; using Windows.System; using Windows.UI.Core; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.Com; using Windows.Win32.System.WinRT; using Windows.Win32.UI.Shell; namespace Files.App.ViewModels.UserControls.Widgets { /// <summary> /// Represents view model of <see cref="QuickAccessWidget"/>. /// </summary> public sealed partial class QuickAccessWidgetViewModel : BaseWidgetViewModel, IWidgetViewModel { // Properties public ObservableCollection<WidgetFolderCardItem> Items { get; } = []; public string WidgetName => nameof(QuickAccessWidget); public string AutomationProperties => Strings.QuickAccess.GetLocalizedResource(); public string WidgetHeader => Strings.QuickAccess.GetLocalizedResource(); public bool IsWidgetSettingEnabled => UserSettingsService.GeneralSettingsService.ShowQuickAccessWidget; public bool ShowMenuFlyout => false; public MenuFlyoutItem? MenuFlyoutItem => null; // Fields // TODO: Replace with IMutableFolder.GetWatcherAsync() once it gets implemented in IWindowsStorable private readonly SystemIO.FileSystemWatcher? _quickAccessFolderWatcher; // Constructor public QuickAccessWidgetViewModel() { Items.CollectionChanged += Items_CollectionChanged; OpenPropertiesCommand = new RelayCommand<WidgetFolderCardItem>(ExecuteOpenPropertiesCommand); PinToSidebarCommand = new AsyncRelayCommand<WidgetFolderCardItem>(ExecutePinToSidebarCommand); UnpinFromSidebarCommand = new AsyncRelayCommand<WidgetFolderCardItem>(ExecuteUnpinFromSidebarCommand); var automaticDestinationsPath = SystemIO.Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", "Windows", "Recent", "AutomaticDestinations"); if (!SystemIO.Directory.Exists(automaticDestinationsPath)) return; _quickAccessFolderWatcher = new() { Path = automaticDestinationsPath, Filter = "f01b4d95cf55d32a.automaticDestinations-ms", NotifyFilter = SystemIO.NotifyFilters.LastAccess | SystemIO.NotifyFilters.LastWrite | SystemIO.NotifyFilters.FileName }; _quickAccessFolderWatcher.Changed += async (s, e) => { await RefreshWidgetAsync(); }; _quickAccessFolderWatcher.EnableRaisingEvents = true; } // Methods public Task RefreshWidgetAsync() { return MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => { foreach (var item in Items) item.Dispose(); Items.Clear(); await foreach (IWindowsStorable folder in HomePageContext.HomeFolder.GetQuickAccessFolderAsync(default)) { folder.GetPropertyValue<bool>("System.Home.IsPinned", out var isPinned); folder.TryGetShellTooltip(out var tooltip); Items.Insert( Items.Count, new WidgetFolderCardItem( folder, folder.GetDisplayName(SIGDN.SIGDN_PARENTRELATIVEFORUI), isPinned, tooltip ?? string.Empty)); } }); } public override List<ContextMenuFlyoutItemViewModel> GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) { return new List<ContextMenuFlyoutItemViewModel>() { new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewTabFromHome) { IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewTab && CommandManager.OpenInNewTabFromHome.IsExecutable }.Build(), new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInNewWindowFromHome) { IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewWindow && CommandManager.OpenInNewWindowFromHome.IsExecutable }.Build(), new ContextMenuFlyoutItemViewModel() { Text = Strings.OpenInNewPane.GetLocalizedResource(), ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenInNewPane && CommandManager.OpenInNewPaneFromHome.IsExecutable, IsEnabled = CommandManager.OpenInNewPaneFromHome.IsExecutable, Items = [ new ContextMenuFlyoutItemViewModel() { Text = Strings.SplitPaneVertically.GetLocalizedResource(), ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.OpenInPaneVertical" }, Command = CommandManager.OpenInNewPaneFromHome, CommandParameter = ShellPaneArrangement.Vertical, }, new ContextMenuFlyoutItemViewModel() { Text = Strings.SplitPaneHorizontally.GetLocalizedResource(), ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.OpenInPaneHorizontal" }, Command = CommandManager.OpenInNewPaneFromHome, CommandParameter = ShellPaneArrangement.Horizontal, }, ] }, new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenInOtherPaneFromHome) { IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenInNewPane && CommandManager.OpenInOtherPaneFromHome.IsExecutable }.Build(), new ContextMenuFlyoutItemViewModelBuilder(CommandManager.CopyItemFromHome) { IsPrimary = true, IsVisible = CommandManager.CopyItemFromHome.IsExecutable }.Build(), new() { Text = Strings.Properties.GetLocalizedResource(), ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.Properties" }, Command = OpenPropertiesCommand, CommandParameter = item, IsPrimary = true }, new() { Text = Strings.PinFolderToSidebar.GetLocalizedResource(), ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.FavoritePin" }, Command = PinToSidebarCommand, CommandParameter = item, ShowItem = !isPinned && UserSettingsService.GeneralSettingsService.ShowPinToSideBar }, new() { Text = Strings.UnpinFolderFromSidebar.GetLocalizedResource(), ThemedIconModel = new() { ThemedIconStyle = "App.ThemedIcons.FavoritePinRemove" }, Command = UnpinFromSidebarCommand, CommandParameter = item, ShowItem = isPinned && UserSettingsService.GeneralSettingsService.ShowPinToSideBar }, new() { Text = Strings.SendTo.GetLocalizedResource(), Tag = "SendToPlaceholder", ShowItem = UserSettingsService.GeneralSettingsService.ShowSendToMenu }, new ContextMenuFlyoutItemViewModel() { ItemType = ContextMenuFlyoutItemType.Separator, ShowItem = UserSettingsService.GeneralSettingsService.ShowOpenTerminal && CommandManager.OpenTerminalFromHome.IsExecutable }, new ContextMenuFlyoutItemViewModelBuilder(CommandManager.OpenTerminalFromHome) { IsVisible = UserSettingsService.GeneralSettingsService.ShowOpenTerminal && CommandManager.OpenTerminalFromHome.IsExecutable }.Build(), new() { ItemType = ContextMenuFlyoutItemType.Separator, Tag = "OverflowSeparator", }, new() { Text = Strings.Loading.GetLocalizedResource(), Glyph = "\xE712", Items = [], ID = "ItemOverflow", Tag = "ItemOverflow", IsEnabled = false, } }.Where(x => x.ShowItem).ToList(); } public async Task NavigateToPath(string path) { var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); if (ctrlPressed) { await NavigationHelpers.OpenPathInNewTab(path); return; } ContentPageContext.ShellPage?.NavigateWithArguments( ContentPageContext.ShellPage.InstanceViewModel.FolderSettings.GetLayoutType(path), new() { NavPathParam = path }); } // Event methods 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 not WidgetFolderCardItem folderCardItem || folderCardItem.Path is null) return; var lastPinnedItemIndex = Items.LastOrDefault(x => x.IsPinned) is { } lastPinnedItem ? Items.IndexOf(lastPinnedItem) : 0; var currentPinnedItemIndex = Items.IndexOf(folderCardItem); if (currentPinnedItemIndex is -1) return; HRESULT hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, typeof(IShellItem).GUID, folderCardItem.Item.ThisPtr, out IAgileReference pAgileReference); if (hr.ThrowIfFailedOnDebug().Failed) return; // Pin to Quick Access on Windows hr = await STATask.Run(() => { hr = pAgileReference.Resolve(out IShellItem pShellItem); if (hr.ThrowIfFailedOnDebug().Failed) return hr; using var windowsFile = new WindowsFile(pShellItem); // NOTE: "pintohome" is an undocumented verb, which calls an undocumented COM class, windows.storage.dll!CPinToFrequentExecute : public IExecuteCommand, ... return windowsFile.TryInvokeContextMenuVerb("pintohome"); }, App.Logger); // The file watcher will update the collection automatically } public override async Task ExecuteUnpinFromSidebarCommand(WidgetCardItem? item) { if (item is not WidgetFolderCardItem folderCardItem || folderCardItem.Path is null) return; HRESULT hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, typeof(IShellItem).GUID, folderCardItem.Item.ThisPtr, out IAgileReference pAgileReference); if (hr.ThrowIfFailedOnDebug().Failed) return; // Unpin from Quick Access on Windows hr = await STATask.Run(() => { hr = pAgileReference.Resolve(out IShellItem pShellItem); if (hr.ThrowIfFailedOnDebug().Failed) return hr; using var windowsFile = new WindowsFile(pShellItem); // NOTE: "unpinfromhome" is an undocumented verb, which calls an undocumented COM class, windows.storage.dll!CRemoveFromFrequentPlacesExecute : public IExecuteCommand, ... // NOTE: "remove" is for some shell folders where the "unpinfromhome" may not work return windowsFile.TryInvokeContextMenuVerbs(["unpinfromhome", "remove"], true); }, App.Logger); if (hr.ThrowIfFailedOnDebug().Failed) return; // The file watcher will update the collection automatically } private void ExecuteOpenPropertiesCommand(WidgetFolderCardItem? item) { if (!HomePageContext.IsAnyItemRightClicked || item is null || item.Item is null) return; var flyout = HomePageContext.ItemContextFlyoutMenu; EventHandler<object> flyoutClosed = null!; flyoutClosed = async (s, e) => { flyout!.Closed -= flyoutClosed; ListedItem listedItem = new(null!) { ItemPath = item.Path, ItemNameRaw = item.Text, PrimaryItemAttribute = StorageItemTypes.Folder, ItemType = Strings.Folder.GetLocalizedResource(), }; if (!string.Equals(item.Path, Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase)) { BaseStorageFolder matchingStorageFolder = await ContentPageContext.ShellPage!.ShellViewModel.GetFolderFromPathAsync(item.Path); if (matchingStorageFolder is not null) { var syncStatus = await ContentPageContext.ShellPage!.ShellViewModel.CheckCloudDriveSyncStatusAsync(matchingStorageFolder); listedItem.SyncStatusUI = CloudDriveSyncStatusUI.FromCloudDriveSyncStatus(syncStatus); } } FilePropertiesHelpers.OpenPropertiesWindow(listedItem, ContentPageContext.ShellPage!); }; flyout!.Closed += flyoutClosed; } // Disposer public void Dispose() { foreach (var item in Items) item.Dispose(); } } }