浏览代码

Merge pull request #1282 from PixiEditor/disable-context-pen

Fixed context menu appearing when holding pen
Krzysztof Krysiński 2 天之前
父节点
当前提交
f94123c884

+ 10 - 6
src/PixiEditor.ChangeableDocument/Changeables/Brushes/BrushEngine.cs

@@ -31,6 +31,7 @@ public class BrushEngine : IDisposable
     private int lastAppliedHistoryIndex = -1;
     private int lastAppliedHistoryIndex = -1;
     private VecI lastCachedTexturePaintableSize = VecI.Zero;
     private VecI lastCachedTexturePaintableSize = VecI.Zero;
     private TexturePaintable? lastCachedTexturePaintable = null;
     private TexturePaintable? lastCachedTexturePaintable = null;
+    private Matrix3X3 lastCachedTransform = Matrix3X3.Identity;
     private readonly List<RecordedPoint> pointsHistory = new();
     private readonly List<RecordedPoint> pointsHistory = new();
 
 
     private bool drawnOnce = false;
     private bool drawnOnce = false;
@@ -217,6 +218,7 @@ public class BrushEngine : IDisposable
             lastPos = point;
             lastPos = point;
             drawnOnce = true;
             drawnOnce = true;
             target?.SetBlendMode(imageBlendMode);
             target?.SetBlendMode(imageBlendMode);
+            brushNode.ResetContentTexture();
         }
         }
 
 
         float strokeWidth = brushData.StrokeWidth;
         float strokeWidth = brushData.StrokeWidth;
@@ -333,10 +335,11 @@ public class BrushEngine : IDisposable
         bool snapToPixels = brushNode.SnapToPixels.Value;
         bool snapToPixels = brushNode.SnapToPixels.Value;
         bool canReuseStamps = brushNode.CanReuseStamps.Value;
         bool canReuseStamps = brushNode.CanReuseStamps.Value;
         Blender? stampBlender = brushNode.UseCustomStampBlender.Value ? brushNode.LastStampBlender : null;
         Blender? stampBlender = brushNode.UseCustomStampBlender.Value ? brushNode.LastStampBlender : null;
+        Matrix3X3 transform = brushNode.Transform.Value;
         //Blender? imageBlender = brushNode.UseCustomImageBlender.Value ? brushNode.LastImageBlender : null;
         //Blender? imageBlender = brushNode.UseCustomImageBlender.Value ? brushNode.LastImageBlender : null;
 
 
         if (PaintBrush(target, autoPosition, vectorShape, rect, fitToStrokeSize, pressure, content, contentTexture,
         if (PaintBrush(target, autoPosition, vectorShape, rect, fitToStrokeSize, pressure, content, contentTexture,
-                stampBlender, brushNode.StampBlendMode.Value, antiAliasing, fill, stroke, snapToPixels, canReuseStamps))
+                stampBlender, brushNode.StampBlendMode.Value, antiAliasing, fill, stroke, snapToPixels, canReuseStamps, transform))
         {
         {
             lastPos = point;
             lastPos = point;
         }
         }
@@ -345,7 +348,7 @@ public class BrushEngine : IDisposable
     public bool PaintBrush(ChunkyImage target, bool autoPosition, ShapeVectorData vectorShape,
     public bool PaintBrush(ChunkyImage target, bool autoPosition, ShapeVectorData vectorShape,
         RectD rect, bool fitToStrokeSize, float pressure, Painter? content,
         RectD rect, bool fitToStrokeSize, float pressure, Painter? content,
         Texture? contentTexture, Blender? blender, DrawingApiBlendMode blendMode, bool antiAliasing, Paintable fill, Paintable stroke,
         Texture? contentTexture, Blender? blender, DrawingApiBlendMode blendMode, bool antiAliasing, Paintable fill, Paintable stroke,
-        bool snapToPixels, bool canReuseStamps)
+        bool snapToPixels, bool canReuseStamps, Matrix3X3 transform)
     {
     {
         var path = vectorShape.ToPath(true);
         var path = vectorShape.ToPath(true);
         if (path == null)
         if (path == null)
@@ -411,11 +414,12 @@ public class BrushEngine : IDisposable
                 TexturePaintable brushPaintable;
                 TexturePaintable brushPaintable;
                 if (canReuseStamps)
                 if (canReuseStamps)
                 {
                 {
-                    if (lastCachedTexturePaintableSize != contentTexture.Size || lastCachedTexturePaintable == null)
+                    if (lastCachedTexturePaintableSize != contentTexture.Size || lastCachedTexturePaintable == null || lastCachedTransform != transform)
                     {
                     {
                         lastCachedTexturePaintable?.Dispose();
                         lastCachedTexturePaintable?.Dispose();
                         lastCachedTexturePaintable = new TexturePaintable(new Texture(contentTexture), false);
                         lastCachedTexturePaintable = new TexturePaintable(new Texture(contentTexture), false);
                         lastCachedTexturePaintableSize = contentTexture.Size;
                         lastCachedTexturePaintableSize = contentTexture.Size;
+                        lastCachedTransform = transform;
                     }
                     }
 
 
                     brushPaintable = lastCachedTexturePaintable;
                     brushPaintable = lastCachedTexturePaintable;
@@ -570,15 +574,15 @@ public class BrushEngine : IDisposable
     {
     {
         if (fitToStrokeSize)
         if (fitToStrokeSize)
         {
         {
-            VecD scale = new VecD(rect.Size.X / (float)path.TightBounds.Width,
-                rect.Size.Y / (float)path.TightBounds.Height);
+            VecD scale = new VecD(rect.Size.X / (float)vectorShape.GeometryAABB.Width,
+                rect.Size.Y / (float)vectorShape.GeometryAABB.Height);
             if (scale.IsNaNOrInfinity())
             if (scale.IsNaNOrInfinity())
             {
             {
                 scale = VecD.Zero;
                 scale = VecD.Zero;
             }
             }
 
 
             VecD uniformScale = new VecD(Math.Min(scale.X, scale.Y));
             VecD uniformScale = new VecD(Math.Min(scale.X, scale.Y));
-            VecD center = autoPosition ? rect.Center : vectorShape.TransformedAABB.Center;
+            VecD center = autoPosition ? rect.Center : vectorShape.GeometryAABB.Center;
 
 
             path.Transform(Matrix3X3.CreateScale((float)uniformScale.X, (float)uniformScale.Y, (float)center.X,
             path.Transform(Matrix3X3.CreateScale((float)uniformScale.X, (float)uniformScale.Y, (float)center.X,
                 (float)center.Y));
                 (float)center.Y));

+ 22 - 7
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Brushes/BrushOutputNode.cs

@@ -89,8 +89,12 @@ public class BrushOutputNode : Node
     public const string UseCustomStampBlenderProperty = "UseCustomStampBlender";
     public const string UseCustomStampBlenderProperty = "UseCustomStampBlender";
     public const string CustomStampBlenderCodeProperty = "CustomStampBlender";
     public const string CustomStampBlenderCodeProperty = "CustomStampBlender";
     public const string StampBlendModeProperty = "StampBlendMode";
     public const string StampBlendModeProperty = "StampBlendMode";
+    public const string ContentProperty = "Content";
+    public const string ContentTransformProperty = "Transform";
 
 
     private VectorPath? previewVectorPath;
     private VectorPath? previewVectorPath;
+    private bool drawnContentTextureOnce = false;
+    private Matrix3X3 lastTranform = Matrix3X3.Identity;
 
 
     public BrushOutputNode()
     public BrushOutputNode()
     {
     {
@@ -98,8 +102,8 @@ public class BrushOutputNode : Node
         VectorShape = CreateInput<ShapeVectorData>("VectorShape", "SHAPE", null);
         VectorShape = CreateInput<ShapeVectorData>("VectorShape", "SHAPE", null);
         Stroke = CreateInput<Paintable>("Stroke", "STROKE", null);
         Stroke = CreateInput<Paintable>("Stroke", "STROKE", null);
         Fill = CreateInput<Paintable>("Fill", "FILL", null);
         Fill = CreateInput<Paintable>("Fill", "FILL", null);
-        Content = CreateRenderInput("Content", "CONTENT");
-        Transform = CreateInput<Matrix3X3>("Transform", "TRANSFORM", Matrix3X3.Identity);
+        Content = CreateRenderInput(ContentProperty, "CONTENT");
+        Transform = CreateInput<Matrix3X3>(ContentTransformProperty, "CONTENT_TRANSFORM", Matrix3X3.Identity);
         ImageBlendMode = CreateInput<Drawie.Backend.Core.Surfaces.BlendMode>("BlendMode", "BLEND_MODE",
         ImageBlendMode = CreateInput<Drawie.Backend.Core.Surfaces.BlendMode>("BlendMode", "BLEND_MODE",
             Drawie.Backend.Core.Surfaces.BlendMode.SrcOver);
             Drawie.Backend.Core.Surfaces.BlendMode.SrcOver);
         StampBlendMode = CreateInput<Drawie.Backend.Core.Surfaces.BlendMode>(StampBlendModeProperty, "STAMP_BLEND_MODE",
         StampBlendMode = CreateInput<Drawie.Backend.Core.Surfaces.BlendMode>(StampBlendModeProperty, "STAMP_BLEND_MODE",
@@ -146,11 +150,17 @@ public class BrushOutputNode : Node
         {
         {
             if (context.RenderOutputSize.LongestAxis > 0)
             if (context.RenderOutputSize.LongestAxis > 0)
             {
             {
-                ContentTexture = cache.RequestTexture(0, context.RenderOutputSize, context.ProcessingColorSpace);
-                ContentTexture.DrawingSurface.Canvas.Save();
-                ContentTexture.DrawingSurface.Canvas.SetMatrix(Transform.Value);
-                Content.Value.Paint(context, ContentTexture.DrawingSurface.Canvas);
-                ContentTexture.DrawingSurface.Canvas.Restore();
+                if (!CanReuseStamps.Value || ContentTexture == null || ContentTexture.Size != context.RenderOutputSize ||
+                    !drawnContentTextureOnce || Transform.Value != lastTranform)
+                {
+                    ContentTexture = cache.RequestTexture(0, context.RenderOutputSize, context.ProcessingColorSpace);
+                    ContentTexture.DrawingSurface.Canvas.Save();
+                    ContentTexture.DrawingSurface.Canvas.SetMatrix(Transform.Value);
+                    Content.Value.Paint(context, ContentTexture.DrawingSurface.Canvas);
+                    ContentTexture.DrawingSurface.Canvas.Restore();
+                    drawnContentTextureOnce = true;
+                    lastTranform = Transform.Value;
+                }
             }
             }
         }
         }
 
 
@@ -179,6 +189,11 @@ public class BrushOutputNode : Node
         additionalData["PersistentId"] = PersistentId;
         additionalData["PersistentId"] = PersistentId;
     }
     }
 
 
+    public void ResetContentTexture()
+    {
+        drawnContentTextureOnce = false;
+    }
+
     internal override void DeserializeAdditionalData(IReadOnlyDocument target, IReadOnlyDictionary<string, object> data,
     internal override void DeserializeAdditionalData(IReadOnlyDocument target, IReadOnlyDictionary<string, object> data,
         List<IChangeInfo> infos)
         List<IChangeInfo> infos)
     {
     {

+ 23 - 2
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/Blackboard/SetBlackboardVariable_Change.cs

@@ -6,7 +6,7 @@ using PixiEditor.ChangeableDocument.ChangeInfos.NodeGraph.Blackboard;
 
 
 namespace PixiEditor.ChangeableDocument.Changes.NodeGraph.Blackboard;
 namespace PixiEditor.ChangeableDocument.Changes.NodeGraph.Blackboard;
 
 
-internal class SetBlackboardVariable_Change : Change
+internal class SetBlackboardVariable_Change : InterruptableUpdateableChange
 {
 {
     private string variable;
     private string variable;
     private object value;
     private object value;
@@ -19,7 +19,7 @@ internal class SetBlackboardVariable_Change : Change
     private string unit;
     private string unit;
     private bool isExposed;
     private bool isExposed;
 
 
-    [GenerateMakeChangeAction]
+    [GenerateUpdateableChangeActions]
     public SetBlackboardVariable_Change(string variable, object value, double min, double max, string unit, bool isExposed)
     public SetBlackboardVariable_Change(string variable, object value, double min, double max, string unit, bool isExposed)
     {
     {
         this.variable = variable;
         this.variable = variable;
@@ -30,6 +30,17 @@ internal class SetBlackboardVariable_Change : Change
         this.isExposed = isExposed;
         this.isExposed = isExposed;
     }
     }
 
 
+    [UpdateChangeMethod]
+    public void Update(string variable, object value, double min, double max, string unit, bool isExposed)
+    {
+        this.variable = variable;
+        this.value = value;
+        this.min = min;
+        this.max = max;
+        this.unit = unit;
+        this.isExposed = isExposed;
+    }
+
     public override bool InitializeAndValidate(Document target)
     public override bool InitializeAndValidate(Document target)
     {
     {
         if (variable == null)
         if (variable == null)
@@ -80,6 +91,11 @@ internal class SetBlackboardVariable_Change : Change
         out bool ignoreInUndo)
         out bool ignoreInUndo)
     {
     {
         ignoreInUndo = false;
         ignoreInUndo = false;
+        return Apply(target);
+    }
+
+    private OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target)
+    {
         if (target.NodeGraph.Blackboard.GetVariable(variable) == null)
         if (target.NodeGraph.Blackboard.GetVariable(variable) == null)
         {
         {
             Type type = value.GetType();
             Type type = value.GetType();
@@ -95,6 +111,11 @@ internal class SetBlackboardVariable_Change : Change
         return new List<IChangeInfo>() { new BlackboardVariable_ChangeInfo(variable, oldVar.Type, value, oldVar.Min ?? double.MinValue, oldVar.Max ?? double.MaxValue, oldVar.Unit), new BlackboardVariableExposed_ChangeInfo(variable, isExposed) };
         return new List<IChangeInfo>() { new BlackboardVariable_ChangeInfo(variable, oldVar.Type, value, oldVar.Min ?? double.MinValue, oldVar.Max ?? double.MaxValue, oldVar.Unit), new BlackboardVariableExposed_ChangeInfo(variable, isExposed) };
     }
     }
 
 
+    public override OneOf<None, IChangeInfo, List<IChangeInfo>> ApplyTemporarily(Document target)
+    {
+        return Apply(target);
+    }
+
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
     {
         if (!existsInBlackboard)
         if (!existsInBlackboard)

+ 1 - 0
src/PixiEditor.UI.Common/Controls/SizeInput.axaml

@@ -40,6 +40,7 @@
                      FocusNext="{Binding FocusNext, ElementName=uc}"
                      FocusNext="{Binding FocusNext, ElementName=uc}"
                      SelectOnMouseClick="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}"
                      SelectOnMouseClick="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}"
                      ConfirmOnEnter="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}"
                      ConfirmOnEnter="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}"
+                     DraggingGrabber="{Binding DraggingGrabber, ElementName=uc, Mode=TwoWay}"
                      Width="53"/>
                      Width="53"/>
             <Grid Grid.Column="1" Background="{Binding BorderBrush, ElementName=border}"
             <Grid Grid.Column="1" Background="{Binding BorderBrush, ElementName=border}"
                   d:Background="{DynamicResource ThemeAccentBrush}"/>
                   d:Background="{DynamicResource ThemeAccentBrush}"/>

+ 8 - 0
src/PixiEditor.UI.Common/Controls/SizeInput.axaml.cs

@@ -9,6 +9,8 @@ namespace PixiEditor.UI.Common.Controls;
 /// </summary>
 /// </summary>
 public partial class SizeInput : UserControl
 public partial class SizeInput : UserControl
 {
 {
+    public static readonly StyledProperty<bool> DraggingGrabberProperty = AvaloniaProperty.Register<SizeInput, bool>("DraggingGrabber");
+
     public static readonly StyledProperty<double> SizeProperty =
     public static readonly StyledProperty<double> SizeProperty =
         AvaloniaProperty.Register<SizeInput, double>(nameof(Size), defaultValue: 1);
         AvaloniaProperty.Register<SizeInput, double>(nameof(Size), defaultValue: 1);
 
 
@@ -115,6 +117,12 @@ public partial class SizeInput : UserControl
         set => SetValue(UnitProperty, value);
         set => SetValue(UnitProperty, value);
     }
     }
 
 
+    public bool DraggingGrabber
+    {
+        get { return (bool)GetValue(DraggingGrabberProperty); }
+        set { SetValue(DraggingGrabberProperty, value); }
+    }
+
     private void Border_MouseWheel(object? sender, PointerWheelEventArgs e)
     private void Border_MouseWheel(object? sender, PointerWheelEventArgs e)
     {
     {
         int step = (int)e.Delta.Y / 100;
         int step = (int)e.Delta.Y / 100;

+ 2 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -1290,5 +1290,6 @@
   "VIEWPORT_INFO_NODE": "Viewport Info",
   "VIEWPORT_INFO_NODE": "Viewport Info",
   "EQUALS_NODE": "Equals",
   "EQUALS_NODE": "Equals",
   "IS_LEFT_BUTTON_PRESSED": "Is Left Button Pressed",
   "IS_LEFT_BUTTON_PRESSED": "Is Left Button Pressed",
-  "IS_RIGHT_BUTTON_PRESSED": "Is Right Button Pressed"
+  "IS_RIGHT_BUTTON_PRESSED": "Is Right Button Pressed",
+  "CONTENT_TRANSFORM": "Content Transform"
 }
 }

+ 3 - 1
src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs

@@ -243,7 +243,9 @@ internal class ChangeExecutionController
         LeftMousePressed = true;
         LeftMousePressed = true;
         RightMousePressed = args.Properties.IsRightButtonPressed;
         RightMousePressed = args.Properties.IsRightButtonPressed;
 
 
-        lastPointerInfo = ConstructPointerInfo(args.Point.PositionOnCanvas, args);
+        // Some drivers do not report pressure for pointer down events. For example it happens on X11. We set pressure to 0, so Brush Engine does not
+        // draw a blob with 0.5 pressure on each start of stroke.
+        lastPointerInfo = ConstructPointerInfo(args.Point.PositionOnCanvas, args) with { Pressure = 0 }; 
         if (_queuedExecutor != null && currentSession == null)
         if (_queuedExecutor != null && currentSession == null)
         {
         {
             StartExecutor(_queuedExecutor);
             StartExecutor(_queuedExecutor);

+ 16 - 1
src/PixiEditor/ViewModels/Document/Blackboard/VariableViewModel.cs

@@ -84,8 +84,23 @@ internal class VariableViewModel : ViewModelBase, IVariableHandler
             if (suppressValueChange)
             if (suppressValueChange)
                 return;
                 return;
 
 
+            if (SettingView.MergeChanges)
+            {
+                internals.ActionAccumulator.AddActions(
+                    new SetBlackboardVariable_Action(Name, AdjustValueForBlackboard(SettingView.Value), min, max, unit, IsExposedBindable));
+            }
+            else
+            {
+                internals.ActionAccumulator.AddFinishedActions(
+                    new SetBlackboardVariable_Action(Name, AdjustValueForBlackboard(SettingView.Value), min, max, unit, IsExposedBindable),
+                    new EndSetBlackboardVariable_Action());
+            }
+        };
+
+        SettingView.MergeChangesEnded += () =>
+        {
             internals.ActionAccumulator.AddFinishedActions(
             internals.ActionAccumulator.AddFinishedActions(
-                new SetBlackboardVariable_Action(Name, AdjustValueForBlackboard(SettingView.Value), min, max, unit, IsExposedBindable));
+                new EndSetBlackboardVariable_Action());
         };
         };
 
 
         RemoveCommand = new RelayCommand(() =>
         RemoveCommand = new RelayCommand(() =>

+ 8 - 0
src/PixiEditor/ViewModels/Document/Nodes/Brushes/BrushOutputNodeViewModel.cs

@@ -15,6 +15,8 @@ internal class BrushOutputNodeViewModel : NodeViewModel<BrushOutputNode>
         InputPropertyMap[BrushOutputNode.BrushNameProperty].SocketEnabled = false;
         InputPropertyMap[BrushOutputNode.BrushNameProperty].SocketEnabled = false;
         InputPropertyMap[BrushOutputNode.FitToStrokeSizeProperty].SocketEnabled = false;
         InputPropertyMap[BrushOutputNode.FitToStrokeSizeProperty].SocketEnabled = false;
         InputPropertyMap[BrushOutputNode.UseCustomStampBlenderProperty].ValueChanged += OnValueChanged;
         InputPropertyMap[BrushOutputNode.UseCustomStampBlenderProperty].ValueChanged += OnValueChanged;
+        InputPropertyMap[BrushOutputNode.ContentProperty].ConnectedOutputChanged += OnContentConnectionChanged;
+        InputPropertyMap[BrushOutputNode.ContentTransformProperty].IsVisible = InputPropertyMap[BrushOutputNode.ContentProperty].ConnectedOutput != null;
         if(InputPropertyMap[BrushOutputNode.CustomStampBlenderCodeProperty] is StringPropertyViewModel codeProperty)
         if(InputPropertyMap[BrushOutputNode.CustomStampBlenderCodeProperty] is StringPropertyViewModel codeProperty)
         {
         {
             codeProperty.IsVisible = (bool)InputPropertyMap[BrushOutputNode.UseCustomStampBlenderProperty].Value;
             codeProperty.IsVisible = (bool)InputPropertyMap[BrushOutputNode.UseCustomStampBlenderProperty].Value;
@@ -27,4 +29,10 @@ internal class BrushOutputNodeViewModel : NodeViewModel<BrushOutputNode>
         InputPropertyMap[BrushOutputNode.CustomStampBlenderCodeProperty].IsVisible = (bool)args.NewValue;
         InputPropertyMap[BrushOutputNode.CustomStampBlenderCodeProperty].IsVisible = (bool)args.NewValue;
         InputPropertyMap[BrushOutputNode.StampBlendModeProperty].IsVisible = !(bool)args.NewValue;
         InputPropertyMap[BrushOutputNode.StampBlendModeProperty].IsVisible = !(bool)args.NewValue;
     }
     }
+
+    private void OnContentConnectionChanged(object? sender, EventArgs eventArgs)
+    {
+        var connection = InputPropertyMap[BrushOutputNode.ContentProperty].ConnectedOutput;
+        InputPropertyMap[BrushOutputNode.ContentTransformProperty].IsVisible = connection != null;
+    }
 }
 }

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/ToolsViewModel.cs

@@ -625,7 +625,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
             ViewModelMain.Current.DocumentManagerSubViewModel.ActiveDocument?.Operations.TryStopToolLinkedExecutor();
             ViewModelMain.Current.DocumentManagerSubViewModel.ActiveDocument?.Operations.TryStopToolLinkedExecutor();
         }
         }
 
 
-        ActiveTool.UseTool(canvasPos);
+        ActiveTool?.UseTool(canvasPos);
     }
     }
 
 
     public void ConvertedKeyDownInlet(FilteredKeyEventArgs args)
     public void ConvertedKeyDownInlet(FilteredKeyEventArgs args)

+ 16 - 0
src/PixiEditor/ViewModels/Tools/ToolSettings/Settings/Setting.cs

@@ -58,6 +58,8 @@ internal abstract class Setting : ObservableObject
     protected bool hasOverwrittenValue;
     protected bool hasOverwrittenValue;
     protected bool hasOverwrittenExposed;
     protected bool hasOverwrittenExposed;
 
 
+    private bool mergeChanges;
+
     protected Setting(string name)
     protected Setting(string name)
     {
     {
         Name = name;
         Name = name;
@@ -126,6 +128,20 @@ internal abstract class Setting : ObservableObject
         set => toolsetValues[currentToolset] = value;
         set => toolsetValues[currentToolset] = value;
     }
     }
 
 
+    public bool MergeChanges
+    {
+        get => mergeChanges;
+        set
+        {
+            if (SetProperty(ref mergeChanges, value) && !value)
+            {
+                MergeChangesEnded?.Invoke();
+            }
+        }
+    }
+
+    public event Action MergeChangesEnded;
+
     public abstract Type GetSettingType();
     public abstract Type GetSettingType();
 
 
     public void SetOverwriteValue(object value)
     public void SetOverwriteValue(object value)

+ 1 - 1
src/PixiEditor/Views/Blackboard/BlackboardView.axaml

@@ -33,7 +33,7 @@
                 Classes="pixi-icon"
                 Classes="pixi-icon"
                 Content="{DynamicResource icon-plus-square}"
                 Content="{DynamicResource icon-plus-square}"
                 Command="{Binding AddVariableCommand, RelativeSource={RelativeSource AncestorType=blackboard:BlackboardView, Mode=FindAncestor}}"
                 Command="{Binding AddVariableCommand, RelativeSource={RelativeSource AncestorType=blackboard:BlackboardView, Mode=FindAncestor}}"
-                CommandParameter="{Binding SelectedItem, ElementName=typeComboBox}"/>
+                CommandParameter="{Binding SelectedVariableOption, RelativeSource={RelativeSource AncestorType=blackboard:BlackboardView, Mode=FindAncestor}}"/>
 
 
         <Separator Background="{DynamicResource ThemeControlHighBrush}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,5"/>
         <Separator Background="{DynamicResource ThemeControlHighBrush}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,5"/>
 
 

+ 1 - 0
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml

@@ -277,6 +277,7 @@
             BackgroundBitmap="{Binding BackgroundBitmap, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
             BackgroundBitmap="{Binding BackgroundBitmap, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
             MaxBilinearSamplingSize="{Binding MaxBilinearSamplingSize, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
             MaxBilinearSamplingSize="{Binding MaxBilinearSamplingSize, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
             PointerPressed="Scene_OnContextMenuOpening"
             PointerPressed="Scene_OnContextMenuOpening"
+            ContextRequested="Scene_OnContextRequested"
             ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, RelativeSource={RelativeSource Self}}">
             ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, RelativeSource={RelativeSource Self}}">
             <rendering:Scene.ContextFlyout>
             <rendering:Scene.ContextFlyout>
                 <Flyout x:CompileBindings="True" x:DataType="subViewModels:ViewportWindowViewModel"
                 <Flyout x:CompileBindings="True" x:DataType="subViewModels:ViewportWindowViewModel"

+ 6 - 0
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml.cs

@@ -434,6 +434,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         Scene.ScaleChanged += OnScaleChanged;
         Scene.ScaleChanged += OnScaleChanged;
     }
     }
 
 
+
     private void OnScaleChanged(double newScale)
     private void OnScaleChanged(double newScale)
     {
     {
         SnappingViewModel.SnappingController.SnapDistance = SnappingController.DefaultSnapDistance / newScale;
         SnappingViewModel.SnappingController.SnapDistance = SnappingController.DefaultSnapDistance / newScale;
@@ -776,4 +777,9 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
             }
             }
         }
         }
     }
     }
+
+    private void Scene_OnContextRequested(object? sender, ContextRequestedEventArgs e)
+    {
+        e.Handled = true;
+    }
 }
 }

+ 1 - 0
src/PixiEditor/Views/Tools/ToolSettings/Settings/FloatSettingView.axaml

@@ -14,6 +14,7 @@
                        Value="{Binding Value, Mode=TwoWay}"
                        Value="{Binding Value, Mode=TwoWay}"
                        Min="{Binding Min}"
                        Min="{Binding Min}"
                        Max="{Binding Max}"
                        Max="{Binding Max}"
+                       DraggingGrabber="{Binding MergeChanges, Mode=OneWayToSource}"
                        Decimals="{Binding DecimalPlaces}"
                        Decimals="{Binding DecimalPlaces}"
                        Margin="0,0,0,0" 
                        Margin="0,0,0,0" 
                        FocusNext="False"
                        FocusNext="False"

+ 1 - 0
src/PixiEditor/Views/Tools/ToolSettings/Settings/PercentSettingView.axaml

@@ -15,6 +15,7 @@
                        Size="{Binding Value, Mode=TwoWay, Converter={converters:IntPercentConverter}}"
                        Size="{Binding Value, Mode=TwoWay, Converter={converters:IntPercentConverter}}"
                        MinSize="{Binding Min, Converter={converters:IntPercentConverter}}"
                        MinSize="{Binding Min, Converter={converters:IntPercentConverter}}"
                        MaxSize="{Binding Max, Converter={converters:IntPercentConverter}}"
                        MaxSize="{Binding Max, Converter={converters:IntPercentConverter}}"
+                       DraggingGrabber="{Binding MergeChanges, Mode=OneWayToSource}"
                        Unit="%"
                        Unit="%"
                        Margin="0,0,0,0" 
                        Margin="0,0,0,0" 
                         />
                         />

+ 1 - 0
src/PixiEditor/Views/Tools/ToolSettings/Settings/SizeSettingView.axaml

@@ -17,5 +17,6 @@
                      Decimals="{Binding DecimalPlaces}"
                      Decimals="{Binding DecimalPlaces}"
                      Unit="{Binding Unit}"
                      Unit="{Binding Unit}"
                      IsEnabled="{Binding IsEnabled}" FocusNext="False"
                      IsEnabled="{Binding IsEnabled}" FocusNext="False"
+                     DraggingGrabber="{Binding MergeChanges, Mode=OneWayToSource}"
                      Size="{Binding Value, Mode=TwoWay}"/>
                      Size="{Binding Value, Mode=TwoWay}"/>
 </UserControl>
 </UserControl>