Skip to content

Rebuild and merge context menu code #4190

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 37 commits into from
Mar 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
d770399
Added viewmodel for context menu items
winston-de Mar 21, 2021
a8a92ce
Added test implementation for GridView browser
winston-de Mar 22, 2021
83f2c23
Merge with main
winston-de Mar 22, 2021
81809bb
- added support for generic file browser
winston-de Mar 23, 2021
a429f59
Fixed crash
winston-de Mar 23, 2021
e9f89cd
- Fixed duplication issue
winston-de Mar 23, 2021
c7b73e7
Added item filtering with function
winston-de Mar 23, 2021
c63a40c
Added icons
winston-de Mar 23, 2021
590bc53
Updated check
winston-de Mar 23, 2021
f5cf6e1
Merge remote-tracking branch 'upstream/main' into ContextMenus
winston-de Mar 25, 2021
ba17414
- Added most menu items over
winston-de Mar 25, 2021
6fbe085
Use single flyout
winston-de Mar 25, 2021
a41e13c
-Added SingleItemOnly
winston-de Mar 25, 2021
0d6d423
- Added menu flyout as context flyout
winston-de Mar 26, 2021
211e875
Added base layout context menu
winston-de Mar 26, 2021
a7d49e2
Fixed potential crashRe-implemented remaining menu items
winston-de Mar 27, 2021
a63a0d4
Removed old context menu code
winston-de Mar 27, 2021
442ab9a
Implemented quick look item
winston-de Mar 27, 2021
9d7f7a2
Fix string localization
winston-de Mar 27, 2021
053e83d
Merge from upstream/main
winston-de Mar 27, 2021
3a1fd2f
Revert removing ".Text" because it was causing a build error
winston-de Mar 28, 2021
52a3831
Manually update keys for resources
winston-de Mar 28, 2021
d1a7f22
Move shell context menu stuff to helper class
winston-de Mar 28, 2021
3dc1132
Remove context flyout view model, move entirley to static helpers for…
winston-de Mar 28, 2021
d8c5e51
Fixed issue where item menu wouldn't always be set
winston-de Mar 28, 2021
6e7d6eb
Move item to overflow if it's show on and shiftPressed is false
winston-de Mar 29, 2021
18d08aa
Codefactor
winston-de Mar 29, 2021
4bad473
Added and implemented refresh command
winston-de Mar 29, 2021
40d7331
Added ShellItemEntry param to new file command
winston-de Mar 29, 2021
0f54945
Made new file items code much more efficient
winston-de Mar 29, 2021
0284c4a
Minor bugfix
winston-de Mar 29, 2021
5e00678
Full fix for 4331
winston-de Mar 29, 2021
bc30262
Unsubscribe from event on nav from
winston-de Mar 29, 2021
574954c
Merge with upstream/main
winston-de Mar 30, 2021
2b220fc
Merge with upstream/main
winston-de Mar 30, 2021
1781e74
Added column view base context menu flyout
winston-de Mar 30, 2021
fc0595c
Added column view item context menus
winston-de Mar 30, 2021
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
450 changes: 41 additions & 409 deletions Files/BaseLayout.cs

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Files/Files.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,10 @@
<Compile Include="Dialogs\ConfirmDeleteDialog.xaml.cs">
<DependentUpon>ConfirmDeleteDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Helpers\ContextFlyoutItemHelper.cs" />
<Compile Include="Helpers\ItemModelListToContextFlyoutHelper.cs" />
<Compile Include="Helpers\FilePropertiesHelpers.cs" />
<Compile Include="Helpers\ShellContextMenuHelper.cs" />
<Compile Include="Helpers\UIFilesystemHelpers.cs" />
<Compile Include="Helpers\NavigationHelpers.cs" />
<Compile Include="Helpers\UIHelpers.cs" />
Expand Down Expand Up @@ -293,6 +296,7 @@
<Compile Include="ViewModels\Dialogs\DynamicDialogViewModel.cs" />
<Compile Include="ViewModels\FolderSettingsViewModel.cs" />
<Compile Include="ViewModels\MainPageViewModel.cs" />
<Compile Include="ViewModels\ContextMenuFlyoutItemViewModel.cs" />
<Compile Include="ViewModels\PreviewPaneViewModel.cs" />
<Compile Include="ViewModels\Previews\BasePreviewModel.cs" />
<Compile Include="ViewModels\Previews\BasicPreviewViewModel.cs" />
Expand Down
566 changes: 566 additions & 0 deletions Files/Helpers/ContextFlyoutItemHelper.cs

Large diffs are not rendered by default.

125 changes: 125 additions & 0 deletions Files/Helpers/ItemModelListToContextFlyoutHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using Files.UserControls;
using Files.ViewModels;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;

namespace Files.Helpers.ContextFlyouts
{
public static class ItemModelListToContextFlyoutHelper
{
public static List<MenuFlyoutItemBase> GetMenuFlyoutItemsFromModel(List<ContextMenuFlyoutItemViewModel> items)
{
var flyout = new List<MenuFlyoutItemBase>();
items.ForEach(i =>
{
flyout.Add(GetMenuItem(i));
});
return flyout;
}

private static MenuFlyoutItemBase GetMenuItem(ContextMenuFlyoutItemViewModel item)
{
switch (item.ItemType)
{
case ItemType.Separator:
return new MenuFlyoutSeparator();

default:
return GetMenuFlyoutItem(item);
}
}

private static MenuFlyoutItemBase GetMenuFlyoutItem(ContextMenuFlyoutItemViewModel item, bool isToggle = false)
{
if(item.Items?.Count > 0)
{
var flyoutSubItem = new MenuFlyoutSubItem()
{
Text = item.Text,
Tag = item.Tag,
};
item.Items.ForEach(i =>
{
flyoutSubItem.Items.Add(GetMenuItem(i));
});
return flyoutSubItem;
} else
{
return GetItem(item);
}
}

private static MenuFlyoutItemBase GetItem(ContextMenuFlyoutItemViewModel i)
{

if (i.BitmapIcon != null)
{
var item = new MenuFlyoutItemWithImage()
{
Text = i.Text,
Tag = i.Tag,
Command = i.Command,
CommandParameter = i.CommandParameter,
};
try
{
item.BitmapIcon = i.BitmapIcon;
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return item;
}
MenuFlyoutItemBase flyoutItem;

if(i.ItemType == ItemType.Toggle)
{
flyoutItem = new ToggleMenuFlyoutItem()
{
Text = i.Text,
Tag = i.Tag,
Command = i.Command,
CommandParameter = i.CommandParameter,
IsChecked = i.IsChecked,
};
} else
{
var icon = new FontIcon
{
Glyph = !string.IsNullOrEmpty(i.Glyph) ? i.Glyph : "",
};

if(!string.IsNullOrEmpty(i.GlyphFontFamilyName))
{
var fontFamily = App.Current.Resources[i.GlyphFontFamilyName] as FontFamily;
icon.FontFamily = fontFamily;
}

flyoutItem = new MenuFlyoutItem()
{
Text = i.Text,
Tag = i.Tag,
Command = i.Command,
CommandParameter = i.CommandParameter,
Icon = icon,
};
}

if(i.KeyboardAccelerator != null)
{
flyoutItem.KeyboardAccelerators.Add(i.KeyboardAccelerator);
}
flyoutItem.IsEnabled = i.IsEnabled;

return flyoutItem;
}
}
}
172 changes: 172 additions & 0 deletions Files/Helpers/ShellContextMenuHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
using Files.Common;
using Files.Filesystem;
using Files.ViewModels;
using Microsoft.Toolkit.Mvvm.Input;
using Microsoft.Toolkit.Uwp;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.AppService;
using Windows.Foundation.Collections;
using Windows.UI.Xaml.Media.Imaging;

namespace Files.Helpers {
public static class ShellContextmenuHelper {
public static List<ContextMenuFlyoutItemViewModel> SetShellContextmenu(List<ContextMenuFlyoutItemViewModel> baseItems, bool shiftPressed, bool showOpenMenu, NamedPipeAsAppServiceConnection connection, string workingDirectory, List<ListedItem> selectedItems)
{
bool IsItemSelected = selectedItems?.Count > 0;

var menuItemsList = new List<ContextMenuFlyoutItemViewModel>(baseItems);

var currentBaseLayoutItemCount = baseItems.Count;
var maxItems = !App.AppSettings.MoveOverflowMenuItemsToSubMenu ? int.MaxValue : shiftPressed ? 6 : 4;

if (connection != null)
{
var (status, response) = Task.Run(() => connection.SendMessageForResponseAsync(new ValueSet()
{
{ "Arguments", "LoadContextMenu" },
{ "FilePath", IsItemSelected ?
string.Join('|', selectedItems.Select(x => x.ItemPath)) :
workingDirectory},
{ "ExtendedMenu", shiftPressed },
{ "ShowOpenMenu", showOpenMenu }
})).Result;
if (status == AppServiceResponseStatus.Success
&& response.ContainsKey("Handle"))
{
var contextMenu = JsonConvert.DeserializeObject<Win32ContextMenu>((string)response["ContextMenu"]);
if (contextMenu != null)
{
LoadMenuFlyoutItem(menuItemsList, contextMenu.Items, (string)response["Handle"], true, maxItems);
}
}
}
var totalFlyoutItems = baseItems.Count - currentBaseLayoutItemCount;
if (totalFlyoutItems > 0 && !(baseItems[totalFlyoutItems].ItemType == ItemType.Separator))
{
menuItemsList.Insert(totalFlyoutItems, new ContextMenuFlyoutItemViewModel() { ItemType = ItemType.Separator });
}

return menuItemsList;
}

public static void LoadMenuFlyoutItem(IList<ContextMenuFlyoutItemViewModel> menuItemsListLocal,
IEnumerable<Win32ContextMenuItem> menuFlyoutItems,
string menuHandle,
bool showIcons = true,
int itemsBeforeOverflow = int.MaxValue)
{
var itemsCount = 0; // Separators do not count for reaching the overflow threshold
var menuItems = menuFlyoutItems.TakeWhile(x => x.Type == MenuItemType.MFT_SEPARATOR || ++itemsCount <= itemsBeforeOverflow).ToList();
var overflowItems = menuFlyoutItems.Except(menuItems).ToList();

if (overflowItems.Where(x => x.Type != MenuItemType.MFT_SEPARATOR).Any())
{
var moreItem = menuItemsListLocal.Where(x => x.ID == "ItemOverflow").FirstOrDefault();
if (moreItem == null)
{
var menuLayoutSubItem = new ContextMenuFlyoutItemViewModel()
{
Text = "ContextMenuMoreItemsLabel".GetLocalized(),
Tag = ((Win32ContextMenuItem)null, menuHandle),
Glyph = "\xE712",
};
LoadMenuFlyoutItem(menuLayoutSubItem.Items, overflowItems, menuHandle, false);
menuItemsListLocal.Insert(0, menuLayoutSubItem);
} else
{
LoadMenuFlyoutItem(moreItem.Items, overflowItems, menuHandle, false);
}
}
foreach (var menuFlyoutItem in menuItems
.SkipWhile(x => x.Type == MenuItemType.MFT_SEPARATOR) // Remove leading separators
.Reverse()
.SkipWhile(x => x.Type == MenuItemType.MFT_SEPARATOR)) // Remove trailing separators
{
if ((menuFlyoutItem.Type == MenuItemType.MFT_SEPARATOR) && (menuItemsListLocal.FirstOrDefault().ItemType == ItemType.Separator))
{
// Avoid duplicate separators
continue;
}

BitmapImage image = null;
if (showIcons)
{
image = new BitmapImage();
if (!string.IsNullOrEmpty(menuFlyoutItem.IconBase64))
{
byte[] bitmapData = Convert.FromBase64String(menuFlyoutItem.IconBase64);
using var ms = new MemoryStream(bitmapData);
image.SetSourceAsync(ms.AsRandomAccessStream()).AsTask().Wait(10);
}
}

if (menuFlyoutItem.Type == MenuItemType.MFT_SEPARATOR)
{
var menuLayoutItem = new ContextMenuFlyoutItemViewModel()
{
ItemType = ItemType.Separator,
Tag = (menuFlyoutItem, menuHandle)
};
menuItemsListLocal.Insert(0, menuLayoutItem);
}
else if (menuFlyoutItem.SubItems.Where(x => x.Type != MenuItemType.MFT_SEPARATOR).Any()
&& !string.IsNullOrEmpty(menuFlyoutItem.Label))
{
var menuLayoutSubItem = new ContextMenuFlyoutItemViewModel()
{
Text = menuFlyoutItem.Label.Replace("&", ""),
Tag = (menuFlyoutItem, menuHandle),
};
LoadMenuFlyoutItem(menuLayoutSubItem.Items, menuFlyoutItem.SubItems, menuHandle, false);
menuItemsListLocal.Insert(0, menuLayoutSubItem);
}
else if (!string.IsNullOrEmpty(menuFlyoutItem.Label))
{
var menuLayoutItem = new ContextMenuFlyoutItemViewModel()
{
Text = menuFlyoutItem.Label.Replace("&", ""),
Tag = (menuFlyoutItem, menuHandle),
BitmapIcon = image
};
menuLayoutItem.Command = new RelayCommand<object>(x => InvokeShellMenuItem(x));
menuLayoutItem.CommandParameter = (menuFlyoutItem, menuHandle);
menuItemsListLocal.Insert(0, menuLayoutItem);
}
}

static async void InvokeShellMenuItem(object tag)
{
var connection = await AppServiceConnectionHelper.Instance;
var (menuItem, menuHandle) = ParseContextMenuTag(tag);
if (connection != null)
{
await connection.SendMessageAsync(new ValueSet()
{
{ "Arguments", "ExecAndCloseContextMenu" },
{ "Handle", menuHandle },
{ "ItemID", menuItem.ID },
{ "CommandString", menuItem.CommandString }
});
}
}

static (Win32ContextMenuItem menuItem, string menuHandle) ParseContextMenuTag(object tag)
{
if (tag is ValueTuple<Win32ContextMenuItem, string> tuple)
{
(Win32ContextMenuItem menuItem, string menuHandle) = tuple;
return (menuItem, menuHandle);
}

return (null, null);
}
}


}
}
1 change: 1 addition & 0 deletions Files/IBaseLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ public interface IBaseLayout : IDisposable
void FocusSelectedItems();

void StartRenameItem();
void RefreshItems();
}
}
10 changes: 8 additions & 2 deletions Files/Interacts/BaseLayoutCommandImplementationModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Windows.Foundation;
using Windows.UI.Xaml.Input;
using Files.ViewModels;
using Files.DataModels;

namespace Files.Interacts
{
Expand Down Expand Up @@ -278,9 +279,9 @@ public virtual void CreateNewFolder(RoutedEventArgs e)
UIFilesystemHelpers.CreateFileFromDialogResultType(AddItemType.Folder, null, associatedInstance);
}

public virtual void CreateNewFile(RoutedEventArgs e)
public virtual void CreateNewFile(ShellNewEntry f)
{
UIFilesystemHelpers.CreateFileFromDialogResultType(AddItemType.File, null, associatedInstance);
UIFilesystemHelpers.CreateFileFromDialogResultType(AddItemType.File, f, associatedInstance);
}

public virtual async void PasteItemsFromClipboard(RoutedEventArgs e)
Expand Down Expand Up @@ -403,6 +404,11 @@ public virtual void ItemPointerPressed(PointerRoutedEventArgs e)
}
}

public virtual void RefreshItems(RoutedEventArgs e)
{
SlimContentPage.RefreshItems();
}

#endregion Command Implementation
}
}
7 changes: 5 additions & 2 deletions Files/Interacts/BaseLayoutCommandsViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Toolkit.Mvvm.Input;
using Files.DataModels;
using Microsoft.Toolkit.Mvvm.Input;
using System;
using System.Windows.Input;
using Windows.UI.Xaml;
Expand Down Expand Up @@ -53,13 +54,14 @@ private void InitializeCommands()
OpenDirectoryInNewPaneCommand = new RelayCommand<RoutedEventArgs>(commandsModel.OpenDirectoryInNewPane);
OpenInNewWindowItemCommand = new RelayCommand<RoutedEventArgs>(commandsModel.OpenInNewWindowItem);
CreateNewFolderCommand = new RelayCommand<RoutedEventArgs>(commandsModel.CreateNewFolder);
CreateNewFileCommand = new RelayCommand<RoutedEventArgs>(commandsModel.CreateNewFile);
CreateNewFileCommand = new RelayCommand<ShellNewEntry>(commandsModel.CreateNewFile);
PasteItemsFromClipboardCommand = new RelayCommand<RoutedEventArgs>(commandsModel.PasteItemsFromClipboard);
CopyPathOfSelectedItemCommand = new RelayCommand<RoutedEventArgs>(commandsModel.CopyPathOfSelectedItem);
OpenDirectoryInDefaultTerminalCommand = new RelayCommand<RoutedEventArgs>(commandsModel.OpenDirectoryInDefaultTerminal);
ShareItemCommand = new RelayCommand<RoutedEventArgs>(commandsModel.ShareItem);
PinDirectoryToSidebarCommand = new RelayCommand<RoutedEventArgs>(commandsModel.PinDirectoryToSidebar);
ItemPointerPressedCommand = new RelayCommand<PointerRoutedEventArgs>(commandsModel.ItemPointerPressed);
RefreshCommand = new RelayCommand<RoutedEventArgs>(commandsModel.RefreshItems);
}

#endregion Command Initialization
Expand Down Expand Up @@ -127,6 +129,7 @@ private void InitializeCommands()
public ICommand PinDirectoryToSidebarCommand { get; private set; }

public ICommand ItemPointerPressedCommand { get; private set; }
public ICommand RefreshCommand { get; private set; }

#endregion Commands

Expand Down
Loading