mirror of
https://github.com/greenshot/greenshot
synced 2025-08-22 06:23:24 -07:00
Merge 3eb07ff4a5
into 245f6f261b
This commit is contained in:
commit
cf9a39215c
29 changed files with 6194 additions and 42 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -215,3 +215,6 @@ ModelManifest.xml
|
||||||
|
|
||||||
# Rider files
|
# Rider files
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
# Files compressed during the build
|
||||||
|
*.gz
|
|
@ -31,6 +31,15 @@ Source: {#ReleaseDir}\Svg.dll; DestDir: {app}; Components: greenshot; Flags: ove
|
||||||
Source: {#ReleaseDir}\Fizzler.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
Source: {#ReleaseDir}\Fizzler.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
Source: {#ReleaseDir}\HtmlAgilityPack.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
Source: {#ReleaseDir}\HtmlAgilityPack.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
Source: {#ReleaseDir}\Newtonsoft.Json.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
Source: {#ReleaseDir}\Newtonsoft.Json.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
|
Source: {#ReleaseDir}\SixLabors.ImageSharp.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
|
Source: {#ReleaseDir}\SixLabors.ImageSharp.Drawing.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
|
Source: {#ReleaseDir}\SixLabors.Fonts.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
|
Source: {#ReleaseDir}\System.Memory.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
|
Source: {#ReleaseDir}\System.Numerics.Vectors.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
|
Source: {#ReleaseDir}\System.Runtime.CompilerServices.Unsafe.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
|
Source: {#ReleaseDir}\System.Buffers.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
|
Source: {#ReleaseDir}\Twemoji.Mozilla.ttf; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
|
Source: {#ReleaseDir}\emojis.xml; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
Source: {#GreenshotProjectDir}\log4net.xml; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion
|
Source: {#GreenshotProjectDir}\log4net.xml; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion
|
||||||
Source: {#ReleaseDir}\checksum.SHA256; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
Source: {#ReleaseDir}\checksum.SHA256; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
;Source: ..\greenshot-defaults.ini; DestDir: {app}; Flags: overwritereadonly ignoreversion replacesameversion
|
;Source: ..\greenshot-defaults.ini; DestDir: {app}; Flags: overwritereadonly ignoreversion replacesameversion
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -265,6 +266,12 @@ namespace Greenshot.Base.Core
|
||||||
|
|
||||||
return exceptionText.ToString();
|
return exceptionText.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the directory where the application is located
|
||||||
|
/// </summary>
|
||||||
|
public static string GetApplicationFolder()
|
||||||
|
=> Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -73,7 +73,7 @@ namespace Greenshot.Base.Core
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string applicationFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
string applicationFolder = EnvironmentInfo.GetApplicationFolder();
|
||||||
|
|
||||||
// PAF Path
|
// PAF Path
|
||||||
if (applicationFolder != null)
|
if (applicationFolder != null)
|
||||||
|
|
28
src/Greenshot.Base/Interfaces/Drawing/IEmojiContainer.cs
Normal file
28
src/Greenshot.Base/Interfaces/Drawing/IEmojiContainer.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Greenshot.Base.Interfaces.Drawing
|
||||||
|
{
|
||||||
|
public interface IEmojiContainer : IDrawableContainer
|
||||||
|
{
|
||||||
|
string Emoji { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ namespace Greenshot.Base.Interfaces
|
||||||
Bitmap,
|
Bitmap,
|
||||||
Path,
|
Path,
|
||||||
SpeechBubble,
|
SpeechBubble,
|
||||||
StepLabel
|
StepLabel,
|
||||||
|
Emoji
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -264,5 +264,10 @@ namespace Greenshot.Base.Interfaces
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provide access to the controls, this is for the EmojiContainer and needs to go.
|
||||||
|
/// </summary>
|
||||||
|
public Control.ControlCollection Controls { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
178
src/Greenshot.BuildTasks/EmojiDataTask.cs
Normal file
178
src/Greenshot.BuildTasks/EmojiDataTask.cs
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Greenshot.Editor.Controls.Emoji;
|
||||||
|
using SixLabors.Fonts.Unicode;
|
||||||
|
using Task = Microsoft.Build.Utilities.Task;
|
||||||
|
using System.Text;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using Microsoft.Build.Framework;
|
||||||
|
|
||||||
|
namespace Greenshot.BuildTasks;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A custom task to generate the emoji data we need for the picker.
|
||||||
|
/// This is based upon code from Sam Hocevar, the license is here:
|
||||||
|
/// Emoji.Wpf — Emoji support for WPF
|
||||||
|
///
|
||||||
|
/// Copyright © 2017—2021 Sam Hocevar <sam@hocevar.net>
|
||||||
|
///
|
||||||
|
/// This library is free software. It comes without any warranty, to
|
||||||
|
/// the extent permitted by applicable law. You can redistribute it
|
||||||
|
/// and/or modify it under the terms of the Do What the Fuck You Want
|
||||||
|
/// to Public License, Version 2, as published by the WTFPL Task Force.
|
||||||
|
/// See http://www.wtfpl.net/ for more details.
|
||||||
|
/// </summary>
|
||||||
|
public class EmojiDataTask : Task
|
||||||
|
{
|
||||||
|
private static readonly Regex MatchGroup = new(@"^# group: (.*)", RegexOptions.Compiled);
|
||||||
|
private static readonly Regex MatchSubgroup = new(@"^# subgroup: (.*)", RegexOptions.Compiled);
|
||||||
|
private static readonly Regex MatchSequence = new(@"^([0-9a-fA-F ]+[0-9a-fA-F]).*; *([-a-z]*) *# [^ ]* (E[0-9.]* )?(.*)", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
private static string ToColonSyntax(string s) => Regex.Replace(s.Trim().ToLowerInvariant(), "[^a-z0-9]+", "-");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the output file
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public string OutputFilename { get; set; }
|
||||||
|
|
||||||
|
//The name of the namespace where the class is going to be generated
|
||||||
|
[Required]
|
||||||
|
public string EmojiTestTxtFile { get; set; }
|
||||||
|
|
||||||
|
public override bool Execute()
|
||||||
|
{
|
||||||
|
var data = ParseEmojiList(EmojiTestTxtFile);
|
||||||
|
if (!data.Groups.Any())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Log.LogMessage($"Creating file {OutputFilename}");
|
||||||
|
var x = new XmlSerializer(typeof(Emojis));
|
||||||
|
using var writer = new XmlTextWriter(OutputFilename, Encoding.UTF8);
|
||||||
|
x.Serialize(writer, data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Emojis ParseEmojiList(string emojiTestTxtFile)
|
||||||
|
{
|
||||||
|
var result = new Emojis();
|
||||||
|
var lookupByName = new Dictionary<string, Emojis.Emoji>();
|
||||||
|
var qualifiedLut = new Dictionary<string, string>();
|
||||||
|
Emojis.Group currentGroup = null;
|
||||||
|
Emojis.Group currentSubgroup = null;
|
||||||
|
|
||||||
|
foreach (var line in ReadLines(emojiTestTxtFile))
|
||||||
|
{
|
||||||
|
var m = MatchGroup.Match(line);
|
||||||
|
if (m.Success)
|
||||||
|
{
|
||||||
|
currentGroup = new Emojis.Group { Name = m.Groups[1].ToString() };
|
||||||
|
result.Groups.Add(currentGroup);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m = MatchSubgroup.Match(line);
|
||||||
|
if (m.Success)
|
||||||
|
{
|
||||||
|
currentSubgroup = new Emojis.Group { Name = m.Groups[1].ToString() };
|
||||||
|
currentGroup?.SubGroups?.Add(currentSubgroup);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m = MatchSequence.Match(line);
|
||||||
|
if (!m.Success)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
string sequence = m.Groups[1].ToString();
|
||||||
|
string name = m.Groups[4].ToString();
|
||||||
|
|
||||||
|
string text = string.Join("", sequence.Split(' ').Select(c => char.ConvertFromUtf32(Convert.ToInt32(c, 16))));
|
||||||
|
|
||||||
|
// If there is already a differently-qualified version of this character, skip it.
|
||||||
|
// FIXME: this only works well if fully-qualified appears first in the list.
|
||||||
|
var unqualified = text.Replace("\ufe0f", "");
|
||||||
|
if (qualifiedLut.ContainsKey(unqualified))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix simple fully-qualified emojis
|
||||||
|
if (CodePoint.GetCodePointCount(text.AsSpan()) == 2)
|
||||||
|
{
|
||||||
|
text = text.TrimEnd('\ufe0f');
|
||||||
|
}
|
||||||
|
|
||||||
|
qualifiedLut[unqualified] = text;
|
||||||
|
|
||||||
|
var emoji = new Emojis.Emoji { Text = text };
|
||||||
|
|
||||||
|
lookupByName[ToColonSyntax(name)] = emoji;
|
||||||
|
|
||||||
|
// Get the left part of the name and check whether we’re a variation of an existing
|
||||||
|
// emoji. If so, append to that emoji. Otherwise, add to current subgroup.
|
||||||
|
// FIXME: does not work properly because variations can appear before the generic emoji
|
||||||
|
if (name.Contains(":") && lookupByName.TryGetValue(ToColonSyntax(name.Split(':')[0]), out var parentEmoji))
|
||||||
|
{
|
||||||
|
parentEmoji.Variations.Add(emoji);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentSubgroup?.Emojis?.Add(emoji);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the Component group. Not sure we want to have the skin tones in the picker.
|
||||||
|
result.Groups.RemoveAll(g => g.Name == "Component");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This reads the specified file into lines
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="file">string</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="FileNotFoundException"></exception>
|
||||||
|
private static IEnumerable<string> ReadLines(string file)
|
||||||
|
{
|
||||||
|
if (!File.Exists(file))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException($"Can't find {file}");
|
||||||
|
}
|
||||||
|
|
||||||
|
using var stream = new FileStream(file, FileMode.Open, FileAccess.Read);
|
||||||
|
using var reader = new StreamReader(stream, Encoding.UTF8);
|
||||||
|
while (reader.ReadLine() is { } line)
|
||||||
|
{
|
||||||
|
yield return line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
src/Greenshot.BuildTasks/Greenshot.BuildTasks.csproj
Normal file
17
src/Greenshot.BuildTasks/Greenshot.BuildTasks.csproj
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||||
|
<PropertyGroup>
|
||||||
|
<DefineConstants>$(DefineConstants);GREENSHOT_BUILDTASKS</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Build.Utilities.Core" version="17.3.2">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<ExcludeAssets>runtime</ExcludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" version="1.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Greenshot.Editor\Controls\Emoji\Emojis.cs">
|
||||||
|
<Link>Emojis.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
69
src/Greenshot.Editor/Controls/Emoji/ChunkHelper.cs
Normal file
69
src/Greenshot.Editor/Controls/Emoji/ChunkHelper.cs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* 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.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Greenshot.Editor.Controls.Emoji;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This seems to enumerate multiple IEnumerables.
|
||||||
|
/// TODO: Replace this with LINQ
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
internal sealed class ChunkHelper<T> : IEnumerable<IEnumerable<T>>
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<T> _elements;
|
||||||
|
private readonly int _size;
|
||||||
|
private bool _hasMore;
|
||||||
|
|
||||||
|
public ChunkHelper(IEnumerable<T> elements, int size)
|
||||||
|
{
|
||||||
|
_elements = elements;
|
||||||
|
_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<IEnumerable<T>> GetEnumerator()
|
||||||
|
{
|
||||||
|
using var enumerator = _elements.GetEnumerator();
|
||||||
|
_hasMore = enumerator.MoveNext();
|
||||||
|
while (_hasMore)
|
||||||
|
{
|
||||||
|
yield return GetNextBatch(enumerator).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<T> GetNextBatch(IEnumerator<T> enumerator)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _size; ++i)
|
||||||
|
{
|
||||||
|
yield return enumerator.Current;
|
||||||
|
_hasMore = enumerator.MoveNext();
|
||||||
|
if (!_hasMore)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
}
|
54
src/Greenshot.Editor/Controls/Emoji/EmojiControl.cs
Normal file
54
src/Greenshot.Editor/Controls/Emoji/EmojiControl.cs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Greenshot - a free and open source screenshot tool
|
||||||
|
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel
|
||||||
|
*
|
||||||
|
* 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.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using Greenshot.Editor.Drawing.Emoji;
|
||||||
|
|
||||||
|
namespace Greenshot.Editor.Controls.Emoji
|
||||||
|
{
|
||||||
|
internal class EmojiControl : Image
|
||||||
|
{
|
||||||
|
public static readonly DependencyProperty EmojiProperty = DependencyProperty.Register("Emoji", typeof(string), typeof(EmojiControl), new PropertyMetadata(default(string), OnEmojiPropertyChanged));
|
||||||
|
|
||||||
|
private static void OnEmojiPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
((EmojiControl)d).Source = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Emoji
|
||||||
|
{
|
||||||
|
get => (string)GetValue(EmojiProperty);
|
||||||
|
set => SetValue(EmojiProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnRender(DrawingContext dc)
|
||||||
|
{
|
||||||
|
if (Source == null && !string.IsNullOrEmpty(Emoji))
|
||||||
|
{
|
||||||
|
Source = EmojiRenderer.GetBitmapSource(Emoji, iconSize: 48);
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnRender(dc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
src/Greenshot.Editor/Controls/Emoji/EmojiData.cs
Normal file
53
src/Greenshot.Editor/Controls/Emoji/EmojiData.cs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using Greenshot.Base.Core;
|
||||||
|
|
||||||
|
namespace Greenshot.Editor.Controls.Emoji
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class processes the emoji-test.txt extract, as was generated in a build task, so it can show a list of possible emoji depending on skin tone and hairstyle.
|
||||||
|
/// </summary>
|
||||||
|
public static class EmojiData
|
||||||
|
{
|
||||||
|
private static readonly string EmojisXmlFilePath = Path.Combine(EnvironmentInfo.GetApplicationFolder(), "emojis.xml");
|
||||||
|
|
||||||
|
public static Emojis Data { get; private set; } = new();
|
||||||
|
|
||||||
|
public static void Load()
|
||||||
|
{
|
||||||
|
var x = new XmlSerializer(typeof(Emojis));
|
||||||
|
|
||||||
|
if (File.Exists(EmojisXmlFilePath))
|
||||||
|
{
|
||||||
|
Data = (Emojis)x.Deserialize(new XmlTextReader(EmojisXmlFilePath));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotSupportedException($"Missing {EmojisXmlFilePath}, can't load ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
140
src/Greenshot.Editor/Controls/Emoji/EmojiPicker.xaml
Normal file
140
src/Greenshot.Editor/Controls/Emoji/EmojiPicker.xaml
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
<StackPanel x:Class="Greenshot.Editor.Controls.Emoji.EmojiPicker"
|
||||||
|
x:Name="StackPanel_INTERNAL"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:emoji="clr-namespace:Greenshot.Editor.Controls.Emoji"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
|
||||||
|
<StackPanel.Resources>
|
||||||
|
|
||||||
|
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
|
||||||
|
|
||||||
|
<ControlTemplate x:Key="VariationPopupTemplate" TargetType="ContentControl">
|
||||||
|
<ListView Name="VariationListView" ItemsSource="{Binding AllVariations}"
|
||||||
|
BorderThickness="1" Height="Auto" Padding="0" Margin="1"
|
||||||
|
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||||
|
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||||
|
MaxWidth="280">
|
||||||
|
<!-- 6 columns -->
|
||||||
|
<ListView.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<WrapPanel/>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ListView.ItemsPanel>
|
||||||
|
<ListView.ItemContainerStyle>
|
||||||
|
<Style TargetType="{x:Type ListViewItem}">
|
||||||
|
<Setter Property="Padding" Value="0"/>
|
||||||
|
<Setter Property="Margin" Value="0"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
</Style>
|
||||||
|
</ListView.ItemContainerStyle>
|
||||||
|
<ListView.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Button Background="Transparent" BorderBrush="Transparent"
|
||||||
|
Margin="0" Click="OnEmojiPicked"
|
||||||
|
Width="40" Height="40">
|
||||||
|
<emoji:EmojiControl Height="24" Emoji="{Binding Path=Text}" />
|
||||||
|
</Button>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListView.ItemTemplate>
|
||||||
|
</ListView>
|
||||||
|
</ControlTemplate>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="CellTemplate">
|
||||||
|
<ToggleButton Width="40" Height="40" Margin="0" Padding="2"
|
||||||
|
x:Name="VariationButton" Background="Transparent" BorderBrush="Transparent"
|
||||||
|
Click="OnEmojiPicked" Focusable="False" >
|
||||||
|
<Grid>
|
||||||
|
<emoji:EmojiControl Height="24" Margin="4" Emoji="{Binding Path=Text}"
|
||||||
|
VerticalAlignment="Center" HorizontalAlignment="Center"/>
|
||||||
|
<Polygon Visibility="{Binding HasVariations, Converter={StaticResource BoolToVis}}"
|
||||||
|
Width="6" Height="6" VerticalAlignment="Bottom" HorizontalAlignment="Right"
|
||||||
|
Stretch="Fill" Points="0,1 1,1 1,0" Fill="Black"/>
|
||||||
|
<Popup x:Name="VariationPopup" StaysOpen="False" AllowsTransparency="True" Margin="0"
|
||||||
|
IsOpen="{Binding IsChecked, ElementName=VariationButton, Mode=TwoWay}">
|
||||||
|
<ContentControl Template="{StaticResource ResourceKey=VariationPopupTemplate}"/>
|
||||||
|
</Popup>
|
||||||
|
</Grid>
|
||||||
|
</ToggleButton>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
</StackPanel.Resources>
|
||||||
|
|
||||||
|
<ToggleButton x:Name="Button_INTERNAL">
|
||||||
|
<emoji:EmojiControl x:Name="Image" Emoji="{Binding ElementName=StackPanel_INTERNAL, Mode=OneWay, Path=Selection, UpdateSourceTrigger=PropertyChanged}"/>
|
||||||
|
</ToggleButton>
|
||||||
|
|
||||||
|
<Popup IsOpen="{Binding IsChecked, ElementName=Button_INTERNAL, Mode=TwoWay}"
|
||||||
|
StaysOpen="False"
|
||||||
|
KeyDown="OnPopupKeyDown" Loaded="OnPopupLoaded"
|
||||||
|
AllowsTransparency="True" Margin="0">
|
||||||
|
<Border>
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<TabControl ItemsSource="{Binding EmojiGroups, ElementName=StackPanel_INTERNAL}" Padding="0">
|
||||||
|
<TabControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Grid>
|
||||||
|
<emoji:EmojiControl Height="24" Emoji="{Binding Icon}" >
|
||||||
|
<Image.ToolTip>
|
||||||
|
<TextBlock Text="{Binding Name}"/>
|
||||||
|
</Image.ToolTip>
|
||||||
|
</emoji:EmojiControl>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</TabControl.ItemTemplate>
|
||||||
|
<TabControl.ContentTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<ListView Name="EmojiListView" ItemsSource="{Binding EmojiChunkList}"
|
||||||
|
BorderThickness="0"
|
||||||
|
MaxHeight="320" Height="Auto" Padding="0" Margin="1"
|
||||||
|
VirtualizingStackPanel.IsVirtualizing="True"
|
||||||
|
VirtualizingStackPanel.VirtualizationMode="Recycling"
|
||||||
|
ScrollViewer.VerticalScrollBarVisibility="Visible"
|
||||||
|
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||||
|
<ListView.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<VirtualizingStackPanel />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ListView.ItemsPanel>
|
||||||
|
<ListView.ItemContainerStyle>
|
||||||
|
<Style TargetType="{x:Type ListViewItem}">
|
||||||
|
<!-- Get rid of the ugly padding and margin in default ListViewItem -->
|
||||||
|
<Setter Property="Padding" Value="0"/>
|
||||||
|
<Setter Property="Margin" Value="0"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<!-- Remove the mouse over highlight -->
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="{x:Type ListViewItem}">
|
||||||
|
<ContentPresenter/>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</ListView.ItemContainerStyle>
|
||||||
|
<!-- Now for our actual content -->
|
||||||
|
<ListView.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<ItemsControl ItemsSource="{Binding}"
|
||||||
|
ItemTemplate="{StaticResource ResourceKey=CellTemplate}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel Orientation="Horizontal"/>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
</ItemsControl>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListView.ItemTemplate>
|
||||||
|
</ListView>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</TabControl.ContentTemplate>
|
||||||
|
</TabControl>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Popup>
|
||||||
|
</StackPanel>
|
134
src/Greenshot.Editor/Controls/Emoji/EmojiPicker.xaml.cs
Normal file
134
src/Greenshot.Editor/Controls/Emoji/EmojiPicker.xaml.cs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
//
|
||||||
|
// Emoji.Wpf — Emoji support for WPF
|
||||||
|
//
|
||||||
|
// Copyright © 2017—2021 Sam Hocevar <sam@hocevar.net>
|
||||||
|
//
|
||||||
|
// This program is free software. It comes without any warranty, to
|
||||||
|
// the extent permitted by applicable law. You can redistribute it
|
||||||
|
// and/or modify it under the terms of the Do What the Fuck You Want
|
||||||
|
// to Public License, Version 2, as published by the WTFPL Task Force.
|
||||||
|
// See http://www.wtfpl.net/ for more details.
|
||||||
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace Greenshot.Editor.Controls.Emoji;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The event which is created when the emoji is picked
|
||||||
|
/// </summary>
|
||||||
|
public class EmojiPickedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public EmojiPickedEventArgs() { }
|
||||||
|
public EmojiPickedEventArgs(string emoji) => Emoji = emoji;
|
||||||
|
|
||||||
|
public string Emoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void EmojiPickedEventHandler(object sender, EmojiPickedEventArgs e);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for Picker.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class EmojiPicker : StackPanel
|
||||||
|
{
|
||||||
|
public EmojiPicker()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IList<Emojis.Group> EmojiGroups => EmojiData.Data.Groups;
|
||||||
|
|
||||||
|
// Backwards compatibility for when the backend was a TextBlock.
|
||||||
|
public double FontSize
|
||||||
|
{
|
||||||
|
get => Image.Height * 0.75;
|
||||||
|
set => Image.Height = value / 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler SelectionChanged;
|
||||||
|
|
||||||
|
public event EmojiPickedEventHandler Picked;
|
||||||
|
|
||||||
|
private static void OnSelectionPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
(source as EmojiPicker)?.OnSelectionChanged(e.NewValue as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Selection
|
||||||
|
{
|
||||||
|
get => (string)GetValue(SelectionProperty);
|
||||||
|
set => SetValue(SelectionProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSelectionChanged(string s)
|
||||||
|
{
|
||||||
|
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 not Control { DataContext: Emojis.Emoji emoji })
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emoji.Variations.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(
|
||||||
|
nameof(Selection), typeof(string), typeof(EmojiPicker),
|
||||||
|
new FrameworkPropertyMetadata("☺", OnSelectionPropertyChanged));
|
||||||
|
|
||||||
|
private void OnPopupKeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
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 not Popup popup) return;
|
||||||
|
|
||||||
|
var child = popup.Child;
|
||||||
|
IInputElement oldFocus = null;
|
||||||
|
child.Focusable = true;
|
||||||
|
child.IsVisibleChanged += (o, ea) =>
|
||||||
|
{
|
||||||
|
if (!child.IsVisible) return;
|
||||||
|
oldFocus = Keyboard.FocusedElement;
|
||||||
|
Keyboard.Focus(child);
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
76
src/Greenshot.Editor/Controls/Emoji/Emojis.cs
Normal file
76
src/Greenshot.Editor/Controls/Emoji/Emojis.cs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace Greenshot.Editor.Controls.Emoji;
|
||||||
|
|
||||||
|
[XmlRoot("Es")]
|
||||||
|
public class Emojis
|
||||||
|
{
|
||||||
|
[XmlArray(ElementName = "Gs")]
|
||||||
|
public List<Group> Groups { get; set; } = new();
|
||||||
|
|
||||||
|
[XmlType("G")]
|
||||||
|
public class Group
|
||||||
|
{
|
||||||
|
[XmlAttribute(AttributeName= "N")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[XmlArray(ElementName = "Sg")]
|
||||||
|
public List<Group> SubGroups { get; set; } = new();
|
||||||
|
|
||||||
|
[XmlArray(ElementName = "Es")]
|
||||||
|
public List<Emoji> Emojis { get; set; } = new();
|
||||||
|
|
||||||
|
#if !GREENSHOT_BUILDTASKS
|
||||||
|
public IEnumerable<IEnumerable<Emoji>> EmojiChunkList => new ChunkHelper<Emoji>(EmojiList, 8);
|
||||||
|
|
||||||
|
public string Icon => SubGroups.FirstOrDefault()?.Emojis.FirstOrDefault()?.Text;
|
||||||
|
|
||||||
|
public IEnumerable<Emoji> EmojiList => SubGroups.SelectMany(s => s.Emojis);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[XmlType("E")]
|
||||||
|
public class Emoji
|
||||||
|
{
|
||||||
|
[XmlAttribute(AttributeName = "T")]
|
||||||
|
public string Text { get; set; }
|
||||||
|
|
||||||
|
[XmlArray(ElementName = "V")]
|
||||||
|
public List<Emoji> Variations { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Xml trick so that the Xml serializer does not output the 'Variations' element when the emoji has no variation
|
||||||
|
/// (see https://learn.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlserializer#controlling-generated-xml)
|
||||||
|
/// </summary>
|
||||||
|
[XmlIgnore]
|
||||||
|
public bool VariationsSpecified => HasVariations;
|
||||||
|
|
||||||
|
public bool HasVariations => Variations.Count > 0;
|
||||||
|
|
||||||
|
public IEnumerable<Emoji> AllVariations => HasVariations ? new[] { this }.Union(Variations) : Array.Empty<Emoji>();
|
||||||
|
}
|
||||||
|
}
|
4991
src/Greenshot.Editor/Controls/Emoji/emoji-test.txt
Normal file
4991
src/Greenshot.Editor/Controls/Emoji/emoji-test.txt
Normal file
File diff suppressed because it is too large
Load diff
179
src/Greenshot.Editor/Drawing/Emoji/EmojiContainer.cs
Normal file
179
src/Greenshot.Editor/Drawing/Emoji/EmojiContainer.cs
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.Windows.Forms.Integration;
|
||||||
|
using Greenshot.Base.Core;
|
||||||
|
using Greenshot.Base.Interfaces.Drawing;
|
||||||
|
using Greenshot.Editor.Controls.Emoji;
|
||||||
|
using Greenshot.Editor.Helpers;
|
||||||
|
using Image = System.Drawing.Image;
|
||||||
|
|
||||||
|
namespace Greenshot.Editor.Drawing.Emoji
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Description of EmojiContainer.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public sealed 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;
|
||||||
|
|
||||||
|
private string _emoji;
|
||||||
|
|
||||||
|
public string Emoji
|
||||||
|
{
|
||||||
|
get => _emoji;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_emoji = value;
|
||||||
|
ResetCachedBitmap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmojiContainer(Surface parent, string emoji, int size = 64) : base(parent)
|
||||||
|
{
|
||||||
|
Emoji = emoji;
|
||||||
|
Width = size;
|
||||||
|
Height = size;
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnDoubleClick()
|
||||||
|
{
|
||||||
|
ShowEmojiPicker();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowEmojiPicker()
|
||||||
|
{
|
||||||
|
_currentContainer = this;
|
||||||
|
|
||||||
|
GetOrCreatePickerControl();
|
||||||
|
|
||||||
|
var absRectangle = Bounds;
|
||||||
|
var displayRectangle = Parent.ToSurfaceCoordinates(absRectangle);
|
||||||
|
_emojiPickerHost.Width = 0; // Trick to hide the picker's button
|
||||||
|
_emojiPickerHost.Height = displayRectangle.Height;
|
||||||
|
_emojiPickerHost.Left = displayRectangle.Left;
|
||||||
|
_emojiPickerHost.Top = displayRectangle.Top;
|
||||||
|
|
||||||
|
_emojiPicker.Selection = Emoji;
|
||||||
|
_emojiPicker.ShowPopup(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetOrCreatePickerControl()
|
||||||
|
{
|
||||||
|
// Create one picker control by surface
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
_emojiPicker = new EmojiPicker();
|
||||||
|
_emojiPicker.Picked += (_, args) =>
|
||||||
|
{
|
||||||
|
_currentContainer.Emoji = args.Emoji;
|
||||||
|
_currentContainer.Invalidate();
|
||||||
|
};
|
||||||
|
|
||||||
|
_emojiPickerHost = new ElementHost
|
||||||
|
{
|
||||||
|
Dock = DockStyle.None,
|
||||||
|
Child = _emojiPicker,
|
||||||
|
Name = "EmojiPickerHost"
|
||||||
|
};
|
||||||
|
|
||||||
|
_parent.Controls.Add(_emojiPickerHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HideEmojiPicker()
|
||||||
|
{
|
||||||
|
_emojiPicker?.ShowPopup(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Make sure we register the property changed, to manage the state of the picker
|
||||||
|
/// </summary>
|
||||||
|
protected override void Init()
|
||||||
|
{
|
||||||
|
base.Init();
|
||||||
|
PropertyChanged += OnPropertyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <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 (!e.PropertyName.Equals(nameof(Selected)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Selected)
|
||||||
|
{
|
||||||
|
HideEmojiPicker();
|
||||||
|
}
|
||||||
|
else if (Status == EditStatus.IDLE && _justCreated)
|
||||||
|
{
|
||||||
|
// Show picker just after creation
|
||||||
|
ShowEmojiPicker();
|
||||||
|
_justCreated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Image ComputeBitmap()
|
||||||
|
{
|
||||||
|
var iconSize = Math.Min(Bounds.Width, Bounds.Height);
|
||||||
|
if (iconSize <= 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var image = EmojiRenderer.GetBitmap(Emoji, iconSize);
|
||||||
|
if (RotationAngle != 0)
|
||||||
|
{
|
||||||
|
var newImage = image.Rotate(RotationAngle);
|
||||||
|
image.Dispose();
|
||||||
|
return newImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScaleOptions GetScaleOptions()
|
||||||
|
{
|
||||||
|
return ScaleOptions.Rational;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
123
src/Greenshot.Editor/Drawing/Emoji/EmojiRenderer.cs
Normal file
123
src/Greenshot.Editor/Drawing/Emoji/EmojiRenderer.cs
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Greenshot - a free and open source screenshot tool
|
||||||
|
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Reflection;
|
||||||
|
using Greenshot.Base.Core;
|
||||||
|
using SixLabors.Fonts;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.Drawing.Processing;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
|
||||||
|
namespace Greenshot.Editor.Drawing.Emoji
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This will render Emoji
|
||||||
|
/// </summary>
|
||||||
|
internal static class EmojiRenderer
|
||||||
|
{
|
||||||
|
private static readonly FontCollection TwemojiFontCollection = new();
|
||||||
|
|
||||||
|
private static readonly Lazy<FontFamily> TwemojiFontFamily = new(() =>
|
||||||
|
{
|
||||||
|
var twemojiFontFile = Path.Combine(EnvironmentInfo.GetApplicationFolder(), "Twemoji.Mozilla.ttf");
|
||||||
|
if (!File.Exists(twemojiFontFile))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException($"Can't find {twemojiFontFile}, bad installation?");
|
||||||
|
}
|
||||||
|
|
||||||
|
using var fileStream = new FileStream(twemojiFontFile, FileMode.Open, FileAccess.Read);
|
||||||
|
TwemojiFontCollection.Add(fileStream);
|
||||||
|
TwemojiFontCollection.TryGet("Twemoji Mozilla", out var fontFamily);
|
||||||
|
|
||||||
|
return fontFamily;
|
||||||
|
});
|
||||||
|
|
||||||
|
public static Image<Rgba32> GetImage(string emoji, int iconSize)
|
||||||
|
{
|
||||||
|
var image = new Image<Rgba32>(iconSize, iconSize);
|
||||||
|
|
||||||
|
RenderEmoji(emoji, iconSize, image);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RenderEmoji(string emoji, int iconSize, Image image)
|
||||||
|
{
|
||||||
|
var fontFamily = TwemojiFontFamily.Value;
|
||||||
|
var font = fontFamily.CreateFont(iconSize * 0.95f, FontStyle.Regular);
|
||||||
|
var verticalOffset = font.Size * 0.045f;
|
||||||
|
var textOptions = new RichTextOptions(font)
|
||||||
|
{
|
||||||
|
Origin = new PointF(iconSize / 2.0f, iconSize / 2.0f + verticalOffset),
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Center,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
image.Mutate(x => x.DrawText(textOptions, emoji, Color.Black));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static System.Drawing.Image GetBitmap(string emoji, int iconSize)
|
||||||
|
{
|
||||||
|
int width = iconSize;
|
||||||
|
int height = iconSize;
|
||||||
|
|
||||||
|
var bitmap = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||||
|
var bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, width, height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
var image = Image.WrapMemory<Bgra32>((void*)bitmapData.Scan0, width: width, height: height);
|
||||||
|
RenderEmoji(emoji, iconSize, image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
bitmap.UnlockBits(bitmapData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static System.Windows.Media.Imaging.BitmapSource GetBitmapSource(string emoji, int iconSize)
|
||||||
|
{
|
||||||
|
var pixelFormat = System.Windows.Media.PixelFormats.Bgra32;
|
||||||
|
int width = iconSize;
|
||||||
|
int height = iconSize;
|
||||||
|
int stride = (width * pixelFormat.BitsPerPixel + 7) / 8;
|
||||||
|
byte[] pixels = new byte[stride * height];
|
||||||
|
|
||||||
|
var image = Image.WrapMemory<Bgra32>(byteMemory: pixels, width: width, height: height);
|
||||||
|
|
||||||
|
RenderEmoji(emoji, iconSize, image);
|
||||||
|
|
||||||
|
var source = System.Windows.Media.Imaging.BitmapSource.Create(pixelWidth: width, pixelHeight: height, dpiX: 96, dpiY: 96, pixelFormat: pixelFormat, palette: null, pixels: pixels, stride: stride);
|
||||||
|
source.Freeze();
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
src/Greenshot.Editor/Drawing/Emoji/Twemoji.Mozilla.ttf
Normal file
BIN
src/Greenshot.Editor/Drawing/Emoji/Twemoji.Mozilla.ttf
Normal file
Binary file not shown.
|
@ -181,6 +181,12 @@ namespace Greenshot.Editor.Drawing
|
||||||
/// <param name="matrix"></param>
|
/// <param name="matrix"></param>
|
||||||
public override void Transform(Matrix matrix)
|
public override void Transform(Matrix matrix)
|
||||||
{
|
{
|
||||||
|
if (_image == null)
|
||||||
|
{
|
||||||
|
base.Transform(matrix);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int rotateAngle = CalculateAngle(matrix);
|
int rotateAngle = CalculateAngle(matrix);
|
||||||
// we currently assume only one transformation has been made.
|
// we currently assume only one transformation has been made.
|
||||||
if (rotateAngle != 0)
|
if (rotateAngle != 0)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* Greenshot - a free and open source screenshot tool
|
* Greenshot - a free and open source screenshot tool
|
||||||
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
|
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
|
||||||
*
|
*
|
||||||
|
@ -40,6 +40,7 @@ using Greenshot.Base.Interfaces;
|
||||||
using Greenshot.Base.Interfaces.Drawing;
|
using Greenshot.Base.Interfaces.Drawing;
|
||||||
using Greenshot.Base.Interfaces.Drawing.Adorners;
|
using Greenshot.Base.Interfaces.Drawing.Adorners;
|
||||||
using Greenshot.Editor.Configuration;
|
using Greenshot.Editor.Configuration;
|
||||||
|
using Greenshot.Editor.Drawing.Emoji;
|
||||||
using Greenshot.Editor.Drawing.Fields;
|
using Greenshot.Editor.Drawing.Fields;
|
||||||
using Greenshot.Editor.Helpers;
|
using Greenshot.Editor.Helpers;
|
||||||
using Greenshot.Editor.Memento;
|
using Greenshot.Editor.Memento;
|
||||||
|
@ -798,6 +799,9 @@ namespace Greenshot.Editor.Drawing
|
||||||
case DrawingModes.None:
|
case DrawingModes.None:
|
||||||
_undrawnElement = null;
|
_undrawnElement = null;
|
||||||
break;
|
break;
|
||||||
|
case DrawingModes.Emoji:
|
||||||
|
_undrawnElement = new EmojiContainer(this, "🙂");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_undrawnElement != null)
|
if (_undrawnElement != null)
|
||||||
|
@ -852,6 +856,15 @@ namespace Greenshot.Editor.Drawing
|
||||||
return iconContainer;
|
return iconContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEmojiContainer AddEmojiContainer(string emoji, int x, int y, int size)
|
||||||
|
{
|
||||||
|
var iconContainer = new EmojiContainer(this, emoji, size);
|
||||||
|
iconContainer.Left = x;
|
||||||
|
iconContainer.Top = y;
|
||||||
|
AddElement(iconContainer);
|
||||||
|
return iconContainer;
|
||||||
|
}
|
||||||
|
|
||||||
public ICursorContainer AddCursorContainer(Cursor cursor, int x, int y)
|
public ICursorContainer AddCursorContainer(Cursor cursor, int x, int y)
|
||||||
{
|
{
|
||||||
CursorContainer cursorContainer = new CursorContainer(this)
|
CursorContainer cursorContainer = new CursorContainer(this)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* Greenshot - a free and open source screenshot tool
|
* Greenshot - a free and open source screenshot tool
|
||||||
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
|
* Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
|
||||||
*
|
*
|
||||||
|
@ -22,8 +22,10 @@
|
||||||
using Greenshot.Base.Controls;
|
using Greenshot.Base.Controls;
|
||||||
using Greenshot.Editor.Controls;
|
using Greenshot.Editor.Controls;
|
||||||
using Greenshot.Editor.Drawing;
|
using Greenshot.Editor.Drawing;
|
||||||
|
using Greenshot.Editor.Drawing.Emoji;
|
||||||
|
|
||||||
namespace Greenshot.Editor.Forms {
|
namespace Greenshot.Editor.Forms
|
||||||
|
{
|
||||||
partial class ImageEditorForm {
|
partial class ImageEditorForm {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Designer variable used to keep track of non-visual components.
|
/// Designer variable used to keep track of non-visual components.
|
||||||
|
@ -69,6 +71,7 @@ namespace Greenshot.Editor.Forms {
|
||||||
this.btnText = new GreenshotToolStripButton();
|
this.btnText = new GreenshotToolStripButton();
|
||||||
this.btnSpeechBubble = new GreenshotToolStripButton();
|
this.btnSpeechBubble = new GreenshotToolStripButton();
|
||||||
this.btnStepLabel = new GreenshotToolStripButton();
|
this.btnStepLabel = new GreenshotToolStripButton();
|
||||||
|
this.btnEmoji = new GreenshotToolStripButton();
|
||||||
this.toolStripSeparator14 = new System.Windows.Forms.ToolStripSeparator();
|
this.toolStripSeparator14 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
this.btnHighlight = new GreenshotToolStripButton();
|
this.btnHighlight = new GreenshotToolStripButton();
|
||||||
this.btnObfuscate = new GreenshotToolStripButton();
|
this.btnObfuscate = new GreenshotToolStripButton();
|
||||||
|
@ -336,6 +339,7 @@ namespace Greenshot.Editor.Forms {
|
||||||
this.btnText,
|
this.btnText,
|
||||||
this.btnSpeechBubble,
|
this.btnSpeechBubble,
|
||||||
this.btnStepLabel,
|
this.btnStepLabel,
|
||||||
|
this.btnEmoji,
|
||||||
this.toolStripSeparator14,
|
this.toolStripSeparator14,
|
||||||
this.btnHighlight,
|
this.btnHighlight,
|
||||||
this.btnObfuscate,
|
this.btnObfuscate,
|
||||||
|
@ -446,6 +450,16 @@ namespace Greenshot.Editor.Forms {
|
||||||
this.btnStepLabel.Name = "btnStepLabel";
|
this.btnStepLabel.Name = "btnStepLabel";
|
||||||
this.btnStepLabel.Click += new System.EventHandler(this.BtnStepLabelClick);
|
this.btnStepLabel.Click += new System.EventHandler(this.BtnStepLabelClick);
|
||||||
//
|
//
|
||||||
|
// btnStepEmoji
|
||||||
|
//
|
||||||
|
this.btnEmoji.CheckOnClick = true;
|
||||||
|
this.btnEmoji.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
|
||||||
|
this.btnEmoji.Image = EmojiRenderer.GetBitmap("🙂", 32);
|
||||||
|
this.btnEmoji.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||||
|
this.btnEmoji.Text = "Emoji (M)";
|
||||||
|
this.btnEmoji.Name = "btnEmoji";
|
||||||
|
this.btnEmoji.Click += new System.EventHandler(this.BtnEmojiClick);
|
||||||
|
//
|
||||||
// toolStripSeparator14
|
// toolStripSeparator14
|
||||||
//
|
//
|
||||||
this.toolStripSeparator14.Name = "toolStripSeparator14";
|
this.toolStripSeparator14.Name = "toolStripSeparator14";
|
||||||
|
@ -1976,6 +1990,7 @@ namespace Greenshot.Editor.Forms {
|
||||||
private GreenshotToolStripButton btnText;
|
private GreenshotToolStripButton btnText;
|
||||||
private GreenshotToolStripButton btnSpeechBubble;
|
private GreenshotToolStripButton btnSpeechBubble;
|
||||||
private GreenshotToolStripButton btnStepLabel;
|
private GreenshotToolStripButton btnStepLabel;
|
||||||
|
private GreenshotToolStripButton btnEmoji;
|
||||||
private GreenshotToolStripMenuItem drawLineToolStripMenuItem;
|
private GreenshotToolStripMenuItem drawLineToolStripMenuItem;
|
||||||
private GreenshotToolStripButton btnLine;
|
private GreenshotToolStripButton btnLine;
|
||||||
private GreenshotToolStripButton btnSettings;
|
private GreenshotToolStripButton btnSettings;
|
||||||
|
|
|
@ -27,6 +27,7 @@ using System.Drawing.Imaging;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Dapplo.Windows.Common.Extensions;
|
using Dapplo.Windows.Common.Extensions;
|
||||||
using Dapplo.Windows.Common.Structs;
|
using Dapplo.Windows.Common.Structs;
|
||||||
|
@ -44,6 +45,7 @@ using Greenshot.Base.Interfaces;
|
||||||
using Greenshot.Base.Interfaces.Drawing;
|
using Greenshot.Base.Interfaces.Drawing;
|
||||||
using Greenshot.Base.Interfaces.Forms;
|
using Greenshot.Base.Interfaces.Forms;
|
||||||
using Greenshot.Editor.Configuration;
|
using Greenshot.Editor.Configuration;
|
||||||
|
using Greenshot.Editor.Controls.Emoji;
|
||||||
using Greenshot.Editor.Destinations;
|
using Greenshot.Editor.Destinations;
|
||||||
using Greenshot.Editor.Drawing;
|
using Greenshot.Editor.Drawing;
|
||||||
using Greenshot.Editor.Drawing.Fields;
|
using Greenshot.Editor.Drawing.Fields;
|
||||||
|
@ -142,6 +144,9 @@ namespace Greenshot.Editor.Forms
|
||||||
|
|
||||||
private void Initialize(ISurface surface, bool outputMade)
|
private void Initialize(ISurface surface, bool outputMade)
|
||||||
{
|
{
|
||||||
|
// Compute emojis in background
|
||||||
|
EmojiData.Load();
|
||||||
|
|
||||||
EditorList.Add(this);
|
EditorList.Add(this);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -285,7 +290,7 @@ namespace Greenshot.Editor.Forms
|
||||||
|
|
||||||
_toolbarButtons = new[]
|
_toolbarButtons = new[]
|
||||||
{
|
{
|
||||||
btnCursor, btnRect, btnEllipse, btnText, btnLine, btnArrow, btnFreehand, btnHighlight, btnObfuscate, btnCrop, btnStepLabel, btnSpeechBubble
|
btnCursor, btnRect, btnEllipse, btnText, btnLine, btnArrow, btnFreehand, btnHighlight, btnObfuscate, btnCrop, btnStepLabel, btnSpeechBubble, btnEmoji
|
||||||
};
|
};
|
||||||
//toolbarDropDownButtons = new ToolStripDropDownButton[]{btnBlur, btnPixeliate, btnTextHighlighter, btnAreaHighlighter, btnMagnifier};
|
//toolbarDropDownButtons = new ToolStripDropDownButton[]{btnBlur, btnPixeliate, btnTextHighlighter, btnAreaHighlighter, btnMagnifier};
|
||||||
|
|
||||||
|
@ -631,6 +636,9 @@ namespace Greenshot.Editor.Forms
|
||||||
case DrawingModes.Path:
|
case DrawingModes.Path:
|
||||||
SetButtonChecked(btnFreehand);
|
SetButtonChecked(btnFreehand);
|
||||||
break;
|
break;
|
||||||
|
case DrawingModes.Emoji:
|
||||||
|
SetButtonChecked(btnEmoji);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -707,6 +715,12 @@ namespace Greenshot.Editor.Forms
|
||||||
RefreshFieldControls();
|
RefreshFieldControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BtnEmojiClick(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
_surface.DrawingMode = DrawingModes.Emoji;
|
||||||
|
RefreshFieldControls();
|
||||||
|
}
|
||||||
|
|
||||||
private void BtnLineClick(object sender, EventArgs e)
|
private void BtnLineClick(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
_surface.DrawingMode = DrawingModes.Line;
|
_surface.DrawingMode = DrawingModes.Line;
|
||||||
|
@ -1023,6 +1037,9 @@ namespace Greenshot.Editor.Forms
|
||||||
case Keys.C:
|
case Keys.C:
|
||||||
BtnCropClick(sender, e);
|
BtnCropClick(sender, e);
|
||||||
break;
|
break;
|
||||||
|
case Keys.M:
|
||||||
|
BtnEmojiClick(sender, e);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (e.Modifiers.Equals(Keys.Control))
|
else if (e.Modifiers.Equals(Keys.Control))
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Greenshot.Base\Greenshot.Base.csproj" />
|
<ProjectReference Include="..\Greenshot.Base\Greenshot.Base.csproj" />
|
||||||
|
@ -84,4 +87,11 @@
|
||||||
<DependentUpon>ImageEditorForm.cs</DependentUpon>
|
<DependentUpon>ImageEditorForm.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ContentWithTargetPath Include="Drawing\Emoji\Twemoji.Mozilla.ttf">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
<TargetPath>Twemoji.Mozilla.ttf</TargetPath>
|
||||||
|
</ContentWithTargetPath>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -202,7 +202,7 @@ namespace Greenshot.Editor.Helpers
|
||||||
{
|
{
|
||||||
// scaled rectangle (ratio) would be taller than original
|
// scaled rectangle (ratio) would be taller than original
|
||||||
// keep width and tweak height to maintain aspect ratio
|
// keep width and tweak height to maintain aspect ratio
|
||||||
newSize = newSize.ChangeWidth(selectedSize.Width / originalRatio * flippedRatioSign);
|
newSize = newSize.ChangeHeight(selectedSize.Width / originalRatio * flippedRatioSign);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newSize;
|
return newSize;
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 16
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 16.0.29728.190
|
VisualStudioVersion = 17.3.32929.385
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot", "Greenshot\Greenshot.csproj", "{CD642BF4-D815-4D67-A0B5-C69F0B8231AF}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot", "Greenshot\Greenshot.csproj", "{CD642BF4-D815-4D67-A0B5-C69F0B8231AF}"
|
||||||
ProjectSection(ProjectDependencies) = postProject
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
{92599C09-FF29-4ABD-B6E6-C48ECD781BAB} = {92599C09-FF29-4ABD-B6E6-C48ECD781BAB}
|
{1893A2E4-A78A-4713-A8E7-E70058DABEE0} = {1893A2E4-A78A-4713-A8E7-E70058DABEE0}
|
||||||
{19FEEF09-313F-43C7-819D-F1BCA782B08B} = {19FEEF09-313F-43C7-819D-F1BCA782B08B}
|
{19FEEF09-313F-43C7-819D-F1BCA782B08B} = {19FEEF09-313F-43C7-819D-F1BCA782B08B}
|
||||||
|
{25C870BE-22BB-4CB8-B274-95DC481F53A7} = {25C870BE-22BB-4CB8-B274-95DC481F53A7}
|
||||||
|
{47F23C86-604E-4CC3-8767-B3D4088F30BB} = {47F23C86-604E-4CC3-8767-B3D4088F30BB}
|
||||||
|
{697CF066-9077-4F22-99D9-D989CCE7282B} = {697CF066-9077-4F22-99D9-D989CCE7282B}
|
||||||
|
{7EC72A5A-D73A-4B4B-9CA1-2216C7D92D5E} = {7EC72A5A-D73A-4B4B-9CA1-2216C7D92D5E}
|
||||||
|
{80D8DEB9-94E3-4876-8CCA-2DF1ED5F2C50} = {80D8DEB9-94E3-4876-8CCA-2DF1ED5F2C50}
|
||||||
|
{92599C09-FF29-4ABD-B6E6-C48ECD781BAB} = {92599C09-FF29-4ABD-B6E6-C48ECD781BAB}
|
||||||
{9801F62C-540F-4BFE-9211-6405DEDE563B} = {9801F62C-540F-4BFE-9211-6405DEDE563B}
|
{9801F62C-540F-4BFE-9211-6405DEDE563B} = {9801F62C-540F-4BFE-9211-6405DEDE563B}
|
||||||
{9C0ECC4C-7807-4111-916A-4F57BB29788A} = {9C0ECC4C-7807-4111-916A-4F57BB29788A}
|
{9C0ECC4C-7807-4111-916A-4F57BB29788A} = {9C0ECC4C-7807-4111-916A-4F57BB29788A}
|
||||||
{C3052651-598A-44E2-AAB3-2E41311D50F9} = {C3052651-598A-44E2-AAB3-2E41311D50F9}
|
|
||||||
{7EC72A5A-D73A-4B4B-9CA1-2216C7D92D5E} = {7EC72A5A-D73A-4B4B-9CA1-2216C7D92D5E}
|
|
||||||
{697CF066-9077-4F22-99D9-D989CCE7282B} = {697CF066-9077-4F22-99D9-D989CCE7282B}
|
|
||||||
{47F23C86-604E-4CC3-8767-B3D4088F30BB} = {47F23C86-604E-4CC3-8767-B3D4088F30BB}
|
|
||||||
{80D8DEB9-94E3-4876-8CCA-2DF1ED5F2C50} = {80D8DEB9-94E3-4876-8CCA-2DF1ED5F2C50}
|
|
||||||
{AD7CFFE2-40E7-46CF-A172-D48CF7AE9A12} = {AD7CFFE2-40E7-46CF-A172-D48CF7AE9A12}
|
{AD7CFFE2-40E7-46CF-A172-D48CF7AE9A12} = {AD7CFFE2-40E7-46CF-A172-D48CF7AE9A12}
|
||||||
{1893A2E4-A78A-4713-A8E7-E70058DABEE0} = {1893A2E4-A78A-4713-A8E7-E70058DABEE0}
|
{C3052651-598A-44E2-AAB3-2E41311D50F9} = {C3052651-598A-44E2-AAB3-2E41311D50F9}
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot.Base", "Greenshot.Base\Greenshot.Base.csproj", "{5B924697-4DCD-4F98-85F1-105CB84B7341}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot.Base", "Greenshot.Base\Greenshot.Base.csproj", "{5B924697-4DCD-4F98-85F1-105CB84B7341}"
|
||||||
|
@ -52,6 +53,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot.Editor", "Greenshot.Editor\Greenshot.Editor.csproj", "{148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Greenshot.Editor", "Greenshot.Editor\Greenshot.Editor.csproj", "{148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Greenshot.BuildTasks", "Greenshot.BuildTasks\Greenshot.BuildTasks.csproj", "{25C870BE-22BB-4CB8-B274-95DC481F53A7}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -172,6 +175,14 @@ Global
|
||||||
{148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}.Release|Any CPU.Build.0 = Release|Any CPU
|
{148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}.Release|x86.ActiveCfg = Release|Any CPU
|
{148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}.Release|x86.Build.0 = Release|Any CPU
|
{148D3C8B-D6EC-4A7D-80E9-243A81F19DD2}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{25C870BE-22BB-4CB8-B274-95DC481F53A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{25C870BE-22BB-4CB8-B274-95DC481F53A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{25C870BE-22BB-4CB8-B274-95DC481F53A7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{25C870BE-22BB-4CB8-B274-95DC481F53A7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{25C870BE-22BB-4CB8-B274-95DC481F53A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{25C870BE-22BB-4CB8-B274-95DC481F53A7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{25C870BE-22BB-4CB8-B274-95DC481F53A7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{25C870BE-22BB-4CB8-B274-95DC481F53A7}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Greenshot.Base\Greenshot.Base.csproj" />
|
<ProjectReference Include="..\Greenshot.Base\Greenshot.Base.csproj" />
|
||||||
<ProjectReference Include="..\Greenshot.Editor\Greenshot.Editor.csproj" />
|
<ProjectReference Include="..\Greenshot.Editor\Greenshot.Editor.csproj" />
|
||||||
|
<ProjectReference Include="..\Greenshot.BuildTasks\Greenshot.BuildTasks.csproj" ReferenceOutputAssembly="false" />
|
||||||
<FilesToHash Include="$(SolutionDir)$(SolutionName)\$(OutDir)\*" />
|
<FilesToHash Include="$(SolutionDir)$(SolutionName)\$(OutDir)\*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -65,6 +66,12 @@
|
||||||
</Task>
|
</Task>
|
||||||
</UsingTask>
|
</UsingTask>
|
||||||
|
|
||||||
|
<UsingTask TaskName="EmojiDataTask" TaskFactory="TaskHostFactory" AssemblyFile="..\Greenshot.BuildTasks\bin\$(Configuration)\$(TargetFramework)\Greenshot.BuildTasks.dll" />
|
||||||
|
|
||||||
|
<Target Name="GenerateEmojiTestTxtFile" BeforeTargets="BeforeBuild">
|
||||||
|
<EmojiDataTask EmojiTestTxtFile="..\Greenshot.Editor\Controls\Emoji\emoji-test.txt" OutputFilename="$(SolutionDir)$(SolutionName)\$(OutDir)\emojis.xml" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
<Target Name="Generate hashes" BeforeTargets="PostBuildEvent">
|
<Target Name="Generate hashes" BeforeTargets="PostBuildEvent">
|
||||||
<GetFileHash Files="@(FilesToHash)" Algorithm="SHA256" HashEncoding="hex">
|
<GetFileHash Files="@(FilesToHash)" Algorithm="SHA256" HashEncoding="hex">
|
||||||
<Output TaskParameter="Items" ItemName="FilesWithHashes" />
|
<Output TaskParameter="Items" ItemName="FilesWithHashes" />
|
||||||
|
|
6
src/NuGet.config
Normal file
6
src/NuGet.config
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<packageSources>
|
||||||
|
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||||
|
</packageSources>
|
||||||
|
</configuration>
|
Loading…
Add table
Add a link
Reference in a new issue