Avalonia UI - Part 1 (#3270)
* avalonia part 1 * remove vulkan ui backend * move ui common files to ui common project * get name for oading screen from device * rebase. * review 1 * review 1.1 * review * cleanup * addressed review * use cancellation token * review * review * rebased * cancel library loading when closing window * remove star image, use fonticon instead * delete render control frame buffer when game ends. change position of fav star * addressed @Thog review * ensure the right ui is downloaded in updates * fix crash when showing not supported dialog during controller request * add prefix to artifact names * Auto-format Avalonia project * Fix input * Fix build, simplify app disposal * remove nv stutter thread * addressed review * add missing change * maintain window size if new size is zero length * add game, handheld, docked to local * reverse scale main window * Update de_DE.json * Update de_DE.json * Update de_DE.json * Update italian json * Update it_IT.json * let render timer poll with no wait * remove unused code * more unused code * enabled tiered compilation and trimming * check if window event is not closed before signaling * fix atmospher case * locale fix * locale fix * remove explicit tiered compilation declarations * Remove ) it_IT.json * Remove ) de_DE.json * Update it_IT.json * Update pt_BR locale with latest strings * Remove ')' * add more strings to locale * update locale * remove extra slash * remove extra slash * set firmware version to 0 if key's not found * fix * revert timer changes * lock on object instead * Update it_IT.json * remove unused method * add load screen text to locale * drop swap event * Update de_DE.json * Update de_DE.json * do null check when stopping emulator * Update de_DE.json * Create tr_TR.json * Add tr_TR * Add tr_TR + Turkish * Update it_IT.json * Update Ryujinx.Ava/Input/AvaloniaMappingHelper.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * addressed review * Update Ryujinx.Ava/Ui/Backend/OpenGl/OpenGlRenderTarget.cs Co-authored-by: gdkchan <gab.dark.100@gmail.com> * use avalonia's inbuilt renderer on linux * removed whitespace * workaround for queue render crash with vsync off * drop custom backend * format files * fix not closing issue * remove warnings * rebase * update avalonia library * Reposition the Text and Button on About Page * Assign build version * Remove appveyor text Co-authored-by: gdk <gab.dark.100@gmail.com> Co-authored-by: Niwu34 <67392333+Niwu34@users.noreply.github.com> Co-authored-by: Antonio Brugnolo <36473846+AntoSkate@users.noreply.github.com> Co-authored-by: aegiff <99728970+aegiff@users.noreply.github.com> Co-authored-by: Ac_K <Acoustik666@gmail.com> Co-authored-by: MostlyWhat <78652091+MostlyWhat@users.noreply.github.com>
This commit is contained in:
163
Ryujinx.Ava/Ui/Windows/AboutWindow.axaml
Normal file
163
Ryujinx.Ava/Ui/Windows/AboutWindow.axaml
Normal file
@@ -0,0 +1,163 @@
|
||||
<window:StyleableWindow xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="350"
|
||||
x:Class="Ryujinx.Ava.Ui.Windows.AboutWindow"
|
||||
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
CanResize="False"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Width="850" MinHeight="550" Height="550"
|
||||
SizeToContent="Width"
|
||||
MinWidth="500"
|
||||
Title="Ryujinx - About">
|
||||
<Grid Margin="15" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Row="1" Margin="20" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" Margin="5, 10, 20 , 10"
|
||||
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" Height="110" MinWidth="50" />
|
||||
<TextBlock FontSize="35" TextAlignment="Center" Grid.Row="0" Grid.Column="1" Text="Ryujinx"
|
||||
Margin="0,20,0,0" />
|
||||
<TextBlock FontSize="16" TextAlignment="Center" Grid.Row="1" Grid.Column="1" Text="(REE-YOU-JINX)"
|
||||
Margin="0,0,0,0" />
|
||||
<Button Grid.Column="1" Background="Transparent" HorizontalAlignment="Center" Margin="0" Grid.Row="2"
|
||||
Tag="https://www.ryujinx.org/"
|
||||
Click="Button_OnClick">
|
||||
<TextBlock ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}"
|
||||
TextAlignment="Center" TextDecorations="Underline" Text="www.ryujinx.org" />
|
||||
</Button>
|
||||
</Grid>
|
||||
<TextBlock TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Text="{Binding Version}" Grid.Row="1" />
|
||||
<TextBlock Grid.Row="2" TextAlignment="Center" HorizontalAlignment="Center" Margin="20"
|
||||
Text="{locale:Locale AboutDisclaimerMessage}"
|
||||
MaxLines="2" />
|
||||
<TextBlock Grid.Row="3" TextAlignment="Center" HorizontalAlignment="Center" Margin="20"
|
||||
Text="{locale:Locale AboutAmiiboDisclaimerMessage}"
|
||||
Name="AmiiboLabel"
|
||||
PointerPressed="AmiiboLabel_OnPointerPressed"
|
||||
MaxLines="2" />
|
||||
<StackPanel Spacing="10" Orientation="Horizontal" Grid.Row="4" HorizontalAlignment="Center">
|
||||
<StackPanel Orientation="Vertical"
|
||||
ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}">
|
||||
<Button Height="65" Background="Transparent" Tag="https://www.patreon.com/ryujinx"
|
||||
Click="Button_OnClick">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Patreon.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock Grid.Row="1" Margin="0,5,0,0" Text="Patreon" HorizontalAlignment="Center" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical"
|
||||
ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
|
||||
<Button Height="65" Background="Transparent" Tag="https://github.com/Ryujinx/Ryujinx"
|
||||
Click="Button_OnClick">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_GitHub.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock Grid.Row="1" Margin="0,5,0,0" Text="GitHub" HorizontalAlignment="Center" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical"
|
||||
ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}">
|
||||
<Button Height="65" Background="Transparent" Tag="https://discordapp.com/invite/N2FmfVc"
|
||||
Click="Button_OnClick">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Discord.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock Grid.Row="1" Margin="0,5,0,0" Text="Discord" HorizontalAlignment="Center" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical"
|
||||
ToolTip.Tip="{locale:Locale AboutTwitterUrlTooltipMessage}">
|
||||
<Button Height="65" Background="Transparent" Tag="https://twitter.com/RyujinxEmu"
|
||||
Click="Button_OnClick">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Twitter.png?assembly=Ryujinx.Ui.Common" />
|
||||
<TextBlock Grid.Row="1" Margin="0,5,0,0" Text="Twitter" HorizontalAlignment="Center" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Border Grid.Row="1" Grid.Column="1" VerticalAlignment="Stretch" Margin="5" Width="2" BorderBrush="White"
|
||||
BorderThickness="1,0,0,0">
|
||||
<Separator Width="0" />
|
||||
</Border>
|
||||
<Grid Grid.Row="1" Margin="20" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Column="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="{locale:Locale AboutRyujinxAboutTitle}" FontWeight="Bold" TextDecorations="Underline" />
|
||||
<TextBlock LineHeight="20" Grid.Row="1" Margin="20,5,5,5"
|
||||
Text="{locale:Locale AboutRyujinxAboutContent}" />
|
||||
<TextBlock Grid.Row="2" Margin="0,10,0,0" Text="{locale:Locale AboutRyujinxMaintainersTitle}"
|
||||
FontWeight="Bold"
|
||||
TextDecorations="Underline" />
|
||||
<TextBlock LineHeight="20" Grid.Row="3" Margin="20,5,5,5"
|
||||
Text="{Binding Developers}" />
|
||||
<Button Background="Transparent" HorizontalAlignment="Right" Grid.Row="4"
|
||||
Tag="https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a" Click="Button_OnClick">
|
||||
<TextBlock ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}"
|
||||
TextAlignment="Right" TextDecorations="Underline"
|
||||
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}" />
|
||||
</Button>
|
||||
<TextBlock Grid.Row="5" Margin="0,0,0,0" Text="{locale:Locale AboutRyujinxSupprtersTitle}"
|
||||
FontWeight="Bold"
|
||||
TextDecorations="Underline" />
|
||||
<Border Width="460" Grid.Row="6" VerticalAlignment="Stretch" Height="200" BorderThickness="1" Margin="20,5"
|
||||
BorderBrush="White" Padding="5">
|
||||
<TextBlock TextWrapping="Wrap" VerticalAlignment="Top" Name="SupportersTextBlock"
|
||||
Text="{Binding Supporters}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</window:StyleableWindow>
|
92
Ryujinx.Ava/Ui/Windows/AboutWindow.axaml.cs
Normal file
92
Ryujinx.Ava/Ui/Windows/AboutWindow.axaml.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Threading;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using System.Net.Http;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class AboutWindow : StyleableWindow
|
||||
{
|
||||
public AboutWindow()
|
||||
{
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["MenuBarHelpAbout"];
|
||||
}
|
||||
|
||||
Version = Program.Version;
|
||||
|
||||
DataContext = this;
|
||||
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
|
||||
_ = DownloadPatronsJson();
|
||||
}
|
||||
|
||||
public string Supporters { get; set; }
|
||||
public string Version { get; set; }
|
||||
|
||||
public string Developers => string.Format(LocaleManager.Instance["AboutPageDeveloperListMore"], "gdkchan, Ac_K, Thog, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, Xpl0itR, GoffyDude, »jD«");
|
||||
|
||||
public TextBlock SupportersTextBlock { get; set; }
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
SupportersTextBlock = this.FindControl<TextBlock>("SupportersTextBlock");
|
||||
}
|
||||
|
||||
private void Button_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button)
|
||||
{
|
||||
OpenHelper.OpenUrl(button.Tag.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadPatronsJson()
|
||||
{
|
||||
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||
{
|
||||
Supporters = LocaleManager.Instance["ConnectionError"];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
HttpClient httpClient = new();
|
||||
|
||||
try
|
||||
{
|
||||
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
|
||||
|
||||
Supporters = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString));
|
||||
}
|
||||
catch
|
||||
{
|
||||
Supporters = LocaleManager.Instance["ApiError"];
|
||||
}
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(() => SupportersTextBlock.Text = Supporters);
|
||||
}
|
||||
|
||||
private void AmiiboLabel_OnPointerPressed(object sender, PointerPressedEventArgs e)
|
||||
{
|
||||
if (sender is TextBlock)
|
||||
{
|
||||
OpenHelper.OpenUrl("https://amiiboapi.com");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
192
Ryujinx.Ava/Ui/Windows/IconColorPicker.cs
Normal file
192
Ryujinx.Ava/Ui/Windows/IconColorPicker.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
static class IconColorPicker
|
||||
{
|
||||
private const int ColorsPerLine = 64;
|
||||
private const int TotalColors = ColorsPerLine * ColorsPerLine;
|
||||
|
||||
private const int UvQuantBits = 3;
|
||||
private const int UvQuantShift = BitsPerComponent - UvQuantBits;
|
||||
|
||||
private const int SatQuantBits = 5;
|
||||
private const int SatQuantShift = BitsPerComponent - SatQuantBits;
|
||||
|
||||
private const int BitsPerComponent = 8;
|
||||
|
||||
private const int CutOffLuminosity = 64;
|
||||
|
||||
private struct PaletteColor
|
||||
{
|
||||
public int Qck { get; }
|
||||
public byte R { get; }
|
||||
public byte G { get; }
|
||||
public byte B { get; }
|
||||
|
||||
public PaletteColor(int qck, byte r, byte g, byte b)
|
||||
{
|
||||
Qck = qck;
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
}
|
||||
}
|
||||
|
||||
public static Color GetFilteredColor(Image<Bgra32> image)
|
||||
{
|
||||
var color = GetColor(image).ToPixel<Bgra32>();
|
||||
|
||||
// We don't want colors that are too dark.
|
||||
// If the color is too dark, make it brighter by reducing the range
|
||||
// and adding a constant color.
|
||||
int luminosity = GetColorApproximateLuminosity(color.R, color.G, color.B);
|
||||
if (luminosity < CutOffLuminosity)
|
||||
{
|
||||
color = Color.FromRgb(
|
||||
(byte)Math.Min(CutOffLuminosity + color.R, byte.MaxValue),
|
||||
(byte)Math.Min(CutOffLuminosity + color.G, byte.MaxValue),
|
||||
(byte)Math.Min(CutOffLuminosity + color.B, byte.MaxValue));
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
public static Color GetColor(Image<Bgra32> image)
|
||||
{
|
||||
var colors = new PaletteColor[TotalColors];
|
||||
|
||||
var dominantColorBin = new Dictionary<int, int>();
|
||||
|
||||
var buffer = GetBuffer(image);
|
||||
|
||||
int w = image.Width;
|
||||
|
||||
int w8 = w << 8;
|
||||
int h8 = image.Height << 8;
|
||||
|
||||
int xStep = w8 / ColorsPerLine;
|
||||
int yStep = h8 / ColorsPerLine;
|
||||
|
||||
int i = 0;
|
||||
int maxHitCount = 0;
|
||||
|
||||
for (int y = 0; y < image.Height; y++)
|
||||
{
|
||||
int yOffset = y * image.Width;
|
||||
|
||||
for (int x = 0; x < image.Width && i < TotalColors; x++)
|
||||
{
|
||||
int offset = x + yOffset;
|
||||
|
||||
byte cb = buffer[offset].B;
|
||||
byte cg = buffer[offset].G;
|
||||
byte cr = buffer[offset].R;
|
||||
|
||||
var qck = GetQuantizedColorKey(cr, cg, cb);
|
||||
|
||||
if (dominantColorBin.TryGetValue(qck, out int hitCount))
|
||||
{
|
||||
dominantColorBin[qck] = hitCount + 1;
|
||||
|
||||
if (maxHitCount < hitCount)
|
||||
{
|
||||
maxHitCount = hitCount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dominantColorBin.Add(qck, 1);
|
||||
}
|
||||
|
||||
colors[i++] = new PaletteColor(qck, cr, cg, cb);
|
||||
}
|
||||
}
|
||||
|
||||
int highScore = -1;
|
||||
PaletteColor bestCandidate = default;
|
||||
|
||||
for (i = 0; i < TotalColors; i++)
|
||||
{
|
||||
var score = GetColorScore(dominantColorBin, maxHitCount, colors[i]);
|
||||
|
||||
if (highScore < score)
|
||||
{
|
||||
highScore = score;
|
||||
bestCandidate = colors[i];
|
||||
}
|
||||
}
|
||||
|
||||
return Color.FromRgb(bestCandidate.R, bestCandidate.G, bestCandidate.B);
|
||||
}
|
||||
|
||||
public static Bgra32[] GetBuffer(Image<Bgra32> image)
|
||||
{
|
||||
return image.TryGetSinglePixelSpan(out var data) ? data.ToArray() : new Bgra32[0];
|
||||
}
|
||||
|
||||
private static int GetColorScore(Dictionary<int, int> dominantColorBin, int maxHitCount, PaletteColor color)
|
||||
{
|
||||
var hitCount = dominantColorBin[color.Qck];
|
||||
var balancedHitCount = BalanceHitCount(hitCount, maxHitCount);
|
||||
var quantSat = (GetColorSaturation(color) >> SatQuantShift) << SatQuantShift;
|
||||
var value = GetColorValue(color);
|
||||
|
||||
// If the color is rarely used on the image,
|
||||
// then chances are that theres a better candidate, even if the saturation value
|
||||
// is high. By multiplying the saturation value with a weight, we can lower
|
||||
// it if the color is almost never used (hit count is low).
|
||||
var satWeighted = quantSat;
|
||||
var satWeight = balancedHitCount << 5;
|
||||
if (satWeight < 0x100)
|
||||
{
|
||||
satWeighted = (satWeighted * satWeight) >> 8;
|
||||
}
|
||||
|
||||
// Compute score from saturation and dominance of the color.
|
||||
// We prefer more vivid colors over dominant ones, so give more weight to the saturation.
|
||||
var score = ((satWeighted << 1) + balancedHitCount) * value;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private static int BalanceHitCount(int hitCount, int maxHitCount)
|
||||
{
|
||||
return (hitCount << 8) / maxHitCount;
|
||||
}
|
||||
|
||||
private static int GetColorApproximateLuminosity(byte r, byte g, byte b)
|
||||
{
|
||||
return (r + g + b) / 3;
|
||||
}
|
||||
|
||||
private static int GetColorSaturation(PaletteColor color)
|
||||
{
|
||||
int cMax = Math.Max(Math.Max(color.R, color.G), color.B);
|
||||
|
||||
if (cMax == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cMin = Math.Min(Math.Min(color.R, color.G), color.B);
|
||||
int delta = cMax - cMin;
|
||||
return (delta << 8) / cMax;
|
||||
}
|
||||
|
||||
private static int GetColorValue(PaletteColor color)
|
||||
{
|
||||
return Math.Max(Math.Max(color.R, color.G), color.B);
|
||||
}
|
||||
|
||||
private static int GetQuantizedColorKey(byte r, byte g, byte b)
|
||||
{
|
||||
int u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
|
||||
int v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
|
||||
return (v >> UvQuantShift) | ((u >> UvQuantShift) << UvQuantBits);
|
||||
}
|
||||
}
|
||||
}
|
639
Ryujinx.Ava/Ui/Windows/MainWindow.axaml
Normal file
639
Ryujinx.Ava/Ui/Windows/MainWindow.axaml
Normal file
@@ -0,0 +1,639 @@
|
||||
<window:StyleableWindow
|
||||
x:Class="Ryujinx.Ava.Ui.Windows.MainWindow"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
|
||||
xmlns:models="clr-namespace:Ryujinx.Ava.Ui.Models"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
|
||||
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
|
||||
Title="Ryujinx"
|
||||
Height="785"
|
||||
Width="1280"
|
||||
d:DesignHeight="720"
|
||||
d:DesignWidth="1280"
|
||||
MinWidth="1024"
|
||||
MinHeight="680"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:MainWindowViewModel"
|
||||
mc:Ignorable="d">
|
||||
<Window.Styles>
|
||||
<Style Selector="TitleBar:fullscreen">
|
||||
<Setter Property="Background" Value="#000000" />
|
||||
</Style>
|
||||
</Window.Styles>
|
||||
<Design.DataContext>
|
||||
<viewModels:MainWindowViewModel />
|
||||
</Design.DataContext>
|
||||
<Window.Resources>
|
||||
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
</Window.Resources>
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<controls:OffscreenTextBox Name="HiddenTextBox" Grid.Row="0" />
|
||||
<ContentControl Grid.Row="1"
|
||||
Focusable="False"
|
||||
IsVisible="False"
|
||||
KeyboardNavigation.IsTabStop="False">
|
||||
<ui:ContentDialog Name="ContentDialog"
|
||||
KeyboardNavigation.IsTabStop="False"
|
||||
IsPrimaryButtonEnabled="True"
|
||||
IsSecondaryButtonEnabled="True"
|
||||
IsVisible="True" />
|
||||
</ContentControl>
|
||||
<StackPanel IsVisible="False" Grid.Row="0">
|
||||
<controls:HotKeyControl Name="FullscreenHotKey" Command="{ReflectionBinding ToggleFullscreen}" />
|
||||
<controls:HotKeyControl Name="FullscreenHotKey2" Command="{ReflectionBinding ToggleFullscreen}" />
|
||||
<controls:HotKeyControl Name="DockToggleHotKey" Command="{ReflectionBinding ToggleDockMode}" />
|
||||
<controls:HotKeyControl Name="ExitHotKey" Command="{ReflectionBinding ExitCurrentState}" />
|
||||
</StackPanel>
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
IsVisible="{Binding ShowMenuAndStatusBar}"
|
||||
Orientation="Vertical">
|
||||
<DockPanel HorizontalAlignment="Stretch">
|
||||
<Menu
|
||||
Name="Menu"
|
||||
Margin="0"
|
||||
Height="35"
|
||||
HorizontalAlignment="Left">
|
||||
<Menu.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<DockPanel HorizontalAlignment="Stretch" Margin="0" />
|
||||
</ItemsPanelTemplate>
|
||||
</Menu.ItemsPanel>
|
||||
<MenuItem
|
||||
VerticalAlignment="Center"
|
||||
Header="{locale:Locale MenuBarFile}">
|
||||
<MenuItem IsEnabled="{Binding EnableNonGameRunningControls}"
|
||||
Command="{ReflectionBinding OpenFile}"
|
||||
Header="{locale:Locale MenuBarFileOpenFromFile}"
|
||||
ToolTip.Tip="{locale:Locale LoadApplicationFileTooltip}" />
|
||||
<MenuItem IsEnabled="{Binding EnableNonGameRunningControls}"
|
||||
Command="{ReflectionBinding OpenFolder}"
|
||||
Header="{locale:Locale MenuBarFileOpenUnpacked}"
|
||||
ToolTip.Tip="{locale:Locale LoadApplicationFolderTooltip}" />
|
||||
<MenuItem Header="{locale:Locale MenuBarFileOpenApplet}"
|
||||
IsEnabled="{Binding IsAppletMenuActive}">
|
||||
<MenuItem Command="{ReflectionBinding OpenMiiApplet}" Header="Mii Edit Applet"
|
||||
ToolTip.Tip="{locale:Locale MenuBarFileOpenAppletOpenMiiAppletToolTip}" />
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Command="{ReflectionBinding OpenRyujinxFolder}"
|
||||
Header="{locale:Locale MenuBarFileOpenEmuFolder}"
|
||||
ToolTip.Tip="{locale:Locale OpenRyujinxFolderTooltip}" />
|
||||
<MenuItem Command="{ReflectionBinding OpenLogsFolder}"
|
||||
Header="{locale:Locale MenuBarFileOpenLogsFolder}"
|
||||
ToolTip.Tip="{locale:Locale OpenRyujinxLogsTooltip}" />
|
||||
<Separator />
|
||||
<MenuItem Command="{ReflectionBinding CloseWindow}"
|
||||
Header="{locale:Locale MenuBarFileExit}"
|
||||
ToolTip.Tip="{locale:Locale ExitTooltip}" />
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
VerticalAlignment="Center"
|
||||
Header="{locale:Locale MenuBarOptions}">
|
||||
<MenuItem Command="{ReflectionBinding ToggleFullscreen}"
|
||||
Header="{locale:Locale MenuBarOptionsToggleFullscreen}" InputGesture="F11" />
|
||||
<MenuItem Header="{locale:Locale MenuBarOptionsStartGamesInFullscreen}">
|
||||
<MenuItem.Icon>
|
||||
<CheckBox IsChecked="{Binding StartGamesInFullscreen, Mode=TwoWay}" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="{locale:Locale MenuBarOptionsShowConsole}">
|
||||
<MenuItem.Icon>
|
||||
<CheckBox IsChecked="{Binding ShowConsole, Mode=TwoWay}" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="{locale:Locale MenuBarOptionsChangeLanguage}">
|
||||
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="en_US"
|
||||
Header="American English" />
|
||||
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="pt_BR"
|
||||
Header="Brazilian Portuguese" />
|
||||
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="es_ES"
|
||||
Header="Castilian Spanish" />
|
||||
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="fr_FR"
|
||||
Header="French" />
|
||||
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="de_DE"
|
||||
Header="German" />
|
||||
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="el_GR"
|
||||
Header="Greek" />
|
||||
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="it_IT"
|
||||
Header="Italian" />
|
||||
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="ko_KR"
|
||||
Header="Korean" />
|
||||
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="ru_RU"
|
||||
Header="Russian" />
|
||||
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="tr_TR"
|
||||
Header="Turkish" />
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Command="{ReflectionBinding OpenSettings}"
|
||||
Header="{locale:Locale MenuBarOptionsSettings}"
|
||||
ToolTip.Tip="{locale:Locale OpenSettingsTooltip}" />
|
||||
<MenuItem Command="{ReflectionBinding ManageProfiles}"
|
||||
IsEnabled="{Binding EnableNonGameRunningControls}"
|
||||
Header="{locale:Locale MenuBarOptionsManageUserProfiles}"
|
||||
ToolTip.Tip="{locale:Locale OpenProfileManagerTooltip}" />
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
VerticalAlignment="Center"
|
||||
Header="{locale:Locale MenuBarActions}"
|
||||
Name="ActionsMenuItem"
|
||||
IsEnabled="{Binding IsGameRunning}">
|
||||
<MenuItem
|
||||
Click="PauseEmulation_Click"
|
||||
Header="{locale:Locale MenuBarOptionsPauseEmulation}"
|
||||
IsEnabled="{Binding !IsPaused}"
|
||||
IsVisible="{Binding !IsPaused}"
|
||||
InputGesture="{Binding PauseKey}" />
|
||||
<MenuItem
|
||||
Click="ResumeEmulation_Click"
|
||||
Header="{locale:Locale MenuBarOptionsResumeEmulation}"
|
||||
IsEnabled="{Binding IsPaused}"
|
||||
IsVisible="{Binding IsPaused}"
|
||||
InputGesture="{Binding PauseKey}" />
|
||||
<MenuItem
|
||||
Click="StopEmulation_Click"
|
||||
Header="{locale:Locale MenuBarOptionsStopEmulation}"
|
||||
ToolTip.Tip="{locale:Locale StopEmulationTooltip}"
|
||||
IsEnabled="{Binding IsGameRunning}" InputGesture="Escape" />
|
||||
<MenuItem Command="{ReflectionBinding SimulateWakeUpMessage}"
|
||||
Header="{locale:Locale MenuBarOptionsSimulateWakeUpMessage}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Name="ScanAmiiboMenuItem"
|
||||
AttachedToVisualTree="ScanAmiiboMenuItem_AttachedToVisualTree"
|
||||
Command="{ReflectionBinding OpenAmiiboWindow}"
|
||||
Header="{locale:Locale MenuBarActionsScanAmiibo}"
|
||||
IsEnabled="{Binding IsAmiiboRequested}" />
|
||||
<MenuItem Command="{ReflectionBinding TakeScreenshot}"
|
||||
IsEnabled="{Binding IsGameRunning}"
|
||||
Header="{locale:Locale MenuBarFileToolsTakeScreenshot}"
|
||||
InputGesture="{Binding ScreenshotKey}" />
|
||||
<MenuItem Command="{ReflectionBinding HideUi}"
|
||||
IsEnabled="{Binding IsGameRunning}"
|
||||
Header="{locale:Locale MenuBarFileToolsHideUi}"
|
||||
InputGesture="{Binding ShowUiKey}" />
|
||||
<MenuItem Command="{ReflectionBinding OpenCheatManagerForCurrentApp}"
|
||||
IsEnabled="{Binding IsGameRunning}"
|
||||
Header="{locale:Locale GameListContextMenuManageCheat}" />
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
VerticalAlignment="Center"
|
||||
Header="{locale:Locale MenuBarTools}">
|
||||
<MenuItem Header="{locale:Locale MenuBarToolsInstallFirmware}"
|
||||
IsEnabled="{Binding EnableNonGameRunningControls}">
|
||||
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}"
|
||||
Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
|
||||
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}"
|
||||
Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
VerticalAlignment="Center"
|
||||
Header="{locale:Locale MenuBarHelp}">
|
||||
<MenuItem
|
||||
Name="UpdateMenuItem"
|
||||
Command="{ReflectionBinding CheckForUpdates}"
|
||||
Header="{locale:Locale MenuBarHelpCheckForUpdates}"
|
||||
ToolTip.Tip="{locale:Locale CheckUpdatesTooltip}" />
|
||||
<Separator />
|
||||
<MenuItem Command="{ReflectionBinding OpenAboutWindow}"
|
||||
Header="{locale:Locale MenuBarHelpAbout}"
|
||||
ToolTip.Tip="{locale:Locale OpenAboutTooltip}" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</DockPanel>
|
||||
</StackPanel>
|
||||
<ContentControl
|
||||
Name="Content"
|
||||
Grid.Row="1"
|
||||
Padding="0"
|
||||
IsVisible="{Binding ShowContent}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="0,0,0,0"
|
||||
DockPanel.Dock="Top">
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<DockPanel Grid.Row="0" HorizontalAlignment="Stretch" Margin="0,0,0,5">
|
||||
<Button
|
||||
IsEnabled="{Binding IsGrid}" VerticalAlignment="Stretch" MinWidth="40" Width="40"
|
||||
Margin="5,2,0,2" Command="{ReflectionBinding SetListMode}">
|
||||
<ui:FontIcon FontFamily="avares://FluentAvalonia/Fonts#Symbols"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0"
|
||||
Glyph="{controls:GlyphValueConverter List}"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</Button>
|
||||
<Button
|
||||
IsEnabled="{Binding IsList}" VerticalAlignment="Stretch" MinWidth="40" Width="40"
|
||||
Margin="5,2,5,2" Command="{ReflectionBinding SetGridMode}">
|
||||
<ui:FontIcon FontFamily="avares://FluentAvalonia/Fonts#Symbols"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0"
|
||||
Glyph="{controls:GlyphValueConverter Grid}"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</Button>
|
||||
<TextBlock Text="{locale:Locale IconSize}"
|
||||
VerticalAlignment="Center" Margin="10,0"
|
||||
ToolTip.Tip="{locale:Locale IconSizeTooltip}" />
|
||||
<Slider Width="150" Margin="5,-10,5 ,0" Height="35"
|
||||
ToolTip.Tip="{locale:Locale IconSizeTooltip}"
|
||||
VerticalAlignment="Center" Minimum="1" Maximum="4" IsSnapToTickEnabled="True"
|
||||
TickFrequency="1" Value="{Binding GridSizeScale}" />
|
||||
<CheckBox Margin="0" IsChecked="{Binding ShowNames, Mode=TwoWay}" VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsGrid}">
|
||||
<TextBlock Text="{locale:Locale CommonShowNames}" Margin="5,3,0,0" />
|
||||
</CheckBox>
|
||||
<TextBox
|
||||
Name="SearchBox"
|
||||
DockPanel.Dock="Right"
|
||||
VerticalAlignment="Center"
|
||||
MinWidth="200"
|
||||
Margin="5,0,5,0"
|
||||
HorizontalAlignment="Right"
|
||||
KeyUp="SearchBox_OnKeyUp"
|
||||
Text="{Binding SearchText}"
|
||||
Watermark="{locale:Locale MenuSearch}" />
|
||||
<ui:DropDownButton DockPanel.Dock="Right"
|
||||
HorizontalAlignment="Right" Width="150" VerticalAlignment="Center"
|
||||
Content="{Binding SortName}">
|
||||
<ui:DropDownButton.Flyout>
|
||||
<Flyout Placement="Bottom">
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch" Margin="0">
|
||||
<StackPanel>
|
||||
<RadioButton Tag="Favorite"
|
||||
IsChecked="{Binding IsSortedByFavorite, Mode=OneTime}"
|
||||
GroupName="Sort"
|
||||
Content="{locale:Locale CommonFavorite}"
|
||||
Checked="Sort_Checked" />
|
||||
<RadioButton Tag="Title" GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
|
||||
Content="{locale:Locale GameListHeaderApplication}"
|
||||
Checked="Sort_Checked" />
|
||||
<RadioButton Tag="Developer" GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByDeveloper, Mode=OneTime}"
|
||||
Content="{locale:Locale GameListHeaderDeveloper}"
|
||||
Checked="Sort_Checked" />
|
||||
<RadioButton Tag="TotalTimePlayed" GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByTimePlayed, Mode=OneTime}"
|
||||
Content="{locale:Locale GameListHeaderTimePlayed}"
|
||||
Checked="Sort_Checked" />
|
||||
<RadioButton Tag="LastPlayed" GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByLastPlayed, Mode=OneTime}"
|
||||
Content="{locale:Locale GameListHeaderLastPlayed}"
|
||||
Checked="Sort_Checked" />
|
||||
<RadioButton Tag="FileType" GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByType, Mode=OneTime}"
|
||||
Content="{locale:Locale GameListHeaderFileExtension}"
|
||||
Checked="Sort_Checked" />
|
||||
<RadioButton Tag="FileSize" GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedBySize, Mode=OneTime}"
|
||||
Content="{locale:Locale GameListHeaderFileSize}"
|
||||
Checked="Sort_Checked" />
|
||||
<RadioButton Tag="Path" GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByPath, Mode=OneTime}"
|
||||
Content="{locale:Locale GameListHeaderPath}"
|
||||
Checked="Sort_Checked" />
|
||||
</StackPanel>
|
||||
<Border HorizontalAlignment="Stretch" Margin="5" Height="2" BorderBrush="White"
|
||||
Width="60" BorderThickness="0,1,0,0">
|
||||
<Separator HorizontalAlignment="Stretch" Height="0" />
|
||||
</Border>
|
||||
<RadioButton Tag="Ascending" IsChecked="{Binding IsAscending, Mode=OneTime}"
|
||||
GroupName="Order"
|
||||
Content="{locale:Locale OrderAscending}" Checked="Order_Checked" />
|
||||
<RadioButton Tag="Descending" GroupName="Order"
|
||||
IsChecked="{Binding !IsAscending, Mode=OneTime}"
|
||||
Content="{locale:Locale OrderDescending}" Checked="Order_Checked" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</ui:DropDownButton.Flyout>
|
||||
</ui:DropDownButton>
|
||||
<TextBlock DockPanel.Dock="Right" HorizontalAlignment="Right"
|
||||
Text="{locale:Locale CommonSort}" VerticalAlignment="Center" Margin="10,0" />
|
||||
</DockPanel>
|
||||
<controls:GameListView
|
||||
x:Name="GameList"
|
||||
Grid.Row="1"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
IsVisible="{Binding IsList}" />
|
||||
<controls:GameGridView
|
||||
x:Name="GameGrid"
|
||||
Grid.Row="1"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
IsVisible="{Binding IsGrid}" />
|
||||
</Grid>
|
||||
</ContentControl>
|
||||
<Grid Grid.Row="1"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{DynamicResource ThemeContentBackgroundColor}"
|
||||
IsVisible="{Binding ShowLoadProgress}"
|
||||
ZIndex="1000"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid
|
||||
HorizontalAlignment="Center"
|
||||
IsVisible="{Binding ShowLoadProgress}"
|
||||
Margin="40"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
Grid.Column="0"
|
||||
Grid.RowSpan="2"
|
||||
IsVisible="{Binding ShowLoadProgress}"
|
||||
Width="256"
|
||||
Height="256"
|
||||
Margin="10"
|
||||
Padding="4"
|
||||
BorderBrush="Black"
|
||||
BorderThickness="2"
|
||||
BoxShadow="4 4 32 8 #40000000"
|
||||
CornerRadius="3">
|
||||
<Image
|
||||
IsVisible="{Binding ShowLoadProgress}"
|
||||
Width="256"
|
||||
Height="256"
|
||||
Source="{Binding SelectedIcon, Converter={StaticResource ByteImage}}" />
|
||||
</Border>
|
||||
<Grid Grid.Column="1"
|
||||
IsVisible="{Binding ShowLoadProgress}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
IsVisible="{Binding ShowLoadProgress}"
|
||||
Grid.Row="0"
|
||||
Margin="10"
|
||||
FontSize="30"
|
||||
FontWeight="Bold"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding LoadHeading}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
IsVisible="{Binding ShowLoadProgress}"
|
||||
Grid.Row="1"
|
||||
CornerRadius="5"
|
||||
ClipToBounds="True"
|
||||
BorderBrush="{Binding ProgressBarBackgroundColor}"
|
||||
Padding="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="10"
|
||||
BorderThickness="1">
|
||||
<ProgressBar
|
||||
IsVisible="{Binding ShowLoadProgress}"
|
||||
Height="10"
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
CornerRadius="5"
|
||||
ClipToBounds="True"
|
||||
MinWidth="500"
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{Binding ProgressBarBackgroundColor}"
|
||||
Foreground="{Binding ProgressBarForegroundColor}"
|
||||
Maximum="{Binding ProgressMaximum}"
|
||||
Minimum="0"
|
||||
IsIndeterminate="{Binding IsLoadingIndeterminate}"
|
||||
Value="{Binding ProgressValue}" />
|
||||
</Border>
|
||||
<TextBlock
|
||||
IsVisible="{Binding ShowLoadProgress}"
|
||||
Grid.Row="2"
|
||||
Margin="10"
|
||||
FontSize="18"
|
||||
Text="{Binding CacheLoadStatus}"
|
||||
TextAlignment="Left" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid
|
||||
Name="StatusBar"
|
||||
Grid.Row="2"
|
||||
Height="30"
|
||||
Margin="0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{DynamicResource ThemeContentBackgroundColor}"
|
||||
VerticalAlignment="Bottom"
|
||||
DockPanel.Dock="Bottom"
|
||||
IsVisible="{Binding ShowMenuAndStatusBar}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" IsVisible="{Binding EnableNonGameRunningControls}"
|
||||
VerticalAlignment="Center" Margin="10,0">
|
||||
<Grid Margin="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Width="25"
|
||||
Height="25"
|
||||
MinWidth="0"
|
||||
Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent"
|
||||
Command="{ReflectionBinding LoadApplications}">
|
||||
<ui:SymbolIcon Symbol="Refresh" Height="100" Width="50" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
Name="LoadStatus"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding EnableNonGameRunningControls}"
|
||||
Text="{locale:Locale StatusBarGamesLoaded}" />
|
||||
<ProgressBar
|
||||
Name="LoadProgressBar"
|
||||
Grid.Column="2"
|
||||
Height="6"
|
||||
Maximum="{Binding StatusBarProgressMaximum}"
|
||||
Value="{Binding StatusBarProgressValue}"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource HighlightColor}"
|
||||
IsVisible="{Binding EnableNonGameRunningControls}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Margin="10,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsGameRunning}"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Name="VsyncStatus"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{Binding VsyncColor}"
|
||||
PointerReleased="VsyncStatus_PointerReleased"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Margin="0,0,5,0"
|
||||
Text="VSync"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Margin="2,0"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
BorderThickness="1"
|
||||
Height="12"
|
||||
BorderBrush="Gray" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
Name="DockedStatus"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
PointerReleased="DockedStatus_PointerReleased"
|
||||
Text="{Binding DockedStatusText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Margin="2,0"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
BorderThickness="1"
|
||||
Height="12"
|
||||
BorderBrush="Gray" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
Name="AspectRatioStatus"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
PointerReleased="AspectRatioStatus_PointerReleased"
|
||||
Text="{Binding AspectRatioStatusText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Margin="2,0"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
BorderThickness="1"
|
||||
Height="12"
|
||||
BorderBrush="Gray" />
|
||||
<ui:ToggleSplitButton
|
||||
Margin="-2,0,-3,0"
|
||||
Padding="5,0,0,5"
|
||||
Name="VolumeStatus"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
BorderBrush="{DynamicResource ThemeContentBackgroundColor}"
|
||||
Background="{DynamicResource ThemeContentBackgroundColor}"
|
||||
IsChecked="{Binding VolumeMuted}"
|
||||
Content="{Binding VolumeStatusText}">
|
||||
<ui:ToggleSplitButton.Flyout>
|
||||
<Flyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
|
||||
<Grid Margin="0">
|
||||
<Slider Value="{Binding Volume}"
|
||||
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
|
||||
Minimum="0"
|
||||
Maximum="1"
|
||||
TickFrequency="0.05"
|
||||
IsSnapToTickEnabled="True"
|
||||
Padding="0"
|
||||
Margin="0"
|
||||
SmallChange="0.01"
|
||||
LargeChange="0.05"
|
||||
Width="150" />
|
||||
</Grid>
|
||||
</Flyout>
|
||||
</ui:ToggleSplitButton.Flyout>
|
||||
</ui:ToggleSplitButton>
|
||||
<Border
|
||||
Width="2"
|
||||
Margin="2,0"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
BorderThickness="1"
|
||||
Height="12"
|
||||
BorderBrush="Gray" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding GameStatusText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
Margin="2,0"
|
||||
BorderThickness="1"
|
||||
Height="12"
|
||||
BorderBrush="Gray" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding FifoStatusText}"
|
||||
TextAlignment="Left" />
|
||||
<Border
|
||||
Width="2"
|
||||
Margin="2,0"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
BorderThickness="1"
|
||||
Height="12"
|
||||
BorderBrush="Gray" />
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
IsVisible="{Binding !ShowLoadProgress}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding GpuStatusText}"
|
||||
TextAlignment="Left" />
|
||||
</StackPanel>
|
||||
<StackPanel VerticalAlignment="Center" IsVisible="{Binding ShowFirmwareStatus}" Grid.Column="3"
|
||||
Orientation="Horizontal" Margin="10, 0">
|
||||
<TextBlock
|
||||
Name="FirmwareStatus"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0"
|
||||
Text="{locale:Locale StatusBarSystemVersion}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</window:StyleableWindow>
|
719
Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
Normal file
719
Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
Normal file
@@ -0,0 +1,719 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.Win32;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Input;
|
||||
using Ryujinx.Ava.Ui.Applet;
|
||||
using Ryujinx.Ava.Ui.Controls;
|
||||
using Ryujinx.Ava.Ui.Models;
|
||||
using Ryujinx.Ava.Ui.ViewModels;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Gpu;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.Modules;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.Ui.Common;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using InputManager = Ryujinx.Input.HLE.InputManager;
|
||||
using ProgressBar = Avalonia.Controls.ProgressBar;
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class MainWindow : StyleableWindow
|
||||
{
|
||||
private bool _canUpdate;
|
||||
private bool _isClosing;
|
||||
private bool _isLoading;
|
||||
|
||||
private Control _mainViewContent;
|
||||
|
||||
private UserChannelPersistence _userChannelPersistence;
|
||||
private static bool _deferLoad;
|
||||
private static string _launchPath;
|
||||
private static bool _startFullscreen;
|
||||
private string _currentEmulatedGamePath;
|
||||
internal readonly AvaHostUiHandler UiHandler;
|
||||
private AutoResetEvent _rendererWaitEvent;
|
||||
|
||||
public VirtualFileSystem VirtualFileSystem { get; private set; }
|
||||
public ContentManager ContentManager { get; private set; }
|
||||
public AccountManager AccountManager { get; private set; }
|
||||
|
||||
public LibHacHorizonManager LibHacHorizonManager { get; private set; }
|
||||
|
||||
public AppHost AppHost { get; private set; }
|
||||
public InputManager InputManager { get; private set; }
|
||||
|
||||
public RendererControl GlRenderer { get; private set; }
|
||||
public ContentControl ContentFrame { get; private set; }
|
||||
public TextBlock LoadStatus { get; private set; }
|
||||
public TextBlock FirmwareStatus { get; private set; }
|
||||
public TextBox SearchBox { get; private set; }
|
||||
public ProgressBar LoadProgressBar { get; private set; }
|
||||
public Menu Menu { get; private set; }
|
||||
public MenuItem UpdateMenuItem { get; private set; }
|
||||
public MenuItem ActionsMenuItem { get; private set; }
|
||||
public GameGridView GameGrid { get; private set; }
|
||||
public GameListView GameList { get; private set; }
|
||||
public OffscreenTextBox HiddenTextBox { get; private set; }
|
||||
public HotKeyControl FullscreenHotKey { get; private set; }
|
||||
public HotKeyControl FullscreenHotKey2 { get; private set; }
|
||||
public HotKeyControl DockToggleHotKey { get; private set; }
|
||||
public HotKeyControl ExitHotKey { get; private set; }
|
||||
public ToggleSplitButton VolumeStatus { get; set; }
|
||||
public MainWindowViewModel ViewModel { get; private set; }
|
||||
|
||||
public bool CanUpdate
|
||||
{
|
||||
get => _canUpdate;
|
||||
set
|
||||
{
|
||||
_canUpdate = value;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() => UpdateMenuItem.IsEnabled = _canUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ShowKeyErrorOnLoad { get; set; }
|
||||
public ApplicationLibrary ApplicationLibrary { get; set; }
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
ViewModel = new MainWindowViewModel(this);
|
||||
|
||||
DataContext = ViewModel;
|
||||
|
||||
InitializeComponent();
|
||||
AttachDebugDevTools();
|
||||
|
||||
UiHandler = new AvaHostUiHandler(this);
|
||||
|
||||
Title = $"Ryujinx {Program.Version}";
|
||||
|
||||
Height = Height / Program.WindowScaleFactor;
|
||||
Width = Width / Program.WindowScaleFactor;
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
Initialize();
|
||||
|
||||
ViewModel.Initialize();
|
||||
|
||||
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
|
||||
|
||||
LoadGameList();
|
||||
|
||||
CheckLaunchState();
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
Program.WindowScaleFactor = this.PlatformImpl.RenderScaling;
|
||||
}
|
||||
_rendererWaitEvent = new AutoResetEvent(false);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private void AttachDebugDevTools()
|
||||
{
|
||||
this.AttachDevTools();
|
||||
}
|
||||
|
||||
public void LoadGameList()
|
||||
{
|
||||
if (_isLoading)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isLoading = true;
|
||||
|
||||
ViewModel.LoadApplications();
|
||||
|
||||
_isLoading = false;
|
||||
}
|
||||
|
||||
private void Update_StatusBar(object sender, StatusUpdatedEventArgs args)
|
||||
{
|
||||
if (ViewModel.ShowMenuAndStatusBar && !ViewModel.ShowLoadProgress)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
if (args.VSyncEnabled)
|
||||
{
|
||||
ViewModel.VsyncColor = new SolidColorBrush(Color.Parse("#ff2eeac9"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewModel.VsyncColor = new SolidColorBrush(Color.Parse("#ffff4554"));
|
||||
}
|
||||
|
||||
ViewModel.DockedStatusText = args.DockedMode;
|
||||
ViewModel.AspectRatioStatusText = args.AspectRatio;
|
||||
ViewModel.GameStatusText = args.GameStatus;
|
||||
ViewModel.FifoStatusText = args.FifoStatus;
|
||||
ViewModel.GpuStatusText = args.GpuName;
|
||||
|
||||
ViewModel.ShowStatusSeparator = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Application_Opened(object sender, ApplicationOpenedEventArgs args)
|
||||
{
|
||||
if (args.Application != null)
|
||||
{
|
||||
ViewModel.SelectedIcon = args.Application.Icon;
|
||||
|
||||
string path = new FileInfo(args.Application.Path).FullName;
|
||||
|
||||
LoadApplication(path);
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
public async Task PerformanceCheck()
|
||||
{
|
||||
if (ConfigurationState.Instance.Logger.EnableTrace.Value)
|
||||
{
|
||||
string mainMessage = LocaleManager.Instance["DialogPerformanceCheckLoggingEnabledMessage"];
|
||||
string secondaryMessage = LocaleManager.Instance["DialogPerformanceCheckLoggingEnabledConfirmMessage"];
|
||||
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(this, mainMessage, secondaryMessage, LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]);
|
||||
|
||||
if (result != UserResult.Yes)
|
||||
{
|
||||
ConfigurationState.Instance.Logger.EnableTrace.Value = false;
|
||||
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value))
|
||||
{
|
||||
string mainMessage = LocaleManager.Instance["DialogPerformanceCheckShaderDumpEnabledMessage"];
|
||||
string secondaryMessage = LocaleManager.Instance["DialogPerformanceCheckShaderDumpEnabledConfirmMessage"];
|
||||
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(this, mainMessage, secondaryMessage, LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]);
|
||||
|
||||
if (result != UserResult.Yes)
|
||||
{
|
||||
ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = "";
|
||||
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void DeferLoadApplication(string launchPathArg, bool startFullscreenArg)
|
||||
{
|
||||
_deferLoad = true;
|
||||
_launchPath = launchPathArg;
|
||||
_startFullscreen = startFullscreenArg;
|
||||
}
|
||||
|
||||
#pragma warning disable CS1998
|
||||
public async void LoadApplication(string path, bool startFullscreen = false, string titleName = "")
|
||||
#pragma warning restore CS1998
|
||||
{
|
||||
if (AppHost != null)
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(this,
|
||||
LocaleManager.Instance["DialogLoadAppGameAlreadyLoadedMessage"],
|
||||
LocaleManager.Instance["DialogLoadAppGameAlreadyLoadedSubMessage"],
|
||||
LocaleManager.Instance["InputDialogOk"],
|
||||
"",
|
||||
LocaleManager.Instance["RyujinxInfo"]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#if RELEASE
|
||||
await PerformanceCheck();
|
||||
#endif
|
||||
|
||||
Logger.RestartTime();
|
||||
|
||||
if (ViewModel.SelectedIcon == null)
|
||||
{
|
||||
ViewModel.SelectedIcon = ApplicationLibrary.GetApplicationIcon(path);
|
||||
}
|
||||
|
||||
PrepareLoadScreen();
|
||||
|
||||
_mainViewContent = ContentFrame.Content as Control;
|
||||
|
||||
GlRenderer = new RendererControl(3, 3, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
|
||||
AppHost = new AppHost(GlRenderer, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this);
|
||||
|
||||
if (!AppHost.LoadGuestApplication().Result)
|
||||
{
|
||||
AppHost.DisposeContext();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance["LoadingHeading"], AppHost.Device.Application.TitleName) : titleName;
|
||||
ViewModel.TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName;
|
||||
|
||||
SwitchToGameControl(startFullscreen);
|
||||
|
||||
_currentEmulatedGamePath = path;
|
||||
Thread gameThread = new Thread(InitializeGame)
|
||||
{
|
||||
Name = "GUI.WindowThread"
|
||||
};
|
||||
gameThread.Start();
|
||||
}
|
||||
|
||||
private void InitializeGame()
|
||||
{
|
||||
GlRenderer.GlInitialized += GlRenderer_Created;
|
||||
|
||||
AppHost.StatusUpdatedEvent += Update_StatusBar;
|
||||
AppHost.AppExit += AppHost_AppExit;
|
||||
|
||||
_rendererWaitEvent.WaitOne();
|
||||
|
||||
AppHost?.Start();
|
||||
|
||||
AppHost.DisposeContext();
|
||||
}
|
||||
|
||||
|
||||
private void HandleRelaunch()
|
||||
{
|
||||
if (_userChannelPersistence.PreviousIndex != -1 && _userChannelPersistence.ShouldRestart)
|
||||
{
|
||||
_userChannelPersistence.ShouldRestart = false;
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
LoadApplication(_currentEmulatedGamePath);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, clear state.
|
||||
_userChannelPersistence = new UserChannelPersistence();
|
||||
_currentEmulatedGamePath = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SwitchToGameControl(bool startFullscreen = false)
|
||||
{
|
||||
ViewModel.ShowContent = true;
|
||||
ViewModel.ShowLoadProgress = false;
|
||||
ViewModel.IsLoadingIndeterminate = false;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
ContentFrame.Content = GlRenderer;
|
||||
|
||||
if (startFullscreen && WindowState != WindowState.FullScreen)
|
||||
{
|
||||
ViewModel.ToggleFullscreen();
|
||||
}
|
||||
|
||||
GlRenderer.Focus();
|
||||
});
|
||||
}
|
||||
|
||||
public void ShowLoading(bool startFullscreen = false)
|
||||
{
|
||||
ViewModel.ShowContent = false;
|
||||
ViewModel.ShowLoadProgress = true;
|
||||
ViewModel.IsLoadingIndeterminate = true;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
if (startFullscreen && WindowState != WindowState.FullScreen)
|
||||
{
|
||||
ViewModel.ToggleFullscreen();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void GlRenderer_Created(object sender, EventArgs e)
|
||||
{
|
||||
ShowLoading();
|
||||
|
||||
_rendererWaitEvent.Set();
|
||||
}
|
||||
|
||||
private void AppHost_AppExit(object sender, EventArgs e)
|
||||
{
|
||||
if (_isClosing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ViewModel.IsGameRunning = false;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
if (ContentFrame.Content != _mainViewContent)
|
||||
{
|
||||
ContentFrame.Content = _mainViewContent;
|
||||
}
|
||||
|
||||
ViewModel.ShowMenuAndStatusBar = true;
|
||||
ViewModel.ShowContent = true;
|
||||
ViewModel.ShowLoadProgress = false;
|
||||
ViewModel.IsLoadingIndeterminate = false;
|
||||
|
||||
AppHost = null;
|
||||
|
||||
HandleRelaunch();
|
||||
});
|
||||
GlRenderer.GlInitialized -= GlRenderer_Created;
|
||||
GlRenderer = null;
|
||||
|
||||
ViewModel.SelectedIcon = null;
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
Title = $"Ryujinx {Program.Version}";
|
||||
});
|
||||
}
|
||||
|
||||
public void Sort_Checked(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is RadioButton button)
|
||||
{
|
||||
var sort = Enum.Parse<ApplicationSort>(button.Tag.ToString());
|
||||
ViewModel.Sort(sort);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void HandleWindowStateChanged(WindowState state)
|
||||
{
|
||||
WindowState = state;
|
||||
|
||||
if (state != WindowState.Minimized)
|
||||
{
|
||||
Renderer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void Order_Checked(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is RadioButton button)
|
||||
{
|
||||
var tag = button.Tag.ToString();
|
||||
ViewModel.Sort(tag != "Descending");
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
_userChannelPersistence = new UserChannelPersistence();
|
||||
VirtualFileSystem = VirtualFileSystem.CreateInstance();
|
||||
LibHacHorizonManager = new LibHacHorizonManager();
|
||||
ContentManager = new ContentManager(VirtualFileSystem);
|
||||
|
||||
LibHacHorizonManager.InitializeFsServer(VirtualFileSystem);
|
||||
LibHacHorizonManager.InitializeArpServer();
|
||||
LibHacHorizonManager.InitializeBcatServer();
|
||||
LibHacHorizonManager.InitializeSystemClients();
|
||||
|
||||
ApplicationLibrary = new ApplicationLibrary(VirtualFileSystem);
|
||||
|
||||
// Save data created before we supported extra data in directory save data will not work properly if
|
||||
// given empty extra data. Luckily some of that extra data can be created using the data from the
|
||||
// save data indexer, which should be enough to check access permissions for user saves.
|
||||
// Every single save data's extra data will be checked and fixed if needed each time the emulator is opened.
|
||||
// Consider removing this at some point in the future when we don't need to worry about old saves.
|
||||
VirtualFileSystem.FixExtraData(LibHacHorizonManager.RyujinxClient);
|
||||
|
||||
AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient, Program.CommandLineProfile);
|
||||
|
||||
VirtualFileSystem.ReloadKeySet();
|
||||
|
||||
ApplicationHelper.Initialize(VirtualFileSystem, AccountManager, LibHacHorizonManager.RyujinxClient, this);
|
||||
|
||||
RefreshFirmwareStatus();
|
||||
}
|
||||
|
||||
protected async void CheckLaunchState()
|
||||
{
|
||||
if (ShowKeyErrorOnLoad)
|
||||
{
|
||||
ShowKeyErrorOnLoad = false;
|
||||
|
||||
Dispatcher.UIThread.Post(async () => await
|
||||
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, this));
|
||||
}
|
||||
|
||||
if (_deferLoad)
|
||||
{
|
||||
_deferLoad = false;
|
||||
|
||||
LoadApplication(_launchPath, _startFullscreen);
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false, this))
|
||||
{
|
||||
await Updater.BeginParse(this, false).ContinueWith(task =>
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}");
|
||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshFirmwareStatus()
|
||||
{
|
||||
SystemVersion version = null;
|
||||
try
|
||||
{
|
||||
version = ContentManager.GetCurrentFirmwareVersion();
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
bool hasApplet = false;
|
||||
|
||||
if (version != null)
|
||||
{
|
||||
LocaleManager.Instance.UpdateDynamicValue("StatusBarSystemVersion",
|
||||
version.VersionString);
|
||||
|
||||
hasApplet = version.Major > 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
LocaleManager.Instance.UpdateDynamicValue("StatusBarSystemVersion", "0.0");
|
||||
}
|
||||
|
||||
ViewModel.IsAppletMenuActive = hasApplet;
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
ContentFrame = this.FindControl<ContentControl>("Content");
|
||||
GameList = this.FindControl<GameListView>("GameList");
|
||||
LoadStatus = this.FindControl<TextBlock>("LoadStatus");
|
||||
FirmwareStatus = this.FindControl<TextBlock>("FirmwareStatus");
|
||||
LoadProgressBar = this.FindControl<ProgressBar>("LoadProgressBar");
|
||||
SearchBox = this.FindControl<TextBox>("SearchBox");
|
||||
Menu = this.FindControl<Menu>("Menu");
|
||||
UpdateMenuItem = this.FindControl<MenuItem>("UpdateMenuItem");
|
||||
GameGrid = this.FindControl<GameGridView>("GameGrid");
|
||||
HiddenTextBox = this.FindControl<OffscreenTextBox>("HiddenTextBox");
|
||||
FullscreenHotKey = this.FindControl<HotKeyControl>("FullscreenHotKey");
|
||||
FullscreenHotKey2 = this.FindControl<HotKeyControl>("FullscreenHotKey2");
|
||||
DockToggleHotKey = this.FindControl<HotKeyControl>("DockToggleHotKey");
|
||||
ExitHotKey = this.FindControl<HotKeyControl>("ExitHotKey");
|
||||
VolumeStatus = this.FindControl<ToggleSplitButton>("VolumeStatus");
|
||||
ActionsMenuItem = this.FindControl<MenuItem>("ActionsMenuItem");
|
||||
|
||||
VolumeStatus.Click += VolumeStatus_CheckedChanged;
|
||||
|
||||
GameGrid.ApplicationOpened += Application_Opened;
|
||||
|
||||
GameGrid.DataContext = ViewModel;
|
||||
|
||||
GameList.ApplicationOpened += Application_Opened;
|
||||
|
||||
GameList.DataContext = ViewModel;
|
||||
|
||||
LoadHotKeys();
|
||||
}
|
||||
|
||||
public static void UpdateGraphicsConfig()
|
||||
{
|
||||
int resScale = ConfigurationState.Instance.Graphics.ResScale;
|
||||
float resScaleCustom = ConfigurationState.Instance.Graphics.ResScaleCustom;
|
||||
|
||||
GraphicsConfig.ResScale = resScale == -1 ? resScaleCustom : resScale;
|
||||
GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy;
|
||||
GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
|
||||
GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
|
||||
}
|
||||
|
||||
public void LoadHotKeys()
|
||||
{
|
||||
HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt));
|
||||
HotKeyManager.SetHotKey(FullscreenHotKey2, new KeyGesture(Key.F11));
|
||||
HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9));
|
||||
HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape));
|
||||
}
|
||||
|
||||
public static void SaveConfig()
|
||||
{
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
}
|
||||
|
||||
public void UpdateGameMetadata(string titleId)
|
||||
{
|
||||
ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
|
||||
{
|
||||
DateTime lastPlayedDateTime = DateTime.Parse(appMetadata.LastPlayed);
|
||||
double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds;
|
||||
|
||||
appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
|
||||
});
|
||||
}
|
||||
|
||||
private void PrepareLoadScreen()
|
||||
{
|
||||
using MemoryStream stream = new MemoryStream(ViewModel.SelectedIcon);
|
||||
using var gameIconBmp = SixLabors.ImageSharp.Image.Load<Bgra32>(stream);
|
||||
|
||||
var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp).ToPixel<Bgra32>();
|
||||
|
||||
const int ColorDivisor = 4;
|
||||
|
||||
Color progressFgColor = Color.FromRgb(dominantColor.R, dominantColor.G, dominantColor.B);
|
||||
Color progressBgColor = Color.FromRgb(
|
||||
(byte)(dominantColor.R / ColorDivisor),
|
||||
(byte)(dominantColor.G / ColorDivisor),
|
||||
(byte)(dominantColor.B / ColorDivisor));
|
||||
|
||||
ViewModel.ProgressBarForegroundColor = new SolidColorBrush(progressFgColor);
|
||||
ViewModel.ProgressBarBackgroundColor = new SolidColorBrush(progressBgColor);
|
||||
}
|
||||
|
||||
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
ViewModel.SearchText = SearchBox.Text;
|
||||
}
|
||||
|
||||
private async void StopEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AppHost != null)
|
||||
{
|
||||
await AppHost.ShowExitPrompt();
|
||||
}
|
||||
}
|
||||
|
||||
private async void PauseEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
AppHost?.Pause();
|
||||
});
|
||||
}
|
||||
|
||||
private async void ResumeEmulation_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
AppHost?.Resume();
|
||||
});
|
||||
}
|
||||
|
||||
private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (sender is MenuItem)
|
||||
{
|
||||
ViewModel.IsAmiiboRequested = AppHost.Device.System.SearchingForAmiibo(out _);
|
||||
}
|
||||
}
|
||||
|
||||
private void VsyncStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
AppHost.Device.EnableDeviceVsync = !AppHost.Device.EnableDeviceVsync;
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {AppHost.Device.EnableDeviceVsync}");
|
||||
}
|
||||
|
||||
private void DockedStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
|
||||
}
|
||||
|
||||
private void AspectRatioStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
|
||||
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
|
||||
}
|
||||
|
||||
private void VolumeStatus_CheckedChanged(object sender, SplitButtonClickEventArgs e)
|
||||
{
|
||||
var volumeSplitButton = sender as ToggleSplitButton;
|
||||
if (ViewModel.IsGameRunning)
|
||||
{
|
||||
if (!volumeSplitButton.IsChecked)
|
||||
{
|
||||
AppHost.Device.SetVolume(ConfigurationState.Instance.System.AudioVolume);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppHost.Device.SetVolume(0);
|
||||
}
|
||||
|
||||
ViewModel.Volume = AppHost.Device.GetVolume();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
if (!_isClosing && AppHost != null && ConfigurationState.Instance.ShowConfirmExit)
|
||||
{
|
||||
e.Cancel = true;
|
||||
|
||||
ConfirmExit();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_isClosing = true;
|
||||
|
||||
if (AppHost != null)
|
||||
{
|
||||
AppHost.AppExit -= AppHost_AppExit;
|
||||
AppHost.AppExit += (sender, e) =>
|
||||
{
|
||||
AppHost = null;
|
||||
|
||||
Dispatcher.UIThread.Post(Close);
|
||||
};
|
||||
AppHost?.Stop();
|
||||
|
||||
e.Cancel = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationLibrary.CancelLoading();
|
||||
InputManager.Dispose();
|
||||
Program.Exit();
|
||||
|
||||
base.OnClosing(e);
|
||||
}
|
||||
|
||||
private void ConfirmExit()
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
_isClosing = await ContentDialogHelper.CreateExitDialog(this);
|
||||
|
||||
if (_isClosing)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
47
Ryujinx.Ava/Ui/Windows/StyleableWindow.cs
Normal file
47
Ryujinx.Ava/Ui/Windows/StyleableWindow.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class StyleableWindow : Window
|
||||
{
|
||||
public ContentDialog ContentDialog { get; private set; }
|
||||
public IBitmap IconImage { get; set; }
|
||||
|
||||
public StyleableWindow()
|
||||
{
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
TransparencyLevelHint = WindowTransparencyLevel.None;
|
||||
|
||||
using Stream stream = Assembly.GetAssembly(typeof(Ryujinx.Ui.Common.Configuration.ConfigurationState)).GetManifestResourceStream("Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
|
||||
|
||||
Icon = new WindowIcon(stream);
|
||||
stream.Position = 0;
|
||||
IconImage = new Bitmap(stream);
|
||||
}
|
||||
|
||||
public void LoadDialog()
|
||||
{
|
||||
ContentDialog = this.FindControl<ContentDialog>("ContentDialog");
|
||||
}
|
||||
|
||||
protected override void OnOpened(EventArgs e)
|
||||
{
|
||||
base.OnOpened(e);
|
||||
ContentDialog = this.FindControl<ContentDialog>("ContentDialog");
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
|
||||
ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.SystemChrome | ExtendClientAreaChromeHints.OSXThickTitleBar;
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx.Ava/Ui/Windows/TimeZone.cs
Normal file
16
Ryujinx.Ava/Ui/Windows/TimeZone.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class TimeZone
|
||||
{
|
||||
public TimeZone(string utcDifference, string location, string abbreviation)
|
||||
{
|
||||
UtcDifference = utcDifference;
|
||||
Location = location;
|
||||
Abbreviation = abbreviation;
|
||||
}
|
||||
|
||||
public string UtcDifference { get; set; }
|
||||
public string Location { get; set; }
|
||||
public string Abbreviation { get; set; }
|
||||
}
|
||||
}
|
37
Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml
Normal file
37
Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml
Normal file
@@ -0,0 +1,37 @@
|
||||
<window:StyleableWindow xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="350"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||
x:Class="Ryujinx.Ava.Ui.Windows.UpdaterWindow"
|
||||
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
|
||||
CanResize="False"
|
||||
SizeToContent="Height"
|
||||
Width="500" MinHeight="500" Height="500"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
MinWidth="500"
|
||||
Title="Ryujinx Updater">
|
||||
<Grid Margin="20" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="1" HorizontalAlignment="Stretch" TextAlignment="Center" Height="20" Name="MainText" />
|
||||
<TextBlock Height="20" HorizontalAlignment="Stretch" TextAlignment="Center" Name="SecondaryText" Grid.Row="2" />
|
||||
<ProgressBar IsVisible="False" HorizontalAlignment="Stretch" Name="ProgressBar" Maximum="100" Minimum="0"
|
||||
Margin="20" Grid.Row="3" />
|
||||
<StackPanel IsVisible="False" Name="ButtonBox" Orientation="Horizontal" Spacing="20" Grid.Row="4"
|
||||
HorizontalAlignment="Right">
|
||||
<Button Command="{Binding YesPressed}" MinWidth="50">
|
||||
<TextBlock TextAlignment="Center" Text="{locale:Locale InputDialogYes}" />
|
||||
</Button>
|
||||
<Button Command="{Binding NoPressed}" MinWidth="50">
|
||||
<TextBlock TextAlignment="Center" Text="{locale:Locale InputDialogNo}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</window:StyleableWindow>
|
93
Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml.cs
Normal file
93
Ryujinx.Ava/Ui/Windows/UpdaterWindow.axaml.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Modules;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Ava.Ui.Windows
|
||||
{
|
||||
public class UpdaterWindow : StyleableWindow
|
||||
{
|
||||
private readonly string _buildUrl;
|
||||
private readonly MainWindow _mainWindow;
|
||||
private readonly Version _newVersion;
|
||||
private bool _restartQuery;
|
||||
|
||||
public UpdaterWindow()
|
||||
{
|
||||
DataContext = this;
|
||||
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
Title = LocaleManager.Instance["RyujinxUpdater"];
|
||||
}
|
||||
|
||||
public UpdaterWindow(MainWindow mainWindow, Version newVersion, string buildUrl) : this()
|
||||
{
|
||||
_mainWindow = mainWindow;
|
||||
_newVersion = newVersion;
|
||||
_buildUrl = buildUrl;
|
||||
}
|
||||
|
||||
public TextBlock MainText { get; set; }
|
||||
public TextBlock SecondaryText { get; set; }
|
||||
public ProgressBar ProgressBar { get; set; }
|
||||
public StackPanel ButtonBox { get; set; }
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
MainText = this.FindControl<TextBlock>("MainText");
|
||||
SecondaryText = this.FindControl<TextBlock>("SecondaryText");
|
||||
ProgressBar = this.FindControl<ProgressBar>("ProgressBar");
|
||||
ButtonBox = this.FindControl<StackPanel>("ButtonBox");
|
||||
}
|
||||
|
||||
[DllImport("libc", SetLastError = true)]
|
||||
private static extern int chmod(string path, uint mode);
|
||||
|
||||
public void YesPressed()
|
||||
{
|
||||
if (_restartQuery)
|
||||
{
|
||||
string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx";
|
||||
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
||||
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().AsEnumerable().Skip(1).ToArray());
|
||||
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
chmod(ryuExe, 0777);
|
||||
}
|
||||
|
||||
Process.Start(ryuExe, ryuArg);
|
||||
|
||||
Environment.Exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ButtonBox.IsVisible = false;
|
||||
ProgressBar.IsVisible = true;
|
||||
|
||||
SecondaryText.Text = "";
|
||||
_restartQuery = true;
|
||||
|
||||
Updater.UpdateRyujinx(this, _buildUrl);
|
||||
}
|
||||
}
|
||||
|
||||
public void NoPressed()
|
||||
{
|
||||
_mainWindow.UpdateMenuItem.IsEnabled = true;
|
||||
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user