Browse Source

Painting toolset wip

flabbet 10 months ago
parent
commit
e00d3b23ab

+ 55 - 0
src/PixiEditor/Data/Configs/ToolSetsConfig.json

@@ -19,6 +19,61 @@
       "Zoom"
       "Zoom"
     ]
     ]
   },
   },
+  {
+    "Name": "PAINT_TOOLSET",
+    "Tools": [
+      "MoveViewport",
+      "RotateViewport",
+      "Move",
+      {
+        "ToolName": "Pen",
+        "Settings": {
+          "AntiAliasing": true
+        }
+      },
+      "Select",
+      {
+        "ToolName": "MagicWand",
+        "Settings": {
+          "ExposeTolerance": true
+        }
+      },
+      "Lasso",
+      {
+        "ToolName": "FloodFill",
+        "Settings": {
+          "ExposeTolerance": true
+        }
+      },
+      {
+        "ToolName": "RasterLine",
+        "Settings": {
+          "AntiAliasing": true
+        }
+      },
+        {
+            "ToolName": "RasterEllipse",
+            "Settings": {
+            "AntiAliasing": true
+            }
+        },
+        {
+            "ToolName": "RasterRectangle",
+            "Settings": {
+            "AntiAliasing": true
+            }
+        },
+      {
+        "ToolName": "Eraser",
+        "Settings": {
+          "AntiAliasing": true
+        }
+      },
+      "ColorPicker",
+      "Brightness",
+      "Zoom"
+    ]
+  },
   {
   {
     "Name": "VECTOR_TOOLSET",
     "Name": "VECTOR_TOOLSET",
     "Tools": [
     "Tools": [

+ 53 - 3
src/PixiEditor/Models/Config/ConfigManager.cs

@@ -1,21 +1,71 @@
 using System.Reflection;
 using System.Reflection;
 using Avalonia.Platform;
 using Avalonia.Platform;
 using Newtonsoft.Json;
 using Newtonsoft.Json;
+using PixiEditor.Models.IO;
 using PixiEditor.Views;
 using PixiEditor.Views;
 
 
 namespace PixiEditor.Models.Config;
 namespace PixiEditor.Models.Config;
 
 
 public class ConfigManager
 public class ConfigManager
 {
 {
-    // TODO: Copy config to local folder so it can be modified
     public T GetConfig<T>(string configName)
     public T GetConfig<T>(string configName)
     {
     {
-        string path = $"avares://{Assembly.GetExecutingAssembly().GetName().Name}/Data/Configs/{configName}.json";
+        // TODO: Local configs require a mechanism that will allow to update them when the embedded config changes
+        // but merges the changes with the local config or something like that, leaving as is for now
+        /*if (LocalConfigExists(configName))
+        {
+            try
+            {
+                return GetLocalConfig<T>(configName);
+            }
+            catch(JsonReaderException)
+            {
+                // If the local config is corrupted, delete it and load the embedded one
+                File.Delete(Path.Combine(Paths.UserConfigPath, $"Configs/{configName}.json"));
+            }
+        }*/
+
+        var embeddedConfig = GetEmbeddedConfig<T>(configName);
+        //SaveConfig(embeddedConfig, configName);
+        return embeddedConfig;
+    }
+
+    private T GetLocalConfig<T>(string configName)
+    {
+        string path = $"Configs/{configName}.json";
+        using FileStream file = File.Open(Path.Combine(Paths.UserConfigPath, path), FileMode.Open);
+        using StreamReader reader = new(file);
+
+        string json = reader.ReadToEnd();
+        return JsonConvert.DeserializeObject<T>(json);
+    }
+
+    private T GetEmbeddedConfig<T>(string configName)
+    {
+        string path = Path.Combine(Paths.InternalResourceDataPath, $"Configs/{configName}.json");
 
 
         using Stream config = AssetLoader.Open(new Uri(path));
         using Stream config = AssetLoader.Open(new Uri(path));
         using StreamReader reader = new(config);
         using StreamReader reader = new(config);
-        
+
         string json = reader.ReadToEnd();
         string json = reader.ReadToEnd();
         return JsonConvert.DeserializeObject<T>(json);
         return JsonConvert.DeserializeObject<T>(json);
     }
     }
+    
+    private void SaveConfig<T>(T config, string configName)
+    {
+        string path = Path.Combine(Paths.UserConfigPath, $"Configs/{configName}.json");
+        string json = JsonConvert.SerializeObject(config, Formatting.Indented);
+
+        Directory.CreateDirectory(Path.GetDirectoryName(path));
+        using FileStream file = File.Open(path, FileMode.Create);
+        using StreamWriter writer = new(file);
+
+        writer.Write(json);
+    }
+    
+    private bool LocalConfigExists(string configName)
+    {
+        string path = Path.Combine(Paths.UserConfigPath, $"Configs/{configName}.json");
+        return File.Exists(path);
+    }
 }
 }

+ 67 - 1
src/PixiEditor/Models/Config/ToolSetConfig.cs

@@ -1,4 +1,5 @@
 using Newtonsoft.Json;
 using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
 
 
 namespace PixiEditor.Models.Config;
 namespace PixiEditor.Models.Config;
 
 
@@ -9,5 +10,70 @@ public class ToolSetsConfig : List<ToolSetConfig>
 public class ToolSetConfig
 public class ToolSetConfig
 {
 {
     public string Name { get; set; }
     public string Name { get; set; }
-    public List<string> Tools { get; set; }
+    
+    [JsonConverter(typeof(ToolConverter))]
+    public List<ToolConfig> Tools { get; set; }
+}
+
+public class ToolConfig
+{
+    public string ToolName { get; set; }
+    public Dictionary<string, object>? Settings { get; set; }
+    public bool IsSimpleTool => Settings == null || Settings.Count == 0;
+}
+
+public class ToolConverter : JsonConverter
+{
+    public override bool CanConvert(Type objectType) => objectType == typeof(List<ToolConfig>);
+
+    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+    {
+        var token = JToken.Load(reader);
+
+        if (token.Type == JTokenType.Array)
+        {
+            var tools = new List<ToolConfig>();
+
+            foreach (var item in token)
+            {
+                if (item.Type == JTokenType.String)
+                {
+                    tools.Add(new ToolConfig { ToolName = item.ToString() });
+                }
+                else if (item.Type == JTokenType.Object)
+                {
+                    tools.Add(item.ToObject<ToolConfig>(serializer));
+                }
+                else
+                {
+                    throw new JsonSerializationException("Unexpected token type in Tools array");
+                }
+            }
+
+            return tools;
+        }
+
+        throw new JsonSerializationException("Expected array for Tools");
+    }
+
+    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+    {
+        var tools = (List<ToolConfig>)value;
+
+        writer.WriteStartArray();
+
+        foreach (var tool in tools)
+        {
+            if (tool.IsSimpleTool)
+            {
+                writer.WriteValue(tool.ToolName);
+            }
+            else
+            {
+                serializer.Serialize(writer, tool);
+            }
+        }
+
+        writer.WriteEndArray();
+    }
 }
 }

+ 1 - 0
src/PixiEditor/Models/Handlers/IToolHandler.cs

@@ -61,4 +61,5 @@ internal interface IToolHandler : IHandler
     public void OnSelected();
     public void OnSelected();
 
 
     public void OnDeselecting();
     public void OnDeselecting();
+    public void SetToolSetSettings(IToolSetHandler toolset, Dictionary<string, object>? settings);
 }
 }

+ 3 - 0
src/PixiEditor/Models/IO/Paths.cs

@@ -7,6 +7,9 @@ public static class Paths
     public static string DataResourceUri { get; } = $"avares://{typeof(Paths).Assembly.GetName().Name}/Data/";
     public static string DataResourceUri { get; } = $"avares://{typeof(Paths).Assembly.GetName().Name}/Data/";
     public static string DataFullPath { get; } = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Data");
     public static string DataFullPath { get; } = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Data");
     public static string ExtensionPackagesPath { get; } = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Extensions");
     public static string ExtensionPackagesPath { get; } = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Extensions");
+    public static string UserConfigPath { get; } = Path.Combine(
+        Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), 
+        "PixiEditor", "Configs");
     public static string UserExtensionsPath { get; } = Path.Combine(
     public static string UserExtensionsPath { get; } = Path.Combine(
         Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), 
         Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), 
         "PixiEditor", "Extensions");
         "PixiEditor", "Extensions");

+ 8 - 5
src/PixiEditor/ViewModels/SubViewModels/ToolsViewModel.cs

@@ -424,14 +424,17 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
         foreach (ToolSetConfig toolSet in toolSetConfig)
         foreach (ToolSetConfig toolSet in toolSetConfig)
         {
         {
             List<IToolHandler> tools = new List<IToolHandler>();
             List<IToolHandler> tools = new List<IToolHandler>();
-
-            foreach (string toolName in toolSet.Tools)
+            var toolSetViewModel = new ToolSetViewModel(toolSet.Name, tools);
+    
+            foreach (var toolFromToolset in toolSet.Tools)
             {
             {
-                IToolHandler? tool = allTools.FirstOrDefault(tool => tool.ToolName == toolName);
+                IToolHandler? tool = allTools.FirstOrDefault(tool => tool.ToolName == toolFromToolset.ToolName);
+                tool.SetToolSetSettings(toolSetViewModel, toolFromToolset.Settings);
+                
                 if (tool is null)
                 if (tool is null)
                 {
                 {
 #if DEBUG
 #if DEBUG
-                    throw new InvalidOperationException($"Tool '{toolName}' not found.");
+                    throw new InvalidOperationException($"Tool '{tool}' not found.");
 #endif
 #endif
 
 
                     continue;
                     continue;
@@ -440,7 +443,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
                 tools.Add(tool);
                 tools.Add(tool);
             }
             }
 
 
-            AllToolSets.Add(new ToolSetViewModel(toolSet.Name, tools));
+            AllToolSets.Add(toolSetViewModel);
         }
         }
     }
     }
 
 

+ 12 - 0
src/PixiEditor/ViewModels/Tools/ToolViewModel.cs

@@ -91,6 +91,8 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
     public Cursor Cursor { get; set; } = new Cursor(StandardCursorType.Arrow);
     public Cursor Cursor { get; set; } = new Cursor(StandardCursorType.Arrow);
 
 
     public IToolbar Toolbar { get; set; } = new EmptyToolbar();
     public IToolbar Toolbar { get; set; } = new EmptyToolbar();
+    
+    public Dictionary<IToolSetHandler, Dictionary<string, object>> ToolSetSettings { get; } = new();
 
 
     internal ToolViewModel()
     internal ToolViewModel()
     {
     {
@@ -151,6 +153,16 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
     {
     {
     }
     }
 
 
+    public void SetToolSetSettings(IToolSetHandler toolset, Dictionary<string, object>? settings)
+    {
+        if (settings == null || settings.Count == 0 || toolset == null)
+        {
+            return;
+        }
+        
+        ToolSetSettings[toolset] = settings;
+    }
+
     protected T GetValue<T>([CallerMemberName] string name = null)
     protected T GetValue<T>([CallerMemberName] string name = null)
     {
     {
         var setting = Toolbar.GetSetting(name);
         var setting = Toolbar.GetSetting(name);