Skip to content

Feature: Add shell context menu for widgets #11204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Feb 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/Files.App/Filesystem/RecentItem.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Files.App.Filesystem.StorageItems;
using Files.App.Helpers;
using Files.App.UserControls.Widgets;
using Files.Shared;
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.FileProperties;

namespace Files.App.Filesystem
{
public class RecentItem : ObservableObject, IEquatable<RecentItem>
public class RecentItem : WidgetCardItem, IEquatable<RecentItem>
{
private BitmapImage _fileImg;
public BitmapImage FileImg
Expand All @@ -29,6 +29,7 @@ public BitmapImage FileImg
public bool IsFile { get => Type == StorageItemTypes.File; }
public DateTime LastModified { get; set; }
public byte[] PIDL { get; set; }
public string Path { get => RecentPath; }

public RecentItem()
{
Expand Down Expand Up @@ -118,8 +119,8 @@ public bool Equals(RecentItem other)
*/
private static string NameOrPathWithoutExtension(string nameOrPath)
{
string strippedExtension = Path.GetFileNameWithoutExtension(nameOrPath);
return string.IsNullOrEmpty(strippedExtension) ? Path.GetFileName(nameOrPath) : strippedExtension;
string strippedExtension = System.IO.Path.GetFileNameWithoutExtension(nameOrPath);
return string.IsNullOrEmpty(strippedExtension) ? System.IO.Path.GetFileName(nameOrPath) : strippedExtension;
}
}
}
82 changes: 82 additions & 0 deletions src/Files.App/Helpers/ShellContextMenuHelper.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.WinUI.UI;
using Files.App.Extensions;
using Files.App.Filesystem;
using Files.App.Helpers.ContextFlyouts;
using Files.App.ServicesImplementation.Settings;
using Files.App.Shell;
using Files.App.ViewModels;
using Files.Backend.Helpers;
using Files.Backend.Services.Settings;
using Files.Shared;
using Files.Shared.Extensions;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.Collections.Generic;
Expand All @@ -13,11 +22,14 @@
using System.Threading;
using System.Threading.Tasks;
using Vanara.PInvoke;
using Windows.System;
using Windows.UI.Core;

namespace Files.App.Helpers
{
public static class ShellContextmenuHelper
{
public static IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService<IUserSettingsService>();
public static async Task<List<ContextMenuFlyoutItemViewModel>> GetShellContextmenuAsync(bool showOpenMenu, bool shiftPressed, string workingDirectory, List<ListedItem>? selectedItems, CancellationToken cancellationToken)
{
bool IsItemSelected = selectedItems?.Count > 0;
Expand Down Expand Up @@ -210,5 +222,75 @@ void InstallFont(string path, bool asAdmin)
flyout.Remove(item);
return item?.Items;
}

public static async Task LoadShellMenuItems(string path, CommandBarFlyout itemContextMenuFlyout, ContextMenuOptions options = null, bool showOpenWithMenu = false)
{
try
{
if (options is not null)
{
if (options.ShowEmptyRecycleBin)
{
var emptyRecycleBinItem = itemContextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton appBarButton && (appBarButton.Tag as string) == "EmptyRecycleBin") as AppBarButton;
if (emptyRecycleBinItem is not null)
{
var binHasItems = RecycleBinHelpers.RecycleBinHasItems();
emptyRecycleBinItem.IsEnabled = binHasItems;
}
}

if (!options.IsLocationItem)
return;
}

var shiftPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down);
var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(
workingDir: null,
new List<ListedItem>() { new ListedItem(null) { ItemPath = path } },
shiftPressed: shiftPressed,
showOpenMenu: false,
default);

if (showOpenWithMenu)
{
var openWithItem = shellMenuItems.Where(x => (x.Tag as Win32ContextMenuItem)?.ID == 100).ToList().FirstOrDefault();
var (_, openWithItems) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(new List<ContextMenuFlyoutItemViewModel>() { openWithItem });
itemContextMenuFlyout.SecondaryCommands.Insert(0, openWithItems.FirstOrDefault());
}

if (!UserSettingsService.AppearanceSettingsService.MoveShellExtensionsToSubMenu)
{
var (_, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(shellMenuItems);
if (!secondaryElements.Any())
return;

var openedPopups = Microsoft.UI.Xaml.Media.VisualTreeHelper.GetOpenPopups(App.Window);
var secondaryMenu = openedPopups.FirstOrDefault(popup => popup.Name == "OverflowPopup");

var itemsControl = secondaryMenu?.Child.FindDescendant<ItemsControl>();
if (itemsControl is not null)
{
var maxWidth = itemsControl.ActualWidth - Constants.UI.ContextMenuLabelMargin;
secondaryElements.OfType<FrameworkElement>()
.ForEach(x => x.MaxWidth = maxWidth); // Set items max width to current menu width (#5555)
}

itemContextMenuFlyout.SecondaryCommands.Add(new AppBarSeparator());
secondaryElements.ForEach(i => itemContextMenuFlyout.SecondaryCommands.Add(i));
}
else
{
var overflowItems = ItemModelListToContextFlyoutHelper.GetMenuFlyoutItemsFromModel(shellMenuItems);
if (itemContextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton appBarButton && (appBarButton.Tag as string) == "ItemOverflow") is not AppBarButton overflowItem)
return;

var flyoutItems = (overflowItem.Flyout as MenuFlyout)?.Items;
if (flyoutItems is not null)
overflowItems.ForEach(i => flyoutItems.Add(i));
overflowItem.Visibility = overflowItems.Any() ? Visibility.Visible : Visibility.Collapsed;
}
}
catch { }
}
}
}
7 changes: 7 additions & 0 deletions src/Files.App/ServicesImplementation/QuickAccessService.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Files.App.Shell;
using Files.App.UserControls.Widgets;
using Files.Sdk.Storage.LocatableStorage;
using Files.Shared;
using Files.Shared.Extensions;
using Microsoft.UI.Xaml.Shapes;
using System;
using System.Collections.Generic;
using System.IO;
Expand Down Expand Up @@ -52,5 +54,10 @@ await SafetyExtensions.IgnoreExceptions(async () => {

App.QuickAccessManager.UpdateQuickAccessWidget?.Invoke(this, new ModifyQuickAccessEventArgs(folderPaths, false));
}

public bool IsItemPinned(ILocatableFolder folder)
{
return App.QuickAccessManager.Model.FavoriteItems.Contains(folder.Path);
}
}
}
59 changes: 2 additions & 57 deletions src/Files.App/UserControls/SidebarControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ private void PaneRoot_RightTapped(object sender, RightTappedRoutedEventArgs e)
e.Handled = true;
}

private void NavigationViewItem_RightTapped(object sender, RightTappedRoutedEventArgs e)
private async void NavigationViewItem_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
var itemContextMenuFlyout = new CommandBarFlyout { Placement = FlyoutPlacementMode.Full };
if (sender is not NavigationViewItem sidebarItem ||
Expand All @@ -430,7 +430,7 @@ private void NavigationViewItem_RightTapped(object sender, RightTappedRoutedEven
itemContextMenuFlyout.ShowAt(sidebarItem, new FlyoutShowOptions { Position = e.GetPosition(sidebarItem) });

if (item.MenuOptions.ShowShellItems)
LoadShellMenuItems(itemContextMenuFlyout, item.MenuOptions);
await ShellContextmenuHelper.LoadShellMenuItems(rightClickedItem.Path, itemContextMenuFlyout, item.MenuOptions);

e.Handled = true;
}
Expand Down Expand Up @@ -936,61 +936,6 @@ private void ResizeElementBorder_ManipulationStarted(object sender, Manipulation
dragging = true;
}

private async void LoadShellMenuItems(CommandBarFlyout itemContextMenuFlyout, ContextMenuOptions options)
{
try
{
if (options.ShowEmptyRecycleBin)
{
var emptyRecycleBinItem = itemContextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton appBarButton && (appBarButton.Tag as string) == "EmptyRecycleBin") as AppBarButton;
if (emptyRecycleBinItem is not null)
{
var binHasItems = RecycleBinHelpers.RecycleBinHasItems();
emptyRecycleBinItem.IsEnabled = binHasItems;
}
}

if (!options.IsLocationItem)
return;

var shiftPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down);
var shellMenuItems = await ContextFlyoutItemHelper.GetItemContextShellCommandsAsync(workingDir: null,
new List<ListedItem>() { new ListedItem(null) { ItemPath = rightClickedItem.Path } }, shiftPressed: shiftPressed, showOpenMenu: false, default);
if (!userSettingsService.AppearanceSettingsService.MoveShellExtensionsToSubMenu)
{
var (_, secondaryElements) = ItemModelListToContextFlyoutHelper.GetAppBarItemsFromModel(shellMenuItems);
if (!secondaryElements.Any())
return;

var openedPopups = Microsoft.UI.Xaml.Media.VisualTreeHelper.GetOpenPopups(App.Window);
var secondaryMenu = openedPopups.FirstOrDefault(popup => popup.Name == "OverflowPopup");

var itemsControl = secondaryMenu?.Child.FindDescendant<ItemsControl>();
if (itemsControl is not null)
{
var maxWidth = itemsControl.ActualWidth - Constants.UI.ContextMenuLabelMargin;
secondaryElements.OfType<FrameworkElement>()
.ForEach(x => x.MaxWidth = maxWidth); // Set items max width to current menu width (#5555)
}

itemContextMenuFlyout.SecondaryCommands.Add(new AppBarSeparator());
secondaryElements.ForEach(i => itemContextMenuFlyout.SecondaryCommands.Add(i));
}
else
{
var overflowItems = ItemModelListToContextFlyoutHelper.GetMenuFlyoutItemsFromModel(shellMenuItems);
if (itemContextMenuFlyout.SecondaryCommands.FirstOrDefault(x => x is AppBarButton appBarButton && (appBarButton.Tag as string) == "ItemOverflow") is not AppBarButton overflowItem)
return;

var flyoutItems = (overflowItem.Flyout as MenuFlyout)?.Items;
if (flyoutItems is not null)
overflowItems.ForEach(i => flyoutItems.Add(i));
overflowItem.Visibility = overflowItems.Any() ? Visibility.Visible : Visibility.Collapsed;
}
}
catch { }
}

public static GridLength GetSidebarCompactSize()
{
return App.Current.Resources.TryGetValue("NavigationViewCompactPaneLength", out object paneLength) && paneLength is double paneLengthDouble
Expand Down
Loading