Merge 0a533ab367
into c1ad1d4a93
|
@ -22,6 +22,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Windows.Forms;
|
||||
using Dapplo.Windows.Common.Structs;
|
||||
|
@ -29,6 +30,22 @@ using Greenshot.Base.Interfaces.Drawing.Adorners;
|
|||
|
||||
namespace Greenshot.Base.Interfaces.Drawing
|
||||
{
|
||||
public enum Direction
|
||||
{
|
||||
LEFT,
|
||||
RIGHT,
|
||||
TOP,
|
||||
BOTTOM,
|
||||
}
|
||||
|
||||
public struct Expansion
|
||||
{
|
||||
public int Left;
|
||||
public int Right;
|
||||
public int Top;
|
||||
public int Bottom;
|
||||
}
|
||||
|
||||
public interface IDrawableContainer : INotifyPropertyChanged, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -111,5 +128,12 @@ namespace Greenshot.Base.Interfaces.Drawing
|
|||
/// <param name="surface">ISurface</param>
|
||||
/// <param name="mouseEventArgs">MouseEventArgs</param>
|
||||
void AddContextMenuItems(ContextMenuStrip menu, ISurface surface, MouseEventArgs mouseEventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// Snap the container to the edge of the surface.
|
||||
/// </summary>
|
||||
/// <param name="direction">Direction in which to move the container.</param>
|
||||
/// <param name="surface">The surface the container belongs to.</param>
|
||||
void SnapToEdge(Direction direction, Size surfaceSize);
|
||||
}
|
||||
}
|
|
@ -40,6 +40,7 @@ namespace Greenshot.Base.Interfaces
|
|||
event SurfaceMessageEventHandler SurfaceMessage;
|
||||
event SurfaceDrawingModeEventHandler DrawingModeChanged;
|
||||
event SurfaceElementEventHandler MovingElementChanged;
|
||||
event SurfaceExpandedEventHandler SurfaceExpanded;
|
||||
event SurfaceForegroundColorEventHandler ForegroundColorChanged;
|
||||
event SurfaceBackgroundColorEventHandler BackgroundColorChanged;
|
||||
event SurfaceLineThicknessEventHandler LineThicknessChanged;
|
||||
|
@ -203,6 +204,8 @@ namespace Greenshot.Base.Interfaces
|
|||
void RemoveElement(IDrawableContainer elementToRemove, bool makeUndoable = true, bool invalidate = true, bool generateEvents = true);
|
||||
|
||||
void SendMessageEvent(object source, SurfaceMessageTyp messageType, string message);
|
||||
void ResizeCanvas(int left, int right, int top, int bottom);
|
||||
void ResizeCanvas(Expansion expansion);
|
||||
void ApplyBitmapEffect(IEffect effect);
|
||||
void RemoveCursor();
|
||||
bool HasCursor { get; }
|
||||
|
|
27
src/Greenshot.Base/Interfaces/SurfaceExpandedEventHandler.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Greenshot - a free and open source screenshot tool
|
||||
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
|
||||
*
|
||||
* For more information see: https://getgreenshot.org/
|
||||
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace Greenshot.Base.Interfaces
|
||||
{
|
||||
public delegate void SurfaceExpandedEventHandler(object sender, EventArgs e);
|
||||
}
|
|
@ -33,6 +33,10 @@ namespace Greenshot.Editor.Configuration
|
|||
contextmenu_capturefullscreen_right,
|
||||
contextmenu_capturefullscreen_bottom,
|
||||
contextmenu_captureie,
|
||||
editor_align_bottom,
|
||||
editor_align_left,
|
||||
editor_align_right,
|
||||
editor_align_top,
|
||||
editor_autocrop_not_possible,
|
||||
editor_clipboardfailed,
|
||||
editor_close_on_save,
|
||||
|
@ -44,13 +48,18 @@ namespace Greenshot.Editor.Configuration
|
|||
editor_downtobottom,
|
||||
editor_duplicate,
|
||||
editor_email,
|
||||
editor_fit,
|
||||
editor_imagesaved,
|
||||
editor_pushout,
|
||||
editor_snap,
|
||||
editor_title,
|
||||
editor_uponelevel,
|
||||
editor_uptotop,
|
||||
editor_undo,
|
||||
editor_redo,
|
||||
editor_resetsize,
|
||||
editor_resize_height,
|
||||
editor_resize_width,
|
||||
error,
|
||||
error_multipleinstances,
|
||||
error_openfile,
|
||||
|
|
|
@ -684,5 +684,42 @@ namespace Greenshot.Editor.Drawing
|
|||
protected virtual void InitializeFields()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Snap the container to the edge of the surface.
|
||||
/// </summary>
|
||||
/// <param name="direction">Direction in which to move the container.</param>
|
||||
/// <param name="surface">The surface the container belongs to.</param>
|
||||
public void SnapToEdge(Direction direction, Size surfaceSize)
|
||||
{
|
||||
NativeRectFloat newBounds = GetLocationAfterSnap(direction, this.Bounds, surfaceSize);
|
||||
|
||||
this.MakeBoundsChangeUndoable(allowMerge: false);
|
||||
this.ApplyBounds(newBounds);
|
||||
}
|
||||
|
||||
private static NativeRectFloat GetLocationAfterSnap(Direction direction, NativeRect bounds, Size surfaceSize)
|
||||
{
|
||||
switch (direction)
|
||||
{
|
||||
case Direction.LEFT:
|
||||
bounds = bounds.ChangeX(0);
|
||||
break;
|
||||
case Direction.RIGHT:
|
||||
bounds = bounds.Offset(offsetX: surfaceSize.Width - bounds.Right);
|
||||
break;
|
||||
case Direction.TOP:
|
||||
bounds = bounds.ChangeY(0);
|
||||
break;
|
||||
case Direction.BOTTOM:
|
||||
bounds = bounds.Offset(offsetY: surfaceSize.Height - bounds.Bottom);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -672,6 +672,20 @@ namespace Greenshot.Editor.Drawing
|
|||
};
|
||||
menu.Items.Add(item);
|
||||
|
||||
#region Push Out, Fit, Snap
|
||||
var availableTranslations = new List<string>()
|
||||
{
|
||||
"en-US",
|
||||
};
|
||||
if (availableTranslations.Contains(Language.CurrentLanguage))
|
||||
{
|
||||
menu.Items.Add(GetPushOutSubMenu());
|
||||
menu.Items.Add(GetFitSubMenu(surface));
|
||||
menu.Items.Add(GetSnapSubMenu());
|
||||
}
|
||||
|
||||
#endregion Push Out, Fit, Snap
|
||||
|
||||
// Delete
|
||||
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_deleteelement))
|
||||
{
|
||||
|
@ -686,7 +700,6 @@ namespace Greenshot.Editor.Drawing
|
|||
if (canReset)
|
||||
{
|
||||
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_resetsize));
|
||||
//item.Image = ((System.Drawing.Image)(editorFormResources.GetObject("removeObjectToolStripMenuItem.Image")));
|
||||
item.Click += delegate
|
||||
{
|
||||
MakeBoundsChangeUndoable(false);
|
||||
|
@ -780,5 +793,231 @@ namespace Greenshot.Editor.Drawing
|
|||
drawableContainer.AdjustToDpi(dpi);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves all selected elements to one edge of the surface.
|
||||
/// </summary>
|
||||
/// <param name="direction">The direction in which to move the container.</param>
|
||||
public void SnapAllToEdge(Direction direction)
|
||||
{
|
||||
foreach (IDrawableContainer container in this)
|
||||
{
|
||||
SnapContainerToEdge(direction, container);
|
||||
}
|
||||
Parent.DeselectAllElements();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Push an element entirely outside the current bounds of the surface, expanding the surface to accomodate it.
|
||||
/// </summary>
|
||||
/// <param name="direction">Direction in which to move element.</param>
|
||||
/// <param name="targetElement">The element to move.</param>
|
||||
public void PushOut(Direction direction, IDrawableContainer targetElement)
|
||||
{
|
||||
Expansion expansion = GetExpansionFromSize(direction, targetElement.Size);
|
||||
|
||||
Parent.ResizeCanvas(expansion);
|
||||
|
||||
SnapContainerToEdge(direction, targetElement);
|
||||
|
||||
Parent.DeselectAllElements();
|
||||
}
|
||||
|
||||
private void SnapContainerToEdge(Direction direction, IDrawableContainer targetElement)
|
||||
{
|
||||
Size surfaceBounds = GetParentSurfaceSize();
|
||||
targetElement.SnapToEdge(direction, surfaceBounds);
|
||||
}
|
||||
|
||||
private Size GetParentSurfaceSize()
|
||||
{
|
||||
return new Size(Parent.Image.Width, Parent.Image.Height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the directional expansion needed to accommodate an element of the given size.
|
||||
/// </summary>
|
||||
/// <param name="direction">The direction in which to expand.</param>
|
||||
/// <param name="elementSize">The size of the element to accommodate.</param>
|
||||
/// <returns>The new expansion object, or null if an invalid Direction was given.</returns>
|
||||
private static Expansion GetExpansionFromSize(Direction direction, Size elementSize)
|
||||
{
|
||||
var expansion = new Expansion();
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case Direction.LEFT:
|
||||
expansion.Left = elementSize.Width;
|
||||
break;
|
||||
case Direction.RIGHT:
|
||||
expansion.Right = elementSize.Width;
|
||||
break;
|
||||
case Direction.TOP:
|
||||
expansion.Top = elementSize.Height;
|
||||
break;
|
||||
case Direction.BOTTOM:
|
||||
expansion.Bottom = elementSize.Height;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return expansion;
|
||||
}
|
||||
|
||||
private ToolStripMenuItem GetPushOutSubMenu()
|
||||
{
|
||||
var pushOutSubmenu = new ToolStripMenuItem(Language.GetString(LangKey.editor_pushout));
|
||||
|
||||
// Top
|
||||
var item = new ToolStripMenuItem(Language.GetString(LangKey.editor_align_top))
|
||||
{
|
||||
Image = (Image)EditorFormResources.GetObject("PushOut-Top.Image")
|
||||
};
|
||||
item.Click += delegate
|
||||
{
|
||||
if (this.Count > 0)
|
||||
{
|
||||
PushOut(Direction.TOP, this[0]);
|
||||
}
|
||||
};
|
||||
pushOutSubmenu.DropDownItems.Add(item);
|
||||
|
||||
// Right
|
||||
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_align_right))
|
||||
{
|
||||
Image = (Image)EditorFormResources.GetObject("PushOut-Right.Image")
|
||||
};
|
||||
item.Click += delegate
|
||||
{
|
||||
if (this.Count > 0)
|
||||
{
|
||||
PushOut(Direction.RIGHT, this[0]);
|
||||
}
|
||||
};
|
||||
pushOutSubmenu.DropDownItems.Add(item);
|
||||
|
||||
// Bottom
|
||||
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_align_bottom))
|
||||
{
|
||||
Image = (Image)EditorFormResources.GetObject("PushOut-Bottom.Image")
|
||||
};
|
||||
item.Click += delegate
|
||||
{
|
||||
if (this.Count > 0)
|
||||
{
|
||||
PushOut(Direction.BOTTOM, this[0]);
|
||||
}
|
||||
};
|
||||
pushOutSubmenu.DropDownItems.Add(item);
|
||||
|
||||
// Left
|
||||
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_align_left))
|
||||
{
|
||||
Image = (Image)EditorFormResources.GetObject("PushOut-Left.Image")
|
||||
};
|
||||
item.Click += delegate
|
||||
{
|
||||
if (this.Count > 0)
|
||||
{
|
||||
PushOut(Direction.LEFT, this[0]);
|
||||
}
|
||||
};
|
||||
pushOutSubmenu.DropDownItems.Add(item);
|
||||
|
||||
return pushOutSubmenu;
|
||||
}
|
||||
|
||||
private ToolStripMenuItem GetFitSubMenu(ISurface surface)
|
||||
{
|
||||
var fitSubmenu = new ToolStripMenuItem(Language.GetString(LangKey.editor_fit));
|
||||
|
||||
// Fit width
|
||||
var item = new ToolStripMenuItem(Language.GetString(LangKey.editor_resize_width))
|
||||
{
|
||||
Image = (Image)EditorFormResources.GetObject("Fit-width.Image")
|
||||
};
|
||||
item.Click += delegate
|
||||
{
|
||||
foreach (IDrawableContainer item in this)
|
||||
{
|
||||
MakeBoundsChangeUndoable(false);
|
||||
item.Width = surface.Image.Width;
|
||||
}
|
||||
SnapAllToEdge(Direction.LEFT);
|
||||
surface.Invalidate();
|
||||
};
|
||||
fitSubmenu.DropDownItems.Add(item);
|
||||
|
||||
// Fit height
|
||||
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_resize_height))
|
||||
{
|
||||
Image = (Image)EditorFormResources.GetObject("Fit-height.Image")
|
||||
};
|
||||
item.Click += delegate
|
||||
{
|
||||
foreach (IDrawableContainer item in this)
|
||||
{
|
||||
MakeBoundsChangeUndoable(false);
|
||||
item.Height = surface.Image.Height;
|
||||
}
|
||||
SnapAllToEdge(Direction.TOP);
|
||||
surface.Invalidate();
|
||||
};
|
||||
fitSubmenu.DropDownItems.Add(item);
|
||||
|
||||
return fitSubmenu;
|
||||
}
|
||||
|
||||
private ToolStripMenuItem GetSnapSubMenu()
|
||||
{
|
||||
var snapSubmenu = new ToolStripMenuItem(Language.GetString(LangKey.editor_snap));
|
||||
|
||||
// Snap to top
|
||||
var item = new ToolStripMenuItem(Language.GetString(LangKey.editor_align_top))
|
||||
{
|
||||
Image = (Image)EditorFormResources.GetObject("Snap-top.Image")
|
||||
};
|
||||
item.Click += delegate
|
||||
{
|
||||
SnapAllToEdge(Direction.TOP);
|
||||
};
|
||||
snapSubmenu.DropDownItems.Add(item);
|
||||
|
||||
// Snap right
|
||||
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_align_right))
|
||||
{
|
||||
Image = (Image)EditorFormResources.GetObject("Snap-right.Image")
|
||||
};
|
||||
item.Click += delegate
|
||||
{
|
||||
SnapAllToEdge(Direction.RIGHT);
|
||||
};
|
||||
snapSubmenu.DropDownItems.Add(item);
|
||||
|
||||
// Snap to bottom
|
||||
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_align_bottom))
|
||||
{
|
||||
Image = (Image)EditorFormResources.GetObject("Snap-bottom.Image")
|
||||
};
|
||||
item.Click += delegate
|
||||
{
|
||||
SnapAllToEdge(Direction.BOTTOM);
|
||||
};
|
||||
snapSubmenu.DropDownItems.Add(item);
|
||||
|
||||
// Snap left
|
||||
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_align_left))
|
||||
{
|
||||
Image = (Image)EditorFormResources.GetObject("Snap-left.Image")
|
||||
};
|
||||
item.Click += delegate
|
||||
{
|
||||
SnapAllToEdge(Direction.LEFT);
|
||||
};
|
||||
snapSubmenu.DropDownItems.Add(item);
|
||||
|
||||
return snapSubmenu;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -107,6 +107,14 @@ namespace Greenshot.Editor.Drawing
|
|||
remove => _surfaceSizeChanged -= value;
|
||||
}
|
||||
|
||||
[NonSerialized] private SurfaceExpandedEventHandler _surfaceExpanded;
|
||||
|
||||
public event SurfaceExpandedEventHandler SurfaceExpanded
|
||||
{
|
||||
add => _surfaceExpanded += value;
|
||||
remove => _surfaceExpanded -= value;
|
||||
}
|
||||
|
||||
[NonSerialized] private SurfaceMessageEventHandler _surfaceMessage;
|
||||
|
||||
public event SurfaceMessageEventHandler SurfaceMessage
|
||||
|
@ -1039,6 +1047,28 @@ namespace Greenshot.Editor.Drawing
|
|||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the canvas to a new size using the given bounds.
|
||||
/// Each parameter is the distance to expand in that direction.
|
||||
/// </summary>
|
||||
public void ResizeCanvas(int left, int right, int top, int bottom)
|
||||
{
|
||||
var resizeEffect = new ResizeCanvasEffect(left, right, top, bottom);
|
||||
ApplyBitmapEffect(resizeEffect);
|
||||
_surfaceExpanded(this, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the canvas to a new size using the given expansion directions.
|
||||
/// </summary>
|
||||
/// <param name="expansion">The amount to expand in each direction.</param>
|
||||
public void ResizeCanvas(Expansion expansion)
|
||||
{
|
||||
var resizeEffect = new ResizeCanvasEffect(expansion.Left, expansion.Right, expansion.Top, expansion.Bottom);
|
||||
ApplyBitmapEffect(resizeEffect);
|
||||
_surfaceExpanded(this, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a bitmap effect to the surface
|
||||
/// </summary>
|
||||
|
|
|
@ -246,6 +246,7 @@ namespace Greenshot.Editor.Forms
|
|||
_surface.MovingElementChanged += delegate { RefreshEditorControls(); };
|
||||
_surface.DrawingModeChanged += Surface_DrawingModeChanged;
|
||||
_surface.SurfaceSizeChanged += SurfaceSizeChanged;
|
||||
_surface.SurfaceExpanded += SurfaceExpanded;
|
||||
_surface.SurfaceMessage += SurfaceMessageReceived;
|
||||
_surface.ForegroundColorChanged += ForegroundColorChanged;
|
||||
_surface.BackgroundColorChanged += BackgroundColorChanged;
|
||||
|
@ -566,6 +567,16 @@ namespace Greenshot.Editor.Forms
|
|||
AlignCanvasPositionAfterResize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is called when expanding the surface in one direction, used to accomodate shifting an object to one side.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void SurfaceExpanded(object sender, EventArgs e)
|
||||
{
|
||||
UpdateUndoRedoSurfaceDependencies();
|
||||
}
|
||||
|
||||
public ISurface Surface
|
||||
{
|
||||
get { return _surface; }
|
||||
|
|
BIN
src/Greenshot.Editor/icons/application-dock-090.png
Normal file
After Width: | Height: | Size: 521 B |
BIN
src/Greenshot.Editor/icons/application-dock-180.png
Normal file
After Width: | Height: | Size: 626 B |
BIN
src/Greenshot.Editor/icons/application-dock-270.png
Normal file
After Width: | Height: | Size: 529 B |
BIN
src/Greenshot.Editor/icons/application-dock.png
Normal file
After Width: | Height: | Size: 622 B |
BIN
src/Greenshot.Editor/icons/arrow-resize-090.png
Normal file
After Width: | Height: | Size: 455 B |
BIN
src/Greenshot.Editor/icons/arrow-resize.png
Normal file
After Width: | Height: | Size: 447 B |
BIN
src/Greenshot.Editor/icons/fill-down.png
Normal file
After Width: | Height: | Size: 585 B |
BIN
src/Greenshot.Editor/icons/fill-left.png
Normal file
After Width: | Height: | Size: 572 B |
BIN
src/Greenshot.Editor/icons/fill-right.png
Normal file
After Width: | Height: | Size: 560 B |
BIN
src/Greenshot.Editor/icons/fill-up.png
Normal file
After Width: | Height: | Size: 577 B |
|
@ -117,6 +117,7 @@ Also, we would highly appreciate if you checked whether a tracker item already e
|
|||
<resource name="editor_email">E-Mail</resource>
|
||||
<resource name="editor_file">File</resource>
|
||||
<resource name="editor_fontsize">Size</resource>
|
||||
<resource name="editor_fit">Fit</resource>
|
||||
<resource name="editor_forecolor">Line color (NumPad0-9, Shift+0-9)</resource>
|
||||
<resource name="editor_grayscale">Grayscale</resource>
|
||||
<resource name="editor_highlight_area">Highlight area</resource>
|
||||
|
@ -140,6 +141,7 @@ Also, we would highly appreciate if you checked whether a tracker item already e
|
|||
<resource name="editor_opendirinexplorer">Open directory in Windows Explorer</resource>
|
||||
<resource name="editor_pastefromclipboard">Paste</resource>
|
||||
<resource name="editor_pixel_size">Pixel size</resource>
|
||||
<resource name="editor_pushout">Stitch</resource>
|
||||
<resource name="editor_preview_quality">Preview quality</resource>
|
||||
<resource name="editor_print">Print</resource>
|
||||
<resource name="editor_redo">Redo {0}</resource>
|
||||
|
@ -155,6 +157,7 @@ Also, we would highly appreciate if you checked whether a tracker item already e
|
|||
<resource name="editor_senttoprinter">Print job was sent to '{0}'.</resource>
|
||||
<resource name="editor_shadow">Drop shadow (/)</resource>
|
||||
<resource name="editor_storedtoclipboard">Image stored to clipboard.</resource>
|
||||
<resource name="editor_snap">Snap to</resource>
|
||||
<resource name="editor_thickness">Line thickness</resource>
|
||||
<resource name="editor_title">Greenshot image editor</resource>
|
||||
<resource name="editor_torn_edge">Torn edge</resource>
|
||||
|
|