Browse Source

Merge pull request #707 from PixiEditor/fixes/31.12.2024

Fixes/31.12.2024
Krzysztof Krysiński 7 months ago
parent
commit
8f0588be51

+ 17 - 0
src/PixiEditor.ChangeableDocument/Changes/Root/RotateImage_Change.cs

@@ -37,10 +37,22 @@ internal sealed class RotateImage_Change : Change
         {
         {
             membersToRotate = target.ExtractLayers(membersToRotate);
             membersToRotate = target.ExtractLayers(membersToRotate);
 
 
+            RectD? bounds = null;
             foreach (var layer in membersToRotate)
             foreach (var layer in membersToRotate)
             {
             {
                 if (!target.HasMember(layer)) return false;
                 if (!target.HasMember(layer)) return false;
+
+                if (frame != null)
+                {
+                    var layerBounds = target.FindMember(layer).GetTightBounds(frame.Value);
+                    if (layerBounds.HasValue)
+                    {
+                        bounds = bounds?.Union(layerBounds.Value) ?? layerBounds.Value;
+                    }
+                }
             }
             }
+            
+            if(frame != null && (bounds == null || bounds.Value.IsZeroArea)) return false;
         }
         }
 
 
         originalSize = target.Size;
         originalSize = target.Size;
@@ -70,6 +82,11 @@ internal sealed class RotateImage_Change : Change
                 bounds = preciseBounds.Value;
                 bounds = preciseBounds.Value;
             }
             }
         }
         }
+        
+        if (bounds.IsZeroArea)
+        {
+            return;
+        }
 
 
         int originalWidth = bounds.Width;
         int originalWidth = bounds.Width;
         int originalHeight = bounds.Height;
         int originalHeight = bounds.Height;

+ 10 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineExecutor.cs

@@ -268,6 +268,16 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
         document!.LineToolOverlayHandler.Hide();
         document!.LineToolOverlayHandler.Hide();
     }
     }
 
 
+    protected override void StartMode(ShapeToolMode mode)
+    {
+        base.StartMode(mode);
+        if (mode == ShapeToolMode.Transform)
+        {
+            document!.LineToolOverlayHandler.Hide();
+            document!.LineToolOverlayHandler.Show(lastStartPos, curPos, ShowApplyButton, AddToUndo);
+        }
+    }
+
     private void AddToUndo((VecD, VecD) newPos)
     private void AddToUndo((VecD, VecD) newPos)
     {
     {
         if (UseGlobalUndo)
         if (UseGlobalUndo)

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

@@ -171,8 +171,6 @@ internal class LayoutManager
             }
             }
         }
         }
 
 
-        ;
-
         return result;
         return result;
     }
     }
 
 

+ 22 - 13
src/PixiEditor/ViewModels/Tools/Tools/PenToolViewModel.cs

@@ -63,7 +63,10 @@ namespace PixiEditor.ViewModels.Tools.Tools
             {
             {
                 var toolbar = (PenToolbar)Toolbar;
                 var toolbar = (PenToolbar)Toolbar;
                 var setting = toolbar.Settings.FirstOrDefault(x => x.Name == nameof(toolbar.ToolSize));
                 var setting = toolbar.Settings.FirstOrDefault(x => x.Name == nameof(toolbar.ToolSize));
-                setting.Value = 1;
+                if (setting is SizeSettingViewModel sizeSetting)
+                {
+                    sizeSetting.Value = 1;
+                }
             }
             }
             
             
             if (!PixiEditorSettings.Tools.EnableSharedToolbar.Value)
             if (!PixiEditorSettings.Tools.EnableSharedToolbar.Value)
@@ -100,25 +103,31 @@ namespace PixiEditor.ViewModels.Tools.Tools
             }
             }
 
 
             var toolbar = (PenToolbar)Toolbar;
             var toolbar = (PenToolbar)Toolbar;
-            var setting = (SizeSettingViewModel)toolbar.Settings[0];
-            setting.Value = actualToolSize;
+            var setting = toolbar.Settings.FirstOrDefault(x => x.Name == nameof(toolbar.ToolSize));
+            if(setting is SizeSettingViewModel sizeSetting)
+            {
+                sizeSetting.Value = actualToolSize;
+            }
         }
         }
 
 
         private void PixelPerfectChanged()
         private void PixelPerfectChanged()
         {
         {
             var toolbar = (PenToolbar)Toolbar;
             var toolbar = (PenToolbar)Toolbar;
-            var setting = (SizeSettingViewModel)toolbar.Settings[0];
-
-            setting.IsEnabled = !PixelPerfectEnabled;
+            var setting = toolbar.Settings.FirstOrDefault(x => x.Name == nameof(toolbar.ToolSize));
 
 
-            if (PixelPerfectEnabled)
-            {
-                actualToolSize = ToolSize;
-                setting.Value = 1;
-            }
-            else
+            if (setting is SizeSettingViewModel sizeSettingViewModel)
             {
             {
-                setting.Value = actualToolSize;
+                sizeSettingViewModel.IsEnabled = !PixelPerfectEnabled;
+
+                if (PixelPerfectEnabled)
+                {
+                    actualToolSize = ToolSize;
+                    sizeSettingViewModel.Value = 1;
+                }
+                else
+                {
+                    sizeSettingViewModel.Value = actualToolSize;
+                }
             }
             }
         }
         }
         
         

+ 0 - 79
src/PixiEditor/Views/Dock/DocumentTemplate.axaml

@@ -37,85 +37,6 @@
         Channels="{Binding Channels, Mode=TwoWay}"
         Channels="{Binding Channels, Mode=TwoWay}"
         SnappingViewModel="{Binding ActiveDocument.SnappingViewModel, Source={viewModels1:MainVM DocumentManagerSVM}}"
         SnappingViewModel="{Binding ActiveDocument.SnappingViewModel, Source={viewModels1:MainVM DocumentManagerSVM}}"
         SnappingEnabled="{Binding ViewportSubViewModel.SnappingEnabled, Source={viewModels1:MainVM}, Mode=TwoWay}"
         SnappingEnabled="{Binding ViewportSubViewModel.SnappingEnabled, Source={viewModels1:MainVM}, Mode=TwoWay}"
-        ContextRequested="Viewport_OnContextMenuOpening"
         Document="{Binding Document}">
         Document="{Binding Document}">
-        <viewportControls:Viewport.ContextFlyout>
-            <Flyout x:CompileBindings="True" x:DataType="subViewModels:ViewportWindowViewModel" ShowMode="Transient">
-                <Border CornerRadius="{DynamicResource ControlCornerRadius}">
-                    <Grid>
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition
-                                Width="{Binding Document.Palette, Converter={converters:PaletteItemsToWidthConverter}}" />
-                            <ColumnDefinition />
-                        </Grid.ColumnDefinitions>
-                        <Border Grid.Column="1" BorderThickness="0 0 1 0">
-                            <ContextMenu Name="ContextMenu" BorderBrush="Transparent" BorderThickness="0" Margin="0" Padding="0">
-                                <MenuItem PointerReleased="MenuItem_OnClick"
-                                    ui:Translator.Key="SELECT_ALL"
-                                    xaml:Menu.Command="PixiEditor.Selection.SelectAll" />
-                                <MenuItem PointerReleased="MenuItem_OnClick"
-                                          ui:Translator.Key="DESELECT"
-                                          xaml:Menu.Command="PixiEditor.Selection.Clear" />
-                                <Separator />
-                                <MenuItem PointerReleased="MenuItem_OnClick"
-                                          ui:Translator.Key="CUT"
-                                          xaml:Menu.Command="PixiEditor.Clipboard.Cut" />
-                                <MenuItem PointerReleased="MenuItem_OnClick"
-                                          ui:Translator.Key="COPY"
-                                          xaml:Menu.Command="PixiEditor.Clipboard.Copy" />
-                                <MenuItem PointerReleased="MenuItem_OnClick"
-                                          ui:Translator.Key="PASTE"
-                                          xaml:Menu.Command="PixiEditor.Clipboard.Paste" />
-                                <Separator />
-                                <MenuItem  PointerReleased="MenuItem_OnClick"
-                                           ui:Translator.Key="FLIP_LAYERS_HORIZONTALLY"
-                                           xaml:Menu.Command="PixiEditor.Document.FlipLayersHorizontal" />
-                                <MenuItem  PointerReleased="MenuItem_OnClick"
-                                           ui:Translator.Key="FLIP_LAYERS_VERTICALLY"
-                                           xaml:Menu.Command="PixiEditor.Document.FlipLayersVertical" />
-                                <Separator />
-                                <MenuItem PointerReleased="MenuItem_OnClick"
-                                          ui:Translator.Key="ROT_LAYERS_90_D"
-                                          xaml:Menu.Command="PixiEditor.Document.Rotate90DegLayers" />
-                                <MenuItem PointerReleased="MenuItem_OnClick"
-                                          ui:Translator.Key="ROT_LAYERS_180_D"
-                                          xaml:Menu.Command="PixiEditor.Document.Rotate180DegLayers" />
-                                <MenuItem PointerReleased="MenuItem_OnClick"
-                                          ui:Translator.Key="ROT_LAYERS_-90_D"
-                                          xaml:Menu.Command="PixiEditor.Document.Rotate270DegLayers" />
-                            </ContextMenu>
-                        </Border>
-                        <ScrollViewer Margin="5" Grid.Column="0" HorizontalScrollBarVisibility="Disabled"
-                                      VerticalScrollBarVisibility="Auto" MaxHeight="{Binding #ContextMenu.Items,
-                                      Converter={converters:PaletteItemsHeightConverter}}">
-                            <ItemsControl ItemsSource="{Binding Document.Palette}">
-                                <ItemsControl.ItemsPanel>
-                                    <ItemsPanelTemplate>
-                                        <WrapPanel MaxWidth="{Binding Document.Palette, Converter={converters:PaletteItemsToWidthConverter}}" Orientation="Horizontal"
-                                                   HorizontalAlignment="Left" VerticalAlignment="Top" />
-                                    </ItemsPanelTemplate>
-                                </ItemsControl.ItemsPanel>
-                                <ItemsControl.ItemTemplate>
-                                    <DataTemplate x:DataType="palettes:PaletteColor">
-                                        <palettes1:PaletteColorControl Cursor="Hand" CornerRadius="0"
-                                                                       ui:Translator.TooltipKey="CLICK_SELECT_PRIMARY"
-                                                                       PointerReleased="InputElement_OnPointerReleased"
-                                                                       Width="22" Height="22" Color="{Binding}">
-                                            <Interaction.Behaviors>
-                                                <EventTriggerBehavior EventName="PointerPressed">
-                                                    <InvokeCommandAction
-                                                        Command="{xaml:Command PixiEditor.Colors.SelectColor, UseProvided=True}"
-                                                        CommandParameter="{Binding}" />
-                                                </EventTriggerBehavior>
-                                            </Interaction.Behaviors>
-                                        </palettes1:PaletteColorControl>
-                                    </DataTemplate>
-                                </ItemsControl.ItemTemplate>
-                            </ItemsControl>
-                        </ScrollViewer>
-                    </Grid>
-                </Border>
-            </Flyout>
-        </viewportControls:Viewport.ContextFlyout>
     </viewportControls:Viewport>
     </viewportControls:Viewport>
 </UserControl>
 </UserControl>

+ 1 - 34
src/PixiEditor/Views/Dock/DocumentTemplate.axaml.cs

@@ -1,5 +1,6 @@
 using System.ComponentModel;
 using System.ComponentModel;
 using Avalonia.Controls;
 using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.Interactivity;
 using PixiEditor.Models.Preferences;
 using PixiEditor.Models.Preferences;
@@ -17,39 +18,5 @@ public partial class DocumentTemplate : UserControl
     {
     {
         InitializeComponent();
         InitializeComponent();
     }
     }
-
-    private void Viewport_OnContextMenuOpening(object? sender, ContextRequestedEventArgs e)
-    {
-        ViewportWindowViewModel vm = ((ViewportWindowViewModel)DataContext);
-        var tools = vm.Owner.Owner.ToolsSubViewModel;
-
-        var superSpecialBrightnessTool = tools.RightClickMode == RightClickMode.SecondaryColor && tools.ActiveTool is BrightnessToolViewModel;
-        var superSpecialColorPicker = tools.RightClickMode == RightClickMode.Erase && tools.ActiveTool is ColorPickerToolViewModel;
-
-        if (superSpecialBrightnessTool || superSpecialColorPicker)
-        {
-            e.Handled = true;
-            return;
-        }
-
-        var useContextMenu = vm.Owner.Owner.ToolsSubViewModel.RightClickMode == RightClickMode.ContextMenu;
-        var usesErase = tools.RightClickMode == RightClickMode.Erase && tools.ActiveTool.IsErasable;
-        var usesSecondaryColor = tools.RightClickMode == RightClickMode.SecondaryColor && tools.ActiveTool.UsesColor;
-
-        if (!useContextMenu && (usesErase || usesSecondaryColor))
-        {
-            e.Handled = true;
-        }
-    }
-
-    private void InputElement_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
-    {
-        Viewport?.ContextFlyout?.Hide();
-    }
-
-    private void MenuItem_OnClick(object? sender, PointerReleasedEventArgs e)
-    {
-        Viewport?.ContextFlyout?.Hide();
-    }
 }
 }
 
 

+ 1 - 0
src/PixiEditor/Views/Input/EditableTextBlock.axaml

@@ -19,6 +19,7 @@
                  Padding="0" Margin="-1"
                  Padding="0" Margin="-1"
                  KeyDown="TextBox_KeyDown"
                  KeyDown="TextBox_KeyDown"
                  IsVisible="{Binding !TextBlockVisibility, ElementName=etb}"
                  IsVisible="{Binding !TextBlockVisibility, ElementName=etb}"
+                 RenderTransform="scale(0.95)"
                  Name="textBox">
                  Name="textBox">
             <Interaction.Behaviors>
             <Interaction.Behaviors>
                 <behaviours:GlobalShortcutFocusBehavior/>
                 <behaviours:GlobalShortcutFocusBehavior/>

+ 11 - 0
src/PixiEditor/Views/Layers/FolderControl.axaml.cs

@@ -49,6 +49,9 @@ internal partial class FolderControl : UserControl
         Loaded += OnLoaded;
         Loaded += OnLoaded;
         Unloaded += OnUnloaded;
         Unloaded += OnUnloaded;
         
         
+        AddHandler(DragDrop.DragEnterEvent, FolderControl_DragEnter);
+        AddHandler(DragDrop.DragLeaveEvent, FolderControl_DragLeave);
+        
         TopDropGrid.AddHandler(DragDrop.DragEnterEvent, Grid_DragEnter);
         TopDropGrid.AddHandler(DragDrop.DragEnterEvent, Grid_DragEnter);
         TopDropGrid.AddHandler(DragDrop.DragLeaveEvent, Grid_DragLeave);
         TopDropGrid.AddHandler(DragDrop.DragLeaveEvent, Grid_DragLeave);
         TopDropGrid.AddHandler(DragDrop.DropEvent, Grid_Drop_Top);
         TopDropGrid.AddHandler(DragDrop.DropEvent, Grid_Drop_Top);
@@ -60,6 +63,10 @@ internal partial class FolderControl : UserControl
         middleDropGrid.AddHandler(DragDrop.DragEnterEvent, Grid_CenterEnter);
         middleDropGrid.AddHandler(DragDrop.DragEnterEvent, Grid_CenterEnter);
         middleDropGrid.AddHandler(DragDrop.DragLeaveEvent, Grid_CenterLeave);
         middleDropGrid.AddHandler(DragDrop.DragLeaveEvent, Grid_CenterLeave);
         middleDropGrid.AddHandler(DragDrop.DropEvent, Grid_Drop_Center);
         middleDropGrid.AddHandler(DragDrop.DropEvent, Grid_Drop_Center);
+        
+        TopDropGrid.IsVisible = false;
+        middleDropGrid.IsVisible = false;
+        BottomDropGrid.IsVisible = false;
     }
     }
 
 
     private void OnUnloaded(object? sender, RoutedEventArgs e)
     private void OnUnloaded(object? sender, RoutedEventArgs e)
@@ -122,12 +129,16 @@ internal partial class FolderControl : UserControl
 
 
     private void FolderControl_DragEnter(object sender, DragEventArgs e)
     private void FolderControl_DragEnter(object sender, DragEventArgs e)
     {
     {
+        TopDropGrid.IsVisible = true;
         middleDropGrid.IsVisible = true;
         middleDropGrid.IsVisible = true;
+        BottomDropGrid.IsVisible = true;
     }
     }
 
 
     private void FolderControl_DragLeave(object sender, DragEventArgs e)
     private void FolderControl_DragLeave(object sender, DragEventArgs e)
     {
     {
+        TopDropGrid.IsVisible = false;
         middleDropGrid.IsVisible = false;
         middleDropGrid.IsVisible = false;
+        BottomDropGrid.IsVisible = false;
     }
     }
 
 
     private void RenameMenuItem_Click(object sender, RoutedEventArgs e)
     private void RenameMenuItem_Click(object sender, RoutedEventArgs e)

+ 99 - 13
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml

@@ -5,23 +5,17 @@
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-    xmlns:sys="clr-namespace:System;assembly=System.Runtime"
     xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
     xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
     xmlns:xaml="clr-namespace:PixiEditor.Models.Commands.XAML"
     xmlns:xaml="clr-namespace:PixiEditor.Models.Commands.XAML"
-    xmlns:zoombox="clr-namespace:PixiEditor.Zoombox;assembly=PixiEditor.Zoombox"
-    xmlns:brushShapeOverlay="clr-namespace:PixiEditor.Views.Overlays.BrushShapeOverlay"
     xmlns:viewModels="clr-namespace:PixiEditor.ViewModels"
     xmlns:viewModels="clr-namespace:PixiEditor.ViewModels"
-    xmlns:symmetryOverlay="clr-namespace:PixiEditor.Views.Overlays.SymmetryOverlay"
-    xmlns:lineToolOverlay="clr-namespace:PixiEditor.Views.Overlays.LineToolOverlay"
-    xmlns:transformOverlay="clr-namespace:PixiEditor.Views.Overlays.TransformOverlay"
     xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
     xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
-    xmlns:ui1="clr-namespace:PixiEditor.Helpers.UI"
-    xmlns:visuals="clr-namespace:PixiEditor.Views.Visuals"
     xmlns:viewportControls="clr-namespace:PixiEditor.Views.Main.ViewportControls"
     xmlns:viewportControls="clr-namespace:PixiEditor.Views.Main.ViewportControls"
     xmlns:overlays="clr-namespace:PixiEditor.Views.Overlays"
     xmlns:overlays="clr-namespace:PixiEditor.Views.Overlays"
-    xmlns:selectionOverlay="clr-namespace:PixiEditor.Views.Overlays.SelectionOverlay"
     xmlns:rendering="clr-namespace:PixiEditor.Views.Rendering"
     xmlns:rendering="clr-namespace:PixiEditor.Views.Rendering"
     xmlns:tools="clr-namespace:PixiEditor.Views.Main.Tools"
     xmlns:tools="clr-namespace:PixiEditor.Views.Main.Tools"
+    xmlns:subViewModels="clr-namespace:PixiEditor.ViewModels.SubViewModels"
+    xmlns:palettes="clr-namespace:PixiEditor.Views.Palettes"
+    xmlns:palettes1="clr-namespace:PixiEditor.Extensions.CommonApi.Palettes;assembly=PixiEditor.Extensions.CommonApi"
     mc:Ignorable="d"
     mc:Ignorable="d"
     x:Name="vpUc"
     x:Name="vpUc"
     d:DesignHeight="450"
     d:DesignHeight="450"
@@ -143,7 +137,7 @@
         </overlays:TogglableFlyout>
         </overlays:TogglableFlyout>
         <Grid ZIndex="100" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10">
         <Grid ZIndex="100" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10">
             <Grid.RowDefinitions>
             <Grid.RowDefinitions>
-                <RowDefinition MinHeight="40" MaxHeight="120"/>
+                <RowDefinition MinHeight="40" MaxHeight="120" />
                 <RowDefinition Height="35" />
                 <RowDefinition Height="35" />
                 <RowDefinition Height="*" />
                 <RowDefinition Height="*" />
             </Grid.RowDefinitions>
             </Grid.RowDefinitions>
@@ -163,8 +157,8 @@
                     VerticalAlignment="Center" />
                     VerticalAlignment="Center" />
             </Border>
             </Border>
             <tools:ToolsPicker Grid.Row="2" IsHitTestVisible="True"
             <tools:ToolsPicker Grid.Row="2" IsHitTestVisible="True"
-                HorizontalAlignment="Left"
-                ToolSet="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.ActiveToolSet, Mode=TwoWay}"
+                               HorizontalAlignment="Left"
+                               ToolSet="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.ActiveToolSet, Mode=TwoWay}"
                                ToolSets="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.AllToolSets, Mode=OneWay}"
                                ToolSets="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.AllToolSets, Mode=OneWay}"
                                SwitchToolSetCommand="{xaml:Command Name=PixiEditor.Tools.SwitchToolSet, UseProvided=True}" />
                                SwitchToolSetCommand="{xaml:Command Name=PixiEditor.Tools.SwitchToolSet, UseProvided=True}" />
         </Grid>
         </Grid>
@@ -186,7 +180,99 @@
             FadeOut="{Binding Source={viewModels:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}"
             FadeOut="{Binding Source={viewModels:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}"
             DefaultCursor="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.ToolCursor, Mode=OneWay}"
             DefaultCursor="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.ToolCursor, Mode=OneWay}"
             CheckerImagePath="/Images/CheckerTile.png"
             CheckerImagePath="/Images/CheckerTile.png"
-            ui:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, RelativeSource={RelativeSource Self}}" />
+            PointerPressed="Scene_OnContextMenuOpening"
+            ui:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, RelativeSource={RelativeSource Self}}">
+            <rendering:Scene.ContextFlyout>
+                <Flyout x:CompileBindings="True" x:DataType="subViewModels:ViewportWindowViewModel"
+                        Placement="Pointer"
+                        ShowMode="Transient">
+                    <Border Cursor="Arrow" CornerRadius="{DynamicResource ControlCornerRadius}">
+                        <Grid>
+                            <Grid.ColumnDefinitions>
+                                <ColumnDefinition
+                                    Width="Auto" />
+                                <ColumnDefinition />
+                            </Grid.ColumnDefinitions>
+                            <Border Grid.Column="1" BorderThickness="0 0 1 0">
+                                <ContextMenu Name="ContextMenu" BorderBrush="Transparent" BorderThickness="0"
+                                             Margin="0"
+                                             Padding="0">
+                                    <MenuItem PointerReleased="MenuItem_OnClick"
+                                              ui:Translator.Key="SELECT_ALL"
+                                              xaml:Menu.Command="PixiEditor.Selection.SelectAll" />
+                                    <MenuItem PointerReleased="MenuItem_OnClick"
+                                              ui:Translator.Key="DESELECT"
+                                              xaml:Menu.Command="PixiEditor.Selection.Clear" />
+                                    <Separator />
+                                    <MenuItem PointerReleased="MenuItem_OnClick"
+                                              ui:Translator.Key="CUT"
+                                              xaml:Menu.Command="PixiEditor.Clipboard.Cut" />
+                                    <MenuItem PointerReleased="MenuItem_OnClick"
+                                              ui:Translator.Key="COPY"
+                                              xaml:Menu.Command="PixiEditor.Clipboard.Copy" />
+                                    <MenuItem PointerReleased="MenuItem_OnClick"
+                                              ui:Translator.Key="COPY_VISIBLE"
+                                              xaml:Menu.Command="PixiEditor.Clipboard.CopyVisible" />
+                                    <MenuItem PointerReleased="MenuItem_OnClick"
+                                              ui:Translator.Key="PASTE"
+                                              xaml:Menu.Command="PixiEditor.Clipboard.Paste" />
+                                    <Separator />
+                                    <MenuItem PointerReleased="MenuItem_OnClick"
+                                              ui:Translator.Key="FLIP_LAYERS_HORIZONTALLY"
+                                              xaml:Menu.Command="PixiEditor.Document.FlipLayersHorizontal" />
+                                    <MenuItem PointerReleased="MenuItem_OnClick"
+                                              ui:Translator.Key="FLIP_LAYERS_VERTICALLY"
+                                              xaml:Menu.Command="PixiEditor.Document.FlipLayersVertical" />
+                                    <Separator />
+                                    <MenuItem PointerReleased="MenuItem_OnClick"
+                                              ui:Translator.Key="ROT_LAYERS_90_D"
+                                              xaml:Menu.Command="PixiEditor.Document.Rotate90DegLayers" />
+                                    <MenuItem PointerReleased="MenuItem_OnClick"
+                                              ui:Translator.Key="ROT_LAYERS_180_D"
+                                              xaml:Menu.Command="PixiEditor.Document.Rotate180DegLayers" />
+                                    <MenuItem PointerReleased="MenuItem_OnClick"
+                                              ui:Translator.Key="ROT_LAYERS_-90_D"
+                                              xaml:Menu.Command="PixiEditor.Document.Rotate270DegLayers" />
+                                </ContextMenu>
+                            </Border>
+                            <ScrollViewer Margin="0" Grid.Column="0" HorizontalScrollBarVisibility="Disabled"
+                                          VerticalScrollBarVisibility="Auto"
+                                          IsVisible="{Binding !!Document.Palette.Count}"
+                                          Width="{Binding Document.Palette, Converter={converters:PaletteItemsToWidthConverter}}"
+                                          MaxHeight="{Binding #ContextMenu.Items,
+                                                  Converter={converters:PaletteItemsHeightConverter}}">
+                                <ItemsControl ItemsSource="{Binding Document.Palette}">
+                                    <ItemsControl.ItemsPanel>
+                                        <ItemsPanelTemplate>
+                                            <WrapPanel
+                                                MaxWidth="{Binding Document.Palette, Converter={converters:PaletteItemsToWidthConverter}}"
+                                                Orientation="Horizontal"
+                                                HorizontalAlignment="Center" VerticalAlignment="Top" />
+                                        </ItemsPanelTemplate>
+                                    </ItemsControl.ItemsPanel>
+                                    <ItemsControl.ItemTemplate>
+                                        <DataTemplate x:DataType="palettes1:PaletteColor">
+                                            <palettes:PaletteColorControl Cursor="Hand" CornerRadius="0"
+                                                                          ui:Translator.TooltipKey="CLICK_SELECT_PRIMARY"
+                                                                          PointerReleased="InputElement_OnPointerReleased"
+                                                                          Width="22" Height="22" Color="{Binding}">
+                                                <Interaction.Behaviors>
+                                                    <EventTriggerBehavior EventName="PointerPressed">
+                                                        <InvokeCommandAction
+                                                            Command="{xaml:Command PixiEditor.Colors.SelectColor, UseProvided=True}"
+                                                            CommandParameter="{Binding}" />
+                                                    </EventTriggerBehavior>
+                                                </Interaction.Behaviors>
+                                            </palettes:PaletteColorControl>
+                                        </DataTemplate>
+                                    </ItemsControl.ItemTemplate>
+                                </ItemsControl>
+                            </ScrollViewer>
+                        </Grid>
+                    </Border>
+                </Flyout>
+            </rendering:Scene.ContextFlyout>
+        </rendering:Scene>
         <Button
         <Button
             ZIndex="99999"
             ZIndex="99999"
             DockPanel.Dock="Bottom"
             DockPanel.Dock="Bottom"

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

@@ -18,7 +18,10 @@ using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using Drawie.Numerics;
 using Drawie.Numerics;
+using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.ViewModels.Document;
+using PixiEditor.ViewModels.SubViewModels;
+using PixiEditor.ViewModels.Tools.Tools;
 using PixiEditor.Views.Overlays;
 using PixiEditor.Views.Overlays;
 using PixiEditor.Views.Rendering;
 using PixiEditor.Views.Rendering;
 using PixiEditor.Zoombox;
 using PixiEditor.Zoombox;
@@ -93,12 +96,13 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     public static readonly StyledProperty<bool> IsOverCanvasProperty = AvaloniaProperty.Register<Viewport, bool>(
     public static readonly StyledProperty<bool> IsOverCanvasProperty = AvaloniaProperty.Register<Viewport, bool>(
         nameof(IsOverCanvas));
         nameof(IsOverCanvas));
 
 
-    public static readonly StyledProperty<SnappingViewModel> SnappingViewModelProperty = 
+    public static readonly StyledProperty<SnappingViewModel> SnappingViewModelProperty =
         AvaloniaProperty.Register<Viewport, SnappingViewModel>(
         AvaloniaProperty.Register<Viewport, SnappingViewModel>(
-        nameof(SnappingViewModel));
+            nameof(SnappingViewModel));
 
 
-    public static readonly StyledProperty<bool> HighResPreviewProperty = 
+    public static readonly StyledProperty<bool> HighResPreviewProperty =
         AvaloniaProperty.Register<Viewport, bool>(nameof(HighResPreview), true);
         AvaloniaProperty.Register<Viewport, bool>(nameof(HighResPreview), true);
+
     public SnappingViewModel SnappingViewModel
     public SnappingViewModel SnappingViewModel
     {
     {
         get => GetValue(SnappingViewModelProperty);
         get => GetValue(SnappingViewModelProperty);
@@ -290,7 +294,9 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
 
     private MouseUpdateController? mouseUpdateController;
     private MouseUpdateController? mouseUpdateController;
     private ViewportOverlays builtInOverlays = new();
     private ViewportOverlays builtInOverlays = new();
-    public static readonly StyledProperty<bool> SnappingEnabledProperty = AvaloniaProperty.Register<Viewport, bool>("SnappingEnabled");
+
+    public static readonly StyledProperty<bool> SnappingEnabledProperty =
+        AvaloniaProperty.Register<Viewport, bool>("SnappingEnabled");
 
 
     static Viewport()
     static Viewport()
     {
     {
@@ -314,7 +320,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         //TODO: It's weird that I had to do it this way, right click didn't raise Image_MouseUp otherwise.
         //TODO: It's weird that I had to do it this way, right click didn't raise Image_MouseUp otherwise.
         viewportGrid.AddHandler(PointerReleasedEvent, Image_MouseUp, RoutingStrategies.Tunnel);
         viewportGrid.AddHandler(PointerReleasedEvent, Image_MouseUp, RoutingStrategies.Tunnel);
         viewportGrid.AddHandler(PointerPressedEvent, Image_MouseDown, RoutingStrategies.Bubble);
         viewportGrid.AddHandler(PointerPressedEvent, Image_MouseDown, RoutingStrategies.Bubble);
-        
+
         Scene.PointerExited += (sender, args) => IsOverCanvas = false;
         Scene.PointerExited += (sender, args) => IsOverCanvas = false;
         Scene.PointerEntered += (sender, args) => IsOverCanvas = true;
         Scene.PointerEntered += (sender, args) => IsOverCanvas = true;
         Scene.ScaleChanged += OnScaleChanged;
         Scene.ScaleChanged += OnScaleChanged;
@@ -496,10 +502,50 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         if (isMiddle && MiddleMouseClickedCommand.CanExecute(null))
         if (isMiddle && MiddleMouseClickedCommand.CanExecute(null))
             MiddleMouseClickedCommand.Execute(null);
             MiddleMouseClickedCommand.Execute(null);
     }
     }
-    
+
     private static void OnHighResPreviewChanged(AvaloniaPropertyChangedEventArgs<bool> e)
     private static void OnHighResPreviewChanged(AvaloniaPropertyChangedEventArgs<bool> e)
     {
     {
         Viewport? viewport = (Viewport)e.Sender;
         Viewport? viewport = (Viewport)e.Sender;
         viewport.ForceRefreshFinalImage();
         viewport.ForceRefreshFinalImage();
     }
     }
+
+    private void MenuItem_OnClick(object? sender, PointerReleasedEventArgs e)
+    {
+        Scene?.ContextFlyout?.Hide();
+    }
+
+    private void InputElement_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
+    {
+        Scene?.ContextFlyout?.Hide();
+    }
+
+    private void Scene_OnContextMenuOpening(object? sender, PointerPressedEventArgs e)
+    {
+        if(e.GetMouseButton(this) != MouseButton.Right) return;
+        
+        ViewportWindowViewModel vm = ((ViewportWindowViewModel)DataContext);
+        var tools = vm.Owner.Owner.ToolsSubViewModel;
+
+        var superSpecialBrightnessTool = tools.RightClickMode == RightClickMode.SecondaryColor &&
+                                         tools.ActiveTool is BrightnessToolViewModel;
+        var superSpecialColorPicker =
+            tools.RightClickMode == RightClickMode.Erase && tools.ActiveTool is ColorPickerToolViewModel;
+
+        if (superSpecialBrightnessTool || superSpecialColorPicker)
+        {
+            return;
+        }
+
+        var useContextMenu = vm.Owner.Owner.ToolsSubViewModel.RightClickMode == RightClickMode.ContextMenu;
+        var usesErase = tools.RightClickMode == RightClickMode.Erase && tools.ActiveTool.IsErasable;
+        var usesSecondaryColor = tools.RightClickMode == RightClickMode.SecondaryColor && tools.ActiveTool.UsesColor;
+
+        if (!useContextMenu && (usesErase || usesSecondaryColor))
+        {
+            return;
+        }
+
+        Scene?.ContextFlyout?.ShowAt(Scene);
+        e.Handled = true;
+    }
 }
 }