From 1b601709247324ac06fca94c37150aaa74185711 Mon Sep 17 00:00:00 2001 From: ferrariofilippo Date: Sun, 11 Sep 2022 20:54:28 +0200 Subject: [PATCH 01/15] Feature: setting to select files and folders when hovering over them #3682 --- src/Files.App/BaseLayout.cs | 377 ++++++++---------- src/Files.App/IBaseLayout.cs | 8 +- .../Settings/PreferencesSettingsService.cs | 7 + src/Files.App/Strings/en-US/Resources.resw | 3 + .../PreferencesViewModel.cs | 13 + .../Views/LayoutModes/ColumnViewBase.xaml | 2 + .../LayoutModes/DetailsLayoutBrowser.xaml | 2 + .../Views/LayoutModes/GridViewBrowser.xaml | 4 + .../Views/SettingsPages/Preferences.xaml | 9 +- .../Settings/IPreferencesSettingsService.cs | 5 + 10 files changed, 217 insertions(+), 213 deletions(-) diff --git a/src/Files.App/BaseLayout.cs b/src/Files.App/BaseLayout.cs index 8e8cd25fe121..728747d35573 100644 --- a/src/Files.App/BaseLayout.cs +++ b/src/Files.App/BaseLayout.cs @@ -1,5 +1,7 @@ using CommunityToolkit.Mvvm.DependencyInjection; -using Files.Backend.Services.Settings; +using CommunityToolkit.WinUI; +using CommunityToolkit.WinUI.UI; +using Files.App.DataModels; using Files.App.EventArguments; using Files.App.Extensions; using Files.App.Filesystem; @@ -7,12 +9,19 @@ using Files.App.Helpers; using Files.App.Helpers.ContextFlyouts; using Files.App.Interacts; -using Files.Shared.Enums; -using Files.Shared.Extensions; using Files.App.UserControls; +using Files.App.UserControls.Menus; using Files.App.ViewModels; using Files.App.Views; -using CommunityToolkit.WinUI.UI; +using Files.Shared.Enums; +using Files.Shared.Extensions; +using Files.Backend.Services.Settings; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Navigation; using System; using System.Collections.Generic; using System.ComponentModel; @@ -27,17 +36,8 @@ using Windows.Foundation.Collections; using Windows.Storage; using Windows.System; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Navigation; -using Files.App.UserControls.Menus; using static Files.App.Helpers.PathNormalization; -using CommunityToolkit.WinUI; using DispatcherQueueTimer = Microsoft.UI.Dispatching.DispatcherQueueTimer; -using Files.App.DataModels; namespace Files.App { @@ -48,40 +48,40 @@ public abstract class BaseLayout : Page, IBaseLayout, INotifyPropertyChanged { private readonly DispatcherQueueTimer jumpTimer; - protected IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetService(); + protected IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetService()!; - protected IFileTagsSettingsService FileTagsSettingsService { get; } = Ioc.Default.GetService(); + protected IFileTagsSettingsService FileTagsSettingsService { get; } = Ioc.Default.GetService()!; protected Task Connection => AppServiceConnectionHelper.Instance; public SelectedItemsPropertiesViewModel SelectedItemsPropertiesViewModel { get; } - public FolderSettingsViewModel FolderSettings => ParentShellPageInstance.InstanceViewModel.FolderSettings; + public FolderSettingsViewModel? FolderSettings => ParentShellPageInstance?.InstanceViewModel.FolderSettings; - public CurrentInstanceViewModel InstanceViewModel => ParentShellPageInstance.InstanceViewModel; + public CurrentInstanceViewModel? InstanceViewModel => ParentShellPageInstance?.InstanceViewModel; public IPaneViewModel PaneViewModel => App.PaneViewModel; public AppModel AppModel => App.AppModel; public DirectoryPropertiesViewModel DirectoryPropertiesViewModel { get; } - public Microsoft.UI.Xaml.Controls.CommandBarFlyout ItemContextMenuFlyout { get; set; } = new Microsoft.UI.Xaml.Controls.CommandBarFlyout() + public CommandBarFlyout ItemContextMenuFlyout { get; set; } = new CommandBarFlyout() { AlwaysExpanded = true, }; - public Microsoft.UI.Xaml.Controls.CommandBarFlyout BaseContextMenuFlyout { get; set; } = new Microsoft.UI.Xaml.Controls.CommandBarFlyout() + public CommandBarFlyout BaseContextMenuFlyout { get; set; } = new CommandBarFlyout() { AlwaysExpanded = true, }; - public BaseLayoutCommandsViewModel CommandsViewModel { get; protected set; } + public BaseLayoutCommandsViewModel? CommandsViewModel { get; protected set; } - public IShellPage ParentShellPageInstance { get; private set; } = null; + public IShellPage? ParentShellPageInstance { get; private set; } = null; public bool IsRenamingItem { get; set; } = false; - public ListedItem RenamingItem { get; set; } = null; + public ListedItem? RenamingItem { get; set; } = null; - public string OldItemName { get; set; } = null; + public string? OldItemName { get; set; } = null; private bool isMiddleClickToScrollEnabled = true; @@ -98,7 +98,7 @@ public bool IsMiddleClickToScrollEnabled } } - protected AddressToolbar NavToolbar => (App.Window.Content as Frame).FindDescendant(); + protected AddressToolbar? NavToolbar => (App.Window.Content as Frame)?.FindDescendant(); private CollectionViewSource collectionViewSource = new CollectionViewSource() { @@ -111,23 +111,17 @@ public CollectionViewSource CollectionViewSource set { if (collectionViewSource == value) - { return; - } if (collectionViewSource?.View is not null) - { collectionViewSource.View.VectorChanged -= View_VectorChanged; - } collectionViewSource = value; NotifyPropertyChanged(nameof(CollectionViewSource)); if (collectionViewSource?.View is not null) - { collectionViewSource.View.VectorChanged += View_VectorChanged; - } } } - protected NavigationArguments navigationArguments; + protected NavigationArguments? navigationArguments; private bool isItemSelected = false; @@ -157,24 +151,20 @@ public string JumpString // If current string is "a", and the next character typed is "a", // search for next file that starts with "a" (a.k.a. _jumpString = "a") if (jumpString.Length == 1 && value == jumpString + jumpString) - { value = jumpString; - } if (value != string.Empty) { - ListedItem jumpedToItem = null; - ListedItem previouslySelectedItem = null; + ListedItem? jumpedToItem = null; + ListedItem? previouslySelectedItem = null; if (IsItemSelected) - { previouslySelectedItem = SelectedItem; - } // Select first matching item after currently selected item if (previouslySelectedItem != null) { // Use FilesAndFolders because only displayed entries should be jumped to - IEnumerable candidateItems = ParentShellPageInstance.FilesystemViewModel.FilesAndFolders + IEnumerable candidateItems = ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders .SkipWhile(x => x != previouslySelectedItem) .Skip(value.Length == 1 ? 1 : 0) // User is trying to cycle through items starting with the same letter .Where(f => f.ItemName.Length >= value.Length && string.Equals(f.ItemName.Substring(0, value.Length), value, StringComparison.OrdinalIgnoreCase)); @@ -184,7 +174,7 @@ public string JumpString if (jumpedToItem == null) { // Use FilesAndFolders because only displayed entries should be jumped to - IEnumerable candidateItems = ParentShellPageInstance.FilesystemViewModel.FilesAndFolders + IEnumerable candidateItems = ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders .Where(f => f.ItemName.Length >= value.Length && string.Equals(f.ItemName.Substring(0, value.Length), value, StringComparison.OrdinalIgnoreCase)); jumpedToItem = candidateItems.FirstOrDefault(); } @@ -203,9 +193,9 @@ public string JumpString } } - private List selectedItems = new List(); + private List? selectedItems = new List(); - public List SelectedItems + public List? SelectedItems { get { @@ -235,14 +225,12 @@ internal set { bool isPaneEnabled = ((App.Window.Content as Frame)?.Content as MainPage)?.IsPaneEnabled ?? false; if (isPaneEnabled) - { App.PreviewPaneViewModel.UpdateSelectedItemPreview(); - } } } selectedItems = value; - if (selectedItems.Count == 0 || selectedItems[0] == null) + if (selectedItems?.Count == 0 || selectedItems?[0] == null) { IsItemSelected = false; SelectedItem = null; @@ -257,12 +245,10 @@ internal set SelectedItemsPropertiesViewModel.IsItemSelected = true; UpdateSelectionSize(); - if (SelectedItems.Count >= 1) - { + if (SelectedItems?.Count >= 1) SelectedItemsPropertiesViewModel.SelectedItemsCount = SelectedItems.Count; - } - if (SelectedItems.Count == 1) + if (SelectedItems?.Count == 1) { SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems.Count} {"ItemSelected/Text".GetLocalizedResource()}"; DispatcherQueue.EnqueueAsync(async () => @@ -273,7 +259,7 @@ internal set } else { - SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems.Count} {"ItemsSelected/Text".GetLocalizedResource()}"; + SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems!.Count} {"ItemsSelected/Text".GetLocalizedResource()}"; ResetRenameDoubleClick(); } } @@ -282,13 +268,13 @@ internal set //ItemManipulationModel.SetDragModeForItems(); } - ParentShellPageInstance.ToolbarViewModel.SelectedItems = value; + ParentShellPageInstance!.ToolbarViewModel.SelectedItems = value; } } - public ListedItem SelectedItem { get; private set; } + public ListedItem? SelectedItem { get; private set; } - private DispatcherQueueTimer dragOverTimer, tapDebounceTimer; + private DispatcherQueueTimer dragOverTimer, tapDebounceTimer, hoverTimer; protected abstract uint IconSize { get; } @@ -310,6 +296,7 @@ public BaseLayout() dragOverTimer = DispatcherQueue.CreateTimer(); tapDebounceTimer = DispatcherQueue.CreateTimer(); + hoverTimer = DispatcherQueue.CreateTimer(); } protected abstract void HookEvents(); @@ -336,13 +323,11 @@ private void JumpTimer_Tick(object sender, object e) protected abstract void InitializeCommandsViewModel(); - protected IEnumerable GetAllItems() + protected IEnumerable? GetAllItems() { if (CollectionViewSource.IsSourceGrouped) - { // add all items from each group to the new list return (CollectionViewSource.Source as BulkConcurrentObservableCollection>)?.SelectMany(g => g); - } return CollectionViewSource.Source as IEnumerable; } @@ -351,43 +336,38 @@ public virtual void ResetItemOpacity() { var items = GetAllItems(); if (items == null) - { return; - } foreach (var item in items) { if (item != null) - { item.Opacity = item.IsHiddenItem ? Constants.UI.DimItemOpacity : 1.0d; - } } } - protected ListedItem GetItemFromElement(object element) + protected ListedItem? GetItemFromElement(object element) { var item = element as ContentControl; if (item == null || !CanGetItemFromElement(element)) - { return null; - } return (item.DataContext as ListedItem) ?? (item.Content as ListedItem) ?? (ItemsControl.ItemFromContainer(item) as ListedItem); } protected abstract bool CanGetItemFromElement(object element); - protected virtual void BaseFolderSettings_LayoutModeChangeRequested(object sender, LayoutModeEventArgs e) + protected virtual void BaseFolderSettings_LayoutModeChangeRequested(object? sender, LayoutModeEventArgs e) { - if (ParentShellPageInstance.SlimContentPage != null) + if (ParentShellPageInstance?.SlimContentPage != null) { - var layoutType = FolderSettings.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); + var layoutType = FolderSettings?.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); - if (layoutType != ParentShellPageInstance.CurrentPageType) + if (layoutType is not null && + layoutType != ParentShellPageInstance.CurrentPageType) { ParentShellPageInstance.NavigateWithArguments(layoutType, new NavigationArguments() { - NavPathParam = navigationArguments.NavPathParam, + NavPathParam = navigationArguments!.NavPathParam, IsSearchResultPage = navigationArguments.IsSearchResultPage, SearchPathParam = navigationArguments.SearchPathParam, SearchQuery = navigationArguments.SearchQuery, @@ -403,7 +383,7 @@ protected virtual void BaseFolderSettings_LayoutModeChangeRequested(object sende } } - public event PropertyChangedEventHandler PropertyChanged; + public event PropertyChangedEventHandler? PropertyChanged; protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "") { @@ -420,7 +400,7 @@ protected override async void OnNavigatedTo(NavigationEventArgs eventArgs) InitializeCommandsViewModel(); IsItemSelected = false; - FolderSettings.LayoutModeChangeRequested += BaseFolderSettings_LayoutModeChangeRequested; + FolderSettings!.LayoutModeChangeRequested += BaseFolderSettings_LayoutModeChangeRequested; FolderSettings.GroupOptionPreferenceUpdated += FolderSettings_GroupOptionPreferenceUpdated; ParentShellPageInstance.FilesystemViewModel.EmptyTextType = EmptyTextType.None; ParentShellPageInstance.ToolbarViewModel.UpdateSortAndGroupOptions(); @@ -435,13 +415,9 @@ protected override async void OnNavigatedTo(NavigationEventArgs eventArgs) var workingDir = ParentShellPageInstance.FilesystemViewModel.WorkingDirectory ?? string.Empty; string pathRoot = GetPathRoot(workingDir); if (string.IsNullOrEmpty(pathRoot) || workingDir.StartsWith(CommonPaths.RecycleBinPath, StringComparison.Ordinal)) // Can't go up from recycle bin - { ParentShellPageInstance.ToolbarViewModel.CanNavigateToParent = false; - } else - { ParentShellPageInstance.ToolbarViewModel.CanNavigateToParent = true; - } ParentShellPageInstance.InstanceViewModel.IsPageTypeRecycleBin = workingDir.StartsWith(CommonPaths.RecycleBinPath, StringComparison.Ordinal); ParentShellPageInstance.InstanceViewModel.IsPageTypeMtpDevice = workingDir.StartsWith("\\\\?\\", StringComparison.Ordinal); @@ -451,13 +427,9 @@ protected override async void OnNavigatedTo(NavigationEventArgs eventArgs) ParentShellPageInstance.InstanceViewModel.IsPageTypeSearchResults = false; ParentShellPageInstance.ToolbarViewModel.PathControlDisplayText = navigationArguments.NavPathParam; if (!navigationArguments.IsLayoutSwitch || previousDir != workingDir) - { ParentShellPageInstance.FilesystemViewModel.RefreshItems(previousDir, SetSelectedItemsOnNavigation); - } else - { ParentShellPageInstance.ToolbarViewModel.CanGoForward = false; - } } else { @@ -484,7 +456,7 @@ protected override async void OnNavigatedTo(NavigationEventArgs eventArgs) { Query = navigationArguments.SearchQuery, Folder = navigationArguments.SearchPathParam, - ThumbnailSize = InstanceViewModel.FolderSettings.GetIconSize(), + ThumbnailSize = InstanceViewModel!.FolderSettings.GetIconSize(), SearchUnindexedItems = navigationArguments.SearchUnindexedItems }; _ = ParentShellPageInstance.FilesystemViewModel.SearchAsync(searchInstance); @@ -510,9 +482,7 @@ public void SetSelectedItemsOnNavigation() { List liItemsToSelect = new List(); foreach (string item in navigationArguments.SelectItems) - { - liItemsToSelect.Add(ParentShellPageInstance.FilesystemViewModel.FilesAndFolders.Where((li) => li.ItemNameRaw == item).First()); - } + liItemsToSelect.Add(ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders.Where((li) => li.ItemNameRaw == item).First()); ItemManipulationModel.SetSelectedItems(liItemsToSelect); ItemManipulationModel.FocusSelectedItems(); @@ -527,15 +497,15 @@ public void SetSelectedItemsOnNavigation() } } - private CancellationTokenSource groupingCancellationToken; + private CancellationTokenSource? groupingCancellationToken; - private async void FolderSettings_GroupOptionPreferenceUpdated(object sender, GroupOption e) + private async void FolderSettings_GroupOptionPreferenceUpdated(object? sender, GroupOption e) { // Two or more of these running at the same time will cause a crash, so cancel the previous one before beginning groupingCancellationToken?.Cancel(); groupingCancellationToken = new CancellationTokenSource(); var token = groupingCancellationToken.Token; - await ParentShellPageInstance.FilesystemViewModel.GroupOptionsUpdated(token); + await ParentShellPageInstance!.FilesystemViewModel.GroupOptionsUpdated(token); UpdateCollectionViewSource(); await ParentShellPageInstance.FilesystemViewModel.ReloadItemGroupHeaderImagesAsync(); } @@ -545,33 +515,27 @@ protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) base.OnNavigatingFrom(e); // Remove item jumping handler this.CharacterReceived -= Page_CharacterReceived; - FolderSettings.LayoutModeChangeRequested -= BaseFolderSettings_LayoutModeChangeRequested; + FolderSettings!.LayoutModeChangeRequested -= BaseFolderSettings_LayoutModeChangeRequested; FolderSettings.GroupOptionPreferenceUpdated -= FolderSettings_GroupOptionPreferenceUpdated; ItemContextMenuFlyout.Opening -= ItemContextFlyout_Opening; BaseContextMenuFlyout.Opening -= BaseContextFlyout_Opening; var parameter = e.Parameter as NavigationArguments; - if (!parameter.IsLayoutSwitch) - { - ParentShellPageInstance.FilesystemViewModel.CancelLoadAndClearFiles(); - } + if (parameter is not null && !parameter.IsLayoutSwitch) + ParentShellPageInstance!.FilesystemViewModel.CancelLoadAndClearFiles(); } - public async void ItemContextFlyout_Opening(object sender, object e) + public async void ItemContextFlyout_Opening(object? sender, object e) { try { if (!IsItemSelected) // Workaround for item sometimes not getting selected { - if (((sender as Microsoft.UI.Xaml.Controls.CommandBarFlyout)?.Target as ListViewItem)?.Content is ListedItem li) - { + if (((sender as CommandBarFlyout)?.Target as ListViewItem)?.Content is ListedItem li) ItemManipulationModel.SetSelectedItem(li); - } } if (IsItemSelected) - { await LoadMenuItemsAsync(); - } } catch (Exception error) { @@ -579,38 +543,35 @@ public async void ItemContextFlyout_Opening(object sender, object e) } } - private CancellationTokenSource shellContextMenuItemCancellationToken; + private CancellationTokenSource? shellContextMenuItemCancellationToken; - public async void BaseContextFlyout_Opening(object sender, object e) + public async void BaseContextFlyout_Opening(object? sender, object e) { try { if (BaseContextMenuFlyout.GetValue(ContextMenuExtensions.ItemsControlProperty) is ItemsControl itc) - { itc.MaxHeight = Constants.UI.ContextMenuMaxHeight; // Reset menu max height - } shellContextMenuItemCancellationToken?.Cancel(); shellContextMenuItemCancellationToken = new CancellationTokenSource(); var shiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down); - var items = ContextFlyoutItemHelper.GetBaseContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel, itemViewModel: ParentShellPageInstance.FilesystemViewModel, commandsViewModel: CommandsViewModel, shiftPressed: shiftPressed, false); + var items = ContextFlyoutItemHelper.GetBaseContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, itemViewModel: ParentShellPageInstance!.FilesystemViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, false); BaseContextMenuFlyout.PrimaryCommands.Clear(); BaseContextMenuFlyout.SecondaryCommands.Clear(); var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); primaryElements.Where(i => i is AppBarButton).ForEach(i => { - (i as AppBarButton).Click += new RoutedEventHandler((s, e) => BaseContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) + if (i is AppBarButton button) + button.Click += new RoutedEventHandler((s, e) => BaseContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) }); primaryElements.ForEach(i => BaseContextMenuFlyout.PrimaryCommands.Add(i)); secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width secondaryElements.ForEach(i => BaseContextMenuFlyout.SecondaryCommands.Add(i)); - if (!InstanceViewModel.IsPageTypeSearchResults && !InstanceViewModel.IsPageTypeZipFolder) + if (!InstanceViewModel!.IsPageTypeSearchResults && !InstanceViewModel.IsPageTypeZipFolder) { var shellMenuItems = await ContextFlyoutItemHelper.GetBaseContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); if (shellMenuItems.Any()) - { AddShellItemsToMenu(shellMenuItems, BaseContextMenuFlyout, shiftPressed); - } } } catch (Exception error) @@ -622,64 +583,58 @@ public async void BaseContextFlyout_Opening(object sender, object e) public void UpdateSelectionSize() { var items = (selectedItems?.Any() ?? false) ? selectedItems : GetAllItems(); - if (items is not null) + if (items is null) + return; + bool isSizeKnown = !items.Any(item => string.IsNullOrEmpty(item.FileSize)); + if (isSizeKnown) { - bool isSizeKnown = !items.Any(item => string.IsNullOrEmpty(item.FileSize)); - if (isSizeKnown) - { - long size = items.Sum(item => item.FileSizeBytes); - SelectedItemsPropertiesViewModel.ItemSizeBytes = size; - SelectedItemsPropertiesViewModel.ItemSize = size.ToSizeString(); - } - else - { - SelectedItemsPropertiesViewModel.ItemSizeBytes = 0; - SelectedItemsPropertiesViewModel.ItemSize = string.Empty; - } - SelectedItemsPropertiesViewModel.ItemSizeVisibility = isSizeKnown; + long size = items.Sum(item => item.FileSizeBytes); + SelectedItemsPropertiesViewModel.ItemSizeBytes = size; + SelectedItemsPropertiesViewModel.ItemSize = size.ToSizeString(); + } + else + { + SelectedItemsPropertiesViewModel.ItemSizeBytes = 0; + SelectedItemsPropertiesViewModel.ItemSize = string.Empty; } + SelectedItemsPropertiesViewModel.ItemSizeVisibility = isSizeKnown; } private async Task LoadMenuItemsAsync() { if (ItemContextMenuFlyout.GetValue(ContextMenuExtensions.ItemsControlProperty) is ItemsControl itc) - { itc.MaxHeight = Constants.UI.ContextMenuMaxHeight; // Reset menu max height - } shellContextMenuItemCancellationToken?.Cancel(); shellContextMenuItemCancellationToken = new CancellationTokenSource(); - SelectedItemsPropertiesViewModel.CheckAllFileExtensions(this.SelectedItems.Select(selectedItem => selectedItem?.FileExtension).ToList()); + SelectedItemsPropertiesViewModel.CheckAllFileExtensions(this.SelectedItems!.Select(selectedItem => selectedItem?.FileExtension).ToList()!); var shiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down); - var items = ContextFlyoutItemHelper.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems, selectedItemsPropertiesViewModel: SelectedItemsPropertiesViewModel, commandsViewModel: CommandsViewModel, shiftPressed: shiftPressed, showOpenMenu: false); + var items = ContextFlyoutItemHelper.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, workingDir: ParentShellPageInstance!.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, selectedItemsPropertiesViewModel: SelectedItemsPropertiesViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, showOpenMenu: false); ItemContextMenuFlyout.PrimaryCommands.Clear(); ItemContextMenuFlyout.SecondaryCommands.Clear(); var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); primaryElements.Where(i => i is AppBarButton).ForEach(i => { - (i as AppBarButton).Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) + if (i is AppBarButton button) + button.Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) }); primaryElements.ForEach(i => ItemContextMenuFlyout.PrimaryCommands.Add(i)); secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width secondaryElements.ForEach(i => ItemContextMenuFlyout.SecondaryCommands.Add(i)); - if (InstanceViewModel.CanTagFilesInPage) - { + if (InstanceViewModel!.CanTagFilesInPage) AddNewFileTagsToMenu(ItemContextMenuFlyout); - } if (!InstanceViewModel.IsPageTypeZipFolder) { - var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); + var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); if (shellMenuItems.Any()) - { AddShellItemsToMenu(shellMenuItems, ItemContextMenuFlyout, shiftPressed); - } } } - private void AddNewFileTagsToMenu(Microsoft.UI.Xaml.Controls.CommandBarFlyout contextMenu) + private void AddNewFileTagsToMenu(CommandBarFlyout contextMenu) { - var fileTagsContextMenu = new FileTagsContextMenu(SelectedItems); + var fileTagsContextMenu = new FileTagsContextMenu(SelectedItems!); var overflowSeparator = contextMenu.SecondaryCommands.FirstOrDefault(x => x is FrameworkElement fe && fe.Tag as string == "OverflowSeparator") as AppBarSeparator; var index = contextMenu.SecondaryCommands.IndexOf(overflowSeparator); index = index >= 0 ? index : contextMenu.SecondaryCommands.Count; @@ -704,7 +659,7 @@ private void AddShellItemsToMenu(List shellMenuI var openedPopups = Microsoft.UI.Xaml.Media.VisualTreeHelper.GetOpenPopups(App.Window); var secondaryMenu = openedPopups.FirstOrDefault(popup => popup.Name == "OverflowPopup"); var itemsControl = secondaryMenu?.Child.FindDescendant(); - if (itemsControl is not null) + if (itemsControl is not null && secondaryMenu is not null) { contextMenuFlyout.SetValue(ContextMenuExtensions.ItemsControlProperty, itemsControl); @@ -713,9 +668,7 @@ private void AddShellItemsToMenu(List shellMenuI var requiredHeight = contextMenuFlyout.SecondaryCommands.Concat(mainItems).Where(x => x is not AppBarSeparator).Count() * Constants.UI.ContextMenuSecondaryItemsHeight; var availableHeight = App.Window.Bounds.Height - cMenuPos.Y - Constants.UI.ContextMenuPrimaryItemsHeight; if (requiredHeight > availableHeight) - { itemsControl.MaxHeight = Math.Min(Constants.UI.ContextMenuMaxHeight, Math.Max(itemsControl.ActualHeight, Math.Min(availableHeight, requiredHeight))); // Set menu max height to current height (avoids menu repositioning) - } mainItems.OfType().ForEach(x => x.MaxWidth = itemsControl.ActualWidth - Constants.UI.ContextMenuLabelMargin); // Set items max width to current menu width (#5555) } @@ -724,29 +677,30 @@ private void AddShellItemsToMenu(List shellMenuI if (overflowItem is not null) { var overflowItemFlyout = overflowItem.Flyout as MenuFlyout; - if (overflowItemFlyout.Items.Count > 0) + if (overflowItemFlyout is not null) { - overflowItemFlyout.Items.Insert(0, new MenuFlyoutSeparator()); - } + if (overflowItemFlyout.Items.Count > 0) + overflowItemFlyout.Items.Insert(0, new MenuFlyoutSeparator()); - var index = contextMenuFlyout.SecondaryCommands.Count - 2; - foreach (var i in mainItems) - { - index++; - contextMenuFlyout.SecondaryCommands.Insert(index, i); - } + var index = contextMenuFlyout.SecondaryCommands.Count - 2; + foreach (var i in mainItems) + { + index++; + contextMenuFlyout.SecondaryCommands.Insert(index, i); + } - index = 0; - foreach (var i in overflowItems) - { - overflowItemFlyout.Items.Insert(index, i); - index++; - } + index = 0; + foreach (var i in overflowItems) + { + overflowItemFlyout.Items.Insert(index, i); + index++; + } - if (overflowItemFlyout.Items.Count > 0) - { - (contextMenuFlyout.SecondaryCommands.First(x => x is FrameworkElement fe && fe.Tag as string == "OverflowSeparator") as AppBarSeparator).Visibility = Visibility.Visible; - overflowItem.Visibility = Visibility.Visible; + if (overflowItemFlyout.Items.Count > 0) + { + (contextMenuFlyout.SecondaryCommands.First(x => x is FrameworkElement fe && fe.Tag as string == "OverflowSeparator") as AppBarSeparator)!.Visibility = Visibility.Visible; + overflowItem.Visibility = Visibility.Visible; + } } } else @@ -760,7 +714,8 @@ private void AddShellItemsToMenu(List shellMenuI { var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; var flyout = openWithOverflow.Flyout as MenuFlyout; - flyout.Items.Clear(); + + flyout!.Items.Clear(); foreach (var item in openWithSubItems) { @@ -768,7 +723,7 @@ private void AddShellItemsToMenu(List shellMenuI } openWithOverflow.Flyout = flyout; - openWith.Visibility = Visibility.Collapsed; + openWith!.Visibility = Visibility.Collapsed; openWithOverflow.Visibility = Visibility.Visible; } @@ -777,12 +732,10 @@ private void AddShellItemsToMenu(List shellMenuI itemsControl.Items.OfType().ForEach(item => { if (item.FindDescendant("OverflowTextLabel") is TextBlock label) // Enable CharacterEllipsis text trimming for menu items - { label.TextTrimming = TextTrimming.CharacterEllipsis; - } if ((item as AppBarButton)?.Flyout as MenuFlyout is MenuFlyout flyout) // Close main menu when clicking on subitems (#5508) { - Action> clickAction = null; + Action>? clickAction = null; clickAction = (items) => { items.OfType().ForEach(i => @@ -791,7 +744,7 @@ private void AddShellItemsToMenu(List shellMenuI }); items.OfType().ForEach(i => { - clickAction(i.Items); + clickAction!(i.Items); }); }; clickAction(flyout.Items); @@ -802,7 +755,7 @@ private void AddShellItemsToMenu(List shellMenuI protected virtual void Page_CharacterReceived(UIElement sender, CharacterReceivedRoutedEventArgs args) { - if (ParentShellPageInstance.IsCurrentInstance) + if (ParentShellPageInstance!.IsCurrentInstance) { char letter = args.Character; JumpString += letter.ToString().ToLowerInvariant(); @@ -811,7 +764,7 @@ protected virtual void Page_CharacterReceived(UIElement sender, CharacterReceive protected void FileList_DragItemsStarting(object sender, DragItemsStartingEventArgs e) { - e.Items.OfType().ForEach(item => SelectedItems.Add(item)); + e.Items.OfType().ForEach(item => SelectedItems!.Add(item)); try { @@ -825,27 +778,22 @@ protected void FileList_DragItemsStarting(object sender, DragItemsStartingEventA } } - private ListedItem dragOverItem = null; + private ListedItem? dragOverItem = null; private void Item_DragLeave(object sender, DragEventArgs e) { - ListedItem item = GetItemFromElement(sender); + var item = GetItemFromElement(sender); if (item == dragOverItem) - { - // Reset dragged over item - dragOverItem = null; - } + dragOverItem = null; // Reset dragged over item } protected async void Item_DragOver(object sender, DragEventArgs e) { - ListedItem item = GetItemFromElement(sender); + var item = GetItemFromElement(sender); if (item is null) - { return; - } - DragOperationDeferral deferral = null; + DragOperationDeferral? deferral = null; try { deferral = e.GetDeferral(); @@ -862,7 +810,7 @@ protected async void Item_DragOver(object sender, DragEventArgs e) { dragOverItem = null; dragOverTimer.Stop(); - NavigationHelpers.OpenSelectedItems(ParentShellPageInstance, false); + NavigationHelpers.OpenSelectedItems(ParentShellPageInstance!, false); } }, TimeSpan.FromMilliseconds(1000), false); } @@ -871,8 +819,8 @@ protected async void Item_DragOver(object sender, DragEventArgs e) { e.Handled = true; - var handledByFtp = await Filesystem.FilesystemHelpers.CheckDragNeedsFulltrust(e.DataView); - var draggedItems = await Filesystem.FilesystemHelpers.GetDraggedStorageItems(e.DataView); + var handledByFtp = await FilesystemHelpers.CheckDragNeedsFulltrust(e.DataView); + var draggedItems = await FilesystemHelpers.GetDraggedStorageItems(e.DataView); if (draggedItems.Any(draggedItem => draggedItem.Path == item.ItemPath)) { @@ -943,11 +891,9 @@ protected async void Item_Drop(object sender, DragEventArgs e) e.Handled = true; dragOverItem = null; // Reset dragged over item - ListedItem item = GetItemFromElement(sender); + var item = GetItemFromElement(sender); if (item != null) - { - await ParentShellPageInstance.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as ShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable); - } + await ParentShellPageInstance!.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as ShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable); deferral.Complete(); } @@ -961,25 +907,19 @@ private void RefreshContainer(SelectorItem container, bool inRecycleQueue) { container.PointerPressed -= FileListItem_PointerPressed; if (inRecycleQueue) - { UninitializeDrag(container); - } else - { container.PointerPressed += FileListItem_PointerPressed; - } } private void RefreshItem(SelectorItem container, object item, bool inRecycleQueue, ContainerContentChangingEventArgs args) { if (item is not ListedItem listedItem) - { return; - } if (inRecycleQueue) { - ParentShellPageInstance.FilesystemViewModel.CancelExtendedPropertiesLoadingForItem(listedItem); + ParentShellPageInstance!.FilesystemViewModel.CancelExtendedPropertiesLoadingForItem(listedItem); } else { @@ -990,7 +930,7 @@ private void RefreshItem(SelectorItem container, object item, bool inRecycleQueu uint callbackPhase = 3; args.RegisterUpdateCallback(callbackPhase, async (s, c) => { - await ParentShellPageInstance.FilesystemViewModel.LoadExtendedItemProperties(listedItem, IconSize); + await ParentShellPageInstance!.FilesystemViewModel.LoadExtendedItemProperties(listedItem, IconSize); }); } } @@ -999,9 +939,7 @@ private void RefreshItem(SelectorItem container, object item, bool inRecycleQueu protected static void FileListItem_PointerPressed(object sender, PointerRoutedEventArgs e) { if (sender is not SelectorItem selectorItem) - { return; - } if (selectorItem.IsSelected && e.KeyModifiers == VirtualKeyModifiers.Control) { @@ -1015,14 +953,44 @@ protected static void FileListItem_PointerPressed(object sender, PointerRoutedEv } } + private ListedItem? hoveredItem = null; + + protected internal void ListedItem_PointerEntered(object sender, PointerRoutedEventArgs e) + { + if (!UserSettingsService.PreferencesSettingsService.SelectOnHover) + return; + + var hovered = (sender as Grid)?.DataContext as ListedItem; + if (hovered != hoveredItem) + { + hoveredItem = hovered; + hoverTimer.Stop(); + hoverTimer.Debounce(() => + { + if (hoveredItem is not null) + { + hoverTimer.Stop(); + ItemManipulationModel.SetSelectedItem(hoveredItem); + hoveredItem = null; + } + }, TimeSpan.FromMilliseconds(600), false); + } + } + + protected internal void ListedItem_PointerExited(object sender, PointerRoutedEventArgs e) + { + if (!UserSettingsService.PreferencesSettingsService.SelectOnHover) + return; + hoverTimer.Stop(); + hoveredItem = null; + } + private readonly RecycleBinHelpers recycleBinHelpers = new(); protected void InitializeDrag(UIElement containter, ListedItem item) { if (item is null) - { return; - } UninitializeDrag(containter); if ((item.PrimaryItemAttribute == StorageItemTypes.Folder && !recycleBinHelpers.IsPathUnderRecycleBin(item.ItemPath)) || item.IsExecutable) @@ -1065,6 +1033,8 @@ protected void ItemsLayout_Drop(object sender, DragEventArgs e) public void UpdateCollectionViewSource() { + if (ParentShellPageInstance is null) + return; if (ParentShellPageInstance.FilesystemViewModel.FilesAndFolders.IsGrouped) { CollectionViewSource = new CollectionViewSource() @@ -1096,53 +1066,44 @@ protected void SemanticZoom_ViewChangeStarted(object sender, SemanticZoomViewCha protected void StackPanel_PointerEntered(object sender, PointerRoutedEventArgs e) { var element = (sender as UIElement)?.FindAscendant(); - if (!(element is null)) - { + if (element is not null) VisualStateManager.GoToState(element, "PointerOver", true); - } } protected void StackPanel_PointerCanceled(object sender, PointerRoutedEventArgs e) { var element = (sender as UIElement)?.FindAscendant(); - if (!(element is null)) - { + if (element is not null) VisualStateManager.GoToState(element, "Normal", true); - } } protected void RootPanel_PointerPressed(object sender, PointerRoutedEventArgs e) { var element = (sender as UIElement)?.FindAscendant(); - if (!(element is null)) - { + if (element is not null) VisualStateManager.GoToState(element, "Pressed", true); - } } - private void ItemManipulationModel_RefreshItemsOpacityInvoked(object sender, EventArgs e) + private void ItemManipulationModel_RefreshItemsOpacityInvoked(object? sender, EventArgs e) { - foreach (ListedItem listedItem in GetAllItems()) + foreach (ListedItem listedItem in GetAllItems()!) { if (listedItem.IsHiddenItem) - { listedItem.Opacity = Constants.UI.DimItemOpacity; - } else - { listedItem.Opacity = 1; - } } } private void View_VectorChanged(IObservableVector sender, IVectorChangedEventArgs @event) { - ParentShellPageInstance.ToolbarViewModel.HasItem = CollectionViewSource.View.Any(); + if (ParentShellPageInstance is not null) + ParentShellPageInstance.ToolbarViewModel.HasItem = CollectionViewSource.View.Any(); } virtual public void StartRenameItem() { } - private ListedItem preRenamingItem = null; + private ListedItem? preRenamingItem = null; public void CheckRenameDoubleClick(object clickedItem) { @@ -1213,4 +1174,4 @@ public static void SetItemsControl(DependencyObject obj, ItemsControl value) public static readonly DependencyProperty ItemsControlProperty = DependencyProperty.RegisterAttached("ItemsControl", typeof(ItemsControl), typeof(ContextMenuExtensions), new PropertyMetadata(null)); } -} \ No newline at end of file +} diff --git a/src/Files.App/IBaseLayout.cs b/src/Files.App/IBaseLayout.cs index df9bc20b124d..946854eb0df4 100644 --- a/src/Files.App/IBaseLayout.cs +++ b/src/Files.App/IBaseLayout.cs @@ -14,9 +14,9 @@ public interface IBaseLayout : IDisposable bool IsMiddleClickToScrollEnabled { get; set; } - public List SelectedItems { get; } + public List? SelectedItems { get; } - public ListedItem SelectedItem { get; } + public ListedItem? SelectedItem { get; } ItemManipulationModel ItemManipulationModel { get; } @@ -24,6 +24,6 @@ public interface IBaseLayout : IDisposable public SelectedItemsPropertiesViewModel SelectedItemsPropertiesViewModel { get; } public DirectoryPropertiesViewModel DirectoryPropertiesViewModel { get; } - public BaseLayoutCommandsViewModel CommandsViewModel { get; } + public BaseLayoutCommandsViewModel? CommandsViewModel { get; } } -} \ No newline at end of file +} diff --git a/src/Files.App/ServicesImplementation/Settings/PreferencesSettingsService.cs b/src/Files.App/ServicesImplementation/Settings/PreferencesSettingsService.cs index e4df5171f31b..e970c742ccb0 100644 --- a/src/Files.App/ServicesImplementation/Settings/PreferencesSettingsService.cs +++ b/src/Files.App/ServicesImplementation/Settings/PreferencesSettingsService.cs @@ -62,6 +62,12 @@ public bool ShowDotFiles set => Set(value); } + public bool SelectOnHover + { + get => Get(false); + set => Set(value); + } + public bool OpenFilesWithOneClick { get => Get(false); @@ -151,6 +157,7 @@ protected override void RaiseOnSettingChangedEvent(object sender, SettingChanged case nameof(AreSystemItemsHidden): case nameof(AreAlternateStreamsVisible): case nameof(ShowDotFiles): + case nameof(SelectOnHover): case nameof(OpenFilesWithOneClick): case nameof(OpenFoldersWithOneClick): case nameof(ColumnLayoutOpenFoldersWithOneClick): diff --git a/src/Files.App/Strings/en-US/Resources.resw b/src/Files.App/Strings/en-US/Resources.resw index 1cba5764b6af..583698781db4 100644 --- a/src/Files.App/Strings/en-US/Resources.resw +++ b/src/Files.App/Strings/en-US/Resources.resw @@ -2825,4 +2825,7 @@ We use App Center to track which settings are being used, find bugs, and fix cra Behavior for opening files and folders + + Select files and folders hovering them + \ No newline at end of file diff --git a/src/Files.App/ViewModels/SettingsViewModels/PreferencesViewModel.cs b/src/Files.App/ViewModels/SettingsViewModels/PreferencesViewModel.cs index 48845a719595..6354c93b7b01 100644 --- a/src/Files.App/ViewModels/SettingsViewModels/PreferencesViewModel.cs +++ b/src/Files.App/ViewModels/SettingsViewModels/PreferencesViewModel.cs @@ -589,6 +589,19 @@ public bool ShowThumbnails } } + public bool SelectOnHover + { + get => UserSettingsService.PreferencesSettingsService.SelectOnHover; + set + { + if (value != UserSettingsService.PreferencesSettingsService.SelectOnHover) + { + UserSettingsService.PreferencesSettingsService.SelectOnHover = value; + OnPropertyChanged(); + } + } + } + public bool OpenFilesWithOneClick { get => UserSettingsService.PreferencesSettingsService.OpenFilesWithOneClick; diff --git a/src/Files.App/Views/LayoutModes/ColumnViewBase.xaml b/src/Files.App/Views/LayoutModes/ColumnViewBase.xaml index 90014ee48a39..3bcfe0f50f46 100644 --- a/src/Files.App/Views/LayoutModes/ColumnViewBase.xaml +++ b/src/Files.App/Views/LayoutModes/ColumnViewBase.xaml @@ -192,6 +192,8 @@ CornerRadius="{StaticResource ControlCornerRadius}" IsRightTapEnabled="True" Loaded="Grid_Loaded" + PointerEntered="ListedItem_PointerEntered" + PointerExited="ListedItem_PointerExited" RightTapped="StackPanel_RightTapped" ToolTipService.ToolTip="{x:Bind ItemName, Mode=OneWay}"> diff --git a/src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml b/src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml index 9c14ba460de1..f1c8ef0c418f 100644 --- a/src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml +++ b/src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml @@ -507,6 +507,8 @@ AutomationProperties.Name="{x:Bind ItemName, Mode=OneWay}" IsRightTapEnabled="True" Loaded="Grid_Loaded" + PointerEntered="ListedItem_PointerEntered" + PointerExited="ListedItem_PointerExited" RightTapped="StackPanel_RightTapped" ToolTipService.ToolTip="{x:Bind ItemTooltipText, Mode=OneWay}"> diff --git a/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml b/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml index 72fc56a34322..26022a3abce6 100644 --- a/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml +++ b/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml @@ -62,6 +62,8 @@ Background="Transparent" IsRightTapEnabled="True" Loaded="Grid_Loaded" + PointerEntered="ListedItem_PointerEntered" + PointerExited="ListedItem_PointerExited" RightTapped="StackPanel_RightTapped" ToolTipService.ToolTip="{x:Bind ItemTooltipText, Mode=OneWay}"> @@ -214,6 +216,8 @@ ColumnSpacing="4" IsRightTapEnabled="True" Loaded="Grid_Loaded" + PointerEntered="ListedItem_PointerEntered" + PointerExited="ListedItem_PointerExited" RightTapped="StackPanel_RightTapped" ToolTipService.ToolTip="{x:Bind ItemTooltipText, Mode=OneWay}"> diff --git a/src/Files.App/Views/SettingsPages/Preferences.xaml b/src/Files.App/Views/SettingsPages/Preferences.xaml index bad4ac8c4464..6318a060aa56 100644 --- a/src/Files.App/Views/SettingsPages/Preferences.xaml +++ b/src/Files.App/Views/SettingsPages/Preferences.xaml @@ -162,7 +162,14 @@ IsOn="{Binding ListAndSortDirectoriesAlongsideFiles, Mode=TwoWay}" Style="{StaticResource RightAlignedToggleSwitchStyle}" /> - + + + + + diff --git a/src/Files.Backend/Services/Settings/IPreferencesSettingsService.cs b/src/Files.Backend/Services/Settings/IPreferencesSettingsService.cs index f37e820f3d89..04baacd03cce 100644 --- a/src/Files.Backend/Services/Settings/IPreferencesSettingsService.cs +++ b/src/Files.Backend/Services/Settings/IPreferencesSettingsService.cs @@ -45,6 +45,11 @@ public interface IPreferencesSettingsService : IBaseSettingsService, INotifyProp /// bool ShowDotFiles{ get; set; } + /// + /// Gets or sets a value indicating whether or not to select files and folders when hovering them. + /// + bool SelectOnHover { get; set; } + /// /// Gets or sets a value indicating whether or not files should open with one click. /// From 5ed553fe2d7ba9311cdff5850160d37e1538a235 Mon Sep 17 00:00:00 2001 From: ferrariofilippo Date: Mon, 12 Sep 2022 13:40:31 +0200 Subject: [PATCH 02/15] Edit: Code Style --- .../Settings/PreferencesSettingsService.cs | 4 ++-- src/Files.App/Strings/en-US/Resources.resw | 4 ++-- .../ViewModels/SettingsViewModels/PreferencesViewModel.cs | 8 ++++---- src/Files.App/Views/LayoutModes/ColumnViewBase.xaml | 4 ++-- src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml | 4 ++-- src/Files.App/Views/LayoutModes/GridViewBrowser.xaml | 6 +++--- src/Files.App/Views/SettingsPages/Preferences.xaml | 6 +++--- .../Services/Settings/IPreferencesSettingsService.cs | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Files.App/ServicesImplementation/Settings/PreferencesSettingsService.cs b/src/Files.App/ServicesImplementation/Settings/PreferencesSettingsService.cs index e970c742ccb0..08b6bf3e86df 100644 --- a/src/Files.App/ServicesImplementation/Settings/PreferencesSettingsService.cs +++ b/src/Files.App/ServicesImplementation/Settings/PreferencesSettingsService.cs @@ -62,7 +62,7 @@ public bool ShowDotFiles set => Set(value); } - public bool SelectOnHover + public bool SelectFilesOnHover { get => Get(false); set => Set(value); @@ -157,7 +157,7 @@ protected override void RaiseOnSettingChangedEvent(object sender, SettingChanged case nameof(AreSystemItemsHidden): case nameof(AreAlternateStreamsVisible): case nameof(ShowDotFiles): - case nameof(SelectOnHover): + case nameof(SelectFilesOnHover): case nameof(OpenFilesWithOneClick): case nameof(OpenFoldersWithOneClick): case nameof(ColumnLayoutOpenFoldersWithOneClick): diff --git a/src/Files.App/Strings/en-US/Resources.resw b/src/Files.App/Strings/en-US/Resources.resw index 583698781db4..be952a803b78 100644 --- a/src/Files.App/Strings/en-US/Resources.resw +++ b/src/Files.App/Strings/en-US/Resources.resw @@ -2825,7 +2825,7 @@ We use App Center to track which settings are being used, find bugs, and fix cra Behavior for opening files and folders - - Select files and folders hovering them + + Select files and folders hovering them \ No newline at end of file diff --git a/src/Files.App/ViewModels/SettingsViewModels/PreferencesViewModel.cs b/src/Files.App/ViewModels/SettingsViewModels/PreferencesViewModel.cs index 6354c93b7b01..f0ca0ed519f3 100644 --- a/src/Files.App/ViewModels/SettingsViewModels/PreferencesViewModel.cs +++ b/src/Files.App/ViewModels/SettingsViewModels/PreferencesViewModel.cs @@ -589,14 +589,14 @@ public bool ShowThumbnails } } - public bool SelectOnHover + public bool SelectFilesOnHover { - get => UserSettingsService.PreferencesSettingsService.SelectOnHover; + get => UserSettingsService.PreferencesSettingsService.SelectFilesOnHover; set { - if (value != UserSettingsService.PreferencesSettingsService.SelectOnHover) + if (value != UserSettingsService.PreferencesSettingsService.SelectFilesOnHover) { - UserSettingsService.PreferencesSettingsService.SelectOnHover = value; + UserSettingsService.PreferencesSettingsService.SelectFilesOnHover = value; OnPropertyChanged(); } } diff --git a/src/Files.App/Views/LayoutModes/ColumnViewBase.xaml b/src/Files.App/Views/LayoutModes/ColumnViewBase.xaml index 3bcfe0f50f46..19d540adb6c8 100644 --- a/src/Files.App/Views/LayoutModes/ColumnViewBase.xaml +++ b/src/Files.App/Views/LayoutModes/ColumnViewBase.xaml @@ -192,8 +192,8 @@ CornerRadius="{StaticResource ControlCornerRadius}" IsRightTapEnabled="True" Loaded="Grid_Loaded" - PointerEntered="ListedItem_PointerEntered" - PointerExited="ListedItem_PointerExited" + PointerEntered="ListedItem_PointerEntered" + PointerExited="ListedItem_PointerExited" RightTapped="StackPanel_RightTapped" ToolTipService.ToolTip="{x:Bind ItemName, Mode=OneWay}"> diff --git a/src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml b/src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml index f1c8ef0c418f..8baa499edf49 100644 --- a/src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml +++ b/src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml @@ -507,8 +507,8 @@ AutomationProperties.Name="{x:Bind ItemName, Mode=OneWay}" IsRightTapEnabled="True" Loaded="Grid_Loaded" - PointerEntered="ListedItem_PointerEntered" - PointerExited="ListedItem_PointerExited" + PointerEntered="ListedItem_PointerEntered" + PointerExited="ListedItem_PointerExited" RightTapped="StackPanel_RightTapped" ToolTipService.ToolTip="{x:Bind ItemTooltipText, Mode=OneWay}"> diff --git a/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml b/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml index 26022a3abce6..f7527e8c07bf 100644 --- a/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml +++ b/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml @@ -62,8 +62,8 @@ Background="Transparent" IsRightTapEnabled="True" Loaded="Grid_Loaded" - PointerEntered="ListedItem_PointerEntered" - PointerExited="ListedItem_PointerExited" + PointerEntered="ListedItem_PointerEntered" + PointerExited="ListedItem_PointerExited" RightTapped="StackPanel_RightTapped" ToolTipService.ToolTip="{x:Bind ItemTooltipText, Mode=OneWay}"> @@ -217,7 +217,7 @@ IsRightTapEnabled="True" Loaded="Grid_Loaded" PointerEntered="ListedItem_PointerEntered" - PointerExited="ListedItem_PointerExited" + PointerExited="ListedItem_PointerExited" RightTapped="StackPanel_RightTapped" ToolTipService.ToolTip="{x:Bind ItemTooltipText, Mode=OneWay}"> diff --git a/src/Files.App/Views/SettingsPages/Preferences.xaml b/src/Files.App/Views/SettingsPages/Preferences.xaml index 6318a060aa56..fcc228add865 100644 --- a/src/Files.App/Views/SettingsPages/Preferences.xaml +++ b/src/Files.App/Views/SettingsPages/Preferences.xaml @@ -163,10 +163,10 @@ Style="{StaticResource RightAlignedToggleSwitchStyle}" /> - + diff --git a/src/Files.Backend/Services/Settings/IPreferencesSettingsService.cs b/src/Files.Backend/Services/Settings/IPreferencesSettingsService.cs index 04baacd03cce..5d4c38a21c8b 100644 --- a/src/Files.Backend/Services/Settings/IPreferencesSettingsService.cs +++ b/src/Files.Backend/Services/Settings/IPreferencesSettingsService.cs @@ -48,7 +48,7 @@ public interface IPreferencesSettingsService : IBaseSettingsService, INotifyProp /// /// Gets or sets a value indicating whether or not to select files and folders when hovering them. /// - bool SelectOnHover { get; set; } + bool SelectFilesOnHover { get; set; } /// /// Gets or sets a value indicating whether or not files should open with one click. From bd54a1dabeffc55090f000a8f3b4af7d71818cbf Mon Sep 17 00:00:00 2001 From: ferrariofilippo Date: Mon, 12 Sep 2022 14:36:03 +0200 Subject: [PATCH 03/15] Fix --- src/Files.App/BaseLayout.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Files.App/BaseLayout.cs b/src/Files.App/BaseLayout.cs index 728747d35573..1cac66bbd15b 100644 --- a/src/Files.App/BaseLayout.cs +++ b/src/Files.App/BaseLayout.cs @@ -957,7 +957,7 @@ protected static void FileListItem_PointerPressed(object sender, PointerRoutedEv protected internal void ListedItem_PointerEntered(object sender, PointerRoutedEventArgs e) { - if (!UserSettingsService.PreferencesSettingsService.SelectOnHover) + if (!UserSettingsService.PreferencesSettingsService.SelectFilesOnHover) return; var hovered = (sender as Grid)?.DataContext as ListedItem; @@ -979,7 +979,7 @@ protected internal void ListedItem_PointerEntered(object sender, PointerRoutedEv protected internal void ListedItem_PointerExited(object sender, PointerRoutedEventArgs e) { - if (!UserSettingsService.PreferencesSettingsService.SelectOnHover) + if (!UserSettingsService.PreferencesSettingsService.SelectFilesOnHover) return; hoverTimer.Stop(); hoveredItem = null; From b4191972fccbc76b6ab8e7366449bfb61759a4af Mon Sep 17 00:00:00 2001 From: Yair Aichenbaum <39923744+yaichenbaum@users.noreply.github.com> Date: Mon, 12 Sep 2022 10:19:27 -0400 Subject: [PATCH 04/15] Update src/Files.App/Strings/en-US/Resources.resw --- src/Files.App/Strings/en-US/Resources.resw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Files.App/Strings/en-US/Resources.resw b/src/Files.App/Strings/en-US/Resources.resw index be952a803b78..f37a52cf6e50 100644 --- a/src/Files.App/Strings/en-US/Resources.resw +++ b/src/Files.App/Strings/en-US/Resources.resw @@ -2826,6 +2826,6 @@ We use App Center to track which settings are being used, find bugs, and fix cra Behavior for opening files and folders - Select files and folders hovering them + Select files and folders when hovering over them \ No newline at end of file From 169e09e1abe5d39bc94c9e31e57c1c661c170a54 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Tue, 13 Sep 2022 00:04:15 +0200 Subject: [PATCH 05/15] Nullable changes --- src/Files.App/BaseLayout.cs | 144 ++++++++++++++++------------------- src/Files.App/IBaseLayout.cs | 2 +- 2 files changed, 68 insertions(+), 78 deletions(-) diff --git a/src/Files.App/BaseLayout.cs b/src/Files.App/BaseLayout.cs index 1cac66bbd15b..27c76e25da00 100644 --- a/src/Files.App/BaseLayout.cs +++ b/src/Files.App/BaseLayout.cs @@ -56,9 +56,9 @@ public abstract class BaseLayout : Page, IBaseLayout, INotifyPropertyChanged public SelectedItemsPropertiesViewModel SelectedItemsPropertiesViewModel { get; } - public FolderSettingsViewModel? FolderSettings => ParentShellPageInstance?.InstanceViewModel.FolderSettings; + public FolderSettingsViewModel FolderSettings => ParentShellPageInstance.InstanceViewModel.FolderSettings; - public CurrentInstanceViewModel? InstanceViewModel => ParentShellPageInstance?.InstanceViewModel; + public CurrentInstanceViewModel InstanceViewModel => ParentShellPageInstance.InstanceViewModel; public IPaneViewModel PaneViewModel => App.PaneViewModel; @@ -74,9 +74,9 @@ public abstract class BaseLayout : Page, IBaseLayout, INotifyPropertyChanged AlwaysExpanded = true, }; - public BaseLayoutCommandsViewModel? CommandsViewModel { get; protected set; } + public BaseLayoutCommandsViewModel CommandsViewModel { get; protected set; } = null!; // Non-null after OnNavigatedTo() - public IShellPage? ParentShellPageInstance { get; private set; } = null; + public IShellPage ParentShellPageInstance { get; private set; } = null!; // Non-null after OnNavigatedTo() public bool IsRenamingItem { get; set; } = false; public ListedItem? RenamingItem { get; set; } = null; @@ -112,16 +112,16 @@ public CollectionViewSource CollectionViewSource { if (collectionViewSource == value) return; - if (collectionViewSource?.View is not null) + if (collectionViewSource.View is not null) collectionViewSource.View.VectorChanged -= View_VectorChanged; collectionViewSource = value; NotifyPropertyChanged(nameof(CollectionViewSource)); - if (collectionViewSource?.View is not null) + if (collectionViewSource.View is not null) collectionViewSource.View.VectorChanged += View_VectorChanged; } } - protected NavigationArguments? navigationArguments; + protected NavigationArguments navigationArguments = null!; // Non-null after OnNavigatedTo() private bool isItemSelected = false; @@ -164,7 +164,7 @@ public string JumpString if (previouslySelectedItem != null) { // Use FilesAndFolders because only displayed entries should be jumped to - IEnumerable candidateItems = ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders + IEnumerable candidateItems = ParentShellPageInstance.FilesystemViewModel.FilesAndFolders .SkipWhile(x => x != previouslySelectedItem) .Skip(value.Length == 1 ? 1 : 0) // User is trying to cycle through items starting with the same letter .Where(f => f.ItemName.Length >= value.Length && string.Equals(f.ItemName.Substring(0, value.Length), value, StringComparison.OrdinalIgnoreCase)); @@ -174,7 +174,7 @@ public string JumpString if (jumpedToItem == null) { // Use FilesAndFolders because only displayed entries should be jumped to - IEnumerable candidateItems = ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders + IEnumerable candidateItems = ParentShellPageInstance.FilesystemViewModel.FilesAndFolders .Where(f => f.ItemName.Length >= value.Length && string.Equals(f.ItemName.Substring(0, value.Length), value, StringComparison.OrdinalIgnoreCase)); jumpedToItem = candidateItems.FirstOrDefault(); } @@ -193,9 +193,9 @@ public string JumpString } } - private List? selectedItems = new List(); + private List selectedItems = new List(); - public List? SelectedItems + public List SelectedItems { get { @@ -203,20 +203,23 @@ public List? SelectedItems } internal set { - //if (!(value?.All(x => selectedItems?.Contains(x) ?? false) ?? value == selectedItems)) // check if the new list is different then the old one + if (value is null) + return; + + //if (!(value.All(x => selectedItems.Contains(x) ?? false) ?? value == selectedItems)) // check if the new list is different then the old one if (value != selectedItems) // check if the new list is different then the old one { - if (value?.FirstOrDefault() != selectedItems?.FirstOrDefault()) + if (value.FirstOrDefault() != selectedItems.FirstOrDefault()) { // update preview pane properties - if (value?.Count == 1) + if (value.Count == 1) { App.PreviewPaneViewModel.IsItemSelected = true; App.PreviewPaneViewModel.SelectedItem = value.First(); } else { - App.PreviewPaneViewModel.IsItemSelected = value?.Count > 0; + App.PreviewPaneViewModel.IsItemSelected = value.Count > 0; App.PreviewPaneViewModel.SelectedItem = null; } @@ -230,7 +233,7 @@ internal set } selectedItems = value; - if (selectedItems?.Count == 0 || selectedItems?[0] == null) + if (selectedItems.Count == 0 || selectedItems[0] == null) { IsItemSelected = false; SelectedItem = null; @@ -245,10 +248,10 @@ internal set SelectedItemsPropertiesViewModel.IsItemSelected = true; UpdateSelectionSize(); - if (SelectedItems?.Count >= 1) + if (SelectedItems.Count >= 1) SelectedItemsPropertiesViewModel.SelectedItemsCount = SelectedItems.Count; - if (SelectedItems?.Count == 1) + if (SelectedItems.Count == 1) { SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems.Count} {"ItemSelected/Text".GetLocalizedResource()}"; DispatcherQueue.EnqueueAsync(async () => @@ -259,7 +262,7 @@ internal set } else { - SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems!.Count} {"ItemsSelected/Text".GetLocalizedResource()}"; + SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems.Count} {"ItemsSelected/Text".GetLocalizedResource()}"; ResetRenameDoubleClick(); } } @@ -268,7 +271,7 @@ internal set //ItemManipulationModel.SetDragModeForItems(); } - ParentShellPageInstance!.ToolbarViewModel.SelectedItems = value; + ParentShellPageInstance.ToolbarViewModel.SelectedItems = value; } } @@ -323,22 +326,17 @@ private void JumpTimer_Tick(object sender, object e) protected abstract void InitializeCommandsViewModel(); - protected IEnumerable? GetAllItems() + protected IEnumerable GetAllItems() { - if (CollectionViewSource.IsSourceGrouped) - // add all items from each group to the new list - return (CollectionViewSource.Source as BulkConcurrentObservableCollection>)?.SelectMany(g => g); - - return CollectionViewSource.Source as IEnumerable; + var items = CollectionViewSource.IsSourceGrouped ? // add all items from each group to the new list + (CollectionViewSource.Source as BulkConcurrentObservableCollection>)?.SelectMany(g => g) : + CollectionViewSource.Source as IEnumerable; + return items ?? new List(); } public virtual void ResetItemOpacity() { - var items = GetAllItems(); - if (items == null) - return; - - foreach (var item in items) + foreach (var item in GetAllItems()) { if (item != null) item.Opacity = item.IsHiddenItem ? Constants.UI.DimItemOpacity : 1.0d; @@ -358,16 +356,15 @@ public virtual void ResetItemOpacity() protected virtual void BaseFolderSettings_LayoutModeChangeRequested(object? sender, LayoutModeEventArgs e) { - if (ParentShellPageInstance?.SlimContentPage != null) + if (ParentShellPageInstance.SlimContentPage != null) { - var layoutType = FolderSettings?.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); + var layoutType = FolderSettings.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); - if (layoutType is not null && - layoutType != ParentShellPageInstance.CurrentPageType) + if (layoutType != ParentShellPageInstance.CurrentPageType) { ParentShellPageInstance.NavigateWithArguments(layoutType, new NavigationArguments() { - NavPathParam = navigationArguments!.NavPathParam, + NavPathParam = navigationArguments.NavPathParam, IsSearchResultPage = navigationArguments.IsSearchResultPage, SearchPathParam = navigationArguments.SearchPathParam, SearchQuery = navigationArguments.SearchQuery, @@ -400,7 +397,7 @@ protected override async void OnNavigatedTo(NavigationEventArgs eventArgs) InitializeCommandsViewModel(); IsItemSelected = false; - FolderSettings!.LayoutModeChangeRequested += BaseFolderSettings_LayoutModeChangeRequested; + FolderSettings.LayoutModeChangeRequested += BaseFolderSettings_LayoutModeChangeRequested; FolderSettings.GroupOptionPreferenceUpdated += FolderSettings_GroupOptionPreferenceUpdated; ParentShellPageInstance.FilesystemViewModel.EmptyTextType = EmptyTextType.None; ParentShellPageInstance.ToolbarViewModel.UpdateSortAndGroupOptions(); @@ -456,7 +453,7 @@ protected override async void OnNavigatedTo(NavigationEventArgs eventArgs) { Query = navigationArguments.SearchQuery, Folder = navigationArguments.SearchPathParam, - ThumbnailSize = InstanceViewModel!.FolderSettings.GetIconSize(), + ThumbnailSize = InstanceViewModel.FolderSettings.GetIconSize(), SearchUnindexedItems = navigationArguments.SearchUnindexedItems }; _ = ParentShellPageInstance.FilesystemViewModel.SearchAsync(searchInstance); @@ -482,7 +479,7 @@ public void SetSelectedItemsOnNavigation() { List liItemsToSelect = new List(); foreach (string item in navigationArguments.SelectItems) - liItemsToSelect.Add(ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders.Where((li) => li.ItemNameRaw == item).First()); + liItemsToSelect.Add(ParentShellPageInstance.FilesystemViewModel.FilesAndFolders.Where((li) => li.ItemNameRaw == item).First()); ItemManipulationModel.SetSelectedItems(liItemsToSelect); ItemManipulationModel.FocusSelectedItems(); @@ -505,7 +502,7 @@ private async void FolderSettings_GroupOptionPreferenceUpdated(object? sender, G groupingCancellationToken?.Cancel(); groupingCancellationToken = new CancellationTokenSource(); var token = groupingCancellationToken.Token; - await ParentShellPageInstance!.FilesystemViewModel.GroupOptionsUpdated(token); + await ParentShellPageInstance.FilesystemViewModel.GroupOptionsUpdated(token); UpdateCollectionViewSource(); await ParentShellPageInstance.FilesystemViewModel.ReloadItemGroupHeaderImagesAsync(); } @@ -515,14 +512,14 @@ protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) base.OnNavigatingFrom(e); // Remove item jumping handler this.CharacterReceived -= Page_CharacterReceived; - FolderSettings!.LayoutModeChangeRequested -= BaseFolderSettings_LayoutModeChangeRequested; + FolderSettings.LayoutModeChangeRequested -= BaseFolderSettings_LayoutModeChangeRequested; FolderSettings.GroupOptionPreferenceUpdated -= FolderSettings_GroupOptionPreferenceUpdated; ItemContextMenuFlyout.Opening -= ItemContextFlyout_Opening; BaseContextMenuFlyout.Opening -= BaseContextFlyout_Opening; var parameter = e.Parameter as NavigationArguments; if (parameter is not null && !parameter.IsLayoutSwitch) - ParentShellPageInstance!.FilesystemViewModel.CancelLoadAndClearFiles(); + ParentShellPageInstance.FilesystemViewModel.CancelLoadAndClearFiles(); } public async void ItemContextFlyout_Opening(object? sender, object e) @@ -554,20 +551,19 @@ public async void BaseContextFlyout_Opening(object? sender, object e) shellContextMenuItemCancellationToken?.Cancel(); shellContextMenuItemCancellationToken = new CancellationTokenSource(); var shiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down); - var items = ContextFlyoutItemHelper.GetBaseContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, itemViewModel: ParentShellPageInstance!.FilesystemViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, false); + var items = ContextFlyoutItemHelper.GetBaseContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel, itemViewModel: ParentShellPageInstance.FilesystemViewModel, commandsViewModel: CommandsViewModel, shiftPressed: shiftPressed, false); BaseContextMenuFlyout.PrimaryCommands.Clear(); BaseContextMenuFlyout.SecondaryCommands.Clear(); var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); - primaryElements.Where(i => i is AppBarButton).ForEach(i => + primaryElements.OfType().ForEach(i => { - if (i is AppBarButton button) - button.Click += new RoutedEventHandler((s, e) => BaseContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) + i.Click += new RoutedEventHandler((s, e) => BaseContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) }); primaryElements.ForEach(i => BaseContextMenuFlyout.PrimaryCommands.Add(i)); secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width secondaryElements.ForEach(i => BaseContextMenuFlyout.SecondaryCommands.Add(i)); - if (!InstanceViewModel!.IsPageTypeSearchResults && !InstanceViewModel.IsPageTypeZipFolder) + if (!InstanceViewModel.IsPageTypeSearchResults && !InstanceViewModel.IsPageTypeZipFolder) { var shellMenuItems = await ContextFlyoutItemHelper.GetBaseContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); if (shellMenuItems.Any()) @@ -582,9 +578,7 @@ public async void BaseContextFlyout_Opening(object? sender, object e) public void UpdateSelectionSize() { - var items = (selectedItems?.Any() ?? false) ? selectedItems : GetAllItems(); - if (items is null) - return; + var items = selectedItems.Any() ? selectedItems : GetAllItems(); bool isSizeKnown = !items.Any(item => string.IsNullOrEmpty(item.FileSize)); if (isSizeKnown) { @@ -606,27 +600,26 @@ private async Task LoadMenuItemsAsync() itc.MaxHeight = Constants.UI.ContextMenuMaxHeight; // Reset menu max height shellContextMenuItemCancellationToken?.Cancel(); shellContextMenuItemCancellationToken = new CancellationTokenSource(); - SelectedItemsPropertiesViewModel.CheckAllFileExtensions(this.SelectedItems!.Select(selectedItem => selectedItem?.FileExtension).ToList()!); + SelectedItemsPropertiesViewModel.CheckAllFileExtensions(SelectedItems.Select(selectedItem => selectedItem?.FileExtension).ToList()); var shiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down); - var items = ContextFlyoutItemHelper.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, workingDir: ParentShellPageInstance!.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, selectedItemsPropertiesViewModel: SelectedItemsPropertiesViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, showOpenMenu: false); + var items = ContextFlyoutItemHelper.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems, selectedItemsPropertiesViewModel: SelectedItemsPropertiesViewModel, commandsViewModel: CommandsViewModel, shiftPressed: shiftPressed, showOpenMenu: false); ItemContextMenuFlyout.PrimaryCommands.Clear(); ItemContextMenuFlyout.SecondaryCommands.Clear(); var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); - primaryElements.Where(i => i is AppBarButton).ForEach(i => + primaryElements.OfType().ForEach(i => { - if (i is AppBarButton button) - button.Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) + i.Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) }); primaryElements.ForEach(i => ItemContextMenuFlyout.PrimaryCommands.Add(i)); secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width secondaryElements.ForEach(i => ItemContextMenuFlyout.SecondaryCommands.Add(i)); - if (InstanceViewModel!.CanTagFilesInPage) + if (InstanceViewModel.CanTagFilesInPage) AddNewFileTagsToMenu(ItemContextMenuFlyout); if (!InstanceViewModel.IsPageTypeZipFolder) { - var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); + var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); if (shellMenuItems.Any()) AddShellItemsToMenu(shellMenuItems, ItemContextMenuFlyout, shiftPressed); } @@ -634,7 +627,7 @@ private async Task LoadMenuItemsAsync() private void AddNewFileTagsToMenu(CommandBarFlyout contextMenu) { - var fileTagsContextMenu = new FileTagsContextMenu(SelectedItems!); + var fileTagsContextMenu = new FileTagsContextMenu(SelectedItems); var overflowSeparator = contextMenu.SecondaryCommands.FirstOrDefault(x => x is FrameworkElement fe && fe.Tag as string == "OverflowSeparator") as AppBarSeparator; var index = contextMenu.SecondaryCommands.IndexOf(overflowSeparator); index = index >= 0 ? index : contextMenu.SecondaryCommands.Count; @@ -710,12 +703,12 @@ private void AddShellItemsToMenu(List shellMenuI // add items to openwith dropdown var openWithOverflow = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWithOverflow") as AppBarButton; - if (openWithSubItems is not null && openWithOverflow is not null) + var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; + if (openWithSubItems is not null && openWithOverflow is not null && openWith is not null) { - var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; - var flyout = openWithOverflow.Flyout as MenuFlyout; + var flyout = (MenuFlyout)openWithOverflow.Flyout; - flyout!.Items.Clear(); + flyout.Items.Clear(); foreach (var item in openWithSubItems) { @@ -723,7 +716,7 @@ private void AddShellItemsToMenu(List shellMenuI } openWithOverflow.Flyout = flyout; - openWith!.Visibility = Visibility.Collapsed; + openWith.Visibility = Visibility.Collapsed; openWithOverflow.Visibility = Visibility.Visible; } @@ -735,7 +728,7 @@ private void AddShellItemsToMenu(List shellMenuI label.TextTrimming = TextTrimming.CharacterEllipsis; if ((item as AppBarButton)?.Flyout as MenuFlyout is MenuFlyout flyout) // Close main menu when clicking on subitems (#5508) { - Action>? clickAction = null; + Action> clickAction = null!; clickAction = (items) => { items.OfType().ForEach(i => @@ -744,7 +737,7 @@ private void AddShellItemsToMenu(List shellMenuI }); items.OfType().ForEach(i => { - clickAction!(i.Items); + clickAction(i.Items); }); }; clickAction(flyout.Items); @@ -755,7 +748,7 @@ private void AddShellItemsToMenu(List shellMenuI protected virtual void Page_CharacterReceived(UIElement sender, CharacterReceivedRoutedEventArgs args) { - if (ParentShellPageInstance!.IsCurrentInstance) + if (ParentShellPageInstance.IsCurrentInstance) { char letter = args.Character; JumpString += letter.ToString().ToLowerInvariant(); @@ -764,7 +757,7 @@ protected virtual void Page_CharacterReceived(UIElement sender, CharacterReceive protected void FileList_DragItemsStarting(object sender, DragItemsStartingEventArgs e) { - e.Items.OfType().ForEach(item => SelectedItems!.Add(item)); + e.Items.OfType().ForEach(item => SelectedItems.Add(item)); try { @@ -810,7 +803,7 @@ protected async void Item_DragOver(object sender, DragEventArgs e) { dragOverItem = null; dragOverTimer.Stop(); - NavigationHelpers.OpenSelectedItems(ParentShellPageInstance!, false); + NavigationHelpers.OpenSelectedItems(ParentShellPageInstance, false); } }, TimeSpan.FromMilliseconds(1000), false); } @@ -893,7 +886,7 @@ protected async void Item_Drop(object sender, DragEventArgs e) var item = GetItemFromElement(sender); if (item != null) - await ParentShellPageInstance!.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as ShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable); + await ParentShellPageInstance.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as ShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable); deferral.Complete(); } @@ -919,7 +912,7 @@ private void RefreshItem(SelectorItem container, object item, bool inRecycleQueu if (inRecycleQueue) { - ParentShellPageInstance!.FilesystemViewModel.CancelExtendedPropertiesLoadingForItem(listedItem); + ParentShellPageInstance.FilesystemViewModel.CancelExtendedPropertiesLoadingForItem(listedItem); } else { @@ -930,7 +923,7 @@ private void RefreshItem(SelectorItem container, object item, bool inRecycleQueu uint callbackPhase = 3; args.RegisterUpdateCallback(callbackPhase, async (s, c) => { - await ParentShellPageInstance!.FilesystemViewModel.LoadExtendedItemProperties(listedItem, IconSize); + await ParentShellPageInstance.FilesystemViewModel.LoadExtendedItemProperties(listedItem, IconSize); }); } } @@ -1031,10 +1024,8 @@ protected void ItemsLayout_Drop(object sender, DragEventArgs e) CommandsViewModel?.DropCommand?.Execute(e); } - public void UpdateCollectionViewSource() + private void UpdateCollectionViewSource() { - if (ParentShellPageInstance is null) - return; if (ParentShellPageInstance.FilesystemViewModel.FilesAndFolders.IsGrouped) { CollectionViewSource = new CollectionViewSource() @@ -1086,7 +1077,7 @@ protected void RootPanel_PointerPressed(object sender, PointerRoutedEventArgs e) private void ItemManipulationModel_RefreshItemsOpacityInvoked(object? sender, EventArgs e) { - foreach (ListedItem listedItem in GetAllItems()!) + foreach (ListedItem listedItem in GetAllItems()) { if (listedItem.IsHiddenItem) listedItem.Opacity = Constants.UI.DimItemOpacity; @@ -1097,8 +1088,7 @@ private void ItemManipulationModel_RefreshItemsOpacityInvoked(object? sender, Ev private void View_VectorChanged(IObservableVector sender, IVectorChangedEventArgs @event) { - if (ParentShellPageInstance is not null) - ParentShellPageInstance.ToolbarViewModel.HasItem = CollectionViewSource.View.Any(); + ParentShellPageInstance.ToolbarViewModel.HasItem = CollectionViewSource.View.Any(); } virtual public void StartRenameItem() { } diff --git a/src/Files.App/IBaseLayout.cs b/src/Files.App/IBaseLayout.cs index cd7e10c4fa3c..887f5fd223c1 100644 --- a/src/Files.App/IBaseLayout.cs +++ b/src/Files.App/IBaseLayout.cs @@ -15,7 +15,7 @@ public interface IBaseLayout : IDisposable bool IsMiddleClickToScrollEnabled { get; set; } - public List? SelectedItems { get; } + public List SelectedItems { get; } public ListedItem? SelectedItem { get; } From ff95e0b580cefc71abf1f1cc810881f0630f97ad Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Tue, 13 Sep 2022 00:24:09 +0200 Subject: [PATCH 06/15] Revert "Nullable changes" This reverts commit 169e09e1abe5d39bc94c9e31e57c1c661c170a54. --- src/Files.App/BaseLayout.cs | 144 +++++++++++++++++++---------------- src/Files.App/IBaseLayout.cs | 2 +- 2 files changed, 78 insertions(+), 68 deletions(-) diff --git a/src/Files.App/BaseLayout.cs b/src/Files.App/BaseLayout.cs index 27c76e25da00..1cac66bbd15b 100644 --- a/src/Files.App/BaseLayout.cs +++ b/src/Files.App/BaseLayout.cs @@ -56,9 +56,9 @@ public abstract class BaseLayout : Page, IBaseLayout, INotifyPropertyChanged public SelectedItemsPropertiesViewModel SelectedItemsPropertiesViewModel { get; } - public FolderSettingsViewModel FolderSettings => ParentShellPageInstance.InstanceViewModel.FolderSettings; + public FolderSettingsViewModel? FolderSettings => ParentShellPageInstance?.InstanceViewModel.FolderSettings; - public CurrentInstanceViewModel InstanceViewModel => ParentShellPageInstance.InstanceViewModel; + public CurrentInstanceViewModel? InstanceViewModel => ParentShellPageInstance?.InstanceViewModel; public IPaneViewModel PaneViewModel => App.PaneViewModel; @@ -74,9 +74,9 @@ public abstract class BaseLayout : Page, IBaseLayout, INotifyPropertyChanged AlwaysExpanded = true, }; - public BaseLayoutCommandsViewModel CommandsViewModel { get; protected set; } = null!; // Non-null after OnNavigatedTo() + public BaseLayoutCommandsViewModel? CommandsViewModel { get; protected set; } - public IShellPage ParentShellPageInstance { get; private set; } = null!; // Non-null after OnNavigatedTo() + public IShellPage? ParentShellPageInstance { get; private set; } = null; public bool IsRenamingItem { get; set; } = false; public ListedItem? RenamingItem { get; set; } = null; @@ -112,16 +112,16 @@ public CollectionViewSource CollectionViewSource { if (collectionViewSource == value) return; - if (collectionViewSource.View is not null) + if (collectionViewSource?.View is not null) collectionViewSource.View.VectorChanged -= View_VectorChanged; collectionViewSource = value; NotifyPropertyChanged(nameof(CollectionViewSource)); - if (collectionViewSource.View is not null) + if (collectionViewSource?.View is not null) collectionViewSource.View.VectorChanged += View_VectorChanged; } } - protected NavigationArguments navigationArguments = null!; // Non-null after OnNavigatedTo() + protected NavigationArguments? navigationArguments; private bool isItemSelected = false; @@ -164,7 +164,7 @@ public string JumpString if (previouslySelectedItem != null) { // Use FilesAndFolders because only displayed entries should be jumped to - IEnumerable candidateItems = ParentShellPageInstance.FilesystemViewModel.FilesAndFolders + IEnumerable candidateItems = ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders .SkipWhile(x => x != previouslySelectedItem) .Skip(value.Length == 1 ? 1 : 0) // User is trying to cycle through items starting with the same letter .Where(f => f.ItemName.Length >= value.Length && string.Equals(f.ItemName.Substring(0, value.Length), value, StringComparison.OrdinalIgnoreCase)); @@ -174,7 +174,7 @@ public string JumpString if (jumpedToItem == null) { // Use FilesAndFolders because only displayed entries should be jumped to - IEnumerable candidateItems = ParentShellPageInstance.FilesystemViewModel.FilesAndFolders + IEnumerable candidateItems = ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders .Where(f => f.ItemName.Length >= value.Length && string.Equals(f.ItemName.Substring(0, value.Length), value, StringComparison.OrdinalIgnoreCase)); jumpedToItem = candidateItems.FirstOrDefault(); } @@ -193,9 +193,9 @@ public string JumpString } } - private List selectedItems = new List(); + private List? selectedItems = new List(); - public List SelectedItems + public List? SelectedItems { get { @@ -203,23 +203,20 @@ public List SelectedItems } internal set { - if (value is null) - return; - - //if (!(value.All(x => selectedItems.Contains(x) ?? false) ?? value == selectedItems)) // check if the new list is different then the old one + //if (!(value?.All(x => selectedItems?.Contains(x) ?? false) ?? value == selectedItems)) // check if the new list is different then the old one if (value != selectedItems) // check if the new list is different then the old one { - if (value.FirstOrDefault() != selectedItems.FirstOrDefault()) + if (value?.FirstOrDefault() != selectedItems?.FirstOrDefault()) { // update preview pane properties - if (value.Count == 1) + if (value?.Count == 1) { App.PreviewPaneViewModel.IsItemSelected = true; App.PreviewPaneViewModel.SelectedItem = value.First(); } else { - App.PreviewPaneViewModel.IsItemSelected = value.Count > 0; + App.PreviewPaneViewModel.IsItemSelected = value?.Count > 0; App.PreviewPaneViewModel.SelectedItem = null; } @@ -233,7 +230,7 @@ internal set } selectedItems = value; - if (selectedItems.Count == 0 || selectedItems[0] == null) + if (selectedItems?.Count == 0 || selectedItems?[0] == null) { IsItemSelected = false; SelectedItem = null; @@ -248,10 +245,10 @@ internal set SelectedItemsPropertiesViewModel.IsItemSelected = true; UpdateSelectionSize(); - if (SelectedItems.Count >= 1) + if (SelectedItems?.Count >= 1) SelectedItemsPropertiesViewModel.SelectedItemsCount = SelectedItems.Count; - if (SelectedItems.Count == 1) + if (SelectedItems?.Count == 1) { SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems.Count} {"ItemSelected/Text".GetLocalizedResource()}"; DispatcherQueue.EnqueueAsync(async () => @@ -262,7 +259,7 @@ internal set } else { - SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems.Count} {"ItemsSelected/Text".GetLocalizedResource()}"; + SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems!.Count} {"ItemsSelected/Text".GetLocalizedResource()}"; ResetRenameDoubleClick(); } } @@ -271,7 +268,7 @@ internal set //ItemManipulationModel.SetDragModeForItems(); } - ParentShellPageInstance.ToolbarViewModel.SelectedItems = value; + ParentShellPageInstance!.ToolbarViewModel.SelectedItems = value; } } @@ -326,17 +323,22 @@ private void JumpTimer_Tick(object sender, object e) protected abstract void InitializeCommandsViewModel(); - protected IEnumerable GetAllItems() + protected IEnumerable? GetAllItems() { - var items = CollectionViewSource.IsSourceGrouped ? // add all items from each group to the new list - (CollectionViewSource.Source as BulkConcurrentObservableCollection>)?.SelectMany(g => g) : - CollectionViewSource.Source as IEnumerable; - return items ?? new List(); + if (CollectionViewSource.IsSourceGrouped) + // add all items from each group to the new list + return (CollectionViewSource.Source as BulkConcurrentObservableCollection>)?.SelectMany(g => g); + + return CollectionViewSource.Source as IEnumerable; } public virtual void ResetItemOpacity() { - foreach (var item in GetAllItems()) + var items = GetAllItems(); + if (items == null) + return; + + foreach (var item in items) { if (item != null) item.Opacity = item.IsHiddenItem ? Constants.UI.DimItemOpacity : 1.0d; @@ -356,15 +358,16 @@ public virtual void ResetItemOpacity() protected virtual void BaseFolderSettings_LayoutModeChangeRequested(object? sender, LayoutModeEventArgs e) { - if (ParentShellPageInstance.SlimContentPage != null) + if (ParentShellPageInstance?.SlimContentPage != null) { - var layoutType = FolderSettings.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); + var layoutType = FolderSettings?.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); - if (layoutType != ParentShellPageInstance.CurrentPageType) + if (layoutType is not null && + layoutType != ParentShellPageInstance.CurrentPageType) { ParentShellPageInstance.NavigateWithArguments(layoutType, new NavigationArguments() { - NavPathParam = navigationArguments.NavPathParam, + NavPathParam = navigationArguments!.NavPathParam, IsSearchResultPage = navigationArguments.IsSearchResultPage, SearchPathParam = navigationArguments.SearchPathParam, SearchQuery = navigationArguments.SearchQuery, @@ -397,7 +400,7 @@ protected override async void OnNavigatedTo(NavigationEventArgs eventArgs) InitializeCommandsViewModel(); IsItemSelected = false; - FolderSettings.LayoutModeChangeRequested += BaseFolderSettings_LayoutModeChangeRequested; + FolderSettings!.LayoutModeChangeRequested += BaseFolderSettings_LayoutModeChangeRequested; FolderSettings.GroupOptionPreferenceUpdated += FolderSettings_GroupOptionPreferenceUpdated; ParentShellPageInstance.FilesystemViewModel.EmptyTextType = EmptyTextType.None; ParentShellPageInstance.ToolbarViewModel.UpdateSortAndGroupOptions(); @@ -453,7 +456,7 @@ protected override async void OnNavigatedTo(NavigationEventArgs eventArgs) { Query = navigationArguments.SearchQuery, Folder = navigationArguments.SearchPathParam, - ThumbnailSize = InstanceViewModel.FolderSettings.GetIconSize(), + ThumbnailSize = InstanceViewModel!.FolderSettings.GetIconSize(), SearchUnindexedItems = navigationArguments.SearchUnindexedItems }; _ = ParentShellPageInstance.FilesystemViewModel.SearchAsync(searchInstance); @@ -479,7 +482,7 @@ public void SetSelectedItemsOnNavigation() { List liItemsToSelect = new List(); foreach (string item in navigationArguments.SelectItems) - liItemsToSelect.Add(ParentShellPageInstance.FilesystemViewModel.FilesAndFolders.Where((li) => li.ItemNameRaw == item).First()); + liItemsToSelect.Add(ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders.Where((li) => li.ItemNameRaw == item).First()); ItemManipulationModel.SetSelectedItems(liItemsToSelect); ItemManipulationModel.FocusSelectedItems(); @@ -502,7 +505,7 @@ private async void FolderSettings_GroupOptionPreferenceUpdated(object? sender, G groupingCancellationToken?.Cancel(); groupingCancellationToken = new CancellationTokenSource(); var token = groupingCancellationToken.Token; - await ParentShellPageInstance.FilesystemViewModel.GroupOptionsUpdated(token); + await ParentShellPageInstance!.FilesystemViewModel.GroupOptionsUpdated(token); UpdateCollectionViewSource(); await ParentShellPageInstance.FilesystemViewModel.ReloadItemGroupHeaderImagesAsync(); } @@ -512,14 +515,14 @@ protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) base.OnNavigatingFrom(e); // Remove item jumping handler this.CharacterReceived -= Page_CharacterReceived; - FolderSettings.LayoutModeChangeRequested -= BaseFolderSettings_LayoutModeChangeRequested; + FolderSettings!.LayoutModeChangeRequested -= BaseFolderSettings_LayoutModeChangeRequested; FolderSettings.GroupOptionPreferenceUpdated -= FolderSettings_GroupOptionPreferenceUpdated; ItemContextMenuFlyout.Opening -= ItemContextFlyout_Opening; BaseContextMenuFlyout.Opening -= BaseContextFlyout_Opening; var parameter = e.Parameter as NavigationArguments; if (parameter is not null && !parameter.IsLayoutSwitch) - ParentShellPageInstance.FilesystemViewModel.CancelLoadAndClearFiles(); + ParentShellPageInstance!.FilesystemViewModel.CancelLoadAndClearFiles(); } public async void ItemContextFlyout_Opening(object? sender, object e) @@ -551,19 +554,20 @@ public async void BaseContextFlyout_Opening(object? sender, object e) shellContextMenuItemCancellationToken?.Cancel(); shellContextMenuItemCancellationToken = new CancellationTokenSource(); var shiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down); - var items = ContextFlyoutItemHelper.GetBaseContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel, itemViewModel: ParentShellPageInstance.FilesystemViewModel, commandsViewModel: CommandsViewModel, shiftPressed: shiftPressed, false); + var items = ContextFlyoutItemHelper.GetBaseContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, itemViewModel: ParentShellPageInstance!.FilesystemViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, false); BaseContextMenuFlyout.PrimaryCommands.Clear(); BaseContextMenuFlyout.SecondaryCommands.Clear(); var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); - primaryElements.OfType().ForEach(i => + primaryElements.Where(i => i is AppBarButton).ForEach(i => { - i.Click += new RoutedEventHandler((s, e) => BaseContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) + if (i is AppBarButton button) + button.Click += new RoutedEventHandler((s, e) => BaseContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) }); primaryElements.ForEach(i => BaseContextMenuFlyout.PrimaryCommands.Add(i)); secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width secondaryElements.ForEach(i => BaseContextMenuFlyout.SecondaryCommands.Add(i)); - if (!InstanceViewModel.IsPageTypeSearchResults && !InstanceViewModel.IsPageTypeZipFolder) + if (!InstanceViewModel!.IsPageTypeSearchResults && !InstanceViewModel.IsPageTypeZipFolder) { var shellMenuItems = await ContextFlyoutItemHelper.GetBaseContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); if (shellMenuItems.Any()) @@ -578,7 +582,9 @@ public async void BaseContextFlyout_Opening(object? sender, object e) public void UpdateSelectionSize() { - var items = selectedItems.Any() ? selectedItems : GetAllItems(); + var items = (selectedItems?.Any() ?? false) ? selectedItems : GetAllItems(); + if (items is null) + return; bool isSizeKnown = !items.Any(item => string.IsNullOrEmpty(item.FileSize)); if (isSizeKnown) { @@ -600,26 +606,27 @@ private async Task LoadMenuItemsAsync() itc.MaxHeight = Constants.UI.ContextMenuMaxHeight; // Reset menu max height shellContextMenuItemCancellationToken?.Cancel(); shellContextMenuItemCancellationToken = new CancellationTokenSource(); - SelectedItemsPropertiesViewModel.CheckAllFileExtensions(SelectedItems.Select(selectedItem => selectedItem?.FileExtension).ToList()); + SelectedItemsPropertiesViewModel.CheckAllFileExtensions(this.SelectedItems!.Select(selectedItem => selectedItem?.FileExtension).ToList()!); var shiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down); - var items = ContextFlyoutItemHelper.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems, selectedItemsPropertiesViewModel: SelectedItemsPropertiesViewModel, commandsViewModel: CommandsViewModel, shiftPressed: shiftPressed, showOpenMenu: false); + var items = ContextFlyoutItemHelper.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, workingDir: ParentShellPageInstance!.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, selectedItemsPropertiesViewModel: SelectedItemsPropertiesViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, showOpenMenu: false); ItemContextMenuFlyout.PrimaryCommands.Clear(); ItemContextMenuFlyout.SecondaryCommands.Clear(); var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); - primaryElements.OfType().ForEach(i => + primaryElements.Where(i => i is AppBarButton).ForEach(i => { - i.Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) + if (i is AppBarButton button) + button.Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) }); primaryElements.ForEach(i => ItemContextMenuFlyout.PrimaryCommands.Add(i)); secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width secondaryElements.ForEach(i => ItemContextMenuFlyout.SecondaryCommands.Add(i)); - if (InstanceViewModel.CanTagFilesInPage) + if (InstanceViewModel!.CanTagFilesInPage) AddNewFileTagsToMenu(ItemContextMenuFlyout); if (!InstanceViewModel.IsPageTypeZipFolder) { - var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); + var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); if (shellMenuItems.Any()) AddShellItemsToMenu(shellMenuItems, ItemContextMenuFlyout, shiftPressed); } @@ -627,7 +634,7 @@ private async Task LoadMenuItemsAsync() private void AddNewFileTagsToMenu(CommandBarFlyout contextMenu) { - var fileTagsContextMenu = new FileTagsContextMenu(SelectedItems); + var fileTagsContextMenu = new FileTagsContextMenu(SelectedItems!); var overflowSeparator = contextMenu.SecondaryCommands.FirstOrDefault(x => x is FrameworkElement fe && fe.Tag as string == "OverflowSeparator") as AppBarSeparator; var index = contextMenu.SecondaryCommands.IndexOf(overflowSeparator); index = index >= 0 ? index : contextMenu.SecondaryCommands.Count; @@ -703,12 +710,12 @@ private void AddShellItemsToMenu(List shellMenuI // add items to openwith dropdown var openWithOverflow = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWithOverflow") as AppBarButton; - var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; - if (openWithSubItems is not null && openWithOverflow is not null && openWith is not null) + if (openWithSubItems is not null && openWithOverflow is not null) { - var flyout = (MenuFlyout)openWithOverflow.Flyout; + var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; + var flyout = openWithOverflow.Flyout as MenuFlyout; - flyout.Items.Clear(); + flyout!.Items.Clear(); foreach (var item in openWithSubItems) { @@ -716,7 +723,7 @@ private void AddShellItemsToMenu(List shellMenuI } openWithOverflow.Flyout = flyout; - openWith.Visibility = Visibility.Collapsed; + openWith!.Visibility = Visibility.Collapsed; openWithOverflow.Visibility = Visibility.Visible; } @@ -728,7 +735,7 @@ private void AddShellItemsToMenu(List shellMenuI label.TextTrimming = TextTrimming.CharacterEllipsis; if ((item as AppBarButton)?.Flyout as MenuFlyout is MenuFlyout flyout) // Close main menu when clicking on subitems (#5508) { - Action> clickAction = null!; + Action>? clickAction = null; clickAction = (items) => { items.OfType().ForEach(i => @@ -737,7 +744,7 @@ private void AddShellItemsToMenu(List shellMenuI }); items.OfType().ForEach(i => { - clickAction(i.Items); + clickAction!(i.Items); }); }; clickAction(flyout.Items); @@ -748,7 +755,7 @@ private void AddShellItemsToMenu(List shellMenuI protected virtual void Page_CharacterReceived(UIElement sender, CharacterReceivedRoutedEventArgs args) { - if (ParentShellPageInstance.IsCurrentInstance) + if (ParentShellPageInstance!.IsCurrentInstance) { char letter = args.Character; JumpString += letter.ToString().ToLowerInvariant(); @@ -757,7 +764,7 @@ protected virtual void Page_CharacterReceived(UIElement sender, CharacterReceive protected void FileList_DragItemsStarting(object sender, DragItemsStartingEventArgs e) { - e.Items.OfType().ForEach(item => SelectedItems.Add(item)); + e.Items.OfType().ForEach(item => SelectedItems!.Add(item)); try { @@ -803,7 +810,7 @@ protected async void Item_DragOver(object sender, DragEventArgs e) { dragOverItem = null; dragOverTimer.Stop(); - NavigationHelpers.OpenSelectedItems(ParentShellPageInstance, false); + NavigationHelpers.OpenSelectedItems(ParentShellPageInstance!, false); } }, TimeSpan.FromMilliseconds(1000), false); } @@ -886,7 +893,7 @@ protected async void Item_Drop(object sender, DragEventArgs e) var item = GetItemFromElement(sender); if (item != null) - await ParentShellPageInstance.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as ShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable); + await ParentShellPageInstance!.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as ShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable); deferral.Complete(); } @@ -912,7 +919,7 @@ private void RefreshItem(SelectorItem container, object item, bool inRecycleQueu if (inRecycleQueue) { - ParentShellPageInstance.FilesystemViewModel.CancelExtendedPropertiesLoadingForItem(listedItem); + ParentShellPageInstance!.FilesystemViewModel.CancelExtendedPropertiesLoadingForItem(listedItem); } else { @@ -923,7 +930,7 @@ private void RefreshItem(SelectorItem container, object item, bool inRecycleQueu uint callbackPhase = 3; args.RegisterUpdateCallback(callbackPhase, async (s, c) => { - await ParentShellPageInstance.FilesystemViewModel.LoadExtendedItemProperties(listedItem, IconSize); + await ParentShellPageInstance!.FilesystemViewModel.LoadExtendedItemProperties(listedItem, IconSize); }); } } @@ -1024,8 +1031,10 @@ protected void ItemsLayout_Drop(object sender, DragEventArgs e) CommandsViewModel?.DropCommand?.Execute(e); } - private void UpdateCollectionViewSource() + public void UpdateCollectionViewSource() { + if (ParentShellPageInstance is null) + return; if (ParentShellPageInstance.FilesystemViewModel.FilesAndFolders.IsGrouped) { CollectionViewSource = new CollectionViewSource() @@ -1077,7 +1086,7 @@ protected void RootPanel_PointerPressed(object sender, PointerRoutedEventArgs e) private void ItemManipulationModel_RefreshItemsOpacityInvoked(object? sender, EventArgs e) { - foreach (ListedItem listedItem in GetAllItems()) + foreach (ListedItem listedItem in GetAllItems()!) { if (listedItem.IsHiddenItem) listedItem.Opacity = Constants.UI.DimItemOpacity; @@ -1088,7 +1097,8 @@ private void ItemManipulationModel_RefreshItemsOpacityInvoked(object? sender, Ev private void View_VectorChanged(IObservableVector sender, IVectorChangedEventArgs @event) { - ParentShellPageInstance.ToolbarViewModel.HasItem = CollectionViewSource.View.Any(); + if (ParentShellPageInstance is not null) + ParentShellPageInstance.ToolbarViewModel.HasItem = CollectionViewSource.View.Any(); } virtual public void StartRenameItem() { } diff --git a/src/Files.App/IBaseLayout.cs b/src/Files.App/IBaseLayout.cs index 887f5fd223c1..cd7e10c4fa3c 100644 --- a/src/Files.App/IBaseLayout.cs +++ b/src/Files.App/IBaseLayout.cs @@ -15,7 +15,7 @@ public interface IBaseLayout : IDisposable bool IsMiddleClickToScrollEnabled { get; set; } - public List SelectedItems { get; } + public List? SelectedItems { get; } public ListedItem? SelectedItem { get; } From 9dfb8c3351e326c0f726a27ccce4d64081389332 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Tue, 13 Sep 2022 00:31:44 +0200 Subject: [PATCH 07/15] Make SelectedItems non-nullable --- src/Files.App/BaseLayout.cs | 102 +++++++++++++++++------------------- 1 file changed, 48 insertions(+), 54 deletions(-) diff --git a/src/Files.App/BaseLayout.cs b/src/Files.App/BaseLayout.cs index 1cac66bbd15b..d2125d1f096a 100644 --- a/src/Files.App/BaseLayout.cs +++ b/src/Files.App/BaseLayout.cs @@ -56,10 +56,6 @@ public abstract class BaseLayout : Page, IBaseLayout, INotifyPropertyChanged public SelectedItemsPropertiesViewModel SelectedItemsPropertiesViewModel { get; } - public FolderSettingsViewModel? FolderSettings => ParentShellPageInstance?.InstanceViewModel.FolderSettings; - - public CurrentInstanceViewModel? InstanceViewModel => ParentShellPageInstance?.InstanceViewModel; - public IPaneViewModel PaneViewModel => App.PaneViewModel; public AppModel AppModel => App.AppModel; @@ -74,9 +70,15 @@ public abstract class BaseLayout : Page, IBaseLayout, INotifyPropertyChanged AlwaysExpanded = true, }; - public BaseLayoutCommandsViewModel? CommandsViewModel { get; protected set; } + protected NavigationArguments? navigationArguments = null; // Non-null after OnNavigatedTo() + + public BaseLayoutCommandsViewModel? CommandsViewModel { get; protected set; } // Non-null after OnNavigatedTo() + + public IShellPage? ParentShellPageInstance { get; private set; } // Non-null after OnNavigatedTo() + + public FolderSettingsViewModel? FolderSettings => ParentShellPageInstance?.InstanceViewModel.FolderSettings; - public IShellPage? ParentShellPageInstance { get; private set; } = null; + public CurrentInstanceViewModel? InstanceViewModel => ParentShellPageInstance?.InstanceViewModel; public bool IsRenamingItem { get; set; } = false; public ListedItem? RenamingItem { get; set; } = null; @@ -112,17 +114,15 @@ public CollectionViewSource CollectionViewSource { if (collectionViewSource == value) return; - if (collectionViewSource?.View is not null) + if (collectionViewSource.View is not null) collectionViewSource.View.VectorChanged -= View_VectorChanged; collectionViewSource = value; NotifyPropertyChanged(nameof(CollectionViewSource)); - if (collectionViewSource?.View is not null) + if (collectionViewSource.View is not null) collectionViewSource.View.VectorChanged += View_VectorChanged; } } - protected NavigationArguments? navigationArguments; - private bool isItemSelected = false; public bool IsItemSelected @@ -193,9 +193,9 @@ public string JumpString } } - private List? selectedItems = new List(); + private List selectedItems = new List(); - public List? SelectedItems + public List SelectedItems { get { @@ -203,20 +203,23 @@ public List? SelectedItems } internal set { - //if (!(value?.All(x => selectedItems?.Contains(x) ?? false) ?? value == selectedItems)) // check if the new list is different then the old one + if (value is null) + return; + + //if (!(value.All(x => selectedItems.Contains(x) ?? false) ?? value == selectedItems)) // check if the new list is different then the old one if (value != selectedItems) // check if the new list is different then the old one { - if (value?.FirstOrDefault() != selectedItems?.FirstOrDefault()) + if (value.FirstOrDefault() != selectedItems.FirstOrDefault()) { // update preview pane properties - if (value?.Count == 1) + if (value.Count == 1) { App.PreviewPaneViewModel.IsItemSelected = true; App.PreviewPaneViewModel.SelectedItem = value.First(); } else { - App.PreviewPaneViewModel.IsItemSelected = value?.Count > 0; + App.PreviewPaneViewModel.IsItemSelected = value.Count > 0; App.PreviewPaneViewModel.SelectedItem = null; } @@ -230,7 +233,7 @@ internal set } selectedItems = value; - if (selectedItems?.Count == 0 || selectedItems?[0] == null) + if (selectedItems.Count == 0) { IsItemSelected = false; SelectedItem = null; @@ -245,10 +248,10 @@ internal set SelectedItemsPropertiesViewModel.IsItemSelected = true; UpdateSelectionSize(); - if (SelectedItems?.Count >= 1) + if (SelectedItems.Count >= 1) SelectedItemsPropertiesViewModel.SelectedItemsCount = SelectedItems.Count; - if (SelectedItems?.Count == 1) + if (SelectedItems.Count == 1) { SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems.Count} {"ItemSelected/Text".GetLocalizedResource()}"; DispatcherQueue.EnqueueAsync(async () => @@ -259,7 +262,7 @@ internal set } else { - SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems!.Count} {"ItemsSelected/Text".GetLocalizedResource()}"; + SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems.Count} {"ItemsSelected/Text".GetLocalizedResource()}"; ResetRenameDoubleClick(); } } @@ -323,22 +326,17 @@ private void JumpTimer_Tick(object sender, object e) protected abstract void InitializeCommandsViewModel(); - protected IEnumerable? GetAllItems() + protected IEnumerable GetAllItems() { - if (CollectionViewSource.IsSourceGrouped) - // add all items from each group to the new list - return (CollectionViewSource.Source as BulkConcurrentObservableCollection>)?.SelectMany(g => g); - - return CollectionViewSource.Source as IEnumerable; + var items = CollectionViewSource.IsSourceGrouped ? // add all items from each group to the new list + (CollectionViewSource.Source as BulkConcurrentObservableCollection>)?.SelectMany(g => g) : + CollectionViewSource.Source as IEnumerable; + return items ?? new List(); } public virtual void ResetItemOpacity() { - var items = GetAllItems(); - if (items == null) - return; - - foreach (var item in items) + foreach (var item in GetAllItems()) { if (item != null) item.Opacity = item.IsHiddenItem ? Constants.UI.DimItemOpacity : 1.0d; @@ -360,10 +358,9 @@ protected virtual void BaseFolderSettings_LayoutModeChangeRequested(object? send { if (ParentShellPageInstance?.SlimContentPage != null) { - var layoutType = FolderSettings?.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); + var layoutType = FolderSettings!.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); - if (layoutType is not null && - layoutType != ParentShellPageInstance.CurrentPageType) + if (layoutType != ParentShellPageInstance.CurrentPageType) { ParentShellPageInstance.NavigateWithArguments(layoutType, new NavigationArguments() { @@ -606,16 +603,15 @@ private async Task LoadMenuItemsAsync() itc.MaxHeight = Constants.UI.ContextMenuMaxHeight; // Reset menu max height shellContextMenuItemCancellationToken?.Cancel(); shellContextMenuItemCancellationToken = new CancellationTokenSource(); - SelectedItemsPropertiesViewModel.CheckAllFileExtensions(this.SelectedItems!.Select(selectedItem => selectedItem?.FileExtension).ToList()!); + SelectedItemsPropertiesViewModel.CheckAllFileExtensions(SelectedItems.Select(selectedItem => selectedItem.FileExtension).ToList()); var shiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down); - var items = ContextFlyoutItemHelper.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, workingDir: ParentShellPageInstance!.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, selectedItemsPropertiesViewModel: SelectedItemsPropertiesViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, showOpenMenu: false); + var items = ContextFlyoutItemHelper.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, workingDir: ParentShellPageInstance!.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems, selectedItemsPropertiesViewModel: SelectedItemsPropertiesViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, showOpenMenu: false); ItemContextMenuFlyout.PrimaryCommands.Clear(); ItemContextMenuFlyout.SecondaryCommands.Clear(); var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); - primaryElements.Where(i => i is AppBarButton).ForEach(i => + primaryElements.OfType().ForEach(i => { - if (i is AppBarButton button) - button.Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) + i.Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) }); primaryElements.ForEach(i => ItemContextMenuFlyout.PrimaryCommands.Add(i)); secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width @@ -626,7 +622,7 @@ private async Task LoadMenuItemsAsync() if (!InstanceViewModel.IsPageTypeZipFolder) { - var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); + var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); if (shellMenuItems.Any()) AddShellItemsToMenu(shellMenuItems, ItemContextMenuFlyout, shiftPressed); } @@ -634,7 +630,7 @@ private async Task LoadMenuItemsAsync() private void AddNewFileTagsToMenu(CommandBarFlyout contextMenu) { - var fileTagsContextMenu = new FileTagsContextMenu(SelectedItems!); + var fileTagsContextMenu = new FileTagsContextMenu(SelectedItems); var overflowSeparator = contextMenu.SecondaryCommands.FirstOrDefault(x => x is FrameworkElement fe && fe.Tag as string == "OverflowSeparator") as AppBarSeparator; var index = contextMenu.SecondaryCommands.IndexOf(overflowSeparator); index = index >= 0 ? index : contextMenu.SecondaryCommands.Count; @@ -710,12 +706,12 @@ private void AddShellItemsToMenu(List shellMenuI // add items to openwith dropdown var openWithOverflow = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWithOverflow") as AppBarButton; - if (openWithSubItems is not null && openWithOverflow is not null) + var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; + if (openWithSubItems is not null && openWithOverflow is not null && openWith is not null) { - var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; - var flyout = openWithOverflow.Flyout as MenuFlyout; + var flyout = (MenuFlyout)openWithOverflow.Flyout; - flyout!.Items.Clear(); + flyout.Items.Clear(); foreach (var item in openWithSubItems) { @@ -723,7 +719,7 @@ private void AddShellItemsToMenu(List shellMenuI } openWithOverflow.Flyout = flyout; - openWith!.Visibility = Visibility.Collapsed; + openWith.Visibility = Visibility.Collapsed; openWithOverflow.Visibility = Visibility.Visible; } @@ -735,7 +731,7 @@ private void AddShellItemsToMenu(List shellMenuI label.TextTrimming = TextTrimming.CharacterEllipsis; if ((item as AppBarButton)?.Flyout as MenuFlyout is MenuFlyout flyout) // Close main menu when clicking on subitems (#5508) { - Action>? clickAction = null; + Action> clickAction = null!; clickAction = (items) => { items.OfType().ForEach(i => @@ -744,7 +740,7 @@ private void AddShellItemsToMenu(List shellMenuI }); items.OfType().ForEach(i => { - clickAction!(i.Items); + clickAction(i.Items); }); }; clickAction(flyout.Items); @@ -764,7 +760,7 @@ protected virtual void Page_CharacterReceived(UIElement sender, CharacterReceive protected void FileList_DragItemsStarting(object sender, DragItemsStartingEventArgs e) { - e.Items.OfType().ForEach(item => SelectedItems!.Add(item)); + e.Items.OfType().ForEach(item => SelectedItems.Add(item)); try { @@ -1031,11 +1027,9 @@ protected void ItemsLayout_Drop(object sender, DragEventArgs e) CommandsViewModel?.DropCommand?.Execute(e); } - public void UpdateCollectionViewSource() + private void UpdateCollectionViewSource() { - if (ParentShellPageInstance is null) - return; - if (ParentShellPageInstance.FilesystemViewModel.FilesAndFolders.IsGrouped) + if (ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders.IsGrouped) { CollectionViewSource = new CollectionViewSource() { @@ -1086,7 +1080,7 @@ protected void RootPanel_PointerPressed(object sender, PointerRoutedEventArgs e) private void ItemManipulationModel_RefreshItemsOpacityInvoked(object? sender, EventArgs e) { - foreach (ListedItem listedItem in GetAllItems()!) + foreach (ListedItem listedItem in GetAllItems()) { if (listedItem.IsHiddenItem) listedItem.Opacity = Constants.UI.DimItemOpacity; From b39020f0781a23c8cebb602e1109fdb470cece2d Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Tue, 13 Sep 2022 00:36:37 +0200 Subject: [PATCH 08/15] Use OfType<> instead of where to filter for type --- src/Files.App/BaseLayout.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Files.App/BaseLayout.cs b/src/Files.App/BaseLayout.cs index d2125d1f096a..6fc8e4f9603d 100644 --- a/src/Files.App/BaseLayout.cs +++ b/src/Files.App/BaseLayout.cs @@ -555,10 +555,9 @@ public async void BaseContextFlyout_Opening(object? sender, object e) BaseContextMenuFlyout.PrimaryCommands.Clear(); BaseContextMenuFlyout.SecondaryCommands.Clear(); var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); - primaryElements.Where(i => i is AppBarButton).ForEach(i => + primaryElements.OfType().ForEach(i => { - if (i is AppBarButton button) - button.Click += new RoutedEventHandler((s, e) => BaseContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) + i.Click += new RoutedEventHandler((s, e) => BaseContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) }); primaryElements.ForEach(i => BaseContextMenuFlyout.PrimaryCommands.Add(i)); secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width From 216137406b175dd33fe35359668721f6668d10c0 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Tue, 13 Sep 2022 00:58:32 +0200 Subject: [PATCH 09/15] Revert "Make SelectedItems non-nullable" This reverts commit 9dfb8c3351e326c0f726a27ccce4d64081389332. --- src/Files.App/BaseLayout.cs | 102 +++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 48 deletions(-) diff --git a/src/Files.App/BaseLayout.cs b/src/Files.App/BaseLayout.cs index 6fc8e4f9603d..917e4c4c7a63 100644 --- a/src/Files.App/BaseLayout.cs +++ b/src/Files.App/BaseLayout.cs @@ -56,6 +56,10 @@ public abstract class BaseLayout : Page, IBaseLayout, INotifyPropertyChanged public SelectedItemsPropertiesViewModel SelectedItemsPropertiesViewModel { get; } + public FolderSettingsViewModel? FolderSettings => ParentShellPageInstance?.InstanceViewModel.FolderSettings; + + public CurrentInstanceViewModel? InstanceViewModel => ParentShellPageInstance?.InstanceViewModel; + public IPaneViewModel PaneViewModel => App.PaneViewModel; public AppModel AppModel => App.AppModel; @@ -70,15 +74,9 @@ public abstract class BaseLayout : Page, IBaseLayout, INotifyPropertyChanged AlwaysExpanded = true, }; - protected NavigationArguments? navigationArguments = null; // Non-null after OnNavigatedTo() - - public BaseLayoutCommandsViewModel? CommandsViewModel { get; protected set; } // Non-null after OnNavigatedTo() - - public IShellPage? ParentShellPageInstance { get; private set; } // Non-null after OnNavigatedTo() - - public FolderSettingsViewModel? FolderSettings => ParentShellPageInstance?.InstanceViewModel.FolderSettings; + public BaseLayoutCommandsViewModel? CommandsViewModel { get; protected set; } - public CurrentInstanceViewModel? InstanceViewModel => ParentShellPageInstance?.InstanceViewModel; + public IShellPage? ParentShellPageInstance { get; private set; } = null; public bool IsRenamingItem { get; set; } = false; public ListedItem? RenamingItem { get; set; } = null; @@ -114,15 +112,17 @@ public CollectionViewSource CollectionViewSource { if (collectionViewSource == value) return; - if (collectionViewSource.View is not null) + if (collectionViewSource?.View is not null) collectionViewSource.View.VectorChanged -= View_VectorChanged; collectionViewSource = value; NotifyPropertyChanged(nameof(CollectionViewSource)); - if (collectionViewSource.View is not null) + if (collectionViewSource?.View is not null) collectionViewSource.View.VectorChanged += View_VectorChanged; } } + protected NavigationArguments? navigationArguments; + private bool isItemSelected = false; public bool IsItemSelected @@ -193,9 +193,9 @@ public string JumpString } } - private List selectedItems = new List(); + private List? selectedItems = new List(); - public List SelectedItems + public List? SelectedItems { get { @@ -203,23 +203,20 @@ public List SelectedItems } internal set { - if (value is null) - return; - - //if (!(value.All(x => selectedItems.Contains(x) ?? false) ?? value == selectedItems)) // check if the new list is different then the old one + //if (!(value?.All(x => selectedItems?.Contains(x) ?? false) ?? value == selectedItems)) // check if the new list is different then the old one if (value != selectedItems) // check if the new list is different then the old one { - if (value.FirstOrDefault() != selectedItems.FirstOrDefault()) + if (value?.FirstOrDefault() != selectedItems?.FirstOrDefault()) { // update preview pane properties - if (value.Count == 1) + if (value?.Count == 1) { App.PreviewPaneViewModel.IsItemSelected = true; App.PreviewPaneViewModel.SelectedItem = value.First(); } else { - App.PreviewPaneViewModel.IsItemSelected = value.Count > 0; + App.PreviewPaneViewModel.IsItemSelected = value?.Count > 0; App.PreviewPaneViewModel.SelectedItem = null; } @@ -233,7 +230,7 @@ internal set } selectedItems = value; - if (selectedItems.Count == 0) + if (selectedItems?.Count == 0 || selectedItems?[0] == null) { IsItemSelected = false; SelectedItem = null; @@ -248,10 +245,10 @@ internal set SelectedItemsPropertiesViewModel.IsItemSelected = true; UpdateSelectionSize(); - if (SelectedItems.Count >= 1) + if (SelectedItems?.Count >= 1) SelectedItemsPropertiesViewModel.SelectedItemsCount = SelectedItems.Count; - if (SelectedItems.Count == 1) + if (SelectedItems?.Count == 1) { SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems.Count} {"ItemSelected/Text".GetLocalizedResource()}"; DispatcherQueue.EnqueueAsync(async () => @@ -262,7 +259,7 @@ internal set } else { - SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems.Count} {"ItemsSelected/Text".GetLocalizedResource()}"; + SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems!.Count} {"ItemsSelected/Text".GetLocalizedResource()}"; ResetRenameDoubleClick(); } } @@ -326,17 +323,22 @@ private void JumpTimer_Tick(object sender, object e) protected abstract void InitializeCommandsViewModel(); - protected IEnumerable GetAllItems() + protected IEnumerable? GetAllItems() { - var items = CollectionViewSource.IsSourceGrouped ? // add all items from each group to the new list - (CollectionViewSource.Source as BulkConcurrentObservableCollection>)?.SelectMany(g => g) : - CollectionViewSource.Source as IEnumerable; - return items ?? new List(); + if (CollectionViewSource.IsSourceGrouped) + // add all items from each group to the new list + return (CollectionViewSource.Source as BulkConcurrentObservableCollection>)?.SelectMany(g => g); + + return CollectionViewSource.Source as IEnumerable; } public virtual void ResetItemOpacity() { - foreach (var item in GetAllItems()) + var items = GetAllItems(); + if (items == null) + return; + + foreach (var item in items) { if (item != null) item.Opacity = item.IsHiddenItem ? Constants.UI.DimItemOpacity : 1.0d; @@ -358,9 +360,10 @@ protected virtual void BaseFolderSettings_LayoutModeChangeRequested(object? send { if (ParentShellPageInstance?.SlimContentPage != null) { - var layoutType = FolderSettings!.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); + var layoutType = FolderSettings?.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); - if (layoutType != ParentShellPageInstance.CurrentPageType) + if (layoutType is not null && + layoutType != ParentShellPageInstance.CurrentPageType) { ParentShellPageInstance.NavigateWithArguments(layoutType, new NavigationArguments() { @@ -602,15 +605,16 @@ private async Task LoadMenuItemsAsync() itc.MaxHeight = Constants.UI.ContextMenuMaxHeight; // Reset menu max height shellContextMenuItemCancellationToken?.Cancel(); shellContextMenuItemCancellationToken = new CancellationTokenSource(); - SelectedItemsPropertiesViewModel.CheckAllFileExtensions(SelectedItems.Select(selectedItem => selectedItem.FileExtension).ToList()); + SelectedItemsPropertiesViewModel.CheckAllFileExtensions(this.SelectedItems!.Select(selectedItem => selectedItem?.FileExtension).ToList()!); var shiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down); - var items = ContextFlyoutItemHelper.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, workingDir: ParentShellPageInstance!.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems, selectedItemsPropertiesViewModel: SelectedItemsPropertiesViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, showOpenMenu: false); + var items = ContextFlyoutItemHelper.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, workingDir: ParentShellPageInstance!.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, selectedItemsPropertiesViewModel: SelectedItemsPropertiesViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, showOpenMenu: false); ItemContextMenuFlyout.PrimaryCommands.Clear(); ItemContextMenuFlyout.SecondaryCommands.Clear(); var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); - primaryElements.OfType().ForEach(i => + primaryElements.Where(i => i is AppBarButton).ForEach(i => { - i.Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) + if (i is AppBarButton button) + button.Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) }); primaryElements.ForEach(i => ItemContextMenuFlyout.PrimaryCommands.Add(i)); secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width @@ -621,7 +625,7 @@ private async Task LoadMenuItemsAsync() if (!InstanceViewModel.IsPageTypeZipFolder) { - var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); + var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); if (shellMenuItems.Any()) AddShellItemsToMenu(shellMenuItems, ItemContextMenuFlyout, shiftPressed); } @@ -629,7 +633,7 @@ private async Task LoadMenuItemsAsync() private void AddNewFileTagsToMenu(CommandBarFlyout contextMenu) { - var fileTagsContextMenu = new FileTagsContextMenu(SelectedItems); + var fileTagsContextMenu = new FileTagsContextMenu(SelectedItems!); var overflowSeparator = contextMenu.SecondaryCommands.FirstOrDefault(x => x is FrameworkElement fe && fe.Tag as string == "OverflowSeparator") as AppBarSeparator; var index = contextMenu.SecondaryCommands.IndexOf(overflowSeparator); index = index >= 0 ? index : contextMenu.SecondaryCommands.Count; @@ -705,12 +709,12 @@ private void AddShellItemsToMenu(List shellMenuI // add items to openwith dropdown var openWithOverflow = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWithOverflow") as AppBarButton; - var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; - if (openWithSubItems is not null && openWithOverflow is not null && openWith is not null) + if (openWithSubItems is not null && openWithOverflow is not null) { - var flyout = (MenuFlyout)openWithOverflow.Flyout; + var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; + var flyout = openWithOverflow.Flyout as MenuFlyout; - flyout.Items.Clear(); + flyout!.Items.Clear(); foreach (var item in openWithSubItems) { @@ -718,7 +722,7 @@ private void AddShellItemsToMenu(List shellMenuI } openWithOverflow.Flyout = flyout; - openWith.Visibility = Visibility.Collapsed; + openWith!.Visibility = Visibility.Collapsed; openWithOverflow.Visibility = Visibility.Visible; } @@ -730,7 +734,7 @@ private void AddShellItemsToMenu(List shellMenuI label.TextTrimming = TextTrimming.CharacterEllipsis; if ((item as AppBarButton)?.Flyout as MenuFlyout is MenuFlyout flyout) // Close main menu when clicking on subitems (#5508) { - Action> clickAction = null!; + Action>? clickAction = null; clickAction = (items) => { items.OfType().ForEach(i => @@ -739,7 +743,7 @@ private void AddShellItemsToMenu(List shellMenuI }); items.OfType().ForEach(i => { - clickAction(i.Items); + clickAction!(i.Items); }); }; clickAction(flyout.Items); @@ -759,7 +763,7 @@ protected virtual void Page_CharacterReceived(UIElement sender, CharacterReceive protected void FileList_DragItemsStarting(object sender, DragItemsStartingEventArgs e) { - e.Items.OfType().ForEach(item => SelectedItems.Add(item)); + e.Items.OfType().ForEach(item => SelectedItems!.Add(item)); try { @@ -1026,9 +1030,11 @@ protected void ItemsLayout_Drop(object sender, DragEventArgs e) CommandsViewModel?.DropCommand?.Execute(e); } - private void UpdateCollectionViewSource() + public void UpdateCollectionViewSource() { - if (ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders.IsGrouped) + if (ParentShellPageInstance is null) + return; + if (ParentShellPageInstance.FilesystemViewModel.FilesAndFolders.IsGrouped) { CollectionViewSource = new CollectionViewSource() { @@ -1079,7 +1085,7 @@ protected void RootPanel_PointerPressed(object sender, PointerRoutedEventArgs e) private void ItemManipulationModel_RefreshItemsOpacityInvoked(object? sender, EventArgs e) { - foreach (ListedItem listedItem in GetAllItems()) + foreach (ListedItem listedItem in GetAllItems()!) { if (listedItem.IsHiddenItem) listedItem.Opacity = Constants.UI.DimItemOpacity; From 98efea3f166ea56339483c5ef70c261127ab1caa Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Tue, 13 Sep 2022 01:03:09 +0200 Subject: [PATCH 10/15] Make GetAllItems() non nullable --- src/Files.App/BaseLayout.cs | 41 +++++++++++++++---------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/Files.App/BaseLayout.cs b/src/Files.App/BaseLayout.cs index 917e4c4c7a63..d295f38dd18c 100644 --- a/src/Files.App/BaseLayout.cs +++ b/src/Files.App/BaseLayout.cs @@ -112,11 +112,11 @@ public CollectionViewSource CollectionViewSource { if (collectionViewSource == value) return; - if (collectionViewSource?.View is not null) + if (collectionViewSource.View is not null) collectionViewSource.View.VectorChanged -= View_VectorChanged; collectionViewSource = value; NotifyPropertyChanged(nameof(CollectionViewSource)); - if (collectionViewSource?.View is not null) + if (collectionViewSource.View is not null) collectionViewSource.View.VectorChanged += View_VectorChanged; } } @@ -325,20 +325,15 @@ private void JumpTimer_Tick(object sender, object e) protected IEnumerable? GetAllItems() { - if (CollectionViewSource.IsSourceGrouped) - // add all items from each group to the new list - return (CollectionViewSource.Source as BulkConcurrentObservableCollection>)?.SelectMany(g => g); - - return CollectionViewSource.Source as IEnumerable; + var items = CollectionViewSource.IsSourceGrouped ? // add all items from each group to the new list + (CollectionViewSource.Source as BulkConcurrentObservableCollection>)?.SelectMany(g => g) : + CollectionViewSource.Source as IEnumerable; + return items ?? new List(); } public virtual void ResetItemOpacity() { - var items = GetAllItems(); - if (items == null) - return; - - foreach (var item in items) + foreach (var item in GetAllItems()) { if (item != null) item.Opacity = item.IsHiddenItem ? Constants.UI.DimItemOpacity : 1.0d; @@ -360,10 +355,9 @@ protected virtual void BaseFolderSettings_LayoutModeChangeRequested(object? send { if (ParentShellPageInstance?.SlimContentPage != null) { - var layoutType = FolderSettings?.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); + var layoutType = FolderSettings!.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); - if (layoutType is not null && - layoutType != ParentShellPageInstance.CurrentPageType) + if (layoutType != ParentShellPageInstance.CurrentPageType) { ParentShellPageInstance.NavigateWithArguments(layoutType, new NavigationArguments() { @@ -611,10 +605,9 @@ private async Task LoadMenuItemsAsync() ItemContextMenuFlyout.PrimaryCommands.Clear(); ItemContextMenuFlyout.SecondaryCommands.Clear(); var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); - primaryElements.Where(i => i is AppBarButton).ForEach(i => + primaryElements.OfType().ForEach(i => { - if (i is AppBarButton button) - button.Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) + i.Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) }); primaryElements.ForEach(i => ItemContextMenuFlyout.PrimaryCommands.Add(i)); secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width @@ -709,9 +702,9 @@ private void AddShellItemsToMenu(List shellMenuI // add items to openwith dropdown var openWithOverflow = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWithOverflow") as AppBarButton; - if (openWithSubItems is not null && openWithOverflow is not null) + var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; + if (openWithSubItems is not null && openWithOverflow is not null && openWith is not null) { - var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; var flyout = openWithOverflow.Flyout as MenuFlyout; flyout!.Items.Clear(); @@ -722,7 +715,7 @@ private void AddShellItemsToMenu(List shellMenuI } openWithOverflow.Flyout = flyout; - openWith!.Visibility = Visibility.Collapsed; + openWith.Visibility = Visibility.Collapsed; openWithOverflow.Visibility = Visibility.Visible; } @@ -734,7 +727,7 @@ private void AddShellItemsToMenu(List shellMenuI label.TextTrimming = TextTrimming.CharacterEllipsis; if ((item as AppBarButton)?.Flyout as MenuFlyout is MenuFlyout flyout) // Close main menu when clicking on subitems (#5508) { - Action>? clickAction = null; + Action> clickAction = null!; clickAction = (items) => { items.OfType().ForEach(i => @@ -1030,7 +1023,7 @@ protected void ItemsLayout_Drop(object sender, DragEventArgs e) CommandsViewModel?.DropCommand?.Execute(e); } - public void UpdateCollectionViewSource() + private void UpdateCollectionViewSource() { if (ParentShellPageInstance is null) return; @@ -1085,7 +1078,7 @@ protected void RootPanel_PointerPressed(object sender, PointerRoutedEventArgs e) private void ItemManipulationModel_RefreshItemsOpacityInvoked(object? sender, EventArgs e) { - foreach (ListedItem listedItem in GetAllItems()!) + foreach (ListedItem listedItem in GetAllItems()) { if (listedItem.IsHiddenItem) listedItem.Opacity = Constants.UI.DimItemOpacity; From a37a68e3da456cc148f5a34192e581d1e8812a7a Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Tue, 13 Sep 2022 01:07:03 +0200 Subject: [PATCH 11/15] Minor nullable changes --- src/Files.App/BaseLayout.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Files.App/BaseLayout.cs b/src/Files.App/BaseLayout.cs index d295f38dd18c..98690d5ac023 100644 --- a/src/Files.App/BaseLayout.cs +++ b/src/Files.App/BaseLayout.cs @@ -705,9 +705,9 @@ private void AddShellItemsToMenu(List shellMenuI var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; if (openWithSubItems is not null && openWithOverflow is not null && openWith is not null) { - var flyout = openWithOverflow.Flyout as MenuFlyout; + var flyout = (MenuFlyout)openWithOverflow.Flyout; - flyout!.Items.Clear(); + flyout.Items.Clear(); foreach (var item in openWithSubItems) { @@ -736,7 +736,7 @@ private void AddShellItemsToMenu(List shellMenuI }); items.OfType().ForEach(i => { - clickAction!(i.Items); + clickAction(i.Items); }); }; clickAction(flyout.Items); From 458fd9b3b142543cfd17163be488bdfdf88a8382 Mon Sep 17 00:00:00 2001 From: ferrariofilippo Date: Tue, 13 Sep 2022 14:51:14 +0200 Subject: [PATCH 12/15] Support for Ctrl & Shift and Bug Fix --- src/Files.App/BaseLayout.cs | 2272 +++++++++-------- .../Views/LayoutModes/GridViewBrowser.xaml | 2 +- 2 files changed, 1148 insertions(+), 1126 deletions(-) diff --git a/src/Files.App/BaseLayout.cs b/src/Files.App/BaseLayout.cs index 98690d5ac023..b9de56c17ba7 100644 --- a/src/Files.App/BaseLayout.cs +++ b/src/Files.App/BaseLayout.cs @@ -41,1129 +41,1151 @@ namespace Files.App { - /// - /// The base class which every layout page must derive from - /// - public abstract class BaseLayout : Page, IBaseLayout, INotifyPropertyChanged - { - private readonly DispatcherQueueTimer jumpTimer; - - protected IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetService()!; - - protected IFileTagsSettingsService FileTagsSettingsService { get; } = Ioc.Default.GetService()!; - - protected Task Connection => AppServiceConnectionHelper.Instance; - - public SelectedItemsPropertiesViewModel SelectedItemsPropertiesViewModel { get; } - - public FolderSettingsViewModel? FolderSettings => ParentShellPageInstance?.InstanceViewModel.FolderSettings; - - public CurrentInstanceViewModel? InstanceViewModel => ParentShellPageInstance?.InstanceViewModel; - - public IPaneViewModel PaneViewModel => App.PaneViewModel; - - public AppModel AppModel => App.AppModel; - public DirectoryPropertiesViewModel DirectoryPropertiesViewModel { get; } - - public CommandBarFlyout ItemContextMenuFlyout { get; set; } = new CommandBarFlyout() - { - AlwaysExpanded = true, - }; - public CommandBarFlyout BaseContextMenuFlyout { get; set; } = new CommandBarFlyout() - { - AlwaysExpanded = true, - }; - - public BaseLayoutCommandsViewModel? CommandsViewModel { get; protected set; } - - public IShellPage? ParentShellPageInstance { get; private set; } = null; - - public bool IsRenamingItem { get; set; } = false; - public ListedItem? RenamingItem { get; set; } = null; - - public string? OldItemName { get; set; } = null; - - private bool isMiddleClickToScrollEnabled = true; - - public bool IsMiddleClickToScrollEnabled - { - get => isMiddleClickToScrollEnabled; - set - { - if (isMiddleClickToScrollEnabled != value) - { - isMiddleClickToScrollEnabled = value; - NotifyPropertyChanged(nameof(IsMiddleClickToScrollEnabled)); - } - } - } - - protected AddressToolbar? NavToolbar => (App.Window.Content as Frame)?.FindDescendant(); - - private CollectionViewSource collectionViewSource = new CollectionViewSource() - { - IsSourceGrouped = true, - }; - - public CollectionViewSource CollectionViewSource - { - get => collectionViewSource; - set - { - if (collectionViewSource == value) - return; - if (collectionViewSource.View is not null) - collectionViewSource.View.VectorChanged -= View_VectorChanged; - collectionViewSource = value; - NotifyPropertyChanged(nameof(CollectionViewSource)); - if (collectionViewSource.View is not null) - collectionViewSource.View.VectorChanged += View_VectorChanged; - } - } - - protected NavigationArguments? navigationArguments; - - private bool isItemSelected = false; - - public bool IsItemSelected - { - get - { - return isItemSelected; - } - internal set - { - if (value != isItemSelected) - { - isItemSelected = value; - NotifyPropertyChanged(nameof(IsItemSelected)); - } - } - } - - private string jumpString = string.Empty; - - public string JumpString - { - get => jumpString; - set - { - // If current string is "a", and the next character typed is "a", - // search for next file that starts with "a" (a.k.a. _jumpString = "a") - if (jumpString.Length == 1 && value == jumpString + jumpString) - value = jumpString; - if (value != string.Empty) - { - ListedItem? jumpedToItem = null; - ListedItem? previouslySelectedItem = null; - - if (IsItemSelected) - previouslySelectedItem = SelectedItem; - - // Select first matching item after currently selected item - if (previouslySelectedItem != null) - { - // Use FilesAndFolders because only displayed entries should be jumped to - IEnumerable candidateItems = ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders - .SkipWhile(x => x != previouslySelectedItem) - .Skip(value.Length == 1 ? 1 : 0) // User is trying to cycle through items starting with the same letter - .Where(f => f.ItemName.Length >= value.Length && string.Equals(f.ItemName.Substring(0, value.Length), value, StringComparison.OrdinalIgnoreCase)); - jumpedToItem = candidateItems.FirstOrDefault(); - } - - if (jumpedToItem == null) - { - // Use FilesAndFolders because only displayed entries should be jumped to - IEnumerable candidateItems = ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders - .Where(f => f.ItemName.Length >= value.Length && string.Equals(f.ItemName.Substring(0, value.Length), value, StringComparison.OrdinalIgnoreCase)); - jumpedToItem = candidateItems.FirstOrDefault(); - } - - if (jumpedToItem != null) - { - ItemManipulationModel.SetSelectedItem(jumpedToItem); - ItemManipulationModel.ScrollIntoView(jumpedToItem); - ItemManipulationModel.FocusSelectedItems(); - } - - // Restart the timer - jumpTimer.Start(); - } - jumpString = value; - } - } - - private List? selectedItems = new List(); - - public List? SelectedItems - { - get - { - return selectedItems; - } - internal set - { - //if (!(value?.All(x => selectedItems?.Contains(x) ?? false) ?? value == selectedItems)) // check if the new list is different then the old one - if (value != selectedItems) // check if the new list is different then the old one - { - if (value?.FirstOrDefault() != selectedItems?.FirstOrDefault()) - { - // update preview pane properties - if (value?.Count == 1) - { - App.PreviewPaneViewModel.IsItemSelected = true; - App.PreviewPaneViewModel.SelectedItem = value.First(); - } - else - { - App.PreviewPaneViewModel.IsItemSelected = value?.Count > 0; - App.PreviewPaneViewModel.SelectedItem = null; - } - - // check if the preview pane is open before updating the model - if (PaneViewModel.IsPreviewSelected) - { - bool isPaneEnabled = ((App.Window.Content as Frame)?.Content as MainPage)?.IsPaneEnabled ?? false; - if (isPaneEnabled) - App.PreviewPaneViewModel.UpdateSelectedItemPreview(); - } - } - - selectedItems = value; - if (selectedItems?.Count == 0 || selectedItems?[0] == null) - { - IsItemSelected = false; - SelectedItem = null; - SelectedItemsPropertiesViewModel.IsItemSelected = false; - ResetRenameDoubleClick(); - UpdateSelectionSize(); - } - else - { - IsItemSelected = true; - SelectedItem = selectedItems.First(); - SelectedItemsPropertiesViewModel.IsItemSelected = true; - UpdateSelectionSize(); - - if (SelectedItems?.Count >= 1) - SelectedItemsPropertiesViewModel.SelectedItemsCount = SelectedItems.Count; - - if (SelectedItems?.Count == 1) - { - SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems.Count} {"ItemSelected/Text".GetLocalizedResource()}"; - DispatcherQueue.EnqueueAsync(async () => - { - await Task.Delay(50); // Tapped event must be executed first - preRenamingItem = SelectedItem; - }); - } - else - { - SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems!.Count} {"ItemsSelected/Text".GetLocalizedResource()}"; - ResetRenameDoubleClick(); - } - } - - NotifyPropertyChanged(nameof(SelectedItems)); - //ItemManipulationModel.SetDragModeForItems(); - } - - ParentShellPageInstance!.ToolbarViewModel.SelectedItems = value; - } - } - - public ListedItem? SelectedItem { get; private set; } - - private DispatcherQueueTimer dragOverTimer, tapDebounceTimer, hoverTimer; - - protected abstract uint IconSize { get; } - - protected abstract ItemsControl ItemsControl { get; } - - public BaseLayout() - { - ItemManipulationModel = new ItemManipulationModel(); - - HookBaseEvents(); - HookEvents(); - - jumpTimer = DispatcherQueue.CreateTimer(); - jumpTimer.Interval = TimeSpan.FromSeconds(0.8); - jumpTimer.Tick += JumpTimer_Tick; - - SelectedItemsPropertiesViewModel = new SelectedItemsPropertiesViewModel(); - DirectoryPropertiesViewModel = new DirectoryPropertiesViewModel(); - - dragOverTimer = DispatcherQueue.CreateTimer(); - tapDebounceTimer = DispatcherQueue.CreateTimer(); - hoverTimer = DispatcherQueue.CreateTimer(); - } - - protected abstract void HookEvents(); - - protected abstract void UnhookEvents(); - - private void HookBaseEvents() - { - ItemManipulationModel.RefreshItemsOpacityInvoked += ItemManipulationModel_RefreshItemsOpacityInvoked; - } - - private void UnhookBaseEvents() - { - ItemManipulationModel.RefreshItemsOpacityInvoked -= ItemManipulationModel_RefreshItemsOpacityInvoked; - } - - public ItemManipulationModel ItemManipulationModel { get; private set; } - - private void JumpTimer_Tick(object sender, object e) - { - jumpString = string.Empty; - jumpTimer.Stop(); - } - - protected abstract void InitializeCommandsViewModel(); - - protected IEnumerable? GetAllItems() - { - var items = CollectionViewSource.IsSourceGrouped ? // add all items from each group to the new list - (CollectionViewSource.Source as BulkConcurrentObservableCollection>)?.SelectMany(g => g) : - CollectionViewSource.Source as IEnumerable; - return items ?? new List(); - } - - public virtual void ResetItemOpacity() - { - foreach (var item in GetAllItems()) - { - if (item != null) - item.Opacity = item.IsHiddenItem ? Constants.UI.DimItemOpacity : 1.0d; - } - } - - protected ListedItem? GetItemFromElement(object element) - { - var item = element as ContentControl; - if (item == null || !CanGetItemFromElement(element)) - return null; - - return (item.DataContext as ListedItem) ?? (item.Content as ListedItem) ?? (ItemsControl.ItemFromContainer(item) as ListedItem); - } - - protected abstract bool CanGetItemFromElement(object element); - - protected virtual void BaseFolderSettings_LayoutModeChangeRequested(object? sender, LayoutModeEventArgs e) - { - if (ParentShellPageInstance?.SlimContentPage != null) - { - var layoutType = FolderSettings!.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); - - if (layoutType != ParentShellPageInstance.CurrentPageType) - { - ParentShellPageInstance.NavigateWithArguments(layoutType, new NavigationArguments() - { - NavPathParam = navigationArguments!.NavPathParam, - IsSearchResultPage = navigationArguments.IsSearchResultPage, - SearchPathParam = navigationArguments.SearchPathParam, - SearchQuery = navigationArguments.SearchQuery, - SearchUnindexedItems = navigationArguments.SearchUnindexedItems, - IsLayoutSwitch = true, - AssociatedTabInstance = ParentShellPageInstance - }); - - // Remove old layout from back stack - ParentShellPageInstance.RemoveLastPageFromBackStack(); - } - ParentShellPageInstance.FilesystemViewModel.UpdateEmptyTextType(); - } - } - - public event PropertyChangedEventHandler? PropertyChanged; - - protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "") - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - protected override async void OnNavigatedTo(NavigationEventArgs eventArgs) - { - base.OnNavigatedTo(eventArgs); - // Add item jumping handler - this.CharacterReceived += Page_CharacterReceived; - navigationArguments = (NavigationArguments)eventArgs.Parameter; - ParentShellPageInstance = navigationArguments.AssociatedTabInstance; - InitializeCommandsViewModel(); - - IsItemSelected = false; - FolderSettings!.LayoutModeChangeRequested += BaseFolderSettings_LayoutModeChangeRequested; - FolderSettings.GroupOptionPreferenceUpdated += FolderSettings_GroupOptionPreferenceUpdated; - ParentShellPageInstance.FilesystemViewModel.EmptyTextType = EmptyTextType.None; - ParentShellPageInstance.ToolbarViewModel.UpdateSortAndGroupOptions(); - - if (!navigationArguments.IsSearchResultPage) - { - ParentShellPageInstance.ToolbarViewModel.CanRefresh = true; - string previousDir = ParentShellPageInstance.FilesystemViewModel.WorkingDirectory; - await ParentShellPageInstance.FilesystemViewModel.SetWorkingDirectoryAsync(navigationArguments.NavPathParam); - - // pathRoot will be empty on recycle bin path - var workingDir = ParentShellPageInstance.FilesystemViewModel.WorkingDirectory ?? string.Empty; - string pathRoot = GetPathRoot(workingDir); - if (string.IsNullOrEmpty(pathRoot) || workingDir.StartsWith(CommonPaths.RecycleBinPath, StringComparison.Ordinal)) // Can't go up from recycle bin - ParentShellPageInstance.ToolbarViewModel.CanNavigateToParent = false; - else - ParentShellPageInstance.ToolbarViewModel.CanNavigateToParent = true; - - ParentShellPageInstance.InstanceViewModel.IsPageTypeRecycleBin = workingDir.StartsWith(CommonPaths.RecycleBinPath, StringComparison.Ordinal); - ParentShellPageInstance.InstanceViewModel.IsPageTypeMtpDevice = workingDir.StartsWith("\\\\?\\", StringComparison.Ordinal); - ParentShellPageInstance.InstanceViewModel.IsPageTypeFtp = FtpHelpers.IsFtpPath(workingDir); - ParentShellPageInstance.InstanceViewModel.IsPageTypeZipFolder = ZipStorageFolder.IsZipPath(workingDir); - ParentShellPageInstance.InstanceViewModel.IsPageTypeLibrary = LibraryHelper.IsLibraryPath(workingDir); - ParentShellPageInstance.InstanceViewModel.IsPageTypeSearchResults = false; - ParentShellPageInstance.ToolbarViewModel.PathControlDisplayText = navigationArguments.NavPathParam; - if (!navigationArguments.IsLayoutSwitch || previousDir != workingDir) - ParentShellPageInstance.FilesystemViewModel.RefreshItems(previousDir, SetSelectedItemsOnNavigation); - else - ParentShellPageInstance.ToolbarViewModel.CanGoForward = false; - } - else - { - ParentShellPageInstance.ToolbarViewModel.CanRefresh = true; - await ParentShellPageInstance.FilesystemViewModel.SetWorkingDirectoryAsync(navigationArguments.SearchPathParam); - - ParentShellPageInstance.ToolbarViewModel.CanGoForward = false; - ParentShellPageInstance.ToolbarViewModel.CanGoBack = true; // Impose no artificial restrictions on back navigation. Even in a search results page. - ParentShellPageInstance.ToolbarViewModel.CanNavigateToParent = false; - - var workingDir = ParentShellPageInstance.FilesystemViewModel.WorkingDirectory ?? string.Empty; - ParentShellPageInstance.InstanceViewModel.IsPageTypeRecycleBin = workingDir.StartsWith(CommonPaths.RecycleBinPath, StringComparison.Ordinal); - ParentShellPageInstance.InstanceViewModel.IsPageTypeMtpDevice = workingDir.StartsWith("\\\\?\\", StringComparison.Ordinal); - ParentShellPageInstance.InstanceViewModel.IsPageTypeFtp = FtpHelpers.IsFtpPath(workingDir); - ParentShellPageInstance.InstanceViewModel.IsPageTypeZipFolder = ZipStorageFolder.IsZipPath(workingDir); - ParentShellPageInstance.InstanceViewModel.IsPageTypeLibrary = LibraryHelper.IsLibraryPath(workingDir); - ParentShellPageInstance.InstanceViewModel.IsPageTypeSearchResults = true; - - if (!navigationArguments.IsLayoutSwitch) - { - var displayName = App.LibraryManager.TryGetLibrary(navigationArguments.SearchPathParam, out var lib) ? lib.Text : navigationArguments.SearchPathParam; - ParentShellPageInstance.UpdatePathUIToWorkingDirectory(null, string.Format("SearchPagePathBoxOverrideText".GetLocalizedResource(), navigationArguments.SearchQuery, displayName)); - var searchInstance = new Filesystem.Search.FolderSearch - { - Query = navigationArguments.SearchQuery, - Folder = navigationArguments.SearchPathParam, - ThumbnailSize = InstanceViewModel!.FolderSettings.GetIconSize(), - SearchUnindexedItems = navigationArguments.SearchUnindexedItems - }; - _ = ParentShellPageInstance.FilesystemViewModel.SearchAsync(searchInstance); - } - } - - ParentShellPageInstance.InstanceViewModel.IsPageTypeNotHome = true; // show controls that were hidden on the home page - ParentShellPageInstance.FilesystemViewModel.UpdateGroupOptions(); - UpdateCollectionViewSource(); - FolderSettings.IsLayoutModeChanging = false; - - SetSelectedItemsOnNavigation(); - - ItemContextMenuFlyout.Opening += ItemContextFlyout_Opening; - BaseContextMenuFlyout.Opening += BaseContextFlyout_Opening; - } - - public void SetSelectedItemsOnNavigation() - { - try - { - if (navigationArguments != null && navigationArguments.SelectItems != null && navigationArguments.SelectItems.Any()) - { - List liItemsToSelect = new List(); - foreach (string item in navigationArguments.SelectItems) - liItemsToSelect.Add(ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders.Where((li) => li.ItemNameRaw == item).First()); - - ItemManipulationModel.SetSelectedItems(liItemsToSelect); - ItemManipulationModel.FocusSelectedItems(); - } - else if (navigationArguments != null && navigationArguments.FocusOnNavigation) - { - ItemManipulationModel.FocusFileList(); // Set focus on layout specific file list control - } - } - catch (Exception) - { - } - } - - private CancellationTokenSource? groupingCancellationToken; - - private async void FolderSettings_GroupOptionPreferenceUpdated(object? sender, GroupOption e) - { - // Two or more of these running at the same time will cause a crash, so cancel the previous one before beginning - groupingCancellationToken?.Cancel(); - groupingCancellationToken = new CancellationTokenSource(); - var token = groupingCancellationToken.Token; - await ParentShellPageInstance!.FilesystemViewModel.GroupOptionsUpdated(token); - UpdateCollectionViewSource(); - await ParentShellPageInstance.FilesystemViewModel.ReloadItemGroupHeaderImagesAsync(); - } - - protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) - { - base.OnNavigatingFrom(e); - // Remove item jumping handler - this.CharacterReceived -= Page_CharacterReceived; - FolderSettings!.LayoutModeChangeRequested -= BaseFolderSettings_LayoutModeChangeRequested; - FolderSettings.GroupOptionPreferenceUpdated -= FolderSettings_GroupOptionPreferenceUpdated; - ItemContextMenuFlyout.Opening -= ItemContextFlyout_Opening; - BaseContextMenuFlyout.Opening -= BaseContextFlyout_Opening; - - var parameter = e.Parameter as NavigationArguments; - if (parameter is not null && !parameter.IsLayoutSwitch) - ParentShellPageInstance!.FilesystemViewModel.CancelLoadAndClearFiles(); - } - - public async void ItemContextFlyout_Opening(object? sender, object e) - { - try - { - if (!IsItemSelected) // Workaround for item sometimes not getting selected - { - if (((sender as CommandBarFlyout)?.Target as ListViewItem)?.Content is ListedItem li) - ItemManipulationModel.SetSelectedItem(li); - } - if (IsItemSelected) - await LoadMenuItemsAsync(); - } - catch (Exception error) - { - Debug.WriteLine(error); - } - } - - private CancellationTokenSource? shellContextMenuItemCancellationToken; - - public async void BaseContextFlyout_Opening(object? sender, object e) - { - try - { - if (BaseContextMenuFlyout.GetValue(ContextMenuExtensions.ItemsControlProperty) is ItemsControl itc) - itc.MaxHeight = Constants.UI.ContextMenuMaxHeight; // Reset menu max height - shellContextMenuItemCancellationToken?.Cancel(); - shellContextMenuItemCancellationToken = new CancellationTokenSource(); - var shiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down); - var items = ContextFlyoutItemHelper.GetBaseContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, itemViewModel: ParentShellPageInstance!.FilesystemViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, false); - BaseContextMenuFlyout.PrimaryCommands.Clear(); - BaseContextMenuFlyout.SecondaryCommands.Clear(); - var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); - primaryElements.OfType().ForEach(i => - { - i.Click += new RoutedEventHandler((s, e) => BaseContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) - }); - primaryElements.ForEach(i => BaseContextMenuFlyout.PrimaryCommands.Add(i)); - secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width - secondaryElements.ForEach(i => BaseContextMenuFlyout.SecondaryCommands.Add(i)); - - if (!InstanceViewModel!.IsPageTypeSearchResults && !InstanceViewModel.IsPageTypeZipFolder) - { - var shellMenuItems = await ContextFlyoutItemHelper.GetBaseContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); - if (shellMenuItems.Any()) - AddShellItemsToMenu(shellMenuItems, BaseContextMenuFlyout, shiftPressed); - } - } - catch (Exception error) - { - Debug.WriteLine(error); - } - } - - public void UpdateSelectionSize() - { - var items = (selectedItems?.Any() ?? false) ? selectedItems : GetAllItems(); - if (items is null) - return; - bool isSizeKnown = !items.Any(item => string.IsNullOrEmpty(item.FileSize)); - if (isSizeKnown) - { - long size = items.Sum(item => item.FileSizeBytes); - SelectedItemsPropertiesViewModel.ItemSizeBytes = size; - SelectedItemsPropertiesViewModel.ItemSize = size.ToSizeString(); - } - else - { - SelectedItemsPropertiesViewModel.ItemSizeBytes = 0; - SelectedItemsPropertiesViewModel.ItemSize = string.Empty; - } - SelectedItemsPropertiesViewModel.ItemSizeVisibility = isSizeKnown; - } - - private async Task LoadMenuItemsAsync() - { - if (ItemContextMenuFlyout.GetValue(ContextMenuExtensions.ItemsControlProperty) is ItemsControl itc) - itc.MaxHeight = Constants.UI.ContextMenuMaxHeight; // Reset menu max height - shellContextMenuItemCancellationToken?.Cancel(); - shellContextMenuItemCancellationToken = new CancellationTokenSource(); - SelectedItemsPropertiesViewModel.CheckAllFileExtensions(this.SelectedItems!.Select(selectedItem => selectedItem?.FileExtension).ToList()!); - var shiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down); - var items = ContextFlyoutItemHelper.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, workingDir: ParentShellPageInstance!.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, selectedItemsPropertiesViewModel: SelectedItemsPropertiesViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, showOpenMenu: false); - ItemContextMenuFlyout.PrimaryCommands.Clear(); - ItemContextMenuFlyout.SecondaryCommands.Clear(); - var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); - primaryElements.OfType().ForEach(i => - { - i.Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) - }); - primaryElements.ForEach(i => ItemContextMenuFlyout.PrimaryCommands.Add(i)); - secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width - secondaryElements.ForEach(i => ItemContextMenuFlyout.SecondaryCommands.Add(i)); - - if (InstanceViewModel!.CanTagFilesInPage) - AddNewFileTagsToMenu(ItemContextMenuFlyout); - - if (!InstanceViewModel.IsPageTypeZipFolder) - { - var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); - if (shellMenuItems.Any()) - AddShellItemsToMenu(shellMenuItems, ItemContextMenuFlyout, shiftPressed); - } - } - - private void AddNewFileTagsToMenu(CommandBarFlyout contextMenu) - { - var fileTagsContextMenu = new FileTagsContextMenu(SelectedItems!); - var overflowSeparator = contextMenu.SecondaryCommands.FirstOrDefault(x => x is FrameworkElement fe && fe.Tag as string == "OverflowSeparator") as AppBarSeparator; - var index = contextMenu.SecondaryCommands.IndexOf(overflowSeparator); - index = index >= 0 ? index : contextMenu.SecondaryCommands.Count; - contextMenu.SecondaryCommands.Insert(index, new AppBarSeparator()); - contextMenu.SecondaryCommands.Insert(index + 1, new AppBarButton() - { - Label = "SettingsEditFileTagsExpander/Title".GetLocalizedResource(), - Icon = new FontIcon() { Glyph = "\uE1CB" }, - Flyout = fileTagsContextMenu - }); - } - - private void AddShellItemsToMenu(List shellMenuItems, Microsoft.UI.Xaml.Controls.CommandBarFlyout contextMenuFlyout, bool shiftPressed) - { - var openWithSubItems = ItemModelListToContextFlyoutHelper.GetMenuFlyoutItemsFromModel(ShellContextmenuHelper.GetOpenWithItems(shellMenuItems)); - var mainShellMenuItems = shellMenuItems.RemoveFrom(!UserSettingsService.AppearanceSettingsService.MoveOverflowMenuItemsToSubMenu ? int.MaxValue : shiftPressed ? 6 : 4); - var overflowShellMenuItems = shellMenuItems.Except(mainShellMenuItems).ToList(); - - var overflowItems = ItemModelListToContextFlyoutHelper.GetMenuFlyoutItemsFromModel(overflowShellMenuItems); - var mainItems = ItemModelListToContextFlyoutHelper.GetAppBarButtonsFromModelIgnorePrimary(mainShellMenuItems); - - var openedPopups = Microsoft.UI.Xaml.Media.VisualTreeHelper.GetOpenPopups(App.Window); - var secondaryMenu = openedPopups.FirstOrDefault(popup => popup.Name == "OverflowPopup"); - var itemsControl = secondaryMenu?.Child.FindDescendant(); - if (itemsControl is not null && secondaryMenu is not null) - { - contextMenuFlyout.SetValue(ContextMenuExtensions.ItemsControlProperty, itemsControl); - - var ttv = secondaryMenu.TransformToVisual(App.Window.Content); - var cMenuPos = ttv.TransformPoint(new Point(0, 0)); - var requiredHeight = contextMenuFlyout.SecondaryCommands.Concat(mainItems).Where(x => x is not AppBarSeparator).Count() * Constants.UI.ContextMenuSecondaryItemsHeight; - var availableHeight = App.Window.Bounds.Height - cMenuPos.Y - Constants.UI.ContextMenuPrimaryItemsHeight; - if (requiredHeight > availableHeight) - itemsControl.MaxHeight = Math.Min(Constants.UI.ContextMenuMaxHeight, Math.Max(itemsControl.ActualHeight, Math.Min(availableHeight, requiredHeight))); // Set menu max height to current height (avoids menu repositioning) - - mainItems.OfType().ForEach(x => x.MaxWidth = itemsControl.ActualWidth - Constants.UI.ContextMenuLabelMargin); // Set items max width to current menu width (#5555) - } - - var overflowItem = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton appBarButton && (appBarButton.Tag as string) == "ItemOverflow") as AppBarButton; - if (overflowItem is not null) - { - var overflowItemFlyout = overflowItem.Flyout as MenuFlyout; - if (overflowItemFlyout is not null) - { - if (overflowItemFlyout.Items.Count > 0) - overflowItemFlyout.Items.Insert(0, new MenuFlyoutSeparator()); - - var index = contextMenuFlyout.SecondaryCommands.Count - 2; - foreach (var i in mainItems) - { - index++; - contextMenuFlyout.SecondaryCommands.Insert(index, i); - } - - index = 0; - foreach (var i in overflowItems) - { - overflowItemFlyout.Items.Insert(index, i); - index++; - } - - if (overflowItemFlyout.Items.Count > 0) - { - (contextMenuFlyout.SecondaryCommands.First(x => x is FrameworkElement fe && fe.Tag as string == "OverflowSeparator") as AppBarSeparator)!.Visibility = Visibility.Visible; - overflowItem.Visibility = Visibility.Visible; - } - } - } - else - { - mainItems.ForEach(x => contextMenuFlyout.SecondaryCommands.Add(x)); - } - - // add items to openwith dropdown - var openWithOverflow = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWithOverflow") as AppBarButton; - var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; - if (openWithSubItems is not null && openWithOverflow is not null && openWith is not null) - { - var flyout = (MenuFlyout)openWithOverflow.Flyout; - - flyout.Items.Clear(); - - foreach (var item in openWithSubItems) - { - flyout.Items.Add(item); - } - - openWithOverflow.Flyout = flyout; - openWith.Visibility = Visibility.Collapsed; - openWithOverflow.Visibility = Visibility.Visible; - } - - if (itemsControl is not null) - { - itemsControl.Items.OfType().ForEach(item => - { - if (item.FindDescendant("OverflowTextLabel") is TextBlock label) // Enable CharacterEllipsis text trimming for menu items - label.TextTrimming = TextTrimming.CharacterEllipsis; - if ((item as AppBarButton)?.Flyout as MenuFlyout is MenuFlyout flyout) // Close main menu when clicking on subitems (#5508) - { - Action> clickAction = null!; - clickAction = (items) => - { - items.OfType().ForEach(i => - { - i.Click += new RoutedEventHandler((s, e) => contextMenuFlyout.Hide()); - }); - items.OfType().ForEach(i => - { - clickAction(i.Items); - }); - }; - clickAction(flyout.Items); - } - }); - } - } - - protected virtual void Page_CharacterReceived(UIElement sender, CharacterReceivedRoutedEventArgs args) - { - if (ParentShellPageInstance!.IsCurrentInstance) - { - char letter = args.Character; - JumpString += letter.ToString().ToLowerInvariant(); - } - } - - protected void FileList_DragItemsStarting(object sender, DragItemsStartingEventArgs e) - { - e.Items.OfType().ForEach(item => SelectedItems!.Add(item)); - - try - { - // Only support IStorageItem capable paths - var itemList = e.Items.OfType().Where(x => !(x.IsHiddenItem && x.IsLinkItem && x.IsRecycleBinItem && x.IsShortcutItem)).Select(x => VirtualStorageItem.FromListedItem(x)); - e.Data.SetStorageItems(itemList, false); - } - catch (Exception) - { - e.Cancel = true; - } - } - - private ListedItem? dragOverItem = null; - - private void Item_DragLeave(object sender, DragEventArgs e) - { - var item = GetItemFromElement(sender); - if (item == dragOverItem) - dragOverItem = null; // Reset dragged over item - } - - protected async void Item_DragOver(object sender, DragEventArgs e) - { - var item = GetItemFromElement(sender); - if (item is null) - return; - - DragOperationDeferral? deferral = null; - try - { - deferral = e.GetDeferral(); - - ItemManipulationModel.SetSelectedItem(item); - - if (dragOverItem != item) - { - dragOverItem = item; - dragOverTimer.Stop(); - dragOverTimer.Debounce(() => - { - if (dragOverItem != null && !dragOverItem.IsExecutable) - { - dragOverItem = null; - dragOverTimer.Stop(); - NavigationHelpers.OpenSelectedItems(ParentShellPageInstance!, false); - } - }, TimeSpan.FromMilliseconds(1000), false); - } - - if (FilesystemHelpers.HasDraggedStorageItems(e.DataView)) - { - e.Handled = true; - - var handledByFtp = await FilesystemHelpers.CheckDragNeedsFulltrust(e.DataView); - var draggedItems = await FilesystemHelpers.GetDraggedStorageItems(e.DataView); - - if (draggedItems.Any(draggedItem => draggedItem.Path == item.ItemPath)) - { - e.AcceptedOperation = DataPackageOperation.None; - } - else if (handledByFtp) - { - e.DragUIOverride.IsCaptionVisible = true; - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), item.ItemName); - e.AcceptedOperation = DataPackageOperation.Copy; - } - else if (!draggedItems.Any()) - { - e.AcceptedOperation = DataPackageOperation.None; - } - else - { - e.DragUIOverride.IsCaptionVisible = true; - if (item.IsExecutable) - { - e.DragUIOverride.Caption = $"{"OpenItemsWithCaptionText".GetLocalizedResource()} {item.ItemName}"; - e.AcceptedOperation = DataPackageOperation.Link; - } // Items from the same drive as this folder are dragged into this folder, so we move the items instead of copy - else if (e.Modifiers.HasFlag(DragDropModifiers.Alt) || e.Modifiers.HasFlag(DragDropModifiers.Control | DragDropModifiers.Shift)) - { - e.DragUIOverride.Caption = string.Format("LinkToFolderCaptionText".GetLocalizedResource(), item.ItemName); - e.AcceptedOperation = DataPackageOperation.Link; - } - else if (e.Modifiers.HasFlag(DragDropModifiers.Control)) - { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), item.ItemName); - e.AcceptedOperation = DataPackageOperation.Copy; - } - else if (e.Modifiers.HasFlag(DragDropModifiers.Shift)) - { - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), item.ItemName); - e.AcceptedOperation = DataPackageOperation.Move; - } - else if (draggedItems.Any(x => x.Item is ZipStorageFile || x.Item is ZipStorageFolder) - || ZipStorageFolder.IsZipPath(item.ItemPath)) - { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), item.ItemName); - e.AcceptedOperation = DataPackageOperation.Copy; - } - else if (draggedItems.AreItemsInSameDrive(item.ItemPath)) - { - e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), item.ItemName); - e.AcceptedOperation = DataPackageOperation.Move; - } - else - { - e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), item.ItemName); - e.AcceptedOperation = DataPackageOperation.Copy; - } - } - } - } - finally - { - deferral?.Complete(); - } - } - - protected async void Item_Drop(object sender, DragEventArgs e) - { - var deferral = e.GetDeferral(); - - e.Handled = true; - dragOverItem = null; // Reset dragged over item - - var item = GetItemFromElement(sender); - if (item != null) - await ParentShellPageInstance!.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as ShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable); - deferral.Complete(); - } - - protected void FileList_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args) - { - RefreshContainer(args.ItemContainer, args.InRecycleQueue); - RefreshItem(args.ItemContainer, args.Item, args.InRecycleQueue, args); - } - - private void RefreshContainer(SelectorItem container, bool inRecycleQueue) - { - container.PointerPressed -= FileListItem_PointerPressed; - if (inRecycleQueue) - UninitializeDrag(container); - else - container.PointerPressed += FileListItem_PointerPressed; - } - - private void RefreshItem(SelectorItem container, object item, bool inRecycleQueue, ContainerContentChangingEventArgs args) - { - if (item is not ListedItem listedItem) - return; - - if (inRecycleQueue) - { - ParentShellPageInstance!.FilesystemViewModel.CancelExtendedPropertiesLoadingForItem(listedItem); - } - else - { - InitializeDrag(container, listedItem); - - if (!listedItem.ItemPropertiesInitialized) - { - uint callbackPhase = 3; - args.RegisterUpdateCallback(callbackPhase, async (s, c) => - { - await ParentShellPageInstance!.FilesystemViewModel.LoadExtendedItemProperties(listedItem, IconSize); - }); - } - } - } - - protected static void FileListItem_PointerPressed(object sender, PointerRoutedEventArgs e) - { - if (sender is not SelectorItem selectorItem) - return; - - if (selectorItem.IsSelected && e.KeyModifiers == VirtualKeyModifiers.Control) - { - selectorItem.IsSelected = false; - // Prevent issues arising caused by the default handlers attempting to select the item that has just been deselected by ctrl + click - e.Handled = true; - } - else if (!selectorItem.IsSelected && e.GetCurrentPoint(selectorItem).Properties.IsLeftButtonPressed) - { - selectorItem.IsSelected = true; - } - } - - private ListedItem? hoveredItem = null; - - protected internal void ListedItem_PointerEntered(object sender, PointerRoutedEventArgs e) - { - if (!UserSettingsService.PreferencesSettingsService.SelectFilesOnHover) - return; - - var hovered = (sender as Grid)?.DataContext as ListedItem; - if (hovered != hoveredItem) - { - hoveredItem = hovered; - hoverTimer.Stop(); - hoverTimer.Debounce(() => - { - if (hoveredItem is not null) - { - hoverTimer.Stop(); - ItemManipulationModel.SetSelectedItem(hoveredItem); - hoveredItem = null; - } - }, TimeSpan.FromMilliseconds(600), false); - } - } - - protected internal void ListedItem_PointerExited(object sender, PointerRoutedEventArgs e) - { - if (!UserSettingsService.PreferencesSettingsService.SelectFilesOnHover) - return; - hoverTimer.Stop(); - hoveredItem = null; - } - - private readonly RecycleBinHelpers recycleBinHelpers = new(); - - protected void InitializeDrag(UIElement containter, ListedItem item) - { - if (item is null) - return; - - UninitializeDrag(containter); - if ((item.PrimaryItemAttribute == StorageItemTypes.Folder && !recycleBinHelpers.IsPathUnderRecycleBin(item.ItemPath)) || item.IsExecutable) - { - containter.AllowDrop = true; - containter.DragOver += Item_DragOver; - containter.DragLeave += Item_DragLeave; - containter.Drop += Item_Drop; - } - } - - protected void UninitializeDrag(UIElement element) - { - element.AllowDrop = false; - element.DragOver -= Item_DragOver; - element.DragLeave -= Item_DragLeave; - element.Drop -= Item_Drop; - } - - // VirtualKey doesn't support / accept plus and minus by default. - public readonly VirtualKey PlusKey = (VirtualKey)187; - - public readonly VirtualKey MinusKey = (VirtualKey)189; - - public virtual void Dispose() - { - PaneViewModel?.Dispose(); - UnhookBaseEvents(); - } - - protected void ItemsLayout_DragOver(object sender, DragEventArgs e) - { - CommandsViewModel?.DragOverCommand?.Execute(e); - } - - protected void ItemsLayout_Drop(object sender, DragEventArgs e) - { - CommandsViewModel?.DropCommand?.Execute(e); - } - - private void UpdateCollectionViewSource() - { - if (ParentShellPageInstance is null) - return; - if (ParentShellPageInstance.FilesystemViewModel.FilesAndFolders.IsGrouped) - { - CollectionViewSource = new CollectionViewSource() - { - IsSourceGrouped = true, - Source = ParentShellPageInstance.FilesystemViewModel.FilesAndFolders.GroupedCollection - }; - } - else - { - CollectionViewSource = new CollectionViewSource() - { - IsSourceGrouped = false, - Source = ParentShellPageInstance.FilesystemViewModel.FilesAndFolders - }; - } - } - - protected void SemanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e) - { - if (!e.IsSourceZoomedInView) - { - // According to the docs this isn't necessary, but it would crash otherwise - var destination = e.DestinationItem.Item as GroupedCollection; - e.DestinationItem.Item = destination?.FirstOrDefault(); - } - } - - protected void StackPanel_PointerEntered(object sender, PointerRoutedEventArgs e) - { - var element = (sender as UIElement)?.FindAscendant(); - if (element is not null) - VisualStateManager.GoToState(element, "PointerOver", true); - } - - protected void StackPanel_PointerCanceled(object sender, PointerRoutedEventArgs e) - { - var element = (sender as UIElement)?.FindAscendant(); - if (element is not null) - VisualStateManager.GoToState(element, "Normal", true); - } - - protected void RootPanel_PointerPressed(object sender, PointerRoutedEventArgs e) - { - var element = (sender as UIElement)?.FindAscendant(); - if (element is not null) - VisualStateManager.GoToState(element, "Pressed", true); - } - - private void ItemManipulationModel_RefreshItemsOpacityInvoked(object? sender, EventArgs e) - { - foreach (ListedItem listedItem in GetAllItems()) - { - if (listedItem.IsHiddenItem) - listedItem.Opacity = Constants.UI.DimItemOpacity; - else - listedItem.Opacity = 1; - } - } - - private void View_VectorChanged(IObservableVector sender, IVectorChangedEventArgs @event) - { - if (ParentShellPageInstance is not null) - ParentShellPageInstance.ToolbarViewModel.HasItem = CollectionViewSource.View.Any(); - } - - virtual public void StartRenameItem() { } - - private ListedItem? preRenamingItem = null; - - public void CheckRenameDoubleClick(object clickedItem) - { - if (clickedItem is ListedItem item) - { - if (item == preRenamingItem) - { - tapDebounceTimer.Debounce(() => - { - if (item == preRenamingItem) - { - StartRenameItem(); - tapDebounceTimer.Stop(); - } - }, TimeSpan.FromMilliseconds(500)); - } - else - { - tapDebounceTimer.Stop(); - preRenamingItem = item; - } - } - else - { - ResetRenameDoubleClick(); - } - } - - public void ResetRenameDoubleClick() - { - preRenamingItem = null; - tapDebounceTimer.Stop(); - } - - protected async void ValidateItemNameInputText(TextBox textBox, TextBoxBeforeTextChangingEventArgs args, Action showError) - { - if (FilesystemHelpers.ContainsRestrictedCharacters(args.NewText)) - { - args.Cancel = true; - await DispatcherQueue.EnqueueAsync(() => - { - var oldSelection = textBox.SelectionStart + textBox.SelectionLength; - var oldText = textBox.Text; - textBox.Text = FilesystemHelpers.FilterRestrictedCharacters(args.NewText); - textBox.SelectionStart = oldSelection + textBox.Text.Length - oldText.Length; - showError?.Invoke(true); - }); - } - else - { - showError?.Invoke(false); - } - } - } - - public class ContextMenuExtensions : DependencyObject - { - public static ItemsControl GetItemsControl(DependencyObject obj) - { - return (ItemsControl)obj.GetValue(ItemsControlProperty); - } - - public static void SetItemsControl(DependencyObject obj, ItemsControl value) - { - obj.SetValue(ItemsControlProperty, value); - } - - public static readonly DependencyProperty ItemsControlProperty = - DependencyProperty.RegisterAttached("ItemsControl", typeof(ItemsControl), typeof(ContextMenuExtensions), new PropertyMetadata(null)); - } + /// + /// The base class which every layout page must derive from + /// + public abstract class BaseLayout : Page, IBaseLayout, INotifyPropertyChanged + { + private readonly DispatcherQueueTimer jumpTimer; + + protected IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetService()!; + + protected IFileTagsSettingsService FileTagsSettingsService { get; } = Ioc.Default.GetService()!; + + protected Task Connection => AppServiceConnectionHelper.Instance; + + public SelectedItemsPropertiesViewModel SelectedItemsPropertiesViewModel { get; } + + public FolderSettingsViewModel? FolderSettings => ParentShellPageInstance?.InstanceViewModel.FolderSettings; + + public CurrentInstanceViewModel? InstanceViewModel => ParentShellPageInstance?.InstanceViewModel; + + public IPaneViewModel PaneViewModel => App.PaneViewModel; + + public AppModel AppModel => App.AppModel; + public DirectoryPropertiesViewModel DirectoryPropertiesViewModel { get; } + + public CommandBarFlyout ItemContextMenuFlyout { get; set; } = new CommandBarFlyout() + { + AlwaysExpanded = true, + }; + public CommandBarFlyout BaseContextMenuFlyout { get; set; } = new CommandBarFlyout() + { + AlwaysExpanded = true, + }; + + public BaseLayoutCommandsViewModel? CommandsViewModel { get; protected set; } + + public IShellPage? ParentShellPageInstance { get; private set; } = null; + + public bool IsRenamingItem { get; set; } = false; + public ListedItem? RenamingItem { get; set; } = null; + + public string? OldItemName { get; set; } = null; + + private bool isMiddleClickToScrollEnabled = true; + + public bool IsMiddleClickToScrollEnabled + { + get => isMiddleClickToScrollEnabled; + set + { + if (isMiddleClickToScrollEnabled != value) + { + isMiddleClickToScrollEnabled = value; + NotifyPropertyChanged(nameof(IsMiddleClickToScrollEnabled)); + } + } + } + + protected AddressToolbar? NavToolbar => (App.Window.Content as Frame)?.FindDescendant(); + + private CollectionViewSource collectionViewSource = new CollectionViewSource() + { + IsSourceGrouped = true, + }; + + public CollectionViewSource CollectionViewSource + { + get => collectionViewSource; + set + { + if (collectionViewSource == value) + return; + if (collectionViewSource.View is not null) + collectionViewSource.View.VectorChanged -= View_VectorChanged; + collectionViewSource = value; + NotifyPropertyChanged(nameof(CollectionViewSource)); + if (collectionViewSource.View is not null) + collectionViewSource.View.VectorChanged += View_VectorChanged; + } + } + + protected NavigationArguments? navigationArguments; + + private bool isItemSelected = false; + + public bool IsItemSelected + { + get + { + return isItemSelected; + } + internal set + { + if (value != isItemSelected) + { + isItemSelected = value; + NotifyPropertyChanged(nameof(IsItemSelected)); + } + } + } + + private string jumpString = string.Empty; + + public string JumpString + { + get => jumpString; + set + { + // If current string is "a", and the next character typed is "a", + // search for next file that starts with "a" (a.k.a. _jumpString = "a") + if (jumpString.Length == 1 && value == jumpString + jumpString) + value = jumpString; + if (value != string.Empty) + { + ListedItem? jumpedToItem = null; + ListedItem? previouslySelectedItem = null; + + if (IsItemSelected) + previouslySelectedItem = SelectedItem; + + // Select first matching item after currently selected item + if (previouslySelectedItem != null) + { + // Use FilesAndFolders because only displayed entries should be jumped to + IEnumerable candidateItems = ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders + .SkipWhile(x => x != previouslySelectedItem) + .Skip(value.Length == 1 ? 1 : 0) // User is trying to cycle through items starting with the same letter + .Where(f => f.ItemName.Length >= value.Length && string.Equals(f.ItemName.Substring(0, value.Length), value, StringComparison.OrdinalIgnoreCase)); + jumpedToItem = candidateItems.FirstOrDefault(); + } + + if (jumpedToItem == null) + { + // Use FilesAndFolders because only displayed entries should be jumped to + IEnumerable candidateItems = ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders + .Where(f => f.ItemName.Length >= value.Length && string.Equals(f.ItemName.Substring(0, value.Length), value, StringComparison.OrdinalIgnoreCase)); + jumpedToItem = candidateItems.FirstOrDefault(); + } + + if (jumpedToItem != null) + { + ItemManipulationModel.SetSelectedItem(jumpedToItem); + ItemManipulationModel.ScrollIntoView(jumpedToItem); + ItemManipulationModel.FocusSelectedItems(); + } + + // Restart the timer + jumpTimer.Start(); + } + jumpString = value; + } + } + + private List? selectedItems = new List(); + + public List? SelectedItems + { + get + { + return selectedItems; + } + internal set + { + //if (!(value?.All(x => selectedItems?.Contains(x) ?? false) ?? value == selectedItems)) // check if the new list is different then the old one + if (value != selectedItems) // check if the new list is different then the old one + { + if (value?.FirstOrDefault() != selectedItems?.FirstOrDefault()) + { + // update preview pane properties + if (value?.Count == 1) + { + App.PreviewPaneViewModel.IsItemSelected = true; + App.PreviewPaneViewModel.SelectedItem = value.First(); + } + else + { + App.PreviewPaneViewModel.IsItemSelected = value?.Count > 0; + App.PreviewPaneViewModel.SelectedItem = null; + } + + // check if the preview pane is open before updating the model + if (PaneViewModel.IsPreviewSelected) + { + bool isPaneEnabled = ((App.Window.Content as Frame)?.Content as MainPage)?.IsPaneEnabled ?? false; + if (isPaneEnabled) + App.PreviewPaneViewModel.UpdateSelectedItemPreview(); + } + } + + selectedItems = value; + if (selectedItems?.Count == 0 || selectedItems?[0] == null) + { + IsItemSelected = false; + SelectedItem = null; + SelectedItemsPropertiesViewModel.IsItemSelected = false; + ResetRenameDoubleClick(); + UpdateSelectionSize(); + } + else + { + IsItemSelected = true; + SelectedItem = selectedItems.First(); + SelectedItemsPropertiesViewModel.IsItemSelected = true; + UpdateSelectionSize(); + + if (SelectedItems?.Count >= 1) + SelectedItemsPropertiesViewModel.SelectedItemsCount = SelectedItems.Count; + + if (SelectedItems?.Count == 1) + { + SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems.Count} {"ItemSelected/Text".GetLocalizedResource()}"; + DispatcherQueue.EnqueueAsync(async () => + { + await Task.Delay(50); // Tapped event must be executed first + preRenamingItem = SelectedItem; + }); + } + else + { + SelectedItemsPropertiesViewModel.SelectedItemsCountString = $"{SelectedItems!.Count} {"ItemsSelected/Text".GetLocalizedResource()}"; + ResetRenameDoubleClick(); + } + } + + NotifyPropertyChanged(nameof(SelectedItems)); + //ItemManipulationModel.SetDragModeForItems(); + } + + ParentShellPageInstance!.ToolbarViewModel.SelectedItems = value; + } + } + + public ListedItem? SelectedItem { get; private set; } + + private DispatcherQueueTimer dragOverTimer, tapDebounceTimer, hoverTimer; + + protected abstract uint IconSize { get; } + + protected abstract ItemsControl ItemsControl { get; } + + public BaseLayout() + { + ItemManipulationModel = new ItemManipulationModel(); + + HookBaseEvents(); + HookEvents(); + + jumpTimer = DispatcherQueue.CreateTimer(); + jumpTimer.Interval = TimeSpan.FromSeconds(0.8); + jumpTimer.Tick += JumpTimer_Tick; + + SelectedItemsPropertiesViewModel = new SelectedItemsPropertiesViewModel(); + DirectoryPropertiesViewModel = new DirectoryPropertiesViewModel(); + + dragOverTimer = DispatcherQueue.CreateTimer(); + tapDebounceTimer = DispatcherQueue.CreateTimer(); + hoverTimer = DispatcherQueue.CreateTimer(); + } + + protected abstract void HookEvents(); + + protected abstract void UnhookEvents(); + + private void HookBaseEvents() + { + ItemManipulationModel.RefreshItemsOpacityInvoked += ItemManipulationModel_RefreshItemsOpacityInvoked; + } + + private void UnhookBaseEvents() + { + ItemManipulationModel.RefreshItemsOpacityInvoked -= ItemManipulationModel_RefreshItemsOpacityInvoked; + } + + public ItemManipulationModel ItemManipulationModel { get; private set; } + + private void JumpTimer_Tick(object sender, object e) + { + jumpString = string.Empty; + jumpTimer.Stop(); + } + + protected abstract void InitializeCommandsViewModel(); + + protected IEnumerable? GetAllItems() + { + var items = CollectionViewSource.IsSourceGrouped ? // add all items from each group to the new list + (CollectionViewSource.Source as BulkConcurrentObservableCollection>)?.SelectMany(g => g) : + CollectionViewSource.Source as IEnumerable; + return items ?? new List(); + } + + public virtual void ResetItemOpacity() + { + foreach (var item in GetAllItems()) + { + if (item != null) + item.Opacity = item.IsHiddenItem ? Constants.UI.DimItemOpacity : 1.0d; + } + } + + protected ListedItem? GetItemFromElement(object element) + { + var item = element as ContentControl; + if (item == null || !CanGetItemFromElement(element)) + return null; + + return (item.DataContext as ListedItem) ?? (item.Content as ListedItem) ?? (ItemsControl.ItemFromContainer(item) as ListedItem); + } + + protected abstract bool CanGetItemFromElement(object element); + + protected virtual void BaseFolderSettings_LayoutModeChangeRequested(object? sender, LayoutModeEventArgs e) + { + if (ParentShellPageInstance?.SlimContentPage != null) + { + var layoutType = FolderSettings!.GetLayoutType(ParentShellPageInstance.FilesystemViewModel.WorkingDirectory); + + if (layoutType != ParentShellPageInstance.CurrentPageType) + { + ParentShellPageInstance.NavigateWithArguments(layoutType, new NavigationArguments() + { + NavPathParam = navigationArguments!.NavPathParam, + IsSearchResultPage = navigationArguments.IsSearchResultPage, + SearchPathParam = navigationArguments.SearchPathParam, + SearchQuery = navigationArguments.SearchQuery, + SearchUnindexedItems = navigationArguments.SearchUnindexedItems, + IsLayoutSwitch = true, + AssociatedTabInstance = ParentShellPageInstance + }); + + // Remove old layout from back stack + ParentShellPageInstance.RemoveLastPageFromBackStack(); + } + ParentShellPageInstance.FilesystemViewModel.UpdateEmptyTextType(); + } + } + + public event PropertyChangedEventHandler? PropertyChanged; + + protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected override async void OnNavigatedTo(NavigationEventArgs eventArgs) + { + base.OnNavigatedTo(eventArgs); + // Add item jumping handler + this.CharacterReceived += Page_CharacterReceived; + navigationArguments = (NavigationArguments)eventArgs.Parameter; + ParentShellPageInstance = navigationArguments.AssociatedTabInstance; + InitializeCommandsViewModel(); + + IsItemSelected = false; + FolderSettings!.LayoutModeChangeRequested += BaseFolderSettings_LayoutModeChangeRequested; + FolderSettings.GroupOptionPreferenceUpdated += FolderSettings_GroupOptionPreferenceUpdated; + ParentShellPageInstance.FilesystemViewModel.EmptyTextType = EmptyTextType.None; + ParentShellPageInstance.ToolbarViewModel.UpdateSortAndGroupOptions(); + + if (!navigationArguments.IsSearchResultPage) + { + ParentShellPageInstance.ToolbarViewModel.CanRefresh = true; + string previousDir = ParentShellPageInstance.FilesystemViewModel.WorkingDirectory; + await ParentShellPageInstance.FilesystemViewModel.SetWorkingDirectoryAsync(navigationArguments.NavPathParam); + + // pathRoot will be empty on recycle bin path + var workingDir = ParentShellPageInstance.FilesystemViewModel.WorkingDirectory ?? string.Empty; + string pathRoot = GetPathRoot(workingDir); + if (string.IsNullOrEmpty(pathRoot) || workingDir.StartsWith(CommonPaths.RecycleBinPath, StringComparison.Ordinal)) // Can't go up from recycle bin + ParentShellPageInstance.ToolbarViewModel.CanNavigateToParent = false; + else + ParentShellPageInstance.ToolbarViewModel.CanNavigateToParent = true; + + ParentShellPageInstance.InstanceViewModel.IsPageTypeRecycleBin = workingDir.StartsWith(CommonPaths.RecycleBinPath, StringComparison.Ordinal); + ParentShellPageInstance.InstanceViewModel.IsPageTypeMtpDevice = workingDir.StartsWith("\\\\?\\", StringComparison.Ordinal); + ParentShellPageInstance.InstanceViewModel.IsPageTypeFtp = FtpHelpers.IsFtpPath(workingDir); + ParentShellPageInstance.InstanceViewModel.IsPageTypeZipFolder = ZipStorageFolder.IsZipPath(workingDir); + ParentShellPageInstance.InstanceViewModel.IsPageTypeLibrary = LibraryHelper.IsLibraryPath(workingDir); + ParentShellPageInstance.InstanceViewModel.IsPageTypeSearchResults = false; + ParentShellPageInstance.ToolbarViewModel.PathControlDisplayText = navigationArguments.NavPathParam; + if (!navigationArguments.IsLayoutSwitch || previousDir != workingDir) + ParentShellPageInstance.FilesystemViewModel.RefreshItems(previousDir, SetSelectedItemsOnNavigation); + else + ParentShellPageInstance.ToolbarViewModel.CanGoForward = false; + } + else + { + ParentShellPageInstance.ToolbarViewModel.CanRefresh = true; + await ParentShellPageInstance.FilesystemViewModel.SetWorkingDirectoryAsync(navigationArguments.SearchPathParam); + + ParentShellPageInstance.ToolbarViewModel.CanGoForward = false; + ParentShellPageInstance.ToolbarViewModel.CanGoBack = true; // Impose no artificial restrictions on back navigation. Even in a search results page. + ParentShellPageInstance.ToolbarViewModel.CanNavigateToParent = false; + + var workingDir = ParentShellPageInstance.FilesystemViewModel.WorkingDirectory ?? string.Empty; + ParentShellPageInstance.InstanceViewModel.IsPageTypeRecycleBin = workingDir.StartsWith(CommonPaths.RecycleBinPath, StringComparison.Ordinal); + ParentShellPageInstance.InstanceViewModel.IsPageTypeMtpDevice = workingDir.StartsWith("\\\\?\\", StringComparison.Ordinal); + ParentShellPageInstance.InstanceViewModel.IsPageTypeFtp = FtpHelpers.IsFtpPath(workingDir); + ParentShellPageInstance.InstanceViewModel.IsPageTypeZipFolder = ZipStorageFolder.IsZipPath(workingDir); + ParentShellPageInstance.InstanceViewModel.IsPageTypeLibrary = LibraryHelper.IsLibraryPath(workingDir); + ParentShellPageInstance.InstanceViewModel.IsPageTypeSearchResults = true; + + if (!navigationArguments.IsLayoutSwitch) + { + var displayName = App.LibraryManager.TryGetLibrary(navigationArguments.SearchPathParam, out var lib) ? lib.Text : navigationArguments.SearchPathParam; + ParentShellPageInstance.UpdatePathUIToWorkingDirectory(null, string.Format("SearchPagePathBoxOverrideText".GetLocalizedResource(), navigationArguments.SearchQuery, displayName)); + var searchInstance = new Filesystem.Search.FolderSearch + { + Query = navigationArguments.SearchQuery, + Folder = navigationArguments.SearchPathParam, + ThumbnailSize = InstanceViewModel!.FolderSettings.GetIconSize(), + SearchUnindexedItems = navigationArguments.SearchUnindexedItems + }; + _ = ParentShellPageInstance.FilesystemViewModel.SearchAsync(searchInstance); + } + } + + ParentShellPageInstance.InstanceViewModel.IsPageTypeNotHome = true; // show controls that were hidden on the home page + ParentShellPageInstance.FilesystemViewModel.UpdateGroupOptions(); + UpdateCollectionViewSource(); + FolderSettings.IsLayoutModeChanging = false; + + SetSelectedItemsOnNavigation(); + + ItemContextMenuFlyout.Opening += ItemContextFlyout_Opening; + BaseContextMenuFlyout.Opening += BaseContextFlyout_Opening; + } + + public void SetSelectedItemsOnNavigation() + { + try + { + if (navigationArguments != null && navigationArguments.SelectItems != null && navigationArguments.SelectItems.Any()) + { + List liItemsToSelect = new List(); + foreach (string item in navigationArguments.SelectItems) + liItemsToSelect.Add(ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders.Where((li) => li.ItemNameRaw == item).First()); + + ItemManipulationModel.SetSelectedItems(liItemsToSelect); + ItemManipulationModel.FocusSelectedItems(); + } + else if (navigationArguments != null && navigationArguments.FocusOnNavigation) + { + ItemManipulationModel.FocusFileList(); // Set focus on layout specific file list control + } + } + catch (Exception) + { + } + } + + private CancellationTokenSource? groupingCancellationToken; + + private async void FolderSettings_GroupOptionPreferenceUpdated(object? sender, GroupOption e) + { + // Two or more of these running at the same time will cause a crash, so cancel the previous one before beginning + groupingCancellationToken?.Cancel(); + groupingCancellationToken = new CancellationTokenSource(); + var token = groupingCancellationToken.Token; + await ParentShellPageInstance!.FilesystemViewModel.GroupOptionsUpdated(token); + UpdateCollectionViewSource(); + await ParentShellPageInstance.FilesystemViewModel.ReloadItemGroupHeaderImagesAsync(); + } + + protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) + { + base.OnNavigatingFrom(e); + // Remove item jumping handler + this.CharacterReceived -= Page_CharacterReceived; + FolderSettings!.LayoutModeChangeRequested -= BaseFolderSettings_LayoutModeChangeRequested; + FolderSettings.GroupOptionPreferenceUpdated -= FolderSettings_GroupOptionPreferenceUpdated; + ItemContextMenuFlyout.Opening -= ItemContextFlyout_Opening; + BaseContextMenuFlyout.Opening -= BaseContextFlyout_Opening; + + var parameter = e.Parameter as NavigationArguments; + if (parameter is not null && !parameter.IsLayoutSwitch) + ParentShellPageInstance!.FilesystemViewModel.CancelLoadAndClearFiles(); + } + + public async void ItemContextFlyout_Opening(object? sender, object e) + { + try + { + if (!IsItemSelected) // Workaround for item sometimes not getting selected + { + if (((sender as CommandBarFlyout)?.Target as ListViewItem)?.Content is ListedItem li) + ItemManipulationModel.SetSelectedItem(li); + } + if (IsItemSelected) + await LoadMenuItemsAsync(); + } + catch (Exception error) + { + Debug.WriteLine(error); + } + } + + private CancellationTokenSource? shellContextMenuItemCancellationToken; + + public async void BaseContextFlyout_Opening(object? sender, object e) + { + try + { + if (BaseContextMenuFlyout.GetValue(ContextMenuExtensions.ItemsControlProperty) is ItemsControl itc) + itc.MaxHeight = Constants.UI.ContextMenuMaxHeight; // Reset menu max height + shellContextMenuItemCancellationToken?.Cancel(); + shellContextMenuItemCancellationToken = new CancellationTokenSource(); + var shiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down); + var items = ContextFlyoutItemHelper.GetBaseContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, itemViewModel: ParentShellPageInstance!.FilesystemViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, false); + BaseContextMenuFlyout.PrimaryCommands.Clear(); + BaseContextMenuFlyout.SecondaryCommands.Clear(); + var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); + primaryElements.OfType().ForEach(i => + { + i.Click += new RoutedEventHandler((s, e) => BaseContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) + }); + primaryElements.ForEach(i => BaseContextMenuFlyout.PrimaryCommands.Add(i)); + secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width + secondaryElements.ForEach(i => BaseContextMenuFlyout.SecondaryCommands.Add(i)); + + if (!InstanceViewModel!.IsPageTypeSearchResults && !InstanceViewModel.IsPageTypeZipFolder) + { + var shellMenuItems = await ContextFlyoutItemHelper.GetBaseContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); + if (shellMenuItems.Any()) + AddShellItemsToMenu(shellMenuItems, BaseContextMenuFlyout, shiftPressed); + } + } + catch (Exception error) + { + Debug.WriteLine(error); + } + } + + public void UpdateSelectionSize() + { + var items = (selectedItems?.Any() ?? false) ? selectedItems : GetAllItems(); + if (items is null) + return; + bool isSizeKnown = !items.Any(item => string.IsNullOrEmpty(item.FileSize)); + if (isSizeKnown) + { + long size = items.Sum(item => item.FileSizeBytes); + SelectedItemsPropertiesViewModel.ItemSizeBytes = size; + SelectedItemsPropertiesViewModel.ItemSize = size.ToSizeString(); + } + else + { + SelectedItemsPropertiesViewModel.ItemSizeBytes = 0; + SelectedItemsPropertiesViewModel.ItemSize = string.Empty; + } + SelectedItemsPropertiesViewModel.ItemSizeVisibility = isSizeKnown; + } + + private async Task LoadMenuItemsAsync() + { + if (ItemContextMenuFlyout.GetValue(ContextMenuExtensions.ItemsControlProperty) is ItemsControl itc) + itc.MaxHeight = Constants.UI.ContextMenuMaxHeight; // Reset menu max height + shellContextMenuItemCancellationToken?.Cancel(); + shellContextMenuItemCancellationToken = new CancellationTokenSource(); + SelectedItemsPropertiesViewModel.CheckAllFileExtensions(this.SelectedItems!.Select(selectedItem => selectedItem?.FileExtension).ToList()!); + var shiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down); + var items = ContextFlyoutItemHelper.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, workingDir: ParentShellPageInstance!.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, selectedItemsPropertiesViewModel: SelectedItemsPropertiesViewModel, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, showOpenMenu: false); + ItemContextMenuFlyout.PrimaryCommands.Clear(); + ItemContextMenuFlyout.SecondaryCommands.Clear(); + var (primaryElements, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(items); + primaryElements.OfType().ForEach(i => + { + i.Click += new RoutedEventHandler((s, e) => ItemContextMenuFlyout.Hide()); // Workaround for WinUI (#5508) + }); + primaryElements.ForEach(i => ItemContextMenuFlyout.PrimaryCommands.Add(i)); + secondaryElements.OfType().ForEach(i => i.MinWidth = Constants.UI.ContextMenuItemsMaxWidth); // Set menu min width + secondaryElements.ForEach(i => ItemContextMenuFlyout.SecondaryCommands.Add(i)); + + if (InstanceViewModel!.CanTagFilesInPage) + AddNewFileTagsToMenu(ItemContextMenuFlyout); + + if (!InstanceViewModel.IsPageTypeZipFolder) + { + var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(currentInstanceViewModel: InstanceViewModel, workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: SelectedItems!, shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); + if (shellMenuItems.Any()) + AddShellItemsToMenu(shellMenuItems, ItemContextMenuFlyout, shiftPressed); + } + } + + private void AddNewFileTagsToMenu(CommandBarFlyout contextMenu) + { + var fileTagsContextMenu = new FileTagsContextMenu(SelectedItems!); + var overflowSeparator = contextMenu.SecondaryCommands.FirstOrDefault(x => x is FrameworkElement fe && fe.Tag as string == "OverflowSeparator") as AppBarSeparator; + var index = contextMenu.SecondaryCommands.IndexOf(overflowSeparator); + index = index >= 0 ? index : contextMenu.SecondaryCommands.Count; + contextMenu.SecondaryCommands.Insert(index, new AppBarSeparator()); + contextMenu.SecondaryCommands.Insert(index + 1, new AppBarButton() + { + Label = "SettingsEditFileTagsExpander/Title".GetLocalizedResource(), + Icon = new FontIcon() { Glyph = "\uE1CB" }, + Flyout = fileTagsContextMenu + }); + } + + private void AddShellItemsToMenu(List shellMenuItems, Microsoft.UI.Xaml.Controls.CommandBarFlyout contextMenuFlyout, bool shiftPressed) + { + var openWithSubItems = ItemModelListToContextFlyoutHelper.GetMenuFlyoutItemsFromModel(ShellContextmenuHelper.GetOpenWithItems(shellMenuItems)); + var mainShellMenuItems = shellMenuItems.RemoveFrom(!UserSettingsService.AppearanceSettingsService.MoveOverflowMenuItemsToSubMenu ? int.MaxValue : shiftPressed ? 6 : 4); + var overflowShellMenuItems = shellMenuItems.Except(mainShellMenuItems).ToList(); + + var overflowItems = ItemModelListToContextFlyoutHelper.GetMenuFlyoutItemsFromModel(overflowShellMenuItems); + var mainItems = ItemModelListToContextFlyoutHelper.GetAppBarButtonsFromModelIgnorePrimary(mainShellMenuItems); + + var openedPopups = Microsoft.UI.Xaml.Media.VisualTreeHelper.GetOpenPopups(App.Window); + var secondaryMenu = openedPopups.FirstOrDefault(popup => popup.Name == "OverflowPopup"); + var itemsControl = secondaryMenu?.Child.FindDescendant(); + if (itemsControl is not null && secondaryMenu is not null) + { + contextMenuFlyout.SetValue(ContextMenuExtensions.ItemsControlProperty, itemsControl); + + var ttv = secondaryMenu.TransformToVisual(App.Window.Content); + var cMenuPos = ttv.TransformPoint(new Point(0, 0)); + var requiredHeight = contextMenuFlyout.SecondaryCommands.Concat(mainItems).Where(x => x is not AppBarSeparator).Count() * Constants.UI.ContextMenuSecondaryItemsHeight; + var availableHeight = App.Window.Bounds.Height - cMenuPos.Y - Constants.UI.ContextMenuPrimaryItemsHeight; + if (requiredHeight > availableHeight) + itemsControl.MaxHeight = Math.Min(Constants.UI.ContextMenuMaxHeight, Math.Max(itemsControl.ActualHeight, Math.Min(availableHeight, requiredHeight))); // Set menu max height to current height (avoids menu repositioning) + + mainItems.OfType().ForEach(x => x.MaxWidth = itemsControl.ActualWidth - Constants.UI.ContextMenuLabelMargin); // Set items max width to current menu width (#5555) + } + + var overflowItem = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton appBarButton && (appBarButton.Tag as string) == "ItemOverflow") as AppBarButton; + if (overflowItem is not null) + { + var overflowItemFlyout = overflowItem.Flyout as MenuFlyout; + if (overflowItemFlyout is not null) + { + if (overflowItemFlyout.Items.Count > 0) + overflowItemFlyout.Items.Insert(0, new MenuFlyoutSeparator()); + + var index = contextMenuFlyout.SecondaryCommands.Count - 2; + foreach (var i in mainItems) + { + index++; + contextMenuFlyout.SecondaryCommands.Insert(index, i); + } + + index = 0; + foreach (var i in overflowItems) + { + overflowItemFlyout.Items.Insert(index, i); + index++; + } + + if (overflowItemFlyout.Items.Count > 0) + { + (contextMenuFlyout.SecondaryCommands.First(x => x is FrameworkElement fe && fe.Tag as string == "OverflowSeparator") as AppBarSeparator)!.Visibility = Visibility.Visible; + overflowItem.Visibility = Visibility.Visible; + } + } + } + else + { + mainItems.ForEach(x => contextMenuFlyout.SecondaryCommands.Add(x)); + } + + // add items to openwith dropdown + var openWithOverflow = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWithOverflow") as AppBarButton; + var openWith = contextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton abb && (abb.Tag as string) == "OpenWith") as AppBarButton; + if (openWithSubItems is not null && openWithOverflow is not null && openWith is not null) + { + var flyout = (MenuFlyout)openWithOverflow.Flyout; + + flyout.Items.Clear(); + + foreach (var item in openWithSubItems) + { + flyout.Items.Add(item); + } + + openWithOverflow.Flyout = flyout; + openWith.Visibility = Visibility.Collapsed; + openWithOverflow.Visibility = Visibility.Visible; + } + + if (itemsControl is not null) + { + itemsControl.Items.OfType().ForEach(item => + { + if (item.FindDescendant("OverflowTextLabel") is TextBlock label) // Enable CharacterEllipsis text trimming for menu items + label.TextTrimming = TextTrimming.CharacterEllipsis; + if ((item as AppBarButton)?.Flyout as MenuFlyout is MenuFlyout flyout) // Close main menu when clicking on subitems (#5508) + { + Action> clickAction = null!; + clickAction = (items) => + { + items.OfType().ForEach(i => + { + i.Click += new RoutedEventHandler((s, e) => contextMenuFlyout.Hide()); + }); + items.OfType().ForEach(i => + { + clickAction(i.Items); + }); + }; + clickAction(flyout.Items); + } + }); + } + } + + protected virtual void Page_CharacterReceived(UIElement sender, CharacterReceivedRoutedEventArgs args) + { + if (ParentShellPageInstance!.IsCurrentInstance) + { + char letter = args.Character; + JumpString += letter.ToString().ToLowerInvariant(); + } + } + + protected void FileList_DragItemsStarting(object sender, DragItemsStartingEventArgs e) + { + e.Items.OfType().ForEach(item => SelectedItems!.Add(item)); + + try + { + // Only support IStorageItem capable paths + var itemList = e.Items.OfType().Where(x => !(x.IsHiddenItem && x.IsLinkItem && x.IsRecycleBinItem && x.IsShortcutItem)).Select(x => VirtualStorageItem.FromListedItem(x)); + e.Data.SetStorageItems(itemList, false); + } + catch (Exception) + { + e.Cancel = true; + } + } + + private ListedItem? dragOverItem = null; + + private void Item_DragLeave(object sender, DragEventArgs e) + { + var item = GetItemFromElement(sender); + if (item == dragOverItem) + dragOverItem = null; // Reset dragged over item + } + + protected async void Item_DragOver(object sender, DragEventArgs e) + { + var item = GetItemFromElement(sender); + if (item is null) + return; + + DragOperationDeferral? deferral = null; + try + { + deferral = e.GetDeferral(); + + ItemManipulationModel.SetSelectedItem(item); + + if (dragOverItem != item) + { + dragOverItem = item; + dragOverTimer.Stop(); + dragOverTimer.Debounce(() => + { + if (dragOverItem != null && !dragOverItem.IsExecutable) + { + dragOverItem = null; + dragOverTimer.Stop(); + NavigationHelpers.OpenSelectedItems(ParentShellPageInstance!, false); + } + }, TimeSpan.FromMilliseconds(1000), false); + } + + if (FilesystemHelpers.HasDraggedStorageItems(e.DataView)) + { + e.Handled = true; + + var handledByFtp = await FilesystemHelpers.CheckDragNeedsFulltrust(e.DataView); + var draggedItems = await FilesystemHelpers.GetDraggedStorageItems(e.DataView); + + if (draggedItems.Any(draggedItem => draggedItem.Path == item.ItemPath)) + { + e.AcceptedOperation = DataPackageOperation.None; + } + else if (handledByFtp) + { + e.DragUIOverride.IsCaptionVisible = true; + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), item.ItemName); + e.AcceptedOperation = DataPackageOperation.Copy; + } + else if (!draggedItems.Any()) + { + e.AcceptedOperation = DataPackageOperation.None; + } + else + { + e.DragUIOverride.IsCaptionVisible = true; + if (item.IsExecutable) + { + e.DragUIOverride.Caption = $"{"OpenItemsWithCaptionText".GetLocalizedResource()} {item.ItemName}"; + e.AcceptedOperation = DataPackageOperation.Link; + } // Items from the same drive as this folder are dragged into this folder, so we move the items instead of copy + else if (e.Modifiers.HasFlag(DragDropModifiers.Alt) || e.Modifiers.HasFlag(DragDropModifiers.Control | DragDropModifiers.Shift)) + { + e.DragUIOverride.Caption = string.Format("LinkToFolderCaptionText".GetLocalizedResource(), item.ItemName); + e.AcceptedOperation = DataPackageOperation.Link; + } + else if (e.Modifiers.HasFlag(DragDropModifiers.Control)) + { + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), item.ItemName); + e.AcceptedOperation = DataPackageOperation.Copy; + } + else if (e.Modifiers.HasFlag(DragDropModifiers.Shift)) + { + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), item.ItemName); + e.AcceptedOperation = DataPackageOperation.Move; + } + else if (draggedItems.Any(x => x.Item is ZipStorageFile || x.Item is ZipStorageFolder) + || ZipStorageFolder.IsZipPath(item.ItemPath)) + { + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), item.ItemName); + e.AcceptedOperation = DataPackageOperation.Copy; + } + else if (draggedItems.AreItemsInSameDrive(item.ItemPath)) + { + e.DragUIOverride.Caption = string.Format("MoveToFolderCaptionText".GetLocalizedResource(), item.ItemName); + e.AcceptedOperation = DataPackageOperation.Move; + } + else + { + e.DragUIOverride.Caption = string.Format("CopyToFolderCaptionText".GetLocalizedResource(), item.ItemName); + e.AcceptedOperation = DataPackageOperation.Copy; + } + } + } + } + finally + { + deferral?.Complete(); + } + } + + protected async void Item_Drop(object sender, DragEventArgs e) + { + var deferral = e.GetDeferral(); + + e.Handled = true; + dragOverItem = null; // Reset dragged over item + + var item = GetItemFromElement(sender); + if (item != null) + await ParentShellPageInstance!.FilesystemHelpers.PerformOperationTypeAsync(e.AcceptedOperation, e.DataView, (item as ShortcutItem)?.TargetPath ?? item.ItemPath, false, true, item.IsExecutable); + deferral.Complete(); + } + + protected void FileList_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args) + { + RefreshContainer(args.ItemContainer, args.InRecycleQueue); + RefreshItem(args.ItemContainer, args.Item, args.InRecycleQueue, args); + } + + private void RefreshContainer(SelectorItem container, bool inRecycleQueue) + { + container.PointerPressed -= FileListItem_PointerPressed; + if (inRecycleQueue) + UninitializeDrag(container); + else + container.PointerPressed += FileListItem_PointerPressed; + } + + private void RefreshItem(SelectorItem container, object item, bool inRecycleQueue, ContainerContentChangingEventArgs args) + { + if (item is not ListedItem listedItem) + return; + + if (inRecycleQueue) + { + ParentShellPageInstance!.FilesystemViewModel.CancelExtendedPropertiesLoadingForItem(listedItem); + } + else + { + InitializeDrag(container, listedItem); + + if (!listedItem.ItemPropertiesInitialized) + { + uint callbackPhase = 3; + args.RegisterUpdateCallback(callbackPhase, async (s, c) => + { + await ParentShellPageInstance!.FilesystemViewModel.LoadExtendedItemProperties(listedItem, IconSize); + }); + } + } + } + + protected static void FileListItem_PointerPressed(object sender, PointerRoutedEventArgs e) + { + if (sender is not SelectorItem selectorItem) + return; + + if (selectorItem.IsSelected && e.KeyModifiers == VirtualKeyModifiers.Control) + { + selectorItem.IsSelected = false; + // Prevent issues arising caused by the default handlers attempting to select the item that has just been deselected by ctrl + click + e.Handled = true; + } + else if (!selectorItem.IsSelected && e.GetCurrentPoint(selectorItem).Properties.IsLeftButtonPressed) + { + selectorItem.IsSelected = true; + } + } + + private ListedItem? hoveredItem = null; + + protected internal void ListedItem_PointerEntered(object sender, PointerRoutedEventArgs e) + { + if (!UserSettingsService.PreferencesSettingsService.SelectFilesOnHover) + return; + + var hovered = (sender as Grid)?.DataContext as ListedItem; + if (hovered != hoveredItem) + { + hoveredItem = hovered; + hoverTimer.Stop(); + hoverTimer.Debounce(() => + { + if (hoveredItem is not null) + { + hoverTimer.Stop(); + if (e.KeyModifiers == VirtualKeyModifiers.Control && + selectedItems is not null) + { + ItemManipulationModel.AddSelectedItem(hoveredItem); + } + else if (e.KeyModifiers == VirtualKeyModifiers.Shift && + selectedItems is not null && + selectedItems.Any()) + { + var last = selectedItems.Last(); + byte found = 0; + for (int i = 0; i < ItemsControl.Items.Count && found != 2; i++) + { + if (ItemsControl.Items[i] == last || ItemsControl.Items[i] == hoveredItem) + found++; + if (found != 0 && !selectedItems.Contains(ItemsControl.Items[i])) + ItemManipulationModel.AddSelectedItem((ListedItem)ItemsControl.Items[i]); + } + } + else + { + ItemManipulationModel.SetSelectedItem(hoveredItem); + } + hoveredItem = null; + } + }, TimeSpan.FromMilliseconds(600), false); + } + } + + protected internal void ListedItem_PointerExited(object sender, PointerRoutedEventArgs e) + { + if (!UserSettingsService.PreferencesSettingsService.SelectFilesOnHover) + return; + hoverTimer.Stop(); + hoveredItem = null; + } + + private readonly RecycleBinHelpers recycleBinHelpers = new(); + + protected void InitializeDrag(UIElement containter, ListedItem item) + { + if (item is null) + return; + + UninitializeDrag(containter); + if ((item.PrimaryItemAttribute == StorageItemTypes.Folder && !recycleBinHelpers.IsPathUnderRecycleBin(item.ItemPath)) || item.IsExecutable) + { + containter.AllowDrop = true; + containter.DragOver += Item_DragOver; + containter.DragLeave += Item_DragLeave; + containter.Drop += Item_Drop; + } + } + + protected void UninitializeDrag(UIElement element) + { + element.AllowDrop = false; + element.DragOver -= Item_DragOver; + element.DragLeave -= Item_DragLeave; + element.Drop -= Item_Drop; + } + + // VirtualKey doesn't support / accept plus and minus by default. + public readonly VirtualKey PlusKey = (VirtualKey)187; + + public readonly VirtualKey MinusKey = (VirtualKey)189; + + public virtual void Dispose() + { + PaneViewModel?.Dispose(); + UnhookBaseEvents(); + } + + protected void ItemsLayout_DragOver(object sender, DragEventArgs e) + { + CommandsViewModel?.DragOverCommand?.Execute(e); + } + + protected void ItemsLayout_Drop(object sender, DragEventArgs e) + { + CommandsViewModel?.DropCommand?.Execute(e); + } + + private void UpdateCollectionViewSource() + { + if (ParentShellPageInstance is null) + return; + if (ParentShellPageInstance.FilesystemViewModel.FilesAndFolders.IsGrouped) + { + CollectionViewSource = new CollectionViewSource() + { + IsSourceGrouped = true, + Source = ParentShellPageInstance.FilesystemViewModel.FilesAndFolders.GroupedCollection + }; + } + else + { + CollectionViewSource = new CollectionViewSource() + { + IsSourceGrouped = false, + Source = ParentShellPageInstance.FilesystemViewModel.FilesAndFolders + }; + } + } + + protected void SemanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e) + { + if (!e.IsSourceZoomedInView) + { + // According to the docs this isn't necessary, but it would crash otherwise + var destination = e.DestinationItem.Item as GroupedCollection; + e.DestinationItem.Item = destination?.FirstOrDefault(); + } + } + + protected void StackPanel_PointerEntered(object sender, PointerRoutedEventArgs e) + { + var element = (sender as UIElement)?.FindAscendant(); + if (element is not null) + VisualStateManager.GoToState(element, "PointerOver", true); + } + + protected void StackPanel_PointerCanceled(object sender, PointerRoutedEventArgs e) + { + var element = (sender as UIElement)?.FindAscendant(); + if (element is not null) + VisualStateManager.GoToState(element, "Normal", true); + } + + protected void RootPanel_PointerPressed(object sender, PointerRoutedEventArgs e) + { + var element = (sender as UIElement)?.FindAscendant(); + if (element is not null) + VisualStateManager.GoToState(element, "Pressed", true); + } + + private void ItemManipulationModel_RefreshItemsOpacityInvoked(object? sender, EventArgs e) + { + foreach (ListedItem listedItem in GetAllItems()) + { + if (listedItem.IsHiddenItem) + listedItem.Opacity = Constants.UI.DimItemOpacity; + else + listedItem.Opacity = 1; + } + } + + private void View_VectorChanged(IObservableVector sender, IVectorChangedEventArgs @event) + { + if (ParentShellPageInstance is not null) + ParentShellPageInstance.ToolbarViewModel.HasItem = CollectionViewSource.View.Any(); + } + + virtual public void StartRenameItem() { } + + private ListedItem? preRenamingItem = null; + + public void CheckRenameDoubleClick(object clickedItem) + { + if (clickedItem is ListedItem item) + { + if (item == preRenamingItem) + { + tapDebounceTimer.Debounce(() => + { + if (item == preRenamingItem) + { + StartRenameItem(); + tapDebounceTimer.Stop(); + } + }, TimeSpan.FromMilliseconds(500)); + } + else + { + tapDebounceTimer.Stop(); + preRenamingItem = item; + } + } + else + { + ResetRenameDoubleClick(); + } + } + + public void ResetRenameDoubleClick() + { + preRenamingItem = null; + tapDebounceTimer.Stop(); + } + + protected async void ValidateItemNameInputText(TextBox textBox, TextBoxBeforeTextChangingEventArgs args, Action showError) + { + if (FilesystemHelpers.ContainsRestrictedCharacters(args.NewText)) + { + args.Cancel = true; + await DispatcherQueue.EnqueueAsync(() => + { + var oldSelection = textBox.SelectionStart + textBox.SelectionLength; + var oldText = textBox.Text; + textBox.Text = FilesystemHelpers.FilterRestrictedCharacters(args.NewText); + textBox.SelectionStart = oldSelection + textBox.Text.Length - oldText.Length; + showError?.Invoke(true); + }); + } + else + { + showError?.Invoke(false); + } + } + } + + public class ContextMenuExtensions : DependencyObject + { + public static ItemsControl GetItemsControl(DependencyObject obj) + { + return (ItemsControl)obj.GetValue(ItemsControlProperty); + } + + public static void SetItemsControl(DependencyObject obj, ItemsControl value) + { + obj.SetValue(ItemsControlProperty, value); + } + + public static readonly DependencyProperty ItemsControlProperty = + DependencyProperty.RegisterAttached("ItemsControl", typeof(ItemsControl), typeof(ContextMenuExtensions), new PropertyMetadata(null)); + } } diff --git a/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml b/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml index f7527e8c07bf..121a99d2dc01 100644 --- a/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml +++ b/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml @@ -210,7 +210,7 @@ MaxHeight="68" Margin="8,4" Padding="0" - HorizontalAlignment="Left" + HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent" ColumnSpacing="4" From df4132143df5de157dc6c0c62afc4e03eb5596cb Mon Sep 17 00:00:00 2001 From: ferrariofilippo Date: Fri, 16 Sep 2022 16:37:07 +0200 Subject: [PATCH 13/15] Tiles Entered Area Fixed --- src/Files.App/Views/LayoutModes/GridViewBrowser.xaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml b/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml index 121a99d2dc01..262b98194bea 100644 --- a/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml +++ b/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml @@ -208,8 +208,7 @@ Date: Fri, 16 Sep 2022 20:54:29 +0200 Subject: [PATCH 14/15] Fix hover on empty space --- src/Files.App/BaseLayout.cs | 12 ++++++++++-- src/Files.App/Views/LayoutModes/ColumnViewBase.xaml | 2 -- .../Views/LayoutModes/DetailsLayoutBrowser.xaml | 2 -- src/Files.App/Views/LayoutModes/GridViewBrowser.xaml | 9 +++------ 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Files.App/BaseLayout.cs b/src/Files.App/BaseLayout.cs index b9de56c17ba7..a6119e00fd61 100644 --- a/src/Files.App/BaseLayout.cs +++ b/src/Files.App/BaseLayout.cs @@ -897,11 +897,19 @@ protected void FileList_ContainerContentChanging(ListViewBase sender, ContainerC private void RefreshContainer(SelectorItem container, bool inRecycleQueue) { + container.PointerEntered -= ListedItem_PointerEntered; + container.PointerExited -= ListedItem_PointerExited; container.PointerPressed -= FileListItem_PointerPressed; if (inRecycleQueue) - UninitializeDrag(container); + { + UninitializeDrag(container); + } else + { + container.PointerEntered += ListedItem_PointerEntered; + container.PointerExited += ListedItem_PointerExited; container.PointerPressed += FileListItem_PointerPressed; + } } private void RefreshItem(SelectorItem container, object item, bool inRecycleQueue, ContainerContentChangingEventArgs args) @@ -952,7 +960,7 @@ protected internal void ListedItem_PointerEntered(object sender, PointerRoutedEv if (!UserSettingsService.PreferencesSettingsService.SelectFilesOnHover) return; - var hovered = (sender as Grid)?.DataContext as ListedItem; + var hovered = GetItemFromElement(sender); if (hovered != hoveredItem) { hoveredItem = hovered; diff --git a/src/Files.App/Views/LayoutModes/ColumnViewBase.xaml b/src/Files.App/Views/LayoutModes/ColumnViewBase.xaml index 19d540adb6c8..90014ee48a39 100644 --- a/src/Files.App/Views/LayoutModes/ColumnViewBase.xaml +++ b/src/Files.App/Views/LayoutModes/ColumnViewBase.xaml @@ -192,8 +192,6 @@ CornerRadius="{StaticResource ControlCornerRadius}" IsRightTapEnabled="True" Loaded="Grid_Loaded" - PointerEntered="ListedItem_PointerEntered" - PointerExited="ListedItem_PointerExited" RightTapped="StackPanel_RightTapped" ToolTipService.ToolTip="{x:Bind ItemName, Mode=OneWay}"> diff --git a/src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml b/src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml index 8baa499edf49..9c14ba460de1 100644 --- a/src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml +++ b/src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml @@ -507,8 +507,6 @@ AutomationProperties.Name="{x:Bind ItemName, Mode=OneWay}" IsRightTapEnabled="True" Loaded="Grid_Loaded" - PointerEntered="ListedItem_PointerEntered" - PointerExited="ListedItem_PointerExited" RightTapped="StackPanel_RightTapped" ToolTipService.ToolTip="{x:Bind ItemTooltipText, Mode=OneWay}"> diff --git a/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml b/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml index 262b98194bea..72fc56a34322 100644 --- a/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml +++ b/src/Files.App/Views/LayoutModes/GridViewBrowser.xaml @@ -62,8 +62,6 @@ Background="Transparent" IsRightTapEnabled="True" Loaded="Grid_Loaded" - PointerEntered="ListedItem_PointerEntered" - PointerExited="ListedItem_PointerExited" RightTapped="StackPanel_RightTapped" ToolTipService.ToolTip="{x:Bind ItemTooltipText, Mode=OneWay}"> @@ -208,15 +206,14 @@ From 6e747399f0d6393c3ec2fe50fc0c5e9e400890cc Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Fri, 16 Sep 2022 21:15:05 +0200 Subject: [PATCH 15/15] Only subscribe if option is on. Rename to match PointerPressed --- src/Files.App/BaseLayout.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Files.App/BaseLayout.cs b/src/Files.App/BaseLayout.cs index 89e9a046fba1..459010536eb4 100644 --- a/src/Files.App/BaseLayout.cs +++ b/src/Files.App/BaseLayout.cs @@ -897,18 +897,22 @@ protected void FileList_ContainerContentChanging(ListViewBase sender, ContainerC private void RefreshContainer(SelectorItem container, bool inRecycleQueue) { - container.PointerEntered -= ListedItem_PointerEntered; - container.PointerExited -= ListedItem_PointerExited; container.PointerPressed -= FileListItem_PointerPressed; + container.PointerEntered -= FileListItem_PointerEntered; + container.PointerExited -= FileListItem_PointerExited; + if (inRecycleQueue) { UninitializeDrag(container); } else { - container.PointerEntered += ListedItem_PointerEntered; - container.PointerExited += ListedItem_PointerExited; container.PointerPressed += FileListItem_PointerPressed; + if (UserSettingsService.PreferencesSettingsService.SelectFilesOnHover) + { + container.PointerEntered += FileListItem_PointerEntered; + container.PointerExited += FileListItem_PointerExited; + } } } @@ -955,7 +959,7 @@ protected static void FileListItem_PointerPressed(object sender, PointerRoutedEv private ListedItem? hoveredItem = null; - protected internal void ListedItem_PointerEntered(object sender, PointerRoutedEventArgs e) + protected internal void FileListItem_PointerEntered(object sender, PointerRoutedEventArgs e) { if (!UserSettingsService.PreferencesSettingsService.SelectFilesOnHover) return; @@ -999,10 +1003,11 @@ selectedItems is not null && } } - protected internal void ListedItem_PointerExited(object sender, PointerRoutedEventArgs e) + protected internal void FileListItem_PointerExited(object sender, PointerRoutedEventArgs e) { if (!UserSettingsService.PreferencesSettingsService.SelectFilesOnHover) return; + hoverTimer.Stop(); hoveredItem = null; }