Commit bf0eacc2 authored by RedArcaneArcher's avatar RedArcaneArcher

port to avalonia framework

parent 92c5602c
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SunriseLauncher"
x:Class="SunriseLauncher.App">
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Resources>
<LinearGradientBrush x:Key="GradientHighlight" StartPoint="0%,50%" EndPoint="66%,50%" Opacity=".25" >
<GradientStop Color="Transparent" Offset="0.0" />
<GradientStop Color="White" Offset="0.5" />
<GradientStop Color="Transparent" Offset="1.0" />
</LinearGradientBrush>
<SolidColorBrush x:Key="TransparentBrush" Color="Transparent"/>
</Application.Resources>
<Application.Styles>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<!-- List Box Item -->
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{StaticResource TransparentBrush}"/>
</Style>
<Style Selector="ListBoxItem:selected /template/ ContentPresenter">
<Setter Property="Background" Value="{StaticResource GradientHighlight}"/>
</Style>
<Style Selector="ListBoxItem:selected:focus /template/ ContentPresenter">
<Setter Property="Background" Value="{StaticResource GradientHighlight}"/>
</Style>
<Style Selector="ListBoxItem:selected:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{StaticResource GradientHighlight}"/>
</Style>
<Style Selector="ListBoxItem:selected:focus:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{StaticResource GradientHighlight}"/>
</Style>
<!-- ScrollViewer -->
<Style Selector="ScrollViewer.Server">
<Style.Resources>
<SolidColorBrush x:Key="ThemeControlMidBrush" Color="Transparent" />
<SolidColorBrush x:Key="ThemeForegroundLowBrush" Color="#14466e" />
<SolidColorBrush x:Key="ThemeControlMidHighBrush" Color="#14466e" />
<SolidColorBrush x:Key="ThemeControlHighBrush" Color="#CC119EDA" />
<SolidColorBrush x:Key="ThemeControlVeryHighBrush" Color="#CC119EDA" />
</Style.Resources>
</Style>
<!-- Image Button Style -->
<Style Selector="Button.Image">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0"/>
</Style>
<Style Selector="Button.Image:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Opacity" Value=".6"/>
</Style>
<!-- Server List Style -->
<Style Selector="ListBox.Server">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled"/>
</Style>
<!-- Progress Bar Style -->
<Style Selector="ProgressBar.Files">
<Setter Property="Background" Value="DimGray" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Height" Value="4"/>
<Setter Property="MinHeight" Value="4"/>
</Style>
<!-- Text Label -->
<Style Selector="TextBlock.Label">
<Setter Property="Margin" Value="4"/>
<Setter Property="FontFamily" Value="Corbel,Trebuchet MS,Arial"/>
<Setter Property="FontSize" Value="16" />
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!-- Text Field -->
<Style Selector="TextBox.Field">
<Setter Property="Margin" Value="4"/>
<Setter Property="Height" Value="26"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!-- Button Field -->
<Style Selector="Button.Field">
<Setter Property="Margin" Value="4"/>
<Setter Property="Height" Value="26" />
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!-- Combo Box Field -->
<Style Selector="ComboBox.Field">
<Setter Property="Margin" Value="4"/>
<Setter Property="Height" Value="26" />
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</Application.Styles>
</Application>
\ No newline at end of file
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using SunriseLauncher.ViewModels;
using SunriseLauncher.Views;
using SunriseLauncher.Services;
using System.Threading.Tasks;
namespace SunriseLauncher
{
public class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow();
desktop.MainWindow.DataContext = new MainWindowViewModel(desktop.MainWindow);
}
base.OnFrameworkInitializationCompleted();
}
}
}
using System.Collections.Generic;
using System.Threading.Tasks;
namespace sunrise_launcher
{
public interface IManifest
{
public Task<ManifestMetadata> GetMetadataAsync();
public Task<IList<ManifestFile>> GetFilesAsync();
}
}
......@@ -4,7 +4,7 @@ using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
namespace sunrise_launcher
namespace SunriseLauncher.Models
{
public class Manifest : ManifestMetadata
{
......@@ -74,7 +74,7 @@ namespace sunrise_launcher
Console.WriteLine("illegal sequence in manifest file path: '..' in {0}", Path);
return false;
}
if (Path.Contains("~"))
{
Console.WriteLine("illegal sequence in manifest file path: '~' in {0}", Path);
......
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;
using ReactiveUI;
namespace SunriseLauncher.Models
{
public class Server : INotifyPropertyChanged
{
[JsonPropertyName("manifest_url")]
public string ManifestURL { get; set; }
[JsonPropertyName("install_path")]
public string InstallPath { get; set; }
[JsonPropertyName("metadata")]
public ManifestMetadata Metadata { get; set; }
private string launch;
[JsonPropertyName("launch")]
public string Launch
{
get => launch;
set
{
launch = value;
NotifyPropertyChanged();
}
}
private State state;
[JsonIgnore]
public State State
{
get => state;
set
{
state = value;
NotifyPropertyChanged();
}
}
private bool progressShow;
[JsonIgnore]
public bool ProgressShow
{
get => progressShow;
set
{
progressShow = value;
NotifyPropertyChanged();
}
}
private string progressDesc;
[JsonIgnore]
public string ProgressDesc
{
get => progressDesc;
set
{
progressDesc = value;
NotifyPropertyChanged();
}
}
private int progressValue;
[JsonIgnore]
public int ProgressValue
{
get => progressValue;
set
{
progressValue = value;
NotifyPropertyChanged();
}
}
private int progressMax;
[JsonIgnore]
public int ProgressMax
{
get => progressMax;
set
{
progressMax = value;
NotifyPropertyChanged();
}
}
private long progressValueFile;
[JsonIgnore]
public long ProgressValueFile
{
get => progressValueFile;
set
{
progressValueFile = value;
NotifyPropertyChanged();
}
}
private long progressMaxFile;
[JsonIgnore]
public long ProgressMaxFile
{
get => progressMaxFile;
set
{
progressMaxFile = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public enum State
{
Unchecked = 0,
Ready = 1,
Updating = 2,
Error = 3
}
}
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Avalonia.Data.Converters;
namespace SunriseLauncher.Models
{
public class StateConverterReady : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is null)
return false;
var state = (State)value;
return state == State.Ready;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class StateConverterNotUpdating : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is null)
return false;
var state = (State)value;
return state != State.Updating && state != State.Unchecked;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
using Qml.Net;
using Qml.Net.Runtimes;
using System;
using System;
using System.IO;
using System.Net;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Logging.Serilog;
using Avalonia.ReactiveUI;
namespace sunrise_launcher
namespace SunriseLauncher
{
public class Program
class Program
{
public static QGuiApplication App;
static int Main(string[] args)
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
public static void Main(string[] args)
{
using (var fileout = new FileStream("./log.txt", FileMode.Create, FileAccess.Write))
using (var writer = new StreamWriter(fileout))
......@@ -18,31 +20,19 @@ namespace sunrise_launcher
Console.SetOut(writer);
writer.AutoFlush = true;
try
{
RuntimeManager.DiscoverOrDownloadSuitableQtRuntime();
using (var app = new QGuiApplication(args))
{
App = app;
using (var engine = new QQmlApplicationEngine())
{
//register types
Qml.Net.Qml.RegisterType<ServerList>("sunrise", 1, 1);
Qml.Net.Qml.RegisterType<Server>("sunrise", 1, 1);
//load qml files
engine.Load("main.qml");
return app.Exec();
}
}
}
catch (Exception ex)
{
Console.WriteLine("exception in main: {0}", ex.Message);
return 1;
}
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
}
// => BuildAvaloniaApp()
// .StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.LogToDebug()
.UseReactiveUI();
}
}
{
"profiles": {
"sunrise-launcher": {
"commandName": "Project"
}
}
}
\ No newline at end of file
......@@ -2,8 +2,4 @@
A video game launcher.
## 3rd Party Libraries
[QML.NET](https://github.com/qmlnet/qmlnet)
## To Build - Windows
dotnet publish -r win-x64 -c release
\ No newline at end of file
[Avalonia](https://avaloniaui.net/)
\ No newline at end of file
using Qml.Net;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace sunrise_launcher
{
public class Server
{
[JsonPropertyName("manifest_url")]
[NotifySignal("manifestUrlChanged")]
public string ManifestURL { get; set; }
[JsonPropertyName("install_path")]
public string InstallPath { get; set; }
[JsonPropertyName("metadata")]
public ManifestMetadata Metadata { get; set; }
[JsonPropertyName("launch")]
[NotifySignal("launchChanged")]
public string Launch { get; set; }
[JsonIgnore]
public State State { get; set; }
[JsonIgnore]
public string Error { get; set; }
[JsonIgnore]
public string TaskName { get; set; }
[JsonIgnore]
public int TaskDone { get; set; }
[JsonIgnore]
public int TaskCount { get; set; }
}
public enum State
{
Unchecked = 0,
Ready = 1,
Updating = 2,
Error = 3
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace sunrise_launcher
{
public class ServerFile
{
[JsonPropertyName("servers")]
public List<Server> Servers { get; set; }
[JsonPropertyName("selected")]
public string Selected { get; set; }
public ServerFile()
{
Servers = new List<Server>();
}
public static ServerFile Load(string path)
{
if (!File.Exists(path))
return null;
var json = File.ReadAllText(path);
return JsonSerializer.Deserialize<ServerFile>(json);
}
public void Save(string path)
{
var json = JsonSerializer.Serialize(this);
File.WriteAllText(path, json);
}
}
}
This diff is collapsed.
using System;
using SunriseLauncher.Models;
using System;
using System.Security.Cryptography;
namespace sunrise_launcher
namespace SunriseLauncher.Services
{
public static class Hashing
public class Hashing
{
public static HashAlgorithm GetHashAlgorithm(ManifestFile file)
{
......
using SunriseLauncher.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace SunriseLauncher.Services
{
public class Launcher
{
public void Launch(Server server, LaunchOption launch)
{
try
{
if (launch == null)
return;
var fullpath = Path.Combine(server.InstallPath, launch.LaunchPath);
var process = new Process();
process.StartInfo.WorkingDirectory = Path.GetDirectoryName(fullpath);
process.StartInfo.FileName = fullpath;
process.StartInfo.Arguments = launch.Args;
process.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
process.Start();
}
catch (Exception ex)
{
Console.WriteLine("exception while launching: {0}", ex.Message);
}
}
}
}
using System.Threading.Tasks;
using SunriseLauncher.Models;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace sunrise_launcher
namespace SunriseLauncher.Services
{
public interface IManifestFactory
{
public IManifest Get(string manifesturl);
}
public class ManifestFactory : IManifestFactory
public class MainfestFactory
{
const string sunrise_api = "sunrise-api";
const string sunrise_json = "sunrise-json";
const string tequila_xml = "tequila-xml";
public IManifest Get(string manifesturl)
public static IManifest Get(string manifesturl)
{
var schema = getSchema(manifesturl);
switch (schema)
......@@ -28,7 +27,7 @@ namespace sunrise_launcher
return null;
}
private string getSchema(string manifesturl)
private static string getSchema(string manifesturl)
{
if (manifesturl.ToLower().EndsWith(".xml"))
return tequila_xml;
......@@ -38,4 +37,11 @@ namespace sunrise_launcher
return sunrise_api;
}
}
public interface IManifest
{
public Task<ManifestMetadata> GetMetadataAsync();
public Task<IList<ManifestFile>> GetFilesAsync();
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using SunriseLauncher.Models;
namespace SunriseLauncher.Services
{
public class ServerFile
{
const string path = "./servers.json";
public ServerFileJson Load()
{
if (!File.Exists(path))
return null;
try
{
var json = File.ReadAllText(path);
return JsonSerializer.Deserialize<ServerFileJson>(json);
}
catch(Exception ex)
{
Console.WriteLine("exception while reading serverfile: {0}", ex.Message);
}
return null;
}
public void Save(IEnumerable<Server> servers, string selected)
{
var file = new ServerFileJson();
file.Servers = servers.ToList();
file.Selected = selected;
var json = JsonSerializer.Serialize(file);
File.WriteAllText(path, json);
}
public class ServerFileJson
{
[JsonPropertyName("servers")]
public List<Server> Servers { get; set; }
[JsonPropertyName("selected")]
public string Selected { get; set; }
}
}
}
......@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace sunrise_launcher
namespace SunriseLauncher.Services
{
public static class Shuffler
{
......
using System;
using SunriseLauncher.Models;