Compare commits

...

7 Commits

10 changed files with 115 additions and 41 deletions

View File

@@ -489,7 +489,7 @@ namespace Ryujinx.Ava
Dispatcher.UIThread.InvokeAsync(() => Dispatcher.UIThread.InvokeAsync(() =>
{ {
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device.Processes.ActiveApplication, Program.Version); _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowTitleBar);
}); });
_viewModel.SetUiProgressHandlers(Device); _viewModel.SetUiProgressHandlers(Device);
@@ -872,7 +872,7 @@ namespace Ryujinx.Ava
Device?.System.TogglePauseEmulation(false); Device?.System.TogglePauseEmulation(false);
_viewModel.IsPaused = false; _viewModel.IsPaused = false;
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version); _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowTitleBar);
Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed"); Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed");
} }
@@ -881,7 +881,7 @@ namespace Ryujinx.Ava
Device?.System.TogglePauseEmulation(true); Device?.System.TogglePauseEmulation(true);
_viewModel.IsPaused = true; _viewModel.IsPaused = true;
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, LocaleManager.Instance[LocaleKeys.Paused]); _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowTitleBar, LocaleManager.Instance[LocaleKeys.Paused]);
Logger.Info?.Print(LogClass.Emulation, "Emulation was paused"); Logger.Info?.Print(LogClass.Emulation, "Emulation was paused");
} }

View File

@@ -2,6 +2,7 @@ using DiscordRPC;
using Gommon; using Gommon;
using Humanizer; using Humanizer;
using Humanizer.Localisation; using Humanizer.Localisation;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common; using Ryujinx.Common;
@@ -97,7 +98,7 @@ namespace Ryujinx.Ava
}, },
Details = TruncateToByteLength($"Playing {appMeta.Title}"), Details = TruncateToByteLength($"Playing {appMeta.Title}"),
State = appMeta.LastPlayed.HasValue && appMeta.TimePlayed.TotalSeconds > 5 State = appMeta.LastPlayed.HasValue && appMeta.TimePlayed.TotalSeconds > 5
? $"Total play time: {appMeta.TimePlayed.Humanize(2, false, maxUnit: TimeUnit.Hour)}" ? $"Total play time: {ValueFormatUtils.FormatTimeSpan(appMeta.TimePlayed)}"
: "Never played", : "Never played",
Timestamps = Timestamps.Now Timestamps = Timestamps.Now
}); });

View File

@@ -279,13 +279,13 @@ namespace Ryujinx.Ava.UI.Applet
.ForEach(profile => profiles.Add(new Models.UserProfile(profile, nav))); .ForEach(profile => profiles.Add(new Models.UserProfile(profile, nav)));
profiles.Add(new Models.UserProfile(guest, nav)); profiles.Add(new Models.UserProfile(guest, nav));
UserSelectorDialogViewModel viewModel = new(); UserSelectorDialogViewModel viewModel = new()
viewModel.Profiles = profiles; {
viewModel.SelectedUserId = _parent.AccountManager.LastOpenedUser.UserId; Profiles = profiles,
SelectedUserId = _parent.AccountManager.LastOpenedUser.UserId
};
UserSelectorDialog content = new(viewModel); UserSelectorDialog content = new(viewModel);
(UserId id, _) = await UserSelectorDialog.ShowInputDialog(content); (selected, _) = await UserSelectorDialog.ShowInputDialog(content);
selected = id;
dialogCloseEvent.Set(); dialogCloseEvent.Set();
}); });

View File

@@ -0,0 +1,48 @@
using CommunityToolkit.Mvvm.Input;
using System;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Helpers
{
#nullable enable
public static class Commands
{
public static RelayCommand Create(Action action)
=> new(action);
public static RelayCommand CreateConditional(Action action, Func<bool> canExecute)
=> new(action, canExecute);
public static RelayCommand<T> CreateWithArg<T>(Action<T?> action)
=> new(action);
public static RelayCommand<T> CreateConditionalWithArg<T>(Action<T?> action, Predicate<T?> canExecute)
=> new(action, canExecute);
public static AsyncRelayCommand Create(Func<Task> action)
=> new(action, AsyncRelayCommandOptions.None);
public static AsyncRelayCommand CreateConcurrent(Func<Task> action)
=> new(action, AsyncRelayCommandOptions.AllowConcurrentExecutions);
public static AsyncRelayCommand CreateSilentFail(Func<Task> action)
=> new(action, AsyncRelayCommandOptions.FlowExceptionsToTaskScheduler);
public static AsyncRelayCommand<T> CreateWithArg<T>(Func<T?, Task> action)
=> new(action, AsyncRelayCommandOptions.None);
public static AsyncRelayCommand<T> CreateConcurrentWithArg<T>(Func<T?, Task> action)
=> new(action, AsyncRelayCommandOptions.AllowConcurrentExecutions);
public static AsyncRelayCommand<T> CreateSilentFailWithArg<T>(Func<T?, Task> action)
=> new(action, AsyncRelayCommandOptions.FlowExceptionsToTaskScheduler);
public static AsyncRelayCommand CreateConditional(Func<Task> action, Func<bool> canExecute)
=> new(action, canExecute, AsyncRelayCommandOptions.None);
public static AsyncRelayCommand CreateConcurrentConditional(Func<Task> action, Func<bool> canExecute)
=> new(action, canExecute, AsyncRelayCommandOptions.AllowConcurrentExecutions);
public static AsyncRelayCommand CreateSilentFailConditional(Func<Task> action, Func<bool> canExecute)
=> new(action, canExecute, AsyncRelayCommandOptions.FlowExceptionsToTaskScheduler);
public static AsyncRelayCommand<T> CreateConditionalWithArg<T>(Func<T?, Task> action, Predicate<T?> canExecute)
=> new(action, canExecute, AsyncRelayCommandOptions.None);
public static AsyncRelayCommand<T> CreateConcurrentConditionalWithArg<T>(Func<T?, Task> action, Predicate<T?> canExecute)
=> new(action, canExecute, AsyncRelayCommandOptions.AllowConcurrentExecutions);
public static AsyncRelayCommand<T> CreateSilentFailConditionalWithArg<T>(Func<T?, Task> action, Predicate<T?> canExecute)
=> new(action, canExecute, AsyncRelayCommandOptions.FlowExceptionsToTaskScheduler);
}
}

View File

@@ -735,21 +735,19 @@ namespace Ryujinx.Ava.UI.Windows
}); });
} }
private static bool _intelMacWarningShown; private static bool _intelMacWarningShown = !(OperatingSystem.IsMacOS() &&
(RuntimeInformation.OSArchitecture == Architecture.X64 ||
RuntimeInformation.OSArchitecture == Architecture.X86));
public static async Task ShowIntelMacWarningAsync() public static async Task ShowIntelMacWarningAsync()
{ {
if (!_intelMacWarningShown && if (_intelMacWarningShown) return;
(OperatingSystem.IsMacOS() &&
(RuntimeInformation.OSArchitecture == Architecture.X64 ||
RuntimeInformation.OSArchitecture == Architecture.X86)))
{
_intelMacWarningShown = true;
await Dispatcher.UIThread.InvokeAsync(async () => await ContentDialogHelper.CreateWarningDialog( await Dispatcher.UIThread.InvokeAsync(async () => await ContentDialogHelper.CreateWarningDialog(
"Intel Mac Warning", "Intel Mac Warning",
"Intel Macs are not supported and will not work properly.\nIf you continue, do not come to our Discord asking for support.")); "Intel Macs are not supported and will not work properly.\nIf you continue, do not come to our Discord asking for support;\nand do not report bugs on the GitHub. They will be closed."));
}
_intelMacWarningShown = true;
} }
} }
} }

View File

@@ -28,7 +28,9 @@ namespace Ryujinx.Ava.Utilities.Compat
public class CompatibilityCsv public class CompatibilityCsv
{ {
static CompatibilityCsv() static CompatibilityCsv() => Load();
public static void Load()
{ {
using Stream csvStream = Assembly.GetExecutingAssembly() using Stream csvStream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream("RyujinxGameCompatibilityList")!; .GetManifestResourceStream("RyujinxGameCompatibilityList")!;
@@ -37,15 +39,31 @@ namespace Ryujinx.Ava.Utilities.Compat
using SepReader reader = Sep.Reader().From(csvStream); using SepReader reader = Sep.Reader().From(csvStream);
ColumnIndices columnIndices = new(reader.Header.IndexOf); ColumnIndices columnIndices = new(reader.Header.IndexOf);
Entries = reader _entries = reader
.Enumerate(row => new CompatibilityEntry(ref columnIndices, row)) .Enumerate(row => new CompatibilityEntry(ref columnIndices, row))
.OrderBy(it => it.GameName) .OrderBy(it => it.GameName)
.ToArray(); .ToArray();
Logger.Debug?.Print(LogClass.UI, "Compatibility CSV loaded.", "LoadCompatCsv"); Logger.Debug?.Print(LogClass.UI, "Compatibility CSV loaded.", "LoadCompatibility");
} }
public static CompatibilityEntry[] Entries { get; private set; } public static void Unload()
{
_entries = null;
}
private static CompatibilityEntry[] _entries;
public static CompatibilityEntry[] Entries
{
get
{
if (_entries == null)
Load();
return _entries;
}
}
} }
public class CompatibilityEntry public class CompatibilityEntry

View File

@@ -1,11 +1,8 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Styling; using Avalonia.Styling;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using nietras.SeparatedValues;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using System.IO;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava.Utilities.Compat namespace Ryujinx.Ava.Utilities.Compat
@@ -35,6 +32,8 @@ namespace Ryujinx.Ava.Utilities.Compat
contentDialog.Styles.Add(closeButtonParent); contentDialog.Styles.Add(closeButtonParent);
await ContentDialogHelper.ShowAsync(contentDialog); await ContentDialogHelper.ShowAsync(contentDialog);
CompatibilityCsv.Unload();
} }
public CompatibilityList() public CompatibilityList()

View File

@@ -1,19 +1,18 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using ExCSS;
using Gommon; using Gommon;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Ryujinx.Ava.Utilities.Compat namespace Ryujinx.Ava.Utilities.Compat
{ {
public partial class CompatibilityViewModel : ObservableObject public class CompatibilityViewModel : BaseModel
{ {
[ObservableProperty] private bool _onlyShowOwnedGames = true; private bool _onlyShowOwnedGames = true;
private IEnumerable<CompatibilityEntry> _currentEntries = CompatibilityCsv.Entries; private IEnumerable<CompatibilityEntry> _currentEntries = CompatibilityCsv.Entries;
private readonly string[] _ownedGameTitleIds = []; private string[] _ownedGameTitleIds = [];
private readonly ApplicationLibrary _appLibrary;
public IEnumerable<CompatibilityEntry> CurrentEntries => OnlyShowOwnedGames public IEnumerable<CompatibilityEntry> CurrentEntries => OnlyShowOwnedGames
? _currentEntries.Where(x => ? _currentEntries.Where(x =>
@@ -24,14 +23,23 @@ namespace Ryujinx.Ava.Utilities.Compat
public CompatibilityViewModel(ApplicationLibrary appLibrary) public CompatibilityViewModel(ApplicationLibrary appLibrary)
{ {
_appLibrary = appLibrary; appLibrary.ApplicationCountUpdated += (_, _)
_ownedGameTitleIds = appLibrary.Applications.Keys.Select(x => x.ToString("X16")).ToArray(); => _ownedGameTitleIds = appLibrary.Applications.Keys.Select(x => x.ToString("X16")).ToArray();
PropertyChanged += (_, args) => _ownedGameTitleIds = appLibrary.Applications.Keys.Select(x => x.ToString("X16")).ToArray();
}
public bool OnlyShowOwnedGames
{ {
if (args.PropertyName is nameof(OnlyShowOwnedGames)) get => _onlyShowOwnedGames;
set
{
OnPropertyChanging();
OnPropertyChanging(nameof(CurrentEntries));
_onlyShowOwnedGames = value;
OnPropertyChanged();
OnPropertyChanged(nameof(CurrentEntries)); OnPropertyChanged(nameof(CurrentEntries));
}; }
} }
public void Search(string searchTerm) public void Search(string searchTerm)

View File

@@ -4,7 +4,7 @@ namespace Ryujinx.Ava.Utilities
{ {
public static class TitleHelper public static class TitleHelper
{ {
public static string ActiveApplicationTitle(ProcessResult activeProcess, string applicationVersion, string pauseString = "") public static string ActiveApplicationTitle(ProcessResult activeProcess, string applicationVersion, bool customTitlebar, string pauseString = "")
{ {
if (activeProcess == null) if (activeProcess == null)
return string.Empty; return string.Empty;
@@ -14,7 +14,9 @@ namespace Ryujinx.Ava.Utilities
string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})"; string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})";
string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)";
string appTitle = $"Ryujinx {applicationVersion} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; string appTitle = customTitlebar
? $"Ryujinx {applicationVersion}\n{titleNameSection.Trim()}\n{titleVersionSection.Trim()}\n{titleIdSection.Trim()}{titleArchSection}"
: $"Ryujinx {applicationVersion} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
return !string.IsNullOrEmpty(pauseString) return !string.IsNullOrEmpty(pauseString)
? appTitle + $" ({pauseString})" ? appTitle + $" ({pauseString})"