Sfoglia il codice sorgente

Sync fixes + optimizations

flabbet 3 anni fa
parent
commit
ef70344acf

+ 1 - 3
PixiEditor/Models/DataHolders/Palettes/Palette.cs

@@ -1,10 +1,8 @@
 #nullable enable
+using System;
 using PixiEditor.Helpers;
 using System.Collections.Generic;
-using System.Collections.ObjectModel;
 using System.IO;
-using System.Text.RegularExpressions;
-
 namespace PixiEditor.Models.DataHolders.Palettes
 {
     public class Palette : NotifyableObject

+ 10 - 0
PixiEditor/Models/DataHolders/Palettes/RefreshType.cs

@@ -0,0 +1,10 @@
+namespace PixiEditor.Models.DataHolders.Palettes;
+
+public enum RefreshType
+{
+    All,
+    Created,
+    Updated,
+    Deleted,
+    Renamed
+}

+ 175 - 27
PixiEditor/Models/DataProviders/LocalPalettesFetcher.cs

@@ -13,6 +13,8 @@ using SkiaSharp;
 
 namespace PixiEditor.Models.DataProviders
 {
+    public delegate void CacheUpdate(RefreshType refreshType, Palette itemAffected, string oldName);
+        
     public class LocalPalettesFetcher : PaletteListDataSource
     {
         public static string PathToPalettesFolder { get; } = Path.Join(
@@ -21,15 +23,37 @@ namespace PixiEditor.Models.DataProviders
 
         public List<Palette> CachedPalettes { get; private set; }
 
-        public event Action<List<Palette>> CacheUpdated; 
+        public event CacheUpdate CacheUpdated; 
+        
+        private List<string> _cachedFavoritePalettes;
 
         private FileSystemWatcher _watcher;
+        
+
+        public override void Initialize()
+        {
+            InitDir();
+            _watcher = new FileSystemWatcher(PathToPalettesFolder);
+            _watcher.Filter = "*.pal";
+            _watcher.Changed += FileSystemChanged;
+            _watcher.Deleted += FileSystemChanged;
+            _watcher.Renamed += RenamedFile;
+            _watcher.Created += FileSystemChanged;
+
+            _watcher.EnableRaisingEvents = true;
+            _cachedFavoritePalettes = IPreferences.Current.GetLocalPreference<List<string>>(PreferencesConstants.FavouritePalettes);
+            
+            IPreferences.Current.AddCallback(PreferencesConstants.FavouritePalettes, updated =>
+            {
+                _cachedFavoritePalettes = (List<string>)updated;
+            });
+        }
 
         public override async Task<PaletteList> FetchPaletteList(int startIndex, int count, FilteringSettings filtering)
         {
             if(CachedPalettes == null)
             {
-                await RefreshCache();
+                await RefreshCacheAll();
             }
 
             PaletteList result = new PaletteList
@@ -80,18 +104,110 @@ namespace PixiEditor.Models.DataProviders
             return newName;
         }
 
-        public async Task RefreshCache()
+        public async Task RefreshCache(RefreshType refreshType, string file)
+        {
+            Palette updated = null;
+            string affectedFileName = null;
+            
+            switch (refreshType)
+            {
+                case RefreshType.All:
+                    throw new ArgumentException("To handle refreshing all items, use RefreshCacheAll");
+                case RefreshType.Created:
+                    updated = await RefreshCacheAdded(file);
+                    break;
+                case RefreshType.Updated:
+                    updated = await RefreshCacheUpdated(file);
+                    break;
+                case RefreshType.Deleted:
+                    affectedFileName = RefreshCacheDeleted(file);
+                    break;
+                case RefreshType.Renamed:
+                    throw new ArgumentException("To handle renaming, use RefreshCacheRenamed");
+                default:
+                    throw new ArgumentOutOfRangeException(nameof(refreshType), refreshType, null);
+            }
+            CacheUpdated?.Invoke(refreshType, updated, affectedFileName);
+        }
+
+        public void RefreshCacheRenamed(string newFilePath, string oldFilePath)
+        {
+            string oldFileName = Path.GetFileName(oldFilePath);
+            int index = CachedPalettes.FindIndex(p => p.FileName == oldFileName);
+            if (index == -1) return;
+            
+            Palette palette = CachedPalettes[index];
+            palette.FileName = Path.GetFileName(newFilePath);
+            palette.Name = Path.GetFileNameWithoutExtension(newFilePath);
+            
+            CacheUpdated?.Invoke(RefreshType.Renamed, palette, oldFileName);
+        }
+
+        private string RefreshCacheDeleted(string filePath)
+        {
+            string fileName = Path.GetFileName(filePath);
+            int index = CachedPalettes.FindIndex(p => p.FileName == fileName);
+            if (index == -1) return null;
+            
+            CachedPalettes.RemoveAt(index);
+            return fileName;
+        }
+
+        private async Task<Palette> RefreshCacheItem(string file, Action<Palette> action)
+        {
+            if (File.Exists(file))
+            {
+                string extension = Path.GetExtension(file);
+                var foundParser = AvailableParsers.FirstOrDefault(x => x.SupportedFileExtensions.Contains(extension));
+                if (foundParser != null)
+                {
+                    var newPalette = await foundParser.Parse(file);
+                    if (newPalette != null)
+                    {
+                        Palette pal = CreatePalette(newPalette, file,
+                            _cachedFavoritePalettes?.Contains(newPalette.Title) ?? false);
+                        action(pal);
+                        
+                        return pal;
+                    }
+                }
+            }
+            
+            return null;
+        }
+        
+        private async Task<Palette> RefreshCacheUpdated(string file)
+        {
+            return await RefreshCacheItem(file, palette =>
+            {
+                Palette existingPalette = CachedPalettes.FirstOrDefault(x => x.FileName == palette.FileName);
+                if (existingPalette != null)
+                {
+                    existingPalette.Colors = palette.Colors.ToList();
+                    existingPalette.Name = palette.Name;
+                    existingPalette.FileName = palette.FileName;
+                }
+            });
+        }
+
+        private async Task<Palette> RefreshCacheAdded(string file)
+        {
+            return await RefreshCacheItem(file, CachedPalettes.Add);
+        }
+
+        public async Task RefreshCacheAll()
         {
             string[] files = DirectoryExtensions.GetFiles(PathToPalettesFolder,
                 string.Join("|", AvailableParsers.SelectMany(x => x.SupportedFileExtensions)),
                 SearchOption.TopDirectoryOnly);
             CachedPalettes = await ParseAll(files);
-            CacheUpdated?.Invoke(CachedPalettes);
+            CacheUpdated?.Invoke(RefreshType.All, null, null);
         }
 
         private async Task<List<Palette>> ParseAll(string[] files)
         {
             List<Palette> result = new List<Palette>();
+
             foreach (var file in files)
             {
                 string extension = Path.GetExtension(file);
@@ -99,15 +215,7 @@ namespace PixiEditor.Models.DataProviders
                 var foundParser = AvailableParsers.First(x => x.SupportedFileExtensions.Contains(extension));
                 {
                     PaletteFileData fileData = await foundParser.Parse(file);
-                    var palette = new Palette(
-                        fileData.Title,
-                        new List<string>(fileData.GetHexColors()),
-                        Path.GetFileName(file));
-                    List<string> favouritePalettes = IPreferences.Current.GetLocalPreference<List<string>>(PreferencesConstants.FavouritePalettes);
-                    if (favouritePalettes != null)
-                    {
-                        palette.IsFavourite = favouritePalettes.Contains(palette.Name);
-                    }
+                    var palette = CreatePalette(fileData, file, _cachedFavoritePalettes?.Contains(fileData.Title) ?? false);
 
                     result.Add(palette);
                 }
@@ -116,6 +224,19 @@ namespace PixiEditor.Models.DataProviders
             return result;
         }
 
+        private static Palette CreatePalette(PaletteFileData fileData, string file, bool isFavourite)
+        {
+            var palette = new Palette(
+                fileData.Title,
+                new List<string>(fileData.GetHexColors()),
+                Path.GetFileName(file))
+            {
+                IsFavourite = isFavourite
+            };
+
+            return palette;
+        }
+
         public async Task SavePalette(string fileName, SKColor[] colors)
         {
             _watcher.EnableRaisingEvents = false;
@@ -125,7 +246,7 @@ namespace PixiEditor.Models.DataProviders
 
             
             _watcher.EnableRaisingEvents = true;
-            await RefreshCache();
+            await RefreshCache(RefreshType.Created, path);
         }
 
         public void DeletePalette(string name)
@@ -137,22 +258,49 @@ namespace PixiEditor.Models.DataProviders
             File.Delete(path);
         }
 
-        public override void Initialize()
+        private async void FileSystemChanged(object sender, FileSystemEventArgs e)
         {
-            InitDir();
-            _watcher = new FileSystemWatcher(PathToPalettesFolder);
-            _watcher.Filter = "*.pal";
-            _watcher.Changed += FileSystemChanged;
-            _watcher.Deleted += FileSystemChanged;
-            _watcher.Renamed += FileSystemChanged;
-            _watcher.Created += FileSystemChanged;
-
-            _watcher.EnableRaisingEvents = true;
+            bool waitableExceptionOccured = false;
+            do
+            {
+                try
+                {
+                    switch (e.ChangeType)
+                    {
+                        case WatcherChangeTypes.Created:
+                            await RefreshCache(RefreshType.Created, e.FullPath);
+                            break;
+                        case WatcherChangeTypes.Deleted:
+                            await RefreshCache(RefreshType.Deleted, e.FullPath);
+                            break;
+                        case WatcherChangeTypes.Changed:
+                            await RefreshCache(RefreshType.Updated, e.FullPath);
+                            break;
+                        case WatcherChangeTypes.Renamed:
+                            // Handled by method below
+                            break;
+                        case WatcherChangeTypes.All:
+                            await RefreshCache(RefreshType.Created, e.FullPath);
+                            break;
+                        default:
+                            throw new ArgumentOutOfRangeException();
+                    }
+                    
+                    waitableExceptionOccured = false;
+                }
+                catch(IOException ex)
+                {
+                    waitableExceptionOccured = true;
+                    await Task.Delay(100);
+                }
+                
+            }
+            while (waitableExceptionOccured);
         }
-
-        private async void FileSystemChanged(object sender, FileSystemEventArgs e)
+        
+        private void RenamedFile(object sender, RenamedEventArgs e)
         {
-            await RefreshCache();
+            RefreshCacheRenamed(e.FullPath, e.OldFullPath);
         }
 
         private static void InitDir()

+ 4 - 2
PixiEditor/Models/IO/JascPalFile/JascFileParser.cs

@@ -15,8 +15,10 @@ public class JascFileParser : PaletteFileParser
     public override string FileName => "Jasc Palette";
 
     public static async Task<PaletteFileData> ParseFile(string path)
-    {
-        string fileContent = await File.ReadAllTextAsync(path);
+    { 
+        using var stream = File.OpenText(path);
+        
+        string fileContent = await stream.ReadToEndAsync();
         string[] lines = fileContent.Split('\n');
         string name = Path.GetFileNameWithoutExtension(path);
         string fileType = lines[0];

+ 81 - 11
PixiEditor/Views/Dialogs/PalettesBrowser.xaml.cs

@@ -10,10 +10,8 @@ using System.Linq;
 using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Controls;
-using System.Windows.Documents;
 using System.Windows.Input;
 using System.Windows.Navigation;
-using System.Windows.Threading;
 using Microsoft.Win32;
 using PixiEditor.Views.UserControls.Palettes;
 using SkiaSharp;
@@ -171,7 +169,7 @@ namespace PixiEditor.Views.Dialogs
             Loaded += async (sender, args) =>
             {
                 localPalettesFetcher.CacheUpdated += LocalCacheRefreshed;
-                await localPalettesFetcher.RefreshCache();
+                await localPalettesFetcher.RefreshCacheAll();
             };
             Closed += (s, e) =>
             {
@@ -187,23 +185,81 @@ namespace PixiEditor.Views.Dialogs
             {
                 Owner = Application.Current.MainWindow,
                 ImportPaletteCommand = importPaletteCommand,
-                PaletteListDataSources = dataSources
+                PaletteListDataSources = dataSources,
+                CurrentEditingPalette = currentEditingPalette
             };
 
-            browser.CurrentEditingPalette = currentEditingPalette;
-
             browser.Show();
             return browser;
         }
 
-        private async void LocalCacheRefreshed(List<Palette> obj)
+        private async void LocalCacheRefreshed(RefreshType refreshType, Palette itemAffected, string fileNameAffected)
         {
             await Dispatcher.InvokeAsync(async () =>
             {
-                await UpdatePaletteList();
+            switch (refreshType)
+            {
+                case RefreshType.All:
+                    await UpdatePaletteList();
+                    break;
+                case RefreshType.Created:
+                    HandleCachePaletteCreated(itemAffected);
+                    break;
+                case RefreshType.Updated:
+                    HandleCacheItemUpdated(itemAffected);
+                    break;
+                case RefreshType.Deleted:
+                    HandleCacheItemDeleted(fileNameAffected);
+                    break;
+                case RefreshType.Renamed:
+                    HandleCacheItemRenamed(itemAffected, fileNameAffected);
+                    break;
+                default:
+                    throw new ArgumentOutOfRangeException(nameof(refreshType), refreshType, null);
+            }
+            
             });
         }
 
+        private void HandleCacheItemRenamed(Palette itemAffected, string oldFileName)
+        {
+            var old = SortedResults.FirstOrDefault(x => x.FileName == oldFileName);
+            if (old != null)
+            {
+                old.Name = itemAffected.Name;
+                old.FileName = itemAffected.FileName;
+            }
+            
+            UpdateRenamedFavourite(Path.GetFileNameWithoutExtension(oldFileName), itemAffected.Name);
+        }
+
+        private void HandleCacheItemDeleted(string deletedItemFileName)
+        {
+            Palette item = SortedResults.FirstOrDefault(x => x.FileName == deletedItemFileName);
+            if (item != null)
+            {
+                SortedResults.Remove(item);
+            }
+        }
+
+        private void HandleCacheItemUpdated(Palette updatedItem)
+        {
+            var item = SortedResults.FirstOrDefault(x => x.FileName == updatedItem.FileName);
+            if (item == null) return;
+            
+            item.Name = updatedItem.Name;
+            item.IsFavourite = updatedItem.IsFavourite;
+            item.Colors = updatedItem.Colors;
+            
+            Sort();
+        }
+
+        private void HandleCachePaletteCreated(Palette updatedItem)
+        {
+            SortedResults.Add(updatedItem);
+            Sort();
+        }
+
         private async void ToggleFavourite(Palette palette)
         {
             palette.IsFavourite = !palette.IsFavourite;
@@ -462,14 +518,12 @@ namespace PixiEditor.Views.Dialogs
         private void PaletteItem_OnRename(object sender, EditableTextBlock.TextChangedEventArgs e)
         {
             PaletteItem item = (PaletteItem)sender;
-
             string oldFileName = $"{e.OldText}.pal";
 
             if (string.IsNullOrWhiteSpace(e.NewText) || e.NewText == item.Palette.Name || e.NewText.Length > 50)
             {
                 item.Palette.FileName = oldFileName;
                 item.Palette.Name = e.OldText;
-                item.UpdateName(e.OldText);
                 return;
             }
 
@@ -488,7 +542,6 @@ namespace PixiEditor.Views.Dialogs
             {
                 item.Palette.FileName = oldFileName;
                 item.Palette.Name = e.OldText;
-                item.UpdateName(e.OldText);
                 return;
             }
 
@@ -496,6 +549,23 @@ namespace PixiEditor.Views.Dialogs
             File.Move(oldPath, newPath);
 
             item.Palette.FileName = finalNewName;
+            item.Palette.Name = e.NewText;
+            
+            HandleCacheItemRenamed(item.Palette, oldFileName);
+        }
+
+        private static void UpdateRenamedFavourite(string old, string newName)
+        {
+            var favourites = IPreferences.Current.GetLocalPreference(PreferencesConstants.FavouritePalettes,
+                new List<string>());
+
+            if (favourites.Contains(old))
+            {
+                favourites.Remove(old);
+                favourites.Add(newName);
+            }
+
+            IPreferences.Current.UpdateLocalPreference(PreferencesConstants.FavouritePalettes, favourites);
         }
 
         private void BrowseOnLospec_OnClick(object sender, RoutedEventArgs e)

+ 2 - 7
PixiEditor/Views/UserControls/Palettes/PaletteItem.xaml.cs

@@ -19,7 +19,7 @@ namespace PixiEditor.Views.UserControls.Palettes
 
         // Using a DependencyProperty as the backing store for Palette.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty PaletteProperty =
-            DependencyProperty.Register("Palette", typeof(Palette), typeof(PaletteItem), new PropertyMetadata(null));
+            DependencyProperty.Register("Palette", typeof(Palette), typeof(PaletteItem));
 
         public ICommand ImportPaletteCommand
         {
@@ -52,17 +52,12 @@ namespace PixiEditor.Views.UserControls.Palettes
 
         public event EventHandler<EditableTextBlock.TextChangedEventArgs> OnRename;
 
+ 
         public PaletteItem()
         {
             InitializeComponent();
         }
 
-        public void UpdateName(string newName)
-        {
-            titleTextBlock.Text = newName;
-            titleTextBlock.textBox.Text = newName;
-        }
-
         private void EditableTextBlock_OnSubmit(object sender, EditableTextBlock.TextChangedEventArgs e)
         {
             OnRename?.Invoke(this, e);