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 @@ + + + + + + + + + + +