Skip to content

Code Quality: Improved the code quality of the customization properties page #11848

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 3 commits into from
Mar 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
127 changes: 99 additions & 28 deletions src/Files.App/ViewModels/Properties/CustomizationViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,108 @@
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.WinUI;
using Files.App.Shell;
using Files.App.Helpers;
using Files.App.Views.Properties;
using Files.Shared;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Input;
using Windows.Storage.Pickers;

namespace Files.App.ViewModels.Properties
{
public class CustomizationViewModel : ObservableObject
{
public CustomizationViewModel(string selectedItemPath, string iconResourceItemPath, IShellPage appInstance, bool isShortcut)
public CustomizationViewModel(IShellPage appInstance, BaseProperties baseProperties)
{
SelectedItemPath = selectedItemPath;
IconResourceItemPath = iconResourceItemPath;
Filesystem.ListedItem item;

if (baseProperties is FileProperties fileProperties)
item = fileProperties.Item;
else if (baseProperties is FolderProperties folderProperties)
item = folderProperties.Item;
else
return;

AppInstance = appInstance;
IsShortcut = isShortcut;
IconResourceItemPath = Path.Combine(CommonPaths.SystemRootPath, "System32", "SHELL32.dll");
IsShortcut = item.IsShortcut;
SelectedItemPath = item.ItemPath;

_dllIcons = new();
DllIcons = new(_dllIcons);

RestoreButtonIsEnabled = true;

// Get default
LoadIconsForPath(IconResourceItemPath);

RestoreDefaultIconCommand = new AsyncRelayCommand(RestoreDefaultIcon);
RestoreDefaultIconCommand = new AsyncRelayCommand(ExecuteRestoreDefaultIconAsync);
PickDllFileCommand = new AsyncRelayCommand<XamlRoot>(ExecuteOpenFilePickerAsync);
}

private string? _selectedItemPath;
public string? SelectedItemPath { get => _selectedItemPath; set => SetProperty(ref _selectedItemPath, value); }
public string? SelectedItemPath
{
get => _selectedItemPath;
set => SetProperty(ref _selectedItemPath, value);
}

private string? _iconResourceItemPath;
public string? IconResourceItemPath { get => _iconResourceItemPath; set => SetProperty(ref _iconResourceItemPath, value); }
public string? IconResourceItemPath
{
get => _iconResourceItemPath;
set => SetProperty(ref _iconResourceItemPath, value);
}

private IShellPage? _appInstance;
private IShellPage? AppInstance { get => _appInstance; set => SetProperty(ref _appInstance, value); }
private IShellPage? AppInstance
{
get => _appInstance;
set => SetProperty(ref _appInstance, value);
}

private bool _isShortcut;
public bool IsShortcut { get => _isShortcut; private set => SetProperty(ref _isShortcut, value); }
public bool IsShortcut
{
get => _isShortcut;
private set => SetProperty(ref _isShortcut, value);
}

private bool _restoreButtonIsEnabled;
public bool RestoreButtonIsEnabled { get => _restoreButtonIsEnabled; private set => SetProperty(ref _restoreButtonIsEnabled, value); }
private bool _restoreButtonIsEnabled = true;
public bool RestoreButtonIsEnabled
{
get => _restoreButtonIsEnabled;
private set => SetProperty(ref _restoreButtonIsEnabled, value);
}

private readonly ObservableCollection<IconFileInfo> _dllIcons;
public ReadOnlyObservableCollection<IconFileInfo> DllIcons { get; }

public ICommand RestoreDefaultIconCommand { get; private set; }
private IconFileInfo _SelectedDllIcon;
public IconFileInfo SelectedDllIcon
{
get => _SelectedDllIcon;
set
{
if (value is not null && SetProperty(ref _SelectedDllIcon, value))
{
ChangeIcon(value);
}
}
}

public IAsyncRelayCommand RestoreDefaultIconCommand { get; private set; }
public IAsyncRelayCommand<XamlRoot> PickDllFileCommand { get; private set; }

private async Task RestoreDefaultIcon()
private async Task ExecuteRestoreDefaultIconAsync()
{
RestoreButtonIsEnabled = false;

var setIconResult = IsShortcut
? SetCustomFileIcon(SelectedItemPath, null)
: SetCustomFolderIcon(SelectedItemPath, null);
? Win32API.SetCustomFileIcon(SelectedItemPath, null)
: Win32API.SetCustomDirectoryIcon(SelectedItemPath, null);

if (setIconResult)
{
Expand All @@ -70,11 +118,40 @@ await App.Window.DispatcherQueue.EnqueueAsync(() =>
}
}

public async Task ChangeIcon(IconFileInfo selectedIconInfo)
private async Task ExecuteOpenFilePickerAsync(XamlRoot? xamlRoot)
{
if (xamlRoot is null)
return;

// Initialize picker
FileOpenPicker picker = new()
{
SuggestedStartLocation = PickerLocationId.ComputerFolder,
ViewMode = PickerViewMode.Thumbnail,
};

picker.FileTypeFilter.Add(".dll");
picker.FileTypeFilter.Add(".exe");
picker.FileTypeFilter.Add(".ico");

// WINUI3: Create and initialize new window
var parentWindowId = ((MainPropertiesPage)((Frame)xamlRoot.Content).Content).AppWindow.Id;
var handle = Microsoft.UI.Win32Interop.GetWindowFromWindowId(parentWindowId);
WinRT.Interop.InitializeWithWindow.Initialize(picker, handle);

// Open picker
var file = await picker.PickSingleFileAsync();
if (file is null)
return;

LoadIconsForPath(file.Path);
}

private async Task ChangeIcon(IconFileInfo selectedIconInfo)
{
var setIconResult = IsShortcut
? SetCustomFileIcon(SelectedItemPath, IconResourceItemPath, selectedIconInfo.Index)
: SetCustomFolderIcon(SelectedItemPath, IconResourceItemPath, selectedIconInfo.Index);
? Win32API.SetCustomFileIcon(SelectedItemPath, IconResourceItemPath, selectedIconInfo.Index)
: Win32API.SetCustomDirectoryIcon(SelectedItemPath, IconResourceItemPath, selectedIconInfo.Index);

if (setIconResult)
{
Expand All @@ -85,7 +162,7 @@ await App.Window.DispatcherQueue.EnqueueAsync(() =>
}
}

public void LoadIconsForPath(string path)
private void LoadIconsForPath(string path)
{
IconResourceItemPath = path;
_dllIcons.Clear();
Expand All @@ -97,11 +174,5 @@ public void LoadIconsForPath(string path)
foreach(var item in icons)
_dllIcons.Add(item);
}

private bool SetCustomFolderIcon(string? folderPath, string? iconFile, int iconIndex = 0)
=> Win32API.SetCustomDirectoryIcon(folderPath, iconFile, iconIndex);

private bool SetCustomFileIcon(string? filePath, string? iconFile, int iconIndex = 0)
=> Win32API.SetCustomFileIcon(filePath, iconFile, iconIndex);
}
}
101 changes: 44 additions & 57 deletions src/Files.App/Views/Properties/CustomizationPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

<vm:BasePropertiesPage.Resources>
<ResourceDictionary>

<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx:///ResourceDictionaries/PropertiesStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
Expand All @@ -24,11 +23,10 @@
</ResourceDictionary>
</vm:BasePropertiesPage.Resources>

<Grid Padding="12">
<Grid x:Name="RootGrid">

<Grid
Grid.Row="0"
Padding="12"
Margin="12"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
Expand All @@ -39,89 +37,78 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<TextBlock
Grid.Row="0"
Grid.Column="0"
Padding="4"
MaxLines="2"
Text="{helpers:ResourceString Name=ChooseCustomIcon}"
TextWrapping="WrapWholeWords" />
<Button
x:Name="RestoreDefaultButton"
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Right"
x:Load="{x:Bind CustomizationViewModel.IsShortcut, Converter={StaticResource BoolNegationConverter}}"
Command="{x:Bind CustomizationViewModel.RestoreDefaultIconCommand}"
Content="{helpers:ResourceString Name=RestoreDefault}"
IsEnabled="{x:Bind CustomizationViewModel.RestoreButtonIsEnabled, Mode=OneWay}" />

<MenuFlyoutSeparator

<Grid Grid.Row="0" Margin="12,12,12,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<TextBlock
Padding="4"
Text="{helpers:ResourceString Name=ChooseCustomIcon}"
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap" />

<Button
x:Name="RestoreDefaultButton"
Grid.Column="1"
x:Load="{x:Bind CustomizationViewModel.IsShortcut, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
Command="{x:Bind CustomizationViewModel.RestoreDefaultIconCommand, Mode=OneWay}"
Content="{helpers:ResourceString Name=RestoreDefault}"
IsEnabled="{x:Bind CustomizationViewModel.RestoreButtonIsEnabled, Mode=OneWay}" />

</Grid>

<Border
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Height="1"
Margin="-12,0"
Background="{ThemeResource CardStrokeColorDefaultBrush}" />
Background="{ThemeResource DividerStrokeColorDefaultBrush}" />

<Grid
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="12,0"
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<TextBox
x:Name="ItemDisplayedPath"
Grid.Column="0"
x:Name="PickedDllFilePathTextBox"
IsReadOnly="True"
Text="{x:Bind CustomizationViewModel.IconResourceItemPath, Mode=OneWay}" />

<Button
x:Name="PickDllButton"
x:Name="PickDllFileButton"
Grid.Column="1"
HorizontalAlignment="Right"
Click="PickDllButton_Click"
Command="{x:Bind CustomizationViewModel.PickDllFileCommand, Mode=OneWay}"
CommandParameter="{x:Bind XamlRoot, Mode=OneWay}"
Content="{helpers:ResourceString Name=Browse}" />

</Grid>

<GridView
x:Name="IconSelectionGrid"
Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="3"
MaxHeight="252"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Padding="12"
ItemsSource="{x:Bind CustomizationViewModel.DllIcons, Mode=OneWay}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollMode="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollMode="Auto"
SelectionChanged="IconSelectionGrid_SelectionChanged"
SelectionMode="Single">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
SelectedItem="{x:Bind CustomizationViewModel.SelectedDllIcon, Mode=TwoWay}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="shared:IconFileInfo">
<Grid Width="32" Height="32">
<Image uc:ImageFromBytes.SourceBytes="{x:Bind IconData}" />
</Grid>

<Image
Width="32"
Height="32"
uc:ImageFromBytes.SourceBytes="{x:Bind IconData}" />

</DataTemplate>
</GridView.ItemTemplate>
</GridView>

</Grid>

</Grid>
Expand Down
Loading