misc: Canary-specific naming & other small changes I had that I need to push.

This commit is contained in:
Evan Husted 2024-11-06 18:23:21 -06:00
parent 36c374cc7a
commit 730ba44043
11 changed files with 200 additions and 229 deletions

View file

@ -23,8 +23,10 @@ namespace Ryujinx.Ava
{ {
internal static string FormatTitle(LocaleKeys? windowTitleKey = null) internal static string FormatTitle(LocaleKeys? windowTitleKey = null)
=> windowTitleKey is null => windowTitleKey is null
? $"Ryujinx {Program.Version}" ? $"{FullAppName} {Program.Version}"
: $"Ryujinx {Program.Version} - {LocaleManager.Instance[windowTitleKey.Value]}"; : $"{FullAppName} {Program.Version} - {LocaleManager.Instance[windowTitleKey.Value]}";
public static readonly string FullAppName = ReleaseInformation.IsCanaryBuild ? "Ryujinx Canary" : "Ryujinx";
public static MainWindow MainWindow => Current! public static MainWindow MainWindow => Current!
.ApplicationLifetime.Cast<IClassicDesktopStyleApplicationLifetime>() .ApplicationLifetime.Cast<IClassicDesktopStyleApplicationLifetime>()

View file

@ -2,19 +2,38 @@
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.MarkupExtensions; using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings; using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings;
using Gommon;
using System; using System;
// ReSharper disable VirtualMemberNeverOverridden.Global
// ReSharper disable MemberCanBeProtected.Global
// ReSharper disable MemberCanBePrivate.Global
#nullable enable
namespace Ryujinx.Ava.Common.Markup namespace Ryujinx.Ava.Common.Markup
{ {
internal abstract class BasicMarkupExtension : MarkupExtension internal abstract class BasicMarkupExtension<T> : MarkupExtension
{ {
protected abstract ClrPropertyInfo PropertyInfo { get; } public virtual string Name => "Item";
public virtual Action<object, T?>? Setter => null;
public override object ProvideValue(IServiceProvider serviceProvider) => protected abstract T? GetValue();
new CompiledBindingExtension(
protected virtual void ConfigureBindingExtension(CompiledBindingExtension _) { }
private ClrPropertyInfo PropertyInfo =>
new(Name,
_ => GetValue(),
Setter as Action<object, object?>,
typeof(T));
public override object ProvideValue(IServiceProvider serviceProvider)
=> new CompiledBindingExtension(
new CompiledBindingPathBuilder() new CompiledBindingPathBuilder()
.Property(PropertyInfo, PropertyInfoAccessorFactory.CreateInpcPropertyAccessor) .Property(PropertyInfo, PropertyInfoAccessorFactory.CreateInpcPropertyAccessor)
.Build() .Build()
).ProvideValue(serviceProvider); )
.Apply(ConfigureBindingExtension)
.ProvideValue(serviceProvider);
} }
} }

View file

@ -1,51 +1,24 @@
using Avalonia.Data.Core;
using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.MarkupExtensions; using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings;
using Projektanker.Icons.Avalonia; using Projektanker.Icons.Avalonia;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using System;
namespace Ryujinx.Ava.Common.Markup namespace Ryujinx.Ava.Common.Markup
{ {
internal class IconExtension(string iconString) : BasicMarkupExtension internal class IconExtension(string iconString) : BasicMarkupExtension<Icon>
{ {
protected override ClrPropertyInfo PropertyInfo protected override Icon GetValue() => new() { Value = iconString };
=> new(
"Item",
_ => new Icon { Value = iconString },
null,
typeof(Icon)
);
} }
internal class SpinningIconExtension(string iconString) : BasicMarkupExtension internal class SpinningIconExtension(string iconString) : BasicMarkupExtension<Icon>
{ {
protected override ClrPropertyInfo PropertyInfo protected override Icon GetValue() => new() { Value = iconString, Animation = IconAnimation.Spin };
=> new(
"Item",
_ => new Icon { Value = iconString, Animation = IconAnimation.Spin },
null,
typeof(Icon)
);
} }
internal class LocaleExtension(LocaleKeys key) : MarkupExtension internal class LocaleExtension(LocaleKeys key) : BasicMarkupExtension<string>
{ {
private ClrPropertyInfo PropertyInfo protected override string GetValue() => LocaleManager.Instance[key];
=> new(
"Item",
_ => LocaleManager.Instance[key],
null,
typeof(string)
);
public override object ProvideValue(IServiceProvider serviceProvider) => protected override void ConfigureBindingExtension(CompiledBindingExtension bindingExtension)
new CompiledBindingExtension( => bindingExtension.Source = LocaleManager.Instance;
new CompiledBindingPathBuilder()
.Property(PropertyInfo, PropertyInfoAccessorFactory.CreateInpcPropertyAccessor)
.Build()
) { Source = LocaleManager.Instance }
.ProvideValue(serviceProvider);
} }
} }

View file

@ -23,21 +23,15 @@ namespace Ryujinx.Ava.Input
public bool IsConnected => true; public bool IsConnected => true;
public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None; public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None;
private class ButtonMappingEntry private class ButtonMappingEntry(GamepadButtonInputId to, Key from)
{ {
public readonly Key From; public readonly GamepadButtonInputId To = to;
public readonly GamepadButtonInputId To; public readonly Key From = from;
public ButtonMappingEntry(GamepadButtonInputId to, Key from)
{
To = to;
From = from;
}
} }
public AvaloniaKeyboard(AvaloniaKeyboardDriver driver, string id, string name) public AvaloniaKeyboard(AvaloniaKeyboardDriver driver, string id, string name)
{ {
_buttonsUserMapping = new List<ButtonMappingEntry>(); _buttonsUserMapping = [];
_driver = driver; _driver = driver;
Id = id; Id = id;

View file

@ -127,11 +127,11 @@ namespace Ryujinx.Ava.UI.Applet
try try
{ {
_parent.ViewModel.AppHost.NpadManager.BlockInputUpdates(); _parent.ViewModel.AppHost.NpadManager.BlockInputUpdates();
var response = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args); (UserResult result, string userInput) = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
if (response.Result == UserResult.Ok) if (result == UserResult.Ok)
{ {
inputText = response.Input; inputText = userInput;
okPressed = true; okPressed = true;
} }
} }

View file

@ -33,7 +33,7 @@ namespace Ryujinx.Ava.UI.Views.Input
{ {
base.OnPointerReleased(e); base.OnPointerReleased(e);
if (_currentAssigner != null && _currentAssigner.ToggledButton != null && !_currentAssigner.ToggledButton.IsPointerOver) if (_currentAssigner is { ToggledButton.IsPointerOver: false })
{ {
_currentAssigner.Cancel(); _currentAssigner.Cancel();
} }
@ -41,9 +41,10 @@ namespace Ryujinx.Ava.UI.Views.Input
private void Button_IsCheckedChanged(object sender, RoutedEventArgs e) private void Button_IsCheckedChanged(object sender, RoutedEventArgs e)
{ {
if (sender is ToggleButton button) if (sender is not ToggleButton button)
{ return;
if ((bool)button.IsChecked)
if (button.IsChecked is true)
{ {
if (_currentAssigner != null && button == _currentAssigner.ToggledButton) if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
{ {
@ -58,12 +59,15 @@ namespace Ryujinx.Ava.UI.Views.Input
PointerPressed += MouseClick; PointerPressed += MouseClick;
var viewModel = (DataContext as KeyboardInputViewModel); if (DataContext is not KeyboardInputViewModel viewModel)
return;
IKeyboard keyboard = (IKeyboard)viewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations. IKeyboard keyboard =
IButtonAssigner assigner = CreateButtonAssigner(); (IKeyboard)viewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
IButtonAssigner assigner =
new KeyboardKeyAssigner((IKeyboard)viewModel.ParentModel.SelectedGamepad);
_currentAssigner.ButtonAssigned += (sender, e) => _currentAssigner.ButtonAssigned += (_, e) =>
{ {
if (e.ButtonValue.HasValue) if (e.ButtonValue.HasValue)
{ {
@ -178,7 +182,6 @@ namespace Ryujinx.Ava.UI.Views.Input
_currentAssigner = null; _currentAssigner = null;
} }
} }
}
private void MouseClick(object sender, PointerPressedEventArgs e) private void MouseClick(object sender, PointerPressedEventArgs e)
{ {
@ -189,15 +192,6 @@ namespace Ryujinx.Ava.UI.Views.Input
PointerPressed -= MouseClick; PointerPressed -= MouseClick;
} }
private IButtonAssigner CreateButtonAssigner()
{
IButtonAssigner assigner;
assigner = new KeyboardKeyAssigner((IKeyboard)(DataContext as KeyboardInputViewModel).ParentModel.SelectedGamepad);
return assigner;
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{ {
base.OnDetachedFromVisualTree(e); base.OnDetachedFromVisualTree(e);

View file

@ -273,8 +273,8 @@
</MenuItem> </MenuItem>
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarView}"> <MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarView}">
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarViewWindow}"> <MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarViewWindow}">
<MenuItem Header="{ext:Locale MenuBarViewWindow720}" Tag="720 1280" Click="ChangeWindowSize_Click" /> <MenuItem Header="{ext:Locale MenuBarViewWindow720}" Tag="1280 720" Click="ChangeWindowSize_Click" />
<MenuItem Header="{ext:Locale MenuBarViewWindow1080}" Tag="1080 1920" Click="ChangeWindowSize_Click" /> <MenuItem Header="{ext:Locale MenuBarViewWindow1080}" Tag="1920 1080" Click="ChangeWindowSize_Click" />
</MenuItem> </MenuItem>
</MenuItem> </MenuItem>
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarHelp}"> <MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarHelp}">

View file

@ -184,8 +184,10 @@ namespace Ryujinx.Ava.UI.Views.Main
if (sender is not MenuItem { Tag: string resolution }) if (sender is not MenuItem { Tag: string resolution })
return; return;
(int height, int width) = resolution.Split(' ') (int width, int height) = resolution.Split(' ', 2)
.Into(parts => (int.Parse(parts[0]), int.Parse(parts[1]))); .Into(parts =>
(int.Parse(parts[0]), int.Parse(parts[1]))
);
await Dispatcher.UIThread.InvokeAsync(() => await Dispatcher.UIThread.InvokeAsync(() =>
{ {
@ -200,7 +202,7 @@ namespace Ryujinx.Ava.UI.Views.Main
public async void CheckForUpdates(object sender, RoutedEventArgs e) public async void CheckForUpdates(object sender, RoutedEventArgs e)
{ {
if (Updater.CanUpdate(true)) if (Updater.CanUpdate(true))
await Updater.BeginParse(Window, true); await Window.BeginUpdateAsync(true);
} }
public async void OpenXCITrimmerWindow(object sender, RoutedEventArgs e) => await XCITrimmerWindow.Show(ViewModel); public async void OpenXCITrimmerWindow(object sender, RoutedEventArgs e) => await XCITrimmerWindow.Show(ViewModel);

View file

@ -63,8 +63,7 @@ namespace Ryujinx.Ava.UI.Views.User
private async void Import_OnClick(object sender, RoutedEventArgs e) private async void Import_OnClick(object sender, RoutedEventArgs e)
{ {
var window = this.GetVisualRoot() as Window; var result = await ((Window)this.GetVisualRoot()!).StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
var result = await window.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{ {
AllowMultiple = false, AllowMultiple = false,
FileTypeFilter = new List<FilePickerFileType> FileTypeFilter = new List<FilePickerFileType>

View file

@ -28,6 +28,7 @@ using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Helper; using Ryujinx.UI.Common.Helper;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using System.Threading; using System.Threading;
@ -349,12 +350,12 @@ namespace Ryujinx.Ava.UI.Windows
await Dispatcher.UIThread.InvokeAsync(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys)); await Dispatcher.UIThread.InvokeAsync(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
} }
if (ConfigurationState.Instance.CheckUpdatesOnStart && Updater.CanUpdate(false)) if (ConfigurationState.Instance.CheckUpdatesOnStart && Updater.CanUpdate())
{ {
await Updater.BeginParse(this, false).ContinueWith(task => await this.BeginUpdateAsync()
{ .ContinueWith(
Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}"); task => Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}"),
}, TaskContinuationOptions.OnlyOnFaulted); TaskContinuationOptions.OnlyOnFaulted);
} }
} }
@ -392,30 +393,17 @@ namespace Ryujinx.Ava.UI.Windows
ViewModel.WindowState = ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value ? WindowState.Maximized : WindowState.Normal; ViewModel.WindowState = ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value ? WindowState.Maximized : WindowState.Normal;
if (CheckScreenBounds(savedPoint)) if (Screens.All.Any(screen => screen.Bounds.Contains(savedPoint)))
{ {
Position = savedPoint; Position = savedPoint;
} }
else else
{ {
Logger.Warning?.Print(LogClass.Application, "Failed to find valid start-up coordinates. Defaulting to primary monitor center.");
WindowStartupLocation = WindowStartupLocation.CenterScreen; WindowStartupLocation = WindowStartupLocation.CenterScreen;
} }
} }
private bool CheckScreenBounds(PixelPoint configPoint)
{
for (int i = 0; i < Screens.ScreenCount; i++)
{
if (Screens.All[i].Bounds.Contains(configPoint))
{
return true;
}
}
Logger.Warning?.Print(LogClass.Application, "Failed to find valid start-up coordinates. Defaulting to primary monitor center.");
return false;
}
private void SaveWindowSizePosition() private void SaveWindowSizePosition()
{ {
ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value = WindowState == WindowState.Maximized; ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value = WindowState == WindowState.Maximized;
@ -507,8 +495,7 @@ namespace Ryujinx.Ava.UI.Windows
private void VolumeStatus_CheckedChanged(object sender, RoutedEventArgs e) private void VolumeStatus_CheckedChanged(object sender, RoutedEventArgs e)
{ {
var volumeSplitButton = sender as ToggleSplitButton; if (ViewModel.IsGameRunning && sender is ToggleSplitButton volumeSplitButton)
if (ViewModel.IsGameRunning)
{ {
if (!volumeSplitButton.IsChecked) if (!volumeSplitButton.IsChecked)
{ {

View file

@ -32,6 +32,8 @@ namespace Ryujinx.Ava
internal static class Updater internal static class Updater
{ {
private const string GitHubApiUrl = "https://api.github.com"; private const string GitHubApiUrl = "https://api.github.com";
private const string LatestReleaseUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
private static readonly GithubReleasesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly GithubReleasesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory; private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory;
@ -46,9 +48,9 @@ namespace Ryujinx.Ava
private static bool _updateSuccessful; private static bool _updateSuccessful;
private static bool _running; private static bool _running;
private static readonly string[] _windowsDependencyDirs = Array.Empty<string>(); private static readonly string[] _windowsDependencyDirs = [];
public static async Task BeginParse(Window mainWindow, bool showVersionUpToDate) public static async Task BeginUpdateAsync(this Window mainWindow, bool showVersionUpToDate = false)
{ {
if (_running) if (_running)
{ {
@ -97,8 +99,7 @@ namespace Ryujinx.Ava
{ {
using HttpClient jsonClient = ConstructHttpClient(); using HttpClient jsonClient = ConstructHttpClient();
string buildInfoUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; string fetchedJson = await jsonClient.GetStringAsync(LatestReleaseUrl);
string fetchedJson = await jsonClient.GetStringAsync(buildInfoUrl);
var fetched = JsonHelper.Deserialize(fetchedJson, _serializerContext.GithubReleasesJsonResponse); var fetched = JsonHelper.Deserialize(fetchedJson, _serializerContext.GithubReleasesJsonResponse);
_buildVer = fetched.Name; _buildVer = fetched.Name;
@ -159,7 +160,7 @@ namespace Ryujinx.Ava
} }
catch catch
{ {
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!"); Logger.Error?.Print(LogClass.Application, $"Failed to convert the received {App.FullAppName} version from GitHub!");
await ContentDialogHelper.CreateWarningDialog( await ContentDialogHelper.CreateWarningDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
@ -636,7 +637,7 @@ namespace Ryujinx.Ava
taskDialog.Hide(); taskDialog.Hide();
} }
public static bool CanUpdate(bool showWarnings) public static bool CanUpdate(bool showWarnings = false)
{ {
#if !DISABLE_UPDATER #if !DISABLE_UPDATER
if (!NetworkInterface.GetIsNetworkAvailable()) if (!NetworkInterface.GetIsNetworkAvailable())