From b7900f342c6992aeb2d5ab9b40c2a46672e86b4e Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Thu, 19 Dec 2024 21:43:41 -0500 Subject: [PATCH 1/2] Add button to sprite window to upload sprites --- .../Options/Sprite.cs | 12 +- .../Options/SpriteFilter.cs | 1 + .../RandomizerDirectories.cs | 5 +- .../Services/SpriteService.cs | 94 +++++++++++- .../TrackerCouncil.Smz3.Data.csproj | 6 + .../ViewModels/SpriteViewModel.cs | 104 +++++++++++-- .../Services/UIService.cs | 3 +- .../Services/SoloRomListService.cs | 2 +- .../Services/TrackerMapWindowService.cs | 9 +- .../ViewModels/TrackerMapWindowViewModel.cs | 3 +- .../GenerationSettingsBasicPanel.axaml.cs | 5 +- .../Views/SpriteWindow.axaml | 131 +++++++++++------ .../Views/SpriteWindow.axaml.cs | 137 +++++++++++++++++- 13 files changed, 429 insertions(+), 83 deletions(-) diff --git a/src/TrackerCouncil.Smz3.Data/Options/Sprite.cs b/src/TrackerCouncil.Smz3.Data/Options/Sprite.cs index 67277ed81..212b9342c 100644 --- a/src/TrackerCouncil.Smz3.Data/Options/Sprite.cs +++ b/src/TrackerCouncil.Smz3.Data/Options/Sprite.cs @@ -39,6 +39,8 @@ public Sprite(string name, string author, string filePath, SpriteType spriteType SpriteType = spriteType; PreviewPath = previewPath; SpriteOption = spriteOption; + IsUserSprite = filePath.StartsWith(RandomizerDirectories.UserSpritePath); + if (string.IsNullOrEmpty(filePath)) IsDefault = true; } @@ -50,7 +52,7 @@ private Sprite(string name, SpriteType spriteType, bool isDefault, bool isRandom SpriteType = spriteType; IsDefault = isDefault; IsRandomSprite = isRandomSprite; - PreviewPath = Path.Combine(SpritePath, s_folderNames[spriteType], sprite); + PreviewPath = Path.Combine(RandomizerDirectories.SpritePath, s_folderNames[spriteType], sprite); } [YamlIgnore] @@ -59,9 +61,9 @@ private Sprite(string name, SpriteType spriteType, bool isDefault, bool isRandom [YamlIgnore] public string Author { get; set; } - public string FilePath { get; set; } = ""; + public string FilePath { get; } = ""; - public SpriteType SpriteType { get; set; } + public SpriteType SpriteType { get; } [YamlIgnore] public string PreviewPath { get; set; } @@ -73,12 +75,15 @@ private Sprite(string name, SpriteType spriteType, bool isDefault, bool isRandom [YamlIgnore] public SpriteOptions SpriteOption { get; set; } + public bool IsUserSprite { get; set; } + public bool MatchesFilter(string searchTerm, SpriteFilter spriteFilter) => (string.IsNullOrEmpty(searchTerm) || Name.Contains(searchTerm, StringComparison.OrdinalIgnoreCase) || Author.Contains(searchTerm, StringComparison.OrdinalIgnoreCase)) && ((spriteFilter == SpriteFilter.Default && SpriteOption != SpriteOptions.Hide) || (spriteFilter == SpriteFilter.Favorited && SpriteOption == SpriteOptions.Favorite) || (spriteFilter == SpriteFilter.Hidden && SpriteOption == SpriteOptions.Hide) || + (spriteFilter == SpriteFilter.User && IsUserSprite) || spriteFilter == SpriteFilter.All); public static bool operator ==(Sprite? a, Sprite? b) @@ -119,5 +124,4 @@ public override string ToString() return string.IsNullOrEmpty(Author) ? Name : $"{Name} by {Author}"; } - public static string SpritePath => RandomizerDirectories.SpritePath; } diff --git a/src/TrackerCouncil.Smz3.Data/Options/SpriteFilter.cs b/src/TrackerCouncil.Smz3.Data/Options/SpriteFilter.cs index 2ec46fee5..38cd85ceb 100644 --- a/src/TrackerCouncil.Smz3.Data/Options/SpriteFilter.cs +++ b/src/TrackerCouncil.Smz3.Data/Options/SpriteFilter.cs @@ -5,5 +5,6 @@ public enum SpriteFilter Default, Favorited, Hidden, + User, All, } diff --git a/src/TrackerCouncil.Smz3.Data/RandomizerDirectories.cs b/src/TrackerCouncil.Smz3.Data/RandomizerDirectories.cs index 5cd6502da..d67ce4422 100644 --- a/src/TrackerCouncil.Smz3.Data/RandomizerDirectories.cs +++ b/src/TrackerCouncil.Smz3.Data/RandomizerDirectories.cs @@ -67,6 +67,9 @@ public static string SpritePath } } + public static string UserSpritePath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "SMZ3CasRandomizer", "Sprites"); + #if DEBUG public static string SpriteHashYamlFilePath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "SMZ3CasRandomizer", "sprite-hashes-debug.yml"); #else @@ -101,5 +104,5 @@ public static string TrackerSpritePath public static string TrackerSpriteHashYamlFilePath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "SMZ3CasRandomizer", "tracker-sprite-hashes.yml"); #endif - public static string TrackerSpriteInitialJsonFilePath => Path.Combine(SpritePath, "tracker-sprites.json"); + public static string TrackerSpriteInitialJsonFilePath => Path.Combine(SpritePath, "tracker-sprites.json"); } diff --git a/src/TrackerCouncil.Smz3.Data/Services/SpriteService.cs b/src/TrackerCouncil.Smz3.Data/Services/SpriteService.cs index 763c95e4e..423cfc170 100644 --- a/src/TrackerCouncil.Smz3.Data/Services/SpriteService.cs +++ b/src/TrackerCouncil.Smz3.Data/Services/SpriteService.cs @@ -23,7 +23,7 @@ public SpriteService(ILogger logger, OptionsFactory optionsFactor _options = optionsFactory.Create(); } - public IEnumerable Sprites { get; set; } = new List(); + public List Sprites { get; set; } = []; public IEnumerable LinkSprites => Sprites.Where(x => x.SpriteType == SpriteType.Link); public IEnumerable SamusSprites => Sprites.Where(x => x.SpriteType == SpriteType.Samus); public IEnumerable ShipSprites => Sprites.Where(x => x.SpriteType == SpriteType.Ship); @@ -33,23 +33,21 @@ public SpriteService(ILogger logger, OptionsFactory optionsFactor /// public Task LoadSpritesAsync() { - if (Sprites.Any() || !Directory.Exists(Sprite.SpritePath)) return Task.CompletedTask; + if (Sprites.Any() || !Directory.Exists(RandomizerDirectories.SpritePath)) return Task.CompletedTask; return Task.Run(() => { var defaults = new List() { Sprite.DefaultSamus, Sprite.DefaultLink, Sprite.DefaultShip, Sprite.RandomSamus, Sprite.RandomLink, Sprite.RandomShip }; - var playerSprites = Directory.EnumerateFiles(Sprite.SpritePath, "*.rdc", SearchOption.AllDirectories) + var playerSprites = Directory.EnumerateFiles(RandomizerDirectories.SpritePath, "*.rdc", SearchOption.AllDirectories) .Select(LoadRdcSprite); - var shipSprites = Directory.EnumerateFiles(Path.Combine(Sprite.SpritePath, "Ships"), "*.ips", SearchOption.AllDirectories) + var shipSprites = Directory.EnumerateFiles(Path.Combine(RandomizerDirectories.SpritePath, "Ships"), "*.ips", SearchOption.AllDirectories) .Select(LoadIpsSprite); var sprites = playerSprites.Concat(shipSprites).Concat(defaults).OrderBy(x => x.Name).ToList(); - var extraSpriteDirectory = - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "SMZ3CasRandomizer", "Sprites"); + var extraSpriteDirectory = RandomizerDirectories.UserSpritePath; if (Directory.Exists(extraSpriteDirectory)) { @@ -65,6 +63,50 @@ public Task LoadSpritesAsync() }); } + public Sprite? AddCustomSprite(string spritePath, string? previewImagePath) + { + var userSpritePath = RandomizerDirectories.UserSpritePath; + + if (spritePath.StartsWith(RandomizerDirectories.SpritePath) || + spritePath.StartsWith(userSpritePath) || + !File.Exists(spritePath)) + { + return null; + } + + var destinationSpritePath = Path.Combine(userSpritePath, Path.GetFileName(spritePath)); + File.Copy(spritePath, destinationSpritePath, true); + if (File.Exists(previewImagePath)) + { + var baseFileName = Path.GetFileNameWithoutExtension(spritePath); + File.Copy(previewImagePath, Path.Combine(userSpritePath, $"{baseFileName}.png"), true); + } + + var sprite = new Sprite(); + try + { + sprite = ".rdc".Equals(Path.GetExtension(destinationSpritePath), StringComparison.OrdinalIgnoreCase) + ? LoadRdcSprite(destinationSpritePath) + : LoadIpsSprite(destinationSpritePath); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to load Sprite {SpritePath}", destinationSpritePath); + } + + if (Sprites.Any(x => x.FilePath == destinationSpritePath)) + { + var oldSprite = Sprites.First(x => x.FilePath == destinationSpritePath); + oldSprite.Name = sprite.Name; + oldSprite.Author = sprite.Author; + oldSprite.PreviewPath = sprite.PreviewPath; + return oldSprite; + } + + Sprites.Add(sprite); + return sprite; + } + /// /// Retrieves the random sprite image for the given sprite type /// @@ -73,7 +115,7 @@ public Task LoadSpritesAsync() public string GetRandomPreviewImage(SpriteType type) { var spriteFolder = type == SpriteType.Ship ? "Ships" : type.ToString(); - return Path.Combine(Sprite.SpritePath, spriteFolder, "random.png"); + return Path.Combine(RandomizerDirectories.SpritePath, spriteFolder, "random.png"); } /// @@ -232,4 +274,40 @@ public Sprite GetSprite(SpriteType type) return sprite; } + /// + /// Deletes a user added sprite + /// + /// The sprite to delete + public bool DeleteSprite(Sprite sprite) + { + if (!sprite.IsUserSprite) + { + return false; + } + + try + { + if (!string.IsNullOrEmpty(sprite.PreviewPath) && File.Exists(sprite.PreviewPath)) + { + File.Delete(sprite.PreviewPath); + } + } + catch (Exception e) + { + _logger.LogError(e, "Failed to delete sprite preview file"); + } + + try + { + File.Delete(sprite.FilePath); + Sprites.Remove(sprite); + return true; + } + catch (Exception e) + { + _logger.LogError(e, "Failed to delete sprite preview file"); + return false; + } + } + } diff --git a/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj b/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj index 380477716..ceef903b2 100644 --- a/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj +++ b/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj @@ -35,6 +35,12 @@ + + + ..\..\..\..\..\.nuget\packages\avalonia\11.0.11\ref\net6.0\Avalonia.Base.dll + + + diff --git a/src/TrackerCouncil.Smz3.Data/ViewModels/SpriteViewModel.cs b/src/TrackerCouncil.Smz3.Data/ViewModels/SpriteViewModel.cs index 3830c3bed..da42ae775 100644 --- a/src/TrackerCouncil.Smz3.Data/ViewModels/SpriteViewModel.cs +++ b/src/TrackerCouncil.Smz3.Data/ViewModels/SpriteViewModel.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Runtime.CompilerServices; +using Avalonia.Media; using TrackerCouncil.Smz3.Data.Options; namespace TrackerCouncil.Smz3.Data.ViewModels; @@ -8,15 +9,18 @@ namespace TrackerCouncil.Smz3.Data.ViewModels; public class SpriteViewModel : INotifyPropertyChanged { private bool _display; + private static readonly IBrush s_defaultIconBrush = Brushes.Silver; + private static readonly IBrush s_starredIconBrush = Brushes.Goldenrod; + private static readonly IBrush s_hiddenIconBrush = Brushes.IndianRed; - private static Dictionary s_ImageDimensions = new() + private static readonly Dictionary s_imageDimensions = new() { { SpriteType.Link, (64, 96) }, { SpriteType.Samus, (64, 106) }, { SpriteType.Ship, (248, 92) }, }; - private static Dictionary s_Widths = new() + private static readonly Dictionary s_widths = new() { - { SpriteType.Link, 250 }, { SpriteType.Samus, 250 }, { SpriteType.Ship, 450 }, + { SpriteType.Link, 235 }, { SpriteType.Samus, 235 }, { SpriteType.Ship, 450 }, }; public SpriteViewModel(Sprite sprite) @@ -25,22 +29,68 @@ public SpriteViewModel(Sprite sprite) Name = sprite.Name; Author = sprite.Author; PreviewPath = sprite.PreviewPath; - SpriteOption = sprite.SpriteOption; Display = sprite.SpriteOption != SpriteOptions.Hide; - PanelWidth = s_Widths[sprite.SpriteType]; - ImageWidth = s_ImageDimensions[sprite.SpriteType].Item1; - ImageHeight = s_ImageDimensions[sprite.SpriteType].Item2; - CanFavoriteAndHide = !sprite.IsRandomSprite; + PanelWidth = s_widths[sprite.SpriteType]; + ImageWidth = s_imageDimensions[sprite.SpriteType].Item1; + ImageHeight = s_imageDimensions[sprite.SpriteType].Item2; + CanFavorite = !sprite.IsRandomSprite; + CanHide = sprite is { IsUserSprite: false }; + CanDelete = sprite is { IsRandomSprite: false, IsUserSprite: true }; + IconOpacity = CanFavorite ? 1f : 0.3f; + + SetSpriteOption(sprite.SpriteOption); } public Sprite Sprite { get; } public string Name { get; set; } public string Author { get; set; } - public string PreviewPath { get; set; } - public bool CanFavoriteAndHide { get; set; } + public bool CanFavorite { get; set; } public int PanelWidth { get; } public int ImageWidth { get; } public int ImageHeight { get; } + public float IconOpacity { get; } + + private string _previewPath; + public string PreviewPath + { + get => _previewPath; + set => SetField(ref _previewPath, value); + } + + private bool _canHide; + public bool CanHide + { + get => _canHide; + set => SetField(ref _canHide, value); + } + + private bool _canDelete; + public bool CanDelete + { + get => _canDelete; + set => SetField(ref _canDelete, value); + } + + private IBrush? _starBrush = s_defaultIconBrush; + public IBrush? StarBrush + { + get => _starBrush; + set => SetField(ref _starBrush, value); + } + + private IBrush? _hideBrush = s_defaultIconBrush; + public IBrush? HideBrush + { + get => _hideBrush; + set => SetField(ref _hideBrush, value); + } + + private IBrush? _deleteBrush = s_defaultIconBrush; + public IBrush? DeleteBrush + { + get => _deleteBrush; + set => SetField(ref _deleteBrush, value); + } public SpriteOptions SpriteOption { @@ -50,7 +100,7 @@ public SpriteOptions SpriteOption Sprite.SpriteOption = value; OnPropertyChanged(); OnPropertyChanged(nameof(IsFavorite)); - OnPropertyChanged(nameof(IsNotFavorite)); + OnPropertyChanged(nameof(IsHidden)); } } @@ -64,7 +114,37 @@ public bool Display public bool IsFavorite => SpriteOption == SpriteOptions.Favorite; - public bool IsNotFavorite => !IsFavorite; + public bool IsHidden => SpriteOption == SpriteOptions.Hide; + + public void SetSpriteOption(SpriteOptions option) + { + SpriteOption = option; + + if (Sprite.IsRandomSprite) + { + StarBrush = s_defaultIconBrush; + HideBrush = s_defaultIconBrush; + return; + } + else if (option == SpriteOptions.Default) + { + StarBrush = s_defaultIconBrush; + HideBrush = s_defaultIconBrush; + DeleteBrush = s_defaultIconBrush; + } + else if (option == SpriteOptions.Favorite) + { + StarBrush = s_starredIconBrush; + HideBrush = s_defaultIconBrush; + DeleteBrush = s_defaultIconBrush; + } + else if (option == SpriteOptions.Hide) + { + StarBrush = s_defaultIconBrush; + HideBrush = s_hiddenIconBrush; + DeleteBrush = s_defaultIconBrush; + } + } public event PropertyChangedEventHandler? PropertyChanged; diff --git a/src/TrackerCouncil.Smz3.Tracking/Services/UIService.cs b/src/TrackerCouncil.Smz3.Tracking/Services/UIService.cs index 94152cf72..b0c541e7c 100644 --- a/src/TrackerCouncil.Smz3.Tracking/Services/UIService.cs +++ b/src/TrackerCouncil.Smz3.Tracking/Services/UIService.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using TrackerCouncil.Smz3.Data; using TrackerCouncil.Smz3.Data.Configuration; using TrackerCouncil.Smz3.Data.Configuration.ConfigFiles; using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes; @@ -37,7 +38,7 @@ UIConfig uiConfig .Where(x => !string.IsNullOrEmpty(x)) .NonNull() .Select(x => Path.Combine(configProvider.ConfigDirectory, x, "Sprites")).Reverse().ToList() ?? new(); - iconPaths.Add(Sprite.SpritePath); + iconPaths.Add(RandomizerDirectories.SpritePath); _iconPaths = iconPaths; } diff --git a/src/TrackerCouncil.Smz3.UI/Services/SoloRomListService.cs b/src/TrackerCouncil.Smz3.UI/Services/SoloRomListService.cs index 24294a67a..104cd3767 100644 --- a/src/TrackerCouncil.Smz3.UI/Services/SoloRomListService.cs +++ b/src/TrackerCouncil.Smz3.UI/Services/SoloRomListService.cs @@ -87,7 +87,7 @@ public async Task GeneratePlando() var storageItem = await CrossPlatformTools.OpenFileDialogAsync(ParentWindow, FileInputControlType.OpenFile, "Yaml files (*.yaml, *.yml)|*.yaml;*.yml|All files (*.*)|*.*", _options.RomOutputPath); - var pathString = HttpUtility.UrlDecode(storageItem?.Path.AbsolutePath); + var pathString = storageItem?.TryGetLocalPath(); if (string.IsNullOrEmpty(pathString) || !File.Exists(pathString)) { return; diff --git a/src/TrackerCouncil.Smz3.UI/Services/TrackerMapWindowService.cs b/src/TrackerCouncil.Smz3.UI/Services/TrackerMapWindowService.cs index b6476fbdf..b9d4fdfdc 100644 --- a/src/TrackerCouncil.Smz3.UI/Services/TrackerMapWindowService.cs +++ b/src/TrackerCouncil.Smz3.UI/Services/TrackerMapWindowService.cs @@ -7,6 +7,7 @@ using AvaloniaControls.ControlServices; using AvaloniaControls.Services; using TrackerCouncil.Smz3.Abstractions; +using TrackerCouncil.Smz3.Data; using TrackerCouncil.Smz3.Data.Configuration.ConfigFiles; using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes; using TrackerCouncil.Smz3.Data.Options; @@ -42,8 +43,8 @@ public TrackerMapWindowViewModel GetViewModel() private void InitViewModelData() { - _markedImageGoodPath = Path.Combine(Sprite.SpritePath, "Maps", "marked_good.png"); - _markedImageUselessPath = Path.Combine(Sprite.SpritePath, "Maps", "marked_useless.png"); + _markedImageGoodPath = Path.Combine(RandomizerDirectories.SpritePath, "Maps", "marked_good.png"); + _markedImageUselessPath = Path.Combine(RandomizerDirectories.SpritePath, "Maps", "marked_useless.png"); var allLocations = worldAccessor.World.Locations.ToList(); @@ -273,7 +274,7 @@ private void UpdateLocationModel(TrackerMapLocationViewModel location, string im { if (!string.IsNullOrEmpty(image)) { - location.ImagePath = Path.Combine(Sprite.SpritePath, "Maps", image); + location.ImagePath = Path.Combine(RandomizerDirectories.SpritePath, "Maps", image); location.HasImage = true; } else @@ -284,7 +285,7 @@ private void UpdateLocationModel(TrackerMapLocationViewModel location, string im if (displayNumber > 1) { location.NumberImagePath = Path.Combine( - Sprite.SpritePath, "Marks", $"{Math.Min(9, displayNumber)}.png"); + RandomizerDirectories.SpritePath, "Marks", $"{Math.Min(9, displayNumber)}.png"); location.NumberVisibility = true; } else diff --git a/src/TrackerCouncil.Smz3.UI/ViewModels/TrackerMapWindowViewModel.cs b/src/TrackerCouncil.Smz3.UI/ViewModels/TrackerMapWindowViewModel.cs index 2ea8e4302..f8c58b9d2 100644 --- a/src/TrackerCouncil.Smz3.UI/ViewModels/TrackerMapWindowViewModel.cs +++ b/src/TrackerCouncil.Smz3.UI/ViewModels/TrackerMapWindowViewModel.cs @@ -3,6 +3,7 @@ using Avalonia; using AvaloniaControls.Models; using ReactiveUI.Fody.Helpers; +using TrackerCouncil.Smz3.Data; using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes; using TrackerCouncil.Smz3.Data.Options; @@ -18,7 +19,7 @@ public class TrackerMapWindowViewModel : ViewModelBase public string MainImage => SelectedMap == null ? "" - : Path.Combine(Sprite.SpritePath, "Maps", SelectedMap.Image); + : Path.Combine(RandomizerDirectories.SpritePath, "Maps", SelectedMap.Image); [Reactive] public List Locations { get; set; } = []; [Reactive] public bool FinishedLoading { get; set; } diff --git a/src/TrackerCouncil.Smz3.UI/Views/GenerationSettingsBasicPanel.axaml.cs b/src/TrackerCouncil.Smz3.UI/Views/GenerationSettingsBasicPanel.axaml.cs index eec09f363..e2051160e 100644 --- a/src/TrackerCouncil.Smz3.UI/Views/GenerationSettingsBasicPanel.axaml.cs +++ b/src/TrackerCouncil.Smz3.UI/Views/GenerationSettingsBasicPanel.axaml.cs @@ -5,6 +5,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; +using Avalonia.Platform.Storage; using AvaloniaControls; using AvaloniaControls.Controls; using AvaloniaControls.Models; @@ -128,7 +129,7 @@ private async void SelectMsuFileMenuItem_OnClick(object? sender, RoutedEventArgs var path = options?.PatchOptions.MsuPaths.FirstOrDefault() ?? options?.GeneralOptions.MsuPath; var storagePath = await CrossPlatformTools.OpenFileDialogAsync(ParentWindow, FileInputControlType.OpenFile, "MSU files (*.msu)|*.msu|All files (*.*)|*.*", path); - var pathString = HttpUtility.UrlDecode(storagePath?.Path.AbsolutePath); + var pathString = storagePath?.TryGetLocalPath(); if (string.IsNullOrEmpty(pathString) || !File.Exists(pathString)) { return; @@ -177,7 +178,7 @@ private void OpenMsuSelectionWindow(MsuRandomizationStyle? randomizationStyle) var storagePath = await CrossPlatformTools.OpenFileDialogAsync(ParentWindow, FileInputControlType.Folder, "", _options?.GeneralOptions.RomOutputPath, title: "Select parent MSU folder"); - var path = Uri.UnescapeDataString(storagePath?.Path.AbsolutePath ?? ""); + var path = storagePath?.TryGetLocalPath(); if (!string.IsNullOrEmpty(path) && Directory.Exists(path)) { diff --git a/src/TrackerCouncil.Smz3.UI/Views/SpriteWindow.axaml b/src/TrackerCouncil.Smz3.UI/Views/SpriteWindow.axaml index 5fa2339be..f92dea554 100644 --- a/src/TrackerCouncil.Smz3.UI/Views/SpriteWindow.axaml +++ b/src/TrackerCouncil.Smz3.UI/Views/SpriteWindow.axaml @@ -28,6 +28,7 @@ + Search @@ -44,63 +45,109 @@ x:Name="FilterComboBox" /> + + + + - + - + - - - - - - - - - + + - - - + + - diff --git a/src/TrackerCouncil.Smz3.UI/Views/SpriteWindow.axaml.cs b/src/TrackerCouncil.Smz3.UI/Views/SpriteWindow.axaml.cs index 838fa00ae..b550a8ca0 100644 --- a/src/TrackerCouncil.Smz3.UI/Views/SpriteWindow.axaml.cs +++ b/src/TrackerCouncil.Smz3.UI/Views/SpriteWindow.axaml.cs @@ -1,10 +1,14 @@ +using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Platform.Storage; using Avalonia.Threading; +using AvaloniaControls; using AvaloniaControls.Controls; using AvaloniaControls.Models; using TrackerCouncil.Smz3.Data.Options; @@ -77,10 +81,11 @@ private void SearchTextBox_OnKeyUp(object? sender, KeyEventArgs e) private void ToggleFavoriteButton_OnClick(object? sender, RoutedEventArgs e) { - if (sender is not Button { Tag: SpriteViewModel sprite }) return; - sprite.SpriteOption = sprite.SpriteOption == SpriteOptions.Favorite + if (sender is not Button { Tag: SpriteViewModel { CanFavorite: true } sprite }) return; + e.Handled = true; + sprite.SetSpriteOption(sprite.SpriteOption == SpriteOptions.Favorite ? SpriteOptions.Default - : SpriteOptions.Favorite; + : SpriteOptions.Favorite); sprite.CheckFilterOption(Model.SearchText, Model.SpriteFilter); } @@ -111,10 +116,36 @@ private void SelectButton_OnClick(object? sender, RoutedEventArgs e) private void HideButton_OnClick(object? sender, RoutedEventArgs e) { - if (sender is not Button { Tag: SpriteViewModel sprite }) return; - sprite.SpriteOption = SpriteOptions.Hide; + if (sender is not Button { Tag: SpriteViewModel { CanHide: true } sprite }) return; + e.Handled = true; + sprite.SetSpriteOption(sprite.SpriteOption == SpriteOptions.Hide + ? SpriteOptions.Default + : SpriteOptions.Hide); sprite.CheckFilterOption(Model.SearchText, Model.SpriteFilter); - sprite.Display = false; + sprite.Display = sprite.SpriteOption == SpriteOptions.Default; + } + + private async void DeleteButton_OnClick(object? sender, RoutedEventArgs e) + { + try + { + if (sender is not Button { Tag: SpriteViewModel { CanDelete: true } sprite } || _spriteService == null) return; + e.Handled = true; + var response = await MessageWindow.ShowYesNoDialog("Are you sure you want to delete this sprite?", parentWindow: this); + if (!response) + { + return; + } + + _spriteService.DeleteSprite(sprite.Sprite); + Model.Sprites.Remove(sprite); + } + catch (Exception exception) + { + Console.WriteLine(exception); + throw; + } + } private void Control_OnLoaded(object? sender, RoutedEventArgs e) @@ -132,7 +163,12 @@ private void Control_OnLoaded(object? sender, RoutedEventArgs e) _ => new Dictionary() }; - _allSprites = _spriteService.Sprites.Where(x => x.SpriteType == _spriteType).OrderByDescending(x => x.IsRandomSprite).ThenByDescending(x => _spriteOptions.ContainsKey(x.FilePath)); + _allSprites = _spriteService.Sprites + .Where(x => x.SpriteType == _spriteType) + .OrderByDescending(x => x.IsRandomSprite) + .ThenBy(x => x.Name) + .ThenBy(x => x.Author) + .ToList(); Task.Factory.StartNew(() => { @@ -168,5 +204,92 @@ private void EnumComboBox_OnValueChanged(object sender, EnumValueChangedEventArg (SpriteFilter?)this.Find(nameof(FilterComboBox))!.Value ?? SpriteFilter.All; Search(); } + + private async void AddSpriteButton_OnClick(object? sender, RoutedEventArgs e) + { + try + { + if (_spriteService == null) + { + return; + } + + var filter = _spriteType == SpriteType.Ship ? "IPS files (*.ips)|*.ips" : "RDC files (*.rdc)|*.rdc"; + var result = await CrossPlatformTools.OpenFileDialogAsync(this, FileInputControlType.OpenFile, filter, Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)); + var spritePath = result?.TryGetLocalPath(); + + if (string.IsNullOrEmpty(spritePath) || !File.Exists(spritePath)) + { + return; + } + + var previewImagePath = Path.ChangeExtension(spritePath, ".png"); + if (!File.Exists(previewImagePath)) + { + if (await MessageWindow.ShowYesNoDialog("Is there a png preview image associated with this sprite?", + parentWindow: this)) + { + result = await CrossPlatformTools.OpenFileDialogAsync(this, FileInputControlType.OpenFile, "png files (*.png)|*.png", Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)); + previewImagePath = result?.TryGetLocalPath(); + + if (!File.Exists(previewImagePath)) + { + previewImagePath = null; + } + } + else + { + previewImagePath = null; + } + } + + var newSprite = _spriteService.AddCustomSprite(spritePath, previewImagePath); + if (newSprite == null) + { + await MessageWindow.ShowErrorDialog("Unable to add custom sprite", parentWindow: this); + return; + } + + var oldModel = Model.Sprites.FirstOrDefault(x => x.Sprite == newSprite); + if (oldModel != null) + { + Model.Sprites.Remove(oldModel); + } + + var model = new SpriteViewModel(newSprite); + model.CheckFilterOption(Model.SearchText, Model.SpriteFilter); + var inserted = false; + for (var i = 0; i < Model.Sprites.Count; i++) + { + if (Model.Sprites[i].Sprite.IsRandomSprite) + { + continue; + } + + if (string.Compare(Model.Sprites[i].Name, newSprite.Name, StringComparison.Ordinal) > 0) + { + Model.Sprites.Insert(i, model); + inserted = true; + break; + } + else if (string.Compare(Model.Sprites[i].Name, newSprite.Name, StringComparison.Ordinal) == 0 && + string.Compare(Model.Sprites[i].Author, newSprite.Author, StringComparison.Ordinal) > 0) + { + Model.Sprites.Insert(i, model); + inserted = true; + break; + } + } + + if (!inserted) + { + Model.Sprites.Add(model); + } + } + catch (Exception exception) + { + await MessageWindow.ShowErrorDialog("Error attempting to select the sprite"); + } + } } From c755856a01ee506b9c0404d74a6811e455135c45 Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Thu, 19 Dec 2024 22:01:30 -0500 Subject: [PATCH 2/2] Fix warnings and back package import --- .../Services/TrackerSpriteService.cs | 3 +-- .../TrackerCouncil.Smz3.Data.csproj | 7 +------ src/TrackerCouncil.Smz3.Data/ViewModels/SpriteViewModel.cs | 4 ++-- .../Services/TrackerSpeechWindowService.cs | 2 +- src/TrackerCouncil.Smz3.UI/Views/SpriteWindow.axaml.cs | 2 +- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/TrackerCouncil.Smz3.Data/Services/TrackerSpriteService.cs b/src/TrackerCouncil.Smz3.Data/Services/TrackerSpriteService.cs index a416c1be0..322ad3340 100644 --- a/src/TrackerCouncil.Smz3.Data/Services/TrackerSpriteService.cs +++ b/src/TrackerCouncil.Smz3.Data/Services/TrackerSpriteService.cs @@ -9,9 +9,8 @@ namespace TrackerCouncil.Smz3.Data.Services; /// /// Service for loading tracker speech sprites /// -/// /// -public class TrackerSpriteService(ILogger logger, OptionsFactory optionsFactory) +public class TrackerSpriteService(OptionsFactory optionsFactory) { private List _packs = []; diff --git a/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj b/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj index ceef903b2..ac272ec37 100644 --- a/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj +++ b/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj @@ -22,6 +22,7 @@ + @@ -35,12 +36,6 @@ - - - ..\..\..\..\..\.nuget\packages\avalonia\11.0.11\ref\net6.0\Avalonia.Base.dll - - - diff --git a/src/TrackerCouncil.Smz3.Data/ViewModels/SpriteViewModel.cs b/src/TrackerCouncil.Smz3.Data/ViewModels/SpriteViewModel.cs index da42ae775..98cfb5c30 100644 --- a/src/TrackerCouncil.Smz3.Data/ViewModels/SpriteViewModel.cs +++ b/src/TrackerCouncil.Smz3.Data/ViewModels/SpriteViewModel.cs @@ -50,8 +50,8 @@ public SpriteViewModel(Sprite sprite) public int ImageHeight { get; } public float IconOpacity { get; } - private string _previewPath; - public string PreviewPath + private string? _previewPath; + public string? PreviewPath { get => _previewPath; set => SetField(ref _previewPath, value); diff --git a/src/TrackerCouncil.Smz3.UI/Services/TrackerSpeechWindowService.cs b/src/TrackerCouncil.Smz3.UI/Services/TrackerSpeechWindowService.cs index 6b39499ec..b49560864 100644 --- a/src/TrackerCouncil.Smz3.UI/Services/TrackerSpeechWindowService.cs +++ b/src/TrackerCouncil.Smz3.UI/Services/TrackerSpeechWindowService.cs @@ -13,7 +13,7 @@ namespace TrackerCouncil.Smz3.UI.Services; -public class TrackerSpeechWindowService(ICommunicator communicator, IUIService uiService, OptionsFactory optionsFactory, TrackerSpriteService trackerSpriteService) : ControlService +public class TrackerSpeechWindowService(ICommunicator communicator, OptionsFactory optionsFactory, TrackerSpriteService trackerSpriteService) : ControlService { TrackerSpeechWindowViewModel _model = new(); diff --git a/src/TrackerCouncil.Smz3.UI/Views/SpriteWindow.axaml.cs b/src/TrackerCouncil.Smz3.UI/Views/SpriteWindow.axaml.cs index b550a8ca0..90c954568 100644 --- a/src/TrackerCouncil.Smz3.UI/Views/SpriteWindow.axaml.cs +++ b/src/TrackerCouncil.Smz3.UI/Views/SpriteWindow.axaml.cs @@ -286,7 +286,7 @@ private async void AddSpriteButton_OnClick(object? sender, RoutedEventArgs e) Model.Sprites.Add(model); } } - catch (Exception exception) + catch { await MessageWindow.ShowErrorDialog("Error attempting to select the sprite"); }