Skip to content

Commit 99ba675

Browse files
authored
Feature: Added support for assigning keyboard shortcuts to change the app theme (#18372)
1 parent 3dcf9a2 commit 99ba675

8 files changed

Lines changed: 207 additions & 0 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.UI.Xaml;
5+
6+
namespace Files.App.Actions
7+
{
8+
internal abstract class BaseAppThemeAction : ObservableObject
9+
{
10+
protected IAppThemeModeService AppThemeModeService { get; } = Ioc.Default.GetRequiredService<IAppThemeModeService>();
11+
12+
protected BaseAppThemeAction()
13+
{
14+
AppThemeModeService.AppThemeModeChanged += AppThemeModeService_AppThemeModeChanged;
15+
}
16+
17+
protected static ElementTheme ResolveSystemThemeFallback()
18+
=> Application.Current.RequestedTheme is ApplicationTheme.Dark
19+
? ElementTheme.Dark
20+
: ElementTheme.Light;
21+
22+
protected ElementTheme GetEffectiveTheme()
23+
{
24+
var requestedTheme = AppThemeModeService.AppThemeMode;
25+
if (requestedTheme is ElementTheme.Light or ElementTheme.Dark)
26+
return requestedTheme;
27+
28+
if (MainWindow.Instance?.Content is FrameworkElement rootElement)
29+
{
30+
var actualTheme = rootElement.ActualTheme;
31+
if (actualTheme is ElementTheme.Light or ElementTheme.Dark)
32+
return actualTheme;
33+
}
34+
35+
return ResolveSystemThemeFallback();
36+
}
37+
38+
protected void SetTheme(ElementTheme appTheme)
39+
{
40+
AppThemeModeService.AppThemeMode = appTheme;
41+
}
42+
43+
private void AppThemeModeService_AppThemeModeChanged(object? sender, EventArgs e)
44+
{
45+
OnPropertyChanged(nameof(IAction.IsExecutable));
46+
}
47+
}
48+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.UI.Xaml;
5+
6+
namespace Files.App.Actions
7+
{
8+
[GeneratedRichCommand]
9+
internal sealed partial class SetDarkThemeAction : BaseAppThemeAction, IAction
10+
{
11+
public string Label
12+
=> $"{Strings.DarkTheme.GetLocalizedResource()} Theme";
13+
14+
public string Description
15+
=> Strings.SwitchToDarkThemeDescription.GetLocalizedResource();
16+
17+
public RichGlyph Glyph
18+
=> new("\uE790");
19+
20+
public ActionCategory Category
21+
=> ActionCategory.Theme;
22+
23+
public bool IsExecutable
24+
=> AppThemeModeService.AppThemeMode is not ElementTheme.Dark;
25+
26+
public Task ExecuteAsync(object? parameter = null)
27+
{
28+
SetTheme(ElementTheme.Dark);
29+
return Task.CompletedTask;
30+
}
31+
}
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.UI.Xaml;
5+
6+
namespace Files.App.Actions
7+
{
8+
[GeneratedRichCommand]
9+
internal sealed partial class SetDefaultThemeAction : BaseAppThemeAction, IAction
10+
{
11+
public string Label
12+
=> Strings.DefaultTheme.GetLocalizedResource();
13+
14+
public string Description
15+
=> Strings.SwitchToDefaultThemeDescription.GetLocalizedResource();
16+
17+
public RichGlyph Glyph
18+
=> new("\uE790");
19+
20+
public ActionCategory Category
21+
=> ActionCategory.Theme;
22+
23+
public bool IsExecutable
24+
=> AppThemeModeService.AppThemeMode is not ElementTheme.Default;
25+
26+
public Task ExecuteAsync(object? parameter = null)
27+
{
28+
SetTheme(ElementTheme.Default);
29+
return Task.CompletedTask;
30+
}
31+
}
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.UI.Xaml;
5+
6+
namespace Files.App.Actions
7+
{
8+
[GeneratedRichCommand]
9+
internal sealed partial class SetLightThemeAction : BaseAppThemeAction, IAction
10+
{
11+
public string Label
12+
=> $"{Strings.LightTheme.GetLocalizedResource()} Theme";
13+
14+
public string Description
15+
=> Strings.SwitchToLightThemeDescription.GetLocalizedResource();
16+
17+
public RichGlyph Glyph
18+
=> new("\uE790");
19+
20+
public ActionCategory Category
21+
=> ActionCategory.Theme;
22+
23+
public bool IsExecutable
24+
=> AppThemeModeService.AppThemeMode is not ElementTheme.Light;
25+
26+
public Task ExecuteAsync(object? parameter = null)
27+
{
28+
SetTheme(ElementTheme.Light);
29+
return Task.CompletedTask;
30+
}
31+
}
32+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.UI.Xaml;
5+
6+
namespace Files.App.Actions
7+
{
8+
[GeneratedRichCommand]
9+
internal sealed partial class ToggleAppThemeAction : BaseAppThemeAction, IAction
10+
{
11+
public string Label
12+
=> Strings.ToggleTheme.GetLocalizedResource();
13+
14+
public string Description
15+
=> Strings.ToggleThemeDescription.GetLocalizedResource();
16+
17+
public HotKey HotKey
18+
=> new(Keys.T, KeyModifiers.CtrlAlt);
19+
20+
public RichGlyph Glyph
21+
=> new("\uE790");
22+
23+
public ActionCategory Category
24+
=> ActionCategory.Theme;
25+
26+
public Task ExecuteAsync(object? parameter = null)
27+
{
28+
var selectedTheme = AppThemeModeService.AppThemeMode;
29+
var nextTheme = selectedTheme switch
30+
{
31+
ElementTheme.Light => ElementTheme.Dark,
32+
ElementTheme.Dark => ElementTheme.Light,
33+
ElementTheme.Default => GetEffectiveTheme() is ElementTheme.Dark
34+
? ElementTheme.Light
35+
: ElementTheme.Dark,
36+
_ => ElementTheme.Light,
37+
};
38+
39+
SetTheme(nextTheme);
40+
return Task.CompletedTask;
41+
}
42+
}
43+
}

src/Files.App/Converters/ActionCategoryConverter.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public static string ToLocalizedCategoryPath(ActionCategory category)
2727
ActionCategory.Sorting => Strings.SortBy.GetLocalizedResource(),
2828
ActionCategory.Start => Strings.Start.GetLocalizedResource(),
2929
ActionCategory.Window => Strings.Window.GetLocalizedResource(),
30+
ActionCategory.Theme => Strings.SettingsAppearanceTheme.GetLocalizedResource(),
3031
ActionCategory.DualPane => Strings.DualPaneCategory.GetLocalizedResource(),
3132
_ => Strings.General.GetLocalizedResource(),
3233
};

src/Files.App/Data/Enums/ActionCategory.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public enum ActionCategory
2525
Sorting,
2626
Start,
2727
Window,
28+
Theme,
2829
DualPane,
2930
}
3031
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,9 @@
309309
<data name="DarkTheme" xml:space="preserve">
310310
<value>Dark</value>
311311
</data>
312+
<data name="DefaultTheme" xml:space="preserve">
313+
<value>System Theme</value>
314+
</data>
312315
<data name="UseSystemSetting" xml:space="preserve">
313316
<value>Use system setting</value>
314317
</data>
@@ -2453,6 +2456,21 @@
24532456
<data name="ToggleCompactOverlayDescription" xml:space="preserve">
24542457
<value>Toggle compact overlay mode</value>
24552458
</data>
2459+
<data name="ToggleTheme" xml:space="preserve">
2460+
<value>Toggle Theme</value>
2461+
</data>
2462+
<data name="ToggleThemeDescription" xml:space="preserve">
2463+
<value>Toggle the app theme</value>
2464+
</data>
2465+
<data name="SwitchToLightThemeDescription" xml:space="preserve">
2466+
<value>Switch to Light Theme</value>
2467+
</data>
2468+
<data name="SwitchToDarkThemeDescription" xml:space="preserve">
2469+
<value>Switch to Dark Theme</value>
2470+
</data>
2471+
<data name="SwitchToDefaultThemeDescription" xml:space="preserve">
2472+
<value>Switch to System Theme</value>
2473+
</data>
24562474
<data name="SearchDescription" xml:space="preserve">
24572475
<value>Start search in the OmniBar</value>
24582476
</data>

0 commit comments

Comments
 (0)