From 880b27777a1cd7036a70733711e9c51ffadb93f0 Mon Sep 17 00:00:00 2001 From: Paolo Ambrosio Date: Mon, 1 May 2023 11:23:03 +0100 Subject: [PATCH 01/13] Blank WinUI 3 app --- AMS2CM.sln | 69 ++++++++++++++++++++++++++++++++++++-- src/GUI/App.xaml | 13 +++++++ src/GUI/App.xaml.cs | 19 +++++++++++ src/GUI/GUI.csproj | 20 +++++++++++ src/GUI/MainWindow.xaml | 13 +++++++ src/GUI/MainWindow.xaml.cs | 16 +++++++++ src/GUI/app.manifest | 17 ++++++++++ 7 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 src/GUI/App.xaml create mode 100644 src/GUI/App.xaml.cs create mode 100644 src/GUI/GUI.csproj create mode 100644 src/GUI/MainWindow.xaml create mode 100644 src/GUI/MainWindow.xaml.cs create mode 100644 src/GUI/app.manifest diff --git a/AMS2CM.sln b/AMS2CM.sln index d72fddf..8397dc8 100644 --- a/AMS2CM.sln +++ b/AMS2CM.sln @@ -1,22 +1,87 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLI", "src\CLI\CLI.csproj", "{77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}" +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33530.505 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CLI", "src\CLI\CLI.csproj", "{77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{A20048F0-D212-499D-8CCF-5E0B989E21F7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "src\Core\Core.csproj", "{A20048F0-D212-499D-8CCF-5E0B989E21F7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUI", "src\GUI\GUI.csproj", "{2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Debug|ARM64.Build.0 = Debug|Any CPU + {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Debug|x64.ActiveCfg = Debug|Any CPU + {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Debug|x64.Build.0 = Debug|Any CPU + {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Debug|x86.ActiveCfg = Debug|Any CPU + {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Debug|x86.Build.0 = Debug|Any CPU {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Release|Any CPU.ActiveCfg = Release|Any CPU {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Release|Any CPU.Build.0 = Release|Any CPU + {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Release|ARM64.ActiveCfg = Release|Any CPU + {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Release|ARM64.Build.0 = Release|Any CPU + {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Release|x64.ActiveCfg = Release|Any CPU + {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Release|x64.Build.0 = Release|Any CPU + {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Release|x86.ActiveCfg = Release|Any CPU + {77400FE1-383E-4D92-9A7F-D4AEA8A1AE0C}.Release|x86.Build.0 = Release|Any CPU {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Debug|ARM64.Build.0 = Debug|Any CPU + {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Debug|x64.ActiveCfg = Debug|Any CPU + {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Debug|x64.Build.0 = Debug|Any CPU + {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Debug|x86.ActiveCfg = Debug|Any CPU + {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Debug|x86.Build.0 = Debug|Any CPU {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Release|Any CPU.ActiveCfg = Release|Any CPU {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Release|Any CPU.Build.0 = Release|Any CPU + {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Release|ARM64.ActiveCfg = Release|Any CPU + {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Release|ARM64.Build.0 = Release|Any CPU + {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Release|x64.ActiveCfg = Release|Any CPU + {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Release|x64.Build.0 = Release|Any CPU + {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Release|x86.ActiveCfg = Release|Any CPU + {A20048F0-D212-499D-8CCF-5E0B989E21F7}.Release|x86.Build.0 = Release|Any CPU + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Debug|Any CPU.ActiveCfg = Debug|x64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Debug|Any CPU.Build.0 = Debug|x64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Debug|Any CPU.Deploy.0 = Debug|x64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Debug|ARM64.Build.0 = Debug|ARM64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Debug|x64.ActiveCfg = Debug|x64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Debug|x64.Build.0 = Debug|x64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Debug|x64.Deploy.0 = Debug|x64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Debug|x86.ActiveCfg = Debug|x86 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Debug|x86.Build.0 = Debug|x86 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Debug|x86.Deploy.0 = Debug|x86 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Release|Any CPU.ActiveCfg = Release|x64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Release|Any CPU.Build.0 = Release|x64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Release|Any CPU.Deploy.0 = Release|x64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Release|ARM64.ActiveCfg = Release|ARM64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Release|ARM64.Build.0 = Release|ARM64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Release|ARM64.Deploy.0 = Release|ARM64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Release|x64.ActiveCfg = Release|x64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Release|x64.Build.0 = Release|x64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Release|x64.Deploy.0 = Release|x64 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Release|x86.ActiveCfg = Release|x86 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Release|x86.Build.0 = Release|x86 + {2C96062E-2EDF-4BBE-8BC2-968B7A1F4EFE}.Release|x86.Deploy.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6C69475D-BCFD-4B42-A90D-845A72BFB1F8} EndGlobalSection EndGlobal diff --git a/src/GUI/App.xaml b/src/GUI/App.xaml new file mode 100644 index 0000000..5f4c61b --- /dev/null +++ b/src/GUI/App.xaml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/src/GUI/App.xaml.cs b/src/GUI/App.xaml.cs new file mode 100644 index 0000000..a68c378 --- /dev/null +++ b/src/GUI/App.xaml.cs @@ -0,0 +1,19 @@ +using Microsoft.UI.Xaml; + +namespace AMS2CM.GUI; + +public partial class App : Application +{ + public App() + { + this.InitializeComponent(); + } + + protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) + { + m_window = new MainWindow(); + m_window.Activate(); + } + + private Window m_window; +} diff --git a/src/GUI/GUI.csproj b/src/GUI/GUI.csproj new file mode 100644 index 0000000..545a389 --- /dev/null +++ b/src/GUI/GUI.csproj @@ -0,0 +1,20 @@ + + + WinExe + net6.0-windows10.0.19041.0 + 10.0.17763.0 + AMS2CM.GUI + app.manifest + x86;x64 + win10-x86;win10-x64 + win10-$(Platform).pubxml + true + true + + + + + + + + diff --git a/src/GUI/MainWindow.xaml b/src/GUI/MainWindow.xaml new file mode 100644 index 0000000..a560a13 --- /dev/null +++ b/src/GUI/MainWindow.xaml @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/GUI/MainWindow.xaml.cs b/src/GUI/MainWindow.xaml.cs new file mode 100644 index 0000000..882618c --- /dev/null +++ b/src/GUI/MainWindow.xaml.cs @@ -0,0 +1,16 @@ +using Microsoft.UI.Xaml; + +namespace AMS2CM.GUI; + +public sealed partial class MainWindow : Window +{ + public MainWindow() + { + this.InitializeComponent(); + } + + private void myButton_Click(object sender, RoutedEventArgs e) + { + myButton.Content = "Clicked"; + } +} diff --git a/src/GUI/app.manifest b/src/GUI/app.manifest new file mode 100644 index 0000000..51325a1 --- /dev/null +++ b/src/GUI/app.manifest @@ -0,0 +1,17 @@ + + + + + + + + + + + + + true/PM + PerMonitorV2, PerMonitor + + + \ No newline at end of file From 3cec41d866ae8894847d80a6a696f4ab84bc062f Mon Sep 17 00:00:00 2001 From: Paolo Ambrosio Date: Mon, 1 May 2023 11:41:02 +0100 Subject: [PATCH 02/13] Display current state --- src/CLI/CLI.csproj | 10 +--- src/{CLI => Core}/Config.cs | 0 src/Core/Core.csproj | 6 +++ src/Core/ModManager.cs | 86 +++++++++++++++++++++++++------- src/Core/ModState.cs | 8 +++ src/GUI/GUI.csproj | 11 ++++ src/GUI/MainWindow.xaml | 52 +++++++++++++++++-- src/GUI/MainWindow.xaml.cs | 22 +++++++- src/GUI/ModVM.cs | 29 +++++++++++ src/{CLI => Shared}/AMS2CM.ico | Bin src/{CLI => Shared}/Config.yaml | Bin 11 files changed, 192 insertions(+), 32 deletions(-) rename src/{CLI => Core}/Config.cs (100%) create mode 100644 src/Core/ModState.cs create mode 100644 src/GUI/ModVM.cs rename src/{CLI => Shared}/AMS2CM.ico (100%) rename src/{CLI => Shared}/Config.yaml (100%) diff --git a/src/CLI/CLI.csproj b/src/CLI/CLI.csproj index e70b46c..ecfed4d 100644 --- a/src/CLI/CLI.csproj +++ b/src/CLI/CLI.csproj @@ -9,21 +9,15 @@ OpenSimTools Contributors AMS2CM AMS2CM.CLI - AMS2CM.ico + ..\Shared\AMS2CM.ico - - - - - - - + Always diff --git a/src/CLI/Config.cs b/src/Core/Config.cs similarity index 100% rename from src/CLI/Config.cs rename to src/Core/Config.cs diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index fb34204..dc29cba 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -17,4 +17,10 @@ + + + + + + diff --git a/src/Core/ModManager.cs b/src/Core/ModManager.cs index 4e6eadf..40ccf75 100644 --- a/src/Core/ModManager.cs +++ b/src/Core/ModManager.cs @@ -1,4 +1,4 @@ -using Core.Games; +using Core.Games; using Core.Mods; using Newtonsoft.Json; using SevenZipExtractor; @@ -8,7 +8,8 @@ namespace Core; public class ModManager { private record WorkPaths( - string ModArchivesDir, + string EnabledModArchivesDir, + string DisabledModArchivesDir, string TempDir, string CurrentStateFile ); @@ -20,6 +21,7 @@ string CurrentStateFile private const string ModsDirName = "Mods"; private const string EnabledModsDirName = "Enabled"; + private const string DisabledModsSubdir = "Disabled"; private const string TempDirName = "Temp"; private const string CurrentStateFileName = "installed.json"; @@ -33,11 +35,13 @@ string CurrentStateFile public ModManager(IGame game, IModFactory modFactory) { + this.game = game; this.modFactory = modFactory; var modsDir = Path.Combine(game.InstallationDirectory, ModsDirName); workPaths = new WorkPaths( - ModArchivesDir: Path.Combine(modsDir, EnabledModsDirName), + EnabledModArchivesDir: Path.Combine(modsDir, EnabledModsDirName), + DisabledModArchivesDir: Path.Combine(modsDir, DisabledModsSubdir), TempDir: Path.Combine(modsDir, TempDirName), CurrentStateFile: Path.Combine(modsDir, CurrentStateFileName) ); @@ -54,6 +58,35 @@ private static void AddToEnvionmentPath(string additionalPath) Environment.SetEnvironmentVariable(pathEnvVar, $"{env};{additionalPath}"); } + public List FetchState() + { + var installedPackageNames = ReadPreviouslyInstalledFiles().Keys.Where(_ => !IsBootFiles(_)).ToHashSet(); + var enabledTuples = ListEnabledModPackages().Select(_ => (_, true)); + var disabledTuples = ListDisabledModPackages().Select(_ => (_, false)); + var availableTuples = enabledTuples.Concat(disabledTuples); + var availableModsState = availableTuples.Select(_ => + { + var packagePath = _._; + var isEnabled = _.Item2; + var packageName = PackageName(packagePath); + return new ModState( + PackageName: packageName, + PackagePath: packagePath, + IsEnabled: isEnabled, + IsInstalled: installedPackageNames.Contains(packageName) + ); + }); + var availablePackageNames = availableModsState.Select(_ => _.PackageName); + var unavailablePackageNames = installedPackageNames.Except(availablePackageNames); + var unavailableModsState = unavailablePackageNames.Select(packageName => new ModState( + PackageName: packageName, + PackagePath: null, + IsEnabled: null, + IsInstalled: true + )); + return unavailableModsState.Concat(availableModsState).ToList(); + } + public void InstallEnabledMods() { // It shoulnd't be needed, but some systems seem to want to load oo2core @@ -112,27 +145,18 @@ private void CheckNoBootfilesInstalled() private void InstallAllModFiles() { - if (!Directory.Exists(workPaths.ModArchivesDir)) - { - Console.WriteLine($"No mod archives found in {workPaths.ModArchivesDir}"); - return; - } + var modPackages = ListEnabledModPackages(); var modConfigs = new List(); - var modArchives = Directory.EnumerateFiles(workPaths.ModArchivesDir).ToList(); var installedFilesByMod = new Dictionary>(); try { - if (!modArchives.Any()) - { - Console.WriteLine($"No mod archives found in {workPaths.ModArchivesDir}"); - } - else + if (modPackages.Any()) { Console.WriteLine("Installing mods:"); - foreach (var archivePath in modArchives) + foreach (var packagePath in modPackages) { - var packageName = Path.GetFileNameWithoutExtension(archivePath); - if (packageName.StartsWith(BootfilesPrefix)) + var packageName = Path.GetFileNameWithoutExtension(packagePath); + if (IsBootFiles(packageName)) { Console.WriteLine($"- {packageName} (skipped)"); continue; @@ -140,7 +164,7 @@ private void InstallAllModFiles() Console.WriteLine($"- {packageName}"); - var mod = ExtractMod(packageName, archivePath); + var mod = ExtractMod(packageName, packagePath); try { mod.Install(game.InstallationDirectory); @@ -173,6 +197,10 @@ private void InstallAllModFiles() Console.WriteLine("Post-processing not required"); } } + else + { + Console.WriteLine($"No mod archives found in {workPaths.EnabledModArchivesDir}"); + } } finally { @@ -180,6 +208,8 @@ private void InstallAllModFiles() } } + private bool IsBootFiles(string packageName) => packageName.StartsWith(BootfilesPrefix); + private IMod ExtractMod(string packageName, string archivePath) { var extractionDir = Path.Combine(workPaths.TempDir, packageName); @@ -191,7 +221,7 @@ private IMod ExtractMod(string packageName, string archivePath) private IMod BootfilesMod() { - var bootfilesArchives = Directory.EnumerateFiles(workPaths.ModArchivesDir, $"{BootfilesPrefix}*.*"); + var bootfilesArchives = Directory.EnumerateFiles(workPaths.EnabledModArchivesDir, $"{BootfilesPrefix}*.*"); switch (bootfilesArchives.Count()) { case 0: @@ -212,6 +242,24 @@ private IMod BootfilesMod() } } + private IReadOnlyCollection ListEnabledModPackages() => ListModPackages(workPaths.EnabledModArchivesDir); + + private IReadOnlyCollection ListDisabledModPackages() => ListModPackages(workPaths.DisabledModArchivesDir); + + private IReadOnlyCollection ListModPackages(string path) + { + if (Directory.Exists(path)) + { + return Directory.EnumerateFiles(path).ToList(); + } + else + { + return Array.Empty(); + } + } + + private string PackageName(string archivePath) => Path.GetFileNameWithoutExtension(archivePath); + private Dictionary> ReadPreviouslyInstalledFiles() { if (!File.Exists(workPaths.CurrentStateFile)) { diff --git a/src/Core/ModState.cs b/src/Core/ModState.cs new file mode 100644 index 0000000..ead7eb6 --- /dev/null +++ b/src/Core/ModState.cs @@ -0,0 +1,8 @@ +namespace Core; + +public record ModState( + string PackageName, + string? PackagePath, + bool IsInstalled, + bool? IsEnabled +); diff --git a/src/GUI/GUI.csproj b/src/GUI/GUI.csproj index 545a389..560e5f7 100644 --- a/src/GUI/GUI.csproj +++ b/src/GUI/GUI.csproj @@ -10,11 +10,22 @@ win10-$(Platform).pubxml true true + ..\Shared\AMS2CM.ico + + + Always + + + + + + + diff --git a/src/GUI/MainWindow.xaml b/src/GUI/MainWindow.xaml index a560a13..f6e89fe 100644 --- a/src/GUI/MainWindow.xaml +++ b/src/GUI/MainWindow.xaml @@ -7,7 +7,53 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/GUI/MainWindow.xaml.cs b/src/GUI/MainWindow.xaml.cs index 882618c..16b941f 100644 --- a/src/GUI/MainWindow.xaml.cs +++ b/src/GUI/MainWindow.xaml.cs @@ -1,16 +1,34 @@ +using System; +using System.Linq; +using Core; +using Core.Games; using Microsoft.UI.Xaml; namespace AMS2CM.GUI; public sealed partial class MainWindow : Window { + private readonly ModManager modManager; + public MainWindow() { this.InitializeComponent(); + modManager = CreateModManager(); + ModListView.ItemsSource = modManager.FetchState().Select(modState => new ModVM { + PackageName = modState.PackageName, + PackagePath = modState.PackagePath, + IsInstalled = modState.IsInstalled, + IsEnabled = modState.IsEnabled ?? false, + IsInstallable = modState.IsEnabled is not null, + }); } - private void myButton_Click(object sender, RoutedEventArgs e) + private static ModManager CreateModManager() { - myButton.Content = "Clicked"; + var args = Environment.GetCommandLineArgs(); + var config = Config.Load(args); + var game = new Game(config.Game); + var modFactory = new ModFactory(config.ModInstall, game); + return new ModManager(game, modFactory); } } diff --git a/src/GUI/ModVM.cs b/src/GUI/ModVM.cs new file mode 100644 index 0000000..0f0918a --- /dev/null +++ b/src/GUI/ModVM.cs @@ -0,0 +1,29 @@ +namespace AMS2CM.GUI; + +internal class ModVM +{ + public string PackageName + { + get; set; + } + + public string PackagePath + { + get; set; + } + + public bool IsInstalled + { + get; set; + } + + public bool IsEnabled + { + get; set; + } + + public bool IsInstallable + { + get; set; + } +} diff --git a/src/CLI/AMS2CM.ico b/src/Shared/AMS2CM.ico similarity index 100% rename from src/CLI/AMS2CM.ico rename to src/Shared/AMS2CM.ico diff --git a/src/CLI/Config.yaml b/src/Shared/Config.yaml similarity index 100% rename from src/CLI/Config.yaml rename to src/Shared/Config.yaml From ba2ac342fc84e1e145cb9a0de128817fdf7613f1 Mon Sep 17 00:00:00 2001 From: Paolo Ambrosio Date: Mon, 1 May 2023 15:33:33 +0100 Subject: [PATCH 03/13] Enable/disable mods and sync --- src/Core/ModManager.cs | 17 +++++++++++++ src/GUI/App.xaml.cs | 2 +- src/GUI/MainWindow.xaml | 6 +++-- src/GUI/MainWindow.xaml.cs | 23 ++++++++++++------ src/GUI/ModVM.cs | 50 +++++++++++++++++++++++++++++--------- 5 files changed, 76 insertions(+), 22 deletions(-) diff --git a/src/Core/ModManager.cs b/src/Core/ModManager.cs index 40ccf75..31f9d9e 100644 --- a/src/Core/ModManager.cs +++ b/src/Core/ModManager.cs @@ -87,6 +87,23 @@ public List FetchState() return unavailableModsState.Concat(availableModsState).ToList(); } + public string EnableMod(string packagePath) + { + return InstallMod(packagePath, workPaths.EnabledModArchivesDir); + } + + public string DisableMod(string packagePath) + { + return InstallMod(packagePath, workPaths.DisabledModArchivesDir); + } + + public string InstallMod(string packagePath, string destinationDirectoryPath) + { + var destinationFilePath = Path.Combine(destinationDirectoryPath, Path.GetFileName(packagePath)); + File.Move(packagePath, destinationFilePath); + return destinationFilePath; + } + public void InstallEnabledMods() { // It shoulnd't be needed, but some systems seem to want to load oo2core diff --git a/src/GUI/App.xaml.cs b/src/GUI/App.xaml.cs index a68c378..b32fe7f 100644 --- a/src/GUI/App.xaml.cs +++ b/src/GUI/App.xaml.cs @@ -6,7 +6,7 @@ public partial class App : Application { public App() { - this.InitializeComponent(); + InitializeComponent(); } protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) diff --git a/src/GUI/MainWindow.xaml b/src/GUI/MainWindow.xaml index f6e89fe..561a915 100644 --- a/src/GUI/MainWindow.xaml +++ b/src/GUI/MainWindow.xaml @@ -40,7 +40,8 @@ + IsChecked="{x:Bind IsEnabled}" IsEnabled="{x:Bind IsAvailable}" + x:DefaultBindMode="TwoWay"/> Sync + x:Name="SyncButton" + Click="SyncButton_Click">Sync diff --git a/src/GUI/MainWindow.xaml.cs b/src/GUI/MainWindow.xaml.cs index 16b941f..c064185 100644 --- a/src/GUI/MainWindow.xaml.cs +++ b/src/GUI/MainWindow.xaml.cs @@ -12,15 +12,22 @@ public sealed partial class MainWindow : Window public MainWindow() { - this.InitializeComponent(); + InitializeComponent(); modManager = CreateModManager(); - ModListView.ItemsSource = modManager.FetchState().Select(modState => new ModVM { - PackageName = modState.PackageName, - PackagePath = modState.PackagePath, - IsInstalled = modState.IsInstalled, - IsEnabled = modState.IsEnabled ?? false, - IsInstallable = modState.IsEnabled is not null, - }); + SyncModListView(); + } + + private void SyncButton_Click(object sender, RoutedEventArgs e) + { + SyncButton.IsEnabled = false; + modManager.InstallEnabledMods(); + SyncModListView(); + SyncButton.IsEnabled = true; + } + + private void SyncModListView() + { + ModListView.ItemsSource = modManager.FetchState().Select(modState => new ModVM(modState, modManager)); } private static ModManager CreateModManager() diff --git a/src/GUI/ModVM.cs b/src/GUI/ModVM.cs index 0f0918a..5f1645f 100644 --- a/src/GUI/ModVM.cs +++ b/src/GUI/ModVM.cs @@ -1,29 +1,57 @@ -namespace AMS2CM.GUI; +using System.Runtime.CompilerServices; +using Core; +using Microsoft.UI.Composition; + +namespace AMS2CM.GUI; internal class ModVM { - public string PackageName + private readonly ModState modState; + private readonly ModManager modManager; + private bool isEnabled; + private string currentPackagePath; + + public ModVM(ModState modState, ModManager modManager) { - get; set; + this.modState = modState; + this.modManager = modManager; + isEnabled = modState.IsEnabled ?? false; + currentPackagePath = modState.PackagePath; } - public string PackagePath + public string PackageName => modState.PackageName; + + public bool IsInstalled => modState.IsInstalled; + + public bool IsEnabled { - get; set; + get => isEnabled; + set => EnableOrDisable(value); } - public bool IsInstalled + public bool IsAvailable { - get; set; + get => modState.IsEnabled is not null; + set => DoNothing(); } - public bool IsEnabled + private void EnableOrDisable(bool shouldEnable) { - get; set; + if (!IsAvailable || shouldEnable == isEnabled) + return; + + if (shouldEnable) + { + currentPackagePath = modManager.EnableMod(currentPackagePath); + } + else + { + currentPackagePath = modManager.DisableMod(currentPackagePath); + } + isEnabled = shouldEnable; } - public bool IsInstallable + private void DoNothing() { - get; set; } } From 3f51d694de87bbfb6087c718ba4479eb296b2dc7 Mon Sep 17 00:00:00 2001 From: Paolo Ambrosio Date: Mon, 1 May 2023 15:45:47 +0100 Subject: [PATCH 04/13] Resize window --- src/GUI/App.xaml.cs | 2 +- src/GUI/GUI.csproj | 1 + src/GUI/MainWindow.xaml.cs | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/GUI/App.xaml.cs b/src/GUI/App.xaml.cs index b32fe7f..a68c378 100644 --- a/src/GUI/App.xaml.cs +++ b/src/GUI/App.xaml.cs @@ -6,7 +6,7 @@ public partial class App : Application { public App() { - InitializeComponent(); + this.InitializeComponent(); } protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) diff --git a/src/GUI/GUI.csproj b/src/GUI/GUI.csproj index 560e5f7..5214b96 100644 --- a/src/GUI/GUI.csproj +++ b/src/GUI/GUI.csproj @@ -22,6 +22,7 @@ + diff --git a/src/GUI/MainWindow.xaml.cs b/src/GUI/MainWindow.xaml.cs index c064185..07f13c1 100644 --- a/src/GUI/MainWindow.xaml.cs +++ b/src/GUI/MainWindow.xaml.cs @@ -3,6 +3,7 @@ using Core; using Core.Games; using Microsoft.UI.Xaml; +using WinUIEx; namespace AMS2CM.GUI; @@ -14,6 +15,7 @@ public MainWindow() { InitializeComponent(); modManager = CreateModManager(); + this.SetWindowSize(600,600); SyncModListView(); } From b7259649ebf00a3f2a4170a4845734cf6c98fcdb Mon Sep 17 00:00:00 2001 From: Paolo Ambrosio Date: Tue, 2 May 2023 08:35:51 +0100 Subject: [PATCH 05/13] Drop file to install --- src/Core/ModManager.cs | 29 +++++++++++++++++++++--- src/GUI/App.xaml.cs | 2 +- src/GUI/MainWindow.xaml | 14 +++++++++--- src/GUI/MainWindow.xaml.cs | 46 ++++++++++++++++++++++++++++++++------ 4 files changed, 77 insertions(+), 14 deletions(-) diff --git a/src/Core/ModManager.cs b/src/Core/ModManager.cs index 31f9d9e..a9d0679 100644 --- a/src/Core/ModManager.cs +++ b/src/Core/ModManager.cs @@ -87,23 +87,46 @@ public List FetchState() return unavailableModsState.Concat(availableModsState).ToList(); } + public ModState EnableNewMod(string packagePath) + { + var destinationDirectoryPath = workPaths.EnabledModArchivesDir; + ExistingDirectoryOrCreate(destinationDirectoryPath); + var destinationFilePath = Path.Combine(destinationDirectoryPath, Path.GetFileName(packagePath)); + File.Copy(packagePath, destinationFilePath); + return new ModState( + PackageName: PackageName(destinationFilePath), + PackagePath: destinationFilePath, + IsEnabled: true, + IsInstalled: false + ); + } + public string EnableMod(string packagePath) { - return InstallMod(packagePath, workPaths.EnabledModArchivesDir); + return MoveMod(packagePath, workPaths.EnabledModArchivesDir); } public string DisableMod(string packagePath) { - return InstallMod(packagePath, workPaths.DisabledModArchivesDir); + return MoveMod(packagePath, workPaths.DisabledModArchivesDir); } - public string InstallMod(string packagePath, string destinationDirectoryPath) + private string MoveMod(string packagePath, string destinationDirectoryPath) { + ExistingDirectoryOrCreate(destinationDirectoryPath); var destinationFilePath = Path.Combine(destinationDirectoryPath, Path.GetFileName(packagePath)); File.Move(packagePath, destinationFilePath); return destinationFilePath; } + private static void ExistingDirectoryOrCreate(string directoryPath) + { + if (!Directory.Exists(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + } + public void InstallEnabledMods() { // It shoulnd't be needed, but some systems seem to want to load oo2core diff --git a/src/GUI/App.xaml.cs b/src/GUI/App.xaml.cs index a68c378..b32fe7f 100644 --- a/src/GUI/App.xaml.cs +++ b/src/GUI/App.xaml.cs @@ -6,7 +6,7 @@ public partial class App : Application { public App() { - this.InitializeComponent(); + InitializeComponent(); } protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) diff --git a/src/GUI/MainWindow.xaml b/src/GUI/MainWindow.xaml index 561a915..a0bcb05 100644 --- a/src/GUI/MainWindow.xaml +++ b/src/GUI/MainWindow.xaml @@ -20,12 +20,19 @@ + Symbol="Download" + ToolTipService.ToolTip="Installed"/> + Symbol="Import" + ToolTipService.ToolTip="To Install"/> - + @@ -54,6 +61,7 @@ diff --git a/src/GUI/MainWindow.xaml.cs b/src/GUI/MainWindow.xaml.cs index 07f13c1..e11e8c0 100644 --- a/src/GUI/MainWindow.xaml.cs +++ b/src/GUI/MainWindow.xaml.cs @@ -1,14 +1,18 @@ using System; +using System.Collections.ObjectModel; using System.Linq; using Core; using Core.Games; using Microsoft.UI.Xaml; +using Windows.ApplicationModel.DataTransfer; +using Windows.Storage; using WinUIEx; namespace AMS2CM.GUI; public sealed partial class MainWindow : Window { + private readonly ObservableCollection modList; private readonly ModManager modManager; public MainWindow() @@ -16,9 +20,20 @@ public MainWindow() InitializeComponent(); modManager = CreateModManager(); this.SetWindowSize(600,600); + modList = new ObservableCollection(); + ModListView.ItemsSource = modList; SyncModListView(); } + private static ModManager CreateModManager() + { + var args = Environment.GetCommandLineArgs(); + var config = Config.Load(args); + var game = new Game(config.Game); + var modFactory = new ModFactory(config.ModInstall, game); + return new ModManager(game, modFactory); + } + private void SyncButton_Click(object sender, RoutedEventArgs e) { SyncButton.IsEnabled = false; @@ -29,15 +44,32 @@ private void SyncButton_Click(object sender, RoutedEventArgs e) private void SyncModListView() { - ModListView.ItemsSource = modManager.FetchState().Select(modState => new ModVM(modState, modManager)); + modList.Clear(); + foreach (var modState in modManager.FetchState()) + { + modList.Add(new ModVM(modState, modManager)); + } } - private static ModManager CreateModManager() + private void ModListView_DragOver(object sender, DragEventArgs e) { - var args = Environment.GetCommandLineArgs(); - var config = Config.Load(args); - var game = new Game(config.Game); - var modFactory = new ModFactory(config.ModInstall, game); - return new ModManager(game, modFactory); + e.AcceptedOperation = DataPackageOperation.Copy; + } + + private async void ModListView_Drop(object sender, DragEventArgs e) + { + if (e.DataView.Contains(StandardDataFormats.StorageItems)) + { + var items = await e.DataView.GetStorageItemsAsync(); + if (items.Count > 0) + { + foreach (var storageFile in items.OfType()) + { + var filePath = storageFile.Path; + var modState = modManager.EnableNewMod(filePath); + modList.Add(new ModVM(modState, modManager)); + } + } + } } } From c677bc0b0493e8152dc20f58c189226d26a24fb4 Mon Sep 17 00:00:00 2001 From: Paolo Ambrosio Date: Tue, 2 May 2023 17:48:05 +0100 Subject: [PATCH 06/13] GitHub Actions --- .github/workflows/ci.yaml | 61 ++++++++++++++++++++++++++------------- LICENSE | 2 +- src/GUI/GUI.csproj | 4 +-- 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 53b53ad..2035be8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -8,27 +8,48 @@ on: - main jobs: build: + strategy: + matrix: + configuration: [Release] + platform: [x64] runs-on: windows-latest permissions: packages: read steps: - - uses: actions/checkout@v3 - - uses: actions/setup-dotnet@v2 - with: - dotnet-version: '6.0.x' - - if: ${{ github.ref == 'refs/heads/main' }} - run: | - dotnet nuget add source --username USERNAME --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/OpenSimTools/index.json" - dotnet publish -c Release - - if: ${{ github.ref == 'refs/heads/main' }} - uses: actions/upload-artifact@v3 - with: - name: AMS2CM - path: src/CLI/bin/Release/net6.0-windows/publish/** - if-no-files-found: error - - if: ${{ github.ref == 'refs/heads/main' }} - uses: actions/upload-artifact@v3 - with: - name: AMS2CM - path: LICENSE - if-no-files-found: error + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Install .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 6.0.x + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v1.0.2 + - name: Configure NuGet repository + run: dotnet nuget add source --username USERNAME --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/OpenSimTools/index.json" + + - name: Build + run: | + msbuild /t:Restore /p:Configuration=${{ matrix.configuration }} + msbuild /t:Publish /p:Configuration=${{ matrix.configuration }} /p:Platform=${{ matrix.platform }} + + - name: Upload CLI + uses: actions/upload-artifact@v3 + with: + name: AMS2CM + path: src/CLI/bin/${{ matrix.configuration }}/net6.0-windows/AMS2CM.* + if-no-files-found: error + - name: Upload GUI + uses: actions/upload-artifact@v3 + with: + name: AMS2CM + path: src/GUI/bin/${{ matrix.platform }}/${{ matrix.configuration }}/net6.0-windows10.0.19041.0/** + if-no-files-found: error + - name: Upload license + uses: actions/upload-artifact@v3 + with: + name: AMS2CM + path: LICENSE + if-no-files-found: error diff --git a/LICENSE b/LICENSE index 653deb4..7eb47e5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2023 OpenSimTools Contributors +Copyright (c) 2023 Open Sim Tools Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/GUI/GUI.csproj b/src/GUI/GUI.csproj index 5214b96..14131cf 100644 --- a/src/GUI/GUI.csproj +++ b/src/GUI/GUI.csproj @@ -1,13 +1,13 @@ - + WinExe net6.0-windows10.0.19041.0 10.0.17763.0 + AMS2CM.GUI AMS2CM.GUI app.manifest x86;x64 win10-x86;win10-x64 - win10-$(Platform).pubxml true true ..\Shared\AMS2CM.ico From f799b2b76dd5e2c45994165bc56e584a46a0799d Mon Sep 17 00:00:00 2001 From: Paolo Ambrosio Date: Tue, 2 May 2023 21:10:06 +0100 Subject: [PATCH 07/13] Drag and drop to remove mods --- src/GUI/MainWindow.xaml | 7 +++++-- src/GUI/MainWindow.xaml.cs | 20 ++++++++++++++++++++ src/GUI/ModVM.cs | 2 ++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/GUI/MainWindow.xaml b/src/GUI/MainWindow.xaml index a0bcb05..44fea8d 100644 --- a/src/GUI/MainWindow.xaml +++ b/src/GUI/MainWindow.xaml @@ -29,10 +29,13 @@ + Drop="ModListView_Drop" + CanDragItems="True" + DragItemsStarting="ModListView_DragItemsStarting" + DragItemsCompleted="ModListView_DragItemsCompleted"> diff --git a/src/GUI/MainWindow.xaml.cs b/src/GUI/MainWindow.xaml.cs index e11e8c0..50972e4 100644 --- a/src/GUI/MainWindow.xaml.cs +++ b/src/GUI/MainWindow.xaml.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Core; @@ -72,4 +73,23 @@ private async void ModListView_Drop(object sender, DragEventArgs e) } } } + + private async void ModListView_DragItemsStarting(object sender, Microsoft.UI.Xaml.Controls.DragItemsStartingEventArgs e) + { + var storageItems = new List(); + foreach (var o in e.Items) + { + var mvm = (ModVM)o; + var si = await StorageFile.GetFileFromPathAsync(mvm.PackagePath); + storageItems.Add(si); + } + e.Data.SetStorageItems(storageItems); + + e.Data.RequestedOperation = DataPackageOperation.Move; + } + + private void ModListView_DragItemsCompleted(Microsoft.UI.Xaml.Controls.ListViewBase sender, Microsoft.UI.Xaml.Controls.DragItemsCompletedEventArgs args) + { + SyncModListView(); + } } diff --git a/src/GUI/ModVM.cs b/src/GUI/ModVM.cs index 5f1645f..c0ad69f 100644 --- a/src/GUI/ModVM.cs +++ b/src/GUI/ModVM.cs @@ -21,6 +21,8 @@ public ModVM(ModState modState, ModManager modManager) public string PackageName => modState.PackageName; + public string PackagePath => currentPackagePath; + public bool IsInstalled => modState.IsInstalled; public bool IsEnabled From 9624c4f188b87a7983cb4051d5622e0960cffd6d Mon Sep 17 00:00:00 2001 From: Paolo Ambrosio Date: Tue, 2 May 2023 21:33:03 +0100 Subject: [PATCH 08/13] Mod list context menu --- src/GUI/MainWindow.xaml | 10 ++++++++++ src/GUI/MainWindow.xaml.cs | 23 +++++++++++++++++++++++ src/GUI/ModVM.cs | 8 +++++--- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/GUI/MainWindow.xaml b/src/GUI/MainWindow.xaml index 44fea8d..362497a 100644 --- a/src/GUI/MainWindow.xaml +++ b/src/GUI/MainWindow.xaml @@ -36,6 +36,16 @@ CanDragItems="True" DragItemsStarting="ModListView_DragItemsStarting" DragItemsCompleted="ModListView_DragItemsCompleted"> + + + + + + + + + + diff --git a/src/GUI/MainWindow.xaml.cs b/src/GUI/MainWindow.xaml.cs index 50972e4..afe39c1 100644 --- a/src/GUI/MainWindow.xaml.cs +++ b/src/GUI/MainWindow.xaml.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using Microsoft.VisualBasic.FileIO; using System.Linq; using Core; using Core.Games; @@ -92,4 +93,26 @@ private void ModListView_DragItemsCompleted(Microsoft.UI.Xaml.Controls.ListViewB { SyncModListView(); } + + private void ModListMenuToInstall_Click(object sender, RoutedEventArgs e) + { + foreach (var o in ModListView.SelectedItems) + { + var mvm = (ModVM)o; + mvm.IsEnabled = true; + } + } + + private void ModListMenuDelete_Click(object sender, RoutedEventArgs e) + { + foreach (var o in ModListView.SelectedItems) + { + var mvm = (ModVM)o; + if (mvm.IsAvailable) + { + FileSystem.DeleteFile(mvm.PackagePath, UIOption.AllDialogs, RecycleOption.SendToRecycleBin); + } + } + SyncModListView(); + } } diff --git a/src/GUI/ModVM.cs b/src/GUI/ModVM.cs index c0ad69f..1ea0256 100644 --- a/src/GUI/ModVM.cs +++ b/src/GUI/ModVM.cs @@ -1,16 +1,17 @@ -using System.Runtime.CompilerServices; +using System.ComponentModel; using Core; -using Microsoft.UI.Composition; namespace AMS2CM.GUI; -internal class ModVM +internal class ModVM : INotifyPropertyChanged { private readonly ModState modState; private readonly ModManager modManager; private bool isEnabled; private string currentPackagePath; + public event PropertyChangedEventHandler PropertyChanged; + public ModVM(ModState modState, ModManager modManager) { this.modState = modState; @@ -51,6 +52,7 @@ private void EnableOrDisable(bool shouldEnable) currentPackagePath = modManager.DisableMod(currentPackagePath); } isEnabled = shouldEnable; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsEnabled))); } private void DoNothing() From 857104535f9aa096b0e24c006b7c93b9aec3b4af Mon Sep 17 00:00:00 2001 From: Paolo Ambrosio Date: Sat, 27 May 2023 10:25:10 +0100 Subject: [PATCH 09/13] Build GUI in separate package --- .github/workflows/ci.yaml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2035be8..9267718 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -35,21 +35,22 @@ jobs: msbuild /t:Restore /p:Configuration=${{ matrix.configuration }} msbuild /t:Publish /p:Configuration=${{ matrix.configuration }} /p:Platform=${{ matrix.platform }} - - name: Upload CLI + - name: Upload license uses: actions/upload-artifact@v3 with: name: AMS2CM - path: src/CLI/bin/${{ matrix.configuration }}/net6.0-windows/AMS2CM.* + path: LICENSE if-no-files-found: error - - name: Upload GUI + - name: Upload CLI uses: actions/upload-artifact@v3 with: name: AMS2CM - path: src/GUI/bin/${{ matrix.platform }}/${{ matrix.configuration }}/net6.0-windows10.0.19041.0/** + path: src/CLI/bin/${{ matrix.configuration }}/net6.0-windows/publish/** if-no-files-found: error - - name: Upload license + # Separate package until stable, to be able to release CLI independently + - name: Upload GUI uses: actions/upload-artifact@v3 with: - name: AMS2CM - path: LICENSE + name: AMS2CM GUI + path: src/GUI/bin/${{ matrix.platform }}/${{ matrix.configuration }}/net6.0-windows10.0.19041.0/** if-no-files-found: error From cfaa9f765b00f80cde386e2c8e1b6cad5a4a74c5 Mon Sep 17 00:00:00 2001 From: Paolo Ambrosio Date: Sat, 27 May 2023 10:26:12 +0100 Subject: [PATCH 10/13] Icon, title and size in xaml --- src/CLI/CLI.csproj | 28 +++++++++--------- src/Core/Core.csproj | 2 -- src/GUI/App.xaml.cs | 8 +++--- src/GUI/GUI.csproj | 58 ++++++++++++++++++++------------------ src/GUI/MainWindow.xaml | 10 +++++-- src/GUI/MainWindow.xaml.cs | 3 +- 6 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/CLI/CLI.csproj b/src/CLI/CLI.csproj index ecfed4d..6c0c32a 100644 --- a/src/CLI/CLI.csproj +++ b/src/CLI/CLI.csproj @@ -1,25 +1,23 @@  + + Exe + net6.0-windows + enable + enable + OpenSimTools + OpenSimTools Contributors + AMS2CM + AMS2CM.CLI + ..\Shared\AMS2CM.ico + - - Exe - net6.0-windows - enable - enable - OpenSimTools - OpenSimTools Contributors - AMS2CM - AMS2CM.CLI - ..\Shared\AMS2CM.ico - - - + - + Always - diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index dc29cba..754be5b 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -1,5 +1,4 @@  - net6.0-windows enable @@ -22,5 +21,4 @@ - diff --git a/src/GUI/App.xaml.cs b/src/GUI/App.xaml.cs index b32fe7f..9657da3 100644 --- a/src/GUI/App.xaml.cs +++ b/src/GUI/App.xaml.cs @@ -9,11 +9,11 @@ public App() InitializeComponent(); } - protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) + protected override void OnLaunched(LaunchActivatedEventArgs args) { - m_window = new MainWindow(); - m_window.Activate(); + window = new MainWindow(); + window.Activate(); } - private Window m_window; + private Window window; } diff --git a/src/GUI/GUI.csproj b/src/GUI/GUI.csproj index 14131cf..136d05a 100644 --- a/src/GUI/GUI.csproj +++ b/src/GUI/GUI.csproj @@ -1,32 +1,34 @@  - - WinExe - net6.0-windows10.0.19041.0 - 10.0.17763.0 - AMS2CM.GUI - AMS2CM.GUI - app.manifest - x86;x64 - win10-x86;win10-x64 - true - true - ..\Shared\AMS2CM.ico - + + WinExe + net6.0-windows10.0.19041.0 + 10.0.17763.0 + AMS2CM.GUI + AMS2CM.GUI + app.manifest + x86;x64 + win10-x86;win10-x64 + true + true + ..\Shared\AMS2CM.ico + + + + - - - Always - - + + + + + + - - - - - - - - - - + + + Always + + + Always + + diff --git a/src/GUI/MainWindow.xaml b/src/GUI/MainWindow.xaml index 362497a..0cd506a 100644 --- a/src/GUI/MainWindow.xaml +++ b/src/GUI/MainWindow.xaml @@ -1,11 +1,15 @@ - + xmlns:winex="using:WinUIEx" + mc:Ignorable="d" + Title="Automobilista 2 Content Manager" + TaskBarIcon="AMS2CM.ico" + Width="600" Height="600"> @@ -79,4 +83,4 @@ Click="SyncButton_Click">Sync - + diff --git a/src/GUI/MainWindow.xaml.cs b/src/GUI/MainWindow.xaml.cs index afe39c1..28cfe5a 100644 --- a/src/GUI/MainWindow.xaml.cs +++ b/src/GUI/MainWindow.xaml.cs @@ -12,7 +12,7 @@ namespace AMS2CM.GUI; -public sealed partial class MainWindow : Window +public sealed partial class MainWindow : WindowEx { private readonly ObservableCollection modList; private readonly ModManager modManager; @@ -21,7 +21,6 @@ public MainWindow() { InitializeComponent(); modManager = CreateModManager(); - this.SetWindowSize(600,600); modList = new ObservableCollection(); ModListView.ItemsSource = modList; SyncModListView(); From d639e5bb6cc8f84ae2e2c28117f96875641931df Mon Sep 17 00:00:00 2001 From: Paolo Ambrosio Date: Sat, 27 May 2023 11:09:55 +0100 Subject: [PATCH 11/13] Sort mod list by package name --- src/GUI/MainWindow.xaml.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/GUI/MainWindow.xaml.cs b/src/GUI/MainWindow.xaml.cs index 28cfe5a..07bbe85 100644 --- a/src/GUI/MainWindow.xaml.cs +++ b/src/GUI/MainWindow.xaml.cs @@ -46,7 +46,7 @@ private void SyncButton_Click(object sender, RoutedEventArgs e) private void SyncModListView() { modList.Clear(); - foreach (var modState in modManager.FetchState()) + foreach (var modState in modManager.FetchState().OrderBy(_ => _.PackageName)) { modList.Add(new ModVM(modState, modManager)); } @@ -67,9 +67,10 @@ private async void ModListView_Drop(object sender, DragEventArgs e) foreach (var storageFile in items.OfType()) { var filePath = storageFile.Path; - var modState = modManager.EnableNewMod(filePath); - modList.Add(new ModVM(modState, modManager)); + modManager.EnableNewMod(filePath); } + // Refresh list after adding mods with drag and drop + SyncModListView(); } } } @@ -90,6 +91,7 @@ private async void ModListView_DragItemsStarting(object sender, Microsoft.UI.Xam private void ModListView_DragItemsCompleted(Microsoft.UI.Xaml.Controls.ListViewBase sender, Microsoft.UI.Xaml.Controls.DragItemsCompletedEventArgs args) { + // Refresh list after removing mods with drag and drop SyncModListView(); } @@ -112,6 +114,7 @@ private void ModListMenuDelete_Click(object sender, RoutedEventArgs e) FileSystem.DeleteFile(mvm.PackagePath, UIOption.AllDialogs, RecycleOption.SendToRecycleBin); } } + // Refresh list after removing mods with context menu SyncModListView(); } } From f46dc204577e3999a54e3ff3f9d95d4b343aa784 Mon Sep 17 00:00:00 2001 From: Paolo Ambrosio Date: Sat, 27 May 2023 17:46:02 +0100 Subject: [PATCH 12/13] Right click select mod --- src/GUI/MainWindow.xaml | 1 + src/GUI/MainWindow.xaml.cs | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/GUI/MainWindow.xaml b/src/GUI/MainWindow.xaml index 0cd506a..d7b0994 100644 --- a/src/GUI/MainWindow.xaml +++ b/src/GUI/MainWindow.xaml @@ -35,6 +35,7 @@ x:Name="ModListView" SelectionMode="Extended" AllowDrop="True" + RightTapped="ModListView_RightTapped" DragOver="ModListView_DragOver" Drop="ModListView_Drop" CanDragItems="True" diff --git a/src/GUI/MainWindow.xaml.cs b/src/GUI/MainWindow.xaml.cs index 07bbe85..d32720e 100644 --- a/src/GUI/MainWindow.xaml.cs +++ b/src/GUI/MainWindow.xaml.cs @@ -117,4 +117,14 @@ private void ModListMenuDelete_Click(object sender, RoutedEventArgs e) // Refresh list after removing mods with context menu SyncModListView(); } + + private void ModListView_RightTapped(object sender, Microsoft.UI.Xaml.Input.RightTappedRoutedEventArgs e) + { + // Select the mod if right click outside of selection + var mvm = (e.OriginalSource as FrameworkElement).DataContext as ModVM; + if (!ModListView.SelectedItems.Contains(mvm)) + { + ModListView.SelectedItem = mvm; + } + } } From 33819f414b0824026fd24e8db7fc9ca32cc22de4 Mon Sep 17 00:00:00 2001 From: Paolo Ambrosio Date: Sat, 27 May 2023 17:57:41 +0100 Subject: [PATCH 13/13] Fix crash if sync when no Mods dir --- src/Core/ModManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Core/ModManager.cs b/src/Core/ModManager.cs index a9d0679..e0f15d4 100644 --- a/src/Core/ModManager.cs +++ b/src/Core/ModManager.cs @@ -312,6 +312,9 @@ private Dictionary> ReadPreviouslyInstalledF private void WriteInstalledFiles(Dictionary> filesByMod) { + if (!filesByMod.Any() && !File.Exists(workPaths.CurrentStateFile)) { + return; + } File.WriteAllText(workPaths.CurrentStateFile, JsonConvert.SerializeObject(filesByMod, JsonSerializerSettings)); } } \ No newline at end of file