This commit is contained in:
Nathan_B 2025-08-19 09:04:14 +03:00 committed by GitHub
commit 0b2543e3bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 998 additions and 596 deletions

View file

@ -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);
}
}

View file

@ -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; }

View 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);
}

View file

@ -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,

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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>

View file

@ -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; }

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

View file

@ -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>