feat: add ability to change app icon
Add ability to change application icon to any image file inside the src\Ryujinx\Assets\Icons\AppIcons directory. The app should automatically load any resource in that folder as a selectable icon.merge-requests/128/head
parent
462c93e1ff
commit
af0f8e2720
|
|
@ -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": {
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 189 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
|
|
@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -164,6 +164,7 @@
|
|||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConPair.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConRight.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\Controller_ProCon.svg" />
|
||||
<EmbeddedResource Include="Assets\Icons\AppIcons\*" />
|
||||
<EmbeddedResource Include="Assets\UIImages\Icon_NCA.png" />
|
||||
<EmbeddedResource Include="Assets\UIImages\Icon_NRO.png" />
|
||||
<EmbeddedResource Include="Assets\UIImages\Icon_NSO.png" />
|
||||
|
|
|
|||
|
|
@ -354,6 +354,11 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||
/// </summary>
|
||||
public string BaseStyle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the currently selected window icon
|
||||
/// </summary>
|
||||
public string SelectedWindowIcon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Chooses the view mode of the game list // Not Used
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -150,6 +150,11 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||
/// </summary>
|
||||
public ReactiveObject<string> BaseStyle { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected window icon.
|
||||
/// </summary>
|
||||
public ReactiveObject<string> SelectedWindowIcon { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Start games in fullscreen mode
|
||||
/// </summary>
|
||||
|
|
@ -199,6 +204,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||
ShownFileTypes = new ShownFileTypeSettings();
|
||||
WindowStartup = new WindowStartupSettings();
|
||||
BaseStyle = new ReactiveObject<string>();
|
||||
SelectedWindowIcon = new ReactiveObject<string>();
|
||||
StartFullscreen = new ReactiveObject<bool>();
|
||||
StartNoUI = new ReactiveObject<bool>();
|
||||
GameListViewMode = new ReactiveObject<int>();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<ApplicationIcon> AvailableApplicationIcons { get; set; } = [];
|
||||
private static void RetrieveAvailableAppIcons()
|
||||
{
|
||||
AvailableApplicationIcons.Clear();
|
||||
string resourceAssemblyPrefix = "Ryujinx.Assets.Icons.AppIcons.";
|
||||
|
||||
IEnumerable<string> 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<string> rArgs) => UpdateAppIcon(rArgs.NewValue);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ApplicationIcon> _appIcons = [];
|
||||
public AvaloniaList<ApplicationIcon> ApplicationIcons
|
||||
{
|
||||
get => _appIcons;
|
||||
set
|
||||
{
|
||||
_appIcons = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private int _appIconSelectedIndex;
|
||||
public int AppIconSelectedIndex
|
||||
{
|
||||
get => _appIconSelectedIndex;
|
||||
set
|
||||
{
|
||||
_appIconSelectedIndex = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,6 +156,28 @@
|
|||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
<TextBlock Classes="globalConfigMarker" IsVisible="{Binding IsGameTitleNotNull}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
IsEnabled="{Binding !IsGameTitleNotNull}"
|
||||
Opacity="{Binding PanelOpacity}"
|
||||
Margin="0, 15, 0, 10"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="{ext:Locale SettingsTabGeneralIcon}"
|
||||
Width="150" />
|
||||
<ComboBox
|
||||
MinWidth="100"
|
||||
HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding ApplicationIcons}"
|
||||
SelectedIndex="{Binding AppIconSelectedIndex}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<TextBlock Classes="globalConfigMarker" IsVisible="{Binding IsGameTitleNotNull}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
|
|
|||
Loading…
Reference in New Issue