Fixed merge conflict, and merged the EmojiContainer and VectorGraphicsContainer

This commit is contained in:
Robin Krom 2022-04-14 12:17:24 +02:00
commit 8e6a13f00a
No known key found for this signature in database
GPG key ID: BCC01364F1371490
7 changed files with 144 additions and 171 deletions

View file

@ -6,12 +6,12 @@
<ItemGroup>
<PackageReference Include="Dapplo.HttpExtensions.JsonNet" Version="1.0.18" />
<PackageReference Include="Dapplo.Windows.Clipboard" Version="1.0.22" />
<PackageReference Include="Dapplo.Windows.Dpi" Version="1.0.22" />
<PackageReference Include="Dapplo.Windows.Gdi32" Version="1.0.22" />
<PackageReference Include="Dapplo.Windows.Icons" Version="1.0.22" />
<PackageReference Include="Dapplo.Windows.Kernel32" Version="1.0.22" />
<PackageReference Include="Dapplo.Windows.Multimedia" Version="1.0.22" />
<PackageReference Include="Dapplo.Windows.Clipboard" Version="1.0.25" />
<PackageReference Include="Dapplo.Windows.Dpi" Version="1.0.25" />
<PackageReference Include="Dapplo.Windows.Gdi32" Version="1.0.25" />
<PackageReference Include="Dapplo.Windows.Icons" Version="1.0.25" />
<PackageReference Include="Dapplo.Windows.Kernel32" Version="1.0.25" />
<PackageReference Include="Dapplo.Windows.Multimedia" Version="1.0.25" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.42" />
<PackageReference Include="log4net" version="2.0.14" />
<PackageReference Include="Svg" Version="3.4.2" />

View file

@ -20,6 +20,9 @@ using System.Windows.Input;
namespace Greenshot.Editor.Controls
{
/// <summary>
/// The event which is created when the emoji is picked
/// </summary>
public class EmojiPickedEventArgs : EventArgs
{
public EmojiPickedEventArgs() { }
@ -61,8 +64,7 @@ namespace Greenshot.Editor.Controls
public event EmojiPickedEventHandler Picked;
private static void OnSelectionPropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
private static void OnSelectionPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
(source as EmojiPicker)?.OnSelectionChanged(e.NewValue as string);
}
@ -75,24 +77,21 @@ namespace Greenshot.Editor.Controls
private void OnSelectionChanged(string s)
{
var is_disabled = string.IsNullOrEmpty(s);
Image.Emoji = is_disabled ? "???" : s;
Image.Opacity = is_disabled ? 0.3 : 1.0;
var isDisabled = string.IsNullOrEmpty(s);
Image.Emoji = isDisabled ? "???" : s;
Image.Opacity = isDisabled ? 0.3 : 1.0;
SelectionChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Selection)));
}
private void OnEmojiPicked(object sender, RoutedEventArgs e)
{
if (sender is Control control && control.DataContext is EmojiData.Emoji emoji)
{
if (emoji.VariationList.Count == 0 || sender is Button)
{
Selection = emoji.Text;
Button_INTERNAL.IsChecked = false;
e.Handled = true;
Picked?.Invoke(this, new EmojiPickedEventArgs(Selection));
}
}
if (sender is not Control { DataContext: EmojiData.Emoji emoji }) return;
if (emoji.VariationList.Count != 0 && sender is not Button) return;
Selection = emoji.Text;
Button_INTERNAL.IsChecked = false;
e.Handled = true;
Picked?.Invoke(this, new EmojiPickedEventArgs(Selection));
}
public static readonly DependencyProperty SelectionProperty = DependencyProperty.Register(
@ -101,31 +100,37 @@ namespace Greenshot.Editor.Controls
private void OnPopupKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape && sender is Popup popup)
{
popup.IsOpen = false;
e.Handled = true;
}
if (e.Key != Key.Escape || sender is not Popup popup) return;
popup.IsOpen = false;
e.Handled = true;
}
private void OnPopupLoaded(object sender, RoutedEventArgs e)
{
if (!(sender is Popup popup))
return;
if (sender is not Popup popup) return;
var child = popup.Child;
IInputElement old_focus = null;
IInputElement oldFocus = null;
child.Focusable = true;
child.IsVisibleChanged += (o, ea) =>
{
if (child.IsVisible)
{
old_focus = Keyboard.FocusedElement;
Keyboard.Focus(child);
}
if (!child.IsVisible) return;
oldFocus = Keyboard.FocusedElement;
Keyboard.Focus(child);
};
popup.Closed += (o, ea) => Keyboard.Focus(old_focus);
popup.Closed += (o, ea) => Keyboard.Focus(oldFocus);
}
public void ShowPopup(bool show)
{
foreach (var child in Children)
{
if (child is ToggleButton button)
{
button.IsChecked = show;
}
}
}
}
}

View file

@ -250,8 +250,7 @@ namespace Greenshot.Editor.Drawing
break;
}
}
var scaleOptions = (this as IHaveScaleOptions)?.GetScaleOptions();
_boundsAfterResize = ScaleHelper.Scale(_boundsBeforeResize, x, y, GetAngleRoundProcessor(), scaleOptions);
_boundsAfterResize = ScaleHelper.Scale(_boundsBeforeResize, Positions.TopLeft, x, y, GetAngleRoundProcessor());
// apply scaled bounds to this DrawableContainer
ApplyBounds(_boundsAfterResize);

View file

@ -534,7 +534,7 @@ namespace Greenshot.Editor.Drawing
_boundsAfterResize = new NativeRectFloat(_boundsBeforeResize.Left, _boundsBeforeResize.Top, x - _boundsAfterResize.Left, y - _boundsAfterResize.Top);
var scaleOptions = (this as IHaveScaleOptions)?.GetScaleOptions();
_boundsAfterResize = ScaleHelper.Scale(_boundsAfterResize, x, y, GetAngleRoundProcessor(), scaleOptions);
_boundsAfterResize = ScaleHelper.Scale(_boundsAfterResize, Positions.TopLeft, x, y,GetAngleRoundProcessor(), scaleOptions);
// apply scaled bounds to this DrawableContainer
ApplyBounds(_boundsAfterResize);

View file

@ -20,11 +20,9 @@
*/
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Serialization;
using System.Windows.Controls.Primitives;
using System.Windows.Forms;
using System.Windows.Forms.Integration;
using Greenshot.Base.Core;
@ -33,7 +31,6 @@ using Greenshot.Editor.Controls;
using Greenshot.Editor.Drawing.Adorners;
using Greenshot.Editor.Helpers;
using Image = System.Drawing.Image;
using Matrix = System.Drawing.Drawing2D.Matrix;
namespace Greenshot.Editor.Drawing
{
@ -41,17 +38,15 @@ namespace Greenshot.Editor.Drawing
/// Description of EmojiContainer.
/// </summary>
[Serializable]
public class EmojiContainer : DrawableContainer, IEmojiContainer, IHaveScaleOptions
public class EmojiContainer : VectorGraphicsContainer, IEmojiContainer, IHaveScaleOptions
{
[NonSerialized] private static EmojiContainer _currentContainer;
[NonSerialized] private static ElementHost _emojiPickerHost;
[NonSerialized] private static EmojiPicker _emojiPicker;
[NonSerialized] private bool _justCreated = true;
[NonSerialized] private Image _cachedImage = null;
private string _emoji;
private int _rotationAngle;
private bool _useSystemFont;
public string Emoji
@ -111,7 +106,7 @@ namespace Greenshot.Editor.Drawing
private void GetOrCreatePickerControl()
{
// Create one picker control by surface
// TODO: This is not ideal, replace with a different solution.
// TODO: This is not ideal, as we need to controls from the surface, should replace this with a different solution.
_emojiPickerHost = _parent.Controls.Find("EmojiPickerHost", false).OfType<ElementHost>().FirstOrDefault();
if (_emojiPickerHost != null) return;
@ -123,10 +118,12 @@ namespace Greenshot.Editor.Drawing
_currentContainer.Invalidate();
};
_emojiPickerHost = new ElementHost();
_emojiPickerHost.Dock = DockStyle.None;
_emojiPickerHost.Child = _emojiPicker;
_emojiPickerHost.Name = "EmojiPickerHost";
_emojiPickerHost = new ElementHost
{
Dock = DockStyle.None,
Child = _emojiPicker,
Name = "EmojiPickerHost"
};
_parent.Controls.Add(_emojiPickerHost);
}
@ -136,91 +133,46 @@ namespace Greenshot.Editor.Drawing
_emojiPicker?.ShowPopup(false);
}
protected override void OnDeserialized(StreamingContext streamingContext)
/// <summary>
/// Make sure we register the property changed, to manage the state of the picker
/// </summary>
protected override void Init()
{
base.OnDeserialized(streamingContext);
Init();
}
private void Init()
{
Adorners.Add(new ResizeAdorner(this, Positions.TopLeft));
Adorners.Add(new ResizeAdorner(this, Positions.TopRight));
Adorners.Add(new ResizeAdorner(this, Positions.BottomLeft));
Adorners.Add(new ResizeAdorner(this, Positions.BottomRight));
base.Init();
PropertyChanged += OnPropertyChanged;
}
protected override void Dispose(bool disposing)
/// <summary>
/// Handle the state of the Emoji Picker
/// </summary>
/// <param name="sender">object</param>
/// <param name="e">PropertyChangedEventArgs</param>
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (disposing)
if (!e.PropertyName.Equals(nameof(Selected))) return;
if (!Selected)
{
ResetCachedBitmap();
HideEmojiPicker();
}
base.Dispose(disposing);
}
public override void Transform(Matrix matrix)
{
_rotationAngle += CalculateAngle(matrix);
_rotationAngle %= 360;
ResetCachedBitmap();
base.Transform(matrix);
}
private void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName.Equals(nameof(Selected)))
else if (Status == EditStatus.IDLE && _justCreated)
{
if (!Selected)
{
HideEmojiPicker();
}
else if (Status == EditStatus.IDLE && _justCreated)
{
// Show picker just after creation
ShowEmojiPicker();
_justCreated = false;
}
// Show picker just after creation
ShowEmojiPicker();
_justCreated = false;
}
}
public override void Draw(Graphics graphics, RenderMode rm)
protected override Image ComputeBitmap()
{
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
var iconSize = Math.Min(Bounds.Width, Bounds.Height);
if (iconSize <= 0) return null;
var rect = Bounds;
var iconSize = Math.Min(rect.Width, rect.Height);
if (iconSize <= 0) return;
if (_cachedImage == null)
{
// First draw or cache was invalidated
_cachedImage = ComputeBitmap(iconSize);
}
else if (iconSize != _cachedImage.Width)
{
// The elements was resized => recompute
_cachedImage.Dispose();
_cachedImage = ComputeBitmap(iconSize);
}
graphics.DrawImage(_cachedImage, Bounds);
}
private Image ComputeBitmap(int iconSize)
{
var image = EmojiRenderer.GetBitmap(Emoji, iconSize, useSystemFont: _useSystemFont);
if (_rotationAngle != 0)
if (RotationAngle != 0)
{
var newImage = image.Rotate(_rotationAngle);
var newImage = image.Rotate(RotationAngle);
image.Dispose();
return newImage;
}
@ -228,29 +180,9 @@ namespace Greenshot.Editor.Drawing
return image;
}
private void ResetCachedBitmap()
{
_cachedImage?.Dispose();
_cachedImage = null;
}
public ScaleHelper.ScaleOptions GetScaleOptions()
{
return ScaleHelper.ScaleOptions.Rational;
}
}
internal static class PickerExtensions
{
public static void ShowPopup(this EmojiPicker emojiPicker, bool show)
{
foreach (var child in emojiPicker.Children)
{
if (child is ToggleButton button)
{
button.IsChecked = show;
}
}
}
}
}

View file

@ -25,6 +25,7 @@ using System.Drawing.Drawing2D;
using System.Runtime.Serialization;
using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Editor.Drawing.Adorners;
namespace Greenshot.Editor.Drawing
{
@ -35,28 +36,54 @@ namespace Greenshot.Editor.Drawing
[Serializable]
public abstract class VectorGraphicsContainer : DrawableContainer
{
protected int RotationAngle;
private int _rotationAngle;
protected int RotationAngle
{
get => _rotationAngle;
set => _rotationAngle = value;
}
/// <summary>
/// This is the cached version of the bitmap, pre-rendered to save performance
/// Do not serialized, it can be rebuild with some other information.
/// Do not serialized, it can be rebuild with other information.
/// </summary>
[NonSerialized] private Image _cachedImage;
/// <summary>
/// Constructor takes care of calling Init
/// </summary>
/// <param name="parent">ISurface</param>
public VectorGraphicsContainer(ISurface parent) : base(parent)
{
Init();
}
/// <summary>
/// Make sure Init is called after deserializing
/// </summary>
/// <param name="streamingContext">StreamingContext</param>
protected override void OnDeserialized(StreamingContext streamingContext)
{
base.OnDeserialized(streamingContext);
Init();
}
private void Init()
/// <summary>
/// Init is called after creating the instance, and from OnDeserialized
/// This is the place to generate your adorners
/// </summary>
protected virtual void Init()
{
CreateDefaultAdorners();
// Check if the adorners are already defined!
if (Adorners.Count > 0)
{
return;
}
Adorners.Add(new ResizeAdorner(this, Positions.TopLeft));
Adorners.Add(new ResizeAdorner(this, Positions.TopRight));
Adorners.Add(new ResizeAdorner(this, Positions.BottomLeft));
Adorners.Add(new ResizeAdorner(this, Positions.BottomRight));
}
/// <summary>
@ -96,7 +123,10 @@ namespace Greenshot.Editor.Drawing
}
_cachedImage ??= ComputeBitmap();
if (_cachedImage == null)
{
return;
}
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality;
@ -106,9 +136,16 @@ namespace Greenshot.Editor.Drawing
graphics.DrawImage(_cachedImage, Bounds);
}
/// <summary>
/// Implement this to compute the new bitmap according to the size of the container
/// </summary>
/// <returns>Image</returns>
protected abstract Image ComputeBitmap();
private void ResetCachedBitmap()
/// <summary>
/// Dispose of the cached bitmap, forcing the code to regenerate it
/// </summary>
protected void ResetCachedBitmap()
{
_cachedImage?.Dispose();
_cachedImage = null;

View file

@ -58,7 +58,7 @@ namespace Greenshot.Editor.Helpers
/// <param name="currentSize">the size of the element to be resized</param>
/// <param name="targetSize">the target size of the element</param>
/// <param name="crop">in case the aspect ratio of currentSize and targetSize differs: shall the scaled size fit into targetSize (i.e. that one of its dimensions is smaller - false) or vice versa (true)</param>
/// <returns>a new SizeF object indicating the width and height the element should be scaled to</returns>
/// <returns>NativeSizeFloat object indicating the width and height the element should be scaled to</returns>
public static NativeSizeFloat GetScaledSize(NativeSizeFloat currentSize, NativeSizeFloat targetSize, bool crop)
{
float wFactor = targetSize.Width / currentSize.Width;
@ -71,10 +71,10 @@ namespace Greenshot.Editor.Helpers
/// <summary>
/// calculates the position of an element depending on the desired alignment within a RectangleF
/// </summary>
/// <param name="currentRect">the bounds of the element to be aligned</param>
/// <param name="targetRect">the rectangle reference for alignment of the element</param>
/// <param name="currentRect">NativeRectFloat the bounds of the element to be aligned</param>
/// <param name="targetRect">NativeRectFloat with the rectangle for alignment of the element</param>
/// <param name="alignment">the System.Drawing.ContentAlignment value indicating how the element is to be aligned should the width or height differ from targetSize</param>
/// <returns>a new RectangleF object with Location aligned aligned to targetRect</returns>
/// <returns>NativeRectFloat object with Location aligned aligned to targetRect</returns>
public static NativeRectFloat GetAlignedRectangle(NativeRectFloat currentRect, NativeRectFloat targetRect, ContentAlignment alignment)
{
var newRect = new NativeRectFloat(targetRect.Location, currentRect.Size);
@ -96,9 +96,9 @@ namespace Greenshot.Editor.Helpers
/// <summary>
/// Calculates target size of a given rectangle scaled by dragging one of its handles (corners)
/// </summary>
/// <param name="originalRectangle">bounds of the current rectangle</param>
/// <param name="resizeHandlePosition">position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT</param>
/// <param name="resizeHandleCoords">coordinates of the used handle/gripper</param>
/// <param name="originalRectangle">NativeRectFloat bounds of the current rectangle</param>
/// <param name="resizeHandlePosition">Positions with the position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT</param>
/// <param name="resizeHandleCoords">NativePointFloat coordinates of the used handle/gripper</param>
/// <param name="options">ScaleOptions to use when scaling</param>
/// <returns>NativeRectFloat scaled originalRectangle</returns>
public static NativeRectFloat Scale(NativeRectFloat originalRectangle, Positions resizeHandlePosition, NativePointFloat resizeHandleCoords, ScaleOptions? options)
@ -134,9 +134,9 @@ namespace Greenshot.Editor.Helpers
/// <summary>
/// Calculates target size of a given rectangle scaled by dragging one of its handles (corners)
/// </summary>
/// <param name="originalRectangle">bounds of the current rectangle</param>
/// <param name="resizeHandlePosition">position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT</param>
/// <param name="resizeHandleCoords">coordinates of the used handle/gripper</param>
/// <param name="originalRectangle">NativeRectFloat bounds of the current rectangle</param>
/// <param name="resizeHandlePosition">Positions with the position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT</param>
/// <param name="resizeHandleCoords">NativePointFloat with coordinates of the used handle/gripper</param>
/// <returns>NativeRectFloat with the scaled originalRectangle</returns>
private static NativeRectFloat Scale(NativeRectFloat originalRectangle, Positions resizeHandlePosition, NativePointFloat resizeHandleCoords)
{
@ -158,9 +158,9 @@ namespace Greenshot.Editor.Helpers
/// To avoid objects growing near infinity unexpectedly in certain combinations, the adjustment will choose the
/// option resulting in the smaller rectangle.
/// </summary>
/// <param name="originalRectangle">bounds of the current rectangle</param>
/// <param name="resizeHandlePosition">position of the handle/gripper being used for resized, see Position</param>
/// <param name="resizeHandleCoords">coordinates of the used handle/gripper</param>
/// <param name="originalRectangle">NativeRectFloat with the bounds of the current rectangle</param>
/// <param name="resizeHandlePosition">Positions with the position of the handle/gripper being used for resized, see Position</param>
/// <param name="resizeHandleCoords">NativePointFloat with coordinates of the used handle/gripper</param>
/// <returns>NativePointFloat with the adjusted coordinates</returns>
private static NativePointFloat AdjustCoordsForRationalScale(NativeRectFloat originalRectangle, Positions resizeHandlePosition, NativePointFloat resizeHandleCoords)
{
@ -203,7 +203,7 @@ namespace Greenshot.Editor.Helpers
/// </summary>
/// <param name="originalSize">NativeSizeFloat to be considered for keeping aspect ratio</param>
/// <param name="selectedSize">NativeSizeFloat selection size (i.e. the size we'd produce if we wouldn't keep aspect ratio)</param>
/// <returns></returns>
/// <returns>NativeSizeFloat</returns>
private static NativeSizeFloat GetNewSizeForRationalScale(NativeSizeFloat originalSize, NativeSizeFloat selectedSize)
{
NativeSizeFloat newSize = selectedSize;
@ -227,17 +227,17 @@ namespace Greenshot.Editor.Helpers
return newSize;
}
public static NativeRectFloat Scale(NativeRect boundsBeforeResize, int cursorX, int cursorY, ScaleOptions? options)
{
return Scale(boundsBeforeResize, cursorX, cursorY, null, options);
}
public static NativeRectFloat Scale(NativeRect boundsBeforeResize, int cursorX, int cursorY, IDoubleProcessor angleRoundBehavior, ScaleOptions? options)
{
return Scale(boundsBeforeResize, Positions.TopLeft, cursorX, cursorY, angleRoundBehavior, options);
}
public static NativeRectFloat Scale(NativeRect boundsBeforeResize, Positions gripperPosition, int cursorX, int cursorY, IDoubleProcessor angleRoundBehavior, ScaleOptions? options)
/// <summary>
/// Scale the boundsBeforeResize with the specified position and new location, using the angle angleRoundBehavior
/// </summary>
/// <param name="boundsBeforeResize">NativeRect</param>
/// <param name="gripperPosition">Positions</param>
/// <param name="cursorX">int</param>
/// <param name="cursorY">int</param>
/// <param name="angleRoundBehavior">IDoubleProcessor</param>
/// <param name="options">ScaleOptionsProcessor</param>
/// <returns>NativeRectFloat</returns>
public static NativeRectFloat Scale(NativeRect boundsBeforeResize, Positions gripperPosition, int cursorX, int cursorY, IDoubleProcessor angleRoundBehavior, ScaleOptions? options = null)
{
options ??= GetScaleOptions();