Created the Avalonia MVVM template, split the CLI into Avalonia.UI and Avalonia.Core

This commit is contained in:
2023-08-07 20:37:02 +05:30
parent 6b989db91b
commit 1e6ac3b1fc
22 changed files with 464 additions and 20 deletions

View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AutumnLauncher.Core\AutumnLauncher.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,73 @@
using CommandLine;
namespace AutumnLauncher.CLI;
// Command-line options for the --run argument
[Verb("run", HelpText = "Run a game.")]
public class RunOptions
{
[Option('i', "id", Required = true, HelpText = "Game ID.")]
public int Id { get; set; }
}
// Command-line options for the --remove argument
[Verb("remove", HelpText = "Remove a game.")]
public class RemoveOptions
{
[Option('i', "id", Required = true, HelpText = "Game ID.")]
public int Id { get; set; }
}
// Command-line options for the --list argument
[Verb("list", HelpText = "List all games.")]
public class ListOptions
{
}
// Command-line options for the --add argument
[Verb("add", HelpText = "Add a game.")]
public class AddOptions
{
[Option('n', "name", Required = true, HelpText = "Game name.")]
public string? Name { get; set; }
[Option('d', "developer", Required = true, HelpText = "Game developer.")]
public string? Developer { get; set; }
[Option('r', "date", Required = true, HelpText = "Release date.")]
public string? Date { get; set; }
[Option('g', "genre", Required = true, HelpText = "Game genre.")]
public string? Genre { get; set; }
[Option('t', "type", Required = true, HelpText = "Game type.")]
public string? Type { get; set; }
[Option('p', "path", Required = true, HelpText = "Game path.")]
public string? Path { get; set; }
}
[Verb("edit", HelpText = "Add a game.")]
public class EditOptions
{
[Option('i', "id", Required = true, HelpText = "Game id.")]
public int Id { get; set; }
[Option('n', "name", Required = false, HelpText = "Game name.")]
public string? Name { get; set; }
[Option('d', "developer", Required = false, HelpText = "Game developer.")]
public string? Developer { get; set; }
[Option('r', "date", Required = false, HelpText = "Release date.")]
public string? Date { get; set; }
[Option('g', "genre", Required = false, HelpText = "Game genre.")]
public string? Genre { get; set; }
[Option('t', "type", Required = false, HelpText = "Game type.")]
public string? Type { get; set; }
[Option('p', "path", Required = false, HelpText = "Game path.")]
public string? Path { get; set; }
}

View File

@@ -0,0 +1,53 @@
using CommandLine;
namespace AutumnLauncher.CLI;
public static class Program
{
public static void Main(string[] args)
{
// create directory and database if not already created
Directory.CreateDirectory(Configuration.DataDir);
Directory.CreateDirectory(Configuration.ConfigDir);
SqliteData.CreateDatabase();
Parser.Default.ParseArguments<AddOptions, ListOptions, RunOptions, RemoveOptions, EditOptions>(args)
.WithParsed<AddOptions>(RunAdd)
.WithParsed<ListOptions>(RunList)
.WithParsed<RemoveOptions>(RunRemove)
.WithParsed<RunOptions>(RunRun)
.WithParsed<EditOptions>(RunEdit);
//.WithNotParsed(HandleParseError);
}
private static void RunAdd(AddOptions options)
{
ControlActions.Add(options.Name, options.Developer, options.Date, options.Genre, options.Type, options.Path);
}
private static void RunList(ListOptions options)
{
ControlActions.List();
}
private static void RunRemove(RemoveOptions options)
{
ControlActions.Remove(options.Id);
}
private static void RunRun(RunOptions options)
{
ControlActions.Run(options.Id);
}
private static void RunEdit(EditOptions options)
{
ControlActions.Edit(options.Id, options.Name, options.Developer, options.Date, options.Genre, options.Type, options.Path);
}
// private static void HandleParseError(IEnumerable<Error> errors)
// {
// // Handle command-line argument parsing errors
// foreach (var error in errors) Console.WriteLine(error.ToString());
// }
}

View File

@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>AutumnLauncher</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Data.SQLite" Version="1.0.117" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,25 @@
using System.Runtime.InteropServices;
namespace AutumnLauncher;
public static class Configuration
{
public static string DataDir { get; set; } = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData,
Environment.SpecialFolderOption.Create), "AutumnLauncher");
public static string ConfigDir { get; set; } = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData,
Environment.SpecialFolderOption.Create), "AutumnLauncher");
public static LauncherPlatform Platform { get; set; } = GetPlatform();
private static LauncherPlatform GetPlatform()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return LauncherPlatform.Windows;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return LauncherPlatform.Linux;
return LauncherPlatform.Unsupported;
}
}

View File

@@ -0,0 +1,55 @@
using System.Diagnostics;
namespace AutumnLauncher;
public static class ControlActions
{
public static void Run(int num)
{
try
{
var game = SqliteData.GetGameById(num);
Process.Start(game.GamePath ?? throw new InvalidOperationException());
}
catch (Exception ex)
{
Console.WriteLine($"Error running game: {ex.Message}");
}
}
public static void List()
{
var games = SqliteData.GetAllGames();
foreach (var game in games)
{
Console.WriteLine(game + "\n");
}
}
public static void Add(string? gameName, string? developer, string? date, string? genre, string? type, string? path)
{
SqliteData.AddGame(gameName, developer, date, genre, type, path);
}
public static void Remove(int num)
{
SqliteData.RemoveGame(num);
}
public static void Edit(int num, string? gameName = null, string? developer = null, string? date = null,
string? genre = null, string? type = null, string? path = null)
{
var game = SqliteData.GetGameById(num);
if (!string.IsNullOrWhiteSpace(gameName)) game.GameName = gameName;
if (!string.IsNullOrWhiteSpace(developer)) game.GameDeveloper = developer;
if (!string.IsNullOrWhiteSpace(date)) game.GameReleaseDate = date;
if (!string.IsNullOrWhiteSpace(genre)) game.GameGenre = genre;
if (!string.IsNullOrWhiteSpace(type)) game.GameType = type;
if (!string.IsNullOrWhiteSpace(path)) game.GamePath = path;
SqliteData.EditGame(game.GameId, game.GameName, game.GameDeveloper, game.GameReleaseDate, game.GameGenre,
game.GameType, game.GamePath);
}
}

View File

@@ -0,0 +1,19 @@
namespace AutumnLauncher;
public class Game
{
public int GameId { get; set; }
public string? GameName { get; set; }
public string? GameDeveloper { get; set; }
public string? GameReleaseDate { get; set; }
public string? GameGenre { get; set; }
public string? GameType { get; set; }
public string? GamePath { get; set; }
public override string ToString()
{
return "Game ID: " + GameId + "\nGame Name: " + GameName + "\nGame Developer: " + GameDeveloper +
"\nGame Release Date: " + GameReleaseDate + "\nGame Genre: " + GameGenre +
"\nGame Type: " + GameType + "\nGame Path: " + GamePath;
}
}

View File

@@ -0,0 +1,8 @@
namespace AutumnLauncher;
public enum LauncherPlatform
{
Unsupported,
Windows,
Linux
}

View File

@@ -0,0 +1,159 @@
using System.Data.SQLite;
namespace AutumnLauncher;
public static class SqliteData
{
private static readonly string DataPath = Configuration.DataDir;
public static void CreateDatabase()
{
if (File.Exists(Path.Combine(Configuration.DataDir, "GameDB.sqlite"))) return;
Console.WriteLine("Creating Database...");
// Create a new SQLite database file
SQLiteConnection.CreateFile($"{DataPath}/GameDB.sqlite");
// Connect to the database
using var connection = new SQLiteConnection($"Data Source={DataPath}/GameDB.sqlite");
//var connection = new SQLiteConnection($"Data Source={DataPath}/GameDB.sqlite;Version=3;");
connection.Open();
// Create a Games table
const string createTableSql = "CREATE TABLE Games (GameID INTEGER PRIMARY KEY, GameName TEXT, Developer TEXT, ReleaseDate DATE, Genre TEXT, Type TEXT, FilePath TEXT)";
var createTableCmd = new SQLiteCommand(createTableSql, connection);
createTableCmd.ExecuteNonQuery();
// Close the database connection
connection.Close();
}
public static void AddGame(string? gameName, string? developer, string? date, string? genre, string? type,
string? path) // method for adding sample data
{
using var connection = new SQLiteConnection($"Data Source={DataPath}/GameDB.sqlite");
connection.Open();
// var command = connection.CreateCommand();
// Insert game data
const string insertSql = "INSERT INTO Games (GameName, Developer, ReleaseDate, Genre, Type, FilePath) VALUES (@GameName, @Developer, @ReleaseDate, @Genre, @Type, @FilePath)";
var insertGame = new SQLiteCommand(insertSql, connection);
insertGame.Parameters.AddWithValue("@GameName", gameName);
insertGame.Parameters.AddWithValue("@Developer", developer);
insertGame.Parameters.AddWithValue("@ReleaseDate", date);
insertGame.Parameters.AddWithValue("@Genre", genre);
insertGame.Parameters.AddWithValue("@Type", type);
insertGame.Parameters.AddWithValue("@FilePath", path);
insertGame.ExecuteNonQuery();
connection.Close();
}
public static void EditGame(int id, string? gameName, string? developer, string? date, string? genre, string? type,
string? path) // has to replace all at once
{
using var connection = new SQLiteConnection($"Data Source={DataPath}/GameDB.sqlite");
connection.Open();
const string editSql = "UPDATE Games SET GameName = @GameName, Developer = @Developer, ReleaseDate = @GameDate, " +
"Genre = @GameGenre, Type = @GameType, FilePath = @GamePath WHERE GameID = @Id";
var editGame = new SQLiteCommand(editSql, connection);
editGame.Parameters.AddWithValue("@Id", id);
editGame.Parameters.AddWithValue("@GameName", gameName);
editGame.Parameters.AddWithValue("@Developer", developer);
editGame.Parameters.AddWithValue("@GameDate", date);
editGame.Parameters.AddWithValue("@GameGenre", genre);
editGame.Parameters.AddWithValue("@GameType", type);
editGame.Parameters.AddWithValue("@GamePath", path);
editGame.ExecuteNonQuery();
connection.Close();
}
public static void RemoveGame(int? gameId)
{
using var connection = new SQLiteConnection($"Data Source={DataPath}/GameDB.sqlite");
connection.Open();
// var command = connection.CreateCommand();
// remove game data
const string insertSql = "DELETE FROM Games where GameID = @GameID";
var removeGame = new SQLiteCommand(insertSql, connection);
removeGame.Parameters.AddWithValue("@GameID", gameId);
removeGame.ExecuteNonQuery();
connection.Close();
}
public static List<Game> GetAllGames()
{
var games = new List<Game>();
using var connection = new SQLiteConnection($"Data Source={DataPath}/GameDB.sqlite");
connection.Open();
var command = connection.CreateCommand();
command.CommandText = @"SELECT * FROM Games";
using var reader = command.ExecuteReader();
while (reader.Read())
{
var game = new Game
{
GameId = reader.GetInt32(0),
GameName = reader.GetString(1),
GameDeveloper = reader.GetString(2),
GameReleaseDate = reader.GetString(3),
GameGenre = reader.GetString(4),
GameType = reader.GetString(5),
GamePath = reader.GetString(6)
};
games.Add(game);
}
return games;
}
// public static int NumOfGames()
// {
// using (var connection = new SQLiteConnection($"Data Source={DataPath}/GameDB.sqlite"))
// {
// connection.Open();
//
// var command = connection.CreateCommand();
// command.CommandText = @"SELECT COUNT(*) from Games";
//
// using (var reader = command.ExecuteReader())
// {
// while (reader.Read())
// {
// return reader.GetInt32(0);
// }
// }
// }
// return 0;
// }
public static Game GetGameById(int gameId)
{
using var connection = new SQLiteConnection($"Data Source={DataPath}/GameDB.sqlite");
connection.Open();
var command = connection.CreateCommand();
command.CommandText = @"SELECT * FROM Games WHERE GameID = @gameID LIMIT 1";
command.Parameters.AddWithValue("@gameID", gameId);
using var reader = command.ExecuteReader();
if (reader.Read())
return new Game
{
GameId = reader.GetInt32(0),
GameName = reader.GetString(1),
GameDeveloper = reader.GetString(2),
GameReleaseDate = reader.GetString(3),
GameGenre = reader.GetString(4),
GameType = reader.GetString(5),
GamePath = reader.GetString(6)
};
// Handle the case where no game was found with the specified ID
throw new Exception($"No game found with ID {gameId}");
}
}

View File

@@ -0,0 +1,11 @@
namespace AutumnLauncher;
public class Startup
{
public void AppStart()
{
Directory.CreateDirectory(Configuration.DataDir);
Directory.CreateDirectory(Configuration.ConfigDir);
SqliteData.CreateDatabase();
}
}

View File

@@ -1,7 +1,7 @@
<Application xmlns="https://github.com/avaloniaui" <Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AutumnLauncher.App" x:Class="AutumnLauncher.UI.App"
xmlns:local="using:AutumnLauncher" xmlns:local="using:AutumnLauncher.UI"
RequestedThemeVariant="Default"> RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. --> <!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

View File

@@ -1,10 +1,10 @@
using Avalonia; using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using AutumnLauncher.ViewModels; using AutumnLauncher.UI.ViewModels;
using AutumnLauncher.Views; using AutumnLauncher.UI.Views;
namespace AutumnLauncher; namespace AutumnLauncher.UI;
public partial class App : Application public partial class App : Application
{ {

View File

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 172 KiB

View File

@@ -2,7 +2,7 @@
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using System; using System;
namespace AutumnLauncher; namespace AutumnLauncher.UI;
class Program class Program
{ {

View File

@@ -1,9 +1,9 @@
using System; using System;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using AutumnLauncher.ViewModels; using AutumnLauncher.UI.ViewModels;
namespace AutumnLauncher; namespace AutumnLauncher.UI;
public class ViewLocator : IDataTemplate public class ViewLocator : IDataTemplate
{ {

View File

@@ -1,4 +1,4 @@
namespace AutumnLauncher.ViewModels; namespace AutumnLauncher.UI.ViewModels;
public class MainWindowViewModel : ViewModelBase public class MainWindowViewModel : ViewModelBase
{ {

View File

@@ -1,6 +1,6 @@
using ReactiveUI; using ReactiveUI;
namespace AutumnLauncher.ViewModels; namespace AutumnLauncher.UI.ViewModels;
public class ViewModelBase : ReactiveObject public class ViewModelBase : ReactiveObject
{ {

View File

@@ -1,13 +1,13 @@
<Window xmlns="https://github.com/avaloniaui" <Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:AutumnLauncher.ViewModels" xmlns:vm="using:AutumnLauncher.UI.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AutumnLauncher.Views.MainWindow" x:Class="AutumnLauncher.UI.Views.MainWindow"
x:DataType="vm:MainWindowViewModel" x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico" Icon="/Assets/avalonia-logo.ico"
Title="AutumnLauncher"> Title="AutumnLauncher.UI">
<Design.DataContext> <Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE, <!-- This only sets the DataContext for the previewer in an IDE,

View File

@@ -1,6 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
namespace AutumnLauncher.Views; namespace AutumnLauncher.UI.Views;
public partial class MainWindow : Window public partial class MainWindow : Window
{ {

View File

@@ -3,7 +3,7 @@
<!-- This manifest is used on Windows only. <!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embeded controls. Don't remove it as it might cause problems with window transparency and embeded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests --> For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="AutumnLauncher.Desktop"/> <assemblyIdentity version="1.0.0.0" name="AutumnLauncher.UI.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application> <application>

View File

@@ -1,6 +1,10 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutumnLauncher", "AutumnLauncher\AutumnLauncher.csproj", "{9A7C9986-943E-48C6-B0F3-96A0BBA925D1}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutumnLauncher.CLI", "AutumnLauncher.CLI\AutumnLauncher.CLI.csproj", "{848D68E5-10A6-4CF4-8B6F-4F3095B3468A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutumnLauncher.Core", "AutumnLauncher.Core\AutumnLauncher.Core.csproj", "{E9919AFD-C95F-4F13-A8FB-62387D56E237}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutumnLauncher.UI", "AutumnLauncher.UI\AutumnLauncher.UI.csproj", "{F9F1C18F-D201-4C43-99DE-D2881F8CA06A}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -8,9 +12,17 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9A7C9986-943E-48C6-B0F3-96A0BBA925D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {848D68E5-10A6-4CF4-8B6F-4F3095B3468A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A7C9986-943E-48C6-B0F3-96A0BBA925D1}.Debug|Any CPU.Build.0 = Debug|Any CPU {848D68E5-10A6-4CF4-8B6F-4F3095B3468A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A7C9986-943E-48C6-B0F3-96A0BBA925D1}.Release|Any CPU.ActiveCfg = Release|Any CPU {848D68E5-10A6-4CF4-8B6F-4F3095B3468A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A7C9986-943E-48C6-B0F3-96A0BBA925D1}.Release|Any CPU.Build.0 = Release|Any CPU {848D68E5-10A6-4CF4-8B6F-4F3095B3468A}.Release|Any CPU.Build.0 = Release|Any CPU
{E9919AFD-C95F-4F13-A8FB-62387D56E237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9919AFD-C95F-4F13-A8FB-62387D56E237}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9919AFD-C95F-4F13-A8FB-62387D56E237}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9919AFD-C95F-4F13-A8FB-62387D56E237}.Release|Any CPU.Build.0 = Release|Any CPU
{F9F1C18F-D201-4C43-99DE-D2881F8CA06A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F9F1C18F-D201-4C43-99DE-D2881F8CA06A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F9F1C18F-D201-4C43-99DE-D2881F8CA06A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F9F1C18F-D201-4C43-99DE-D2881F8CA06A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal