Browse Source

Added parameter invocation

Krzysztof Krysiński 3 months ago
parent
commit
57ebd22867

+ 1 - 0
src/PixiEditor.Extensions.CommonApi/Commands/ICommandProvider.cs

@@ -4,5 +4,6 @@ public interface ICommandProvider
 {
     public void RegisterCommand(CommandMetadata command, Action execute, Func<bool>? canExecute = null);
     public void InvokeCommand(string commandName);
+    public void InvokeCommand(string commandName, object? parameter);
     public bool CommandExists(string commandName);
 }

+ 5 - 0
src/PixiEditor.Extensions.Sdk/Api/Commands/CommandProvider.cs

@@ -32,6 +32,11 @@ public class CommandProvider : ICommandProvider
         Native.invoke_command(commandName);
     }
 
+    public void InvokeCommand(string commandName, object parameter)
+    {
+        Interop.InvokeCommandGeneric(commandName, parameter);
+    }
+
     public bool CommandExists(string commandName)
     {
         return Native.command_exists(commandName);

+ 36 - 0
src/PixiEditor.Extensions.Sdk/Bridge/Interop.Commands.cs

@@ -20,4 +20,40 @@ internal static partial class Interop
     {
         CommandInvoked?.Invoke(uniqueName);
     }
+
+    public static void InvokeCommandGeneric(string commandName, object? parameter)
+    {
+        if (parameter == null)
+        {
+            Native.invoke_command_null_param(commandName);
+        }
+        else if (parameter is string str)
+        {
+            Native.invoke_command_string(commandName, str);
+        }
+        else if (parameter is int i)
+        {
+            Native.invoke_command_int(commandName, i);
+        }
+        else if (parameter is bool b)
+        {
+            Native.invoke_command_bool(commandName, b);
+        }
+        else if (parameter is float f)
+        {
+            Native.invoke_command_float(commandName, f);
+        }
+        else if (parameter is double d)
+        {
+            Native.invoke_command_double(commandName, d);
+        }
+        else if (parameter is byte[] bytes)
+        {
+            Native.invoke_command_bytes(commandName, InteropUtility.ByteArrayToIntPtr(bytes), bytes.Length);
+        }
+        else
+        {
+            throw new ArgumentException($"Unsupported parameter type: {parameter.GetType()}");
+        }
+    }
 }

+ 21 - 0
src/PixiEditor.Extensions.Sdk/Bridge/Native.Commands.cs

@@ -20,4 +20,25 @@ internal static partial class Native
     {
         CommandInvoked?.Invoke(uniqueName);
     }
+
+    [MethodImpl(MethodImplOptions.InternalCall)]
+    public static extern void invoke_command_null_param(string commandName);
+
+    [MethodImpl(MethodImplOptions.InternalCall)]
+    public static extern void invoke_command_string(string commandName, string parameter);
+
+    [MethodImpl(MethodImplOptions.InternalCall)]
+    public static extern void invoke_command_int(string commandName, int parameter);
+
+    [MethodImpl(MethodImplOptions.InternalCall)]
+    public static extern void invoke_command_bool(string commandName, bool parameter);
+
+    [MethodImpl(MethodImplOptions.InternalCall)]
+    public static extern void invoke_command_float(string commandName, float parameter);
+
+    [MethodImpl(MethodImplOptions.InternalCall)]
+    public static extern void invoke_command_double(string commandName, double parameter);
+
+    [MethodImpl(MethodImplOptions.InternalCall)]
+    public static extern void invoke_command_bytes(string commandName, IntPtr parameter, int length);
 }

+ 53 - 0
src/PixiEditor.Extensions.WasmRuntime/Api/CommandApi.cs

@@ -65,4 +65,57 @@ internal class CommandApi : ApiGroupHandler
             Api.Logger.LogError($"Command {prefixedName} is not accessible from {Metadata.UniqueName} extension.");
         }
     }
+
+    [ApiFunction("invoke_command_null_param")]
+    internal void InvokeCommandNullParam(string commandName)
+    {
+        CommandModule commandModule = Extension.GetModule<CommandModule>();
+        commandModule!.InvokeCommandGeneric(commandName, null);
+    }
+
+    [ApiFunction("invoke_command_string")]
+    internal void InvokeCommandString(string commandName, string parameter)
+    {
+        CommandModule commandModule = Extension.GetModule<CommandModule>();
+        commandModule!.InvokeCommandGeneric(commandName, parameter);
+    }
+
+    [ApiFunction("invoke_command_int")]
+    internal void InvokeCommandInt(string commandName, int parameter)
+    {
+        CommandModule commandModule = Extension.GetModule<CommandModule>();
+        commandModule!.InvokeCommandGeneric(commandName, parameter);
+    }
+
+    [ApiFunction("invoke_command_bool")]
+    internal void InvokeCommandBool(string commandName, bool parameter)
+    {
+        CommandModule commandModule = Extension.GetModule<CommandModule>();
+        commandModule!.InvokeCommandGeneric(commandName, parameter);
+    }
+
+    [ApiFunction("invoke_command_float")]
+    internal void InvokeCommandFloat(string commandName, float parameter)
+    {
+        CommandModule commandModule = Extension.GetModule<CommandModule>();
+        commandModule!.InvokeCommandGeneric(commandName, parameter);
+    }
+
+    [ApiFunction("invoke_command_double")]
+    internal void InvokeCommandDouble(string commandName, double parameter)
+    {
+        CommandModule commandModule = Extension.GetModule<CommandModule>();
+        commandModule!.InvokeCommandGeneric(commandName, parameter);
+    }
+
+    [ApiFunction("invoke_command_bytes")]
+    internal void InvokeCommandBytes(string commandName, Span<byte> parameter)
+    {
+        CommandModule commandModule = Extension.GetModule<CommandModule>();
+
+        byte[] bytes = new byte[parameter.Length];
+        parameter.CopyTo(bytes);
+
+        commandModule!.InvokeCommandGeneric(commandName, bytes);
+    }
 }

+ 23 - 1
src/PixiEditor.Extensions.WasmRuntime/Api/Modules/CommandModule.cs

@@ -1,5 +1,6 @@
 using PixiEditor.Extensions.Commands;
 using PixiEditor.Extensions.CommonApi.Commands;
+using PixiEditor.Extensions.CommonApi.Utilities;
 
 namespace PixiEditor.Extensions.WasmRuntime.Api.Modules;
 
@@ -8,12 +9,14 @@ internal class CommandModule : ApiModule
     public ICommandProvider CommandProvider { get; }
     public ICommandSupervisor CommandSupervisor { get; }
 
-    public CommandModule(WasmExtensionInstance extension, ICommandProvider commandProvider, ICommandSupervisor supervisor) : base(extension)
+    public CommandModule(WasmExtensionInstance extension, ICommandProvider commandProvider,
+        ICommandSupervisor supervisor) : base(extension)
     {
         CommandProvider = commandProvider;
         CommandSupervisor = supervisor;
     }
 
+
     internal void InvokeCommandInvoked(string uniqueName)
     {
         var action = Extension.Instance.GetAction<int>("command_invoked");
@@ -21,4 +24,23 @@ internal class CommandModule : ApiModule
         var pathPtr = Extension.WasmMemoryUtility.WriteString(uniqueName);
         action?.Invoke(pathPtr);
     }
+
+    public void InvokeCommandGeneric(string commandName, object? parameter)
+    {
+        string prefixedName = PrefixedNameUtility.ToCommandUniqueName(Extension.Metadata.UniqueName, commandName, true);
+
+        if (!CommandProvider.CommandExists(prefixedName))
+        {
+            return;
+        }
+
+        if (CommandSupervisor.ValidateCommandPermissions(prefixedName, Extension))
+        {
+            CommandProvider.InvokeCommand(commandName, parameter);
+        }
+        else
+        {
+            Extension.Api.Logger.LogError($"Command {prefixedName} is not accessible from {Extension.Metadata.UniqueName} extension.");
+        }
+    }
 }

+ 2 - 1
src/PixiEditor/Models/Commands/CommandContext/CommandExecutionSourceType.cs

@@ -6,5 +6,6 @@ public enum CommandExecutionSourceType
     Shortcut,
     Menu,
     CommandBinding,
-    Search
+    Search,
+    Extension,
 }

+ 11 - 0
src/PixiEditor/Models/Commands/CommandContext/ExtensionSourceInfo.cs

@@ -0,0 +1,11 @@
+namespace PixiEditor.Models.Commands.CommandContext;
+
+public class ExtensionSourceInfo : ICommandExecutionSourceInfo
+{
+    public CommandExecutionSourceType SourceType { get; } = CommandExecutionSourceType.Extension;
+
+
+    public ExtensionSourceInfo()
+    {
+    }
+}

+ 1 - 0
src/PixiEditor/Models/Commands/CommandContext/ICommandExecutionSourceInfo.cs

@@ -6,6 +6,7 @@ namespace PixiEditor.Models.Commands.CommandContext;
 [JsonDerivedType(typeof(MenuSourceInfo))]
 [JsonDerivedType(typeof(CommandBindingSourceInfo))]
 [JsonDerivedType(typeof(SearchSourceInfo))]
+[JsonDerivedType(typeof(ExtensionSourceInfo))]
 public interface ICommandExecutionSourceInfo
 {
     public CommandExecutionSourceType SourceType { get; }

+ 13 - 0
src/PixiEditor/Models/ExtensionServices/CommandProvider.cs

@@ -1,5 +1,6 @@
 using PixiEditor.Extensions.CommonApi.Commands;
 using PixiEditor.Models.Commands;
+using PixiEditor.Models.Commands.CommandContext;
 using PixiEditor.Models.Commands.Commands;
 using PixiEditor.Models.Commands.Evaluators;
 using PixiEditor.Models.Input;
@@ -62,6 +63,18 @@ public class CommandProvider : ICommandProvider
         }
     }
 
+    public void InvokeCommand(string commandName, object? parameter)
+    {
+        if (CommandController.Current.Commands.ContainsKey(commandName))
+        {
+            var command = CommandController.Current.Commands[commandName];
+            if (command.CanExecute())
+            {
+                command.Execute(new CommandExecutionContext(parameter, new ExtensionSourceInfo()), true);
+            }
+        }
+    }
+
     public bool CommandExists(string commandName)
     {
         return CommandController.Current.Commands.ContainsKey(commandName);

+ 1 - 0
src/PixiEditor/ViewModels/Dock/LayoutManager.cs

@@ -166,6 +166,7 @@ internal class LayoutManager
                 if (dockable != null)
                 {
                     dockableHost.ActiveDockable = dockable;
+                    dockableHost.Context.FocusedTarget = dockableHost;
                     return;
                 }
             }

+ 29 - 0
src/PixiEditor/ViewModels/SubViewModels/LayoutViewModel.cs

@@ -1,5 +1,6 @@
 using System.Collections.ObjectModel;
 using Avalonia.Input;
+using Drawie.Numerics;
 using PixiDocks.Core.Docking;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.ViewModels.Dock;
@@ -24,6 +25,34 @@ internal class LayoutViewModel : SubViewModel<ViewModelMain>
         owner.WindowSubViewModel.LazyViewportRemoved += WindowSubViewModel_LazyViewportRemoved;
     }
 
+    [Command.Internal("PixiEditor.Layout.SplitActiveDockable")]
+    [Command.Internal("PixiEditor.Layout.SplitActiveDockableLeft", Parameter = DockingDirection.Left)]
+    [Command.Internal("PixiEditor.Layout.SplitActiveDockableRight", Parameter = DockingDirection.Right)]
+    [Command.Internal("PixiEditor.Layout.SplitActiveDockableUp", Parameter = DockingDirection.Top)]
+    [Command.Internal("PixiEditor.Layout.SplitActiveDockableDown", Parameter = DockingDirection.Bottom)]
+    public void SplitActiveDockable(DockingDirection direction)
+    {
+        if (LayoutManager.DockContext.FocusedTarget is IDockableHost host)
+        {
+            if (direction == DockingDirection.Bottom)
+            {
+                host.SplitDown(host.ActiveDockable);
+            }
+            else if (direction == DockingDirection.Top)
+            {
+                host.SplitUp(host.ActiveDockable);
+            }
+            else if (direction == DockingDirection.Left)
+            {
+                host.SplitLeft(host.ActiveDockable);
+            }
+            else if (direction == DockingDirection.Right)
+            {
+                host.SplitRight(host.ActiveDockable);
+            }
+        }
+    }
+
     private void WindowSubViewModel_ViewportAdded(ViewportWindowViewModel obj)
     {
         LayoutManager.AddViewport(obj);

+ 2 - 0
src/PixiEditor/ViewModels/SubViewModels/ViewportWindowViewModel.cs

@@ -11,6 +11,7 @@ using PixiEditor.Models.DocumentModels;
 using Drawie.Numerics;
 using PixiEditor.Extensions.CommonApi.UserPreferences.Settings;
 using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
+using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Handlers;
 using PixiEditor.ViewModels.Dock;
 using PixiEditor.ViewModels.Document;
@@ -179,6 +180,7 @@ internal class ViewportWindowViewModel : SubViewModel<WindowViewModel>, IDockabl
         TabCustomizationSettings.Icon = previewPainterControl;
     }
 
+
     private void DocumentOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
     {
         if (e.PropertyName == nameof(DocumentViewModel.FileName))

+ 10 - 0
src/PixiEditor/ViewModels/SubViewModels/WindowViewModel.cs

@@ -96,6 +96,16 @@ internal class WindowViewModel : SubViewModel<ViewModelMain>, IWindowHandler
         }
     }
 
+
+    [Command.Internal("PixiEditor.Viewport.SetRenderOutput")]
+    public void SetRenderOutputOfCurrentViewport(string renderOutput)
+    {
+        if (ActiveWindow is ViewportWindowViewModel viewport)
+        {
+            viewport.RenderOutputName = renderOutput;
+        }
+    }
+
     [Commands_Command.Basic("PixiEditor.Window.FlipHorizontally", "FLIP_VIEWPORT_HORIZONTALLY",
         "FLIP_VIEWPORT_HORIZONTALLY", CanExecute = "PixiEditor.HasDocument",
         Icon = PixiPerfectIcons.YFlip, AnalyticsTrack = true)]