diff --git a/Assets/Icons.axaml b/Assets/Icons.axaml index ddaaea1..c512cd8 100644 --- a/Assets/Icons.axaml +++ b/Assets/Icons.axaml @@ -14,6 +14,7 @@ + @@ -31,6 +32,7 @@ M34.4401 16.1009C34.4506 16.1912 34.5267 16.2603 34.6175 16.2602C38.6957 16.2602 42.0017 19.5577 42.0017 23.6253C42.0017 27.6929 38.6957 30.9904 34.6175 30.9904H29.2491C28.56 30.9904 28.0015 30.4318 28.0015 29.7428C28.0015 29.0537 28.56 28.4952 29.2491 28.4952H34.8059C37.3999 28.4952 39.5028 26.3814 39.5028 23.7738C39.5028 21.1663 37.3999 19.0525 34.8059 19.0525L33.3853 19.0525C32.634 19.0525 31.9778 18.4604 31.9778 17.7058C31.9778 12.882 28.2639 9.49518 24.0028 9.49518C19.7417 9.49518 16.0278 12.9431 16.0278 17.7058C16.0278 18.4604 15.3716 19.0525 14.6203 19.0525L13.1997 19.0525C10.6057 19.0525 8.50276 21.1663 8.50276 23.7738C8.50276 26.3814 10.6057 28.4952 13.1997 28.4952H18.7539C19.4429 28.4952 20.0015 29.0537 20.0015 29.7428C20.0015 30.4318 19.4429 30.9904 18.7539 30.9904H13.3881C9.30991 30.9904 6.00391 27.6929 6.00391 23.6253C6.00391 19.5577 9.30991 16.2602 13.3881 16.2602C13.479 16.2603 13.555 16.1911 13.5655 16.1009C14.1491 11.0749 17.7319 7 24.0028 7C30.2733 7 33.8589 11.1069 34.4401 16.1009Z M30.8343 34.977L25.1775 40.6339C24.6893 41.122 23.8978 41.122 23.4097 40.6339L17.7528 34.977C17.2647 34.4889 17.2647 33.6974 17.7528 33.2093C18.241 32.7211 19.0324 32.7211 19.5206 33.2093L22.9997 36.69L23.0007 23.386L23.0071 23.2582C23.0711 22.6279 23.6035 22.136 24.2507 22.136L24.3785 22.1425C25.0088 22.2065 25.5007 22.7388 25.5007 23.386L25.4997 36.776L29.0665 33.2093C29.5547 32.7211 30.3462 32.7211 30.8343 33.2093C31.3225 33.6974 31.3225 34.4889 30.8343 34.977Z M4.03033009,13.4696699 C3.73743687,13.1767767 3.26256313,13.1767767 2.96966991,13.4696699 C2.6767767,13.7625631 2.6767767,14.2374369 2.96966991,14.5303301 L9.96966991,21.5303301 C10.2625631,21.8232233 10.7374369,21.8232233 11.0303301,21.5303301 L25.0303301,7.53033009 C25.3232233,7.23743687 25.3232233,6.76256313 25.0303301,6.46966991 C24.7374369,6.1767767 24.2625631,6.1767767 23.9696699,6.46966991 L10.5,19.9393398 L4.03033009,13.4696699 Z M7.74944331,5.18010908 C8.0006303,5.50946902 7.93725859,5.9800953 7.60789865,6.23128229 C5.81957892,7.59514774 4.75,9.70820889 4.75,12 C4.75,15.7359812 7.57583716,18.8119527 11.2066921,19.2070952 L10.5303301,18.5303301 C10.2374369,18.2374369 10.2374369,17.7625631 10.5303301,17.4696699 C10.7965966,17.2034034 11.2132603,17.1791973 11.5068718,17.3970518 L11.5909903,17.4696699 L13.5909903,19.4696699 C13.8572568,19.7359365 13.8814629,20.1526002 13.6636084,20.4462117 L13.5909903,20.5303301 L11.5909903,22.5303301 C11.298097,22.8232233 10.8232233,22.8232233 10.5303301,22.5303301 C10.2640635,22.2640635 10.2398575,21.8473998 10.4577119,21.5537883 L10.5303301,21.4696699 L11.280567,20.7208479 C6.78460951,20.3549586 3.25,16.5902554 3.25,12 C3.25,9.23526399 4.54178532,6.68321165 6.6982701,5.03856442 C7.02763004,4.78737743 7.49825632,4.85074914 7.74944331,5.18010908 Z M13.4696699,1.46966991 C13.7625631,1.76256313 13.7625631,2.23743687 13.4696699,2.53033009 L12.7204313,3.27923335 C17.2159137,3.64559867 20.75,7.4100843 20.75,12 C20.75,14.6444569 19.5687435,17.0974104 17.5691913,18.7491089 C17.2498402,19.0129038 16.7771069,18.9678666 16.513312,18.6485156 C16.2495171,18.3291645 16.2945543,17.8564312 16.6139054,17.5926363 C18.2720693,16.2229363 19.25,14.1922015 19.25,12 C19.25,8.26436254 16.4246828,5.18861329 12.7943099,4.7930139 L13.4696699,5.46966991 C13.7625631,5.76256313 13.7625631,6.23743687 13.4696699,6.53033009 C13.1767767,6.8232233 12.701903,6.8232233 12.4090097,6.53033009 L10.4090097,4.53033009 C10.1161165,4.23743687 10.1161165,3.76256313 10.4090097,3.46966991 L12.4090097,1.46966991 C12.701903,1.1767767 13.1767767,1.1767767 13.4696699,1.46966991 Z + M30.9968 12C33.7582 12 35.9968 14.2386 35.9968 17C35.9968 19.7614 33.7582 22 30.9968 22C28.2354 22 25.9968 19.7614 25.9968 17C25.9968 14.2386 28.2354 12 30.9968 12ZM28.4968 17C28.4968 18.3807 29.6161 19.5 30.9968 19.5C32.3775 19.5 33.4968 18.3807 33.4968 17C33.4968 15.6193 32.3775 14.5 30.9968 14.5C29.6161 14.5 28.4968 15.6193 28.4968 17Z M6 10.75C6 8.12665 8.12665 6 10.75 6H37.25C39.8734 6 42 8.12665 42 10.75V37.25C42 38.4377 41.5641 39.5236 40.8435 40.3565C40.7898 40.4531 40.7223 40.5441 40.641 40.6268C40.5548 40.7144 40.4589 40.7866 40.3566 40.8434C39.5238 41.564 38.4378 42 37.25 42H10.75C9.55998 42 8.47218 41.5624 7.63873 40.8393C7.53919 40.7832 7.44575 40.7123 7.36158 40.6268C7.28258 40.5466 7.21664 40.4583 7.16373 40.3648C6.43884 39.5309 6 38.4417 6 37.25V10.75ZM39.5 37.25V10.75C39.5 9.50736 38.4926 8.5 37.25 8.5H10.75C9.50736 8.5 8.5 9.50736 8.5 10.75V37.25C8.5 37.4065 8.51598 37.5592 8.54639 37.7067L21.3697 25.0851C22.8291 23.6486 25.171 23.6485 26.6306 25.0849L39.454 37.7048C39.4842 37.5579 39.5 37.4058 39.5 37.25ZM10.75 39.5H37.25C37.3948 39.5 37.5364 39.4863 37.6736 39.4602L24.877 26.8668C24.3905 26.388 23.6099 26.388 23.1234 26.8668L10.3284 39.4606C10.465 39.4865 10.6059 39.5 10.75 39.5Z \ No newline at end of file diff --git a/Models/Mod.cs b/Models/Mod.cs index 052c612..ca4accf 100644 --- a/Models/Mod.cs +++ b/Models/Mod.cs @@ -1,4 +1,8 @@ using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Text.Json.Serialization; +using Avalonia.Media.Imaging; using ReactiveUI; namespace RelinkModOrganizer.Models; @@ -12,8 +16,14 @@ public class Mod(string id, string name) : ReactiveObject /// public string Id { get; set; } = id; + [Required(AllowEmptyStrings = false, ErrorMessage = "Name is required.")] public string Name { get; set; } = name; + public string? PreviewImagePath { get; set; } + + [JsonIgnore] + public Bitmap? PreviewImage { get => LoadImage(); } + public bool Enabled { get => _enabled; @@ -21,6 +31,16 @@ public bool Enabled } public HashSet RelativeFilePaths { get; set; } = []; + + private Bitmap? LoadImage() + { + if (string.IsNullOrWhiteSpace(PreviewImagePath) || + !File.Exists(PreviewImagePath)) + return null; + + var stream = File.OpenRead(PreviewImagePath); + return new Bitmap(stream); + } } public record ModItem(string Id, string Name); \ No newline at end of file diff --git a/RelinkModOrganizer.csproj b/RelinkModOrganizer.csproj index ec885e0..a3b1a1c 100644 --- a/RelinkModOrganizer.csproj +++ b/RelinkModOrganizer.csproj @@ -8,8 +8,10 @@ true - true + false true + true + true true true en-US;zh-CN diff --git a/Services/ModificationService.cs b/Services/ModificationService.cs index a9bd7d8..1ebbe33 100644 --- a/Services/ModificationService.cs +++ b/Services/ModificationService.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text.Json; using System.Threading.Tasks; +using ReactiveUI; using RelinkModOrganizer.Models; using RelinkModOrganizer.ThirdParties.DataTools; using Results = RelinkModOrganizer.TryResults; @@ -34,7 +35,9 @@ public async Task LoadModsFromDiskAsync() .ToHashSet(); var mod = configService.Config.Mods.GetValueOrDefault(modId) ?? - new Mod(modId, await GetReloadedModNameAsync(modPath) ?? modId); + new Mod(modId, string.Empty); + await SetModNameAndIconAsync(mod, modPath); + mod.RelativeFilePaths = relativeFilePaths; configService.Config.Mods[modId] = mod; @@ -185,19 +188,37 @@ public async Task TryGenerateDataIndexAsync() return Results.Ok(); } - private static async Task GetReloadedModNameAsync(string path) + private static async Task SetModNameAndIconAsync(Mod mod, string modPath) { var modConfigJson = Directory - .GetFiles(path, Consts.ReloadedModConfigName, SearchOption.AllDirectories) + .GetFiles(modPath, Consts.ReloadedModConfigName, SearchOption.AllDirectories) .FirstOrDefault(); if (string.IsNullOrWhiteSpace(modConfigJson)) - return null; + { + mod.Name = mod.Id; + return; + } using var fs = File.OpenRead(modConfigJson); var doc = await JsonDocument.ParseAsync(fs); - doc.RootElement.TryGetProperty("ModName", out var modNameElem); - return modNameElem.GetString(); + + if (string.IsNullOrWhiteSpace(mod.Name)) + { + doc.RootElement.TryGetProperty("ModName", out var modNameElem); + var name = modNameElem.GetString(); + if (!string.IsNullOrWhiteSpace(name)) + mod.Name = name; + } + + doc.RootElement.TryGetProperty("ModIcon", out var modIconElem); + var icon = modIconElem.GetString(); + if (!string.IsNullOrWhiteSpace(icon)) + { + var modDir = Path.GetDirectoryName(modConfigJson)!; // should not be null + var previewImagePath = Path.Combine(modDir, icon); + mod.PreviewImagePath = previewImagePath; + } } private class HashSetComparer : IEqualityComparer> diff --git a/Views/MainWindow.axaml b/Views/MainWindow.axaml index 19f18b5..35726ab 100644 --- a/Views/MainWindow.axaml +++ b/Views/MainWindow.axaml @@ -7,7 +7,7 @@ x:Class="RelinkModOrganizer.Views.MainWindow" x:DataType="vm:MainWindowViewModel" Icon="/Assets/avalonia-logo.ico" - Title="GBFR Mod Organizer" + Title="Relink Mod Organizer" Width="1280" Height="720" TransparencyLevelHint="AcrylicBlur" diff --git a/Views/ModListView.axaml b/Views/ModListView.axaml index 619778e..1e88f29 100644 --- a/Views/ModListView.axaml +++ b/Views/ModListView.axaml @@ -22,28 +22,70 @@ - - + + + - + + - + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Views/ModListView.axaml.cs b/Views/ModListView.axaml.cs index 56faed6..22fe4f0 100644 --- a/Views/ModListView.axaml.cs +++ b/Views/ModListView.axaml.cs @@ -1,6 +1,5 @@ -using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; +using Avalonia.Controls.Primitives; namespace RelinkModOrganizer.Views; @@ -10,4 +9,10 @@ public ModListView() { InitializeComponent(); } + + private void Image_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e) + { + if (sender is Control ctl) + FlyoutBase.ShowAttachedFlyout(ctl); + } } \ No newline at end of file