2
0
CPKreuz 2 жил өмнө
parent
commit
4000ecc7e7

BIN
src/PixiEditor/Images/Guides/RectangleGuide.png


+ 257 - 0
src/PixiEditor/Models/DataHolders/Guides/RectangleGuide.cs

@@ -0,0 +1,257 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using PixiEditor.ViewModels.SubViewModels.Document;
+using PixiEditor.Views.UserControls.Guides;
+
+namespace PixiEditor.Models.DataHolders.Guides;
+
+internal class RectangleGuide : Guide
+{
+    private GrabbedPoint? grabbedPoint;
+
+    private double left;
+    private double top;
+    private double width;
+    private double height;
+    private Color color;
+
+    public double Left
+    {
+        get => left;
+        set
+        {
+            if (SetProperty(ref left, value))
+            {
+                InvalidateVisual();
+            }
+        }
+    }
+
+    public double Top
+    {
+        get => top;
+        set
+        {
+            if (SetProperty(ref top, value))
+            {
+                InvalidateVisual();
+            }
+        }
+    }
+
+    public double Width
+    {
+        get => width;
+        set
+        {
+            if (SetProperty(ref width, value))
+            {
+                InvalidateVisual();
+            }
+        }
+    }
+
+    public double Height
+    {
+        get => height;
+        set
+        {
+            if (SetProperty(ref height, value))
+            {
+                InvalidateVisual();
+            }
+        }
+    }
+
+    public Color Color
+    {
+        get => color;
+        set
+        {
+            if (SetProperty(ref color, value))
+            {
+                InvalidateVisual();
+            }
+        }
+    }
+
+    public override Control SettingsControl { get; }
+
+    public override string TypeNameKey => "RECTANGLE_GUIDE";
+
+    public RectangleGuide(DocumentViewModel document) : base(document)
+    {
+        Color = Colors.CadetBlue;
+        SettingsControl = new RectangleGuideSettings(this);
+    }
+
+    public override void Draw(DrawingContext context, GuideRenderer renderer)
+    {
+        bool skipDraw = false;
+
+        var mod = IsEditing ? 3 : 1;
+
+        var pen = new Pen(new SolidColorBrush(color), renderer.ScreenUnit * 1.5d * mod);
+        context.DrawRectangle(null, pen, new(Left, Top, Width, Height));
+    }
+
+    protected override void RendererAttached(GuideRenderer renderer)
+    {
+        renderer.MouseEnter += Renderer_MouseEnter;
+        renderer.MouseLeave += Renderer_MouseLeave;
+
+        renderer.MouseLeftButtonDown += Renderer_MouseLeftButtonDown;
+        renderer.MouseMove += Renderer_MouseMove;
+        renderer.MouseLeftButtonUp += Renderer_MouseLeftButtonUp;
+    }
+
+    private void Renderer_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
+    {
+        if (!IsEditing)
+        {
+            return;
+        }
+
+        var renderer = (GuideRenderer)sender;
+        var closestPoint = GetPoint(e.GetPosition(renderer));
+
+        renderer.Cursor = closestPoint switch
+        {
+            GrabbedPoint.TopLeft or GrabbedPoint.BottomRight => Cursors.SizeNWSE,
+            GrabbedPoint.TopRight or GrabbedPoint.BottomLeft => Cursors.SizeNESW
+        };
+    }
+
+    private void Renderer_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
+    {
+        var renderer = (GuideRenderer)sender;
+        renderer.Cursor = null;
+    }
+
+    private void Renderer_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
+    {
+        if (!IsEditing)
+        {
+            return;
+        }
+
+        e.Handled = true;
+        var renderer = (GuideRenderer)sender;
+        Mouse.Capture(renderer);
+        grabbedPoint = GetPoint(e.GetPosition(renderer));
+    }
+
+    private void Renderer_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
+    {
+        if (!IsEditing)
+        {
+            return;
+        }
+
+        e.Handled = true;
+        var renderer = (GuideRenderer)sender;
+        var mousePos = e.GetPosition(renderer);
+
+        if (grabbedPoint == null)
+        {
+            var closestPoint = GetPoint(mousePos);
+
+            renderer.Cursor = closestPoint switch
+            {
+                GrabbedPoint.TopLeft or GrabbedPoint.BottomRight => Cursors.SizeNWSE,
+                GrabbedPoint.TopRight or GrabbedPoint.BottomLeft => Cursors.SizeNESW
+            };
+            return;
+        }
+
+        var size = Document.SizeBindable;
+
+        switch (grabbedPoint)
+        {
+            case GrabbedPoint.TopLeft:
+                Left = RoundMod(mousePos.X);
+                Top = RoundMod(mousePos.Y);
+                break;
+            case GrabbedPoint.TopRight:
+                Width = RoundMod(size.X - mousePos.X - Left);
+                Top = RoundMod(mousePos.Y);
+                break;
+            case GrabbedPoint.BottomRight:
+                Width = RoundMod(size.X - mousePos.X - Left);
+                Height = RoundMod(size.Y - mousePos.Y - Top);
+                break;
+            case GrabbedPoint.BottomLeft:
+                Left = RoundMod(mousePos.X);
+                Height = RoundMod(size.Y - mousePos.Y - Top);
+                break;
+        }
+    }
+
+    private void Renderer_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
+    {
+        if (!IsEditing || grabbedPoint == null)
+        {
+            return;
+        }
+
+        grabbedPoint = null;
+        var renderer = (GuideRenderer)sender;
+        renderer.Cursor = null;
+        Mouse.Capture(null);
+    }
+
+    private GrabbedPoint GetPoint(Point mouse)
+    {
+        var size = Document.SizeBindable;
+
+        var topLeft = new Point(Left, Top);
+        var topRight = new Point(size.X - left, Top);
+        var bottomRight = new Point(size.X - left, size.Y - top);
+        var bottomLeft = new Point(Left, size.Y - top);
+
+        double value = double.PositiveInfinity;
+        int index = -1;
+        var points = new Point[] { topLeft, topRight, bottomRight, bottomLeft };
+
+        for (int i = 0; i < points.Length; i++)
+        {
+            var length = (points[i] - mouse).Length;
+            if (value > length)
+            {
+                value = length;
+                index = i;
+            }
+        }
+
+        return (GrabbedPoint)index;
+    }
+
+    private double RoundMod(double value)
+    {
+        if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
+        {
+            return Math.Round(value);
+        }
+        else if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
+        {
+            return value;
+        }
+
+        return Math.Round(value * 2, MidpointRounding.AwayFromZero) / 2;
+    }
+
+    enum GrabbedPoint
+    {
+        TopLeft,
+        TopRight,
+        BottomRight,
+        BottomLeft
+    }
+}

+ 2 - 0
src/PixiEditor/PixiEditor.csproj

@@ -194,6 +194,7 @@
 		<None Remove="Images\Globe.png" />
 		<None Remove="Images\Guides\HorizontalGuide.png" />
 		<None Remove="Images\Guides\LineGuide.png" />
+		<None Remove="Images\Guides\RectangleGuide.png" />
 		<None Remove="Images\Guides\VerticalGuide.png" />
 		<None Remove="Images\hard-drive.png" />
 		<None Remove="Images\Layer-add.png" />
@@ -294,6 +295,7 @@
 		<Resource Include="Images\Globe.png" />
 		<Resource Include="Images\Guides\HorizontalGuide.png" />
 		<Resource Include="Images\Guides\LineGuide.png" />
+		<Resource Include="Images\Guides\RectangleGuide.png" />
 		<Resource Include="Images\Guides\VerticalGuide.png" />
 		<Resource Include="Images\hard-drive.png" />
 		<Resource Include="Images\LanguageFlags\uk.png" />

+ 18 - 0
src/PixiEditor/ViewModels/SubViewModels/Main/GuidesViewModel.cs

@@ -62,4 +62,22 @@ internal class GuidesViewModel : SubViewModel<ViewModelMain>
         document.Guides.Add(guide);
         OpenGuideManager(^0);
     }
+
+    [Command.Basic("PixiEditor.Guides.AddRectangleGuide", "ADD_RECTANGLE_GUIDE", "ADD_RECTANGLE_GUIDE_DESCRIPTIVE", CanExecute = "PixiEditor.HasDocument", IconPath = "Guides/RectangleGuide.png")]
+    public void AddRectangleGuide()
+    {
+        var document = Owner.DocumentManagerSubViewModel.ActiveDocument;
+
+        var margin = document.SizeBindable * 0.25;
+        var guide = new RectangleGuide(document)
+        {
+            Left = margin.X,
+            Top = margin.Y,
+            Height = margin.X,
+            Width = margin.Y
+        };
+
+        document.Guides.Add(guide);
+        OpenGuideManager(^0);
+    }
 }

+ 6 - 0
src/PixiEditor/Views/Dialogs/Guides/GuidesManager.xaml

@@ -154,6 +154,12 @@
                                     views:Translator.TooltipKey="HORIZONTAL_GUIDE">
                                 <Image Source="/Images/Guides/HorizontalGuide.png"/>
                             </Button>
+                            <Button DockPanel.Dock="Left"
+                                    Command="{cmds:Command PixiEditor.Guides.AddRectangleGuide}"
+                                    views:Translator.TooltipKey="RECTANGLE_GUIDE">
+                                <Image Source="/Images/Guides/RectangleGuide.png"/>
+                            </Button>
+                            
                             <Button DockPanel.Dock="Right"  Tag="0,0,4.5,0"
                                     views:Translator.TooltipKey="SAVE_GUIDES">
                                 <Image Source="/Images/Guides/Book.png"/>

+ 3 - 4
src/PixiEditor/Views/UserControls/Guides/LineGuideSettings.xaml

@@ -2,13 +2,12 @@
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
-             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
-             xmlns:gModels="clr-namespace:PixiEditor.Models.DataHolders.Guides"
-             xmlns:local="clr-namespace:PixiEditor.Views.UserControls.Guides"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:views="clr-namespace:PixiEditor.Views"
              xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker"
              xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
-             xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:uc="clr-namespace:PixiEditor.Views.UserControls"
+             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
+             xmlns:uc="clr-namespace:PixiEditor.Views.UserControls"
              mc:Ignorable="d"
              Foreground="White"
              d:DesignHeight="450" d:DesignWidth="800">

+ 76 - 0
src/PixiEditor/Views/UserControls/Guides/RectangleGuideSettings.xaml

@@ -0,0 +1,76 @@
+<UserControl x:Class="PixiEditor.Views.UserControls.Guides.RectangleGuideSettings"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:views="clr-namespace:PixiEditor.Views"
+             xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker"
+             xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
+             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
+             xmlns:uc="clr-namespace:PixiEditor.Views.UserControls"
+             mc:Ignorable="d"
+             d:DesignHeight="450" d:DesignWidth="800"
+             Foreground="White">
+    <StackPanel Grid.IsSharedSizeScope="true">
+        <StackPanel.Resources>
+            <Style TargetType="Grid" x:Key="Child">
+                <Setter Property="Margin" Value="0,0,0,10"/>
+            </Style>
+        </StackPanel.Resources>
+
+        <Grid Style="{StaticResource Child}">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto" SharedSizeGroup="width" MinWidth="70"/>
+                <ColumnDefinition Width="Auto" MinWidth="200"/>
+            </Grid.ColumnDefinitions>
+            <TextBlock views:Translator.Key="NAME" Margin="0,0,10,0" TextAlignment="Right"/>
+            <TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
+                     Style="{StaticResource PlaceholderTextBox}" Tag="{Binding TypeNameKey}" Grid.Column="1">
+                <i:Interaction.Behaviors>
+                    <behaviors:GlobalShortcutFocusBehavior/>
+                    <behaviors:TextBoxFocusBehavior 
+                        SelectOnMouseClick="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}" 
+                        ConfirmOnEnter="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}"
+                        DeselectOnFocusLoss="True"/>
+                </i:Interaction.Behaviors>
+            </TextBox>
+        </Grid>
+
+        <Grid Style="{StaticResource Child}">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto" SharedSizeGroup="width"/>
+                <ColumnDefinition/>
+            </Grid.ColumnDefinitions>
+            <TextBlock views:Translator.Key="COLOR" Margin="0,0,10,0" TextAlignment="Right"/>
+            <colorpicker:PortableColorPicker Grid.Column="1" SelectedColor="{Binding Color, Mode=TwoWay}" Width="30" HorizontalAlignment="Left"/>
+        </Grid>
+
+        <Grid Style="{StaticResource Child}">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto" SharedSizeGroup="width"/>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="Auto" SharedSizeGroup="position"/>
+                <ColumnDefinition Width="Auto"/>
+            </Grid.ColumnDefinitions>
+            <TextBlock views:Translator.Key="LEFT" Margin="0,0,10,0" TextAlignment="Right"/>
+            <uc:NumberInput Margin="0,0,10,0" Grid.Column="1" Value="{Binding Left, Mode=TwoWay}" Width="50" HorizontalAlignment="Left"/>
+
+            <TextBlock Grid.Column="2" views:Translator.Key="TOP" Margin="0,0,10,0"/>
+            <uc:NumberInput Grid.Column="3" Value="{Binding Top, Mode=TwoWay}" Width="50" HorizontalAlignment="Left"/>
+        </Grid>
+
+        <Grid Style="{StaticResource Child}">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="Auto" SharedSizeGroup="width"/>
+                <ColumnDefinition Width="Auto"/>
+                <ColumnDefinition Width="Auto" SharedSizeGroup="position"/>
+                <ColumnDefinition Width="Auto"/>
+            </Grid.ColumnDefinitions>
+            <TextBlock views:Translator.Key="RIGHT" Margin="0,0,10,0" TextAlignment="Right"/>
+            <uc:NumberInput Margin="0,0,10,0" Grid.Column="1" Value="{Binding Right, Mode=TwoWay}" Width="50" HorizontalAlignment="Left"/>
+
+            <TextBlock Grid.Column="2" views:Translator.Key="BOTTOM" Margin="0,0,10,0"/>
+            <uc:NumberInput Grid.Column="3" Value="{Binding Bottom, Mode=TwoWay}" Width="50" HorizontalAlignment="Left"/>
+        </Grid>
+    </StackPanel>
+</UserControl>

+ 28 - 0
src/PixiEditor/Views/UserControls/Guides/RectangleGuideSettings.xaml.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using PixiEditor.Models.DataHolders.Guides;
+
+namespace PixiEditor.Views.UserControls.Guides;
+/// <summary>
+/// Interaction logic for RectangleGuideSettings.xaml
+/// </summary>
+public partial class RectangleGuideSettings : UserControl
+{
+    internal RectangleGuideSettings(RectangleGuide guide)
+    {
+        DataContext = guide;
+        InitializeComponent();
+    }
+}