Skip to content

Commit 4398e82

Browse files
authored
Added more file types to the new file menu (#2644)
1 parent 447aa95 commit 4398e82

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+779
-1159
lines changed

Files/BaseLayout.cs

+47
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Files.Common;
2+
using Files.DataModels;
23
using Files.Filesystem;
34
using Files.Helpers;
45
using Files.UserControls;
@@ -136,6 +137,8 @@ internal set
136137

137138
public ListedItem SelectedItem { get; private set; }
138139

140+
private List<ShellNewEntry> cachedNewContextMenuEntries { get; set; }
141+
139142
public BaseLayout()
140143
{
141144
SelectedItemsPropertiesViewModel = new SelectedItemsPropertiesViewModel(this);
@@ -351,6 +354,8 @@ protected override async void OnNavigatedTo(NavigationEventArgs eventArgs)
351354
ParentShellPageInstance.InstanceViewModel.IsPageTypeNotHome = true; // show controls that were hidden on the home page
352355
ParentShellPageInstance.Clipboard_ContentChanged(null, null);
353356

357+
cachedNewContextMenuEntries = await RegistryHelper.GetNewContextMenuEntries();
358+
354359
FocusFileList(); // Set focus on layout specific file list control
355360
}
356361

@@ -509,6 +514,48 @@ public void RightClickContextMenu_Opening(object sender, object e)
509514
ClearSelection();
510515
var shiftPressed = Window.Current.CoreWindow.GetKeyState(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down);
511516
SetShellContextmenu(BaseLayoutContextFlyout, shiftPressed, false);
517+
var newItemMenu = (MenuFlyoutSubItem)BaseLayoutContextFlyout.Items.SingleOrDefault(x => x.Name == "NewEmptySpace");
518+
if (newItemMenu == null || cachedNewContextMenuEntries == null)
519+
{
520+
return;
521+
}
522+
if (!newItemMenu.Items.Any(x => (x.Tag as string) == "CreateNewFile"))
523+
{
524+
var separatorIndex = newItemMenu.Items.IndexOf(newItemMenu.Items.Single(x => x.Name == "NewMenuFileFolderSeparator"));
525+
foreach (var newEntry in Enumerable.Reverse(cachedNewContextMenuEntries))
526+
{
527+
MenuFlyoutItem menuLayoutItem;
528+
if (newEntry.Icon != null)
529+
{
530+
var image = new BitmapImage();
531+
#pragma warning disable CS4014
532+
image.SetSourceAsync(newEntry.Icon);
533+
#pragma warning restore CS4014
534+
menuLayoutItem = new MenuFlyoutItemWithImage()
535+
{
536+
Text = newEntry.Name,
537+
BitmapIcon = image,
538+
Tag = "CreateNewFile"
539+
};
540+
}
541+
else
542+
{
543+
menuLayoutItem = new MenuFlyoutItem()
544+
{
545+
Text = newEntry.Name,
546+
Icon = new FontIcon()
547+
{
548+
FontFamily = App.Current.Resources["FluentUIGlyphs"] as Windows.UI.Xaml.Media.FontFamily,
549+
Glyph = "\xea00"
550+
},
551+
Tag = "CreateNewFile"
552+
};
553+
}
554+
menuLayoutItem.Command = ParentShellPageInstance.InteractionOperations.CreateNewFile;
555+
menuLayoutItem.CommandParameter = newEntry;
556+
newItemMenu.Items.Insert(separatorIndex + 1, menuLayoutItem);
557+
}
558+
}
512559
}
513560

514561
public void RightClickItemContextMenu_Opening(object sender, object e)

Files/DataModels/ShellNewEntry.cs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using Files.Filesystem;
2+
using System;
3+
using System.IO;
4+
using System.Threading.Tasks;
5+
using Windows.Storage;
6+
using Windows.Storage.FileProperties;
7+
8+
namespace Files.DataModels
9+
{
10+
public class ShellNewEntry
11+
{
12+
public string Extension { get; set; }
13+
public string Name { get; set; }
14+
public string Command { get; set; }
15+
public StorageItemThumbnail Icon { get; set; }
16+
public byte[] Data { get; set; }
17+
public string Template { get; set; }
18+
19+
public async Task<FilesystemResult<StorageFile>> Create(string filePath, IShellPage associatedInstance)
20+
{
21+
var parentFolder = await associatedInstance.FilesystemViewModel.GetFolderFromPathAsync(Path.GetDirectoryName(filePath));
22+
if (parentFolder)
23+
{
24+
return await Create(parentFolder, Path.GetFileName(filePath));
25+
}
26+
return new FilesystemResult<StorageFile>(null, parentFolder.ErrorCode);
27+
}
28+
29+
public async Task<FilesystemResult<StorageFile>> Create(StorageFolder parentFolder, string fileName)
30+
{
31+
FilesystemResult<StorageFile> createdFile = null;
32+
if (!fileName.EndsWith(this.Extension))
33+
{
34+
fileName += this.Extension;
35+
}
36+
if (Template == null)
37+
{
38+
createdFile = await FilesystemTasks.Wrap(() => parentFolder.CreateFileAsync(fileName, CreationCollisionOption.GenerateUniqueName).AsTask());
39+
}
40+
else
41+
{
42+
createdFile = await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Template).AsTask())
43+
.OnSuccess(t => t.CopyAsync(parentFolder, fileName, NameCollisionOption.GenerateUniqueName).AsTask());
44+
}
45+
if (createdFile)
46+
{
47+
if (this.Data != null)
48+
{
49+
await FileIO.WriteBytesAsync(createdFile.Result, this.Data);
50+
}
51+
}
52+
return createdFile;
53+
}
54+
}
55+
}

Files/Dialogs/AddItemDialog.xaml

+46-29
Original file line numberDiff line numberDiff line change
@@ -17,42 +17,59 @@
1717
RequestedTheme="{x:Bind local2:ThemeHelper.RootTheme}"
1818
mc:Ignorable="d">
1919

20-
<StackPanel Orientation="Vertical" Spacing="25">
20+
<Grid RowSpacing="25">
21+
<Grid.RowDefinitions>
22+
<RowDefinition Height="Auto"/>
23+
<RowDefinition Height="*"/>
24+
</Grid.RowDefinitions>
2125
<Grid x:Name="SubtitleArea">
2226
<TextBlock
2327
x:Name="Description"
2428
x:Uid="AddDialogDescription"
2529
Text="Choose a type for this new item below" />
2630
</Grid>
27-
<Grid x:Name="SelectionListContent" HorizontalAlignment="Stretch">
28-
<StackPanel>
29-
<ListView
30-
x:Name="AddItemsListView"
31-
Width="400"
32-
IsItemClickEnabled="True"
33-
ItemClick="ListView_ItemClick"
34-
ItemsSource="{x:Bind AddItemsList}"
35-
SelectionMode="None">
36-
<ListView.ItemTemplate>
37-
<DataTemplate x:DataType="local:AddListItem">
38-
<Grid Height="50">
39-
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
40-
<TextBlock
41-
Margin="0,0,10,0"
31+
<Grid x:Name="SelectionListContent"
32+
Grid.Row="1"
33+
HorizontalAlignment="Stretch">
34+
<ListView
35+
x:Name="AddItemsListView"
36+
Width="400"
37+
IsItemClickEnabled="True"
38+
ItemClick="ListView_ItemClick"
39+
ItemsSource="{x:Bind AddItemsList}"
40+
SelectionMode="None">
41+
<ListView.ItemTemplate>
42+
<DataTemplate x:DataType="local:AddListItem">
43+
<Grid Height="50">
44+
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
45+
<Grid Margin="0,0,10,0"
46+
VerticalAlignment="Center">
47+
<Viewbox
48+
x:Name="IconRoot"
49+
Width="24"
50+
Height="24"
51+
HorizontalAlignment="Left"
52+
VerticalAlignment="Center">
53+
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="{x:Bind Glyph}" />
54+
</Viewbox>
55+
<Image
56+
Width="24"
57+
Height="24"
58+
Margin="0"
59+
HorizontalAlignment="Left"
4260
VerticalAlignment="Center"
43-
FontFamily="Segoe MDL2 Assets"
44-
FontSize="28"
45-
Text="{x:Bind Icon}" />
46-
<StackPanel>
47-
<TextBlock Text="{x:Bind Header}" />
48-
<TextBlock Foreground="Gray" Text="{x:Bind SubHeader}" />
49-
</StackPanel>
61+
Source="{x:Bind Icon}"
62+
Stretch="Uniform" />
63+
</Grid>
64+
<StackPanel>
65+
<TextBlock Text="{x:Bind Header}" />
66+
<TextBlock Foreground="Gray" Text="{x:Bind SubHeader}" />
5067
</StackPanel>
51-
</Grid>
52-
</DataTemplate>
53-
</ListView.ItemTemplate>
54-
</ListView>
55-
</StackPanel>
68+
</StackPanel>
69+
</Grid>
70+
</DataTemplate>
71+
</ListView.ItemTemplate>
72+
</ListView>
5673
</Grid>
57-
</StackPanel>
74+
</Grid>
5875
</ContentDialog>

Files/Dialogs/AddItemDialog.xaml.cs

+56-25
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,75 @@
1-
using Microsoft.Toolkit.Uwp.Extensions;
2-
using System.Collections.Generic;
1+
using Files.DataModels;
2+
using Files.Helpers;
3+
using Microsoft.Toolkit.Uwp.Extensions;
4+
using System;
5+
using System.Collections.ObjectModel;
36
using Windows.UI.Xaml.Controls;
7+
using Windows.UI.Xaml.Media.Imaging;
48

59
namespace Files.Dialogs
610
{
711
public sealed partial class AddItemDialog : ContentDialog
812
{
9-
public AddItemType ResultType { get; private set; } = AddItemType.Cancel;
13+
public AddItemResult ResultType { get; private set; } = new AddItemResult() { ItemType = AddItemType.Cancel };
1014

1115
public AddItemDialog()
1216
{
1317
InitializeComponent();
1418
AddItemsToList();
1519
}
1620

17-
public List<AddListItem> AddItemsList = new List<AddListItem>();
21+
public ObservableCollection<AddListItem> AddItemsList = new ObservableCollection<AddListItem>();
1822

19-
public void AddItemsToList()
23+
public async void AddItemsToList()
2024
{
2125
AddItemsList.Clear();
2226

2327
AddItemsList.Add(new AddListItem
2428
{
2529
Header = "AddDialogListFolderHeader".GetLocalized(),
2630
SubHeader = "AddDialogListFolderSubHeader".GetLocalized(),
27-
Icon = "\xE838",
31+
Glyph = "\xE838",
2832
IsItemEnabled = true,
29-
ItemType = AddItemType.Folder
33+
ItemType = new AddItemResult() { ItemType = AddItemType.Folder }
3034
});
3135

32-
AddItemsList.Add(new AddListItem
36+
var itemTypes = await RegistryHelper.GetNewContextMenuEntries();
37+
38+
foreach (var itemType in itemTypes)
3339
{
34-
Header = "AddDialogListTextFileHeader".GetLocalized(),
35-
SubHeader = "AddDialogListTextFileSubHeader".GetLocalized(),
36-
Icon = "\xE8A5",
37-
IsItemEnabled = true,
38-
ItemType = AddItemType.TextDocument
39-
});
40+
BitmapImage image = null;
41+
if (itemType.Icon != null)
42+
{
43+
image = new BitmapImage();
44+
await image.SetSourceAsync(itemType.Icon);
45+
}
46+
47+
AddItemsList.Add(new AddListItem
48+
{
49+
Header = itemType.Name,
50+
SubHeader = itemType.Extension,
51+
Glyph = itemType.Icon != null ? null : "\xE8A5",
52+
Icon = image,
53+
IsItemEnabled = true,
54+
ItemType = new AddItemResult()
55+
{
56+
ItemType = AddItemType.File,
57+
ItemInfo = itemType
58+
}
59+
});
60+
}
61+
4062
AddItemsList.Add(new AddListItem
4163
{
42-
Header = "AddDialogListBitmapHeader".GetLocalized(),
43-
SubHeader = "AddDialogListBitmapSubHeader".GetLocalized(),
44-
Icon = "\xEB9F",
64+
Header = "AddDialogListFileHeader".GetLocalized(),
65+
SubHeader = "AddDialogListFileSubHeader".GetLocalized(),
66+
Glyph = "\xE8A5",
4567
IsItemEnabled = true,
46-
ItemType = AddItemType.BitmapImage
68+
ItemType = new AddItemResult()
69+
{
70+
ItemType = AddItemType.File,
71+
ItemInfo = new ShellNewEntry()
72+
}
4773
});
4874
}
4975

@@ -56,19 +82,24 @@ private void ListView_ItemClick(object sender, ItemClickEventArgs e)
5682

5783
public enum AddItemType
5884
{
59-
Folder = 0,
60-
TextDocument = 1,
61-
BitmapImage = 2,
62-
CompressedArchive = 3,
63-
Cancel = 4
85+
Folder,
86+
File,
87+
Cancel
88+
}
89+
90+
public class AddItemResult
91+
{
92+
public AddItemType ItemType { get; set; }
93+
public ShellNewEntry ItemInfo { get; set; }
6494
}
6595

6696
public class AddListItem
6797
{
6898
public string Header { get; set; }
6999
public string SubHeader { get; set; }
70-
public string Icon { get; set; }
100+
public string Glyph { get; set; }
101+
public BitmapImage Icon { get; set; }
71102
public bool IsItemEnabled { get; set; }
72-
public AddItemType ItemType { get; set; }
103+
public AddItemResult ItemType { get; set; }
73104
}
74105
}

Files/Files.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@
164164
<Compile Include="Converters\StorageDeleteOptionToBooleanConverter.cs" />
165165
<Compile Include="Converters\StringArrayToString.cs" />
166166
<Compile Include="Converters\UInt32ToString.cs" />
167+
<Compile Include="DataModels\ShellNewEntry.cs" />
167168
<Compile Include="Helpers\AppUpdater.cs" />
168169
<Compile Include="DataModels\DefaultLanguageModel.cs" />
169170
<Compile Include="Enums\FileOperationType.cs" />
@@ -179,6 +180,7 @@
179180
<Compile Include="Dialogs\RestartDialog.xaml.cs">
180181
<DependentUpon>RestartDialog.xaml</DependentUpon>
181182
</Compile>
183+
<Compile Include="Helpers\RegistryHelper.cs" />
182184
<Compile Include="View Models\Properties\FileProperty.cs" />
183185
<Compile Include="Extensions\EnumerableExtensions.cs" />
184186
<Compile Include="Extensions\LinqExtensions.cs" />

Files/Filesystem/FilesystemOperations/FilesystemOperations.cs

+10-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,16 @@ public async Task<IStorageHistory> CreateAsync(IStorageItemWithPath source, IPro
5454
{
5555
case FilesystemItemType.File:
5656
{
57-
StorageFolder folder = await associatedInstance.FilesystemViewModel.GetFolderFromPathAsync(Path.GetDirectoryName(source.Path));
58-
await folder.CreateFileAsync(Path.GetFileName(source.Path));
57+
var newEntryInfo = await RegistryHelper.GetNewContextMenuEntryForType(Path.GetExtension(source.Path));
58+
if (newEntryInfo == null)
59+
{
60+
StorageFolder folder = await associatedInstance.FilesystemViewModel.GetFolderFromPathAsync(Path.GetDirectoryName(source.Path));
61+
await folder.CreateFileAsync(Path.GetFileName(source.Path));
62+
}
63+
else
64+
{
65+
await newEntryInfo.Create(source.Path, associatedInstance);
66+
}
5967

6068
break;
6169
}

Files/Filesystem/StorageFileHelpers/FilesystemResult.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,14 @@ public async static Task<FilesystemResult> Wrap(Func<Task> wrapped)
128128
}
129129
}
130130

131-
public async static Task<FilesystemResult<T>> OnSuccess<T>(this Task<FilesystemResult<T>> wrapped, Func<T, Task<T>> func)
131+
public async static Task<FilesystemResult<V>> OnSuccess<V,T>(this Task<FilesystemResult<T>> wrapped, Func<T, Task<V>> func)
132132
{
133133
var res = await wrapped;
134134
if (res)
135135
{
136136
return await Wrap(() => func(res.Result));
137137
}
138-
return res;
138+
return new FilesystemResult<V>(default, res.ErrorCode);
139139
}
140140

141141
public async static Task<FilesystemResult> OnSuccess<T>(this Task<FilesystemResult<T>> wrapped, Func<T, Task> func)

0 commit comments

Comments
 (0)