UI: Remove title bar, put icon next to File button. Added icons to most dropdown menu actions. Title bar content is now displayed when hovering the logo in the title bar.

This commit is contained in:
Evan Husted 2024-10-17 01:24:16 -05:00
parent 235083ad75
commit 045f9a39bb
21 changed files with 381 additions and 390 deletions

View file

@ -4,6 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:icon="clr-namespace:Ryujinx.Ava.Common.Icon"
mc:Ignorable="d"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
x:DataType="viewModels:MainWindowViewModel"
@ -12,6 +13,13 @@
<viewModels:MainWindowViewModel />
</Design.DataContext>
<DockPanel HorizontalAlignment="Stretch">
<Border Padding="7, 0, 0, 0" VerticalAlignment="Center" HorizontalAlignment="Center">
<Image
ToolTip.Tip="{Binding Title}"
Height="25"
Width="25"
Source="resm:Ryujinx.UI.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.UI.Common" />
</Border>
<Menu
Name="Menu"
Height="35"
@ -27,27 +35,32 @@
<MenuItem
Command="{Binding OpenFile}"
Header="{locale:Locale MenuBarFileOpenFromFile}"
Icon="{icon:Icon fa-solid fa-file}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale LoadApplicationFileTooltip}" />
<MenuItem
Command="{Binding OpenFolder}"
Header="{locale:Locale MenuBarFileOpenUnpacked}"
Icon="{icon:Icon fa-solid fa-folder}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale LoadApplicationFolderTooltip}" />
<MenuItem
Command="{Binding LoadDlcFromFolder}"
Header="{locale:Locale MenuBarFileLoadDlcFromFolder}"
Icon="{icon:Icon fa-solid fa-download}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale LoadDlcFromFolderTooltip}" />
<MenuItem
Command="{Binding LoadTitleUpdatesFromFolder}"
Header="{locale:Locale MenuBarFileLoadTitleUpdatesFromFolder}"
Icon="{icon:Icon fa-solid fa-code-compare}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale LoadTitleUpdatesFromFolderTooltip}" />
<MenuItem Header="{locale:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}">
<MenuItem Header="{locale:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}" Icon="{icon:Icon mdi-launch}">
<MenuItem
Click="OpenMiiApplet"
Header="Mii Edit Applet"
Icon="{icon:Icon fa-solid fa-person}"
ToolTip.Tip="{locale:Locale MenuBarFileOpenAppletOpenMiiAppletToolTip}" />
</MenuItem>
<Separator />
@ -63,14 +76,28 @@
<MenuItem
Click="CloseWindow"
Header="{locale:Locale MenuBarFileExit}"
Icon="{icon:Icon fa-solid fa-xmark}"
ToolTip.Tip="{locale:Locale ExitTooltip}" />
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarOptions}">
<MenuItem
Padding="-10,0,0,0"
Command="{Binding ToggleFullscreen}"
Header="{locale:Locale MenuBarOptionsToggleFullscreen}"
InputGesture="F11" />
Padding="0"
Icon="{icon:Icon fa-solid fa-expand}"
InputGesture="F11">
<MenuItem.Styles>
<Style Selector="Viewbox#PART_IconPresenter">
<Setter Property="MaxHeight" Value="36" />
<Setter Property="MinHeight" Value="36" />
<Setter Property="MaxWidth" Value="36" />
<Setter Property="MinWidth" Value="36" />
</Style>
<Style Selector="ContentPresenter#PART_HeaderPresenter">
<Setter Property="Padding" Value="-10,0,0,0" />
</Style>
</MenuItem.Styles>
</MenuItem>
<MenuItem
Padding="0"
Command="{Binding ToggleStartGamesInFullscreen}"
@ -100,7 +127,7 @@
Command="{Binding ToggleShowConsole}"
Header="{locale:Locale MenuBarOptionsShowConsole}">
<MenuItem.Icon>
<CheckBox
<CheckBox
MinWidth="{DynamicResource CheckBoxSize}"
MinHeight="{DynamicResource CheckBoxSize}"
IsChecked="{Binding ShowConsole, Mode=TwoWay}"
@ -118,11 +145,24 @@
</Style>
</MenuItem.Styles>
</MenuItem>
<Separator />
<Separator/>
<MenuItem
Name="ChangeLanguageMenuItem"
Padding="-10,0,0,0"
Header="{locale:Locale MenuBarOptionsChangeLanguage}" />
Padding="0"
Header="{locale:Locale MenuBarOptionsChangeLanguage}"
Icon="{icon:Icon fa-solid fa-language}">
<MenuItem.Styles>
<Style Selector="Viewbox#PART_IconPresenter">
<Setter Property="MaxHeight" Value="36" />
<Setter Property="MinHeight" Value="36" />
<Setter Property="MaxWidth" Value="36" />
<Setter Property="MinWidth" Value="36" />
</Style>
<Style Selector="ContentPresenter#PART_HeaderPresenter">
<Setter Property="Padding" Value="-10,0,0,0" />
</Style>
</MenuItem.Styles>
</MenuItem>
<MenuItem
Name="ToggleFileTypesMenuItem"
Padding="-10,0,0,0"
@ -130,15 +170,41 @@
<Separator />
<MenuItem
Click="OpenSettings"
Padding="-10,0,0,0"
Padding="0"
Header="{locale:Locale MenuBarOptionsSettings}"
ToolTip.Tip="{locale:Locale OpenSettingsTooltip}" />
Icon="{icon:Icon fa-solid fa-gear}"
ToolTip.Tip="{locale:Locale OpenSettingsTooltip}">
<MenuItem.Styles>
<Style Selector="Viewbox#PART_IconPresenter">
<Setter Property="MaxHeight" Value="36" />
<Setter Property="MinHeight" Value="36" />
<Setter Property="MaxWidth" Value="36" />
<Setter Property="MinWidth" Value="36" />
</Style>
<Style Selector="ContentPresenter#PART_HeaderPresenter">
<Setter Property="Padding" Value="-10,0,0,0" />
</Style>
</MenuItem.Styles>
</MenuItem>
<MenuItem
Command="{Binding ManageProfiles}"
Padding="-10,0,0,0"
Padding="0"
Header="{locale:Locale MenuBarOptionsManageUserProfiles}"
Icon="{icon:Icon mdi-account}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale OpenProfileManagerTooltip}" />
ToolTip.Tip="{locale:Locale OpenProfileManagerTooltip}">
<MenuItem.Styles>
<Style Selector="Viewbox#PART_IconPresenter">
<Setter Property="MaxHeight" Value="36" />
<Setter Property="MinHeight" Value="36" />
<Setter Property="MaxWidth" Value="36" />
<Setter Property="MinWidth" Value="36" />
</Style>
<Style Selector="ContentPresenter#PART_HeaderPresenter">
<Setter Property="Padding" Value="-10,0,0,0" />
</Style>
</MenuItem.Styles>
</MenuItem>
</MenuItem>
<MenuItem
Name="ActionsMenuItem"
@ -148,18 +214,21 @@
<MenuItem
Click="PauseEmulation_Click"
Header="{locale:Locale MenuBarOptionsPauseEmulation}"
Icon="{icon:Icon fa-solid fa-pause}"
InputGesture="{Binding PauseKey}"
IsEnabled="{Binding !IsPaused}"
IsVisible="{Binding !IsPaused}" />
<MenuItem
Click="ResumeEmulation_Click"
Header="{locale:Locale MenuBarOptionsResumeEmulation}"
Icon="{icon:Icon fa-solid fa-play}"
InputGesture="{Binding PauseKey}"
IsEnabled="{Binding IsPaused}"
IsVisible="{Binding IsPaused}" />
<MenuItem
Click="StopEmulation_Click"
Header="{locale:Locale MenuBarOptionsStopEmulation}"
Icon="{icon:Icon fa-solid fa-stop}"
InputGesture="Escape"
IsEnabled="{Binding IsGameRunning}"
ToolTip.Tip="{locale:Locale StopEmulationTooltip}" />
@ -170,26 +239,30 @@
AttachedToVisualTree="ScanAmiiboMenuItem_AttachedToVisualTree"
Click="OpenAmiiboWindow"
Header="{locale:Locale MenuBarActionsScanAmiibo}"
Icon="{icon:Icon mdi-cube-scan}"
IsEnabled="{Binding IsAmiiboRequested}" />
<MenuItem
Command="{Binding TakeScreenshot}"
Header="{locale:Locale MenuBarFileToolsTakeScreenshot}"
Icon="{icon:Icon mdi-monitor-screenshot}"
InputGesture="{Binding ScreenshotKey}"
IsEnabled="{Binding IsGameRunning}" />
<MenuItem
Command="{Binding HideUi}"
Header="{locale:Locale MenuBarFileToolsHideUi}"
Icon="{icon:Icon mdi-eye-off}"
InputGesture="{Binding ShowUiKey}"
IsEnabled="{Binding IsGameRunning}" />
<MenuItem
Click="OpenCheatManagerForCurrentApp"
Header="{locale:Locale GameListContextMenuManageCheat}"
Icon="{icon:Icon fa-solid fa-code}"
IsEnabled="{Binding IsGameRunning}" />
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarTools}">
<MenuItem Header="{locale:Locale MenuBarToolsInstallFirmware}" IsEnabled="{Binding EnableNonGameRunningControls}">
<MenuItem Command="{Binding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
<MenuItem Command="{Binding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
<MenuItem Header="{locale:Locale MenuBarToolsInstallFirmware}" Icon="{icon:Icon fa-solid fa-download}" IsEnabled="{Binding EnableNonGameRunningControls}">
<MenuItem Command="{Binding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" Icon="{icon:Icon mdi-file-cog}" />
<MenuItem Command="{Binding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" Icon="{icon:Icon mdi-folder-cog}" />
</MenuItem>
<MenuItem Header="{locale:Locale MenuBarToolsManageFileTypes}" IsVisible="{Binding ManageFileTypesVisible}">
<MenuItem Header="{locale:Locale MenuBarToolsInstallFileTypes}" Click="InstallFileTypes_Click"/>
@ -198,8 +271,8 @@
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarView}">
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarViewWindow}">
<MenuItem Header="{locale:Locale MenuBarViewWindow720}" Tag="720" Click="ChangeWindowSize_Click" />
<MenuItem Header="{locale:Locale MenuBarViewWindow1080}" Tag="1080" Click="ChangeWindowSize_Click" />
<MenuItem Header="{locale:Locale MenuBarViewWindow720}" Tag="720 1280" Click="ChangeWindowSize_Click" />
<MenuItem Header="{locale:Locale MenuBarViewWindow1080}" Tag="1080 1920" Click="ChangeWindowSize_Click" />
</MenuItem>
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}">
@ -208,11 +281,13 @@
IsEnabled="{Binding CanUpdate}"
Click="CheckForUpdates"
Header="{locale:Locale MenuBarHelpCheckForUpdates}"
Icon="{icon:Icon mdi-update}"
ToolTip.Tip="{locale:Locale CheckUpdatesTooltip}" />
<Separator />
<MenuItem
Click="OpenAboutWindow"
Header="{locale:Locale MenuBarHelpAbout}"
Icon="{icon:Icon fa-solid fa-circle-info}"
ToolTip.Tip="{locale:Locale OpenAboutTooltip}" />
</MenuItem>
</Menu>

View file

@ -2,6 +2,7 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Threading;
using Gommon;
using LibHac.Ncm;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale;
@ -77,11 +78,9 @@ namespace Ryujinx.Ava.UI.Views.Main
MenuItem menuItem = new()
{
Header = languageName,
Command = MiniCommand.Create(() =>
{
MainWindowViewModel.ChangeLanguage(languageCode);
}),
Padding = new Thickness(10, 0, 0, 0),
Header = " " + languageName,
Command = MiniCommand.Create(() => MainWindowViewModel.ChangeLanguage(languageCode)),
};
menuItems.Add(menuItem);
@ -99,23 +98,23 @@ namespace Ryujinx.Ava.UI.Views.Main
Window = window;
}
ViewModel = Window.ViewModel;
ViewModel = MainWindow.ViewModel;
DataContext = ViewModel;
}
private async void StopEmulation_Click(object sender, RoutedEventArgs e)
{
await Window.ViewModel.AppHost?.ShowExitPrompt();
await MainWindow.ViewModel.AppHost?.ShowExitPrompt().OrCompleted()!;
}
private void PauseEmulation_Click(object sender, RoutedEventArgs e)
{
Window.ViewModel.AppHost?.Pause();
MainWindow.ViewModel.AppHost?.Pause();
}
private void ResumeEmulation_Click(object sender, RoutedEventArgs e)
{
Window.ViewModel.AppHost?.Resume();
MainWindow.ViewModel.AppHost?.Resume();
}
public async void OpenSettings(object sender, RoutedEventArgs e)
@ -149,9 +148,7 @@ namespace Ryujinx.Ava.UI.Views.Main
public async void OpenAmiiboWindow(object sender, RoutedEventArgs e)
{
if (!ViewModel.IsAmiiboRequested)
{
return;
}
if (ViewModel.AppHost.Device.System.SearchingForAmiibo(out int deviceId))
{
@ -173,17 +170,15 @@ namespace Ryujinx.Ava.UI.Views.Main
public async void OpenCheatManagerForCurrentApp(object sender, RoutedEventArgs e)
{
if (!ViewModel.IsGameRunning)
{
return;
}
string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString();
await new CheatWindow(
Window.VirtualFileSystem,
ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText,
name,
Window.ViewModel.SelectedApplication.Path).ShowDialog(Window);
MainWindow.ViewModel.SelectedApplication.Path).ShowDialog(Window);
ViewModel.AppHost.Device.EnableCheats();
}
@ -191,85 +186,50 @@ namespace Ryujinx.Ava.UI.Views.Main
private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
{
if (sender is MenuItem)
{
ViewModel.IsAmiiboRequested = Window.ViewModel.AppHost.Device.System.SearchingForAmiibo(out _);
}
ViewModel.IsAmiiboRequested = MainWindow.ViewModel.AppHost.Device.System.SearchingForAmiibo(out _);
}
private async void InstallFileTypes_Click(object sender, RoutedEventArgs e)
{
if (FileAssociationHelper.Install())
{
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
}
else
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesErrorMessage]);
}
}
private async void UninstallFileTypes_Click(object sender, RoutedEventArgs e)
{
if (FileAssociationHelper.Uninstall())
{
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
}
else
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesErrorMessage]);
}
}
private async void ChangeWindowSize_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuItem item)
if (sender is not MenuItem { Tag: string resolution }) return;
(int height, int width) = resolution.Split(' ')
.Into(parts => (int.Parse(parts[0]), int.Parse(parts[1])));
await Dispatcher.UIThread.InvokeAsync(() =>
{
int height;
int width;
ViewModel.WindowState = WindowState.Normal;
switch (item.Tag)
{
case "720":
height = 720;
width = 1280;
break;
height += (int)Window.StatusBarHeight + (int)Window.MenuBarHeight;
case "1080":
height = 1080;
width = 1920;
break;
default:
throw new ArgumentNullException($"Invalid Tag for {item}");
}
await Dispatcher.UIThread.InvokeAsync(() =>
{
ViewModel.WindowState = WindowState.Normal;
height += (int)Window.StatusBarHeight + (int)Window.MenuBarHeight;
Window.Arrange(new Rect(Window.Position.X, Window.Position.Y, width, height));
});
}
Window.Arrange(new Rect(Window.Position.X, Window.Position.Y, width, height));
});
}
public async void CheckForUpdates(object sender, RoutedEventArgs e)
{
if (Updater.CanUpdate(true))
{
await Updater.BeginParse(Window, true);
}
}
public async void OpenAboutWindow(object sender, RoutedEventArgs e)
{
await AboutWindow.Show();
}
public async void OpenAboutWindow(object sender, RoutedEventArgs e) => await AboutWindow.Show();
public void CloseWindow(object sender, RoutedEventArgs e)
{
Window.Close();
}
public void CloseWindow(object sender, RoutedEventArgs e) => Window.Close();
}
}

View file

@ -28,14 +28,14 @@ namespace Ryujinx.Ava.UI.Views.Main
Window = window;
}
DataContext = Window.ViewModel;
DataContext = MainWindow.ViewModel;
}
private void VsyncStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
{
Window.ViewModel.AppHost.ToggleVSync();
MainWindow.ViewModel.AppHost.ToggleVSync();
Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {Window.ViewModel.AppHost.Device.EnableDeviceVsync}");
Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {MainWindow.ViewModel.AppHost.Device.EnableDeviceVsync}");
}
private void DockedStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
@ -57,9 +57,9 @@ namespace Ryujinx.Ava.UI.Views.Main
private void VolumeStatus_OnPointerWheelChanged(object sender, PointerWheelEventArgs e)
{
// Change the volume by 5% at a time
float newValue = Window.ViewModel.Volume + (float)e.Delta.Y * 0.05f;
float newValue = MainWindow.ViewModel.Volume + (float)e.Delta.Y * 0.05f;
Window.ViewModel.Volume = newValue switch
MainWindow.ViewModel.Volume = newValue switch
{
< 0 => 0,
> 1 => 1,

View file

@ -1,4 +1,4 @@
<UserControl
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

View file

@ -22,9 +22,9 @@ namespace Ryujinx.Ava.UI.Views.Main
{
base.OnAttachedToVisualTree(e);
if (VisualRoot is MainWindow window)
if (VisualRoot is MainWindow)
{
ViewModel = window.ViewModel;
ViewModel = MainWindow.ViewModel;
}
DataContext = ViewModel;
@ -32,18 +32,14 @@ namespace Ryujinx.Ava.UI.Views.Main
public void Sort_Checked(object sender, RoutedEventArgs args)
{
if (sender is RadioButton button)
{
ViewModel.Sort(Enum.Parse<ApplicationSort>(button.Tag.ToString()));
}
if (sender is RadioButton { Tag: string sortStrategy })
ViewModel.Sort(Enum.Parse<ApplicationSort>(sortStrategy));
}
public void Order_Checked(object sender, RoutedEventArgs args)
{
if (sender is RadioButton button)
{
ViewModel.Sort(button.Tag.ToString() != "Descending");
}
if (sender is RadioButton { Tag: string sortOrder })
ViewModel.Sort(sortOrder is not "Descending");
}
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e)