ソースを参照

edit align shape

Johann 6 年 前
コミット
f5cd229348

+ 36 - 0
lazpaint/lazpaintmainform.lfm

@@ -8674,6 +8674,42 @@ object FMain: TFMain
       OnExecute = EditMoveToBackExecute
       OnUpdate = EditMoveToBackUpdate
     end
+    object EditShapeAlignLeft: TAction
+      Category = 'Edit'
+      Hint = 'Align shape left'
+      OnExecute = EditShapeAlignLeftExecute
+      OnUpdate = EditShapeAlignLeftUpdate
+    end
+    object EditShapeCenterHorizontally: TAction
+      Category = 'Edit'
+      Hint = 'Center shape horizontally'
+      OnExecute = EditShapeCenterHorizontallyExecute
+      OnUpdate = EditShapeCenterHorizontallyUpdate
+    end
+    object EditShapeAlignRight: TAction
+      Category = 'Edit'
+      Hint = 'Align shape right'
+      OnExecute = EditShapeAlignRightExecute
+      OnUpdate = EditShapeAlignRightUpdate
+    end
+    object EditShapeAlignTop: TAction
+      Category = 'Edit'
+      Hint = 'Align shape to the top'
+      OnExecute = EditShapeAlignTopExecute
+      OnUpdate = EditShapeAlignTopUpdate
+    end
+    object EditShapeCenterVertically: TAction
+      Category = 'Edit'
+      Hint = 'Center shape vertically'
+      OnExecute = EditShapeCenterVerticallyExecute
+      OnUpdate = EditShapeCenterVerticallyUpdate
+    end
+    object EditShapeAlignBottom: TAction
+      Category = 'Edit'
+      Hint = 'Align shape to the bottom'
+      OnExecute = EditShapeAlignBottomExecute
+      OnUpdate = EditShapeAlignBottomUpdate
+    end
   end
   object ColorDialog1: TColorDialog
     Title = 'Choose color'

+ 78 - 0
lazpaint/lazpaintmainform.pas

@@ -28,6 +28,12 @@ type
   { TFMain }
 
   TFMain = class(TForm)
+    EditShapeAlignBottom: TAction;
+    EditShapeCenterVertically: TAction;
+    EditShapeAlignTop: TAction;
+    EditShapeAlignRight: TAction;
+    EditShapeCenterHorizontally: TAction;
+    EditShapeAlignLeft: TAction;
     ComboBox_PenStyle: TBCComboBox;
     EditMoveToBack: TAction;
     EditMoveDown: TAction;
@@ -445,6 +451,18 @@ type
     procedure EditMoveUpUpdate(Sender: TObject);
     procedure EditPasteExecute(Sender: TObject);
     procedure EditSelectionUpdate(Sender: TObject);
+    procedure EditShapeAlignBottomExecute(Sender: TObject);
+    procedure EditShapeAlignBottomUpdate(Sender: TObject);
+    procedure EditShapeAlignLeftExecute(Sender: TObject);
+    procedure EditShapeAlignLeftUpdate(Sender: TObject);
+    procedure EditShapeAlignRightExecute(Sender: TObject);
+    procedure EditShapeAlignRightUpdate(Sender: TObject);
+    procedure EditShapeAlignTopExecute(Sender: TObject);
+    procedure EditShapeAlignTopUpdate(Sender: TObject);
+    procedure EditShapeCenterHorizontallyExecute(Sender: TObject);
+    procedure EditShapeCenterHorizontallyUpdate(Sender: TObject);
+    procedure EditShapeCenterVerticallyExecute(Sender: TObject);
+    procedure EditShapeCenterVerticallyUpdate(Sender: TObject);
     procedure FileChooseEntryExecute(Sender: TObject);
     procedure FileChooseEntryUpdate(Sender: TObject);
     procedure FileImport3DUpdate(Sender: TObject);
@@ -2902,6 +2920,66 @@ begin
   EditSelection.Enabled := not Scripting.Recording;
 end;
 
+procedure TFMain.EditShapeAlignBottomExecute(Sender: TObject);
+begin
+  ToolManager.ToolCommand(tcAlignBottom);
+end;
+
+procedure TFMain.EditShapeAlignBottomUpdate(Sender: TObject);
+begin
+  EditShapeAlignBottom.Enabled := ToolManager.ToolProvideCommand(tcAlignBottom);
+end;
+
+procedure TFMain.EditShapeAlignLeftExecute(Sender: TObject);
+begin
+  ToolManager.ToolCommand(tcAlignLeft);
+end;
+
+procedure TFMain.EditShapeAlignLeftUpdate(Sender: TObject);
+begin
+  EditShapeAlignLeft.Enabled := ToolManager.ToolProvideCommand(tcAlignLeft);
+end;
+
+procedure TFMain.EditShapeAlignRightExecute(Sender: TObject);
+begin
+  ToolManager.ToolCommand(tcAlignRight);
+end;
+
+procedure TFMain.EditShapeAlignRightUpdate(Sender: TObject);
+begin
+  EditShapeAlignRight.Enabled := ToolManager.ToolProvideCommand(tcAlignRight);
+end;
+
+procedure TFMain.EditShapeAlignTopExecute(Sender: TObject);
+begin
+  ToolManager.ToolCommand(tcAlignTop);
+end;
+
+procedure TFMain.EditShapeAlignTopUpdate(Sender: TObject);
+begin
+  EditShapeAlignTop.Enabled := ToolManager.ToolProvideCommand(tcAlignTop);
+end;
+
+procedure TFMain.EditShapeCenterHorizontallyExecute(Sender: TObject);
+begin
+  ToolManager.ToolCommand(tcCenterHorizontally);
+end;
+
+procedure TFMain.EditShapeCenterHorizontallyUpdate(Sender: TObject);
+begin
+  EditShapeCenterHorizontally.Enabled := ToolManager.ToolProvideCommand(tcCenterHorizontally);
+end;
+
+procedure TFMain.EditShapeCenterVerticallyExecute(Sender: TObject);
+begin
+  ToolManager.ToolCommand(tcCenterVertically);
+end;
+
+procedure TFMain.EditShapeCenterVerticallyUpdate(Sender: TObject);
+begin
+  EditShapeCenterVertically.Enabled := ToolManager.ToolProvideCommand(tcCenterVertically);
+end;
+
 procedure TFMain.FileChooseEntryExecute(Sender: TObject);
 var
   openParams: TVariableSet;

+ 28 - 0
lazpaint/release/bin/i18n/lazpaint.ar.po

@@ -193,6 +193,30 @@ msgstr "تحرير التحديد"
 msgid "Selection fit"
 msgstr "تحديد ملائم"
 
+#: tfmain.editshapealignbottom.hint
+msgid "Align shape to the bottom"
+msgstr ""
+
+#: tfmain.editshapealignleft.hint
+msgid "Align shape left"
+msgstr ""
+
+#: tfmain.editshapealignright.hint
+msgid "Align shape right"
+msgstr ""
+
+#: tfmain.editshapealigntop.hint
+msgid "Align shape to the top"
+msgstr ""
+
+#: tfmain.editshapecenterhorizontally.hint
+msgid "Center shape horizontally"
+msgstr ""
+
+#: tfmain.editshapecentervertically.hint
+msgid "Center shape vertically"
+msgstr ""
+
 #: tfmain.editundo.caption
 msgid "Undo"
 msgstr "تراجع"
@@ -1517,6 +1541,10 @@ msgstr "العمل في تقدم"
 msgid "Add files to the image processing list"
 msgstr "إضافة الملفات الى قائمة الصور المعالجة"
 
+#: uresourcestrings.rsalignshape
+msgid "Align shape"
+msgstr ""
+
 #: uresourcestrings.rsallapplications
 msgid "all"
 msgstr "الكل"

+ 28 - 0
lazpaint/release/bin/i18n/lazpaint.cs.po

@@ -189,6 +189,30 @@ msgstr "Upravit výběr..."
 msgid "Selection fit"
 msgstr "Celý výběr"
 
+#: tfmain.editshapealignbottom.hint
+msgid "Align shape to the bottom"
+msgstr ""
+
+#: tfmain.editshapealignleft.hint
+msgid "Align shape left"
+msgstr ""
+
+#: tfmain.editshapealignright.hint
+msgid "Align shape right"
+msgstr ""
+
+#: tfmain.editshapealigntop.hint
+msgid "Align shape to the top"
+msgstr ""
+
+#: tfmain.editshapecenterhorizontally.hint
+msgid "Center shape horizontally"
+msgstr ""
+
+#: tfmain.editshapecentervertically.hint
+msgid "Center shape vertically"
+msgstr ""
+
 #: tfmain.editundo.caption
 msgid "Undo"
 msgstr "Vrátit zpět"
@@ -1514,6 +1538,10 @@ msgstr "Akce v běhu"
 msgid "Add files to the image processing list"
 msgstr "Přidat soubory do seznamu obrázků ke zpracování"
 
+#: uresourcestrings.rsalignshape
+msgid "Align shape"
+msgstr ""
+
 #: uresourcestrings.rsallapplications
 msgid "all"
 msgstr "všechny"

+ 28 - 0
lazpaint/release/bin/i18n/lazpaint.de.po

@@ -204,6 +204,30 @@ msgstr "Auswahl bearbeiten ..."
 msgid "Selection fit"
 msgstr "An Auswahl anpassen"
 
+#: tfmain.editshapealignbottom.hint
+msgid "Align shape to the bottom"
+msgstr ""
+
+#: tfmain.editshapealignleft.hint
+msgid "Align shape left"
+msgstr ""
+
+#: tfmain.editshapealignright.hint
+msgid "Align shape right"
+msgstr ""
+
+#: tfmain.editshapealigntop.hint
+msgid "Align shape to the top"
+msgstr ""
+
+#: tfmain.editshapecenterhorizontally.hint
+msgid "Center shape horizontally"
+msgstr ""
+
+#: tfmain.editshapecentervertically.hint
+msgid "Center shape vertically"
+msgstr ""
+
 #: tfmain.editundo.caption
 msgid "Undo"
 msgstr "Rückgängig"
@@ -1530,6 +1554,10 @@ msgstr "Aktion wird durchgeführt"
 msgid "Add files to the image processing list"
 msgstr "Bild zur Bildverarbeitungsliste hinzufügen"
 
+#: uresourcestrings.rsalignshape
+msgid "Align shape"
+msgstr ""
+
 #: uresourcestrings.rsallapplications
 msgid "all"
 msgstr "alle"

+ 28 - 0
lazpaint/release/bin/i18n/lazpaint.es.po

@@ -187,6 +187,30 @@ msgstr "Editar selección..."
 msgid "Selection fit"
 msgstr "Encajar selección"
 
+#: tfmain.editshapealignbottom.hint
+msgid "Align shape to the bottom"
+msgstr ""
+
+#: tfmain.editshapealignleft.hint
+msgid "Align shape left"
+msgstr ""
+
+#: tfmain.editshapealignright.hint
+msgid "Align shape right"
+msgstr ""
+
+#: tfmain.editshapealigntop.hint
+msgid "Align shape to the top"
+msgstr ""
+
+#: tfmain.editshapecenterhorizontally.hint
+msgid "Center shape horizontally"
+msgstr ""
+
+#: tfmain.editshapecentervertically.hint
+msgid "Center shape vertically"
+msgstr ""
+
 #: tfmain.editundo.caption
 msgid "Undo"
 msgstr "Deshacer"
@@ -1511,6 +1535,10 @@ msgstr "Acción en progreso"
 msgid "Add files to the image processing list"
 msgstr "Añadir a la lista de imágenes"
 
+#: uresourcestrings.rsalignshape
+msgid "Align shape"
+msgstr ""
+
 #: uresourcestrings.rsallapplications
 msgid "all"
 msgstr "todo"

+ 28 - 0
lazpaint/release/bin/i18n/lazpaint.fi.po

@@ -178,6 +178,30 @@ msgstr "Muokkaa valintaa..."
 msgid "Selection fit"
 msgstr ""
 
+#: tfmain.editshapealignbottom.hint
+msgid "Align shape to the bottom"
+msgstr ""
+
+#: tfmain.editshapealignleft.hint
+msgid "Align shape left"
+msgstr ""
+
+#: tfmain.editshapealignright.hint
+msgid "Align shape right"
+msgstr ""
+
+#: tfmain.editshapealigntop.hint
+msgid "Align shape to the top"
+msgstr ""
+
+#: tfmain.editshapecenterhorizontally.hint
+msgid "Center shape horizontally"
+msgstr ""
+
+#: tfmain.editshapecentervertically.hint
+msgid "Center shape vertically"
+msgstr ""
+
 #: tfmain.editundo.caption
 msgid "Undo"
 msgstr "Kumoa"
@@ -1505,6 +1529,10 @@ msgstr ""
 msgid "Add files to the image processing list"
 msgstr ""
 
+#: uresourcestrings.rsalignshape
+msgid "Align shape"
+msgstr ""
+
 #: uresourcestrings.rsallapplications
 msgid "all"
 msgstr "kaikki"

+ 28 - 0
lazpaint/release/bin/i18n/lazpaint.fr.po

@@ -193,6 +193,30 @@ msgstr "Éditer la sélection..."
 msgid "Selection fit"
 msgstr "Ajuster la sélection"
 
+#: tfmain.editshapealignbottom.hint
+msgid "Align shape to the bottom"
+msgstr "Aligner la forme en bas"
+
+#: tfmain.editshapealignleft.hint
+msgid "Align shape left"
+msgstr "Aligner la forme à gauche"
+
+#: tfmain.editshapealignright.hint
+msgid "Align shape right"
+msgstr "Aligner la forme à droite"
+
+#: tfmain.editshapealigntop.hint
+msgid "Align shape to the top"
+msgstr "Aligner la forme en haut"
+
+#: tfmain.editshapecenterhorizontally.hint
+msgid "Center shape horizontally"
+msgstr "Centrer la forme horizontalement"
+
+#: tfmain.editshapecentervertically.hint
+msgid "Center shape vertically"
+msgstr "Centrer la forme verticalement"
+
 #: tfmain.editundo.caption
 msgid "Undo"
 msgstr "Annuler"
@@ -1518,6 +1542,10 @@ msgstr "Action en cours"
 msgid "Add files to the image processing list"
 msgstr "Ajouter des fichiers pour traitement"
 
+#: uresourcestrings.rsalignshape
+msgid "Align shape"
+msgstr "Aligner la forme"
+
 #: uresourcestrings.rsallapplications
 msgid "all"
 msgstr "tout"

+ 28 - 0
lazpaint/release/bin/i18n/lazpaint.ja.po

@@ -193,6 +193,30 @@ msgstr ""
 msgid "Selection fit"
 msgstr "領域をフィットさせる"
 
+#: tfmain.editshapealignbottom.hint
+msgid "Align shape to the bottom"
+msgstr ""
+
+#: tfmain.editshapealignleft.hint
+msgid "Align shape left"
+msgstr ""
+
+#: tfmain.editshapealignright.hint
+msgid "Align shape right"
+msgstr ""
+
+#: tfmain.editshapealigntop.hint
+msgid "Align shape to the top"
+msgstr ""
+
+#: tfmain.editshapecenterhorizontally.hint
+msgid "Center shape horizontally"
+msgstr ""
+
+#: tfmain.editshapecentervertically.hint
+msgid "Center shape vertically"
+msgstr ""
+
 #: tfmain.editundo.caption
 msgid "Undo"
 msgstr "アンドゥ"
@@ -1517,6 +1541,10 @@ msgstr ""
 msgid "Add files to the image processing list"
 msgstr ""
 
+#: uresourcestrings.rsalignshape
+msgid "Align shape"
+msgstr ""
+
 #: uresourcestrings.rsallapplications
 msgid "all"
 msgstr ""

+ 28 - 0
lazpaint/release/bin/i18n/lazpaint.lv.po

@@ -191,6 +191,30 @@ msgstr "Labot iezīmējumu ..."
 msgid "Selection fit"
 msgstr "Ievietot iezīmējumā"
 
+#: tfmain.editshapealignbottom.hint
+msgid "Align shape to the bottom"
+msgstr ""
+
+#: tfmain.editshapealignleft.hint
+msgid "Align shape left"
+msgstr ""
+
+#: tfmain.editshapealignright.hint
+msgid "Align shape right"
+msgstr ""
+
+#: tfmain.editshapealigntop.hint
+msgid "Align shape to the top"
+msgstr ""
+
+#: tfmain.editshapecenterhorizontally.hint
+msgid "Center shape horizontally"
+msgstr ""
+
+#: tfmain.editshapecentervertically.hint
+msgid "Center shape vertically"
+msgstr ""
+
 #: tfmain.editundo.caption
 msgid "Undo"
 msgstr "Atsaukt"
@@ -1516,6 +1540,10 @@ msgstr "Moris darbojas"
 msgid "Add files to the image processing list"
 msgstr "Pievienot failus apstrādājamo attēlu sarakstam"
 
+#: uresourcestrings.rsalignshape
+msgid "Align shape"
+msgstr ""
+
 #: uresourcestrings.rsallapplications
 msgid "all"
 msgstr "visi"

+ 28 - 0
lazpaint/release/bin/i18n/lazpaint.nl.po

@@ -213,6 +213,30 @@ msgstr "Selectie bewerken..."
 msgid "Selection fit"
 msgstr "Selectie passend maken"
 
+#: tfmain.editshapealignbottom.hint
+msgid "Align shape to the bottom"
+msgstr ""
+
+#: tfmain.editshapealignleft.hint
+msgid "Align shape left"
+msgstr ""
+
+#: tfmain.editshapealignright.hint
+msgid "Align shape right"
+msgstr ""
+
+#: tfmain.editshapealigntop.hint
+msgid "Align shape to the top"
+msgstr ""
+
+#: tfmain.editshapecenterhorizontally.hint
+msgid "Center shape horizontally"
+msgstr ""
+
+#: tfmain.editshapecentervertically.hint
+msgid "Center shape vertically"
+msgstr ""
+
 #: tfmain.editundo.caption
 msgid "Undo"
 msgstr "Ongedaan maken"
@@ -1538,6 +1562,10 @@ msgstr "Bezig met actie"
 msgid "Add files to the image processing list"
 msgstr "Voeg bestanden toe aan afbeeldingsverwerkingslijst"
 
+#: uresourcestrings.rsalignshape
+msgid "Align shape"
+msgstr ""
+
 #: uresourcestrings.rsallapplications
 msgid "all"
 msgstr "alles"

+ 28 - 0
lazpaint/release/bin/i18n/lazpaint.po

@@ -176,6 +176,30 @@ msgstr ""
 msgid "Selection fit"
 msgstr ""
 
+#: tfmain.editshapealignbottom.hint
+msgid "Align shape to the bottom"
+msgstr ""
+
+#: tfmain.editshapealignleft.hint
+msgid "Align shape left"
+msgstr ""
+
+#: tfmain.editshapealignright.hint
+msgid "Align shape right"
+msgstr ""
+
+#: tfmain.editshapealigntop.hint
+msgid "Align shape to the top"
+msgstr ""
+
+#: tfmain.editshapecenterhorizontally.hint
+msgid "Center shape horizontally"
+msgstr ""
+
+#: tfmain.editshapecentervertically.hint
+msgid "Center shape vertically"
+msgstr ""
+
 #: tfmain.editundo.caption
 msgid "Undo"
 msgstr ""
@@ -1498,6 +1522,10 @@ msgstr ""
 msgid "Add files to the image processing list"
 msgstr ""
 
+#: uresourcestrings.rsalignshape
+msgid "Align shape"
+msgstr ""
+
 #: uresourcestrings.rsallapplications
 msgid "all"
 msgstr ""

+ 28 - 0
lazpaint/release/bin/i18n/lazpaint.pt_BR.po

@@ -197,6 +197,30 @@ msgstr "Editar seleção..."
 msgid "Selection fit"
 msgstr "Encaixar seleção"
 
+#: tfmain.editshapealignbottom.hint
+msgid "Align shape to the bottom"
+msgstr ""
+
+#: tfmain.editshapealignleft.hint
+msgid "Align shape left"
+msgstr ""
+
+#: tfmain.editshapealignright.hint
+msgid "Align shape right"
+msgstr ""
+
+#: tfmain.editshapealigntop.hint
+msgid "Align shape to the top"
+msgstr ""
+
+#: tfmain.editshapecenterhorizontally.hint
+msgid "Center shape horizontally"
+msgstr ""
+
+#: tfmain.editshapecentervertically.hint
+msgid "Center shape vertically"
+msgstr ""
+
 #: tfmain.editundo.caption
 msgid "Undo"
 msgstr "Desfazer"
@@ -1519,6 +1543,10 @@ msgstr "Ação em progresso"
 msgid "Add files to the image processing list"
 msgstr ""
 
+#: uresourcestrings.rsalignshape
+msgid "Align shape"
+msgstr ""
+
 #: uresourcestrings.rsallapplications
 msgid "all"
 msgstr "todos"

+ 28 - 0
lazpaint/release/bin/i18n/lazpaint.ru.po

@@ -193,6 +193,30 @@ msgstr "Редактировать выделение ..."
 msgid "Selection fit"
 msgstr "Заполнить выделение"
 
+#: tfmain.editshapealignbottom.hint
+msgid "Align shape to the bottom"
+msgstr ""
+
+#: tfmain.editshapealignleft.hint
+msgid "Align shape left"
+msgstr ""
+
+#: tfmain.editshapealignright.hint
+msgid "Align shape right"
+msgstr ""
+
+#: tfmain.editshapealigntop.hint
+msgid "Align shape to the top"
+msgstr ""
+
+#: tfmain.editshapecenterhorizontally.hint
+msgid "Center shape horizontally"
+msgstr ""
+
+#: tfmain.editshapecentervertically.hint
+msgid "Center shape vertically"
+msgstr ""
+
 #: tfmain.editundo.caption
 msgid "Undo"
 msgstr "Отменить"
@@ -1515,6 +1539,10 @@ msgstr "Прогресс действия"
 msgid "Add files to the image processing list"
 msgstr ""
 
+#: uresourcestrings.rsalignshape
+msgid "Align shape"
+msgstr ""
+
 #: uresourcestrings.rsallapplications
 msgid "all"
 msgstr "все"

+ 28 - 0
lazpaint/release/bin/i18n/lazpaint.sv.po

@@ -177,6 +177,30 @@ msgstr ""
 msgid "Selection fit"
 msgstr "Anpassat urval"
 
+#: tfmain.editshapealignbottom.hint
+msgid "Align shape to the bottom"
+msgstr ""
+
+#: tfmain.editshapealignleft.hint
+msgid "Align shape left"
+msgstr ""
+
+#: tfmain.editshapealignright.hint
+msgid "Align shape right"
+msgstr ""
+
+#: tfmain.editshapealigntop.hint
+msgid "Align shape to the top"
+msgstr ""
+
+#: tfmain.editshapecenterhorizontally.hint
+msgid "Center shape horizontally"
+msgstr ""
+
+#: tfmain.editshapecentervertically.hint
+msgid "Center shape vertically"
+msgstr ""
+
 #: tfmain.editundo.caption
 msgid "Undo"
 msgstr "Ångra"
@@ -1501,6 +1525,10 @@ msgstr ""
 msgid "Add files to the image processing list"
 msgstr ""
 
+#: uresourcestrings.rsalignshape
+msgid "Align shape"
+msgstr ""
+
 #: uresourcestrings.rsallapplications
 msgid "all"
 msgstr ""

+ 2 - 1
lazpaint/tools/utool.pas

@@ -39,7 +39,8 @@ type
   TBitmapToVirtualScreenFunction = function(PtF: TPointF): TPointF of object;
 
   TEraserMode = (emEraseAlpha, emSoften);
-  TToolCommand = (tcCut, tcCopy, tcPaste, tcDelete, tcMoveUp, tcMoveDown, tcMoveToFront, tcMoveToBack);
+  TToolCommand = (tcCut, tcCopy, tcPaste, tcDelete, tcMoveUp, tcMoveDown, tcMoveToFront, tcMoveToBack,
+    tcAlignLeft, tcCenterHorizontally, tcAlignRight, tcAlignTop, tcCenterVertically, tcAlignBottom);
 
 function GradientColorSpaceToDisplay(AValue: TBGRAColorInterpolation): string;
 function DisplayToGradientColorSpace(AValue: string): TBGRAColorInterpolation;

+ 49 - 5
lazpaint/tools/utoolvectorial.pas

@@ -183,6 +183,17 @@ begin
   end;
 end;
 
+procedure AlignShape(AShape: TVectorShape; ACommand: TToolCommand; const AMatrix: TAffineMatrix; const ARect: TRect);
+begin
+  case ACommand of
+  tcAlignLeft: AShape.AlignHorizontally(taLeftJustify,AMatrix,ARect);
+  tcCenterHorizontally: AShape.AlignHorizontally(taCenter,AMatrix,ARect);
+  tcAlignRight: AShape.AlignHorizontally(taRightJustify,AMatrix,ARect);
+  tcAlignTop..tcAlignBottom:
+      AShape.AlignVertically(TTextLayout(ord(ACommand)-ord(tcAlignTop)+ord(tlTop)),AMatrix,ARect);
+  end;
+end;
+
 { TEditShapeTool }
 
 procedure TEditShapeTool.SelectShape(ASender: TObject; AShape: TVectorShape;
@@ -681,8 +692,11 @@ begin
     Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex] :=
       GetMatrixFromRect(FOriginalRect,FOriginalRectUntransformed);
   if Assigned(FSelectionRect) then
+  begin
     Manager.Image.SelectionTransform :=
       GetMatrixFromRect(FSelectionRect,FSelectionRectUntransformed);
+    Manager.Image.OnImageChanged.NotifyObservers;
+  end;
 end;
 
 procedure TEditShapeTool.DoEditSelection;
@@ -1088,7 +1102,7 @@ begin
   end else
   begin
     result := true;
-    if IsVectorOriginal then
+    if IsVectorOriginal and not Assigned(FSelectionRect) then
     begin
       BindOriginalEvent(true);
       case ACommand of
@@ -1099,6 +1113,9 @@ begin
         tcCopy: Result:= CopyShapesToClipboard([GetVectorOriginal.SelectedShape]);
         tcCut: result := GetVectorOriginal.RemoveShape(GetVectorOriginal.SelectedShape);
         tcPaste: PasteShapesFromClipboard(GetVectorOriginal);
+        tcAlignLeft..tcAlignBottom: AlignShape(GetVectorOriginal.SelectedShape, ACommand,
+                     Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex],
+                     rect(0,0,Manager.Image.Width,Manager.Image.Height));
         else result := false;
       end;
       BindOriginalEvent(false);
@@ -1110,8 +1127,23 @@ begin
         tcMoveToBack: Manager.Image.MoveLayer(Manager.Image.CurrentLayerIndex, 0);
         tcMoveUp: Manager.Image.MoveLayer(Manager.Image.CurrentLayerIndex, Manager.Image.CurrentLayerIndex+1);
         tcMoveToFront: Manager.Image.MoveLayer(Manager.Image.CurrentLayerIndex, Manager.Image.NbLayers-1);
+        tcAlignLeft..tcAlignBottom:
+            begin
+              AlignShape(FOriginalRect, ACommand,
+                         AffineMatrixIdentity,rect(0,0,Manager.Image.Width,Manager.Image.Height));
+              UpdateMatrixFromRect;
+            end
         else result := false;
       end;
+    end else
+    if Assigned(FSelectionRect) then
+    begin
+      if ACommand in [tcAlignLeft..tcAlignBottom] then
+      begin
+        AlignShape(FSelectionRect, ACommand,
+                   AffineMatrixIdentity,rect(0,0,Manager.Image.Width,Manager.Image.Height));
+        UpdateMatrixFromRect;
+      end;
     end;
   end;
 end;
@@ -1119,13 +1151,19 @@ end;
 function TEditShapeTool.ToolProvideCommand(ACommand: TToolCommand): boolean;
 begin
   case ACommand of
-  tcCut,tcCopy,tcDelete: result:= IsVectorOriginal and Assigned(GetVectorOriginal.SelectedShape);
+  tcCut,tcCopy,tcDelete: result:= IsVectorOriginal and not Assigned(FSelectionRect)
+                                  and Assigned(GetVectorOriginal.SelectedShape);
+  tcAlignLeft..tcAlignBottom: result:= (IsVectorOriginal and not Assigned(FSelectionRect)
+                                        and Assigned(GetVectorOriginal.SelectedShape)) or
+                                       (Assigned(FOriginalRect) or Assigned(FSelectionRect));
   tcPaste: result := IsVectorOriginal and ClipboardHasShapes;
-  tcMoveUp,tcMoveToFront: result := (IsVectorOriginal and Assigned(GetVectorOriginal.SelectedShape)
+  tcMoveUp,tcMoveToFront: result := (IsVectorOriginal and not Assigned(FSelectionRect)
+                                    and Assigned(GetVectorOriginal.SelectedShape)
                                     and not GetVectorOriginal.SelectedShape.IsFront) or
                                     (IsOtherOriginal and Assigned(FOriginalRect) and
                                       (Manager.Image.CurrentLayerIndex < Manager.Image.NbLayers-1));
-  tcMoveDown,tcMoveToBack: result := (IsVectorOriginal and Assigned(GetVectorOriginal.SelectedShape)
+  tcMoveDown,tcMoveToBack: result := (IsVectorOriginal and not Assigned(FSelectionRect)
+                                     and Assigned(GetVectorOriginal.SelectedShape)
                                      and not GetVectorOriginal.SelectedShape.IsBack) or
                                      (IsOtherOriginal and Assigned(FOriginalRect) and
                                       (Manager.Image.CurrentLayerIndex > 0));
@@ -1728,7 +1766,7 @@ begin
         Result:= false;
     end;
   tcCut: begin
-      if ToolProvideCommand(tcCut) and ToolCommand(tcCopy) then
+      if ToolCommand(tcCopy) then
       begin
         toolDest := GetToolDrawingLayer;
         r := CancelShape;
@@ -1737,6 +1775,11 @@ begin
       end else
         result := false;
     end;
+  tcAlignLeft..tcAlignBottom:
+      if ToolProvideCommand(ACommand) then
+        AlignShape(FShape, ACommand,
+                 Manager.Image.LayerOriginalMatrix[Manager.Image.CurrentLayerIndex],
+                 rect(0,0,Manager.Image.Width,Manager.Image.Height));
   else
     result := false;
   end;
@@ -1746,6 +1789,7 @@ function TVectorialTool.ToolProvideCommand(ACommand: TToolCommand): boolean;
 begin
   case ACommand of
   tcCopy,tcCut: Result:= not IsSelectingTool and not FQuickDefine and Assigned(FShape);
+  tcAlignLeft..tcAlignBottom: Result:= not FQuickDefine and Assigned(FShape);
   tcMoveDown,tcMoveToBack: result := not IsSelectingTool and not FQuickDefine and Assigned(FShape)
           and not AlwaysRasterizeShape and Manager.Image.SelectionMaskEmpty and not FLayerWasEmpty;
   else result := false;

+ 24 - 1
lazpaint/umenu.pas

@@ -100,6 +100,16 @@ var actions: TStringList;
     item.Add(subItem);
   end;
 
+  procedure AddSubItem(AAction: TBasicAction; ATag: integer = 0);
+  var
+    subItem: TMenuItem;
+  begin
+    subItem := TMenuItem.Create(item);
+    subItem.Action := AAction;
+    subItem.Tag := ATag;
+    item.Add(subItem);
+  end;
+
 begin
   actions := TStringList.Create;
   actions.CommaText := AActionsCommaText;
@@ -156,6 +166,19 @@ begin
           AddSubItem(rsAutodetect, @IconSizeItemClick, 0);
           AMenu.Add(item);
           item := nil;
+        end else
+        if Assigned(item) and (actions[i] = 'EditShapeAlign') then
+        begin
+          item.Caption := rsAlignShape;
+          AddSubItem(AActionList.ActionByName('EditShapeAlignLeft'));
+          AddSubItem(AActionList.ActionByName('EditShapeCenterHorizontally'));
+          AddSubItem(AActionList.ActionByName('EditShapeAlignRight'));
+          AddSubItem('-',nil,0);
+          AddSubItem(AActionList.ActionByName('EditShapeAlignTop'));
+          AddSubItem(AActionList.ActionByName('EditShapeCenterVertically'));
+          AddSubItem(AActionList.ActionByName('EditShapeAlignBottom'));
+          AMenu.Add(item);
+          item := nil;
         end;
         if Assigned(item) then item.Caption := trim(actions[i])+'?';
       end;
@@ -357,7 +380,7 @@ begin
     if (Caption = '') and (Hint <> '') then Caption := Hint;
 
   AddMenus('MenuFile',   'FileNew,FileOpen,LayerFromFile,FileChooseEntry,FileReload,MenuRecentFiles,-,FileSave,FileSaveAsInSameFolder,FileSaveAs,-,FileImport3D,-,FilePrint,-,'+ImageBrowser+'FileRememberSaveFormat,ForgetDialogAnswers,MenuLanguage,*');
-  AddMenus('MenuEdit',   'EditUndo,EditRedo,-,EditCut,EditCopy,EditPaste,EditPasteAsNew,EditPasteAsNewLayer,EditDeleteSelection,-,EditMoveUp,EditMoveToFront,EditMoveDown,EditMoveToBack,-,EditSelectAll,EditInvertSelection,EditSelectionFit,EditDeselect');
+  AddMenus('MenuEdit',   'EditUndo,EditRedo,-,EditCut,EditCopy,EditPaste,EditPasteAsNew,EditPasteAsNewLayer,EditDeleteSelection,-,EditMoveUp,EditMoveToFront,EditMoveDown,EditMoveToBack,EditShapeAlign,-,EditSelectAll,EditInvertSelection,EditSelectionFit,EditDeselect');
   AddMenus('MenuSelect', 'EditSelection,FileLoadSelection,FileSaveSelectionAs,-,EditSelectAll,EditInvertSelection,EditSelectionFit,EditDeselect,-,ToolSelectRect,ToolSelectEllipse,ToolSelectPoly,ToolSelectSpline,-,ToolMoveSelection,ToolRotateSelection,SelectionHorizontalFlip,SelectionVerticalFlip,-,ToolSelectPen,ToolMagicWand');
   AddMenus('MenuView',   'ViewGrid,ViewZoomOriginal,ViewZoomIn,ViewZoomOut,ViewZoomFit,-,ViewToolBox,ViewColors,ViewPalette,ViewLayerStack,ViewImageList,ViewStatusBar,-,*,-,ViewDarkTheme,ViewWorkspaceColor,MenuIconSize');
   AddMenus('MenuImage',  'ImageCrop,ImageCropLayer,ImageFlatten,MenuRemoveTransparency,-,ImageNegative,ImageLinearNegative,ImageSwapRedBlue,-,ImageChangeCanvasSize,ImageRepeat,-,ImageResample,ImageSmartZoom3,-,ImageRotateCW,ImageRotateCCW,ImageHorizontalFlip,ImageVerticalFlip');

+ 1 - 0
lazpaint/uresourcestrings.pas

@@ -42,6 +42,7 @@ resourcestring
   rsBytes = 'Bytes';
   rsFolder = 'Folder';
   rsIconSize = 'Icon size';
+  rsAlignShape = 'Align shape';
   rsAutodetect = 'Autodetect';
   rsNotReasonableFormat='It is not reasonable to save such a big image in this file format.';
   rsEditTexture='Edit texture';

+ 45 - 0
lazpaintcontrols/lcvectororiginal.pas

@@ -217,6 +217,10 @@ type
     procedure MoveUp(APassNonIntersectingShapes: boolean);
     procedure MoveDown(APassNonIntersectingShapes: boolean);
     procedure Remove;
+    procedure AlignHorizontally(AAlign: TAlignment; const AMatrix: TAffineMatrix; const ABounds: TRect); virtual;
+    procedure AlignVertically(AAlign: TTextLayout; const AMatrix: TAffineMatrix; const ABounds: TRect); virtual;
+    function GetAlignBounds(const ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; virtual;
+    procedure AlignTransform(const AMatrix: TAffineMatrix); virtual;
     function Duplicate: TVectorShape;
     class function StorageClassName: RawByteString; virtual; abstract;
     function GetIsSlow(const {%H-}AMatrix: TAffineMatrix): boolean; virtual;
@@ -2062,6 +2066,47 @@ begin
   else raise exception.Create('Shape does not have a container');
 end;
 
+procedure TVectorShape.AlignHorizontally(AAlign: TAlignment;
+  const AMatrix: TAffineMatrix; const ABounds: TRect);
+var
+  sb: TRectF;
+  m: TAffineMatrix;
+begin
+  sb := GetAlignBounds(ABounds, AMatrix);
+  case AAlign of
+  taRightJustify: m := AffineMatrixTranslation(ABounds.Right-sb.Right,0);
+  taCenter: m := AffineMatrixTranslation((ABounds.Left+ABounds.Right-sb.Left-sb.Right)/2,0);
+  else m := AffineMatrixTranslation(ABounds.Left-sb.Left,0);
+  end;
+  AlignTransform(AffineMatrixInverse(AMatrix)*m*AMatrix);
+end;
+
+procedure TVectorShape.AlignVertically(AAlign: TTextLayout;
+  const AMatrix: TAffineMatrix; const ABounds: TRect);
+var
+  sb: TRectF;
+  m: TAffineMatrix;
+begin
+  sb := GetAlignBounds(ABounds, AMatrix);
+  case AAlign of
+  tlBottom: m := AffineMatrixTranslation(0,ABounds.Bottom-sb.Bottom);
+  tlCenter: m := AffineMatrixTranslation(0,(ABounds.Top+ABounds.Bottom-sb.Top-sb.Bottom)/2);
+  else m := AffineMatrixTranslation(0,ABounds.Top-sb.Top);
+  end;
+  AlignTransform(AffineMatrixInverse(AMatrix)*m*AMatrix);
+end;
+
+function TVectorShape.GetAlignBounds(const ALayoutRect: TRect;
+  const AMatrix: TAffineMatrix): TRectF;
+begin
+  result := GetRenderBounds(ALayoutRect, AMatrix, []);
+end;
+
+procedure TVectorShape.AlignTransform(const AMatrix: TAffineMatrix);
+begin
+  Transform(AMatrix);
+end;
+
 function TVectorShape.Duplicate: TVectorShape;
 var temp: TBGRAMemOriginalStorage;
   shapeClass: TVectorShapeAny;

+ 85 - 0
lazpaintcontrols/lcvectorrectshapes.pas

@@ -73,6 +73,7 @@ type
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
     function GetAffineBox(const AMatrix: TAffineMatrix; APixelCentered: boolean): TAffineBox;
     procedure Transform(const AMatrix: TAffineMatrix); override;
+    procedure AlignTransform(const AMatrix: TAffineMatrix); override;
     property Origin: TPointF read FOrigin write SetOrigin;
     property XAxis: TPointF read FXAxis write SetXAxis;
     property YAxis: TPointF read FYAxis write SetYAxis;
@@ -107,6 +108,7 @@ type
   public
     constructor Create(AContainer: TVectorOriginal); override;
     class function Fields: TVectorShapeFields; override;
+    function GetAlignBounds(const ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
     procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
     function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix; AOptions: TRenderBoundsOptions = []): TRectF; override;
     function PointInShape(APoint: TPointF): boolean; override;
@@ -166,6 +168,7 @@ type
     function GetCornerPositition: single; override;
     class function Fields: TVectorShapeFields; override;
     class function PreferPixelCentered: boolean; override;
+    function GetAlignBounds(const ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
     procedure ConfigureCustomEditor(AEditor: TBGRAOriginalEditor); override;
     procedure MouseDown(RightButton: boolean; Shift: TShiftState; X, Y: single; var ACursor: TOriginalEditorCursor; var AHandled: boolean); override;
     procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
@@ -674,6 +677,11 @@ begin
   EndUpdate;
 end;
 
+procedure TCustomRectShape.AlignTransform(const AMatrix: TAffineMatrix);
+begin
+  Origin := AMatrix*Origin;
+end;
+
 function TCustomRectShape.GetOrthoRect(AMatrix: TAffineMatrix; out ARect: TRectF): boolean;
 var
   sx,sy: single;
@@ -1029,6 +1037,43 @@ begin
   Result:= [vsfPenFill, vsfPenWidth, vsfPenStyle, vsfBackFill];
 end;
 
+function TEllipseShape.GetAlignBounds(const ALayoutRect: TRect;
+  const AMatrix: TAffineMatrix): TRectF;
+var
+  m: TAffineMatrix;
+  pts: ArrayOfTPointF;
+  i: Integer;
+  zoom: Single;
+
+  procedure IncludePoint(const APoint: TPointF);
+  begin
+    if APoint.x < result.Left then result.Left := APoint.x else
+    if APoint.x > result.Right then result.Right := APoint.x;
+    if APoint.y < result.Top then result.Top := APoint.y else
+    if APoint.y > result.Bottom then result.Bottom := APoint.y;
+  end;
+
+begin
+  m:= AffineMatrixTranslation(0.5,0.5)*MatrixForPixelCentered(AMatrix);
+  pts := ComputeEllipse(m*FOrigin, m*FXAxis, m*FYAxis);
+  if pts = nil then exit(EmptyRectF);
+  result.TopLeft := pts[0];
+  result.BottomRight := pts[0];
+  for i := 0 to high(pts) do IncludePoint(pts[i]);
+  IncludePoint(m*XAxis);
+  IncludePoint(m*YAxis);
+  IncludePoint(m*(Origin-(XAxis-Origin)));
+  IncludePoint(m*(Origin-(YAxis-Origin)));
+  if PenVisible then
+  begin
+    zoom := (VectLen(AMatrix[1,1],AMatrix[2,1])+VectLen(AMatrix[1,2],AMatrix[2,2]))/2;
+    result.Left -= zoom*PenWidth/2;
+    result.Right += zoom*PenWidth/2;
+    result.Top -= zoom*PenWidth/2;
+    result.Bottom += zoom*PenWidth/2;
+  end;
+end;
+
 procedure TEllipseShape.Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix;
   ADraft: boolean);
 var
@@ -1302,6 +1347,46 @@ begin
   Result:= false;
 end;
 
+function TPhongShape.GetAlignBounds(const ALayoutRect: TRect;
+  const AMatrix: TAffineMatrix): TRectF;
+var
+  m: TAffineMatrix;
+  pts: ArrayOfTPointF;
+  i: Integer;
+
+  procedure IncludePoint(const APoint: TPointF);
+  begin
+    if APoint.x < result.Left then result.Left := APoint.x else
+    if APoint.x > result.Right then result.Right := APoint.x;
+    if APoint.y < result.Top then result.Top := APoint.y else
+    if APoint.y > result.Bottom then result.Bottom := APoint.y;
+  end;
+
+begin
+  m:= AffineMatrixTranslation(0.5,0.5)*MatrixForPixelCentered(AMatrix);
+  if ShapeKind in[pskHalfSphere,pskConeTop] then
+  begin
+    pts := ComputeEllipse(m*FOrigin, m*FXAxis, m*FYAxis);
+    if pts = nil then exit(EmptyRectF);
+    result.TopLeft := pts[0];
+    result.BottomRight := pts[0];
+    for i := 0 to high(pts) do IncludePoint(pts[i]);
+    IncludePoint(m*XAxis);
+    IncludePoint(m*YAxis);
+    IncludePoint(m*(Origin-(XAxis-Origin)));
+    IncludePoint(m*(Origin-(YAxis-Origin)));
+  end else
+  if ShapeKind = pskConeSide then
+  begin
+    result.TopLeft := m*Origin;
+    result.BottomRight := m*Origin;
+    IncludePoint(m*(XAxis+(YAxis-Origin)));
+    IncludePoint(m*(Origin-(XAxis-Origin)+(YAxis-Origin)));
+    IncludePoint(m*(Origin-(YAxis-Origin)));
+  end else
+    result := inherited GetAlignBounds(ALayoutRect,AMatrix);
+end;
+
 procedure TPhongShape.ConfigureCustomEditor(AEditor: TBGRAOriginalEditor);
 var
   idxLight: Integer;

+ 10 - 0
lazpaintcontrols/lcvectortextshapes.pas

@@ -164,6 +164,7 @@ type
     function CutSelection: boolean;
     function PasteSelection: boolean;
     function DeleteSelection: boolean;
+    function GetAlignBounds(const ALayoutRect: TRect; const AMatrix: TAffineMatrix): TRectF; override;
     procedure Transform(const AMatrix: TAffineMatrix); override;
     property HasSelection: boolean read GetHasSelection;
     property CanPasteSelection: boolean read GetCanPasteSelection;
@@ -886,6 +887,15 @@ begin
     result := false;
 end;
 
+function TTextShape.GetAlignBounds(const ALayoutRect: TRect;
+  const AMatrix: TAffineMatrix): TRectF;
+var
+  ab: TAffineBox;
+begin
+  ab := GetAffineBox(AMatrix, false);
+  Result:= ab.RectBoundsF;
+end;
+
 procedure TTextShape.InsertText(ATextUTF8: string);
 var
   insertCount: Integer;