Skip to content

Commit 6a112b0

Browse files
authored
Feature: Add selection RichCommands (#11502)
1 parent c34e93c commit 6a112b0

16 files changed

+278
-40
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using CommunityToolkit.Mvvm.DependencyInjection;
2+
using Files.App.Commands;
3+
using Files.App.Contexts;
4+
using Files.App.Extensions;
5+
using System.Threading.Tasks;
6+
7+
namespace Files.App.Actions
8+
{
9+
internal class ClearSelectionAction : IAction
10+
{
11+
private readonly IContentPageContext context = Ioc.Default.GetRequiredService<IContentPageContext>();
12+
13+
public string Label { get; } = "ClearSelection".GetLocalizedResource();
14+
15+
public RichGlyph Glyph { get; } = new("\uE8E6");
16+
17+
public bool IsExecutable
18+
{
19+
get
20+
{
21+
if (context.PageType is ContentPageTypes.Home)
22+
return false;
23+
24+
if (!context.HasSelection)
25+
return false;
26+
27+
var page = context.ShellPage;
28+
if (page is null)
29+
return false;
30+
31+
bool isEditing = page.ToolbarViewModel.IsEditModeEnabled;
32+
bool isRenaming = page.SlimContentPage.IsRenamingItem;
33+
34+
return !isEditing && !isRenaming;
35+
}
36+
}
37+
38+
public Task ExecuteAsync()
39+
{
40+
context.ShellPage?.SlimContentPage?.ItemManipulationModel?.ClearSelection();
41+
return Task.CompletedTask;
42+
}
43+
}
44+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using CommunityToolkit.Mvvm.DependencyInjection;
2+
using Files.App.Commands;
3+
using Files.App.Contexts;
4+
using Files.App.Extensions;
5+
using System.Threading.Tasks;
6+
7+
namespace Files.App.Actions
8+
{
9+
internal class InvertSelectionAction : IAction
10+
{
11+
private readonly IContentPageContext context = Ioc.Default.GetRequiredService<IContentPageContext>();
12+
13+
public string Label { get; } = "InvertSelection".GetLocalizedResource();
14+
15+
public RichGlyph Glyph { get; } = new("\uE746");
16+
17+
public bool IsExecutable
18+
{
19+
get
20+
{
21+
if (context.PageType is ContentPageTypes.Home)
22+
return false;
23+
24+
if (!context.HasItem)
25+
return false;
26+
27+
var page = context.ShellPage;
28+
if (page is null)
29+
return false;
30+
31+
bool isEditing = page.ToolbarViewModel.IsEditModeEnabled;
32+
bool isRenaming = page.SlimContentPage.IsRenamingItem;
33+
34+
return !isEditing && !isRenaming;
35+
}
36+
}
37+
38+
public Task ExecuteAsync()
39+
{
40+
context?.ShellPage?.SlimContentPage?.ItemManipulationModel?.InvertSelection();
41+
return Task.CompletedTask;
42+
}
43+
}
44+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using CommunityToolkit.Mvvm.ComponentModel;
2+
using Files.App.Commands;
3+
using Files.App.DataModels;
4+
using System.ComponentModel;
5+
using System.Threading.Tasks;
6+
7+
namespace Files.App.Actions
8+
{
9+
internal class MultiSelectAction : ObservableObject, IToggleAction
10+
{
11+
public string Label { get; } = "MultiSelect";
12+
13+
public RichGlyph Glyph { get; } = new("\uE762");
14+
15+
public bool IsOn => App.AppModel.ShowSelectionCheckboxes;
16+
17+
public MultiSelectAction()
18+
{
19+
App.AppModel.PropertyChanged += Model_PropertyChanged;
20+
}
21+
22+
public Task ExecuteAsync()
23+
{
24+
App.AppModel.ShowSelectionCheckboxes = !App.AppModel.ShowSelectionCheckboxes;
25+
return Task.CompletedTask;
26+
}
27+
28+
private void Model_PropertyChanged(object? sender, PropertyChangedEventArgs e)
29+
{
30+
if (e.PropertyName is nameof(AppModel.ShowSelectionCheckboxes))
31+
OnPropertyChanged(nameof(IsOn));
32+
}
33+
}
34+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using CommunityToolkit.Mvvm.DependencyInjection;
2+
using Files.App.Commands;
3+
using Files.App.Contexts;
4+
using Files.App.Extensions;
5+
using System.Threading.Tasks;
6+
using Windows.System;
7+
8+
namespace Files.App.Actions
9+
{
10+
internal class SelectAllAction : IAction
11+
{
12+
private readonly IContentPageContext context = Ioc.Default.GetRequiredService<IContentPageContext>();
13+
14+
public string Label { get; } = "SelectAll".GetLocalizedResource();
15+
16+
public RichGlyph Glyph { get; } = new("\uE8B3");
17+
public HotKey HotKey { get; } = new(VirtualKey.A, VirtualKeyModifiers.Control);
18+
19+
public bool IsExecutable
20+
{
21+
get
22+
{
23+
if (context.PageType is ContentPageTypes.Home)
24+
return false;
25+
26+
var page = context.ShellPage;
27+
if (page is null)
28+
return false;
29+
30+
int itemCount = page.FilesystemViewModel.FilesAndFolders.Count;
31+
int selectedItemCount = context.SelectedItems.Count;
32+
if (itemCount == selectedItemCount)
33+
return false;
34+
35+
bool isEditing = page.ToolbarViewModel.IsEditModeEnabled;
36+
bool isRenaming = page.SlimContentPage.IsRenamingItem;
37+
38+
return !isEditing && !isRenaming;
39+
}
40+
}
41+
42+
public Task ExecuteAsync()
43+
{
44+
context.ShellPage?.SlimContentPage?.ItemManipulationModel?.SelectAllItems();
45+
return Task.CompletedTask;
46+
}
47+
}
48+
}

src/Files.App/Commands/CommandCodes.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,29 @@ public enum CommandCodes
44
{
55
None,
66

7-
// global
7+
// Global
88
OpenHelp,
99
ToggleFullScreen,
1010

11-
// show
11+
// Show
1212
ToggleShowHiddenItems,
1313
ToggleShowFileExtensions,
1414

15-
// file system
15+
// File System
1616
CreateFolder,
1717
CreateShortcut,
1818
CreateShortcutFromDialog,
1919
EmptyRecycleBin,
2020
RestoreRecycleBin,
2121
RestoreAllRecycleBin,
2222

23-
// Start
23+
// Selection
24+
MultiSelect,
25+
SelectAll,
26+
InvertSelection,
27+
ClearSelection,
28+
29+
// Start
2430
PinToStart,
2531
UnpinFromStart,
2632

src/Files.App/Commands/IRichCommand.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public interface IRichCommand : ICommand, INotifyPropertyChanging, INotifyProper
2121

2222
HotKey DefaultHotKey { get; }
2323
HotKey CustomHotKey { get; set; }
24+
string? HotKeyText { get; }
2425

2526
bool IsToggle { get; }
2627
bool IsOn { get; set; }

src/Files.App/Commands/Manager/CommandManager.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ internal class CommandManager : ICommandManager
3232
public IRichCommand ToggleFullScreen => commands[CommandCodes.ToggleFullScreen];
3333
public IRichCommand ToggleShowHiddenItems => commands[CommandCodes.ToggleShowHiddenItems];
3434
public IRichCommand ToggleShowFileExtensions => commands[CommandCodes.ToggleShowFileExtensions];
35+
public IRichCommand MultiSelect => commands[CommandCodes.MultiSelect];
36+
public IRichCommand SelectAll => commands[CommandCodes.SelectAll];
37+
public IRichCommand InvertSelection => commands[CommandCodes.InvertSelection];
38+
public IRichCommand ClearSelection => commands[CommandCodes.ClearSelection];
3539
public IRichCommand EmptyRecycleBin => commands[CommandCodes.EmptyRecycleBin];
3640
public IRichCommand RestoreRecycleBin => commands[CommandCodes.RestoreRecycleBin];
3741
public IRichCommand RestoreAllRecycleBin => commands[CommandCodes.RestoreAllRecycleBin];
@@ -42,7 +46,7 @@ internal class CommandManager : ICommandManager
4246
public IRichCommand UnpinFromStart => commands[CommandCodes.UnpinFromStart];
4347
public IRichCommand PinItemToFavorites => commands[CommandCodes.PinItemToFavorites];
4448
public IRichCommand UnpinItemFromFavorites => commands[CommandCodes.UnpinItemFromFavorites];
45-
49+
4650
public CommandManager()
4751
{
4852
commands = CreateActions()
@@ -65,6 +69,10 @@ public CommandManager()
6569
[CommandCodes.ToggleFullScreen] = new ToggleFullScreenAction(),
6670
[CommandCodes.ToggleShowHiddenItems] = new ToggleShowHiddenItemsAction(),
6771
[CommandCodes.ToggleShowFileExtensions] = new ToggleShowFileExtensionsAction(),
72+
[CommandCodes.MultiSelect] = new MultiSelectAction(),
73+
[CommandCodes.SelectAll] = new SelectAllAction(),
74+
[CommandCodes.InvertSelection] = new InvertSelectionAction(),
75+
[CommandCodes.ClearSelection] = new ClearSelectionAction(),
6876
[CommandCodes.EmptyRecycleBin] = new EmptyRecycleBinAction(),
6977
[CommandCodes.RestoreRecycleBin] = new RestoreRecycleBinAction(),
7078
[CommandCodes.RestoreAllRecycleBin] = new RestoreAllRecycleBinAction(),
@@ -94,6 +102,7 @@ public event PropertyChangedEventHandler? PropertyChanged { add {} remove {} }
94102
public FontIcon? FontIcon => null;
95103
public OpacityIcon? OpacityIcon => null;
96104

105+
public string? HotKeyText => null;
97106
public HotKey DefaultHotKey => HotKey.None;
98107

99108
public HotKey CustomHotKey
@@ -136,6 +145,7 @@ private class ActionCommand : ObservableObject, IRichCommand
136145
private readonly Lazy<OpacityIcon?> opacityIcon;
137146
public OpacityIcon? OpacityIcon => opacityIcon.Value;
138147

148+
public string? HotKeyText => !customHotKey.IsNone ? CustomHotKey.ToString() : null;
139149
public HotKey DefaultHotKey => action.HotKey;
140150

141151
private HotKey customHotKey;
@@ -164,6 +174,7 @@ public HotKey CustomHotKey
164174
};
165175

166176
SetProperty(ref customHotKey, value);
177+
OnPropertyChanged(nameof(HotKeyText));
167178
manager.HotKeyChanged?.Invoke(manager, args);
168179
}
169180
}

src/Files.App/Commands/Manager/ICommandManager.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ public interface ICommandManager : IEnumerable<IRichCommand>
1818
IRichCommand ToggleShowHiddenItems { get; }
1919
IRichCommand ToggleShowFileExtensions { get; }
2020

21+
IRichCommand MultiSelect { get; }
22+
IRichCommand SelectAll { get; }
23+
IRichCommand InvertSelection { get; }
24+
IRichCommand ClearSelection { get; }
25+
2126
IRichCommand CreateFolder { get; }
2227
IRichCommand CreateShortcut { get; }
2328
IRichCommand CreateShortcutFromDialog { get; }

src/Files.App/Contexts/ContentPage/ContentPageContext.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ internal class ContentPageContext : ObservableObject, IContentPageContext
2525

2626
public bool HasItem => shellPage?.ToolbarViewModel?.HasItem ?? false;
2727

28+
public bool HasSelection => SelectedItems.Count is not 0;
29+
public ListedItem? SelectedItem => SelectedItems.Count is 1 ? SelectedItems[0] : null;
30+
2831
private IReadOnlyList<ListedItem> selectedItems = EmptyListedItemList;
2932
public IReadOnlyList<ListedItem> SelectedItems => selectedItems;
3033

@@ -94,8 +97,17 @@ private void UpdatePageType()
9497

9598
private void UpdateSelectedItems()
9699
{
100+
bool oldHasSelection = HasSelection;
101+
ListedItem? oldSelectedItem = SelectedItem;
102+
97103
IReadOnlyList<ListedItem> items = shellPage?.ToolbarViewModel?.SelectedItems?.AsReadOnly() ?? EmptyListedItemList;
98-
SetProperty(ref selectedItems, items, nameof(SelectedItems));
104+
if (SetProperty(ref selectedItems, items, nameof(SelectedItems)))
105+
{
106+
if (HasSelection != oldHasSelection)
107+
OnPropertyChanged(nameof(HasSelection));
108+
if (SelectedItem != oldSelectedItem)
109+
OnPropertyChanged(nameof(SelectedItem));
110+
}
99111
}
100112

101113
private void BaseShellPage_CurrentInstanceChanged(object? sender, BaseShellPage newShellPage)

src/Files.App/Contexts/ContentPage/IContentPageContext.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public interface IContentPageContext : INotifyPropertyChanged
1313
ListedItem? Folder { get; }
1414

1515
bool HasItem { get; }
16+
bool HasSelection { get; }
17+
ListedItem? SelectedItem { get; }
1618
IReadOnlyList<ListedItem> SelectedItems { get; }
1719
}
1820
}

src/Files.App/Strings/en-US/Resources.resw

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,13 @@
153153
<data name="Skip" xml:space="preserve">
154154
<value>Skip</value>
155155
</data>
156-
<data name="NavToolbarSelectAll.Text" xml:space="preserve">
156+
<data name="SelectAll" xml:space="preserve">
157157
<value>Select All</value>
158158
</data>
159-
<data name="NavToolbarInvertSelection.Text" xml:space="preserve">
159+
<data name="InvertSelection" xml:space="preserve">
160160
<value>Invert Selection</value>
161161
</data>
162-
<data name="NavToolbarClearSelection.Text" xml:space="preserve">
162+
<data name="ClearSelection" xml:space="preserve">
163163
<value>Clear Selection</value>
164164
</data>
165165
<data name="PropertiesModified.Text" xml:space="preserve">
@@ -2604,4 +2604,7 @@
26042604
<data name="Save" xml:space="preserve">
26052605
<value>Save</value>
26062606
</data>
2607+
<data name="MultiSelect" xml:space="preserve">
2608+
<value>Multiselect</value>
2609+
</data>
26072610
</root>

src/Files.App/UserControls/InnerNavigationToolbar.xaml

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -414,34 +414,22 @@
414414
<MenuFlyout Placement="Bottom">
415415
<MenuFlyoutItem
416416
x:Name="SelectAllMFI"
417-
Command="{x:Bind ViewModel.SelectAllContentPageItemsCommand, Mode=OneWay}"
418-
Text="{helpers:ResourceString Name=NavToolbarSelectAll/Text}">
419-
<MenuFlyoutItem.Icon>
420-
<FontIcon Glyph="&#xE8B3;" />
421-
</MenuFlyoutItem.Icon>
422-
<MenuFlyoutItem.KeyboardAccelerators>
423-
<KeyboardAccelerator
424-
Key="A"
425-
IsEnabled="False"
426-
Modifiers="Control" />
427-
</MenuFlyoutItem.KeyboardAccelerators>
428-
</MenuFlyoutItem>
417+
Command="{x:Bind Commands.SelectAll}"
418+
Icon="{x:Bind Commands.SelectAll.FontIcon}"
419+
KeyboardAcceleratorTextOverride="{x:Bind Commands.SelectAll.HotKeyText}"
420+
Text="{x:Bind Commands.SelectAll.Label}" />
429421
<MenuFlyoutItem
430422
x:Name="InvertSelectionMFI"
431-
Command="{x:Bind ViewModel.InvertContentPageSelctionCommand, Mode=OneWay}"
432-
Text="{helpers:ResourceString Name=NavToolbarInvertSelection/Text}">
433-
<MenuFlyoutItem.Icon>
434-
<FontIcon Glyph="&#xE746;" />
435-
</MenuFlyoutItem.Icon>
436-
</MenuFlyoutItem>
423+
Command="{x:Bind Commands.InvertSelection}"
424+
Icon="{x:Bind Commands.InvertSelection.FontIcon}"
425+
KeyboardAcceleratorTextOverride="{x:Bind Commands.InvertSelection.HotKeyText}"
426+
Text="{x:Bind Commands.InvertSelection.Label}" />
437427
<MenuFlyoutItem
438428
x:Name="ClearSelectionMFI"
439-
Command="{x:Bind ViewModel.ClearContentPageSelectionCommand, Mode=OneWay}"
440-
Text="{helpers:ResourceString Name=NavToolbarClearSelection/Text}">
441-
<MenuFlyoutItem.Icon>
442-
<FontIcon Glyph="&#xE8E6;" />
443-
</MenuFlyoutItem.Icon>
444-
</MenuFlyoutItem>
429+
Command="{x:Bind Commands.ClearSelection}"
430+
Icon="{x:Bind Commands.ClearSelection.FontIcon}"
431+
KeyboardAcceleratorTextOverride="{x:Bind Commands.ClearSelection.HotKeyText}"
432+
Text="{x:Bind Commands.ClearSelection.Label}" />
445433
</MenuFlyout>
446434
</AppBarButton.Flyout>
447435

0 commit comments

Comments
 (0)