diff --git a/assets/locales.json b/assets/locales.json
index 2542c0f15..191388d08 100644
--- a/assets/locales.json
+++ b/assets/locales.json
@@ -12217,6 +12217,31 @@
"zh_TW": "淺色"
}
},
+ {
+ "ID": "SettingsTabGeneralIcon",
+ "Translations": {
+ "ar_SA": "",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "Application icon:",
+ "es_ES": "Icono de aplicación:",
+ "fr_FR": "",
+ "he_IL": "",
+ "it_IT": "",
+ "ja_JP": "",
+ "ko_KR": "",
+ "no_NO": "",
+ "pl_PL": "",
+ "pt_BR": "",
+ "ru_RU": "",
+ "sv_SE": "",
+ "th_TH": "",
+ "tr_TR": "",
+ "uk_UA": "",
+ "zh_CN": "",
+ "zh_TW": ""
+ }
+ },
{
"ID": "ControllerSettingsConfigureGeneral",
"Translations": {
diff --git a/src/Ryujinx/Assets/Icons/AppIcons/Classic Ryugay.ico b/src/Ryujinx/Assets/Icons/AppIcons/Classic Ryugay.ico
new file mode 100644
index 000000000..e1ac423e7
Binary files /dev/null and b/src/Ryujinx/Assets/Icons/AppIcons/Classic Ryugay.ico differ
diff --git a/src/Ryujinx/Assets/Icons/AppIcons/Ryufluid.webp b/src/Ryujinx/Assets/Icons/AppIcons/Ryufluid.webp
new file mode 100644
index 000000000..cd34ac32f
Binary files /dev/null and b/src/Ryujinx/Assets/Icons/AppIcons/Ryufluid.webp differ
diff --git a/src/Ryujinx/Assets/Icons/AppIcons/Ryugay.webp b/src/Ryujinx/Assets/Icons/AppIcons/Ryugay.webp
new file mode 100644
index 000000000..64a6748e6
Binary files /dev/null and b/src/Ryujinx/Assets/Icons/AppIcons/Ryugay.webp differ
diff --git a/src/Ryujinx/Assets/Icons/AppIcons/Ryulesbian.webp b/src/Ryujinx/Assets/Icons/AppIcons/Ryulesbian.webp
new file mode 100644
index 000000000..253773ab2
Binary files /dev/null and b/src/Ryujinx/Assets/Icons/AppIcons/Ryulesbian.webp differ
diff --git a/src/Ryujinx/Assets/Icons/AppIcons/Ryupan.webp b/src/Ryujinx/Assets/Icons/AppIcons/Ryupan.webp
new file mode 100644
index 000000000..5fb55aae5
Binary files /dev/null and b/src/Ryujinx/Assets/Icons/AppIcons/Ryupan.webp differ
diff --git a/src/Ryujinx/Assets/Icons/AppIcons/Ryutrans.webp b/src/Ryujinx/Assets/Icons/AppIcons/Ryutrans.webp
new file mode 100644
index 000000000..e6800d3ec
Binary files /dev/null and b/src/Ryujinx/Assets/Icons/AppIcons/Ryutrans.webp differ
diff --git a/src/Ryujinx/Common/Models/ApplicationIcon.cs b/src/Ryujinx/Common/Models/ApplicationIcon.cs
new file mode 100644
index 000000000..aa53d7e7d
--- /dev/null
+++ b/src/Ryujinx/Common/Models/ApplicationIcon.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.Ava.Common.Models
+{
+ public class ApplicationIcon
+ {
+ public string Name { get; set; }
+ public string Filename { get; set; }
+ public string FullPath
+ {
+ get => $"Ryujinx/Assets/Icons/AppIcons/{Filename}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj
index 480d14781..d37567c68 100644
--- a/src/Ryujinx/Ryujinx.csproj
+++ b/src/Ryujinx/Ryujinx.csproj
@@ -164,6 +164,7 @@
+
diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs
index 26ea73f73..6ac66a717 100644
--- a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs
+++ b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs
@@ -354,6 +354,11 @@ namespace Ryujinx.Ava.Systems.Configuration
///
public string BaseStyle { get; set; }
+ ///
+ /// The name of the currently selected window icon
+ ///
+ public string SelectedWindowIcon { get; set; }
+
///
/// Chooses the view mode of the game list // Not Used
///
diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs
index 57619aa70..7f9cb29e8 100644
--- a/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs
+++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs
@@ -129,6 +129,7 @@ namespace Ryujinx.Ava.Systems.Configuration
UI.ShownFileTypes.NSO.Value = shouldLoadFromFile ? cff.ShownFileTypes.NSO : UI.ShownFileTypes.NSO.Value;
UI.LanguageCode.Value = shouldLoadFromFile ? cff.LanguageCode : UI.LanguageCode.Value;
UI.BaseStyle.Value = shouldLoadFromFile ? cff.BaseStyle : UI.BaseStyle.Value;
+ UI.SelectedWindowIcon.Value = shouldLoadFromFile ? cff.SelectedWindowIcon : UI.SelectedWindowIcon.Value;
UI.GameListViewMode.Value = shouldLoadFromFile ? cff.GameListViewMode : UI.GameListViewMode.Value;
UI.ShowNames.Value = shouldLoadFromFile ? cff.ShowNames : UI.ShowNames.Value;
UI.IsAscendingOrder.Value = shouldLoadFromFile ? cff.IsAscendingOrder : UI.IsAscendingOrder.Value;
diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs
index bc8fdb40a..ddedf8754 100644
--- a/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs
+++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs
@@ -150,6 +150,11 @@ namespace Ryujinx.Ava.Systems.Configuration
///
public ReactiveObject BaseStyle { get; private set; }
+ ///
+ /// The currently selected window icon.
+ ///
+ public ReactiveObject SelectedWindowIcon { get; private set; }
+
///
/// Start games in fullscreen mode
///
@@ -199,6 +204,7 @@ namespace Ryujinx.Ava.Systems.Configuration
ShownFileTypes = new ShownFileTypeSettings();
WindowStartup = new WindowStartupSettings();
BaseStyle = new ReactiveObject();
+ SelectedWindowIcon = new ReactiveObject();
StartFullscreen = new ReactiveObject();
StartNoUI = new ReactiveObject();
GameListViewMode = new ReactiveObject();
diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs
index 185aedf64..565aea2e2 100644
--- a/src/Ryujinx/Systems/Configuration/ConfigurationState.cs
+++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs
@@ -126,6 +126,7 @@ namespace Ryujinx.Ava.Systems.Configuration
},
LanguageCode = UI.LanguageCode,
BaseStyle = UI.BaseStyle,
+ SelectedWindowIcon = UI.SelectedWindowIcon,
GameListViewMode = UI.GameListViewMode,
ShowNames = UI.ShowNames,
GridSize = UI.GridSize,
diff --git a/src/Ryujinx/UI/RyujinxApp.axaml.cs b/src/Ryujinx/UI/RyujinxApp.axaml.cs
index 34c2d96ca..ae8c03e72 100644
--- a/src/Ryujinx/UI/RyujinxApp.axaml.cs
+++ b/src/Ryujinx/UI/RyujinxApp.axaml.cs
@@ -2,12 +2,14 @@ using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input.Platform;
using Avalonia.Markup.Xaml;
+using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.Threading;
using FluentAvalonia.UI.Windowing;
using Gommon;
using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Views.Dialog;
@@ -16,7 +18,10 @@ using Ryujinx.Ava.Utilities;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using System;
+using System.Collections.Generic;
using System.Diagnostics;
+using System.IO;
+using System.Linq;
namespace Ryujinx.Ava
{
@@ -52,6 +57,8 @@ namespace Ryujinx.Ava
{
Name = FormatTitle();
+ RetrieveAvailableAppIcons();
+
AvaloniaXamlLoader.Load(this);
if (OperatingSystem.IsMacOS())
@@ -72,8 +79,10 @@ namespace Ryujinx.Ava
if (Program.PreviewerDetached)
{
ApplyConfiguredTheme(ConfigurationState.Instance.UI.BaseStyle);
-
ConfigurationState.Instance.UI.BaseStyle.Event += ThemeChanged_Event;
+
+ UpdateAppIcon(ConfigurationState.Instance.UI.SelectedWindowIcon);
+ ConfigurationState.Instance.UI.SelectedWindowIcon.Event += WindowIconChanged_Event;
}
}
@@ -151,5 +160,39 @@ namespace Ryujinx.Ava
{
await AboutView.Show();
}
+
+ public static List AvailableApplicationIcons { get; set; } = [];
+ private static void RetrieveAvailableAppIcons()
+ {
+ AvailableApplicationIcons.Clear();
+ string resourceAssemblyPrefix = "Ryujinx.Assets.Icons.AppIcons.";
+
+ IEnumerable availableAppIconResources = EmbeddedResources
+ .GetAllAvailableResources("Ryujinx/Assets")
+ .Where(x => x.StartsWith(resourceAssemblyPrefix));
+
+ foreach (string resource in availableAppIconResources)
+ {
+ string filename = resource.Remove(0, resourceAssemblyPrefix.Length);
+ string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filename);
+ AvailableApplicationIcons.Add(new ApplicationIcon()
+ {
+ Name = fileNameWithoutExtension,
+ Filename = filename
+ });
+ }
+ }
+
+ public static void UpdateAppIcon(string newIconName)
+ {
+ ApplicationIcon selectedIcon = AvailableApplicationIcons.First(x => x.Name == newIconName);
+ Stream activeIconStream = EmbeddedResources.GetStream(selectedIcon.FullPath);
+ if (activeIconStream != null)
+ {
+ MainWindow.Icon = new Bitmap(activeIconStream);
+ }
+ }
+
+ private void WindowIconChanged_Event(object _, ReactiveEventArgs rArgs) => UpdateAppIcon(rArgs.NewValue);
}
}
diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs
index 54fd951fb..dd580522e 100644
--- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs
+++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs
@@ -9,12 +9,14 @@ using Ryujinx.Audio.Backends.OpenAL;
using Ryujinx.Audio.Backends.SDL2;
using Ryujinx.Audio.Backends.SoundIo;
using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Ava.Systems.Configuration.System;
using Ryujinx.Ava.Systems.Configuration.UI;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.Windows;
+using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Common.GraphicsDriver;
@@ -496,6 +498,7 @@ namespace Ryujinx.Ava.UI.ViewModels
Task.Run(CheckSoundBackends);
Task.Run(PopulateNetworkInterfaces);
+ ApplicationIcons = new(RyujinxApp.AvailableApplicationIcons);
if (Program.PreviewerDetached)
{
@@ -621,6 +624,7 @@ namespace Ryujinx.Ava.UI.ViewModels
HideCursor = (int)config.HideCursor.Value;
UpdateCheckerType = (int)config.UpdateCheckerType.Value;
FocusLostActionType = (int)config.FocusLostActionType.Value;
+ AppIconSelectedIndex = _appIcons.ToList().FindIndex(x => x.Name == config.UI.SelectedWindowIcon.Value);
GameDirectories.Clear();
GameDirectories.AddRange(config.UI.GameDirs.Value);
@@ -740,6 +744,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.FocusLostActionType.Value = (FocusLostType)FocusLostActionType;
config.UI.GameDirs.Value = [.. GameDirectories];
config.UI.AutoloadDirs.Value = [.. AutoloadDirectories];
+ config.UI.SelectedWindowIcon.Value = _appIcons[_appIconSelectedIndex].Name;
config.UI.BaseStyle.Value = BaseStyleIndex switch
{
@@ -935,5 +940,27 @@ namespace Ryujinx.Ava.UI.ViewModels
RevertIfNotSaved();
CloseWindow?.Invoke();
}
+
+ private AvaloniaList _appIcons = [];
+ public AvaloniaList ApplicationIcons
+ {
+ get => _appIcons;
+ set
+ {
+ _appIcons = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private int _appIconSelectedIndex;
+ public int AppIconSelectedIndex
+ {
+ get => _appIconSelectedIndex;
+ set
+ {
+ _appIconSelectedIndex = value;
+ OnPropertyChanged();
+ }
+ }
}
}
diff --git a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml
index ad05efd06..6f3f77d88 100644
--- a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml
+++ b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml
@@ -156,6 +156,28 @@
+
+
+
+
+
+
+
+
+
+
+