Add the "Auto" theme option in setting (#6611)

* Add "Follow OS theme" option

* Update App.axaml.cs

* Add "Follow OS theme" option

* Update App.axaml.cs

* Remove `this`

* Remove annotation for nullable reference

* Change into switch expression to make it concise

* Change comments to XML docs

* Update en_US.json

* Fix icons in About dialog do not response to "auto" theme

The theme icons seemingly use Dark variant event when the OS theme is light. In addition, I added ThemeManager common to make it accessible for both App and AboutWindow

* Newline at the end

* newline moment

* Update ThemeManager.cs

* bait to switch to lf

* change to lf

* temp. revert

* Add back ThemeManager.cs common, pls pass the format check

* I found the mistake: should have put `ThemeManager.OnThemeChanged();` in try block

Finally solve the formatting check

* test formatting

* Update App.axaml.cs

* Ok i seem to forget to add version lol

* Fix info CA1816
This commit is contained in:
yell0wsuit 2024-05-14 22:00:03 +07:00 committed by GitHub
parent 075575200d
commit 2b6cc4b353
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 128 additions and 21 deletions

View file

@ -1,8 +1,10 @@
using Avalonia; using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Platform;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
@ -84,7 +86,7 @@ namespace Ryujinx.Ava
ApplyConfiguredTheme(); ApplyConfiguredTheme();
} }
private void ApplyConfiguredTheme() public void ApplyConfiguredTheme()
{ {
try try
{ {
@ -92,13 +94,18 @@ namespace Ryujinx.Ava
if (string.IsNullOrWhiteSpace(baseStyle)) if (string.IsNullOrWhiteSpace(baseStyle))
{ {
ConfigurationState.Instance.UI.BaseStyle.Value = "Dark"; ConfigurationState.Instance.UI.BaseStyle.Value = "Auto";
baseStyle = ConfigurationState.Instance.UI.BaseStyle; baseStyle = ConfigurationState.Instance.UI.BaseStyle;
} }
ThemeVariant systemTheme = DetectSystemTheme();
ThemeManager.OnThemeChanged();
RequestedThemeVariant = baseStyle switch RequestedThemeVariant = baseStyle switch
{ {
"Auto" => systemTheme,
"Light" => ThemeVariant.Light, "Light" => ThemeVariant.Light,
"Dark" => ThemeVariant.Dark, "Dark" => ThemeVariant.Dark,
_ => ThemeVariant.Default, _ => ThemeVariant.Default,
@ -111,5 +118,28 @@ namespace Ryujinx.Ava
ShowRestartDialog(); ShowRestartDialog();
} }
} }
/// <summary>
/// Converts a PlatformThemeVariant value to the corresponding ThemeVariant value.
/// </summary>
public static ThemeVariant ConvertThemeVariant(PlatformThemeVariant platformThemeVariant) =>
platformThemeVariant switch
{
PlatformThemeVariant.Dark => ThemeVariant.Dark,
PlatformThemeVariant.Light => ThemeVariant.Light,
_ => ThemeVariant.Default,
};
public static ThemeVariant DetectSystemTheme()
{
if (Application.Current is App app)
{
var colorValues = app.PlatformSettings.GetColorValues();
return ConvertThemeVariant(colorValues.ThemeVariant);
}
return ThemeVariant.Default;
}
} }
} }

View file

@ -404,6 +404,7 @@
"GameListContextMenuToggleFavorite": "Toggle Favorite", "GameListContextMenuToggleFavorite": "Toggle Favorite",
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game", "GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
"SettingsTabGeneralTheme": "Theme:", "SettingsTabGeneralTheme": "Theme:",
"SettingsTabGeneralThemeAuto": "Auto",
"SettingsTabGeneralThemeDark": "Dark", "SettingsTabGeneralThemeDark": "Dark",
"SettingsTabGeneralThemeLight": "Light", "SettingsTabGeneralThemeLight": "Light",
"ControllerSettingsConfigureGeneral": "Configure", "ControllerSettingsConfigureGeneral": "Configure",

View file

@ -0,0 +1,14 @@
using System;
namespace Ryujinx.Ava.Common
{
public static class ThemeManager
{
public static event EventHandler ThemeChanged;
public static void OnThemeChanged()
{
ThemeChanged?.Invoke(null, EventArgs.Empty);
}
}
}

View file

@ -1,6 +1,8 @@
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Configuration;
@ -11,7 +13,7 @@ using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public class AboutWindowViewModel : BaseModel public class AboutWindowViewModel : BaseModel, IDisposable
{ {
private Bitmap _githubLogo; private Bitmap _githubLogo;
private Bitmap _discordLogo; private Bitmap _discordLogo;
@ -86,23 +88,39 @@ namespace Ryujinx.Ava.UI.ViewModels
public AboutWindowViewModel() public AboutWindowViewModel()
{ {
Version = Program.Version; Version = Program.Version;
UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value);
if (ConfigurationState.Instance.UI.BaseStyle.Value == "Light")
{
GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_GitHub_Light.png?assembly=Ryujinx.UI.Common")));
DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Discord_Light.png?assembly=Ryujinx.UI.Common")));
PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Patreon_Light.png?assembly=Ryujinx.UI.Common")));
TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.UI.Common")));
}
else
{
GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_GitHub_Dark.png?assembly=Ryujinx.UI.Common")));
DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Discord_Dark.png?assembly=Ryujinx.UI.Common")));
PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Patreon_Dark.png?assembly=Ryujinx.UI.Common")));
TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Twitter_Dark.png?assembly=Ryujinx.UI.Common")));
}
Dispatcher.UIThread.InvokeAsync(DownloadPatronsJson); Dispatcher.UIThread.InvokeAsync(DownloadPatronsJson);
ThemeManager.ThemeChanged += ThemeManager_ThemeChanged;
}
private void ThemeManager_ThemeChanged(object sender, EventArgs e)
{
Dispatcher.UIThread.Post(() => UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value));
}
private void UpdateLogoTheme(string theme)
{
bool isDarkTheme = theme == "Dark" || (theme == "Auto" && App.DetectSystemTheme() == ThemeVariant.Dark);
string basePath = "resm:Ryujinx.UI.Common.Resources.";
string themeSuffix = isDarkTheme ? "Dark.png" : "Light.png";
GithubLogo = LoadBitmap($"{basePath}Logo_GitHub_{themeSuffix}?assembly=Ryujinx.UI.Common");
DiscordLogo = LoadBitmap($"{basePath}Logo_Discord_{themeSuffix}?assembly=Ryujinx.UI.Common");
PatreonLogo = LoadBitmap($"{basePath}Logo_Patreon_{themeSuffix}?assembly=Ryujinx.UI.Common");
TwitterLogo = LoadBitmap($"{basePath}Logo_Twitter_{themeSuffix}?assembly=Ryujinx.UI.Common");
}
private Bitmap LoadBitmap(string uri)
{
return new Bitmap(Avalonia.Platform.AssetLoader.Open(new Uri(uri)));
}
public void Dispose()
{
ThemeManager.ThemeChanged -= ThemeManager_ThemeChanged;
GC.SuppressFinalize(this);
} }
private async Task DownloadPatronsJson() private async Task DownloadPatronsJson()

View file

@ -397,7 +397,13 @@ namespace Ryujinx.Ava.UI.ViewModels
GameDirectories.Clear(); GameDirectories.Clear();
GameDirectories.AddRange(config.UI.GameDirs.Value); GameDirectories.AddRange(config.UI.GameDirs.Value);
BaseStyleIndex = config.UI.BaseStyle == "Light" ? 0 : 1; BaseStyleIndex = config.UI.BaseStyle.Value switch
{
"Auto" => 0,
"Light" => 1,
"Dark" => 2,
_ => 0
};
// Input // Input
EnableDockedMode = config.System.EnableDockedMode; EnableDockedMode = config.System.EnableDockedMode;
@ -486,7 +492,13 @@ namespace Ryujinx.Ava.UI.ViewModels
config.UI.GameDirs.Value = gameDirs; config.UI.GameDirs.Value = gameDirs;
} }
config.UI.BaseStyle.Value = BaseStyleIndex == 0 ? "Light" : "Dark"; config.UI.BaseStyle.Value = BaseStyleIndex switch
{
0 => "Auto",
1 => "Light",
2 => "Dark",
_ => "Auto"
};
// Input // Input
config.System.EnableDockedMode.Value = EnableDockedMode; config.System.EnableDockedMode.Value = EnableDockedMode;

View file

@ -65,6 +65,9 @@
<ComboBox SelectedIndex="{Binding BaseStyleIndex}" <ComboBox SelectedIndex="{Binding BaseStyleIndex}"
HorizontalContentAlignment="Left" HorizontalContentAlignment="Left"
MinWidth="100"> MinWidth="100">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeAuto}" />
</ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeLight}" /> <TextBlock Text="{locale:Locale SettingsTabGeneralThemeLight}" />
</ComboBoxItem> </ComboBoxItem>

View file

@ -2,6 +2,7 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Platform;
using Avalonia.Threading; using Avalonia.Threading;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common; using Ryujinx.Ava.Common;
@ -92,6 +93,29 @@ namespace Ryujinx.Ava.UI.Windows
} }
} }
/// <summary>
/// Event handler for detecting OS theme change when using "Follow OS theme" option
/// </summary>
private void OnPlatformColorValuesChanged(object sender, PlatformColorValues e)
{
if (Application.Current is App app)
{
app.ApplyConfiguredTheme();
}
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
if (PlatformSettings != null)
{
/// <summary>
/// Unsubscribe to the ColorValuesChanged event
/// </summary>
PlatformSettings.ColorValuesChanged -= OnPlatformColorValuesChanged;
}
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{ {
base.OnApplyTemplate(e); base.OnApplyTemplate(e);
@ -390,6 +414,11 @@ namespace Ryujinx.Ava.UI.Windows
Initialize(); Initialize();
/// <summary>
/// Subscribe to the ColorValuesChanged event
/// </summary>
PlatformSettings.ColorValuesChanged += OnPlatformColorValuesChanged;
ViewModel.Initialize( ViewModel.Initialize(
ContentManager, ContentManager,
StorageProvider, StorageProvider,