Skip to content

Commit 10bfa4b

Browse files
authored
Feature: Added a theming option to display a background image (#15290)
1 parent b9fa67e commit 10bfa4b

File tree

7 files changed

+380
-0
lines changed

7 files changed

+380
-0
lines changed

src/Files.App/Services/Settings/AppearanceSettingsService.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the MIT License. See the LICENSE.
33

44
using Microsoft.AppCenter.Analytics;
5+
using Microsoft.UI.Xaml;
6+
using Microsoft.UI.Xaml.Media;
57

68
namespace Files.App.Services.Settings
79
{
@@ -74,6 +76,41 @@ public BackdropMaterialType AppThemeBackdropMaterial
7476
set => Set(value);
7577
}
7678

79+
/// <inheritdoc/>
80+
public string AppThemeBackgroundImageSource
81+
{
82+
get => Get("");
83+
set => Set(value);
84+
}
85+
86+
/// <inheritdoc/>
87+
public Stretch AppThemeBackgroundImageFit
88+
{
89+
get => Get(Stretch.UniformToFill);
90+
set => Set(value);
91+
}
92+
93+
/// <inheritdoc/>
94+
public float AppThemeBackgroundImageOpacity
95+
{
96+
get => Get(1f);
97+
set => Set(value);
98+
}
99+
100+
/// <inheritdoc/>
101+
public VerticalAlignment AppThemeBackgroundImageVerticalAlignment
102+
{
103+
get => Get(VerticalAlignment.Center);
104+
set => Set(value);
105+
}
106+
107+
/// <inheritdoc/>
108+
public HorizontalAlignment AppThemeBackgroundImageHorizontalAlignment
109+
{
110+
get => Get(HorizontalAlignment.Center);
111+
set => Set(value);
112+
}
113+
77114
protected override void RaiseOnSettingChangedEvent(object sender, SettingChangedEventArgs e)
78115
{
79116
switch (e.SettingName)
@@ -83,6 +120,10 @@ protected override void RaiseOnSettingChangedEvent(object sender, SettingChanged
83120
case nameof(AppThemeSidebarBackgroundColor):
84121
case nameof(AppThemeFileAreaBackgroundColor):
85122
case nameof(AppThemeBackdropMaterial):
123+
case nameof(AppThemeBackgroundImageFit):
124+
case nameof(AppThemeBackgroundImageOpacity):
125+
case nameof(AppThemeBackgroundImageVerticalAlignment):
126+
case nameof(AppThemeBackgroundImageHorizontalAlignment):
86127
Analytics.TrackEvent($"Set {e.SettingName} to {e.NewValue}");
87128
break;
88129
}

src/Files.App/Services/Settings/IAppearanceSettingsService.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Copyright (c) 2024 Files Community
22
// Licensed under the MIT License. See the LICENSE.
33

4+
using Microsoft.UI.Xaml;
5+
using Microsoft.UI.Xaml.Media;
6+
47
namespace Files.App.Services.Settings
58
{
69
public interface IAppearanceSettingsService : IBaseSettingsService, INotifyPropertyChanged
@@ -53,5 +56,30 @@ public interface IAppearanceSettingsService : IBaseSettingsService, INotifyPrope
5356
/// Gets or sets a value for the theme system backdrop.
5457
/// </summary>
5558
BackdropMaterialType AppThemeBackdropMaterial { get; set; }
59+
60+
/// <summary>
61+
/// Gets or sets a value for the app background image source
62+
/// </summary>
63+
string AppThemeBackgroundImageSource { get; set; }
64+
65+
/// <summary>
66+
/// Gets or sets a value for the app background image fit.
67+
/// </summary>
68+
Stretch AppThemeBackgroundImageFit { get; set; }
69+
70+
/// <summary>
71+
/// Gets or sets a value for the app background image opacity.
72+
/// </summary>
73+
float AppThemeBackgroundImageOpacity { get; set; }
74+
75+
/// <summary>
76+
/// Gets or sets a value for the app background image Vertical Alignment.
77+
/// </summary>
78+
VerticalAlignment AppThemeBackgroundImageVerticalAlignment { get; set; }
79+
80+
/// <summary>
81+
/// Gets or sets a value for the app background image Horizontal Alignment.
82+
/// </summary>
83+
HorizontalAlignment AppThemeBackgroundImageHorizontalAlignment { get; set; }
5684
}
5785
}

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3737,4 +3737,55 @@
37373737
<data name="AdaptiveLayoutDisabledNotification" xml:space="preserve">
37383738
<value>Adaptive layout is not available when preferences are synced, the default layout has been changed to Details.</value>
37393739
</data>
3740+
<data name="BackgroundImage" xml:space="preserve">
3741+
<value>Background image</value>
3742+
</data>
3743+
<data name="Bottom" xml:space="preserve">
3744+
<value>Bottom</value>
3745+
<comment>Image alignment type</comment>
3746+
</data>
3747+
<data name="Center" xml:space="preserve">
3748+
<value>Center</value>
3749+
<comment>Image alignment type</comment>
3750+
</data>
3751+
<data name="Fill" xml:space="preserve">
3752+
<value>Fill</value>
3753+
<comment>Image stretch type</comment>
3754+
</data>
3755+
<data name="HorizontalAlignment" xml:space="preserve">
3756+
<value>Horizontal alignment</value>
3757+
</data>
3758+
<data name="ImageFit" xml:space="preserve">
3759+
<value>Image fit</value>
3760+
</data>
3761+
<data name="Left" xml:space="preserve">
3762+
<value>Left</value>
3763+
<comment>Image alignment type</comment>
3764+
</data>
3765+
<data name="Opacity" xml:space="preserve">
3766+
<value>Opacity</value>
3767+
</data>
3768+
<data name="Right" xml:space="preserve">
3769+
<value>Right</value>
3770+
<comment>Image alignment type</comment>
3771+
</data>
3772+
<data name="Stretch" xml:space="preserve">
3773+
<value>Stretch</value>
3774+
<comment>Image alignment type</comment>
3775+
</data>
3776+
<data name="Top" xml:space="preserve">
3777+
<value>Top</value>
3778+
<comment>Image alignment type</comment>
3779+
</data>
3780+
<data name="Uniform" xml:space="preserve">
3781+
<value>Uniform</value>
3782+
<comment>Image stretch type</comment>
3783+
</data>
3784+
<data name="UniformToFill" xml:space="preserve">
3785+
<value>Uniform to fill</value>
3786+
<comment>Image stretch type</comment>
3787+
</data>
3788+
<data name="VerticalAlignment" xml:space="preserve">
3789+
<value>Vertical alignment</value>
3790+
</data>
37403791
</root>

src/Files.App/ViewModels/MainPageViewModel.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// Copyright (c) 2024 Files Community
22
// Licensed under the MIT License. See the LICENSE.
33

4+
using Microsoft.UI.Xaml;
45
using Microsoft.UI.Xaml.Input;
6+
using Microsoft.UI.Xaml.Media;
7+
using Microsoft.UI.Xaml.Media.Imaging;
58
using Microsoft.UI.Xaml.Navigation;
69
using System.Windows.Input;
710
using Windows.System;
@@ -57,6 +60,24 @@ public bool ShouldPreviewPaneBeDisplayed
5760
set => SetProperty(ref shouldPreviewPaneBeDisplayed, value);
5861
}
5962

63+
public Stretch AppThemeBackgroundImageFit
64+
=> AppearanceSettingsService.AppThemeBackgroundImageFit;
65+
66+
public float AppThemeBackgroundImageOpacity
67+
=> AppearanceSettingsService.AppThemeBackgroundImageOpacity;
68+
69+
public ImageSource? AppThemeBackgroundImageSource =>
70+
string.IsNullOrEmpty(AppearanceSettingsService.AppThemeBackgroundImageSource)
71+
? null
72+
: new BitmapImage(new Uri(AppearanceSettingsService.AppThemeBackgroundImageSource, UriKind.RelativeOrAbsolute));
73+
74+
public VerticalAlignment AppThemeBackgroundImageVerticalAlignment
75+
=> AppearanceSettingsService.AppThemeBackgroundImageVerticalAlignment;
76+
77+
public HorizontalAlignment AppThemeBackgroundImageHorizontalAlignment
78+
=> AppearanceSettingsService.AppThemeBackgroundImageHorizontalAlignment;
79+
80+
6081
// Commands
6182

6283
public ICommand NavigateToNumberedTabKeyboardAcceleratorCommand { get; }
@@ -66,6 +87,28 @@ public bool ShouldPreviewPaneBeDisplayed
6687
public MainPageViewModel()
6788
{
6889
NavigateToNumberedTabKeyboardAcceleratorCommand = new RelayCommand<KeyboardAcceleratorInvokedEventArgs>(ExecuteNavigateToNumberedTabKeyboardAcceleratorCommand);
90+
91+
AppearanceSettingsService.PropertyChanged += (s, e) =>
92+
{
93+
switch (e.PropertyName)
94+
{
95+
case nameof(AppearanceSettingsService.AppThemeBackgroundImageSource):
96+
OnPropertyChanged(nameof(AppThemeBackgroundImageSource));
97+
break;
98+
case nameof(AppearanceSettingsService.AppThemeBackgroundImageOpacity):
99+
OnPropertyChanged(nameof(AppThemeBackgroundImageOpacity));
100+
break;
101+
case nameof(AppearanceSettingsService.AppThemeBackgroundImageFit):
102+
OnPropertyChanged(nameof(AppThemeBackgroundImageFit));
103+
break;
104+
case nameof(AppearanceSettingsService.AppThemeBackgroundImageVerticalAlignment):
105+
OnPropertyChanged(nameof(AppThemeBackgroundImageVerticalAlignment));
106+
break;
107+
case nameof(AppearanceSettingsService.AppThemeBackgroundImageHorizontalAlignment):
108+
OnPropertyChanged(nameof(AppThemeBackgroundImageHorizontalAlignment));
109+
break;
110+
}
111+
};
69112
}
70113

71114
// Methods

src/Files.App/ViewModels/Settings/AppearanceViewModel.cs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
// Licensed under the MIT License. See the LICENSE.
33

44
using CommunityToolkit.WinUI.Helpers;
5+
using Microsoft.UI.Windowing;
56
using Microsoft.UI.Xaml;
7+
using Microsoft.UI.Xaml.Media;
8+
using System.Windows.Input;
9+
using Windows.Storage.Pickers;
10+
using Windows.UI.WindowManagement;
611

712
namespace Files.App.ViewModels.Settings
813
{
@@ -15,8 +20,17 @@ public sealed class AppearanceViewModel : ObservableObject
1520
public List<string> Themes { get; private set; }
1621
public Dictionary<BackdropMaterialType, string> BackdropMaterialTypes { get; private set; } = [];
1722

23+
public Dictionary<Stretch, string> ImageStretchTypes { get; private set; } = [];
24+
25+
public Dictionary<VerticalAlignment, string> ImageVerticalAlignmentTypes { get; private set; } = [];
26+
27+
public Dictionary<HorizontalAlignment, string> ImageHorizontalAlignmentTypes { get; private set; } = [];
28+
1829
public ObservableCollection<AppThemeResourceItem> AppThemeResources { get; }
1930

31+
public ICommand SelectImageCommand { get; }
32+
public ICommand RemoveImageCommand { get; }
33+
2034
public AppearanceViewModel(IUserSettingsService userSettingsService, IResourcesService resourcesService)
2135
{
2236
UserSettingsService = userSettingsService;
@@ -42,7 +56,64 @@ public AppearanceViewModel(IUserSettingsService userSettingsService, IResourcesS
4256

4357
AppThemeResources = AppThemeResourceFactory.AppThemeResources;
4458

59+
60+
// Background image fit options
61+
ImageStretchTypes.Add(Stretch.None, "None".GetLocalizedResource());
62+
ImageStretchTypes.Add(Stretch.Fill, "Fill".GetLocalizedResource());
63+
ImageStretchTypes.Add(Stretch.Uniform, "Uniform".GetLocalizedResource());
64+
ImageStretchTypes.Add(Stretch.UniformToFill, "UniformToFill".GetLocalizedResource());
65+
SelectedImageStretchType = ImageStretchTypes[UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageFit];
66+
67+
// Background image allignment options
68+
69+
// VerticalAlignment
70+
ImageVerticalAlignmentTypes.Add(VerticalAlignment.Top, "Top".GetLocalizedResource());
71+
ImageVerticalAlignmentTypes.Add(VerticalAlignment.Center, "Center".GetLocalizedResource());
72+
ImageVerticalAlignmentTypes.Add(VerticalAlignment.Bottom, "Bottom".GetLocalizedResource());
73+
ImageVerticalAlignmentTypes.Add(VerticalAlignment.Stretch, "Stretch".GetLocalizedResource());
74+
SelectedImageVerticalAlignmentType = ImageVerticalAlignmentTypes[UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageVerticalAlignment];
75+
76+
// HorizontalAlignment
77+
ImageHorizontalAlignmentTypes.Add(HorizontalAlignment.Left, "Top".GetLocalizedResource());
78+
ImageHorizontalAlignmentTypes.Add(HorizontalAlignment.Center, "Center".GetLocalizedResource());
79+
ImageHorizontalAlignmentTypes.Add(HorizontalAlignment.Right, "Bottom".GetLocalizedResource());
80+
ImageHorizontalAlignmentTypes.Add(HorizontalAlignment.Stretch, "Stretch".GetLocalizedResource());
81+
SelectedImageHorizontalAlignmentType = ImageHorizontalAlignmentTypes[UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageHorizontalAlignment];
82+
4583
UpdateSelectedResource();
84+
85+
SelectImageCommand = new AsyncRelayCommand(SelectBackgroundImage);
86+
RemoveImageCommand = new RelayCommand(RemoveBackgroundImage);
87+
}
88+
89+
/// <summary>
90+
/// Opens a file picker to select a background image
91+
/// </summary>
92+
private async Task SelectBackgroundImage()
93+
{
94+
var filePicker = new FileOpenPicker
95+
{
96+
ViewMode = PickerViewMode.Thumbnail,
97+
SuggestedStartLocation = PickerLocationId.PicturesLibrary,
98+
FileTypeFilter = { ".jpg", ".jpeg", ".png", ".bmp", ".gif", ".webp" }
99+
};
100+
101+
// WINUI3: Create and initialize new window
102+
var parentWindowId = MainWindow.Instance.AppWindow.Id;
103+
var handle = Microsoft.UI.Win32Interop.GetWindowFromWindowId(parentWindowId);
104+
WinRT.Interop.InitializeWithWindow.Initialize(filePicker, handle);
105+
106+
var file = await filePicker.PickSingleFileAsync();
107+
if (file is not null)
108+
AppThemeBackgroundImageSource = file.Path;
109+
}
110+
111+
/// <summary>
112+
/// Clears the current background image
113+
/// </summary>
114+
private void RemoveBackgroundImage()
115+
{
116+
AppThemeBackgroundImageSource = string.Empty;
46117
}
47118

48119
/// <summary>
@@ -143,5 +214,71 @@ public string SelectedBackdropMaterial
143214
}
144215
}
145216

217+
public string AppThemeBackgroundImageSource
218+
{
219+
get => UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageSource;
220+
set
221+
{
222+
if (value != UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageSource)
223+
{
224+
UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageSource = value;
225+
226+
OnPropertyChanged();
227+
}
228+
}
229+
}
230+
231+
private string selectedImageStretchType;
232+
public string SelectedImageStretchType
233+
{
234+
get => selectedImageStretchType;
235+
set
236+
{
237+
if (SetProperty(ref selectedImageStretchType, value))
238+
{
239+
UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageFit = ImageStretchTypes.First(e => e.Value == value).Key;
240+
}
241+
}
242+
}
243+
244+
public float AppThemeBackgroundImageOpacity
245+
{
246+
get => UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageOpacity;
247+
set
248+
{
249+
if (value != UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageOpacity)
250+
{
251+
UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageOpacity = value;
252+
253+
OnPropertyChanged();
254+
}
255+
}
256+
}
257+
258+
private string selectedImageVerticalAlignmentType;
259+
public string SelectedImageVerticalAlignmentType
260+
{
261+
get => selectedImageVerticalAlignmentType;
262+
set
263+
{
264+
if (SetProperty(ref selectedImageVerticalAlignmentType, value))
265+
{
266+
UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageVerticalAlignment = ImageVerticalAlignmentTypes.First(e => e.Value == value).Key;
267+
}
268+
}
269+
}
270+
271+
private string selectedImageHorizontalAlignmentType;
272+
public string SelectedImageHorizontalAlignmentType
273+
{
274+
get => selectedImageHorizontalAlignmentType;
275+
set
276+
{
277+
if (SetProperty(ref selectedImageHorizontalAlignmentType, value))
278+
{
279+
UserSettingsService.AppearanceSettingsService.AppThemeBackgroundImageHorizontalAlignment = ImageHorizontalAlignmentTypes.First(e => e.Value == value).Key;
280+
}
281+
}
282+
}
146283
}
147284
}

0 commit comments

Comments
 (0)