refactor: completely overhaul app icon management for cleaner workflow
parent
ab4567d0a2
commit
362fbf08a2
|
|
@ -1,28 +1,84 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Ryujinx.Ava.Common.Models;
|
||||
using Ryujinx.Ava.Systems.Configuration;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using System.Reflection;
|
||||
using Ryujinx.Common;
|
||||
using Svg.Skia;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public class RyujinxLogo : Image
|
||||
{
|
||||
// The UI specifically uses a thicker bordered variant of the icon to avoid crunching out the border at lower resolutions.
|
||||
// For an example of this, download canary 1.2.95, then open the settings menu, and look at the icon in the top-left.
|
||||
// The border gets reduced to colored pixels in the 4 corners.
|
||||
public static readonly Bitmap Bitmap =
|
||||
new(Assembly.GetAssembly(typeof(MainWindowViewModel))!
|
||||
.GetManifestResourceStream("Ryujinx.Assets.UIImages.Logo_Ryujinx_AntiAlias.png")!);
|
||||
public static ReactiveObject<Bitmap> CurrentLogoBitmap { get; private set; } = new();
|
||||
|
||||
public RyujinxLogo()
|
||||
{
|
||||
Margin = new Thickness(7, 7, 7, 0);
|
||||
Height = 25;
|
||||
Width = 25;
|
||||
Source = Bitmap;
|
||||
Source = CurrentLogoBitmap.Value;
|
||||
IsVisible = !ConfigurationState.Instance.ShowOldUI;
|
||||
ConfigurationState.Instance.UI.SelectedWindowIcon.Event += WindowIconChanged_Event;
|
||||
}
|
||||
|
||||
public static void RefreshAppIconFromSettings()
|
||||
{
|
||||
SetNewAppIcon(ConfigurationState.Instance.UI.SelectedWindowIcon.Value);
|
||||
}
|
||||
|
||||
private static void SetNewAppIcon(string newIconName)
|
||||
{
|
||||
string defaultIconName = "Bordered Ryupride";
|
||||
if (string.IsNullOrEmpty(newIconName))
|
||||
{
|
||||
SetDefaultAppIcon(defaultIconName);
|
||||
}
|
||||
|
||||
ApplicationIcon selectedIcon = RyujinxApp.AvailableApplicationIcons.FirstOrDefault(x => x.Name == newIconName);
|
||||
if (selectedIcon == null)
|
||||
{
|
||||
// Always try to fallback to "Bordered Ryupride" as a default
|
||||
// If not found, fallback to first found icon
|
||||
if (newIconName != defaultIconName)
|
||||
{
|
||||
SetDefaultAppIcon(defaultIconName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (RyujinxApp.AvailableApplicationIcons.Count > 0)
|
||||
{
|
||||
SetDefaultAppIcon(RyujinxApp.AvailableApplicationIcons.First().Name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Stream activeIconStream = EmbeddedResources.GetStream(selectedIcon.FullPath);
|
||||
if (activeIconStream != null)
|
||||
{
|
||||
// SVG files need to be converted to an image first
|
||||
if (selectedIcon.FullPath.EndsWith(".svg", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// TODO: Convert SVG to a bitmap
|
||||
using SKSvg svg = new();
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentLogoBitmap.Value = new Bitmap(activeIconStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetDefaultAppIcon(string defaultIconName)
|
||||
{
|
||||
// Doing this triggers the WindowIconChanged_Event, which will then
|
||||
// call SetNewAppIcon again
|
||||
ConfigurationState.Instance.UI.SelectedWindowIcon.Value = defaultIconName;
|
||||
}
|
||||
|
||||
private void WindowIconChanged_Event(object _, ReactiveEventArgs<string> rArgs) => SetNewAppIcon(rArgs.NewValue);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ 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;
|
||||
|
|
@ -11,6 +10,7 @@ using Gommon;
|
|||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Common.Models;
|
||||
using Ryujinx.Ava.Systems.Configuration;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Views.Dialog;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
|
|
@ -58,6 +58,7 @@ namespace Ryujinx.Ava
|
|||
Name = FormatTitle();
|
||||
|
||||
RetrieveAvailableAppIcons();
|
||||
RyujinxLogo.RefreshAppIconFromSettings();
|
||||
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
|
|
@ -80,9 +81,6 @@ namespace Ryujinx.Ava
|
|||
{
|
||||
ApplyConfiguredTheme(ConfigurationState.Instance.UI.BaseStyle);
|
||||
ConfigurationState.Instance.UI.BaseStyle.Event += ThemeChanged_Event;
|
||||
|
||||
UpdateAppIcon(ConfigurationState.Instance.UI.SelectedWindowIcon);
|
||||
ConfigurationState.Instance.UI.SelectedWindowIcon.Event += WindowIconChanged_Event;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -182,28 +180,5 @@ namespace Ryujinx.Ava
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateAppIcon(string newIconName)
|
||||
{
|
||||
if (newIconName.IsNullOrEmpty())
|
||||
{
|
||||
newIconName = "Classic Ryugay";
|
||||
}
|
||||
|
||||
ApplicationIcon selectedIcon = AvailableApplicationIcons.FirstOrDefault(x => x.Name == newIconName);
|
||||
if (selectedIcon == null)
|
||||
{
|
||||
// Fallback to default icon if the selected one is not found
|
||||
UpdateAppIcon("Classic Ryugay");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ using Avalonia.Controls;
|
|||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform;
|
||||
using FluentAvalonia.UI.Windowing;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Systems.Configuration;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Common;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Windows
|
||||
|
|
@ -39,7 +41,8 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
TitleBar.Height = titleBarHeight.Value;
|
||||
}
|
||||
|
||||
Icon = RyujinxLogo.Bitmap;
|
||||
Icon = RyujinxLogo.CurrentLogoBitmap.Value;
|
||||
RyujinxLogo.CurrentLogoBitmap.Event += WindowIconChanged_Event;
|
||||
}
|
||||
|
||||
private void LocaleChanged()
|
||||
|
|
@ -53,6 +56,12 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
|
||||
ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.SystemChrome | ExtendClientAreaChromeHints.OSXThickTitleBar;
|
||||
}
|
||||
|
||||
private void WindowIconChanged_Event(object _, ReactiveEventArgs<Bitmap> rArgs) => UpdateIcon(rArgs.NewValue);
|
||||
private void UpdateIcon(Bitmap newIcon)
|
||||
{
|
||||
Icon = newIcon;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class StyleableWindow : Window
|
||||
|
|
@ -73,7 +82,8 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
LocaleManager.Instance.LocaleChanged += LocaleChanged;
|
||||
LocaleChanged();
|
||||
|
||||
Icon = new WindowIcon(RyujinxLogo.Bitmap);
|
||||
Icon = new WindowIcon(RyujinxLogo.CurrentLogoBitmap.Value);
|
||||
RyujinxLogo.CurrentLogoBitmap.Event += WindowIconChanged_Event;
|
||||
}
|
||||
|
||||
private void LocaleChanged()
|
||||
|
|
@ -87,5 +97,11 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
|
||||
ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.SystemChrome | ExtendClientAreaChromeHints.OSXThickTitleBar;
|
||||
}
|
||||
|
||||
private void WindowIconChanged_Event(object _, ReactiveEventArgs<Bitmap> rArgs) => UpdateIcon(rArgs.NewValue);
|
||||
private void UpdateIcon(Bitmap newIcon)
|
||||
{
|
||||
Icon = new WindowIcon(newIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue