From 898e7e4747f0d0b56092c5e7687415753fae8b54 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Tue, 22 Aug 2017 09:55:24 +0300 Subject: [PATCH 01/59] Using a tokenizer instead of string split --- .../Avalonia.Controls.csproj | 1 + src/Avalonia.Controls/GridLength.cs | 9 +- src/Avalonia.Visuals/Matrix.cs | 24 +- src/Avalonia.Visuals/Point.cs | 16 +- src/Avalonia.Visuals/Rect.cs | 20 +- src/Avalonia.Visuals/RelativePoint.cs | 26 +-- src/Avalonia.Visuals/RelativeRect.cs | 55 ++--- src/Avalonia.Visuals/Size.cs | 15 +- src/Avalonia.Visuals/Thickness.cs | 35 ++- .../Utilities/StringTokenizer.cs | 205 ++++++++++++++++++ .../RelativeRectTests.cs | 8 + 11 files changed, 304 insertions(+), 110 deletions(-) create mode 100644 src/Avalonia.Visuals/Utilities/StringTokenizer.cs diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 037546b1860..0b1e778cf0b 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -26,6 +26,7 @@ true + Properties\SharedAssemblyInfo.cs diff --git a/src/Avalonia.Controls/GridLength.cs b/src/Avalonia.Controls/GridLength.cs index c711553e056..b17dc584bd1 100644 --- a/src/Avalonia.Controls/GridLength.cs +++ b/src/Avalonia.Controls/GridLength.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using Avalonia.Utilities; using System; using System.Collections.Generic; using System.Globalization; @@ -210,7 +211,13 @@ public static GridLength Parse(string s, CultureInfo culture) /// The . public static IEnumerable ParseLengths(string s, CultureInfo culture) { - return s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(x => Parse(x, culture)); + using (var tokenizer = new StringTokenizer(s, culture)) + { + while (tokenizer.NextString(out var item)) + { + yield return Parse(item, culture); + } + } } } } \ No newline at end of file diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 10549b967de..6c7d104680b 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using Avalonia.Utilities; using System; using System.Globalization; using System.Linq; @@ -305,23 +306,16 @@ public Matrix Invert() /// The . public static Matrix Parse(string s, CultureInfo culture) { - var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Trim()) - .ToArray(); - - if (parts.Length == 6) + using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Matrix")) { return new Matrix( - double.Parse(parts[0], culture), - double.Parse(parts[1], culture), - double.Parse(parts[2], culture), - double.Parse(parts[3], culture), - double.Parse(parts[4], culture), - double.Parse(parts[5], culture)); - } - else - { - throw new FormatException("Invalid Matrix."); + tokenizer.NextDoubleRequired(), + tokenizer.NextDoubleRequired(), + tokenizer.NextDoubleRequired(), + tokenizer.NextDoubleRequired(), + tokenizer.NextDoubleRequired(), + tokenizer.NextDoubleRequired() + ); } } } diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Visuals/Point.cs index 5fbd0829671..49d2a401bfa 100644 --- a/src/Avalonia.Visuals/Point.cs +++ b/src/Avalonia.Visuals/Point.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using Avalonia.Utilities; using System; using System.Globalization; using System.Linq; @@ -173,17 +174,12 @@ public static implicit operator Vector(Point p) /// The . public static Point Parse(string s, CultureInfo culture) { - var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Trim()) - .ToList(); - - if (parts.Count == 2) - { - return new Point(double.Parse(parts[0], culture), double.Parse(parts[1], culture)); - } - else + using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Point")) { - throw new FormatException("Invalid Point."); + return new Point( + tokenizer.NextDoubleRequired(), + tokenizer.NextDoubleRequired() + ); } } diff --git a/src/Avalonia.Visuals/Rect.cs b/src/Avalonia.Visuals/Rect.cs index d562429fc7e..11f3db31da4 100644 --- a/src/Avalonia.Visuals/Rect.cs +++ b/src/Avalonia.Visuals/Rect.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using Avalonia.Utilities; using System; using System.Globalization; using System.Linq; @@ -490,21 +491,14 @@ public override string ToString() /// The parsed . public static Rect Parse(string s, CultureInfo culture) { - var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Trim()) - .ToList(); - - if (parts.Count == 4) + using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Rect")) { return new Rect( - double.Parse(parts[0], culture), - double.Parse(parts[1], culture), - double.Parse(parts[2], culture), - double.Parse(parts[3], culture)); - } - else - { - throw new FormatException("Invalid Rect."); + tokenizer.NextDoubleRequired(), + tokenizer.NextDoubleRequired(), + tokenizer.NextDoubleRequired(), + tokenizer.NextDoubleRequired() + ); } } } diff --git a/src/Avalonia.Visuals/RelativePoint.cs b/src/Avalonia.Visuals/RelativePoint.cs index cc34feb5f3a..625ee614392 100644 --- a/src/Avalonia.Visuals/RelativePoint.cs +++ b/src/Avalonia.Visuals/RelativePoint.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using Avalonia.Utilities; using System; using System.Globalization; using System.Linq; @@ -157,37 +158,32 @@ public Point ToPixels(Size size) /// The parsed . public static RelativePoint Parse(string s, CultureInfo culture) { - var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Trim()) - .ToList(); - - if (parts.Count == 2) + using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid RelativePoint")) { + var x = tokenizer.NextStringRequired(); + var y = tokenizer.NextStringRequired(); + var unit = RelativeUnit.Absolute; var scale = 1.0; - if (parts[0].EndsWith("%")) + if (x.EndsWith("%")) { - if (!parts[1].EndsWith("%")) + if (!y.EndsWith("%")) { throw new FormatException("If one coordinate is relative, both must be."); } - parts[0] = parts[0].TrimEnd('%'); - parts[1] = parts[1].TrimEnd('%'); + x = x.TrimEnd('%'); + y = y.TrimEnd('%'); unit = RelativeUnit.Relative; scale = 0.01; } return new RelativePoint( - double.Parse(parts[0], culture) * scale, - double.Parse(parts[1], culture) * scale, + double.Parse(x, culture) * scale, + double.Parse(y, culture) * scale, unit); } - else - { - throw new FormatException("Invalid Point."); - } } } } diff --git a/src/Avalonia.Visuals/RelativeRect.cs b/src/Avalonia.Visuals/RelativeRect.cs index a11f080e945..05e344f42b1 100644 --- a/src/Avalonia.Visuals/RelativeRect.cs +++ b/src/Avalonia.Visuals/RelativeRect.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using Avalonia.Utilities; using System; using System.Globalization; using System.Linq; @@ -12,6 +13,8 @@ namespace Avalonia /// public struct RelativeRect : IEquatable { + private static readonly char[] PercentChar = { '%' }; + /// /// A rectangle that represents 100% of an area. /// @@ -159,7 +162,7 @@ public Rect ToPixels(Size size) Rect.Width * size.Width, Rect.Height * size.Height); } - + /// /// Parses a string. /// @@ -168,43 +171,43 @@ public Rect ToPixels(Size size) /// The parsed . public static RelativeRect Parse(string s, CultureInfo culture) { - var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Trim()) - .ToList(); - - if (parts.Count == 4) + using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid RelativeRect")) { + var x = tokenizer.NextStringRequired(); + var y = tokenizer.NextStringRequired(); + var width = tokenizer.NextStringRequired(); + var height = tokenizer.NextStringRequired(); + var unit = RelativeUnit.Absolute; var scale = 1.0; - if (parts[0].EndsWith("%")) + var xRelative = x.EndsWith("%", StringComparison.Ordinal); + var yRelative = y.EndsWith("%", StringComparison.Ordinal); + var widthRelative = width.EndsWith("%", StringComparison.Ordinal); + var heightRelative = height.EndsWith("%", StringComparison.Ordinal); + + if (xRelative && yRelative && widthRelative && heightRelative) { - if (!parts[1].EndsWith("%") - || !parts[2].EndsWith("%") - || !parts[3].EndsWith("%")) - { - throw new FormatException("If one coordinate is relative, all other must be too."); - } - - parts[0] = parts[0].TrimEnd('%'); - parts[1] = parts[1].TrimEnd('%'); - parts[2] = parts[2].TrimEnd('%'); - parts[3] = parts[3].TrimEnd('%'); + x = x.TrimEnd(PercentChar); + y = y.TrimEnd(PercentChar); + width = width.TrimEnd(PercentChar); + height = height.TrimEnd(PercentChar); + unit = RelativeUnit.Relative; scale = 0.01; } + else if (xRelative || yRelative || widthRelative || heightRelative) + { + throw new FormatException("If one coordinate is relative, all must be."); + } return new RelativeRect( - double.Parse(parts[0], culture) * scale, - double.Parse(parts[1], culture) * scale, - double.Parse(parts[2], culture) * scale, - double.Parse(parts[3], culture) * scale, + double.Parse(x, culture) * scale, + double.Parse(y, culture) * scale, + double.Parse(width, culture) * scale, + double.Parse(height, culture) * scale, unit); } - else - { - throw new FormatException("Invalid RelativeRect."); - } } } } diff --git a/src/Avalonia.Visuals/Size.cs b/src/Avalonia.Visuals/Size.cs index 6ad87c6120a..ff734146c21 100644 --- a/src/Avalonia.Visuals/Size.cs +++ b/src/Avalonia.Visuals/Size.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using Avalonia.Utilities; using System; using System.Globalization; using System.Linq; @@ -153,17 +154,11 @@ public Size(double width, double height) /// The . public static Size Parse(string s, CultureInfo culture) { - var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Trim()) - .ToList(); - - if (parts.Count == 2) - { - return new Size(double.Parse(parts[0], culture), double.Parse(parts[1], culture)); - } - else + using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Size")) { - throw new FormatException("Invalid Size."); + return new Size( + tokenizer.NextDoubleRequired(), + tokenizer.NextDoubleRequired()); } } diff --git a/src/Avalonia.Visuals/Thickness.cs b/src/Avalonia.Visuals/Thickness.cs index dc9be7341db..2a1cf6ac57b 100644 --- a/src/Avalonia.Visuals/Thickness.cs +++ b/src/Avalonia.Visuals/Thickness.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using Avalonia.Utilities; using System; using System.Globalization; using System.Linq; @@ -163,28 +164,22 @@ public Thickness(double left, double top, double right, double bottom) /// The . public static Thickness Parse(string s, CultureInfo culture) { - var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Trim()) - .ToList(); - - switch (parts.Count) + using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Thickness")) { - case 1: - var uniform = double.Parse(parts[0], culture); - return new Thickness(uniform); - case 2: - var horizontal = double.Parse(parts[0], culture); - var vertical = double.Parse(parts[1], culture); - return new Thickness(horizontal, vertical); - case 4: - var left = double.Parse(parts[0], culture); - var top = double.Parse(parts[1], culture); - var right = double.Parse(parts[2], culture); - var bottom = double.Parse(parts[3], culture); - return new Thickness(left, top, right, bottom); + var a = tokenizer.NextDoubleRequired(); + + if (tokenizer.NextDouble(out var b)) + { + if (tokenizer.NextDouble(out var c)) + { + return new Thickness(a, b, c, tokenizer.NextDoubleRequired()); + } + + return new Thickness(a, b); + } + + return new Thickness(a); } - - throw new FormatException("Invalid Thickness."); } /// diff --git a/src/Avalonia.Visuals/Utilities/StringTokenizer.cs b/src/Avalonia.Visuals/Utilities/StringTokenizer.cs new file mode 100644 index 00000000000..2f378f44df4 --- /dev/null +++ b/src/Avalonia.Visuals/Utilities/StringTokenizer.cs @@ -0,0 +1,205 @@ +using System; +using System.Globalization; +using static System.Char; + +namespace Avalonia.Utilities +{ + internal struct StringTokenizer : IDisposable + { + private const char DefaultSeparatorChar = ','; + + private readonly string _s; + private readonly int _length; + private readonly char _separator; + private readonly string _exceptionMessage; + private readonly IFormatProvider _formatProvider; + private int _index; + private int _tokenIndex; + private int _tokenLength; + + public StringTokenizer(string s, IFormatProvider formatProvider, string exceptionMessage = null) + : this(s, GetSeparatorFromFormatProvider(formatProvider), exceptionMessage) + { + _formatProvider = formatProvider; + } + + public StringTokenizer(string s, char separator = DefaultSeparatorChar, string exceptionMessage = null) + { + _s = s ?? throw new ArgumentNullException(nameof(s)); + _length = s?.Length ?? 0; + _separator = separator; + _exceptionMessage = exceptionMessage; + _formatProvider = CultureInfo.InvariantCulture; + _index = 0; + _tokenIndex = -1; + _tokenLength = 0; + + while (_index < _length && IsWhiteSpace(_s, _index)) + { + _index++; + } + } + + public string CurrentToken => _tokenIndex < 0 ? null : _s.Substring(_tokenIndex, _tokenLength); + + public void Dispose() + { + if (_index != _length) + { + throw GetFormatException(); + } + } + + public bool NextInt32(out Int32 result, char? separator = null) + { + var success = NextString(out var stringResult, separator); + result = success ? int.Parse(stringResult, _formatProvider) : 0; + return success; + } + + public int NextInt32Required(char? separator = null) + { + if (!NextInt32(out var result, separator)) + { + throw GetFormatException(); + } + + return result; + } + + public bool NextDouble(out double result, char? separator = null) + { + var success = NextString(out var stringResult, separator); + result = success ? double.Parse(stringResult, _formatProvider) : 0; + return success; + } + + public double NextDoubleRequired(char? separator = null) + { + if (!NextDouble(out var result, separator)) + { + throw GetFormatException(); + } + + return result; + } + + public bool NextString(out string result, char? separator = null) + { + var success = NextToken(separator ?? _separator); + result = CurrentToken; + return success; + } + + public string NextStringRequired(char? separator = null) + { + if (!NextString(out var result, separator)) + { + throw GetFormatException(); + } + + return result; + } + + private bool NextToken(char separator) + { + _tokenIndex = -1; + + if (_index >= _length) + { + return false; + } + + var c = _s[_index]; + + var index = _index; + var length = 0; + + while (_index < _length) + { + c = _s[_index]; + + if (IsWhiteSpace(c) || c == separator) + { + break; + } + + _index++; + length++; + } + + SkipToNextToken(separator); + + _tokenIndex = index; + _tokenLength = length; + + if (_tokenLength < 1) + { + throw GetFormatException(); + } + + return true; + } + + private void SkipToNextToken(char separator) + { + if (_index < _length) + { + var c = _s[_index]; + + if (c != separator && !IsWhiteSpace(c)) + { + throw GetFormatException(); + } + + var length = 0; + + while (_index < _length) + { + c = _s[_index]; + + if (c == separator) + { + length++; + _index++; + + if (length > 1) + { + throw GetFormatException(); + } + } + else + { + if (!IsWhiteSpace(c)) + { + break; + } + + _index++; + } + } + + if (length > 0 && _index >= _length) + { + throw GetFormatException(); + } + } + } + + private FormatException GetFormatException() => + _exceptionMessage != null ? new FormatException(_exceptionMessage) : new FormatException(); + + private static char GetSeparatorFromFormatProvider(IFormatProvider provider) + { + var c = DefaultSeparatorChar; + + var formatInfo = NumberFormatInfo.GetInstance(provider); + if (formatInfo.NumberDecimalSeparator.Length > 0 && c == formatInfo.NumberDecimalSeparator[0]) + { + c = ';'; + } + + return c; + } + } +} \ No newline at end of file diff --git a/tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs b/tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs index 8ba4f3b7394..9f25dcd413f 100644 --- a/tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System; using System.Globalization; using Xunit; @@ -25,5 +26,12 @@ public void Parse_Should_Accept_Relative_Value() Assert.Equal(new RelativeRect(0.1, 0.2, 0.4, 0.7, RelativeUnit.Relative), result, Compare); } + + [Fact] + public void Parse_Should_Throw_Mixed_Values() + { + Assert.Throws(() => + RelativeRect.Parse("10%, 20%, 40, 70%", CultureInfo.InvariantCulture)); + } } } From cbdc1b16a59311d7ebf57e3578e62d9c0888b3e7 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Tue, 22 Aug 2017 11:28:48 +0300 Subject: [PATCH 02/59] Improve known color/brush parsing --- src/Avalonia.Visuals/Media/Brush.cs | 23 +- src/Avalonia.Visuals/Media/Brushes.cs | 434 ++++++------------ src/Avalonia.Visuals/Media/Color.cs | 27 +- src/Avalonia.Visuals/Media/Colors.cs | 284 ++++++------ src/Avalonia.Visuals/Media/KnownColors.cs | 224 +++++++++ .../Media/BrushTests.cs | 18 +- 6 files changed, 544 insertions(+), 466 deletions(-) create mode 100644 src/Avalonia.Visuals/Media/KnownColors.cs diff --git a/src/Avalonia.Visuals/Media/Brush.cs b/src/Avalonia.Visuals/Media/Brush.cs index 40ac24b6057..abceb399616 100644 --- a/src/Avalonia.Visuals/Media/Brush.cs +++ b/src/Avalonia.Visuals/Media/Brush.cs @@ -34,26 +34,21 @@ public double Opacity /// The . public static IBrush Parse(string s) { + if (s == null) throw new ArgumentNullException(nameof(s)); + if (s.Length == 0) throw new FormatException(); + if (s[0] == '#') { return new SolidColorBrush(Color.Parse(s)); } - else - { - var upper = s.ToUpperInvariant(); - var member = typeof(Brushes).GetTypeInfo().DeclaredProperties - .FirstOrDefault(x => x.Name.ToUpperInvariant() == upper); - if (member != null) - { - var brush = (ISolidColorBrush)member.GetValue(null); - return new SolidColorBrush(brush.Color, brush.Opacity); - } - else - { - throw new FormatException($"Invalid brush string: '{s}'."); - } + var brush = KnownColors.GetKnownBrush(s); + if (brush != null) + { + return brush; } + + throw new FormatException($"Invalid brush string: '{s}'."); } } } diff --git a/src/Avalonia.Visuals/Media/Brushes.cs b/src/Avalonia.Visuals/Media/Brushes.cs index 4c89c97b49e..83ff0433977 100644 --- a/src/Avalonia.Visuals/Media/Brushes.cs +++ b/src/Avalonia.Visuals/Media/Brushes.cs @@ -1,8 +1,6 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using Avalonia.Media.Immutable; - namespace Avalonia.Media { /// @@ -10,857 +8,709 @@ namespace Avalonia.Media /// public static class Brushes { - /// - /// Initializes static members of the class. - /// - static Brushes() - { - AliceBlue = new ImmutableSolidColorBrush(Colors.AliceBlue); - AntiqueWhite = new ImmutableSolidColorBrush(Colors.AntiqueWhite); - Aqua = new ImmutableSolidColorBrush(Colors.Aqua); - Aquamarine = new ImmutableSolidColorBrush(Colors.Aquamarine); - Azure = new ImmutableSolidColorBrush(Colors.Azure); - Beige = new ImmutableSolidColorBrush(Colors.Beige); - Bisque = new ImmutableSolidColorBrush(Colors.Bisque); - Black = new ImmutableSolidColorBrush(Colors.Black); - BlanchedAlmond = new ImmutableSolidColorBrush(Colors.BlanchedAlmond); - Blue = new ImmutableSolidColorBrush(Colors.Blue); - BlueViolet = new ImmutableSolidColorBrush(Colors.BlueViolet); - Brown = new ImmutableSolidColorBrush(Colors.Brown); - BurlyWood = new ImmutableSolidColorBrush(Colors.BurlyWood); - CadetBlue = new ImmutableSolidColorBrush(Colors.CadetBlue); - Chartreuse = new ImmutableSolidColorBrush(Colors.Chartreuse); - Chocolate = new ImmutableSolidColorBrush(Colors.Chocolate); - Coral = new ImmutableSolidColorBrush(Colors.Coral); - CornflowerBlue = new ImmutableSolidColorBrush(Colors.CornflowerBlue); - Cornsilk = new ImmutableSolidColorBrush(Colors.Cornsilk); - Crimson = new ImmutableSolidColorBrush(Colors.Crimson); - Cyan = new ImmutableSolidColorBrush(Colors.Cyan); - DarkBlue = new ImmutableSolidColorBrush(Colors.DarkBlue); - DarkCyan = new ImmutableSolidColorBrush(Colors.DarkCyan); - DarkGoldenrod = new ImmutableSolidColorBrush(Colors.DarkGoldenrod); - DarkGray = new ImmutableSolidColorBrush(Colors.DarkGray); - DarkGreen = new ImmutableSolidColorBrush(Colors.DarkGreen); - DarkKhaki = new ImmutableSolidColorBrush(Colors.DarkKhaki); - DarkMagenta = new ImmutableSolidColorBrush(Colors.DarkMagenta); - DarkOliveGreen = new ImmutableSolidColorBrush(Colors.DarkOliveGreen); - DarkOrange = new ImmutableSolidColorBrush(Colors.DarkOrange); - DarkOrchid = new ImmutableSolidColorBrush(Colors.DarkOrchid); - DarkRed = new ImmutableSolidColorBrush(Colors.DarkRed); - DarkSalmon = new ImmutableSolidColorBrush(Colors.DarkSalmon); - DarkSeaGreen = new ImmutableSolidColorBrush(Colors.DarkSeaGreen); - DarkSlateBlue = new ImmutableSolidColorBrush(Colors.DarkSlateBlue); - DarkSlateGray = new ImmutableSolidColorBrush(Colors.DarkSlateGray); - DarkTurquoise = new ImmutableSolidColorBrush(Colors.DarkTurquoise); - DarkViolet = new ImmutableSolidColorBrush(Colors.DarkViolet); - DeepPink = new ImmutableSolidColorBrush(Colors.DeepPink); - DeepSkyBlue = new ImmutableSolidColorBrush(Colors.DeepSkyBlue); - DimGray = new ImmutableSolidColorBrush(Colors.DimGray); - DodgerBlue = new ImmutableSolidColorBrush(Colors.DodgerBlue); - Firebrick = new ImmutableSolidColorBrush(Colors.Firebrick); - FloralWhite = new ImmutableSolidColorBrush(Colors.FloralWhite); - ForestGreen = new ImmutableSolidColorBrush(Colors.ForestGreen); - Fuchsia = new ImmutableSolidColorBrush(Colors.Fuchsia); - Gainsboro = new ImmutableSolidColorBrush(Colors.Gainsboro); - GhostWhite = new ImmutableSolidColorBrush(Colors.GhostWhite); - Gold = new ImmutableSolidColorBrush(Colors.Gold); - Goldenrod = new ImmutableSolidColorBrush(Colors.Goldenrod); - Gray = new ImmutableSolidColorBrush(Colors.Gray); - Green = new ImmutableSolidColorBrush(Colors.Green); - GreenYellow = new ImmutableSolidColorBrush(Colors.GreenYellow); - Honeydew = new ImmutableSolidColorBrush(Colors.Honeydew); - HotPink = new ImmutableSolidColorBrush(Colors.HotPink); - IndianRed = new ImmutableSolidColorBrush(Colors.IndianRed); - Indigo = new ImmutableSolidColorBrush(Colors.Indigo); - Ivory = new ImmutableSolidColorBrush(Colors.Ivory); - Khaki = new ImmutableSolidColorBrush(Colors.Khaki); - Lavender = new ImmutableSolidColorBrush(Colors.Lavender); - LavenderBlush = new ImmutableSolidColorBrush(Colors.LavenderBlush); - LawnGreen = new ImmutableSolidColorBrush(Colors.LawnGreen); - LemonChiffon = new ImmutableSolidColorBrush(Colors.LemonChiffon); - LightBlue = new ImmutableSolidColorBrush(Colors.LightBlue); - LightCoral = new ImmutableSolidColorBrush(Colors.LightCoral); - LightCyan = new ImmutableSolidColorBrush(Colors.LightCyan); - LightGoldenrodYellow = new ImmutableSolidColorBrush(Colors.LightGoldenrodYellow); - LightGray = new ImmutableSolidColorBrush(Colors.LightGray); - LightGreen = new ImmutableSolidColorBrush(Colors.LightGreen); - LightPink = new ImmutableSolidColorBrush(Colors.LightPink); - LightSalmon = new ImmutableSolidColorBrush(Colors.LightSalmon); - LightSeaGreen = new ImmutableSolidColorBrush(Colors.LightSeaGreen); - LightSkyBlue = new ImmutableSolidColorBrush(Colors.LightSkyBlue); - LightSlateGray = new ImmutableSolidColorBrush(Colors.LightSlateGray); - LightSteelBlue = new ImmutableSolidColorBrush(Colors.LightSteelBlue); - LightYellow = new ImmutableSolidColorBrush(Colors.LightYellow); - Lime = new ImmutableSolidColorBrush(Colors.Lime); - LimeGreen = new ImmutableSolidColorBrush(Colors.LimeGreen); - Linen = new ImmutableSolidColorBrush(Colors.Linen); - Magenta = new ImmutableSolidColorBrush(Colors.Magenta); - Maroon = new ImmutableSolidColorBrush(Colors.Maroon); - MediumAquamarine = new ImmutableSolidColorBrush(Colors.MediumAquamarine); - MediumBlue = new ImmutableSolidColorBrush(Colors.MediumBlue); - MediumOrchid = new ImmutableSolidColorBrush(Colors.MediumOrchid); - MediumPurple = new ImmutableSolidColorBrush(Colors.MediumPurple); - MediumSeaGreen = new ImmutableSolidColorBrush(Colors.MediumSeaGreen); - MediumSlateBlue = new ImmutableSolidColorBrush(Colors.MediumSlateBlue); - MediumSpringGreen = new ImmutableSolidColorBrush(Colors.MediumSpringGreen); - MediumTurquoise = new ImmutableSolidColorBrush(Colors.MediumTurquoise); - MediumVioletRed = new ImmutableSolidColorBrush(Colors.MediumVioletRed); - MidnightBlue = new ImmutableSolidColorBrush(Colors.MidnightBlue); - MintCream = new ImmutableSolidColorBrush(Colors.MintCream); - MistyRose = new ImmutableSolidColorBrush(Colors.MistyRose); - Moccasin = new ImmutableSolidColorBrush(Colors.Moccasin); - NavajoWhite = new ImmutableSolidColorBrush(Colors.NavajoWhite); - Navy = new ImmutableSolidColorBrush(Colors.Navy); - OldLace = new ImmutableSolidColorBrush(Colors.OldLace); - Olive = new ImmutableSolidColorBrush(Colors.Olive); - OliveDrab = new ImmutableSolidColorBrush(Colors.OliveDrab); - Orange = new ImmutableSolidColorBrush(Colors.Orange); - OrangeRed = new ImmutableSolidColorBrush(Colors.OrangeRed); - Orchid = new ImmutableSolidColorBrush(Colors.Orchid); - PaleGoldenrod = new ImmutableSolidColorBrush(Colors.PaleGoldenrod); - PaleGreen = new ImmutableSolidColorBrush(Colors.PaleGreen); - PaleTurquoise = new ImmutableSolidColorBrush(Colors.PaleTurquoise); - PaleVioletRed = new ImmutableSolidColorBrush(Colors.PaleVioletRed); - PapayaWhip = new ImmutableSolidColorBrush(Colors.PapayaWhip); - PeachPuff = new ImmutableSolidColorBrush(Colors.PeachPuff); - Peru = new ImmutableSolidColorBrush(Colors.Peru); - Pink = new ImmutableSolidColorBrush(Colors.Pink); - Plum = new ImmutableSolidColorBrush(Colors.Plum); - PowderBlue = new ImmutableSolidColorBrush(Colors.PowderBlue); - Purple = new ImmutableSolidColorBrush(Colors.Purple); - Red = new ImmutableSolidColorBrush(Colors.Red); - RosyBrown = new ImmutableSolidColorBrush(Colors.RosyBrown); - RoyalBlue = new ImmutableSolidColorBrush(Colors.RoyalBlue); - SaddleBrown = new ImmutableSolidColorBrush(Colors.SaddleBrown); - Salmon = new ImmutableSolidColorBrush(Colors.Salmon); - SandyBrown = new ImmutableSolidColorBrush(Colors.SandyBrown); - SeaGreen = new ImmutableSolidColorBrush(Colors.SeaGreen); - SeaShell = new ImmutableSolidColorBrush(Colors.SeaShell); - Sienna = new ImmutableSolidColorBrush(Colors.Sienna); - Silver = new ImmutableSolidColorBrush(Colors.Silver); - SkyBlue = new ImmutableSolidColorBrush(Colors.SkyBlue); - SlateBlue = new ImmutableSolidColorBrush(Colors.SlateBlue); - SlateGray = new ImmutableSolidColorBrush(Colors.SlateGray); - Snow = new ImmutableSolidColorBrush(Colors.Snow); - SpringGreen = new ImmutableSolidColorBrush(Colors.SpringGreen); - SteelBlue = new ImmutableSolidColorBrush(Colors.SteelBlue); - Tan = new ImmutableSolidColorBrush(Colors.Tan); - Teal = new ImmutableSolidColorBrush(Colors.Teal); - Thistle = new ImmutableSolidColorBrush(Colors.Thistle); - Tomato = new ImmutableSolidColorBrush(Colors.Tomato); - Transparent = new ImmutableSolidColorBrush(Colors.Transparent); - Turquoise = new ImmutableSolidColorBrush(Colors.Turquoise); - Violet = new ImmutableSolidColorBrush(Colors.Violet); - Wheat = new ImmutableSolidColorBrush(Colors.Wheat); - White = new ImmutableSolidColorBrush(Colors.White); - WhiteSmoke = new ImmutableSolidColorBrush(Colors.WhiteSmoke); - Yellow = new ImmutableSolidColorBrush(Colors.Yellow); - YellowGreen = new ImmutableSolidColorBrush(Colors.YellowGreen); - } - /// /// Gets an colored brush. /// - public static ISolidColorBrush AliceBlue { get; private set; } + public static ISolidColorBrush AliceBlue => KnownColor.AliceBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush AntiqueWhite { get; private set; } + public static ISolidColorBrush AntiqueWhite => KnownColor.AntiqueWhite.ToBrush(); /// - /// Gets an colored brush. + /// Gets an colored brush. /// - public static ISolidColorBrush Aqua { get; private set; } + public static ISolidColorBrush Aqua => KnownColor.Aqua.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Aquamarine { get; private set; } + public static ISolidColorBrush Aquamarine => KnownColor.Aquamarine.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Azure { get; private set; } + public static ISolidColorBrush Azure => KnownColor.Azure.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Beige { get; private set; } + public static ISolidColorBrush Beige => KnownColor.Beige.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Bisque { get; private set; } + public static ISolidColorBrush Bisque => KnownColor.Bisque.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Black { get; private set; } + public static ISolidColorBrush Black => KnownColor.Black.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush BlanchedAlmond { get; private set; } + public static ISolidColorBrush BlanchedAlmond => KnownColor.BlanchedAlmond.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Blue { get; private set; } + public static ISolidColorBrush Blue => KnownColor.Blue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush BlueViolet { get; private set; } + public static ISolidColorBrush BlueViolet => KnownColor.BlueViolet.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Brown { get; private set; } + public static ISolidColorBrush Brown => KnownColor.Brown.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush BurlyWood { get; private set; } + public static ISolidColorBrush BurlyWood => KnownColor.BurlyWood.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush CadetBlue { get; private set; } + public static ISolidColorBrush CadetBlue => KnownColor.CadetBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Chartreuse { get; private set; } + public static ISolidColorBrush Chartreuse => KnownColor.Chartreuse.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Chocolate { get; private set; } + public static ISolidColorBrush Chocolate => KnownColor.Chocolate.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Coral { get; private set; } + public static ISolidColorBrush Coral => KnownColor.Coral.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush CornflowerBlue { get; private set; } + public static ISolidColorBrush CornflowerBlue => KnownColor.CornflowerBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Cornsilk { get; private set; } + public static ISolidColorBrush Cornsilk => KnownColor.Cornsilk.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Crimson { get; private set; } + public static ISolidColorBrush Crimson => KnownColor.Crimson.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Cyan { get; private set; } + public static ISolidColorBrush Cyan => KnownColor.Cyan.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkBlue { get; private set; } + public static ISolidColorBrush DarkBlue => KnownColor.DarkBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkCyan { get; private set; } + public static ISolidColorBrush DarkCyan => KnownColor.DarkCyan.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkGoldenrod { get; private set; } + public static ISolidColorBrush DarkGoldenrod => KnownColor.DarkGoldenrod.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkGray { get; private set; } + public static ISolidColorBrush DarkGray => KnownColor.DarkGray.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkGreen { get; private set; } + public static ISolidColorBrush DarkGreen => KnownColor.DarkGreen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkKhaki { get; private set; } + public static ISolidColorBrush DarkKhaki => KnownColor.DarkKhaki.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkMagenta { get; private set; } + public static ISolidColorBrush DarkMagenta => KnownColor.DarkMagenta.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkOliveGreen { get; private set; } + public static ISolidColorBrush DarkOliveGreen => KnownColor.DarkOliveGreen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkOrange { get; private set; } + public static ISolidColorBrush DarkOrange => KnownColor.DarkOrange.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkOrchid { get; private set; } + public static ISolidColorBrush DarkOrchid => KnownColor.DarkOrchid.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkRed { get; private set; } + public static ISolidColorBrush DarkRed => KnownColor.DarkRed.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkSalmon { get; private set; } + public static ISolidColorBrush DarkSalmon => KnownColor.DarkSalmon.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkSeaGreen { get; private set; } + public static ISolidColorBrush DarkSeaGreen => KnownColor.DarkSeaGreen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkSlateBlue { get; private set; } + public static ISolidColorBrush DarkSlateBlue => KnownColor.DarkSlateBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkSlateGray { get; private set; } + public static ISolidColorBrush DarkSlateGray => KnownColor.DarkSlateGray.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkTurquoise { get; private set; } + public static ISolidColorBrush DarkTurquoise => KnownColor.DarkTurquoise.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DarkViolet { get; private set; } + public static ISolidColorBrush DarkViolet => KnownColor.DarkViolet.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DeepPink { get; private set; } + public static ISolidColorBrush DeepPink => KnownColor.DeepPink.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DeepSkyBlue { get; private set; } + public static ISolidColorBrush DeepSkyBlue => KnownColor.DeepSkyBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DimGray { get; private set; } + public static ISolidColorBrush DimGray => KnownColor.DimGray.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush DodgerBlue { get; private set; } + public static ISolidColorBrush DodgerBlue => KnownColor.DodgerBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Firebrick { get; private set; } + public static ISolidColorBrush Firebrick => KnownColor.Firebrick.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush FloralWhite { get; private set; } + public static ISolidColorBrush FloralWhite => KnownColor.FloralWhite.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush ForestGreen { get; private set; } + public static ISolidColorBrush ForestGreen => KnownColor.ForestGreen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Fuchsia { get; private set; } + public static ISolidColorBrush Fuchsia => KnownColor.Fuchsia.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Gainsboro { get; private set; } + public static ISolidColorBrush Gainsboro => KnownColor.Gainsboro.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush GhostWhite { get; private set; } + public static ISolidColorBrush GhostWhite => KnownColor.GhostWhite.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Gold { get; private set; } + public static ISolidColorBrush Gold => KnownColor.Gold.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Goldenrod { get; private set; } + public static ISolidColorBrush Goldenrod => KnownColor.Goldenrod.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Gray { get; private set; } + public static ISolidColorBrush Gray => KnownColor.Gray.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Green { get; private set; } + public static ISolidColorBrush Green => KnownColor.Green.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush GreenYellow { get; private set; } + public static ISolidColorBrush GreenYellow => KnownColor.GreenYellow.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Honeydew { get; private set; } + public static ISolidColorBrush Honeydew => KnownColor.Honeydew.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush HotPink { get; private set; } + public static ISolidColorBrush HotPink => KnownColor.HotPink.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush IndianRed { get; private set; } + public static ISolidColorBrush IndianRed => KnownColor.IndianRed.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Indigo { get; private set; } + public static ISolidColorBrush Indigo => KnownColor.Indigo.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Ivory { get; private set; } + public static ISolidColorBrush Ivory => KnownColor.Ivory.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Khaki { get; private set; } + public static ISolidColorBrush Khaki => KnownColor.Khaki.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Lavender { get; private set; } + public static ISolidColorBrush Lavender => KnownColor.Lavender.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LavenderBlush { get; private set; } + public static ISolidColorBrush LavenderBlush => KnownColor.LavenderBlush.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LawnGreen { get; private set; } + public static ISolidColorBrush LawnGreen => KnownColor.LawnGreen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LemonChiffon { get; private set; } + public static ISolidColorBrush LemonChiffon => KnownColor.LemonChiffon.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LightBlue { get; private set; } + public static ISolidColorBrush LightBlue => KnownColor.LightBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LightCoral { get; private set; } + public static ISolidColorBrush LightCoral => KnownColor.LightCoral.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LightCyan { get; private set; } + public static ISolidColorBrush LightCyan => KnownColor.LightCyan.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LightGoldenrodYellow { get; private set; } + public static ISolidColorBrush LightGoldenrodYellow => KnownColor.LightGoldenrodYellow.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LightGray { get; private set; } + public static ISolidColorBrush LightGray => KnownColor.LightGray.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LightGreen { get; private set; } + public static ISolidColorBrush LightGreen => KnownColor.LightGreen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LightPink { get; private set; } + public static ISolidColorBrush LightPink => KnownColor.LightPink.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LightSalmon { get; private set; } + public static ISolidColorBrush LightSalmon => KnownColor.LightSalmon.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LightSeaGreen { get; private set; } + public static ISolidColorBrush LightSeaGreen => KnownColor.LightSeaGreen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LightSkyBlue { get; private set; } + public static ISolidColorBrush LightSkyBlue => KnownColor.LightSkyBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LightSlateGray { get; private set; } + public static ISolidColorBrush LightSlateGray => KnownColor.LightSlateGray.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LightSteelBlue { get; private set; } + public static ISolidColorBrush LightSteelBlue => KnownColor.LightSteelBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LightYellow { get; private set; } + public static ISolidColorBrush LightYellow => KnownColor.LightYellow.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Lime { get; private set; } + public static ISolidColorBrush Lime => KnownColor.Lime.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush LimeGreen { get; private set; } + public static ISolidColorBrush LimeGreen => KnownColor.LimeGreen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Linen { get; private set; } + public static ISolidColorBrush Linen => KnownColor.Linen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Magenta { get; private set; } + public static ISolidColorBrush Magenta => KnownColor.Magenta.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Maroon { get; private set; } + public static ISolidColorBrush Maroon => KnownColor.Maroon.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush MediumAquamarine { get; private set; } + public static ISolidColorBrush MediumAquamarine => KnownColor.MediumAquamarine.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush MediumBlue { get; private set; } + public static ISolidColorBrush MediumBlue => KnownColor.MediumBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush MediumOrchid { get; private set; } + public static ISolidColorBrush MediumOrchid => KnownColor.MediumOrchid.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush MediumPurple { get; private set; } + public static ISolidColorBrush MediumPurple => KnownColor.MediumPurple.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush MediumSeaGreen { get; private set; } + public static ISolidColorBrush MediumSeaGreen => KnownColor.MediumSeaGreen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush MediumSlateBlue { get; private set; } + public static ISolidColorBrush MediumSlateBlue => KnownColor.MediumSlateBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush MediumSpringGreen { get; private set; } + public static ISolidColorBrush MediumSpringGreen => KnownColor.MediumSpringGreen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush MediumTurquoise { get; private set; } + public static ISolidColorBrush MediumTurquoise => KnownColor.MediumTurquoise.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush MediumVioletRed { get; private set; } + public static ISolidColorBrush MediumVioletRed => KnownColor.MediumVioletRed.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush MidnightBlue { get; private set; } + public static ISolidColorBrush MidnightBlue => KnownColor.MidnightBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush MintCream { get; private set; } + public static ISolidColorBrush MintCream => KnownColor.MintCream.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush MistyRose { get; private set; } + public static ISolidColorBrush MistyRose => KnownColor.MistyRose.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Moccasin { get; private set; } + public static ISolidColorBrush Moccasin => KnownColor.Moccasin.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush NavajoWhite { get; private set; } + public static ISolidColorBrush NavajoWhite => KnownColor.NavajoWhite.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Navy { get; private set; } + public static ISolidColorBrush Navy => KnownColor.Navy.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush OldLace { get; private set; } + public static ISolidColorBrush OldLace => KnownColor.OldLace.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Olive { get; private set; } + public static ISolidColorBrush Olive => KnownColor.Olive.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush OliveDrab { get; private set; } + public static ISolidColorBrush OliveDrab => KnownColor.OliveDrab.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Orange { get; private set; } + public static ISolidColorBrush Orange => KnownColor.Orange.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush OrangeRed { get; private set; } + public static ISolidColorBrush OrangeRed => KnownColor.OrangeRed.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Orchid { get; private set; } + public static ISolidColorBrush Orchid => KnownColor.Orchid.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush PaleGoldenrod { get; private set; } + public static ISolidColorBrush PaleGoldenrod => KnownColor.PaleGoldenrod.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush PaleGreen { get; private set; } + public static ISolidColorBrush PaleGreen => KnownColor.PaleGreen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush PaleTurquoise { get; private set; } + public static ISolidColorBrush PaleTurquoise => KnownColor.PaleTurquoise.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush PaleVioletRed { get; private set; } + public static ISolidColorBrush PaleVioletRed => KnownColor.PaleVioletRed.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush PapayaWhip { get; private set; } + public static ISolidColorBrush PapayaWhip => KnownColor.PapayaWhip.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush PeachPuff { get; private set; } + public static ISolidColorBrush PeachPuff => KnownColor.PeachPuff.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Peru { get; private set; } + public static ISolidColorBrush Peru => KnownColor.Peru.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Pink { get; private set; } + public static ISolidColorBrush Pink => KnownColor.Pink.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Plum { get; private set; } + public static ISolidColorBrush Plum => KnownColor.Plum.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush PowderBlue { get; private set; } + public static ISolidColorBrush PowderBlue => KnownColor.PowderBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Purple { get; private set; } + public static ISolidColorBrush Purple => KnownColor.Purple.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Red { get; private set; } + public static ISolidColorBrush Red => KnownColor.Red.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush RosyBrown { get; private set; } + public static ISolidColorBrush RosyBrown => KnownColor.RosyBrown.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush RoyalBlue { get; private set; } + public static ISolidColorBrush RoyalBlue => KnownColor.RoyalBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush SaddleBrown { get; private set; } + public static ISolidColorBrush SaddleBrown => KnownColor.SaddleBrown.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Salmon { get; private set; } + public static ISolidColorBrush Salmon => KnownColor.Salmon.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush SandyBrown { get; private set; } + public static ISolidColorBrush SandyBrown => KnownColor.SandyBrown.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush SeaGreen { get; private set; } + public static ISolidColorBrush SeaGreen => KnownColor.SeaGreen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush SeaShell { get; private set; } + public static ISolidColorBrush SeaShell => KnownColor.SeaShell.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Sienna { get; private set; } + public static ISolidColorBrush Sienna => KnownColor.Sienna.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Silver { get; private set; } + public static ISolidColorBrush Silver => KnownColor.Silver.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush SkyBlue { get; private set; } + public static ISolidColorBrush SkyBlue => KnownColor.SkyBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush SlateBlue { get; private set; } + public static ISolidColorBrush SlateBlue => KnownColor.SlateBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush SlateGray { get; private set; } + public static ISolidColorBrush SlateGray => KnownColor.SlateGray.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Snow { get; private set; } + public static ISolidColorBrush Snow => KnownColor.Snow.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush SpringGreen { get; private set; } + public static ISolidColorBrush SpringGreen => KnownColor.SpringGreen.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush SteelBlue { get; private set; } + public static ISolidColorBrush SteelBlue => KnownColor.SteelBlue.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Tan { get; private set; } + public static ISolidColorBrush Tan => KnownColor.Tan.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Teal { get; private set; } + public static ISolidColorBrush Teal => KnownColor.Teal.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Thistle { get; private set; } + public static ISolidColorBrush Thistle => KnownColor.Thistle.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Tomato { get; private set; } + public static ISolidColorBrush Tomato => KnownColor.Tomato.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Transparent { get; private set; } + public static ISolidColorBrush Transparent => KnownColor.Transparent.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Turquoise { get; private set; } + public static ISolidColorBrush Turquoise => KnownColor.Turquoise.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Violet { get; private set; } + public static ISolidColorBrush Violet => KnownColor.Violet.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Wheat { get; private set; } + public static ISolidColorBrush Wheat => KnownColor.Wheat.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush White { get; private set; } + public static ISolidColorBrush White => KnownColor.White.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush WhiteSmoke { get; private set; } + public static ISolidColorBrush WhiteSmoke => KnownColor.WhiteSmoke.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush Yellow { get; private set; } + public static ISolidColorBrush Yellow => KnownColor.Yellow.ToBrush(); /// /// Gets an colored brush. /// - public static ISolidColorBrush YellowGreen { get; private set; } + public static ISolidColorBrush YellowGreen => KnownColor.YellowGreen.ToBrush(); } } diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index cbf5a86fd61..82cc19347ab 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -88,6 +88,9 @@ public static Color FromUInt32(uint value) /// The . public static Color Parse(string s) { + if (s == null) throw new ArgumentNullException(nameof(s)); + if (s.Length == 0) throw new FormatException(); + if (s[0] == '#') { var or = 0u; @@ -103,21 +106,15 @@ public static Color Parse(string s) return FromUInt32(uint.Parse(s.Substring(1), NumberStyles.HexNumber, CultureInfo.InvariantCulture) | or); } - else - { - var upper = s.ToUpperInvariant(); - var member = typeof(Colors).GetTypeInfo().DeclaredProperties - .FirstOrDefault(x => x.Name.ToUpperInvariant() == upper); - if (member != null) - { - return (Color)member.GetValue(null); - } - else - { - throw new FormatException($"Invalid color string: '{s}'."); - } + var knownColor = KnownColors.GetKnownColor(s); + + if (knownColor != KnownColor.None) + { + return knownColor.ToColor(); } + + throw new FormatException($"Invalid color string: '{s}'."); } /// @@ -128,8 +125,8 @@ public static Color Parse(string s) /// public override string ToString() { - uint rgb = ((uint)A << 24) | ((uint)R << 16) | ((uint)G << 8) | (uint)B; - return $"#{rgb:x8}"; + uint rgb = ToUint32(); + return KnownColors.GetKnownColorName(rgb) ?? $"#{rgb:x8}"; } /// diff --git a/src/Avalonia.Visuals/Media/Colors.cs b/src/Avalonia.Visuals/Media/Colors.cs index 7296d7dd39c..1067cf66aab 100644 --- a/src/Avalonia.Visuals/Media/Colors.cs +++ b/src/Avalonia.Visuals/Media/Colors.cs @@ -1,6 +1,8 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System.Linq; + namespace Avalonia.Media { /// @@ -11,706 +13,706 @@ public sealed class Colors /// /// Gets a color with an ARGB value of #fff0f8ff. /// - public static Color AliceBlue => Color.FromUInt32(0xfff0f8ff); + public static Color AliceBlue => KnownColor.AliceBlue.ToColor(); /// /// Gets a color with an ARGB value of #fffaebd7. /// - public static Color AntiqueWhite => Color.FromUInt32(0xfffaebd7); + public static Color AntiqueWhite => KnownColor.AntiqueWhite.ToColor(); /// /// Gets a color with an ARGB value of #ff00ffff. /// - public static Color Aqua => Color.FromUInt32(0xff00ffff); + public static Color Aqua => KnownColor.Aqua.ToColor(); /// /// Gets a color with an ARGB value of #ff7fffd4. /// - public static Color Aquamarine => Color.FromUInt32(0xff7fffd4); + public static Color Aquamarine => KnownColor.Aquamarine.ToColor(); /// /// Gets a color with an ARGB value of #fff0ffff. /// - public static Color Azure => Color.FromUInt32(0xfff0ffff); + public static Color Azure => KnownColor.Azure.ToColor(); /// /// Gets a color with an ARGB value of #fff5f5dc. /// - public static Color Beige => Color.FromUInt32(0xfff5f5dc); + public static Color Beige => KnownColor.Beige.ToColor(); /// /// Gets a color with an ARGB value of #ffffe4c4. /// - public static Color Bisque => Color.FromUInt32(0xffffe4c4); + public static Color Bisque => KnownColor.Bisque.ToColor(); /// /// Gets a color with an ARGB value of #ff000000. /// - public static Color Black => Color.FromUInt32(0xff000000); + public static Color Black => KnownColor.Black.ToColor(); /// /// Gets a color with an ARGB value of #ffffebcd. /// - public static Color BlanchedAlmond => Color.FromUInt32(0xffffebcd); + public static Color BlanchedAlmond => KnownColor.BlanchedAlmond.ToColor(); /// /// Gets a color with an ARGB value of #ff0000ff. /// - public static Color Blue => Color.FromUInt32(0xff0000ff); + public static Color Blue => KnownColor.Blue.ToColor(); /// /// Gets a color with an ARGB value of #ff8a2be2. /// - public static Color BlueViolet => Color.FromUInt32(0xff8a2be2); + public static Color BlueViolet => KnownColor.BlueViolet.ToColor(); /// /// Gets a color with an ARGB value of #ffa52a2a. /// - public static Color Brown => Color.FromUInt32(0xffa52a2a); + public static Color Brown => KnownColor.Brown.ToColor(); /// /// Gets a color with an ARGB value of #ffdeb887. /// - public static Color BurlyWood => Color.FromUInt32(0xffdeb887); + public static Color BurlyWood => KnownColor.BurlyWood.ToColor(); /// /// Gets a color with an ARGB value of #ff5f9ea0. /// - public static Color CadetBlue => Color.FromUInt32(0xff5f9ea0); + public static Color CadetBlue => KnownColor.CadetBlue.ToColor(); /// /// Gets a color with an ARGB value of #ff7fff00. /// - public static Color Chartreuse => Color.FromUInt32(0xff7fff00); + public static Color Chartreuse => KnownColor.Chartreuse.ToColor(); /// /// Gets a color with an ARGB value of #ffd2691e. /// - public static Color Chocolate => Color.FromUInt32(0xffd2691e); + public static Color Chocolate => KnownColor.Chocolate.ToColor(); /// /// Gets a color with an ARGB value of #ffff7f50. /// - public static Color Coral => Color.FromUInt32(0xffff7f50); + public static Color Coral => KnownColor.Coral.ToColor(); /// /// Gets a color with an ARGB value of #ff6495ed. /// - public static Color CornflowerBlue => Color.FromUInt32(0xff6495ed); + public static Color CornflowerBlue => KnownColor.CornflowerBlue.ToColor(); /// /// Gets a color with an ARGB value of #fffff8dc. /// - public static Color Cornsilk => Color.FromUInt32(0xfffff8dc); + public static Color Cornsilk => KnownColor.Cornsilk.ToColor(); /// /// Gets a color with an ARGB value of #ffdc143c. /// - public static Color Crimson => Color.FromUInt32(0xffdc143c); + public static Color Crimson => KnownColor.Crimson.ToColor(); /// /// Gets a color with an ARGB value of #ff00ffff. /// - public static Color Cyan => Color.FromUInt32(0xff00ffff); + public static Color Cyan => KnownColor.Cyan.ToColor(); /// /// Gets a color with an ARGB value of #ff00008b. /// - public static Color DarkBlue => Color.FromUInt32(0xff00008b); + public static Color DarkBlue => KnownColor.DarkBlue.ToColor(); /// /// Gets a color with an ARGB value of #ff008b8b. /// - public static Color DarkCyan => Color.FromUInt32(0xff008b8b); + public static Color DarkCyan => KnownColor.DarkCyan.ToColor(); /// /// Gets a color with an ARGB value of #ffb8860b. /// - public static Color DarkGoldenrod => Color.FromUInt32(0xffb8860b); + public static Color DarkGoldenrod => KnownColor.DarkGoldenrod.ToColor(); /// /// Gets a color with an ARGB value of #ffa9a9a9. /// - public static Color DarkGray => Color.FromUInt32(0xffa9a9a9); + public static Color DarkGray => KnownColor.DarkGray.ToColor(); /// /// Gets a color with an ARGB value of #ff006400. /// - public static Color DarkGreen => Color.FromUInt32(0xff006400); + public static Color DarkGreen => KnownColor.DarkGreen.ToColor(); /// /// Gets a color with an ARGB value of #ffbdb76b. /// - public static Color DarkKhaki => Color.FromUInt32(0xffbdb76b); + public static Color DarkKhaki => KnownColor.DarkKhaki.ToColor(); /// /// Gets a color with an ARGB value of #ff8b008b. /// - public static Color DarkMagenta => Color.FromUInt32(0xff8b008b); + public static Color DarkMagenta => KnownColor.DarkMagenta.ToColor(); /// /// Gets a color with an ARGB value of #ff556b2f. /// - public static Color DarkOliveGreen => Color.FromUInt32(0xff556b2f); + public static Color DarkOliveGreen => KnownColor.DarkOliveGreen.ToColor(); /// /// Gets a color with an ARGB value of #ffff8c00. /// - public static Color DarkOrange => Color.FromUInt32(0xffff8c00); + public static Color DarkOrange => KnownColor.DarkOrange.ToColor(); /// /// Gets a color with an ARGB value of #ff9932cc. /// - public static Color DarkOrchid => Color.FromUInt32(0xff9932cc); + public static Color DarkOrchid => KnownColor.DarkOrchid.ToColor(); /// /// Gets a color with an ARGB value of #ff8b0000. /// - public static Color DarkRed => Color.FromUInt32(0xff8b0000); + public static Color DarkRed => KnownColor.DarkRed.ToColor(); /// /// Gets a color with an ARGB value of #ffe9967a. /// - public static Color DarkSalmon => Color.FromUInt32(0xffe9967a); + public static Color DarkSalmon => KnownColor.DarkSalmon.ToColor(); /// /// Gets a color with an ARGB value of #ff8fbc8f. /// - public static Color DarkSeaGreen => Color.FromUInt32(0xff8fbc8f); + public static Color DarkSeaGreen => KnownColor.DarkSeaGreen.ToColor(); /// /// Gets a color with an ARGB value of #ff483d8b. /// - public static Color DarkSlateBlue => Color.FromUInt32(0xff483d8b); + public static Color DarkSlateBlue => KnownColor.DarkSlateBlue.ToColor(); /// /// Gets a color with an ARGB value of #ff2f4f4f. /// - public static Color DarkSlateGray => Color.FromUInt32(0xff2f4f4f); + public static Color DarkSlateGray => KnownColor.DarkSlateGray.ToColor(); /// /// Gets a color with an ARGB value of #ff00ced1. /// - public static Color DarkTurquoise => Color.FromUInt32(0xff00ced1); + public static Color DarkTurquoise => KnownColor.DarkTurquoise.ToColor(); /// /// Gets a color with an ARGB value of #ff9400d3. /// - public static Color DarkViolet => Color.FromUInt32(0xff9400d3); + public static Color DarkViolet => KnownColor.DarkViolet.ToColor(); /// /// Gets a color with an ARGB value of #ffff1493. /// - public static Color DeepPink => Color.FromUInt32(0xffff1493); + public static Color DeepPink => KnownColor.DeepPink.ToColor(); /// /// Gets a color with an ARGB value of #ff00bfff. /// - public static Color DeepSkyBlue => Color.FromUInt32(0xff00bfff); + public static Color DeepSkyBlue => KnownColor.DeepSkyBlue.ToColor(); /// /// Gets a color with an ARGB value of #ff696969. /// - public static Color DimGray => Color.FromUInt32(0xff696969); + public static Color DimGray => KnownColor.DimGray.ToColor(); /// /// Gets a color with an ARGB value of #ff1e90ff. /// - public static Color DodgerBlue => Color.FromUInt32(0xff1e90ff); + public static Color DodgerBlue => KnownColor.DodgerBlue.ToColor(); /// /// Gets a color with an ARGB value of #ffb22222. /// - public static Color Firebrick => Color.FromUInt32(0xffb22222); + public static Color Firebrick => KnownColor.Firebrick.ToColor(); /// /// Gets a color with an ARGB value of #fffffaf0. /// - public static Color FloralWhite => Color.FromUInt32(0xfffffaf0); + public static Color FloralWhite => KnownColor.FloralWhite.ToColor(); /// /// Gets a color with an ARGB value of #ff228b22. /// - public static Color ForestGreen => Color.FromUInt32(0xff228b22); + public static Color ForestGreen => KnownColor.ForestGreen.ToColor(); /// /// Gets a color with an ARGB value of #ffff00ff. /// - public static Color Fuchsia => Color.FromUInt32(0xffff00ff); + public static Color Fuchsia => KnownColor.Fuchsia.ToColor(); /// /// Gets a color with an ARGB value of #ffdcdcdc. /// - public static Color Gainsboro => Color.FromUInt32(0xffdcdcdc); + public static Color Gainsboro => KnownColor.Gainsboro.ToColor(); /// /// Gets a color with an ARGB value of #fff8f8ff. /// - public static Color GhostWhite => Color.FromUInt32(0xfff8f8ff); + public static Color GhostWhite => KnownColor.GhostWhite.ToColor(); /// /// Gets a color with an ARGB value of #ffffd700. /// - public static Color Gold => Color.FromUInt32(0xffffd700); + public static Color Gold => KnownColor.Gold.ToColor(); /// /// Gets a color with an ARGB value of #ffdaa520. /// - public static Color Goldenrod => Color.FromUInt32(0xffdaa520); + public static Color Goldenrod => KnownColor.Goldenrod.ToColor(); /// /// Gets a color with an ARGB value of #ff808080. /// - public static Color Gray => Color.FromUInt32(0xff808080); + public static Color Gray => KnownColor.Gray.ToColor(); /// /// Gets a color with an ARGB value of #ff008000. /// - public static Color Green => Color.FromUInt32(0xff008000); + public static Color Green => KnownColor.Green.ToColor(); /// /// Gets a color with an ARGB value of #ffadff2f. /// - public static Color GreenYellow => Color.FromUInt32(0xffadff2f); + public static Color GreenYellow => KnownColor.GreenYellow.ToColor(); /// /// Gets a color with an ARGB value of #fff0fff0. /// - public static Color Honeydew => Color.FromUInt32(0xfff0fff0); + public static Color Honeydew => KnownColor.Honeydew.ToColor(); /// /// Gets a color with an ARGB value of #ffff69b4. /// - public static Color HotPink => Color.FromUInt32(0xffff69b4); + public static Color HotPink => KnownColor.HotPink.ToColor(); /// /// Gets a color with an ARGB value of #ffcd5c5c. /// - public static Color IndianRed => Color.FromUInt32(0xffcd5c5c); + public static Color IndianRed => KnownColor.IndianRed.ToColor(); /// /// Gets a color with an ARGB value of #ff4b0082. /// - public static Color Indigo => Color.FromUInt32(0xff4b0082); + public static Color Indigo => KnownColor.Indigo.ToColor(); /// /// Gets a color with an ARGB value of #fffffff0. /// - public static Color Ivory => Color.FromUInt32(0xfffffff0); + public static Color Ivory => KnownColor.Ivory.ToColor(); /// /// Gets a color with an ARGB value of #fff0e68c. /// - public static Color Khaki => Color.FromUInt32(0xfff0e68c); + public static Color Khaki => KnownColor.Khaki.ToColor(); /// /// Gets a color with an ARGB value of #ffe6e6fa. /// - public static Color Lavender => Color.FromUInt32(0xffe6e6fa); + public static Color Lavender => KnownColor.Lavender.ToColor(); /// /// Gets a color with an ARGB value of #fffff0f5. /// - public static Color LavenderBlush => Color.FromUInt32(0xfffff0f5); + public static Color LavenderBlush => KnownColor.LavenderBlush.ToColor(); /// /// Gets a color with an ARGB value of #ff7cfc00. /// - public static Color LawnGreen => Color.FromUInt32(0xff7cfc00); + public static Color LawnGreen => KnownColor.LawnGreen.ToColor(); /// /// Gets a color with an ARGB value of #fffffacd. /// - public static Color LemonChiffon => Color.FromUInt32(0xfffffacd); + public static Color LemonChiffon => KnownColor.LemonChiffon.ToColor(); /// /// Gets a color with an ARGB value of #ffadd8e6. /// - public static Color LightBlue => Color.FromUInt32(0xffadd8e6); + public static Color LightBlue => KnownColor.LightBlue.ToColor(); /// /// Gets a color with an ARGB value of #fff08080. /// - public static Color LightCoral => Color.FromUInt32(0xfff08080); + public static Color LightCoral => KnownColor.LightCoral.ToColor(); /// /// Gets a color with an ARGB value of #ffe0ffff. /// - public static Color LightCyan => Color.FromUInt32(0xffe0ffff); + public static Color LightCyan => KnownColor.LightCyan.ToColor(); /// /// Gets a color with an ARGB value of #fffafad2. /// - public static Color LightGoldenrodYellow => Color.FromUInt32(0xfffafad2); + public static Color LightGoldenrodYellow => KnownColor.LightGoldenrodYellow.ToColor(); /// /// Gets a color with an ARGB value of #ffd3d3d3. /// - public static Color LightGray => Color.FromUInt32(0xffd3d3d3); + public static Color LightGray => KnownColor.LightGray.ToColor(); /// /// Gets a color with an ARGB value of #ff90ee90. /// - public static Color LightGreen => Color.FromUInt32(0xff90ee90); + public static Color LightGreen => KnownColor.LightGreen.ToColor(); /// /// Gets a color with an ARGB value of #ffffb6c1. /// - public static Color LightPink => Color.FromUInt32(0xffffb6c1); + public static Color LightPink => KnownColor.LightPink.ToColor(); /// /// Gets a color with an ARGB value of #ffffa07a. /// - public static Color LightSalmon => Color.FromUInt32(0xffffa07a); + public static Color LightSalmon => KnownColor.LightSalmon.ToColor(); /// /// Gets a color with an ARGB value of #ff20b2aa. /// - public static Color LightSeaGreen => Color.FromUInt32(0xff20b2aa); + public static Color LightSeaGreen => KnownColor.LightSeaGreen.ToColor(); /// /// Gets a color with an ARGB value of #ff87cefa. /// - public static Color LightSkyBlue => Color.FromUInt32(0xff87cefa); + public static Color LightSkyBlue => KnownColor.LightSkyBlue.ToColor(); /// /// Gets a color with an ARGB value of #ff778899. /// - public static Color LightSlateGray => Color.FromUInt32(0xff778899); + public static Color LightSlateGray => KnownColor.LightSlateGray.ToColor(); /// /// Gets a color with an ARGB value of #ffb0c4de. /// - public static Color LightSteelBlue => Color.FromUInt32(0xffb0c4de); + public static Color LightSteelBlue => KnownColor.LightSteelBlue.ToColor(); /// /// Gets a color with an ARGB value of #ffffffe0. /// - public static Color LightYellow => Color.FromUInt32(0xffffffe0); + public static Color LightYellow => KnownColor.LightYellow.ToColor(); /// /// Gets a color with an ARGB value of #ff00ff00. /// - public static Color Lime => Color.FromUInt32(0xff00ff00); + public static Color Lime => KnownColor.Lime.ToColor(); /// /// Gets a color with an ARGB value of #ff32cd32. /// - public static Color LimeGreen => Color.FromUInt32(0xff32cd32); + public static Color LimeGreen => KnownColor.LimeGreen.ToColor(); /// /// Gets a color with an ARGB value of #fffaf0e6. /// - public static Color Linen => Color.FromUInt32(0xfffaf0e6); + public static Color Linen => KnownColor.Linen.ToColor(); /// /// Gets a color with an ARGB value of #ffff00ff. /// - public static Color Magenta => Color.FromUInt32(0xffff00ff); + public static Color Magenta => KnownColor.Magenta.ToColor(); /// /// Gets a color with an ARGB value of #ff800000. /// - public static Color Maroon => Color.FromUInt32(0xff800000); + public static Color Maroon => KnownColor.Maroon.ToColor(); /// /// Gets a color with an ARGB value of #ff66cdaa. /// - public static Color MediumAquamarine => Color.FromUInt32(0xff66cdaa); + public static Color MediumAquamarine => KnownColor.MediumAquamarine.ToColor(); /// /// Gets a color with an ARGB value of #ff0000cd. /// - public static Color MediumBlue => Color.FromUInt32(0xff0000cd); + public static Color MediumBlue => KnownColor.MediumBlue.ToColor(); /// /// Gets a color with an ARGB value of #ffba55d3. /// - public static Color MediumOrchid => Color.FromUInt32(0xffba55d3); + public static Color MediumOrchid => KnownColor.MediumOrchid.ToColor(); /// /// Gets a color with an ARGB value of #ff9370db. /// - public static Color MediumPurple => Color.FromUInt32(0xff9370db); + public static Color MediumPurple => KnownColor.MediumPurple.ToColor(); /// /// Gets a color with an ARGB value of #ff3cb371. /// - public static Color MediumSeaGreen => Color.FromUInt32(0xff3cb371); + public static Color MediumSeaGreen => KnownColor.MediumSeaGreen.ToColor(); /// /// Gets a color with an ARGB value of #ff7b68ee. /// - public static Color MediumSlateBlue => Color.FromUInt32(0xff7b68ee); + public static Color MediumSlateBlue => KnownColor.MediumSlateBlue.ToColor(); /// /// Gets a color with an ARGB value of #ff00fa9a. /// - public static Color MediumSpringGreen => Color.FromUInt32(0xff00fa9a); + public static Color MediumSpringGreen => KnownColor.MediumSpringGreen.ToColor(); /// /// Gets a color with an ARGB value of #ff48d1cc. /// - public static Color MediumTurquoise => Color.FromUInt32(0xff48d1cc); + public static Color MediumTurquoise => KnownColor.MediumTurquoise.ToColor(); /// /// Gets a color with an ARGB value of #ffc71585. /// - public static Color MediumVioletRed => Color.FromUInt32(0xffc71585); + public static Color MediumVioletRed => KnownColor.MediumVioletRed.ToColor(); /// /// Gets a color with an ARGB value of #ff191970. /// - public static Color MidnightBlue => Color.FromUInt32(0xff191970); + public static Color MidnightBlue => KnownColor.MidnightBlue.ToColor(); /// /// Gets a color with an ARGB value of #fff5fffa. /// - public static Color MintCream => Color.FromUInt32(0xfff5fffa); + public static Color MintCream => KnownColor.MintCream.ToColor(); /// /// Gets a color with an ARGB value of #ffffe4e1. /// - public static Color MistyRose => Color.FromUInt32(0xffffe4e1); + public static Color MistyRose => KnownColor.MistyRose.ToColor(); /// /// Gets a color with an ARGB value of #ffffe4b5. /// - public static Color Moccasin => Color.FromUInt32(0xffffe4b5); + public static Color Moccasin => KnownColor.Moccasin.ToColor(); /// /// Gets a color with an ARGB value of #ffffdead. /// - public static Color NavajoWhite => Color.FromUInt32(0xffffdead); + public static Color NavajoWhite => KnownColor.NavajoWhite.ToColor(); /// /// Gets a color with an ARGB value of #ff000080. /// - public static Color Navy => Color.FromUInt32(0xff000080); + public static Color Navy => KnownColor.Navy.ToColor(); /// /// Gets a color with an ARGB value of #fffdf5e6. /// - public static Color OldLace => Color.FromUInt32(0xfffdf5e6); + public static Color OldLace => KnownColor.OldLace.ToColor(); /// /// Gets a color with an ARGB value of #ff808000. /// - public static Color Olive => Color.FromUInt32(0xff808000); + public static Color Olive => KnownColor.Olive.ToColor(); /// /// Gets a color with an ARGB value of #ff6b8e23. /// - public static Color OliveDrab => Color.FromUInt32(0xff6b8e23); + public static Color OliveDrab => KnownColor.OliveDrab.ToColor(); /// /// Gets a color with an ARGB value of #ffffa500. /// - public static Color Orange => Color.FromUInt32(0xffffa500); + public static Color Orange => KnownColor.Orange.ToColor(); /// /// Gets a color with an ARGB value of #ffff4500. /// - public static Color OrangeRed => Color.FromUInt32(0xffff4500); + public static Color OrangeRed => KnownColor.OrangeRed.ToColor(); /// /// Gets a color with an ARGB value of #ffda70d6. /// - public static Color Orchid => Color.FromUInt32(0xffda70d6); + public static Color Orchid => KnownColor.Orchid.ToColor(); /// /// Gets a color with an ARGB value of #ffeee8aa. /// - public static Color PaleGoldenrod => Color.FromUInt32(0xffeee8aa); + public static Color PaleGoldenrod => KnownColor.PaleGoldenrod.ToColor(); /// /// Gets a color with an ARGB value of #ff98fb98. /// - public static Color PaleGreen => Color.FromUInt32(0xff98fb98); + public static Color PaleGreen => KnownColor.PaleGreen.ToColor(); /// /// Gets a color with an ARGB value of #ffafeeee. /// - public static Color PaleTurquoise => Color.FromUInt32(0xffafeeee); + public static Color PaleTurquoise => KnownColor.PaleTurquoise.ToColor(); /// /// Gets a color with an ARGB value of #ffdb7093. /// - public static Color PaleVioletRed => Color.FromUInt32(0xffdb7093); + public static Color PaleVioletRed => KnownColor.PaleVioletRed.ToColor(); /// /// Gets a color with an ARGB value of #ffffefd5. /// - public static Color PapayaWhip => Color.FromUInt32(0xffffefd5); + public static Color PapayaWhip => KnownColor.PapayaWhip.ToColor(); /// /// Gets a color with an ARGB value of #ffffdab9. /// - public static Color PeachPuff => Color.FromUInt32(0xffffdab9); + public static Color PeachPuff => KnownColor.PeachPuff.ToColor(); /// /// Gets a color with an ARGB value of #ffcd853f. /// - public static Color Peru => Color.FromUInt32(0xffcd853f); + public static Color Peru => KnownColor.Peru.ToColor(); /// /// Gets a color with an ARGB value of #ffffc0cb. /// - public static Color Pink => Color.FromUInt32(0xffffc0cb); + public static Color Pink => KnownColor.Pink.ToColor(); /// /// Gets a color with an ARGB value of #ffdda0dd. /// - public static Color Plum => Color.FromUInt32(0xffdda0dd); + public static Color Plum => KnownColor.Plum.ToColor(); /// /// Gets a color with an ARGB value of #ffb0e0e6. /// - public static Color PowderBlue => Color.FromUInt32(0xffb0e0e6); + public static Color PowderBlue => KnownColor.PowderBlue.ToColor(); /// /// Gets a color with an ARGB value of #ff800080. /// - public static Color Purple => Color.FromUInt32(0xff800080); + public static Color Purple => KnownColor.Purple.ToColor(); /// /// Gets a color with an ARGB value of #ffff0000. /// - public static Color Red => Color.FromUInt32(0xffff0000); + public static Color Red => KnownColor.Red.ToColor(); /// /// Gets a color with an ARGB value of #ffbc8f8f. /// - public static Color RosyBrown => Color.FromUInt32(0xffbc8f8f); + public static Color RosyBrown => KnownColor.RosyBrown.ToColor(); /// /// Gets a color with an ARGB value of #ff4169e1. /// - public static Color RoyalBlue => Color.FromUInt32(0xff4169e1); + public static Color RoyalBlue => KnownColor.RoyalBlue.ToColor(); /// /// Gets a color with an ARGB value of #ff8b4513. /// - public static Color SaddleBrown => Color.FromUInt32(0xff8b4513); + public static Color SaddleBrown => KnownColor.SaddleBrown.ToColor(); /// /// Gets a color with an ARGB value of #fffa8072. /// - public static Color Salmon => Color.FromUInt32(0xfffa8072); + public static Color Salmon => KnownColor.Salmon.ToColor(); /// /// Gets a color with an ARGB value of #fff4a460. /// - public static Color SandyBrown => Color.FromUInt32(0xfff4a460); + public static Color SandyBrown => KnownColor.SandyBrown.ToColor(); /// /// Gets a color with an ARGB value of #ff2e8b57. /// - public static Color SeaGreen => Color.FromUInt32(0xff2e8b57); + public static Color SeaGreen => KnownColor.SeaGreen.ToColor(); /// /// Gets a color with an ARGB value of #fffff5ee. /// - public static Color SeaShell => Color.FromUInt32(0xfffff5ee); + public static Color SeaShell => KnownColor.SeaShell.ToColor(); /// /// Gets a color with an ARGB value of #ffa0522d. /// - public static Color Sienna => Color.FromUInt32(0xffa0522d); + public static Color Sienna => KnownColor.Sienna.ToColor(); /// /// Gets a color with an ARGB value of #ffc0c0c0. /// - public static Color Silver => Color.FromUInt32(0xffc0c0c0); + public static Color Silver => KnownColor.Silver.ToColor(); /// /// Gets a color with an ARGB value of #ff87ceeb. /// - public static Color SkyBlue => Color.FromUInt32(0xff87ceeb); + public static Color SkyBlue => KnownColor.SkyBlue.ToColor(); /// /// Gets a color with an ARGB value of #ff6a5acd. /// - public static Color SlateBlue => Color.FromUInt32(0xff6a5acd); + public static Color SlateBlue => KnownColor.SlateBlue.ToColor(); /// /// Gets a color with an ARGB value of #ff708090. /// - public static Color SlateGray => Color.FromUInt32(0xff708090); + public static Color SlateGray => KnownColor.SlateGray.ToColor(); /// /// Gets a color with an ARGB value of #fffffafa. /// - public static Color Snow => Color.FromUInt32(0xfffffafa); + public static Color Snow => KnownColor.Snow.ToColor(); /// /// Gets a color with an ARGB value of #ff00ff7f. /// - public static Color SpringGreen => Color.FromUInt32(0xff00ff7f); + public static Color SpringGreen => KnownColor.SpringGreen.ToColor(); /// /// Gets a color with an ARGB value of #ff4682b4. /// - public static Color SteelBlue => Color.FromUInt32(0xff4682b4); + public static Color SteelBlue => KnownColor.SteelBlue.ToColor(); /// /// Gets a color with an ARGB value of #ffd2b48c. /// - public static Color Tan => Color.FromUInt32(0xffd2b48c); + public static Color Tan => KnownColor.Tan.ToColor(); /// /// Gets a color with an ARGB value of #ff008080. /// - public static Color Teal => Color.FromUInt32(0xff008080); + public static Color Teal => KnownColor.Teal.ToColor(); /// /// Gets a color with an ARGB value of #ffd8bfd8. /// - public static Color Thistle => Color.FromUInt32(0xffd8bfd8); + public static Color Thistle => KnownColor.Thistle.ToColor(); /// /// Gets a color with an ARGB value of #ffff6347. /// - public static Color Tomato => Color.FromUInt32(0xffff6347); + public static Color Tomato => KnownColor.Tomato.ToColor(); /// /// Gets a color with an ARGB value of #00ffffff. /// - public static Color Transparent => Color.FromUInt32(0x00ffffff); + public static Color Transparent => KnownColor.Transparent.ToColor(); /// /// Gets a color with an ARGB value of #ff40e0d0. /// - public static Color Turquoise => Color.FromUInt32(0xff40e0d0); + public static Color Turquoise => KnownColor.Turquoise.ToColor(); /// /// Gets a color with an ARGB value of #ffee82ee. /// - public static Color Violet => Color.FromUInt32(0xffee82ee); + public static Color Violet => KnownColor.Violet.ToColor(); /// /// Gets a color with an ARGB value of #fff5deb3. /// - public static Color Wheat => Color.FromUInt32(0xfff5deb3); + public static Color Wheat => KnownColor.Wheat.ToColor(); /// /// Gets a color with an ARGB value of #ffffffff. /// - public static Color White => Color.FromUInt32(0xffffffff); + public static Color White => KnownColor.White.ToColor(); /// /// Gets a color with an ARGB value of #fff5f5f5. /// - public static Color WhiteSmoke => Color.FromUInt32(0xfff5f5f5); + public static Color WhiteSmoke => KnownColor.WhiteSmoke.ToColor(); /// /// Gets a color with an ARGB value of #ffffff00. /// - public static Color Yellow => Color.FromUInt32(0xffffff00); + public static Color Yellow => KnownColor.Yellow.ToColor(); /// /// Gets a color with an ARGB value of #ff9acd32. /// - public static Color YellowGreen => Color.FromUInt32(0xff9acd32); + public static Color YellowGreen => KnownColor.YellowGreen.ToColor(); } } \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/KnownColors.cs b/src/Avalonia.Visuals/Media/KnownColors.cs new file mode 100644 index 00000000000..0887d2c913c --- /dev/null +++ b/src/Avalonia.Visuals/Media/KnownColors.cs @@ -0,0 +1,224 @@ +using System; +using System.Reflection; +using System.Collections.Generic; + +namespace Avalonia.Media +{ + internal static class KnownColors + { + private static readonly IReadOnlyDictionary _knownColorNames; + private static readonly IReadOnlyDictionary _knownColors; + private static readonly Dictionary _knownBrushes; + + static KnownColors() + { + var knownColorNames = new Dictionary(StringComparer.OrdinalIgnoreCase); + var knownColors = new Dictionary(); + + foreach (var field in typeof(KnownColor).GetRuntimeFields()) + { + if (field.FieldType != typeof(KnownColor)) continue; + var knownColor = (KnownColor)field.GetValue(null); + if (knownColor == KnownColor.None) continue; + + knownColorNames.Add(field.Name, knownColor); + + // some known colors have the same value, so use the first + if (!knownColors.ContainsKey((uint)knownColor)) + { + knownColors.Add((uint)knownColor, field.Name); + } + } + + _knownColorNames = knownColorNames; + _knownColors = knownColors; + _knownBrushes = new Dictionary(); + } + + public static ISolidColorBrush GetKnownBrush(string s) + { + var color = GetKnownColor(s); + return color != KnownColor.None ? color.ToBrush() : null; + } + + public static KnownColor GetKnownColor(string s) + { + if (_knownColorNames.TryGetValue(s, out var color)) + { + return color; + } + + return KnownColor.None; + } + + public static string GetKnownColorName(uint rgb) + { + return _knownColors.TryGetValue(rgb, out var name) ? name : null; + } + + public static Color ToColor(this KnownColor color) + { + return Color.FromUInt32((uint)color); + } + + public static ISolidColorBrush ToBrush(this KnownColor color) + { + lock (_knownBrushes) + { + if (!_knownBrushes.TryGetValue(color, out var brush)) + { + brush = new Immutable.ImmutableSolidColorBrush(color.ToColor()); + _knownBrushes.Add(color, brush); + } + + return brush; + } + } + } + + internal enum KnownColor : uint + { + None, + AliceBlue = 0xfff0f8ff, + AntiqueWhite = 0xfffaebd7, + Aqua = 0xff00ffff, + Aquamarine = 0xff7fffd4, + Azure = 0xfff0ffff, + Beige = 0xfff5f5dc, + Bisque = 0xffffe4c4, + Black = 0xff000000, + BlanchedAlmond = 0xffffebcd, + Blue = 0xff0000ff, + BlueViolet = 0xff8a2be2, + Brown = 0xffa52a2a, + BurlyWood = 0xffdeb887, + CadetBlue = 0xff5f9ea0, + Chartreuse = 0xff7fff00, + Chocolate = 0xffd2691e, + Coral = 0xffff7f50, + CornflowerBlue = 0xff6495ed, + Cornsilk = 0xfffff8dc, + Crimson = 0xffdc143c, + Cyan = 0xff00ffff, + DarkBlue = 0xff00008b, + DarkCyan = 0xff008b8b, + DarkGoldenrod = 0xffb8860b, + DarkGray = 0xffa9a9a9, + DarkGreen = 0xff006400, + DarkKhaki = 0xffbdb76b, + DarkMagenta = 0xff8b008b, + DarkOliveGreen = 0xff556b2f, + DarkOrange = 0xffff8c00, + DarkOrchid = 0xff9932cc, + DarkRed = 0xff8b0000, + DarkSalmon = 0xffe9967a, + DarkSeaGreen = 0xff8fbc8f, + DarkSlateBlue = 0xff483d8b, + DarkSlateGray = 0xff2f4f4f, + DarkTurquoise = 0xff00ced1, + DarkViolet = 0xff9400d3, + DeepPink = 0xffff1493, + DeepSkyBlue = 0xff00bfff, + DimGray = 0xff696969, + DodgerBlue = 0xff1e90ff, + Firebrick = 0xffb22222, + FloralWhite = 0xfffffaf0, + ForestGreen = 0xff228b22, + Fuchsia = 0xffff00ff, + Gainsboro = 0xffdcdcdc, + GhostWhite = 0xfff8f8ff, + Gold = 0xffffd700, + Goldenrod = 0xffdaa520, + Gray = 0xff808080, + Green = 0xff008000, + GreenYellow = 0xffadff2f, + Honeydew = 0xfff0fff0, + HotPink = 0xffff69b4, + IndianRed = 0xffcd5c5c, + Indigo = 0xff4b0082, + Ivory = 0xfffffff0, + Khaki = 0xfff0e68c, + Lavender = 0xffe6e6fa, + LavenderBlush = 0xfffff0f5, + LawnGreen = 0xff7cfc00, + LemonChiffon = 0xfffffacd, + LightBlue = 0xffadd8e6, + LightCoral = 0xfff08080, + LightCyan = 0xffe0ffff, + LightGoldenrodYellow = 0xfffafad2, + LightGreen = 0xff90ee90, + LightGray = 0xffd3d3d3, + LightPink = 0xffffb6c1, + LightSalmon = 0xffffa07a, + LightSeaGreen = 0xff20b2aa, + LightSkyBlue = 0xff87cefa, + LightSlateGray = 0xff778899, + LightSteelBlue = 0xffb0c4de, + LightYellow = 0xffffffe0, + Lime = 0xff00ff00, + LimeGreen = 0xff32cd32, + Linen = 0xfffaf0e6, + Magenta = 0xffff00ff, + Maroon = 0xff800000, + MediumAquamarine = 0xff66cdaa, + MediumBlue = 0xff0000cd, + MediumOrchid = 0xffba55d3, + MediumPurple = 0xff9370db, + MediumSeaGreen = 0xff3cb371, + MediumSlateBlue = 0xff7b68ee, + MediumSpringGreen = 0xff00fa9a, + MediumTurquoise = 0xff48d1cc, + MediumVioletRed = 0xffc71585, + MidnightBlue = 0xff191970, + MintCream = 0xfff5fffa, + MistyRose = 0xffffe4e1, + Moccasin = 0xffffe4b5, + NavajoWhite = 0xffffdead, + Navy = 0xff000080, + OldLace = 0xfffdf5e6, + Olive = 0xff808000, + OliveDrab = 0xff6b8e23, + Orange = 0xffffa500, + OrangeRed = 0xffff4500, + Orchid = 0xffda70d6, + PaleGoldenrod = 0xffeee8aa, + PaleGreen = 0xff98fb98, + PaleTurquoise = 0xffafeeee, + PaleVioletRed = 0xffdb7093, + PapayaWhip = 0xffffefd5, + PeachPuff = 0xffffdab9, + Peru = 0xffcd853f, + Pink = 0xffffc0cb, + Plum = 0xffdda0dd, + PowderBlue = 0xffb0e0e6, + Purple = 0xff800080, + Red = 0xffff0000, + RosyBrown = 0xffbc8f8f, + RoyalBlue = 0xff4169e1, + SaddleBrown = 0xff8b4513, + Salmon = 0xfffa8072, + SandyBrown = 0xfff4a460, + SeaGreen = 0xff2e8b57, + SeaShell = 0xfffff5ee, + Sienna = 0xffa0522d, + Silver = 0xffc0c0c0, + SkyBlue = 0xff87ceeb, + SlateBlue = 0xff6a5acd, + SlateGray = 0xff708090, + Snow = 0xfffffafa, + SpringGreen = 0xff00ff7f, + SteelBlue = 0xff4682b4, + Tan = 0xffd2b48c, + Teal = 0xff008080, + Thistle = 0xffd8bfd8, + Tomato = 0xffff6347, + Transparent = 0x00ffffff, + Turquoise = 0xff40e0d0, + Violet = 0xffee82ee, + Wheat = 0xfff5deb3, + White = 0xffffffff, + WhiteSmoke = 0xfff5f5f5, + Yellow = 0xffffff00, + YellowGreen = 0xff9acd32 + } +} \ No newline at end of file diff --git a/tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs index ae88a940739..a6015c52e59 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs @@ -12,7 +12,7 @@ public class BrushTests [Fact] public void Parse_Parses_RGB_Hash_Brush() { - var result = (SolidColorBrush)Brush.Parse("#ff8844"); + var result = (ISolidColorBrush)Brush.Parse("#ff8844"); Assert.Equal(0xff, result.Color.R); Assert.Equal(0x88, result.Color.G); @@ -23,7 +23,7 @@ public void Parse_Parses_RGB_Hash_Brush() [Fact] public void Parse_Parses_ARGB_Hash_Brush() { - var result = (SolidColorBrush)Brush.Parse("#40ff8844"); + var result = (ISolidColorBrush)Brush.Parse("#40ff8844"); Assert.Equal(0xff, result.Color.R); Assert.Equal(0x88, result.Color.G); @@ -34,7 +34,7 @@ public void Parse_Parses_ARGB_Hash_Brush() [Fact] public void Parse_Parses_Named_Brush_Lowercase() { - var result = (SolidColorBrush)Brush.Parse("red"); + var result = (ISolidColorBrush)Brush.Parse("red"); Assert.Equal(0xff, result.Color.R); Assert.Equal(0x00, result.Color.G); @@ -45,7 +45,7 @@ public void Parse_Parses_Named_Brush_Lowercase() [Fact] public void Parse_Parses_Named_Brush_Uppercase() { - var result = (SolidColorBrush)Brush.Parse("RED"); + var result = (ISolidColorBrush)Brush.Parse("RED"); Assert.Equal(0xff, result.Color.R); Assert.Equal(0x00, result.Color.G); @@ -53,6 +53,16 @@ public void Parse_Parses_Named_Brush_Uppercase() Assert.Equal(0xff, result.Color.A); } + [Fact] + public void Parse_ToString_Named_Brush_Roundtrip() + { + const string expectedName = "Red"; + var brush = (ISolidColorBrush)Brush.Parse(expectedName); + var name = brush.ToString(); + + Assert.Equal(expectedName, name); + } + [Fact] public void Parse_Hex_Value_Doesnt_Accept_Too_Few_Chars() { From edf6b12c4d7c9ba6844201f6c29f83b9ac9d4b1d Mon Sep 17 00:00:00 2001 From: Florian Sundermann Date: Sat, 3 Mar 2018 11:28:21 +0100 Subject: [PATCH 03/59] first bits of drag+drop support for osx --- src/Avalonia.Controls/DragDrop/DataFormats.cs | 15 ++++ .../DragDrop/DragOperation.cs | 13 ++++ src/Avalonia.Controls/DragDrop/IDragData.cs | 15 ++++ .../DragDrop/IDragDispatcher.cs | 12 +++ .../Avalonia.MonoMac/Avalonia.MonoMac.csproj | 4 +- src/OSX/Avalonia.MonoMac/DraggingInfo.cs | 63 +++++++++++++++ src/OSX/Avalonia.MonoMac/TopLevelImpl.cs | 76 +++++++++++++++++++ 7 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 src/Avalonia.Controls/DragDrop/DataFormats.cs create mode 100644 src/Avalonia.Controls/DragDrop/DragOperation.cs create mode 100644 src/Avalonia.Controls/DragDrop/IDragData.cs create mode 100644 src/Avalonia.Controls/DragDrop/IDragDispatcher.cs create mode 100644 src/OSX/Avalonia.MonoMac/DraggingInfo.cs diff --git a/src/Avalonia.Controls/DragDrop/DataFormats.cs b/src/Avalonia.Controls/DragDrop/DataFormats.cs new file mode 100644 index 00000000000..f46651ed3bd --- /dev/null +++ b/src/Avalonia.Controls/DragDrop/DataFormats.cs @@ -0,0 +1,15 @@ +namespace Avalonia.Controls.DragDrop +{ + public static class DataFormats + { + /// + /// Dataformat for plaintext + /// + public static string Text = nameof(Text); + + /// + /// Dataformat for one or more filenames + /// + public static string FileNames = nameof(FileNames); + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/DragOperation.cs b/src/Avalonia.Controls/DragDrop/DragOperation.cs new file mode 100644 index 00000000000..ffc6f666f79 --- /dev/null +++ b/src/Avalonia.Controls/DragDrop/DragOperation.cs @@ -0,0 +1,13 @@ +using System; + +namespace Avalonia.Controls.DragDrop +{ + [Flags] + public enum DragOperation + { + None = 0, + Copy = 1, + Move = 2, + Link = 4, + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/IDragData.cs b/src/Avalonia.Controls/DragDrop/IDragData.cs new file mode 100644 index 00000000000..b6dc53d32d4 --- /dev/null +++ b/src/Avalonia.Controls/DragDrop/IDragData.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Avalonia.Controls.DragDrop +{ + public interface IDragData + { + IEnumerable GetDataFormats(); + + bool Contains(string dataFormat); + + string GetText(); + + IEnumerable GetFileNames(); + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs b/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs new file mode 100644 index 00000000000..5c39635dd04 --- /dev/null +++ b/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs @@ -0,0 +1,12 @@ +using Avalonia.Input; + +namespace Avalonia.Controls.DragDrop +{ + public interface IDragDispatcher + { + DragOperation DragEnter(IInputRoot inputRoot, Point point, IDragData data, DragOperation operation); + DragOperation DragOver(IInputRoot inputRoot, Point point, IDragData data, DragOperation operation); + void DragLeave(IInputRoot inputRoot); + DragOperation Drop(IInputRoot inputRoot, Point point, IDragData data, DragOperation operation); + } +} \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj index 5f6be915718..c31c131ea94 100644 --- a/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj +++ b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 True @@ -19,4 +19,4 @@ - + \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs new file mode 100644 index 00000000000..3abcb8c68c7 --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using Avalonia.Controls.DragDrop; +using MonoMac.AppKit; + +namespace Avalonia.MonoMac +{ + class DraggingInfo : IDragData + { + private readonly NSDraggingInfo _info; + + public DraggingInfo(NSDraggingInfo info) + { + _info = info; + } + + + internal static NSDragOperation ConvertDragOperation(DragOperation d) + { + NSDragOperation result = NSDragOperation.None; + if (d.HasFlag(DragOperation.Copy)) + result |= NSDragOperation.Copy; + if (d.HasFlag(DragOperation.Link)) + result |= NSDragOperation.Link; + if (d.HasFlag(DragOperation.Move)) + result |= NSDragOperation.Move; + return result; + } + + internal static DragOperation ConvertDragOperation(NSDragOperation d) + { + DragOperation result = DragOperation.None; + if (d.HasFlag(NSDragOperation.Copy)) + result |= DragOperation.Copy; + if (d.HasFlag(NSDragOperation.Link)) + result |= DragOperation.Link; + if (d.HasFlag(NSDragOperation.Move)) + result |= DragOperation.Move; + return result; + } + + public Point Location => new Point(_info.DraggingLocation.X, _info.DraggingLocation.Y); + + public IEnumerable GetDataFormats() + { + yield break; + } + + public string GetText() + { + return null; + } + + public IEnumerable GetFileNames() + { + yield break; + } + + public bool Contains(string dataFormat) + { + return false; + } + } +} \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs index 667ee12fa0d..91e822f8027 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Avalonia.Controls.DragDrop; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Platform; @@ -36,6 +37,7 @@ public class TopLevelView : NSView bool _isLeftPressed, _isRightPressed, _isMiddlePressed; private readonly IMouseDevice _mouse; private readonly IKeyboardDevice _keyboard; + private readonly IDragDispatcher _dragDispatcher; private NSTrackingArea _area; private NSCursor _cursor; private bool _nonUiRedrawQueued; @@ -52,6 +54,11 @@ public TopLevelView(TopLevelImpl tl) _tl = tl; _mouse = AvaloniaLocator.Current.GetService(); _keyboard = AvaloniaLocator.Current.GetService(); + _dragDispatcher = AvaloniaLocator.Current.GetService(); + + RegisterForDraggedTypes(new string[] { + "public.data" // register for any kind of data. + }); } protected override void Dispose(bool disposing) @@ -144,6 +151,75 @@ public void SetCursor(NSCursor cursor) UpdateCursor(); } + public override NSDragOperation DraggingEntered(NSDraggingInfo sender) + { + IInputRoot root = _tl?.InputRoot; + if (root == null || _dragDispatcher == null) + return NSDragOperation.None; + + var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask); + DraggingInfo info = new DraggingInfo(sender); + var pt = TranslateLocalPoint(info.Location); + + dragOp = _dragDispatcher.DragEnter(root, pt, info, dragOp); + + return DraggingInfo.ConvertDragOperation(dragOp); + } + + public override NSDragOperation DraggingUpdated(NSDraggingInfo sender) + { + IInputRoot root = _tl?.InputRoot; + if (root == null || _dragDispatcher == null) + return NSDragOperation.None; + + var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask); + DraggingInfo info = new DraggingInfo(sender); + var pt = TranslateLocalPoint(info.Location); + + dragOp = _dragDispatcher.DragOver(root, pt, info, dragOp); + + return DraggingInfo.ConvertDragOperation(dragOp); + } + + public override void DraggingExited(NSDraggingInfo sender) + { + IInputRoot root = _tl?.InputRoot; + if (root == null || _dragDispatcher == null) + return; + _dragDispatcher.DragLeave(root); + } + + public override bool PrepareForDragOperation(NSDraggingInfo sender) + { + IInputRoot root = _tl?.InputRoot; + if (root == null || _dragDispatcher == null) + return false; + + var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask); + DraggingInfo info = new DraggingInfo(sender); + var pt = TranslateLocalPoint(info.Location); + + dragOp = _dragDispatcher.DragOver(root, pt, info, dragOp); + + return DraggingInfo.ConvertDragOperation(dragOp) != DragOperation.None; + } + + public override bool PerformDragOperation(NSDraggingInfo sender) + { + IInputRoot root = _tl?.InputRoot; + if (root == null || _dragDispatcher == null) + return false; + + var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask); + DraggingInfo info = new DraggingInfo(sender); + var pt = TranslateLocalPoint(info.Location); + + dragOp = _dragDispatcher.Drop(root, pt, info, dragOp); + + return DraggingInfo.ConvertDragOperation(dragOp) != DragOperation.None; + } + + public override void SetFrameSize(CGSize newSize) { lock (SyncRoot) From 9ae9f6d2218de3bbde789fb044f22b773b31177d Mon Sep 17 00:00:00 2001 From: Florian Sundermann Date: Sat, 3 Mar 2018 12:40:06 +0100 Subject: [PATCH 04/59] raise routed events on drag and drop --- src/Avalonia.Controls/Application.cs | 4 +- .../DragDrop/DefaultDragDispatcher.cs | 90 +++++++++++++++++++ src/Avalonia.Controls/DragDrop/DragDrop.cs | 24 +++++ .../DragDrop/DragEventArgs.cs | 18 ++++ .../DragDrop/IDragDispatcher.cs | 11 ++- .../Properties/AssemblyInfo.cs | 1 + src/OSX/Avalonia.MonoMac/TopLevelImpl.cs | 4 +- 7 files changed, 145 insertions(+), 7 deletions(-) create mode 100644 src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs create mode 100644 src/Avalonia.Controls/DragDrop/DragDrop.cs create mode 100644 src/Avalonia.Controls/DragDrop/DragEventArgs.cs diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 06c1a8b4ccb..63b17530ff6 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -12,6 +12,7 @@ using Avalonia.Styling; using Avalonia.Threading; using System.Reactive.Concurrency; +using Avalonia.Controls.DragDrop; namespace Avalonia { @@ -234,7 +235,8 @@ public virtual void RegisterServices() .Bind().ToConstant(_styler) .Bind().ToSingleton() .Bind().ToConstant(this) - .Bind().ToConstant(AvaloniaScheduler.Instance); + .Bind().ToConstant(AvaloniaScheduler.Instance) + .Bind().ToConstant(DefaultDragDispatcher.Instance); } } } diff --git a/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs b/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs new file mode 100644 index 00000000000..2ce5a32dc53 --- /dev/null +++ b/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs @@ -0,0 +1,90 @@ +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.VisualTree; +using System.Linq; + +namespace Avalonia.Controls.DragDrop +{ + class DefaultDragDispatcher : IDragDispatcher + { + public static readonly DefaultDragDispatcher Instance = new DefaultDragDispatcher(); + + private Interactive _lastTarget = null; + + private DefaultDragDispatcher() + { + } + + private Interactive GetTarget(IInputElement root, Point local) + { + var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType()?.FirstOrDefault(); + if (target != null && DragDrop.GetAcceptDrag(target)) + return target; + return null; + } + + private DragOperation RaiseDragEvent(Interactive target, RoutedEvent routedEvent, DragOperation operation, IDragData data) + { + if (target == null) + return DragOperation.None; + var args = new DragEventArgs(routedEvent, data) + { + RoutedEvent = routedEvent, + DragOperation = operation + }; + target.RaiseEvent(args); + return args.DragOperation; + } + + public DragOperation DragEnter(IInputElement inputRoot, Point point, IDragData data, DragOperation operation) + { + _lastTarget = GetTarget(inputRoot, point); + return RaiseDragEvent(_lastTarget, DragDrop.DragEnterEvent, operation, data); + } + + public DragOperation DragOver(IInputElement inputRoot, Point point, IDragData data, DragOperation operation) + { + var target = GetTarget(inputRoot, point); + + if (target == _lastTarget) + return RaiseDragEvent(target, DragDrop.DragOverEvent, operation, data); + + try + { + if (_lastTarget != null) + _lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent)); + return RaiseDragEvent(target, DragDrop.DragEnterEvent, operation, data); + } + finally + { + _lastTarget = target; + } + } + + public void DragLeave(IInputElement inputRoot) + { + if (_lastTarget == null) + return; + try + { + _lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent)); + } + finally + { + _lastTarget = null; + } + } + + public DragOperation Drop(IInputElement inputRoot, Point point, IDragData data, DragOperation operation) + { + try + { + return RaiseDragEvent(_lastTarget, DragDrop.DropEvent, operation, data); + } + finally + { + _lastTarget = null; + } + } + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/DragDrop.cs b/src/Avalonia.Controls/DragDrop/DragDrop.cs new file mode 100644 index 00000000000..3645409a5a1 --- /dev/null +++ b/src/Avalonia.Controls/DragDrop/DragDrop.cs @@ -0,0 +1,24 @@ +using Avalonia.Interactivity; + +namespace Avalonia.Controls.DragDrop +{ + public sealed class DragDrop : AvaloniaObject + { + public static RoutedEvent DragEnterEvent = RoutedEvent.Register("DragEnter", RoutingStrategies.Bubble, typeof(DragDrop)); + public static RoutedEvent DragLeaveEvent = RoutedEvent.Register("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop)); + public static RoutedEvent DragOverEvent = RoutedEvent.Register("DragOver", RoutingStrategies.Bubble, typeof(DragDrop)); + public static RoutedEvent DropEvent = RoutedEvent.Register("Drop", RoutingStrategies.Bubble, typeof(DragDrop)); + + public static AvaloniaProperty AcceptDragProperty = AvaloniaProperty.RegisterAttached("AcceptDrag", typeof(DragDrop), inherits: true); + + public static bool GetAcceptDrag(Interactive interactive) + { + return interactive.GetValue(AcceptDragProperty); + } + + public static void SetAcceptDrag(Interactive interactive, bool value) + { + interactive.SetValue(AcceptDragProperty, value); + } + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/DragEventArgs.cs b/src/Avalonia.Controls/DragDrop/DragEventArgs.cs new file mode 100644 index 00000000000..e8bd2528c72 --- /dev/null +++ b/src/Avalonia.Controls/DragDrop/DragEventArgs.cs @@ -0,0 +1,18 @@ +using Avalonia.Interactivity; + +namespace Avalonia.Controls.DragDrop +{ + public class DragEventArgs : RoutedEventArgs + { + public DragOperation DragOperation { get; set; } + + public IDragData Data { get; private set; } + + public DragEventArgs(RoutedEvent routedEvent, IDragData data) + : base(routedEvent) + { + this.Data = data; + } + + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs b/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs index 5c39635dd04..05a211ff76e 100644 --- a/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs +++ b/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs @@ -2,11 +2,14 @@ namespace Avalonia.Controls.DragDrop { + /// + /// Dispatches Drag+Drop events to the correct visual targets, based on the input root and the drag location. + /// public interface IDragDispatcher { - DragOperation DragEnter(IInputRoot inputRoot, Point point, IDragData data, DragOperation operation); - DragOperation DragOver(IInputRoot inputRoot, Point point, IDragData data, DragOperation operation); - void DragLeave(IInputRoot inputRoot); - DragOperation Drop(IInputRoot inputRoot, Point point, IDragData data, DragOperation operation); + DragOperation DragEnter(IInputElement inputRoot, Point point, IDragData data, DragOperation operation); + DragOperation DragOver(IInputElement inputRoot, Point point, IDragData data, DragOperation operation); + void DragLeave(IInputElement inputRoot); + DragOperation Drop(IInputElement inputRoot, Point point, IDragData data, DragOperation operation); } } \ No newline at end of file diff --git a/src/Avalonia.Controls/Properties/AssemblyInfo.cs b/src/Avalonia.Controls/Properties/AssemblyInfo.cs index ae8c88f7e8c..b0877e0fb7f 100644 --- a/src/Avalonia.Controls/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Controls/Properties/AssemblyInfo.cs @@ -11,6 +11,7 @@ [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.DragDrop")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Embedding")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Presenters")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")] diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs index 91e822f8027..711f6d27879 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -201,7 +201,7 @@ public override bool PrepareForDragOperation(NSDraggingInfo sender) dragOp = _dragDispatcher.DragOver(root, pt, info, dragOp); - return DraggingInfo.ConvertDragOperation(dragOp) != DragOperation.None; + return dragOp != DragOperation.None; } public override bool PerformDragOperation(NSDraggingInfo sender) @@ -216,7 +216,7 @@ public override bool PerformDragOperation(NSDraggingInfo sender) dragOp = _dragDispatcher.Drop(root, pt, info, dragOp); - return DraggingInfo.ConvertDragOperation(dragOp) != DragOperation.None; + return dragOp != DragOperation.None; } From a6e8dc0ffc79d00cb635109d6c3d70b6d105fa49 Mon Sep 17 00:00:00 2001 From: boombuler Date: Sat, 3 Mar 2018 13:48:24 +0100 Subject: [PATCH 05/59] implemented IDragData for OSX --- src/OSX/Avalonia.MonoMac/DraggingInfo.cs | 30 +++++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs index 3abcb8c68c7..8164470548a 100644 --- a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs +++ b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs @@ -1,11 +1,14 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using Avalonia.Controls.DragDrop; using MonoMac.AppKit; +using MonoMac.Foundation; namespace Avalonia.MonoMac { class DraggingInfo : IDragData - { + { private readonly NSDraggingInfo _info; public DraggingInfo(NSDraggingInfo info) @@ -42,22 +45,37 @@ internal static DragOperation ConvertDragOperation(NSDragOperation d) public IEnumerable GetDataFormats() { - yield break; + return _info.DraggingPasteboard.Types.Select(NSTypeToWellknownType); + } + + private string NSTypeToWellknownType(string type) + { + if (type == NSPasteboard.NSStringType) + return DataFormats.Text; + if (type == NSPasteboard.NSFilenamesType) + return DataFormats.FileNames; + return type; } public string GetText() { - return null; + return _info.DraggingPasteboard.GetStringForType(NSPasteboard.NSStringType); } public IEnumerable GetFileNames() { - yield break; + using(var fileNames = (NSArray)_info.DraggingPasteboard.GetPropertyListForType(NSPasteboard.NSFilenamesType)) + { + if (fileNames != null) + return NSArray.StringArrayFromHandle(fileNames.Handle); + } + + return Enumerable.Empty(); } public bool Contains(string dataFormat) { - return false; + return GetDataFormats().Any(f => f == dataFormat); } } } \ No newline at end of file From 1647d95aa6a192dc6978fe264d0b6422a83ba632 Mon Sep 17 00:00:00 2001 From: boombuler Date: Sat, 3 Mar 2018 16:17:45 +0100 Subject: [PATCH 06/59] Initial Drag+Drop support for windows --- .../Avalonia.Win32/ClipboardFormats.cs | 80 ++++++++++ .../Interop/UnmanagedMethods.cs | 71 ++++++++- src/Windows/Avalonia.Win32/OleContext.cs | 47 ++++++ src/Windows/Avalonia.Win32/OleDataObject.cs | 141 ++++++++++++++++++ src/Windows/Avalonia.Win32/OleDropTarget.cs | 131 ++++++++++++++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 10 ++ 6 files changed, 479 insertions(+), 1 deletion(-) create mode 100644 src/Windows/Avalonia.Win32/ClipboardFormats.cs create mode 100644 src/Windows/Avalonia.Win32/OleContext.cs create mode 100644 src/Windows/Avalonia.Win32/OleDataObject.cs create mode 100644 src/Windows/Avalonia.Win32/OleDropTarget.cs diff --git a/src/Windows/Avalonia.Win32/ClipboardFormats.cs b/src/Windows/Avalonia.Win32/ClipboardFormats.cs new file mode 100644 index 00000000000..254facf81a2 --- /dev/null +++ b/src/Windows/Avalonia.Win32/ClipboardFormats.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using Avalonia.Controls.DragDrop; +using Avalonia.Win32.Interop; + +namespace Avalonia.Win32 +{ + static class ClipboardFormats + { + class ClipboardFormat + { + public short Format { get; private set; } + public string Name { get; private set; } + + public ClipboardFormat(string name, short format) + { + Format = format; + Name = name; + } + } + + private static readonly List FormatList = new List() + { + new ClipboardFormat(DataFormats.Text, (short)UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT), + new ClipboardFormat(DataFormats.FileNames, (short)UnmanagedMethods.ClipboardFormat.CF_HDROP), + }; + + + private static string QueryFormatName(short format) + { + int len = UnmanagedMethods.GetClipboardFormatName(format, null, 0); + if (len > 0) + { + StringBuilder sb = new StringBuilder(len); + if (UnmanagedMethods.GetClipboardFormatName(format, sb, len) <= len) + return sb.ToString(); + } + return null; + } + + public static string GetFormat(short format) + { + lock (FormatList) + { + var pd = FormatList.FirstOrDefault(f => f.Format == format); + if (pd == null) + { + string name = QueryFormatName(format); + if (string.IsNullOrEmpty(name)) + name = string.Format("Unknown_Format_{0}", format); + pd = new ClipboardFormat(name, format); + FormatList.Add(pd); + } + return pd.Name; + } + } + + public static short GetFormat(string format) + { + lock (FormatList) + { + var pd = FormatList.FirstOrDefault(f => StringComparer.OrdinalIgnoreCase.Equals(f.Name, format)); + if (pd == null) + { + int id = UnmanagedMethods.RegisterClipboardFormat(format); + if (id == 0) + throw new Win32Exception(); + pd = new ClipboardFormat(format, (short)id); + FormatList.Add(pd); + } + return pd.Format; + } + } + + + } +} \ No newline at end of file diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index f13dd3272cb..dada2eb7e68 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; using System.Text; // ReSharper disable InconsistentNaming @@ -951,6 +952,28 @@ public static extern IntPtr CreateFileMapping(IntPtr hFile, [DllImport("msvcrt.dll", EntryPoint="memcpy", SetLastError = false, CallingConvention=CallingConvention.Cdecl)] public static extern IntPtr CopyMemory(IntPtr dest, IntPtr src, UIntPtr count); + [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] + public static extern HRESULT RegisterDragDrop(IntPtr hwnd, IDropTarget target); + + [DllImport("ole32.dll", EntryPoint = "OleInitialize")] + public static extern HRESULT OleInitialize(IntPtr val); + + [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] + internal static extern void ReleaseStgMedium(ref STGMEDIUM medium); + + [DllImport("user32.dll", BestFitMapping = false, CharSet = CharSet.Auto, SetLastError = true)] + public static extern int GetClipboardFormatName(int format, StringBuilder lpString, int cchMax); + + [DllImport("user32.dll", BestFitMapping = false, CharSet = CharSet.Auto, SetLastError = true)] + public static extern int RegisterClipboardFormat(string format); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true, SetLastError = true)] + public static extern IntPtr GlobalSize(IntPtr hGlobal); + + [DllImport("shell32.dll", BestFitMapping = false, CharSet = CharSet.Auto)] + public static extern int DragQueryFile(IntPtr hDrop, int iFile, StringBuilder lpszFile, int cch); + + public enum MONITOR { MONITOR_DEFAULTTONULL = 0x00000000, @@ -993,7 +1016,8 @@ public enum MONITOR_DPI_TYPE public enum ClipboardFormat { CF_TEXT = 1, - CF_UNICODETEXT = 13 + CF_UNICODETEXT = 13, + CF_HDROP = 15 } public struct MSG @@ -1300,4 +1324,49 @@ internal interface IShellItem uint Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder); } + + [Flags] + internal enum DropEffect : int + { + None = 0, + Copy = 1, + Move = 2, + Link = 4, + Scroll = -2147483648, + } + + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("0000010E-0000-0000-C000-000000000046")] + [ComImport] + internal interface IOleDataObject + { + void GetData([In] ref FORMATETC format, out STGMEDIUM medium); + void GetDataHere([In] ref FORMATETC format, ref STGMEDIUM medium); + [PreserveSig] + int QueryGetData([In] ref FORMATETC format); + [PreserveSig] + int GetCanonicalFormatEtc([In] ref FORMATETC formatIn, out FORMATETC formatOut); + void SetData([In] ref FORMATETC formatIn, [In] ref STGMEDIUM medium, [MarshalAs(UnmanagedType.Bool)] bool release); + IEnumFORMATETC EnumFormatEtc(DATADIR direction); + [PreserveSig] + int DAdvise([In] ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection); + void DUnadvise(int connection); + [PreserveSig] + int EnumDAdvise(out IEnumSTATDATA enumAdvise); + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("00000122-0000-0000-C000-000000000046")] + internal interface IDropTarget + { + [PreserveSig] + UnmanagedMethods.HRESULT DragEnter([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); + [PreserveSig] + UnmanagedMethods.HRESULT DragOver([MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); + [PreserveSig] + UnmanagedMethods.HRESULT DragLeave(); + [PreserveSig] + UnmanagedMethods.HRESULT Drop([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); + } } diff --git a/src/Windows/Avalonia.Win32/OleContext.cs b/src/Windows/Avalonia.Win32/OleContext.cs new file mode 100644 index 00000000000..0c597e3d179 --- /dev/null +++ b/src/Windows/Avalonia.Win32/OleContext.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading; +using Avalonia.Platform; +using Avalonia.Threading; +using Avalonia.Win32.Interop; + +namespace Zippr.UIServices.Avalonia.Windows +{ + class OleContext + { + private static OleContext fCurrent; + + internal static OleContext Current + { + get + { + if (!IsValidOleThread()) + return null; + + if (fCurrent == null) + fCurrent = new OleContext(); + return fCurrent; + } + } + + + private OleContext() + { + if (UnmanagedMethods.OleInitialize(IntPtr.Zero) != UnmanagedMethods.HRESULT.S_OK) + throw new SystemException("Failed to initialize OLE"); + } + + private static bool IsValidOleThread() + { + return Dispatcher.UIThread.CheckAccess() && + Thread.CurrentThread.GetApartmentState() == ApartmentState.STA; + } + + internal bool RegisterDragDrop(IPlatformHandle hwnd, IDropTarget target) + { + if (hwnd?.HandleDescriptor != "HWND" || target == null) + return false; + + return UnmanagedMethods.RegisterDragDrop(hwnd.Handle, target) == UnmanagedMethods.HRESULT.S_OK; + } + } +} diff --git a/src/Windows/Avalonia.Win32/OleDataObject.cs b/src/Windows/Avalonia.Win32/OleDataObject.cs new file mode 100644 index 00000000000..6c86cd03e0a --- /dev/null +++ b/src/Windows/Avalonia.Win32/OleDataObject.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using Avalonia.Controls.DragDrop; +using Avalonia.Win32.Interop; + +namespace Avalonia.Win32 +{ + class OleDataObject : IDragData + { + private IOleDataObject _wrapped; + + public OleDataObject(IOleDataObject wrapped) + { + _wrapped = wrapped; + } + + public bool Contains(string dataFormat) + { + return GetDataFormatsCore().Any(df => StringComparer.OrdinalIgnoreCase.Equals(df, dataFormat)); + } + + public IEnumerable GetDataFormats() + { + return GetDataFormatsCore().Distinct(); + } + + public string GetText() + { + return GetDataFromOleHGLOBAL(DataFormats.Text, DVASPECT.DVASPECT_CONTENT) as string; + } + + public IEnumerable GetFileNames() + { + return GetDataFromOleHGLOBAL(DataFormats.FileNames, DVASPECT.DVASPECT_CONTENT) as IEnumerable; + } + + private object GetDataFromOleHGLOBAL(string format, DVASPECT aspect) + { + FORMATETC formatEtc = new FORMATETC(); + formatEtc.cfFormat = ClipboardFormats.GetFormat(format); + formatEtc.dwAspect = aspect; + formatEtc.lindex = -1; + formatEtc.tymed = TYMED.TYMED_HGLOBAL; + if (_wrapped.QueryGetData(ref formatEtc) == 0) + { + _wrapped.GetData(ref formatEtc, out STGMEDIUM medium); + try + { + if (medium.unionmember != IntPtr.Zero && medium.tymed == TYMED.TYMED_HGLOBAL) + { + if (format == DataFormats.Text) + return ReadStringFromHGlobal(medium.unionmember); + if (format == DataFormats.FileNames) + return ReadFileNamesFromHGlobal(medium.unionmember); + return ReadBytesFromHGlobal(medium.unionmember); + } + } + finally + { + UnmanagedMethods.ReleaseStgMedium(ref medium); + } + } + return null; + } + + private static IEnumerable ReadFileNamesFromHGlobal(IntPtr hGlobal) + { + List files = new List(); + int fileCount = UnmanagedMethods.DragQueryFile(hGlobal, -1, null, 0); + if (fileCount > 0) + { + for (int i = 0; i < fileCount; i++) + { + int pathLen = UnmanagedMethods.DragQueryFile(hGlobal, i, null, 0); + StringBuilder sb = new StringBuilder(pathLen+1); + + if (UnmanagedMethods.DragQueryFile(hGlobal, i, sb, sb.Capacity) == pathLen) + { + files.Add(sb.ToString()); + } + } + } + return files; + } + + private static string ReadStringFromHGlobal(IntPtr hGlobal) + { + IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal); + try + { + return Marshal.PtrToStringAuto(ptr); + } + finally + { + UnmanagedMethods.GlobalUnlock(hGlobal); + } + } + + private static byte[] ReadBytesFromHGlobal(IntPtr hGlobal) + { + IntPtr source = UnmanagedMethods.GlobalLock(hGlobal); + try + { + int size = (int)UnmanagedMethods.GlobalSize(hGlobal).ToInt64(); + byte[] data = new byte[size]; + Marshal.Copy(source, data, 0, size); + return data; + } + finally + { + UnmanagedMethods.GlobalUnlock(hGlobal); + } + } + + private IEnumerable GetDataFormatsCore() + { + var enumFormat = _wrapped.EnumFormatEtc(DATADIR.DATADIR_GET); + if (enumFormat != null) + { + enumFormat.Reset(); + FORMATETC[] formats = new FORMATETC[1]; + int[] fetched = { 1 }; + while (fetched[0] > 0) + { + fetched[0] = 0; + if (enumFormat.Next(1, formats, fetched) == 0 && fetched[0] > 0) + { + if (formats[0].ptd != IntPtr.Zero) + Marshal.FreeCoTaskMem(formats[0].ptd); + + yield return ClipboardFormats.GetFormat(formats[0].cfFormat); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs new file mode 100644 index 00000000000..b0f592403b3 --- /dev/null +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -0,0 +1,131 @@ +using System; +using System.Runtime.InteropServices.ComTypes; +using Avalonia.Controls; +using Avalonia.Controls.DragDrop; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.VisualTree; +using Avalonia.Win32.Interop; + +namespace Avalonia.Win32 +{ + class OleDropTarget : IDropTarget + { + private readonly IDragDispatcher _dragDispatcher; + private readonly IInputElement _target; + + private IDragData _currentDrag = null; + + public OleDropTarget(IInputElement target) + { + _dragDispatcher = AvaloniaLocator.Current.GetService(); + _target = target; + } + + static DropEffect ConvertDropEffect(DragOperation operation) + { + DropEffect result = DropEffect.None; + if (operation.HasFlag(DragOperation.Copy)) + result |= DropEffect.Copy; + if (operation.HasFlag(DragOperation.Move)) + result |= DropEffect.Move; + if (operation.HasFlag(DragOperation.Link)) + result |= DropEffect.Link; + return result; + } + + static DragOperation ConvertDropEffect(DropEffect effect) + { + DragOperation result = DragOperation.None; + if (effect.HasFlag(DropEffect.Copy)) + result |= DragOperation.Copy; + if (effect.HasFlag(DropEffect.Move)) + result |= DragOperation.Move; + if (effect.HasFlag(DropEffect.Link)) + result |= DragOperation.Link; + return result; + } + + UnmanagedMethods.HRESULT IDropTarget.DragEnter(IOleDataObject pDataObj, int grfKeyState, long pt, ref DropEffect pdwEffect) + { + if (_dragDispatcher == null) + { + pdwEffect = DropEffect.None; + return UnmanagedMethods.HRESULT.S_OK; + } + + _currentDrag = new OleDataObject(pDataObj); + var dragLocation = GetDragLocation(pt); + + var operation = ConvertDropEffect(pdwEffect); + operation = _dragDispatcher.DragEnter(_target, dragLocation, _currentDrag, operation); + pdwEffect = ConvertDropEffect(operation); + + return UnmanagedMethods.HRESULT.S_OK; + } + + UnmanagedMethods.HRESULT IDropTarget.DragOver(int grfKeyState, long pt, ref DropEffect pdwEffect) + { + if (_dragDispatcher == null) + { + pdwEffect = DropEffect.None; + return UnmanagedMethods.HRESULT.S_OK; + } + + var dragLocation = GetDragLocation(pt); + + var operation = ConvertDropEffect(pdwEffect); + operation = _dragDispatcher.DragOver(_target, dragLocation, _currentDrag, operation); + pdwEffect = ConvertDropEffect(operation); + + return UnmanagedMethods.HRESULT.S_OK; + } + + UnmanagedMethods.HRESULT IDropTarget.DragLeave() + { + try + { + _dragDispatcher?.DragLeave(_target); + return UnmanagedMethods.HRESULT.S_OK; + } + finally + { + _currentDrag = null; + } + } + + UnmanagedMethods.HRESULT IDropTarget.Drop(IOleDataObject pDataObj, int grfKeyState, long pt, ref DropEffect pdwEffect) + { + try + { + if (_dragDispatcher == null) + { + pdwEffect = DropEffect.None; + return UnmanagedMethods.HRESULT.S_OK; + } + + _currentDrag= new OleDataObject(pDataObj); + var dragLocation = GetDragLocation(pt); + + var operation = ConvertDropEffect(pdwEffect); + operation = _dragDispatcher.Drop(_target, dragLocation, _currentDrag, operation); + pdwEffect = ConvertDropEffect(operation); + + return UnmanagedMethods.HRESULT.S_OK; + } + finally + { + _currentDrag = null; + } + } + + private Point GetDragLocation(long dragPoint) + { + int x = (int)dragPoint; + int y = (int)(dragPoint >> 32); + + Point screenPt = new Point(x, y); + return _target.PointToClient(screenPt); + } + } +} \ No newline at end of file diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 3ba926b42a5..2fca0baac73 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -14,6 +14,7 @@ using Avalonia.Rendering; using Avalonia.Win32.Input; using Avalonia.Win32.Interop; +using Zippr.UIServices.Avalonia.Windows; using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 @@ -34,6 +35,7 @@ public class WindowImpl : IWindowImpl private double _scaling = 1; private WindowState _showWindowState; private FramebufferManager _framebuffer; + private OleDropTarget _dropTarget; #if USE_MANAGED_DRAG private readonly ManagedWindowResizeDragHelper _managedDrag; #endif @@ -308,6 +310,7 @@ public Point PointToScreen(Point point) public void SetInputRoot(IInputRoot inputRoot) { _owner = inputRoot; + CreateDropTarget(); } public void SetTitle(string title) @@ -689,6 +692,13 @@ private void CreateWindow() } } + private void CreateDropTarget() + { + OleDropTarget odt = new OleDropTarget(_owner); + if (OleContext.Current?.RegisterDragDrop(Handle, odt) ?? false) + _dropTarget = odt; + } + private Point DipFromLParam(IntPtr lParam) { return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)) / Scaling; From 50212da915cd32db81211a29840150d8eed6d1b2 Mon Sep 17 00:00:00 2001 From: boombuler Date: Sat, 3 Mar 2018 16:20:12 +0100 Subject: [PATCH 07/59] DragDrop is now a static class --- src/Avalonia.Controls/DragDrop/DragDrop.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/DragDrop/DragDrop.cs b/src/Avalonia.Controls/DragDrop/DragDrop.cs index 3645409a5a1..317a903d3c9 100644 --- a/src/Avalonia.Controls/DragDrop/DragDrop.cs +++ b/src/Avalonia.Controls/DragDrop/DragDrop.cs @@ -2,7 +2,7 @@ namespace Avalonia.Controls.DragDrop { - public sealed class DragDrop : AvaloniaObject + public static class DragDrop { public static RoutedEvent DragEnterEvent = RoutedEvent.Register("DragEnter", RoutingStrategies.Bubble, typeof(DragDrop)); public static RoutedEvent DragLeaveEvent = RoutedEvent.Register("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop)); From 4c362818779eec17a1c016faad5c22056055d160 Mon Sep 17 00:00:00 2001 From: boombuler Date: Sat, 3 Mar 2018 16:29:41 +0100 Subject: [PATCH 08/59] renamed DragOperation to DragDropEffects --- .../DragDrop/DefaultDragDispatcher.cs | 22 +++++++++---------- .../{DragOperation.cs => DragDropEffects.cs} | 2 +- .../DragDrop/DragEventArgs.cs | 2 +- .../DragDrop/IDragDispatcher.cs | 6 ++--- src/OSX/Avalonia.MonoMac/DraggingInfo.cs | 18 +++++++-------- src/OSX/Avalonia.MonoMac/TopLevelImpl.cs | 4 ++-- src/Windows/Avalonia.Win32/OleDropTarget.cs | 18 +++++++-------- 7 files changed, 36 insertions(+), 36 deletions(-) rename src/Avalonia.Controls/DragDrop/{DragOperation.cs => DragDropEffects.cs} (82%) diff --git a/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs b/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs index 2ce5a32dc53..68fa6cad758 100644 --- a/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs +++ b/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs @@ -23,37 +23,37 @@ private Interactive GetTarget(IInputElement root, Point local) return null; } - private DragOperation RaiseDragEvent(Interactive target, RoutedEvent routedEvent, DragOperation operation, IDragData data) + private DragDropEffects RaiseDragEvent(Interactive target, RoutedEvent routedEvent, DragDropEffects operation, IDragData data) { if (target == null) - return DragOperation.None; + return DragDropEffects.None; var args = new DragEventArgs(routedEvent, data) { RoutedEvent = routedEvent, - DragOperation = operation + DragEffects = operation }; target.RaiseEvent(args); - return args.DragOperation; + return args.DragEffects; } - public DragOperation DragEnter(IInputElement inputRoot, Point point, IDragData data, DragOperation operation) + public DragDropEffects DragEnter(IInputElement inputRoot, Point point, IDragData data, DragDropEffects effects) { _lastTarget = GetTarget(inputRoot, point); - return RaiseDragEvent(_lastTarget, DragDrop.DragEnterEvent, operation, data); + return RaiseDragEvent(_lastTarget, DragDrop.DragEnterEvent, effects, data); } - public DragOperation DragOver(IInputElement inputRoot, Point point, IDragData data, DragOperation operation) + public DragDropEffects DragOver(IInputElement inputRoot, Point point, IDragData data, DragDropEffects effects) { var target = GetTarget(inputRoot, point); if (target == _lastTarget) - return RaiseDragEvent(target, DragDrop.DragOverEvent, operation, data); + return RaiseDragEvent(target, DragDrop.DragOverEvent, effects, data); try { if (_lastTarget != null) _lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent)); - return RaiseDragEvent(target, DragDrop.DragEnterEvent, operation, data); + return RaiseDragEvent(target, DragDrop.DragEnterEvent, effects, data); } finally { @@ -75,11 +75,11 @@ public void DragLeave(IInputElement inputRoot) } } - public DragOperation Drop(IInputElement inputRoot, Point point, IDragData data, DragOperation operation) + public DragDropEffects Drop(IInputElement inputRoot, Point point, IDragData data, DragDropEffects effects) { try { - return RaiseDragEvent(_lastTarget, DragDrop.DropEvent, operation, data); + return RaiseDragEvent(_lastTarget, DragDrop.DropEvent, effects, data); } finally { diff --git a/src/Avalonia.Controls/DragDrop/DragOperation.cs b/src/Avalonia.Controls/DragDrop/DragDropEffects.cs similarity index 82% rename from src/Avalonia.Controls/DragDrop/DragOperation.cs rename to src/Avalonia.Controls/DragDrop/DragDropEffects.cs index ffc6f666f79..cec3dab89d9 100644 --- a/src/Avalonia.Controls/DragDrop/DragOperation.cs +++ b/src/Avalonia.Controls/DragDrop/DragDropEffects.cs @@ -3,7 +3,7 @@ namespace Avalonia.Controls.DragDrop { [Flags] - public enum DragOperation + public enum DragDropEffects { None = 0, Copy = 1, diff --git a/src/Avalonia.Controls/DragDrop/DragEventArgs.cs b/src/Avalonia.Controls/DragDrop/DragEventArgs.cs index e8bd2528c72..2b6658d3959 100644 --- a/src/Avalonia.Controls/DragDrop/DragEventArgs.cs +++ b/src/Avalonia.Controls/DragDrop/DragEventArgs.cs @@ -4,7 +4,7 @@ namespace Avalonia.Controls.DragDrop { public class DragEventArgs : RoutedEventArgs { - public DragOperation DragOperation { get; set; } + public DragDropEffects DragEffects { get; set; } public IDragData Data { get; private set; } diff --git a/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs b/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs index 05a211ff76e..84c023ddbc4 100644 --- a/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs +++ b/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs @@ -7,9 +7,9 @@ namespace Avalonia.Controls.DragDrop /// public interface IDragDispatcher { - DragOperation DragEnter(IInputElement inputRoot, Point point, IDragData data, DragOperation operation); - DragOperation DragOver(IInputElement inputRoot, Point point, IDragData data, DragOperation operation); + DragDropEffects DragEnter(IInputElement inputRoot, Point point, IDragData data, DragDropEffects effects); + DragDropEffects DragOver(IInputElement inputRoot, Point point, IDragData data, DragDropEffects effects); void DragLeave(IInputElement inputRoot); - DragOperation Drop(IInputElement inputRoot, Point point, IDragData data, DragOperation operation); + DragDropEffects Drop(IInputElement inputRoot, Point point, IDragData data, DragDropEffects effects); } } \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs index 8164470548a..dccc39e861f 100644 --- a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs +++ b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs @@ -17,27 +17,27 @@ public DraggingInfo(NSDraggingInfo info) } - internal static NSDragOperation ConvertDragOperation(DragOperation d) + internal static NSDragOperation ConvertDragOperation(DragDropEffects d) { NSDragOperation result = NSDragOperation.None; - if (d.HasFlag(DragOperation.Copy)) + if (d.HasFlag(DragDropEffects.Copy)) result |= NSDragOperation.Copy; - if (d.HasFlag(DragOperation.Link)) + if (d.HasFlag(DragDropEffects.Link)) result |= NSDragOperation.Link; - if (d.HasFlag(DragOperation.Move)) + if (d.HasFlag(DragDropEffects.Move)) result |= NSDragOperation.Move; return result; } - internal static DragOperation ConvertDragOperation(NSDragOperation d) + internal static DragDropEffects ConvertDragOperation(NSDragOperation d) { - DragOperation result = DragOperation.None; + DragDropEffects result = DragDropEffects.None; if (d.HasFlag(NSDragOperation.Copy)) - result |= DragOperation.Copy; + result |= DragDropEffects.Copy; if (d.HasFlag(NSDragOperation.Link)) - result |= DragOperation.Link; + result |= DragDropEffects.Link; if (d.HasFlag(NSDragOperation.Move)) - result |= DragOperation.Move; + result |= DragDropEffects.Move; return result; } diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs index 711f6d27879..4398b6bddb1 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -201,7 +201,7 @@ public override bool PrepareForDragOperation(NSDraggingInfo sender) dragOp = _dragDispatcher.DragOver(root, pt, info, dragOp); - return dragOp != DragOperation.None; + return dragOp != DragDropEffects.None; } public override bool PerformDragOperation(NSDraggingInfo sender) @@ -216,7 +216,7 @@ public override bool PerformDragOperation(NSDraggingInfo sender) dragOp = _dragDispatcher.Drop(root, pt, info, dragOp); - return dragOp != DragOperation.None; + return dragOp != DragDropEffects.None; } diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs index b0f592403b3..e3725d3bbfe 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -22,27 +22,27 @@ public OleDropTarget(IInputElement target) _target = target; } - static DropEffect ConvertDropEffect(DragOperation operation) + static DropEffect ConvertDropEffect(DragDropEffects operation) { DropEffect result = DropEffect.None; - if (operation.HasFlag(DragOperation.Copy)) + if (operation.HasFlag(DragDropEffects.Copy)) result |= DropEffect.Copy; - if (operation.HasFlag(DragOperation.Move)) + if (operation.HasFlag(DragDropEffects.Move)) result |= DropEffect.Move; - if (operation.HasFlag(DragOperation.Link)) + if (operation.HasFlag(DragDropEffects.Link)) result |= DropEffect.Link; return result; } - static DragOperation ConvertDropEffect(DropEffect effect) + static DragDropEffects ConvertDropEffect(DropEffect effect) { - DragOperation result = DragOperation.None; + DragDropEffects result = DragDropEffects.None; if (effect.HasFlag(DropEffect.Copy)) - result |= DragOperation.Copy; + result |= DragDropEffects.Copy; if (effect.HasFlag(DropEffect.Move)) - result |= DragOperation.Move; + result |= DragDropEffects.Move; if (effect.HasFlag(DropEffect.Link)) - result |= DragOperation.Link; + result |= DragDropEffects.Link; return result; } From 83b18944d3ac81ed089902fea6dd7105e3664c55 Mon Sep 17 00:00:00 2001 From: boombuler Date: Sat, 3 Mar 2018 16:57:25 +0100 Subject: [PATCH 09/59] corrected namespace. (copy+paste error) --- src/Windows/Avalonia.Win32/OleContext.cs | 2 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/OleContext.cs b/src/Windows/Avalonia.Win32/OleContext.cs index 0c597e3d179..085c0f8ea97 100644 --- a/src/Windows/Avalonia.Win32/OleContext.cs +++ b/src/Windows/Avalonia.Win32/OleContext.cs @@ -4,7 +4,7 @@ using Avalonia.Threading; using Avalonia.Win32.Interop; -namespace Zippr.UIServices.Avalonia.Windows +namespace Avalonia.Win32 { class OleContext { diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 2fca0baac73..ed224a6525f 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -14,7 +14,6 @@ using Avalonia.Rendering; using Avalonia.Win32.Input; using Avalonia.Win32.Interop; -using Zippr.UIServices.Avalonia.Windows; using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 From 0adc6e62de8a95129e4cc73b9772db838ec5b74e Mon Sep 17 00:00:00 2001 From: boombuler Date: Sat, 3 Mar 2018 17:36:11 +0100 Subject: [PATCH 10/59] renamed IDragData to IDataObject --- .../DragDrop/DefaultDragDispatcher.cs | 8 +++---- .../DragDrop/DragEventArgs.cs | 4 ++-- .../DragDrop/{IDragData.cs => IDataObject.cs} | 2 +- .../DragDrop/IDragDispatcher.cs | 6 ++--- src/OSX/Avalonia.MonoMac/DraggingInfo.cs | 2 +- .../Interop/UnmanagedMethods.cs | 24 +++---------------- src/Windows/Avalonia.Win32/OleDataObject.cs | 7 +++--- src/Windows/Avalonia.Win32/OleDropTarget.cs | 11 ++++----- 8 files changed, 22 insertions(+), 42 deletions(-) rename src/Avalonia.Controls/DragDrop/{IDragData.cs => IDataObject.cs} (88%) diff --git a/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs b/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs index 68fa6cad758..2db227248b3 100644 --- a/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs +++ b/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs @@ -23,7 +23,7 @@ private Interactive GetTarget(IInputElement root, Point local) return null; } - private DragDropEffects RaiseDragEvent(Interactive target, RoutedEvent routedEvent, DragDropEffects operation, IDragData data) + private DragDropEffects RaiseDragEvent(Interactive target, RoutedEvent routedEvent, DragDropEffects operation, IDataObject data) { if (target == null) return DragDropEffects.None; @@ -36,13 +36,13 @@ private DragDropEffects RaiseDragEvent(Interactive target, RoutedEvent routedEvent, IDragData data) + public DragEventArgs(RoutedEvent routedEvent, IDataObject data) : base(routedEvent) { this.Data = data; diff --git a/src/Avalonia.Controls/DragDrop/IDragData.cs b/src/Avalonia.Controls/DragDrop/IDataObject.cs similarity index 88% rename from src/Avalonia.Controls/DragDrop/IDragData.cs rename to src/Avalonia.Controls/DragDrop/IDataObject.cs index b6dc53d32d4..5ffecc13ed4 100644 --- a/src/Avalonia.Controls/DragDrop/IDragData.cs +++ b/src/Avalonia.Controls/DragDrop/IDataObject.cs @@ -2,7 +2,7 @@ namespace Avalonia.Controls.DragDrop { - public interface IDragData + public interface IDataObject { IEnumerable GetDataFormats(); diff --git a/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs b/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs index 84c023ddbc4..538ffa171bd 100644 --- a/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs +++ b/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs @@ -7,9 +7,9 @@ namespace Avalonia.Controls.DragDrop /// public interface IDragDispatcher { - DragDropEffects DragEnter(IInputElement inputRoot, Point point, IDragData data, DragDropEffects effects); - DragDropEffects DragOver(IInputElement inputRoot, Point point, IDragData data, DragDropEffects effects); + DragDropEffects DragEnter(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects); + DragDropEffects DragOver(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects); void DragLeave(IInputElement inputRoot); - DragDropEffects Drop(IInputElement inputRoot, Point point, IDragData data, DragDropEffects effects); + DragDropEffects Drop(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects); } } \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs index dccc39e861f..ef4d9d99f60 100644 --- a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs +++ b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs @@ -7,7 +7,7 @@ namespace Avalonia.MonoMac { - class DraggingInfo : IDragData + class DraggingInfo : IDataObject { private readonly NSDraggingInfo _info; diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index dada2eb7e68..f43cdb98cc6 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1335,25 +1335,7 @@ internal enum DropEffect : int Scroll = -2147483648, } - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("0000010E-0000-0000-C000-000000000046")] - [ComImport] - internal interface IOleDataObject - { - void GetData([In] ref FORMATETC format, out STGMEDIUM medium); - void GetDataHere([In] ref FORMATETC format, ref STGMEDIUM medium); - [PreserveSig] - int QueryGetData([In] ref FORMATETC format); - [PreserveSig] - int GetCanonicalFormatEtc([In] ref FORMATETC formatIn, out FORMATETC formatOut); - void SetData([In] ref FORMATETC formatIn, [In] ref STGMEDIUM medium, [MarshalAs(UnmanagedType.Bool)] bool release); - IEnumFORMATETC EnumFormatEtc(DATADIR direction); - [PreserveSig] - int DAdvise([In] ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection); - void DUnadvise(int connection); - [PreserveSig] - int EnumDAdvise(out IEnumSTATDATA enumAdvise); - } + [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] @@ -1361,12 +1343,12 @@ internal interface IOleDataObject internal interface IDropTarget { [PreserveSig] - UnmanagedMethods.HRESULT DragEnter([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); + UnmanagedMethods.HRESULT DragEnter([MarshalAs(UnmanagedType.Interface)] [In] IDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); [PreserveSig] UnmanagedMethods.HRESULT DragOver([MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); [PreserveSig] UnmanagedMethods.HRESULT DragLeave(); [PreserveSig] - UnmanagedMethods.HRESULT Drop([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); + UnmanagedMethods.HRESULT Drop([MarshalAs(UnmanagedType.Interface)] [In] IDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); } } diff --git a/src/Windows/Avalonia.Win32/OleDataObject.cs b/src/Windows/Avalonia.Win32/OleDataObject.cs index 6c86cd03e0a..bf6f3d98c3e 100644 --- a/src/Windows/Avalonia.Win32/OleDataObject.cs +++ b/src/Windows/Avalonia.Win32/OleDataObject.cs @@ -6,14 +6,15 @@ using System.Text; using Avalonia.Controls.DragDrop; using Avalonia.Win32.Interop; +using IDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; namespace Avalonia.Win32 { - class OleDataObject : IDragData + class OleDataObject : Avalonia.Controls.DragDrop.IDataObject { - private IOleDataObject _wrapped; + private IDataObject _wrapped; - public OleDataObject(IOleDataObject wrapped) + public OleDataObject(IDataObject wrapped) { _wrapped = wrapped; } diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs index e3725d3bbfe..925f9b7046b 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -1,11 +1,8 @@ -using System; -using System.Runtime.InteropServices.ComTypes; -using Avalonia.Controls; -using Avalonia.Controls.DragDrop; +using Avalonia.Controls.DragDrop; using Avalonia.Input; -using Avalonia.Interactivity; -using Avalonia.VisualTree; using Avalonia.Win32.Interop; +using IDataObject = Avalonia.Controls.DragDrop.IDataObject; +using IOleDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; namespace Avalonia.Win32 { @@ -14,7 +11,7 @@ class OleDropTarget : IDropTarget private readonly IDragDispatcher _dragDispatcher; private readonly IInputElement _target; - private IDragData _currentDrag = null; + private IDataObject _currentDrag = null; public OleDropTarget(IInputElement target) { From 4c6a341b73a5198d1539506c057c4e1b392e9f76 Mon Sep 17 00:00:00 2001 From: boombuler Date: Sun, 4 Mar 2018 21:13:02 +0100 Subject: [PATCH 11/59] reworked Drag event dispatching --- src/Avalonia.Controls/Application.cs | 3 +- ...DefaultDragDispatcher.cs => DragDevice.cs} | 45 ++++++++--- .../DragDrop/IDragDispatcher.cs | 15 ---- .../DragDrop/Raw/IDragDevice.cs | 8 ++ .../DragDrop/Raw/RawDragEvent.cs | 31 ++++++++ .../DragDrop/Raw/RawDragEventType.cs | 10 +++ src/OSX/Avalonia.MonoMac/TopLevelImpl.cs | 65 +++++----------- src/Windows/Avalonia.Win32/OleDropTarget.cs | 76 +++++++++++++------ src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 9 files changed, 158 insertions(+), 97 deletions(-) rename src/Avalonia.Controls/DragDrop/{DefaultDragDispatcher.cs => DragDevice.cs} (62%) delete mode 100644 src/Avalonia.Controls/DragDrop/IDragDispatcher.cs create mode 100644 src/Avalonia.Controls/DragDrop/Raw/IDragDevice.cs create mode 100644 src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs create mode 100644 src/Avalonia.Controls/DragDrop/Raw/RawDragEventType.cs diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 63b17530ff6..b751aeca604 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -13,6 +13,7 @@ using Avalonia.Threading; using System.Reactive.Concurrency; using Avalonia.Controls.DragDrop; +using Avalonia.Controls.DragDrop.Raw; namespace Avalonia { @@ -236,7 +237,7 @@ public virtual void RegisterServices() .Bind().ToSingleton() .Bind().ToConstant(this) .Bind().ToConstant(AvaloniaScheduler.Instance) - .Bind().ToConstant(DefaultDragDispatcher.Instance); + .Bind().ToConstant(DragDevice.Instance); } } } diff --git a/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs b/src/Avalonia.Controls/DragDrop/DragDevice.cs similarity index 62% rename from src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs rename to src/Avalonia.Controls/DragDrop/DragDevice.cs index 2db227248b3..25f82ca3bc9 100644 --- a/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs +++ b/src/Avalonia.Controls/DragDrop/DragDevice.cs @@ -2,19 +2,17 @@ using Avalonia.Interactivity; using Avalonia.VisualTree; using System.Linq; +using Avalonia.Controls.DragDrop.Raw; +using Avalonia.Input.Raw; namespace Avalonia.Controls.DragDrop { - class DefaultDragDispatcher : IDragDispatcher + class DragDevice : IDragDevice { - public static readonly DefaultDragDispatcher Instance = new DefaultDragDispatcher(); - + public static readonly DragDevice Instance = new DragDevice(); + private Interactive _lastTarget = null; - private DefaultDragDispatcher() - { - } - private Interactive GetTarget(IInputElement root, Point local) { var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType()?.FirstOrDefault(); @@ -36,13 +34,13 @@ private DragDropEffects RaiseDragEvent(Interactive target, RoutedEvent - /// Dispatches Drag+Drop events to the correct visual targets, based on the input root and the drag location. - /// - public interface IDragDispatcher - { - DragDropEffects DragEnter(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects); - DragDropEffects DragOver(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects); - void DragLeave(IInputElement inputRoot); - DragDropEffects Drop(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects); - } -} \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/Raw/IDragDevice.cs b/src/Avalonia.Controls/DragDrop/Raw/IDragDevice.cs new file mode 100644 index 00000000000..d1c20fae558 --- /dev/null +++ b/src/Avalonia.Controls/DragDrop/Raw/IDragDevice.cs @@ -0,0 +1,8 @@ +using Avalonia.Input; + +namespace Avalonia.Controls.DragDrop.Raw +{ + public interface IDragDevice : IInputDevice + { + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs b/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs new file mode 100644 index 00000000000..76a2c16b3b5 --- /dev/null +++ b/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs @@ -0,0 +1,31 @@ +using System; +using Avalonia.Input; +using Avalonia.Input.Raw; + +namespace Avalonia.Controls.DragDrop.Raw +{ + public class RawDragEvent : RawInputEventArgs + { + public IInputElement InputRoot { get; } + public Point Location { get; } + public IDataObject Data { get; } + public DragDropEffects Effects { get; set; } + public RawDragEventType Type { get; } + + public RawDragEvent(IDragDevice inputDevice, RawDragEventType type, + IInputElement inputRoot, Point location, IDataObject data, DragDropEffects effects) + :base(inputDevice, GetTimeStamp()) + { + Type = type; + InputRoot = inputRoot; + Location = location; + Data = data; + Effects = effects; + } + + private static uint GetTimeStamp() + { + return (uint)0; + } + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/Raw/RawDragEventType.cs b/src/Avalonia.Controls/DragDrop/Raw/RawDragEventType.cs new file mode 100644 index 00000000000..44c4bbd5955 --- /dev/null +++ b/src/Avalonia.Controls/DragDrop/Raw/RawDragEventType.cs @@ -0,0 +1,10 @@ +namespace Avalonia.Controls.DragDrop.Raw +{ + public enum RawDragEventType + { + DragEnter, + DragOver, + DragLeave, + Drop + } +} \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs index 4398b6bddb1..b767a864902 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Avalonia.Controls.DragDrop; +using Avalonia.Controls.DragDrop.Raw; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Platform; @@ -19,6 +20,7 @@ abstract class TopLevelImpl : ITopLevelImpl, IFramebufferPlatformSurface { public TopLevelView View { get; } private readonly IMouseDevice _mouse = AvaloniaLocator.Current.GetService(); + private readonly IDragDevice _dragDevice = AvaloniaLocator.Current.GetService(); protected TopLevelImpl() { View = new TopLevelView(this); @@ -37,7 +39,6 @@ public class TopLevelView : NSView bool _isLeftPressed, _isRightPressed, _isMiddlePressed; private readonly IMouseDevice _mouse; private readonly IKeyboardDevice _keyboard; - private readonly IDragDispatcher _dragDispatcher; private NSTrackingArea _area; private NSCursor _cursor; private bool _nonUiRedrawQueued; @@ -54,7 +55,6 @@ public TopLevelView(TopLevelImpl tl) _tl = tl; _mouse = AvaloniaLocator.Current.GetService(); _keyboard = AvaloniaLocator.Current.GetService(); - _dragDispatcher = AvaloniaLocator.Current.GetService(); RegisterForDraggedTypes(new string[] { "public.data" // register for any kind of data. @@ -151,72 +151,45 @@ public void SetCursor(NSCursor cursor) UpdateCursor(); } - public override NSDragOperation DraggingEntered(NSDraggingInfo sender) + private NSDragOperation SendRawDragEvent(NSDraggingInfo sender, RawDragEventType type) { + Action input = _tl.Input; + IDragDevice dragDevice = _tl._dragDevice; IInputRoot root = _tl?.InputRoot; - if (root == null || _dragDispatcher == null) + if (root == null || dragDevice == null || input == null) return NSDragOperation.None; - + var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask); DraggingInfo info = new DraggingInfo(sender); var pt = TranslateLocalPoint(info.Location); - - dragOp = _dragDispatcher.DragEnter(root, pt, info, dragOp); - - return DraggingInfo.ConvertDragOperation(dragOp); + var args = new RawDragEvent(dragDevice, type, root, pt, info, dragOp); + input(args); + return DraggingInfo.ConvertDragOperation(args.Effects); } - public override NSDragOperation DraggingUpdated(NSDraggingInfo sender) + public override NSDragOperation DraggingEntered(NSDraggingInfo sender) { - IInputRoot root = _tl?.InputRoot; - if (root == null || _dragDispatcher == null) - return NSDragOperation.None; + return SendRawDragEvent(sender, RawDragEventType.DragEnter); + } - var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask); - DraggingInfo info = new DraggingInfo(sender); - var pt = TranslateLocalPoint(info.Location); - - dragOp = _dragDispatcher.DragOver(root, pt, info, dragOp); - - return DraggingInfo.ConvertDragOperation(dragOp); + public override NSDragOperation DraggingUpdated(NSDraggingInfo sender) + { + return SendRawDragEvent(sender, RawDragEventType.DragOver); } public override void DraggingExited(NSDraggingInfo sender) { - IInputRoot root = _tl?.InputRoot; - if (root == null || _dragDispatcher == null) - return; - _dragDispatcher.DragLeave(root); + SendRawDragEvent(sender, RawDragEventType.DragLeave); } public override bool PrepareForDragOperation(NSDraggingInfo sender) { - IInputRoot root = _tl?.InputRoot; - if (root == null || _dragDispatcher == null) - return false; - - var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask); - DraggingInfo info = new DraggingInfo(sender); - var pt = TranslateLocalPoint(info.Location); - - dragOp = _dragDispatcher.DragOver(root, pt, info, dragOp); - - return dragOp != DragDropEffects.None; + return SendRawDragEvent(sender, RawDragEventType.DragOver) != NSDragOperation.None; } public override bool PerformDragOperation(NSDraggingInfo sender) { - IInputRoot root = _tl?.InputRoot; - if (root == null || _dragDispatcher == null) - return false; - - var dragOp = DraggingInfo.ConvertDragOperation(sender.DraggingSourceOperationMask); - DraggingInfo info = new DraggingInfo(sender); - var pt = TranslateLocalPoint(info.Location); - - dragOp = _dragDispatcher.Drop(root, pt, info, dragOp); - - return dragOp != DragDropEffects.None; + return SendRawDragEvent(sender, RawDragEventType.Drop) != NSDragOperation.None; } diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs index 925f9b7046b..c16c5058f3c 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -1,5 +1,7 @@ using Avalonia.Controls.DragDrop; +using Avalonia.Controls.DragDrop.Raw; using Avalonia.Input; +using Avalonia.Platform; using Avalonia.Win32.Interop; using IDataObject = Avalonia.Controls.DragDrop.IDataObject; using IOleDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; @@ -8,14 +10,16 @@ namespace Avalonia.Win32 { class OleDropTarget : IDropTarget { - private readonly IDragDispatcher _dragDispatcher; private readonly IInputElement _target; + private readonly ITopLevelImpl _tl; + private readonly IDragDevice _dragDevice; private IDataObject _currentDrag = null; - public OleDropTarget(IInputElement target) + public OleDropTarget(ITopLevelImpl tl, IInputElement target) { - _dragDispatcher = AvaloniaLocator.Current.GetService(); + _dragDevice = AvaloniaLocator.Current.GetService(); + _tl = tl; _target = target; } @@ -42,38 +46,50 @@ static DragDropEffects ConvertDropEffect(DropEffect effect) result |= DragDropEffects.Link; return result; } - + UnmanagedMethods.HRESULT IDropTarget.DragEnter(IOleDataObject pDataObj, int grfKeyState, long pt, ref DropEffect pdwEffect) { - if (_dragDispatcher == null) + var dispatch = _tl?.Input; + if (dispatch == null) { pdwEffect = DropEffect.None; return UnmanagedMethods.HRESULT.S_OK; } _currentDrag = new OleDataObject(pDataObj); - var dragLocation = GetDragLocation(pt); - - var operation = ConvertDropEffect(pdwEffect); - operation = _dragDispatcher.DragEnter(_target, dragLocation, _currentDrag, operation); - pdwEffect = ConvertDropEffect(operation); + var args = new RawDragEvent( + _dragDevice, + RawDragEventType.DragEnter, + _target, + GetDragLocation(pt), + _currentDrag, + ConvertDropEffect(pdwEffect) + ); + dispatch(args); + pdwEffect = ConvertDropEffect(args.Effects); return UnmanagedMethods.HRESULT.S_OK; } UnmanagedMethods.HRESULT IDropTarget.DragOver(int grfKeyState, long pt, ref DropEffect pdwEffect) { - if (_dragDispatcher == null) + var dispatch = _tl?.Input; + if (dispatch == null) { pdwEffect = DropEffect.None; return UnmanagedMethods.HRESULT.S_OK; } - var dragLocation = GetDragLocation(pt); - - var operation = ConvertDropEffect(pdwEffect); - operation = _dragDispatcher.DragOver(_target, dragLocation, _currentDrag, operation); - pdwEffect = ConvertDropEffect(operation); + var args = new RawDragEvent( + _dragDevice, + RawDragEventType.DragOver, + _target, + GetDragLocation(pt), + _currentDrag, + ConvertDropEffect(pdwEffect) + ); + dispatch(args); + pdwEffect = ConvertDropEffect(args.Effects); return UnmanagedMethods.HRESULT.S_OK; } @@ -82,7 +98,14 @@ UnmanagedMethods.HRESULT IDropTarget.DragLeave() { try { - _dragDispatcher?.DragLeave(_target); + _tl?.Input(new RawDragEvent( + _dragDevice, + RawDragEventType.DragLeave, + _target, + default(Point), + null, + DragDropEffects.None + )); return UnmanagedMethods.HRESULT.S_OK; } finally @@ -95,18 +118,25 @@ UnmanagedMethods.HRESULT IDropTarget.Drop(IOleDataObject pDataObj, int grfKeySta { try { - if (_dragDispatcher == null) + var dispatch = _tl?.Input; + if (dispatch == null) { pdwEffect = DropEffect.None; return UnmanagedMethods.HRESULT.S_OK; } _currentDrag= new OleDataObject(pDataObj); - var dragLocation = GetDragLocation(pt); - - var operation = ConvertDropEffect(pdwEffect); - operation = _dragDispatcher.Drop(_target, dragLocation, _currentDrag, operation); - pdwEffect = ConvertDropEffect(operation); + + var args = new RawDragEvent( + _dragDevice, + RawDragEventType.Drop, + _target, + GetDragLocation(pt), + _currentDrag, + ConvertDropEffect(pdwEffect) + ); + dispatch(args); + pdwEffect = ConvertDropEffect(args.Effects); return UnmanagedMethods.HRESULT.S_OK; } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index ed224a6525f..85e5b5b4b9c 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -693,7 +693,7 @@ private void CreateWindow() private void CreateDropTarget() { - OleDropTarget odt = new OleDropTarget(_owner); + OleDropTarget odt = new OleDropTarget(this, _owner); if (OleContext.Current?.RegisterDragDrop(Handle, odt) ?? false) _dropTarget = odt; } From be68b32440a4d96eb6268abee1c390dbca6863a8 Mon Sep 17 00:00:00 2001 From: boombuler Date: Wed, 7 Mar 2018 07:25:46 +0100 Subject: [PATCH 12/59] reworked the dataobject for drag+drop --- src/Avalonia.Controls/DragDrop/IDataObject.cs | 2 + src/OSX/Avalonia.MonoMac/DraggingInfo.cs | 13 +- .../Avalonia.Win32/ClipboardFormats.cs | 20 +- src/Windows/Avalonia.Win32/DataObject.cs | 312 ++++++++++++++++++ .../Interop/UnmanagedMethods.cs | 53 ++- src/Windows/Avalonia.Win32/OleDataObject.cs | 5 + src/Windows/Avalonia.Win32/OleDragSource.cs | 41 +++ src/Windows/Avalonia.Win32/OleDropTarget.cs | 11 +- 8 files changed, 437 insertions(+), 20 deletions(-) create mode 100644 src/Windows/Avalonia.Win32/DataObject.cs create mode 100644 src/Windows/Avalonia.Win32/OleDragSource.cs diff --git a/src/Avalonia.Controls/DragDrop/IDataObject.cs b/src/Avalonia.Controls/DragDrop/IDataObject.cs index 5ffecc13ed4..c8fd720c480 100644 --- a/src/Avalonia.Controls/DragDrop/IDataObject.cs +++ b/src/Avalonia.Controls/DragDrop/IDataObject.cs @@ -11,5 +11,7 @@ public interface IDataObject string GetText(); IEnumerable GetFileNames(); + + object Get(string dataFormat); } } \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs index ef4d9d99f60..2fb28a50f52 100644 --- a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs +++ b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs @@ -15,8 +15,7 @@ public DraggingInfo(NSDraggingInfo info) { _info = info; } - - + internal static NSDragOperation ConvertDragOperation(DragDropEffects d) { NSDragOperation result = NSDragOperation.None; @@ -77,5 +76,15 @@ public bool Contains(string dataFormat) { return GetDataFormats().Any(f => f == dataFormat); } + + public object Get(string dataFormat) + { + if (dataFormat == DataFormats.Text) + return GetText(); + if (dataFormat == DataFormats.FileNames) + return GetFileNames(); + + return _info.DraggingPasteboard.GetDataForType(dataFormat).ToArray(); + } } } \ No newline at end of file diff --git a/src/Windows/Avalonia.Win32/ClipboardFormats.cs b/src/Windows/Avalonia.Win32/ClipboardFormats.cs index 254facf81a2..a59c6a1664d 100644 --- a/src/Windows/Avalonia.Win32/ClipboardFormats.cs +++ b/src/Windows/Avalonia.Win32/ClipboardFormats.cs @@ -10,34 +10,34 @@ namespace Avalonia.Win32 { static class ClipboardFormats { + private const int MAX_FORMAT_NAME_LENGTH = 260; + class ClipboardFormat { public short Format { get; private set; } public string Name { get; private set; } + public short[] Synthesized { get; private set; } - public ClipboardFormat(string name, short format) + public ClipboardFormat(string name, short format, params short[] synthesized) { Format = format; Name = name; + Synthesized = synthesized; } } private static readonly List FormatList = new List() { - new ClipboardFormat(DataFormats.Text, (short)UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT), + new ClipboardFormat(DataFormats.Text, (short)UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, (short)UnmanagedMethods.ClipboardFormat.CF_TEXT), new ClipboardFormat(DataFormats.FileNames, (short)UnmanagedMethods.ClipboardFormat.CF_HDROP), }; private static string QueryFormatName(short format) { - int len = UnmanagedMethods.GetClipboardFormatName(format, null, 0); - if (len > 0) - { - StringBuilder sb = new StringBuilder(len); - if (UnmanagedMethods.GetClipboardFormatName(format, sb, len) <= len) - return sb.ToString(); - } + StringBuilder sb = new StringBuilder(MAX_FORMAT_NAME_LENGTH); + if (UnmanagedMethods.GetClipboardFormatName(format, sb, sb.Capacity) > 0) + return sb.ToString(); return null; } @@ -45,7 +45,7 @@ public static string GetFormat(short format) { lock (FormatList) { - var pd = FormatList.FirstOrDefault(f => f.Format == format); + var pd = FormatList.FirstOrDefault(f => f.Format == format || Array.IndexOf(f.Synthesized, format) >= 0); if (pd == null) { string name = QueryFormatName(format); diff --git a/src/Windows/Avalonia.Win32/DataObject.cs b/src/Windows/Avalonia.Win32/DataObject.cs new file mode 100644 index 00000000000..6248afd7bb5 --- /dev/null +++ b/src/Windows/Avalonia.Win32/DataObject.cs @@ -0,0 +1,312 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using Avalonia.Controls.DragDrop; +using Avalonia.Win32.Interop; +using IDataObject = Avalonia.Controls.DragDrop.IDataObject; +using IOleDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; + +namespace Avalonia.Win32 +{ + class DataObject : IDataObject, IOleDataObject + { + class FormatEnumerator : IEnumFORMATETC + { + private FORMATETC[] _formats; + private int _current; + + private FormatEnumerator(FORMATETC[] formats, int current) + { + _formats = formats; + _current = current; + } + + public FormatEnumerator(IDataObject dataobj) + { + _formats = dataobj.GetDataFormats().Select(ConvertToFormatEtc).ToArray(); + _current = 0; + } + + private FORMATETC ConvertToFormatEtc(string aFormatName) + { + FORMATETC result = default(FORMATETC); + result.cfFormat = ClipboardFormats.GetFormat(aFormatName); + result.dwAspect = DVASPECT.DVASPECT_CONTENT; + result.ptd = IntPtr.Zero; + result.lindex = -1; + result.tymed = TYMED.TYMED_HGLOBAL; + return result; + } + + public void Clone(out IEnumFORMATETC newEnum) + { + newEnum = new FormatEnumerator(_formats, _current); + } + + public int Next(int celt, FORMATETC[] rgelt, int[] pceltFetched) + { + if (rgelt == null) + return unchecked((int)UnmanagedMethods.HRESULT.E_INVALIDARG); + + int i = 0; + while (i < celt && _current < _formats.Length) + { + rgelt[i] = _formats[_current]; + _current++; + i++; + } + if (pceltFetched != null) + pceltFetched[0] = i; + + if (i != celt) + return unchecked((int)UnmanagedMethods.HRESULT.S_FALSE); + return unchecked((int)UnmanagedMethods.HRESULT.S_OK); + } + + public int Reset() + { + _current = 0; + return unchecked((int)UnmanagedMethods.HRESULT.S_OK); + } + + public int Skip(int celt) + { + _current += Math.Min(celt, int.MaxValue - _current); + if (_current >= _formats.Length) + return unchecked((int)UnmanagedMethods.HRESULT.S_FALSE); + return unchecked((int)UnmanagedMethods.HRESULT.S_OK); + } + } + + private const int DV_E_TYMED = unchecked((int)0x80040069); + private const int DV_E_DVASPECT = unchecked((int)0x8004006B); + private const int DV_E_FORMATETC = unchecked((int)0x80040064); + private const int OLE_E_ADVISENOTSUPPORTED = unchecked((int)0x80040003); + private const int STG_E_MEDIUMFULL = unchecked((int)0x80030070); + private const int GMEM_ZEROINIT = 0x0040; + private const int GMEM_MOVEABLE = 0x0002; + + + IDataObject _wrapped; + + public DataObject(IDataObject wrapped) + { + _wrapped = wrapped; + } + + #region IDataObject + bool IDataObject.Contains(string dataFormat) + { + return _wrapped.Contains(dataFormat); + } + + IEnumerable IDataObject.GetDataFormats() + { + return _wrapped.GetDataFormats(); + } + + IEnumerable IDataObject.GetFileNames() + { + return _wrapped.GetFileNames(); + } + + string IDataObject.GetText() + { + return _wrapped.GetText(); + } + + object IDataObject.Get(string dataFormat) + { + return _wrapped.Get(dataFormat); + } + #endregion + + #region IOleDataObject + + int IOleDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection) + { + if (_wrapped is IOleDataObject ole) + return ole.DAdvise(ref pFormatetc, advf, adviseSink, out connection); + connection = 0; + return OLE_E_ADVISENOTSUPPORTED; + } + + void IOleDataObject.DUnadvise(int connection) + { + if (_wrapped is IOleDataObject ole) + ole.DUnadvise(connection); + Marshal.ThrowExceptionForHR(OLE_E_ADVISENOTSUPPORTED); + } + + int IOleDataObject.EnumDAdvise(out IEnumSTATDATA enumAdvise) + { + if (_wrapped is IOleDataObject ole) + return ole.EnumDAdvise(out enumAdvise); + + enumAdvise = null; + return OLE_E_ADVISENOTSUPPORTED; + } + + IEnumFORMATETC IOleDataObject.EnumFormatEtc(DATADIR direction) + { + if (_wrapped is IOleDataObject ole) + return ole.EnumFormatEtc(direction); + if (direction == DATADIR.DATADIR_GET) + return new FormatEnumerator(_wrapped); + throw new NotSupportedException(); + } + + int IOleDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut) + { + if (_wrapped is IOleDataObject ole) + return ole.GetCanonicalFormatEtc(ref formatIn, out formatOut); + + formatOut = new FORMATETC(); + formatOut.ptd = IntPtr.Zero; + return unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL); + } + + void IOleDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium) + { + if (_wrapped is IOleDataObject ole) + { + ole.GetData(ref format, out medium); + return; + } + if(!format.tymed.HasFlag(TYMED.TYMED_HGLOBAL)) + Marshal.ThrowExceptionForHR(DV_E_TYMED); + + if (format.dwAspect != DVASPECT.DVASPECT_CONTENT) + Marshal.ThrowExceptionForHR(DV_E_DVASPECT); + + string fmt = ClipboardFormats.GetFormat(format.cfFormat); + if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt)) + Marshal.ThrowExceptionForHR(DV_E_FORMATETC); + + medium = default(STGMEDIUM); + medium.tymed = TYMED.TYMED_HGLOBAL; + int result = WriteDataToHGlobal(fmt, ref medium.unionmember); + Marshal.ThrowExceptionForHR(result); + } + + void IOleDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium) + { + if (_wrapped is IOleDataObject ole) + { + ole.GetDataHere(ref format, ref medium); + return; + } + + if (medium.tymed != TYMED.TYMED_HGLOBAL || !format.tymed.HasFlag(TYMED.TYMED_HGLOBAL)) + Marshal.ThrowExceptionForHR(DV_E_TYMED); + + if (format.dwAspect != DVASPECT.DVASPECT_CONTENT) + Marshal.ThrowExceptionForHR(DV_E_DVASPECT); + + string fmt = ClipboardFormats.GetFormat(format.cfFormat); + if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt)) + Marshal.ThrowExceptionForHR(DV_E_FORMATETC); + + if (medium.unionmember == IntPtr.Zero) + Marshal.ThrowExceptionForHR(STG_E_MEDIUMFULL); + + int result = WriteDataToHGlobal(fmt, ref medium.unionmember); + Marshal.ThrowExceptionForHR(result); + } + + int IOleDataObject.QueryGetData(ref FORMATETC format) + { + if (_wrapped is IOleDataObject ole) + return ole.QueryGetData(ref format); + if (format.dwAspect != DVASPECT.DVASPECT_CONTENT) + return DV_E_DVASPECT; + if (!format.tymed.HasFlag(TYMED.TYMED_HGLOBAL)) + return DV_E_TYMED; + + string dataFormat = ClipboardFormats.GetFormat(format.cfFormat); + if (!string.IsNullOrEmpty(dataFormat) && _wrapped.Contains(dataFormat)) + return unchecked((int)UnmanagedMethods.HRESULT.S_OK); + return DV_E_FORMATETC; + } + + void IOleDataObject.SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release) + { + if (_wrapped is IOleDataObject ole) + { + ole.SetData(ref formatIn, ref medium, release); + return; + } + Marshal.ThrowExceptionForHR(unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL)); + } + + private int WriteDataToHGlobal(string dataFormat, ref IntPtr hGlobal) + { + object data = _wrapped.Get(dataFormat); + if (dataFormat == DataFormats.Text || data is string) + return WriteStringToHGlobal(ref hGlobal, Convert.ToString(data)); + if (dataFormat == DataFormats.FileNames && data is IEnumerable files) + return WriteFileListToHGlobal(ref hGlobal, files); + return DV_E_TYMED; + } + + private int WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable files) + { + if (!files?.Any() ?? false) + return unchecked((int)UnmanagedMethods.HRESULT.S_OK); + + char[] filesStr = (string.Join("\0", files) + "\0\0").ToCharArray(); + _DROPFILES df = new _DROPFILES(); + df.pFiles = Marshal.SizeOf<_DROPFILES>(); + df.fWide = true; + + int required = (filesStr.Length * sizeof(char)) + Marshal.SizeOf<_DROPFILES>(); + if (hGlobal == IntPtr.Zero) + hGlobal = UnmanagedMethods.GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, required); + + long available = UnmanagedMethods.GlobalSize(hGlobal).ToInt64(); + if (required > available) + return STG_E_MEDIUMFULL; + + IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal); + try + { + Marshal.StructureToPtr(df, ptr, false); + + Marshal.Copy(filesStr, 0, ptr + Marshal.SizeOf<_DROPFILES>(), filesStr.Length); + return unchecked((int)UnmanagedMethods.HRESULT.S_OK); + } + finally + { + UnmanagedMethods.GlobalUnlock(hGlobal); + } + } + + private int WriteStringToHGlobal(ref IntPtr hGlobal, string data) + { + int required = (data.Length + 1) * sizeof(char); + if (hGlobal == IntPtr.Zero) + hGlobal = UnmanagedMethods.GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, required); + + long available = UnmanagedMethods.GlobalSize(hGlobal).ToInt64(); + if (required > available) + return STG_E_MEDIUMFULL; + + IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal); + try + { + char[] chars = (data + '\0').ToCharArray(); + Marshal.Copy(chars, 0, ptr, chars.Length); + return unchecked((int)UnmanagedMethods.HRESULT.S_OK); + } + finally + { + UnmanagedMethods.GlobalUnlock(hGlobal); + } + } + + #endregion + } +} diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index f43cdb98cc6..aa86ab0f8d5 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -973,7 +973,11 @@ public static extern IntPtr CreateFileMapping(IntPtr hFile, [DllImport("shell32.dll", BestFitMapping = false, CharSet = CharSet.Auto)] public static extern int DragQueryFile(IntPtr hDrop, int iFile, StringBuilder lpszFile, int cch); - + [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = false)] + public static extern void DoDragDrop(IDataObject dataObject, IDropSource dropSource, int allowedEffects, int[] finalEffect); + + + public enum MONITOR { MONITOR_DEFAULTTONULL = 0x00000000, @@ -1013,11 +1017,28 @@ public enum MONITOR_DPI_TYPE MDT_DEFAULT = MDT_EFFECTIVE_DPI } - public enum ClipboardFormat + public enum ClipboardFormat { + /// + /// Text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals the end of the data. Use this format for ANSI text. + /// CF_TEXT = 1, + /// + /// A handle to a bitmap + /// + CF_BITMAP = 2, + /// + /// A memory object containing a BITMAPINFO structure followed by the bitmap bits. + /// + CF_DIB = 3, + /// + /// Unicode text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals the end of the data. + /// CF_UNICODETEXT = 13, - CF_HDROP = 15 + /// + /// A handle to type HDROP that identifies a list of files. + /// + CF_HDROP = 15, } public struct MSG @@ -1160,7 +1181,9 @@ public enum HRESULT : long S_FALSE = 0x0001, S_OK = 0x0000, E_INVALIDARG = 0x80070057, - E_OUTOFMEMORY = 0x8007000E + E_OUTOFMEMORY = 0x8007000E, + E_NOTIMPL = 0x80004001, + E_UNEXPECTED = 0x8000FFFF, } public enum Icons @@ -1351,4 +1374,26 @@ internal interface IDropTarget [PreserveSig] UnmanagedMethods.HRESULT Drop([MarshalAs(UnmanagedType.Interface)] [In] IDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("00000121-0000-0000-C000-000000000046")] + internal interface IDropSource + { + [PreserveSig] + int QueryContinueDrag(int fEscapePressed, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState); + [PreserveSig] + int GiveFeedback([MarshalAs(UnmanagedType.U4)] [In] int dwEffect); + } + + + [StructLayoutAttribute(LayoutKind.Sequential)] + internal struct _DROPFILES + { + public Int32 pFiles; + public Int32 X; + public Int32 Y; + public bool fNC; + public bool fWide; + } } diff --git a/src/Windows/Avalonia.Win32/OleDataObject.cs b/src/Windows/Avalonia.Win32/OleDataObject.cs index bf6f3d98c3e..fe6faa162cd 100644 --- a/src/Windows/Avalonia.Win32/OleDataObject.cs +++ b/src/Windows/Avalonia.Win32/OleDataObject.cs @@ -39,6 +39,11 @@ public IEnumerable GetFileNames() return GetDataFromOleHGLOBAL(DataFormats.FileNames, DVASPECT.DVASPECT_CONTENT) as IEnumerable; } + public object Get(string dataFormat) + { + return GetDataFromOleHGLOBAL(dataFormat, DVASPECT.DVASPECT_CONTENT); + } + private object GetDataFromOleHGLOBAL(string format, DVASPECT aspect) { FORMATETC formatEtc = new FORMATETC(); diff --git a/src/Windows/Avalonia.Win32/OleDragSource.cs b/src/Windows/Avalonia.Win32/OleDragSource.cs new file mode 100644 index 00000000000..1bff9840875 --- /dev/null +++ b/src/Windows/Avalonia.Win32/OleDragSource.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Avalonia.Win32.Interop; + +namespace Avalonia.Win32 +{ + class OleDragSource : IDropSource + { + private const int DRAGDROP_S_USEDEFAULTCURSORS = 0x00040102; + private const int DRAGDROP_S_DROP = 0x00040100; + private const int DRAGDROP_S_CANCEL = 0x00040101; + + private const int KEYSTATE_LEFTMB = 1; + private const int KEYSTATE_MIDDLEMB = 16; + private const int KEYSTATE_RIGHTMB = 2; + private static readonly int[] MOUSE_BUTTONS = new int[] { KEYSTATE_LEFTMB, KEYSTATE_MIDDLEMB, KEYSTATE_RIGHTMB }; + + public int QueryContinueDrag(int fEscapePressed, int grfKeyState) + { + if (fEscapePressed != 0) + return DRAGDROP_S_CANCEL; + + int pressedMouseButtons = MOUSE_BUTTONS.Where(mb => (grfKeyState & mb) == mb).Count(); + + if (pressedMouseButtons >= 2) + return DRAGDROP_S_CANCEL; + if (pressedMouseButtons == 0) + return DRAGDROP_S_DROP; + + return unchecked((int)UnmanagedMethods.HRESULT.S_OK); + } + + public int GiveFeedback(int dwEffect) + { + if (dwEffect != 0) + return DRAGDROP_S_USEDEFAULTCURSORS; + return unchecked((int)UnmanagedMethods.HRESULT.S_OK); + } + } +} diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs index c16c5058f3c..6bc298951ec 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -55,8 +55,9 @@ UnmanagedMethods.HRESULT IDropTarget.DragEnter(IOleDataObject pDataObj, int grfK pdwEffect = DropEffect.None; return UnmanagedMethods.HRESULT.S_OK; } - - _currentDrag = new OleDataObject(pDataObj); + _currentDrag = pDataObj as IDataObject; + if (_currentDrag == null) + _currentDrag = new OleDataObject(pDataObj); var args = new RawDragEvent( _dragDevice, RawDragEventType.DragEnter, @@ -124,8 +125,10 @@ UnmanagedMethods.HRESULT IDropTarget.Drop(IOleDataObject pDataObj, int grfKeySta pdwEffect = DropEffect.None; return UnmanagedMethods.HRESULT.S_OK; } - - _currentDrag= new OleDataObject(pDataObj); + + _currentDrag = pDataObj as IDataObject; + if (_currentDrag == null) + _currentDrag= new OleDataObject(pDataObj); var args = new RawDragEvent( _dragDevice, From f57fbdc2da30ff6ab7b713e5fae2c7cb867b79bc Mon Sep 17 00:00:00 2001 From: boombuler Date: Wed, 7 Mar 2018 07:31:54 +0100 Subject: [PATCH 13/59] renamed DragDevice to DragDropDevice --- .../DragDrop/{DragDevice.cs => DragDropDevice.cs} | 4 ++-- .../DragDrop/Raw/{IDragDevice.cs => IDragDropDevice.cs} | 2 +- src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs | 2 +- src/OSX/Avalonia.MonoMac/TopLevelImpl.cs | 4 ++-- src/Windows/Avalonia.Win32/OleDropTarget.cs | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) rename src/Avalonia.Controls/DragDrop/{DragDevice.cs => DragDropDevice.cs} (96%) rename src/Avalonia.Controls/DragDrop/Raw/{IDragDevice.cs => IDragDropDevice.cs} (61%) diff --git a/src/Avalonia.Controls/DragDrop/DragDevice.cs b/src/Avalonia.Controls/DragDrop/DragDropDevice.cs similarity index 96% rename from src/Avalonia.Controls/DragDrop/DragDevice.cs rename to src/Avalonia.Controls/DragDrop/DragDropDevice.cs index 25f82ca3bc9..0aadda79f42 100644 --- a/src/Avalonia.Controls/DragDrop/DragDevice.cs +++ b/src/Avalonia.Controls/DragDrop/DragDropDevice.cs @@ -7,9 +7,9 @@ namespace Avalonia.Controls.DragDrop { - class DragDevice : IDragDevice + class DragDropDevice : IDragDropDevice { - public static readonly DragDevice Instance = new DragDevice(); + public static readonly DragDropDevice Instance = new DragDropDevice(); private Interactive _lastTarget = null; diff --git a/src/Avalonia.Controls/DragDrop/Raw/IDragDevice.cs b/src/Avalonia.Controls/DragDrop/Raw/IDragDropDevice.cs similarity index 61% rename from src/Avalonia.Controls/DragDrop/Raw/IDragDevice.cs rename to src/Avalonia.Controls/DragDrop/Raw/IDragDropDevice.cs index d1c20fae558..6e2db67ac33 100644 --- a/src/Avalonia.Controls/DragDrop/Raw/IDragDevice.cs +++ b/src/Avalonia.Controls/DragDrop/Raw/IDragDropDevice.cs @@ -2,7 +2,7 @@ namespace Avalonia.Controls.DragDrop.Raw { - public interface IDragDevice : IInputDevice + public interface IDragDropDevice : IInputDevice { } } \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs b/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs index 76a2c16b3b5..e9c1119e444 100644 --- a/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs +++ b/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs @@ -12,7 +12,7 @@ public class RawDragEvent : RawInputEventArgs public DragDropEffects Effects { get; set; } public RawDragEventType Type { get; } - public RawDragEvent(IDragDevice inputDevice, RawDragEventType type, + public RawDragEvent(IDragDropDevice inputDevice, RawDragEventType type, IInputElement inputRoot, Point location, IDataObject data, DragDropEffects effects) :base(inputDevice, GetTimeStamp()) { diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs index b767a864902..20d255a608d 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -20,7 +20,7 @@ abstract class TopLevelImpl : ITopLevelImpl, IFramebufferPlatformSurface { public TopLevelView View { get; } private readonly IMouseDevice _mouse = AvaloniaLocator.Current.GetService(); - private readonly IDragDevice _dragDevice = AvaloniaLocator.Current.GetService(); + private readonly IDragDropDevice _dragDevice = AvaloniaLocator.Current.GetService(); protected TopLevelImpl() { View = new TopLevelView(this); @@ -154,7 +154,7 @@ public void SetCursor(NSCursor cursor) private NSDragOperation SendRawDragEvent(NSDraggingInfo sender, RawDragEventType type) { Action input = _tl.Input; - IDragDevice dragDevice = _tl._dragDevice; + IDragDropDevice dragDevice = _tl._dragDevice; IInputRoot root = _tl?.InputRoot; if (root == null || dragDevice == null || input == null) return NSDragOperation.None; diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs index 6bc298951ec..31db5f88a91 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -12,13 +12,13 @@ class OleDropTarget : IDropTarget { private readonly IInputElement _target; private readonly ITopLevelImpl _tl; - private readonly IDragDevice _dragDevice; + private readonly IDragDropDevice _dragDevice; private IDataObject _currentDrag = null; public OleDropTarget(ITopLevelImpl tl, IInputElement target) { - _dragDevice = AvaloniaLocator.Current.GetService(); + _dragDevice = AvaloniaLocator.Current.GetService(); _tl = tl; _target = target; } From 30b5a1fd4df156180b2151562d5689f9a3ab4185 Mon Sep 17 00:00:00 2001 From: boombuler Date: Wed, 7 Mar 2018 07:43:51 +0100 Subject: [PATCH 14/59] fixed locator setup --- src/Avalonia.Controls/Application.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index b751aeca604..0a9bf4eab2e 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -237,7 +237,7 @@ public virtual void RegisterServices() .Bind().ToSingleton() .Bind().ToConstant(this) .Bind().ToConstant(AvaloniaScheduler.Instance) - .Bind().ToConstant(DragDevice.Instance); + .Bind().ToConstant(DragDropDevice.Instance); } } } From c1abc0535534e66697b08b0cd3e58530189ef297 Mon Sep 17 00:00:00 2001 From: boombuler Date: Wed, 7 Mar 2018 07:45:55 +0100 Subject: [PATCH 15/59] Modified the ControlCatalog examples to run in STA ApartmentState --- samples/ControlCatalog.Desktop/Program.cs | 1 + samples/ControlCatalog.NetCore/Program.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index b151cabf43f..a2048005a4d 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/samples/ControlCatalog.Desktop/Program.cs @@ -10,6 +10,7 @@ namespace ControlCatalog { internal class Program { + [STAThread] static void Main(string[] args) { // TODO: Make this work with GTK/Skia/Cairo depending on command-line args diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 346535d39de..b45a93455e3 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -9,8 +9,10 @@ namespace ControlCatalog.NetCore { static class Program { + static void Main(string[] args) { + Thread.CurrentThread.TrySetApartmentState(ApartmentState.STA); if (args.Contains("--wait-for-attach")) { Console.WriteLine("Attach debugger and use 'Set next statement'"); From 411e3c8860b1756ce8b01943a1091d5481493b02 Mon Sep 17 00:00:00 2001 From: boombuler Date: Wed, 7 Mar 2018 07:46:28 +0100 Subject: [PATCH 16/59] started dragging API --- src/Avalonia.Controls/DragDrop/DataObject.cs | 43 ++++++++++++++++++++ src/Avalonia.Controls/DragDrop/DragDrop.cs | 10 +++++ 2 files changed, 53 insertions(+) create mode 100644 src/Avalonia.Controls/DragDrop/DataObject.cs diff --git a/src/Avalonia.Controls/DragDrop/DataObject.cs b/src/Avalonia.Controls/DragDrop/DataObject.cs new file mode 100644 index 00000000000..e0e7b6d069f --- /dev/null +++ b/src/Avalonia.Controls/DragDrop/DataObject.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Avalonia.Controls.DragDrop +{ + public class DataObject : IDataObject + { + private readonly Dictionary _items = new Dictionary(); + + public bool Contains(string dataFormat) + { + return _items.ContainsKey(dataFormat); + } + + public object Get(string dataFormat) + { + if (_items.ContainsKey(dataFormat)) + return _items[dataFormat]; + return null; + } + + public IEnumerable GetDataFormats() + { + return _items.Keys; + } + + public IEnumerable GetFileNames() + { + return Get(DataFormats.FileNames) as IEnumerable; + } + + public string GetText() + { + return Get(DataFormats.Text) as string; + } + + public void Set(string dataFormat, object value) + { + _items[dataFormat] = value; + } + } +} diff --git a/src/Avalonia.Controls/DragDrop/DragDrop.cs b/src/Avalonia.Controls/DragDrop/DragDrop.cs index 317a903d3c9..af003516b12 100644 --- a/src/Avalonia.Controls/DragDrop/DragDrop.cs +++ b/src/Avalonia.Controls/DragDrop/DragDrop.cs @@ -20,5 +20,15 @@ public static void SetAcceptDrag(Interactive interactive, bool value) { interactive.SetValue(AcceptDragProperty, value); } + + /// + /// Starts a dragging operation with the given and returns the applied drop effect from the target. + /// + /// + public static DragDropEffects DoDragDrop(this Interactive source, IDataObject data, DragDropEffects allowedEffects) + { + + return DragDropEffects.None; + } } } \ No newline at end of file From 7b3942685e4495231a658afb22d204508d995a9d Mon Sep 17 00:00:00 2001 From: boombuler Date: Wed, 7 Mar 2018 14:02:06 +0100 Subject: [PATCH 17/59] Basic generic + Windows Drag support. --- src/Avalonia.Controls/Application.cs | 4 +- src/Avalonia.Controls/DragDrop/DragDrop.cs | 10 +- src/Avalonia.Controls/DragDrop/DragSource.cs | 129 ++++++++++++++++++ .../Platform/IPlatformDragSource.cs | 14 ++ src/Windows/Avalonia.Win32/DragSource.cs | 28 ++++ src/Windows/Avalonia.Win32/OleDragSource.cs | 4 +- src/Windows/Avalonia.Win32/OleDropTarget.cs | 4 +- src/Windows/Avalonia.Win32/Win32Platform.cs | 3 +- 8 files changed, 185 insertions(+), 11 deletions(-) create mode 100644 src/Avalonia.Controls/DragDrop/DragSource.cs create mode 100644 src/Avalonia.Controls/Platform/IPlatformDragSource.cs create mode 100644 src/Windows/Avalonia.Win32/DragSource.cs diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 0a9bf4eab2e..6ed797dc513 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -14,6 +14,7 @@ using System.Reactive.Concurrency; using Avalonia.Controls.DragDrop; using Avalonia.Controls.DragDrop.Raw; +using Avalonia.Controls.Platform; namespace Avalonia { @@ -237,7 +238,8 @@ public virtual void RegisterServices() .Bind().ToSingleton() .Bind().ToConstant(this) .Bind().ToConstant(AvaloniaScheduler.Instance) - .Bind().ToConstant(DragDropDevice.Instance); + .Bind().ToConstant(DragDropDevice.Instance) + .Bind().ToTransient(); } } } diff --git a/src/Avalonia.Controls/DragDrop/DragDrop.cs b/src/Avalonia.Controls/DragDrop/DragDrop.cs index af003516b12..bbac80e38fa 100644 --- a/src/Avalonia.Controls/DragDrop/DragDrop.cs +++ b/src/Avalonia.Controls/DragDrop/DragDrop.cs @@ -1,4 +1,6 @@ -using Avalonia.Interactivity; +using System.Threading.Tasks; +using Avalonia.Controls.Platform; +using Avalonia.Interactivity; namespace Avalonia.Controls.DragDrop { @@ -25,10 +27,10 @@ public static void SetAcceptDrag(Interactive interactive, bool value) /// Starts a dragging operation with the given and returns the applied drop effect from the target. /// /// - public static DragDropEffects DoDragDrop(this Interactive source, IDataObject data, DragDropEffects allowedEffects) + public static Task DoDragDrop(IDataObject data, DragDropEffects allowedEffects) { - - return DragDropEffects.None; + var src = AvaloniaLocator.Current.GetService(); + return src?.DoDragDrop(data, allowedEffects) ?? Task.FromResult(DragDropEffects.None); } } } \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/DragSource.cs b/src/Avalonia.Controls/DragDrop/DragSource.cs new file mode 100644 index 00000000000..f899eabfbd0 --- /dev/null +++ b/src/Avalonia.Controls/DragDrop/DragSource.cs @@ -0,0 +1,129 @@ +using System; +using System.Linq; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls.DragDrop.Raw; +using Avalonia.Controls.Platform; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Interactivity; +using Avalonia.Threading; +using Avalonia.VisualTree; + +namespace Avalonia.Controls.DragDrop +{ + class DragSource : IPlatformDragSource + { + private const InputModifiers MOUSE_INPUTMODIFIERS = InputModifiers.LeftMouseButton|InputModifiers.MiddleMouseButton|InputModifiers.RightMouseButton; + private readonly IDragDropDevice _dragDrop; + private readonly IInputManager _inputManager; + + + private readonly Subject _result = new Subject(); + private IDataObject _draggedData; + private IInputRoot _lastRoot; + private InputModifiers? _initialInputModifiers; + + public DragSource() + { + _inputManager = AvaloniaLocator.Current.GetService(); + _dragDrop = AvaloniaLocator.Current.GetService(); + } + + public async Task DoDragDrop(IDataObject data, DragDropEffects allowedEffects) + { + Dispatcher.UIThread.VerifyAccess(); + if (_draggedData == null) + { + _draggedData = data; + _lastRoot = null; + + using (_inputManager.PreProcess.OfType().Subscribe(e => ProcessMouseEvents(e, allowedEffects))) + { + var effect = await _result.FirstAsync(); + return effect; + } + } + return DragDropEffects.None; + } + + private DragDropEffects RaiseDragEvent(RawDragEventType type, IInputElement root, Point pt, DragDropEffects allowedEffects) + { + RawDragEvent rawEvent = new RawDragEvent(_dragDrop, type, root, pt, _draggedData, allowedEffects); + var tl = root.GetSelfAndVisualAncestors().OfType().FirstOrDefault(); + tl.PlatformImpl.Input(rawEvent); + return rawEvent.Effects; + } + + private void ProcessMouseEvents(RawMouseEventArgs e, DragDropEffects allowedEffects) + { + if (!_initialInputModifiers.HasValue) + _initialInputModifiers = e.InputModifiers & MOUSE_INPUTMODIFIERS; + + void CancelDragging() + { + if (_lastRoot != null) + RaiseDragEvent(RawDragEventType.DragLeave, _lastRoot, _lastRoot.PointToClient(e.Root.PointToScreen(e.Position)), allowedEffects); + _result.OnNext(DragDropEffects.None); + e.Handled = true; + } + void AcceptDragging() + { + var result = RaiseDragEvent(RawDragEventType.Drop, e.Root, e.Position, allowedEffects) & allowedEffects; + _result.OnNext(result); + e.Handled = true; + } + + switch (e.Type) + { + case RawMouseEventType.LeftButtonDown: + case RawMouseEventType.RightButtonDown: + case RawMouseEventType.MiddleButtonDown: + case RawMouseEventType.NonClientLeftButtonDown: + CancelDragging(); + return; + case RawMouseEventType.LeaveWindow: + RaiseDragEvent(RawDragEventType.DragLeave, e.Root, e.Position, allowedEffects); + break; + case RawMouseEventType.LeftButtonUp: + if (_initialInputModifiers.Value.HasFlag(InputModifiers.LeftMouseButton)) + AcceptDragging(); + else + CancelDragging(); + return; + case RawMouseEventType.MiddleButtonUp: + if (_initialInputModifiers.Value.HasFlag(InputModifiers.MiddleMouseButton)) + AcceptDragging(); + else + CancelDragging(); + return; + case RawMouseEventType.RightButtonUp: + if (_initialInputModifiers.Value.HasFlag(InputModifiers.RightMouseButton)) + AcceptDragging(); + else + CancelDragging(); + return; + case RawMouseEventType.Move: + var mods = e.InputModifiers & MOUSE_INPUTMODIFIERS; + if (_initialInputModifiers.Value != mods) + { + CancelDragging(); + return; + } + + if (e.Root != _lastRoot) + { + if (_lastRoot != null) + RaiseDragEvent(RawDragEventType.DragLeave, _lastRoot, _lastRoot.PointToClient(e.Root.PointToScreen(e.Position)), allowedEffects); + RaiseDragEvent(RawDragEventType.DragEnter, e.Root, e.Position, allowedEffects); + _lastRoot = e.Root; + } + else + RaiseDragEvent(RawDragEventType.DragOver, e.Root, e.Position, allowedEffects); + return; + } + } + } +} diff --git a/src/Avalonia.Controls/Platform/IPlatformDragSource.cs b/src/Avalonia.Controls/Platform/IPlatformDragSource.cs new file mode 100644 index 00000000000..667e1f005c1 --- /dev/null +++ b/src/Avalonia.Controls/Platform/IPlatformDragSource.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls.DragDrop; +using Avalonia.Interactivity; + +namespace Avalonia.Controls.Platform +{ + public interface IPlatformDragSource + { + Task DoDragDrop(IDataObject data, DragDropEffects allowedEffects); + } +} diff --git a/src/Windows/Avalonia.Win32/DragSource.cs b/src/Windows/Avalonia.Win32/DragSource.cs new file mode 100644 index 00000000000..937471f9ad4 --- /dev/null +++ b/src/Windows/Avalonia.Win32/DragSource.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls.DragDrop; +using Avalonia.Controls.Platform; +using Avalonia.Interactivity; +using Avalonia.Threading; +using Avalonia.Win32.Interop; + +namespace Avalonia.Win32 +{ + class DragSource : IPlatformDragSource + { + public Task DoDragDrop(IDataObject data, DragDropEffects allowedEffects) + { + Dispatcher.UIThread.VerifyAccess(); + + OleDragSource src = new OleDragSource(); + DataObject dataObject = new DataObject(data); + int allowed = (int)OleDropTarget.ConvertDropEffect(allowedEffects); + + int[] finalEffect = new int[1]; + UnmanagedMethods.DoDragDrop(dataObject, src, allowed, finalEffect); + + return Task.FromResult(OleDropTarget.ConvertDropEffect((DropEffect)finalEffect[0]));} + } +} diff --git a/src/Windows/Avalonia.Win32/OleDragSource.cs b/src/Windows/Avalonia.Win32/OleDragSource.cs index 1bff9840875..522014abc07 100644 --- a/src/Windows/Avalonia.Win32/OleDragSource.cs +++ b/src/Windows/Avalonia.Win32/OleDragSource.cs @@ -33,9 +33,7 @@ public int QueryContinueDrag(int fEscapePressed, int grfKeyState) public int GiveFeedback(int dwEffect) { - if (dwEffect != 0) - return DRAGDROP_S_USEDEFAULTCURSORS; - return unchecked((int)UnmanagedMethods.HRESULT.S_OK); + return DRAGDROP_S_USEDEFAULTCURSORS; } } } diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs index 31db5f88a91..747006673aa 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -23,7 +23,7 @@ public OleDropTarget(ITopLevelImpl tl, IInputElement target) _target = target; } - static DropEffect ConvertDropEffect(DragDropEffects operation) + public static DropEffect ConvertDropEffect(DragDropEffects operation) { DropEffect result = DropEffect.None; if (operation.HasFlag(DragDropEffects.Copy)) @@ -35,7 +35,7 @@ static DropEffect ConvertDropEffect(DragDropEffects operation) return result; } - static DragDropEffects ConvertDropEffect(DropEffect effect) + public static DragDropEffects ConvertDropEffect(DropEffect effect) { DragDropEffects result = DragDropEffects.None; if (effect.HasFlag(DropEffect.Copy)) diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 4e1ba618a8b..ef1c4e987d6 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -84,7 +84,8 @@ public static void Initialize(bool deferredRendering = true) .Bind().ToConstant(new RenderLoop(60)) .Bind().ToSingleton() .Bind().ToConstant(s_instance) - .Bind().ToConstant(s_instance); + .Bind().ToConstant(s_instance) + .Bind().ToSingleton(); UseDeferredRendering = deferredRendering; _uiThread = UnmanagedMethods.GetCurrentThreadId(); From 81aaa0938309fab331725189c841b5b5bd35c247 Mon Sep 17 00:00:00 2001 From: boombuler Date: Wed, 7 Mar 2018 18:43:07 +0100 Subject: [PATCH 18/59] set the cursor for the drop-effects --- src/Avalonia.Controls/DragDrop/DragSource.cs | 47 ++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/DragDrop/DragSource.cs b/src/Avalonia.Controls/DragDrop/DragSource.cs index f899eabfbd0..2048a0c0da5 100644 --- a/src/Avalonia.Controls/DragDrop/DragSource.cs +++ b/src/Avalonia.Controls/DragDrop/DragSource.cs @@ -23,8 +23,9 @@ class DragSource : IPlatformDragSource private readonly Subject _result = new Subject(); private IDataObject _draggedData; - private IInputRoot _lastRoot; + private IInputElement _lastRoot; private InputModifiers? _initialInputModifiers; + private object _lastCursor; public DragSource() { @@ -54,9 +55,48 @@ private DragDropEffects RaiseDragEvent(RawDragEventType type, IInputElement root RawDragEvent rawEvent = new RawDragEvent(_dragDrop, type, root, pt, _draggedData, allowedEffects); var tl = root.GetSelfAndVisualAncestors().OfType().FirstOrDefault(); tl.PlatformImpl.Input(rawEvent); + + SetCursor(root, rawEvent.Effects); return rawEvent.Effects; } + private Cursor GetCursorForDropEffect(DragDropEffects effects) + { + // Todo. Needs to choose cursor by effect. + if (effects == DragDropEffects.None) + return new Cursor(StandardCursorType.No); + return new Cursor(StandardCursorType.Hand); + } + + private void SetCursor(IInputElement root, DragDropEffects effect) + { + if (_lastRoot != root) + { + if (_lastRoot is InputElement ieLast) + { + if (_lastCursor == AvaloniaProperty.UnsetValue) + ieLast.ClearValue(InputElement.CursorProperty); + else + ieLast.Cursor = _lastCursor as Cursor; + } + + if (root is InputElement ieNew) + { + if (!ieNew.IsSet(InputElement.CursorProperty)) + _lastCursor = AvaloniaProperty.UnsetValue; + else + _lastCursor = root.Cursor; + } + else + _lastCursor = null; + + _lastRoot = root; + } + + if (root is InputElement ie) + ie.Cursor = GetCursorForDropEffect(effect); + } + private void ProcessMouseEvents(RawMouseEventArgs e, DragDropEffects allowedEffects) { if (!_initialInputModifiers.HasValue) @@ -66,16 +106,18 @@ void CancelDragging() { if (_lastRoot != null) RaiseDragEvent(RawDragEventType.DragLeave, _lastRoot, _lastRoot.PointToClient(e.Root.PointToScreen(e.Position)), allowedEffects); + SetCursor(null, DragDropEffects.None); _result.OnNext(DragDropEffects.None); e.Handled = true; } void AcceptDragging() { var result = RaiseDragEvent(RawDragEventType.Drop, e.Root, e.Position, allowedEffects) & allowedEffects; + SetCursor(null, DragDropEffects.None); _result.OnNext(result); e.Handled = true; } - + switch (e.Type) { case RawMouseEventType.LeftButtonDown: @@ -118,7 +160,6 @@ void AcceptDragging() if (_lastRoot != null) RaiseDragEvent(RawDragEventType.DragLeave, _lastRoot, _lastRoot.PointToClient(e.Root.PointToScreen(e.Position)), allowedEffects); RaiseDragEvent(RawDragEventType.DragEnter, e.Root, e.Position, allowedEffects); - _lastRoot = e.Root; } else RaiseDragEvent(RawDragEventType.DragOver, e.Root, e.Position, allowedEffects); From 74163ff668d744a1c3b06917cdeef63977df89fd Mon Sep 17 00:00:00 2001 From: boombuler Date: Thu, 8 Mar 2018 15:03:51 +0100 Subject: [PATCH 19/59] Added D+D StandardCursors --- src/Avalonia.Input/Cursors.cs | 5 +++- src/Gtk/Avalonia.Gtk3/CursorFactory.cs | 5 +++- src/OSX/Avalonia.MonoMac/Cursor.cs | 4 +++ src/Windows/Avalonia.Win32/CursorFactory.cs | 27 +++++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Input/Cursors.cs b/src/Avalonia.Input/Cursors.cs index e3860e58e5b..02a026c9980 100644 --- a/src/Avalonia.Input/Cursors.cs +++ b/src/Avalonia.Input/Cursors.cs @@ -38,7 +38,10 @@ public enum StandardCursorType TopLeftCorner, TopRightCorner, BottomLeftCorner, - BottomRightCorner + BottomRightCorner, + DragMove, + DragCopy, + DragLink, // Not available in GTK directly, see http://www.pixelbeat.org/programming/x_cursors/ // We might enable them later, preferably, by loading pixmax direclty from theme with fallback image diff --git a/src/Gtk/Avalonia.Gtk3/CursorFactory.cs b/src/Gtk/Avalonia.Gtk3/CursorFactory.cs index ac547b8bc23..d6a3c1f2604 100644 --- a/src/Gtk/Avalonia.Gtk3/CursorFactory.cs +++ b/src/Gtk/Avalonia.Gtk3/CursorFactory.cs @@ -32,7 +32,10 @@ class CursorFactory : IStandardCursorFactory {StandardCursorType.TopLeftCorner, CursorType.TopLeftCorner}, {StandardCursorType.TopRightCorner, CursorType.TopRightCorner}, {StandardCursorType.BottomLeftCorner, CursorType.BottomLeftCorner}, - {StandardCursorType.BottomRightCorner, CursorType.BottomRightCorner} + {StandardCursorType.BottomRightCorner, CursorType.BottomRightCorner}, + {StandardCursorType.DragCopy, CursorType.CenterPtr}, + {StandardCursorType.DragMove, CursorType.Fleur}, + {StandardCursorType.DragLink, CursorType.Cross}, }; private static readonly Dictionary Cache = diff --git a/src/OSX/Avalonia.MonoMac/Cursor.cs b/src/OSX/Avalonia.MonoMac/Cursor.cs index 10445e62e27..d9370e527bb 100644 --- a/src/OSX/Avalonia.MonoMac/Cursor.cs +++ b/src/OSX/Avalonia.MonoMac/Cursor.cs @@ -51,6 +51,10 @@ public CursorFactoryStub() [StandardCursorType.TopSide] = NSCursor.ResizeUpCursor, [StandardCursorType.UpArrow] = NSCursor.ResizeUpCursor, [StandardCursorType.Wait] = NSCursor.ArrowCursor, //TODO + [StandardCursorType.DragMove] = NSCursor.DragCopyCursor, // TODO + [StandardCursorType.DragCopy] = NSCursor.DragCopyCursor, + [StandardCursorType.DragLink] = NSCursor.DragLinkCursor, + }; } diff --git a/src/Windows/Avalonia.Win32/CursorFactory.cs b/src/Windows/Avalonia.Win32/CursorFactory.cs index 0d529d6b91d..fa2fbe48109 100644 --- a/src/Windows/Avalonia.Win32/CursorFactory.cs +++ b/src/Windows/Avalonia.Win32/CursorFactory.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Avalonia.Input; using Avalonia.Platform; +using System.Runtime.InteropServices; namespace Avalonia.Win32 { @@ -20,6 +21,27 @@ private CursorFactory() { } + static CursorFactory() + { + LoadModuleCursor(StandardCursorType.DragMove, "ole32.dll", 2); + LoadModuleCursor(StandardCursorType.DragCopy, "ole32.dll", 3); + LoadModuleCursor(StandardCursorType.DragLink, "ole32.dll", 4); + } + + private static void LoadModuleCursor(StandardCursorType cursorType, string module, int id) + { + IntPtr mh = UnmanagedMethods.GetModuleHandle(module); + if (mh != IntPtr.Zero) + { + IntPtr cursor = UnmanagedMethods.LoadCursor(mh, new IntPtr(id)); + if (cursor != IntPtr.Zero) + { + PlatformHandle phCursor = new PlatformHandle(cursor, PlatformConstants.CursorHandleType); + Cache.Add(cursorType, phCursor); + } + } + } + private static readonly Dictionary CursorTypeMapping = new Dictionary { @@ -47,6 +69,11 @@ private CursorFactory() //Using SizeNorthEastSouthWest {StandardCursorType.TopRightCorner, 32643}, {StandardCursorType.BottomLeftCorner, 32643}, + + // Fallback, should have been loaded from ole32.dll + {StandardCursorType.DragMove, 32516}, + {StandardCursorType.DragCopy, 32516}, + {StandardCursorType.DragLink, 32516}, }; private static readonly Dictionary Cache = From 97f581af496fc8172b8e1863bb6fae89e8f544dc Mon Sep 17 00:00:00 2001 From: boombuler Date: Thu, 8 Mar 2018 15:05:13 +0100 Subject: [PATCH 20/59] Improved DragSource the drag source now gets the preferred drag effect and handles keyboard inputs too --- src/Avalonia.Controls/DragDrop/DragSource.cs | 129 ++++++++++++------- 1 file changed, 81 insertions(+), 48 deletions(-) diff --git a/src/Avalonia.Controls/DragDrop/DragSource.cs b/src/Avalonia.Controls/DragDrop/DragSource.cs index 2048a0c0da5..fd3d8ef143b 100644 --- a/src/Avalonia.Controls/DragDrop/DragSource.cs +++ b/src/Avalonia.Controls/DragDrop/DragSource.cs @@ -19,13 +19,14 @@ class DragSource : IPlatformDragSource private const InputModifiers MOUSE_INPUTMODIFIERS = InputModifiers.LeftMouseButton|InputModifiers.MiddleMouseButton|InputModifiers.RightMouseButton; private readonly IDragDropDevice _dragDrop; private readonly IInputManager _inputManager; - - private readonly Subject _result = new Subject(); + + private DragDropEffects _allowedEffects; private IDataObject _draggedData; private IInputElement _lastRoot; - private InputModifiers? _initialInputModifiers; + private Point _lastPosition; private object _lastCursor; + private InputModifiers? _initialInputModifiers; public DragSource() { @@ -40,35 +41,58 @@ public async Task DoDragDrop(IDataObject data, DragDropEffects { _draggedData = data; _lastRoot = null; + _lastPosition = default(Point); + _allowedEffects = allowedEffects; - using (_inputManager.PreProcess.OfType().Subscribe(e => ProcessMouseEvents(e, allowedEffects))) + using (_inputManager.PreProcess.OfType().Subscribe(ProcessMouseEvents)) { - var effect = await _result.FirstAsync(); - return effect; + using (_inputManager.PreProcess.OfType().Subscribe(ProcessKeyEvents)) + { + var effect = await _result.FirstAsync(); + return effect; + } } } return DragDropEffects.None; } - private DragDropEffects RaiseDragEvent(RawDragEventType type, IInputElement root, Point pt, DragDropEffects allowedEffects) + + private DragDropEffects RaiseEventAndUpdateCursor(RawDragEventType type, IInputElement root, Point pt, InputModifiers modifiers) { - RawDragEvent rawEvent = new RawDragEvent(_dragDrop, type, root, pt, _draggedData, allowedEffects); + _lastPosition = pt; + + RawDragEvent rawEvent = new RawDragEvent(_dragDrop, type, root, pt, _draggedData, _allowedEffects); var tl = root.GetSelfAndVisualAncestors().OfType().FirstOrDefault(); tl.PlatformImpl.Input(rawEvent); - - SetCursor(root, rawEvent.Effects); - return rawEvent.Effects; + + var effect = GetPreferredEffect(rawEvent.Effects & _allowedEffects, modifiers); + UpdateCursor(root, effect); + return effect; + } + + private DragDropEffects GetPreferredEffect(DragDropEffects effect, InputModifiers modifiers) + { + if (effect == DragDropEffects.Copy || effect == DragDropEffects.Move || effect == DragDropEffects.Link || effect == DragDropEffects.None) + return effect; // No need to check for the modifiers. + if (effect.HasFlag(DragDropEffects.Link) && modifiers.HasFlag(InputModifiers.Alt)) + return DragDropEffects.Link; + if (effect.HasFlag(DragDropEffects.Copy) && modifiers.HasFlag(InputModifiers.Control)) + return DragDropEffects.Copy; + return DragDropEffects.Move; } private Cursor GetCursorForDropEffect(DragDropEffects effects) { - // Todo. Needs to choose cursor by effect. - if (effects == DragDropEffects.None) - return new Cursor(StandardCursorType.No); - return new Cursor(StandardCursorType.Hand); + if (effects.HasFlag(DragDropEffects.Copy)) + return new Cursor(StandardCursorType.DragCopy); + if (effects.HasFlag(DragDropEffects.Move)) + return new Cursor(StandardCursorType.DragMove); + if (effects.HasFlag(DragDropEffects.Link)) + return new Cursor(StandardCursorType.DragLink); + return new Cursor(StandardCursorType.No); } - private void SetCursor(IInputElement root, DragDropEffects effect) + private void UpdateCursor(IInputElement root, DragDropEffects effect) { if (_lastRoot != root) { @@ -96,25 +120,45 @@ private void SetCursor(IInputElement root, DragDropEffects effect) if (root is InputElement ie) ie.Cursor = GetCursorForDropEffect(effect); } - - private void ProcessMouseEvents(RawMouseEventArgs e, DragDropEffects allowedEffects) + + private void CancelDragging() { - if (!_initialInputModifiers.HasValue) - _initialInputModifiers = e.InputModifiers & MOUSE_INPUTMODIFIERS; + if (_lastRoot != null) + RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, _lastRoot, _lastPosition, InputModifiers.None); + UpdateCursor(null, DragDropEffects.None); + _result.OnNext(DragDropEffects.None); + } - void CancelDragging() + private void ProcessKeyEvents(RawKeyEventArgs e) + { + if (e.Type == RawKeyEventType.KeyDown && e.Key == Key.Escape) { if (_lastRoot != null) - RaiseDragEvent(RawDragEventType.DragLeave, _lastRoot, _lastRoot.PointToClient(e.Root.PointToScreen(e.Position)), allowedEffects); - SetCursor(null, DragDropEffects.None); + RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, _lastRoot, _lastPosition, e.Modifiers); + UpdateCursor(null, DragDropEffects.None); _result.OnNext(DragDropEffects.None); e.Handled = true; } - void AcceptDragging() + else if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl || e.Key == Key.LeftAlt || e.Key == Key.RightAlt) + RaiseEventAndUpdateCursor(RawDragEventType.DragOver, _lastRoot, _lastPosition, e.Modifiers); + } + + private void ProcessMouseEvents(RawMouseEventArgs e) + { + if (!_initialInputModifiers.HasValue) + _initialInputModifiers = e.InputModifiers & MOUSE_INPUTMODIFIERS; + + + void CheckDraggingAccepted(InputModifiers changedMouseButton) { - var result = RaiseDragEvent(RawDragEventType.Drop, e.Root, e.Position, allowedEffects) & allowedEffects; - SetCursor(null, DragDropEffects.None); - _result.OnNext(result); + if (_initialInputModifiers.Value.HasFlag(changedMouseButton)) + { + var result = RaiseEventAndUpdateCursor(RawDragEventType.Drop, e.Root, e.Position, e.InputModifiers); + UpdateCursor(null, DragDropEffects.None); + _result.OnNext(result); + } + else + CancelDragging(); e.Handled = true; } @@ -125,45 +169,34 @@ void AcceptDragging() case RawMouseEventType.MiddleButtonDown: case RawMouseEventType.NonClientLeftButtonDown: CancelDragging(); + e.Handled = true; return; case RawMouseEventType.LeaveWindow: - RaiseDragEvent(RawDragEventType.DragLeave, e.Root, e.Position, allowedEffects); - break; + RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, e.Root, e.Position, e.InputModifiers); break; case RawMouseEventType.LeftButtonUp: - if (_initialInputModifiers.Value.HasFlag(InputModifiers.LeftMouseButton)) - AcceptDragging(); - else - CancelDragging(); - return; + CheckDraggingAccepted(InputModifiers.LeftMouseButton); break; case RawMouseEventType.MiddleButtonUp: - if (_initialInputModifiers.Value.HasFlag(InputModifiers.MiddleMouseButton)) - AcceptDragging(); - else - CancelDragging(); - return; + CheckDraggingAccepted(InputModifiers.MiddleMouseButton); break; case RawMouseEventType.RightButtonUp: - if (_initialInputModifiers.Value.HasFlag(InputModifiers.RightMouseButton)) - AcceptDragging(); - else - CancelDragging(); - return; + CheckDraggingAccepted(InputModifiers.RightMouseButton); break; case RawMouseEventType.Move: var mods = e.InputModifiers & MOUSE_INPUTMODIFIERS; if (_initialInputModifiers.Value != mods) { CancelDragging(); + e.Handled = true; return; } if (e.Root != _lastRoot) { if (_lastRoot != null) - RaiseDragEvent(RawDragEventType.DragLeave, _lastRoot, _lastRoot.PointToClient(e.Root.PointToScreen(e.Position)), allowedEffects); - RaiseDragEvent(RawDragEventType.DragEnter, e.Root, e.Position, allowedEffects); + RaiseEventAndUpdateCursor(RawDragEventType.DragLeave, _lastRoot, _lastRoot.PointToClient(e.Root.PointToScreen(e.Position)), e.InputModifiers); + RaiseEventAndUpdateCursor(RawDragEventType.DragEnter, e.Root, e.Position, e.InputModifiers); } else - RaiseDragEvent(RawDragEventType.DragOver, e.Root, e.Position, allowedEffects); - return; + RaiseEventAndUpdateCursor(RawDragEventType.DragOver, e.Root, e.Position, e.InputModifiers); + break; } } } From ed4a85fef992f16e5c84554c2a2c8caee8c8ffcf Mon Sep 17 00:00:00 2001 From: boombuler Date: Fri, 9 Mar 2018 16:41:41 +0100 Subject: [PATCH 21/59] Allow Winforms + WPF compatible object drag --- src/Windows/Avalonia.Win32/DataObject.cs | 51 ++++++++++++++++++++- src/Windows/Avalonia.Win32/OleDataObject.cs | 26 ++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/DataObject.cs b/src/Windows/Avalonia.Win32/DataObject.cs index 6248afd7bb5..aa3ee91ef95 100644 --- a/src/Windows/Avalonia.Win32/DataObject.cs +++ b/src/Windows/Avalonia.Win32/DataObject.cs @@ -8,11 +8,16 @@ using Avalonia.Win32.Interop; using IDataObject = Avalonia.Controls.DragDrop.IDataObject; using IOleDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; namespace Avalonia.Win32 { class DataObject : IDataObject, IOleDataObject { + // Compatibility with WinForms + WPF... + internal static readonly byte[] SerializedObjectGUID = new Guid("FD9EA796-3B13-4370-A679-56106BB288FB").ToByteArray(); + class FormatEnumerator : IEnumFORMATETC { private FORMATETC[] _formats; @@ -249,7 +254,51 @@ private int WriteDataToHGlobal(string dataFormat, ref IntPtr hGlobal) return WriteStringToHGlobal(ref hGlobal, Convert.ToString(data)); if (dataFormat == DataFormats.FileNames && data is IEnumerable files) return WriteFileListToHGlobal(ref hGlobal, files); - return DV_E_TYMED; + if (data is Stream stream) + { + byte[] buffer = new byte[stream.Length - stream.Position]; + stream.Read(buffer, 0, buffer.Length); + return WriteBytesToHGlobal(ref hGlobal, buffer); + } + if (data is IEnumerable bytes) + { + var byteArr = bytes is byte[] ? (byte[])bytes : bytes.ToArray(); + return WriteBytesToHGlobal(ref hGlobal, byteArr); + } + return WriteBytesToHGlobal(ref hGlobal, SerializeObject(data)); + } + + private byte[] SerializeObject(object data) + { + using (var ms = new MemoryStream()) + { + ms.Write(SerializedObjectGUID, 0, SerializedObjectGUID.Length); + BinaryFormatter binaryFormatter = new BinaryFormatter(); + binaryFormatter.Serialize(ms, data); + return ms.ToArray(); + } + } + + private int WriteBytesToHGlobal(ref IntPtr hGlobal, byte[] data) + { + int required = data.Length; + if (hGlobal == IntPtr.Zero) + hGlobal = UnmanagedMethods.GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, required); + + long available = UnmanagedMethods.GlobalSize(hGlobal).ToInt64(); + if (required > available) + return STG_E_MEDIUMFULL; + + IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal); + try + { + Marshal.Copy(data, 0, ptr, data.Length); + return unchecked((int)UnmanagedMethods.HRESULT.S_OK); + } + finally + { + UnmanagedMethods.GlobalUnlock(hGlobal); + } } private int WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable files) diff --git a/src/Windows/Avalonia.Win32/OleDataObject.cs b/src/Windows/Avalonia.Win32/OleDataObject.cs index fe6faa162cd..60527bb7d65 100644 --- a/src/Windows/Avalonia.Win32/OleDataObject.cs +++ b/src/Windows/Avalonia.Win32/OleDataObject.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; +using System.Runtime.Serialization.Formatters.Binary; using System.Text; using Avalonia.Controls.DragDrop; using Avalonia.Win32.Interop; @@ -62,7 +64,19 @@ private object GetDataFromOleHGLOBAL(string format, DVASPECT aspect) return ReadStringFromHGlobal(medium.unionmember); if (format == DataFormats.FileNames) return ReadFileNamesFromHGlobal(medium.unionmember); - return ReadBytesFromHGlobal(medium.unionmember); + + byte[] data = ReadBytesFromHGlobal(medium.unionmember); + + if (IsSerializedObject(data)) + { + using (var ms = new MemoryStream(data)) + { + ms.Position = DataObject.SerializedObjectGUID.Length; + BinaryFormatter binaryFormatter = new BinaryFormatter(); + return binaryFormatter.Deserialize(ms); + } + } + return data; } } finally @@ -73,6 +87,16 @@ private object GetDataFromOleHGLOBAL(string format, DVASPECT aspect) return null; } + private bool IsSerializedObject(byte[] data) + { + if (data.Length < DataObject.SerializedObjectGUID.Length) + return false; + for (int i = 0; i < DataObject.SerializedObjectGUID.Length; i++) + if (data[i] != DataObject.SerializedObjectGUID[i]) + return false; + return true; + } + private static IEnumerable ReadFileNamesFromHGlobal(IntPtr hGlobal) { List files = new List(); From cfb3924c0fa645c6256b32af93a8000a3a936b92 Mon Sep 17 00:00:00 2001 From: boombuler Date: Fri, 9 Mar 2018 16:42:06 +0100 Subject: [PATCH 22/59] Prevent setting the cursor to often. --- src/Avalonia.Controls/DragDrop/DragSource.cs | 33 +++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Controls/DragDrop/DragSource.cs b/src/Avalonia.Controls/DragDrop/DragSource.cs index fd3d8ef143b..ceb962ad986 100644 --- a/src/Avalonia.Controls/DragDrop/DragSource.cs +++ b/src/Avalonia.Controls/DragDrop/DragSource.cs @@ -25,7 +25,8 @@ class DragSource : IPlatformDragSource private IDataObject _draggedData; private IInputElement _lastRoot; private Point _lastPosition; - private object _lastCursor; + private StandardCursorType _lastCursorType; + private object _originalCursor; private InputModifiers? _initialInputModifiers; public DragSource() @@ -81,15 +82,15 @@ private DragDropEffects GetPreferredEffect(DragDropEffects effect, InputModifier return DragDropEffects.Move; } - private Cursor GetCursorForDropEffect(DragDropEffects effects) + private StandardCursorType GetCursorForDropEffect(DragDropEffects effects) { if (effects.HasFlag(DragDropEffects.Copy)) - return new Cursor(StandardCursorType.DragCopy); + return StandardCursorType.DragCopy; if (effects.HasFlag(DragDropEffects.Move)) - return new Cursor(StandardCursorType.DragMove); + return StandardCursorType.DragMove; if (effects.HasFlag(DragDropEffects.Link)) - return new Cursor(StandardCursorType.DragLink); - return new Cursor(StandardCursorType.No); + return StandardCursorType.DragLink; + return StandardCursorType.No; } private void UpdateCursor(IInputElement root, DragDropEffects effect) @@ -98,27 +99,35 @@ private void UpdateCursor(IInputElement root, DragDropEffects effect) { if (_lastRoot is InputElement ieLast) { - if (_lastCursor == AvaloniaProperty.UnsetValue) + if (_originalCursor == AvaloniaProperty.UnsetValue) ieLast.ClearValue(InputElement.CursorProperty); else - ieLast.Cursor = _lastCursor as Cursor; + ieLast.Cursor = _originalCursor as Cursor; } if (root is InputElement ieNew) { if (!ieNew.IsSet(InputElement.CursorProperty)) - _lastCursor = AvaloniaProperty.UnsetValue; + _originalCursor = AvaloniaProperty.UnsetValue; else - _lastCursor = root.Cursor; + _originalCursor = root.Cursor; } else - _lastCursor = null; + _originalCursor = null; + _lastCursorType = StandardCursorType.Arrow; _lastRoot = root; } if (root is InputElement ie) - ie.Cursor = GetCursorForDropEffect(effect); + { + var ct = GetCursorForDropEffect(effect); + if (ct != _lastCursorType) + { + _lastCursorType = ct; + ie.Cursor = new Cursor(ct); + } + } } private void CancelDragging() From c7fd7183bd13ab9aca6533909273a4ae2ac85c5b Mon Sep 17 00:00:00 2001 From: boombuler Date: Sun, 11 Mar 2018 09:56:52 +0100 Subject: [PATCH 23/59] basic dragging support for OSX --- src/OSX/Avalonia.MonoMac/DragSource.cs | 81 +++++++++++++++++++++ src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs | 3 +- 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/OSX/Avalonia.MonoMac/DragSource.cs diff --git a/src/OSX/Avalonia.MonoMac/DragSource.cs b/src/OSX/Avalonia.MonoMac/DragSource.cs new file mode 100644 index 00000000000..ea1739e33a1 --- /dev/null +++ b/src/OSX/Avalonia.MonoMac/DragSource.cs @@ -0,0 +1,81 @@ +using System.Data; +using System.Linq.Expressions; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.DragDrop; +using Avalonia.Controls.Platform; +using Avalonia.Input; +using Avalonia.Input.Raw; +using MonoMac.AppKit; +using MonoMac.CoreGraphics; +using MonoMac.Foundation; +using MonoMac.OpenGL; + +namespace Avalonia.MonoMac +{ + public class DragSource : NSDraggingSource, IPlatformDragSource + { + class DS : NSDraggingSource + { + private readonly DragDropEffects _allowedEffects; + public ReplaySubject Result { get; } = new ReplaySubject(); + + public DS(DragDropEffects allowedEffects) + { + _allowedEffects = allowedEffects; + } + + public override bool IgnoreModifierKeysWhileDragging => false; + + public override NSDragOperation DraggingSourceOperationMaskForLocal(bool flag) + { + return DraggingInfo.ConvertDragOperation(_allowedEffects); + } + + public override void DraggedImageEndedAtOperation(NSImage image, CGPoint screenPoint, NSDragOperation operation) + { + Result.OnNext(DraggingInfo.ConvertDragOperation(operation)); + Result.OnCompleted(); + } + } + + + + private readonly IInputManager _inputManager; + + + public DragSource() + { + _inputManager = AvaloniaLocator.Current.GetService(); + } + + private NSDraggingItem[] ConvertDraggedItems(IDataObject data) + { + NSString s = new NSString("FOOBAR"); + NSPasteboardWriting w = new NSPasteboardWriting(s.Handle); + var text = new NSDraggingItem(w); + return new[] {text}; + } + + + public async Task DoDragDrop(IDataObject data, DragDropEffects allowedEffects) + { + // We need the TopLevelImpl + a mouse location so we just wait for the next event. + var mouseEv = await _inputManager.PreProcess.OfType().FirstAsync(); + var view = ((mouseEv.Root as TopLevel)?.PlatformImpl as TopLevelImpl)?.View; + if (view == null) + return DragDropEffects.None; + + // Prepare the source event: + var pt = view.TranslateLocalPoint(mouseEv.Position).ToMonoMacPoint(); + var ev = NSEvent.MouseEvent(NSEventType.LeftMouseDown, pt, 0, 0, 0, null, 0, 0, 0); + + var ds = new DS(allowedEffects); + view.BeginDraggingSession(ConvertDraggedItems(data) ,ev, ds); + + return await ds.Result; + } + } +} \ No newline at end of file diff --git a/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs index 59074594590..3db02de209c 100644 --- a/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs +++ b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs @@ -35,7 +35,8 @@ void DoInitialize() .Bind().ToSingleton() .Bind().ToSingleton() .Bind().ToConstant(s_renderLoop) - .Bind().ToConstant(PlatformThreadingInterface.Instance); + .Bind().ToConstant(PlatformThreadingInterface.Instance) + .Bind().ToTransient(); } public static void Initialize() From 1afb52b27d08ac10412f4945b09196f4a399c25a Mon Sep 17 00:00:00 2001 From: boombuler Date: Sun, 11 Mar 2018 16:18:39 +0100 Subject: [PATCH 24/59] OSX drag cleanup --- src/OSX/Avalonia.MonoMac/DragSource.cs | 96 ++++++++++++++++---------- 1 file changed, 61 insertions(+), 35 deletions(-) diff --git a/src/OSX/Avalonia.MonoMac/DragSource.cs b/src/OSX/Avalonia.MonoMac/DragSource.cs index ea1739e33a1..77bbee86f1d 100644 --- a/src/OSX/Avalonia.MonoMac/DragSource.cs +++ b/src/OSX/Avalonia.MonoMac/DragSource.cs @@ -1,13 +1,19 @@ -using System.Data; +using System; +using System.Data; +using System.IO; +using System.Linq; using System.Linq.Expressions; using System.Reactive.Linq; using System.Reactive.Subjects; +using System.Runtime.InteropServices; +using System.Runtime.Serialization.Formatters.Binary; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.DragDrop; using Avalonia.Controls.Platform; using Avalonia.Input; using Avalonia.Input.Raw; +using MonoMac; using MonoMac.AppKit; using MonoMac.CoreGraphics; using MonoMac.Foundation; @@ -17,46 +23,54 @@ namespace Avalonia.MonoMac { public class DragSource : NSDraggingSource, IPlatformDragSource { - class DS : NSDraggingSource - { - private readonly DragDropEffects _allowedEffects; - public ReplaySubject Result { get; } = new ReplaySubject(); - - public DS(DragDropEffects allowedEffects) - { - _allowedEffects = allowedEffects; - } - - public override bool IgnoreModifierKeysWhileDragging => false; - - public override NSDragOperation DraggingSourceOperationMaskForLocal(bool flag) - { - return DraggingInfo.ConvertDragOperation(_allowedEffects); - } - - public override void DraggedImageEndedAtOperation(NSImage image, CGPoint screenPoint, NSDragOperation operation) - { - Result.OnNext(DraggingInfo.ConvertDragOperation(operation)); - Result.OnCompleted(); - } - } - - + private const string NSPasteboardTypeString = "public.utf8-plain-text"; + private readonly Subject _result = new Subject(); private readonly IInputManager _inputManager; + private DragDropEffects _allowedEffects; - + public override bool IgnoreModifierKeysWhileDragging => false; + public DragSource() { _inputManager = AvaloniaLocator.Current.GetService(); } - private NSDraggingItem[] ConvertDraggedItems(IDataObject data) + private string DataFormatToUTI(string s) + { + if (s == DataFormats.FileNames) + throw new NotImplementedException(); + if (s == DataFormats.Text) + return NSPasteboardTypeString; + return s; + } + + private NSDraggingItem CreateDraggingItem(string format, object data) { - NSString s = new NSString("FOOBAR"); - NSPasteboardWriting w = new NSPasteboardWriting(s.Handle); - var text = new NSDraggingItem(w); - return new[] {text}; + + var pasteboardItem = new NSPasteboardItem(); + NSData nsData; + if(data is string s) + nsData = NSData.FromString(s); + else if (data is Stream strm) + nsData = NSData.FromStream(strm); + else if (data is byte[] bytes) + nsData = NSData.FromArray(bytes); + else + { + BinaryFormatter bf = new BinaryFormatter(); + using (var ms = new MemoryStream()) + { + bf.Serialize(ms, data); + ms.Position = 0; + nsData = NSData.FromStream(ms); + } + } + pasteboardItem.SetDataForType(nsData, DataFormatToUTI(format)); + + NSPasteboardWriting writing = new NSPasteboardWriting(pasteboardItem.Handle); + + return new NSDraggingItem(writing); } @@ -72,10 +86,22 @@ public async Task DoDragDrop(IDataObject data, DragDropEffects var pt = view.TranslateLocalPoint(mouseEv.Position).ToMonoMacPoint(); var ev = NSEvent.MouseEvent(NSEventType.LeftMouseDown, pt, 0, 0, 0, null, 0, 0, 0); - var ds = new DS(allowedEffects); - view.BeginDraggingSession(ConvertDraggedItems(data) ,ev, ds); + _allowedEffects = allowedEffects; + var items = data.GetDataFormats().Select(fmt => CreateDraggingItem(fmt, data.Get(fmt))).ToArray(); + view.BeginDraggingSession(items ,ev, this); - return await ds.Result; + return await _result; + } + + public override NSDragOperation DraggingSourceOperationMaskForLocal(bool flag) + { + return DraggingInfo.ConvertDragOperation(_allowedEffects); + } + + public override void DraggedImageEndedAtOperation(NSImage image, CGPoint screenPoint, NSDragOperation operation) + { + _result.OnNext(DraggingInfo.ConvertDragOperation(operation)); + _result.OnCompleted(); } } } \ No newline at end of file From 772ce18ca487d4949ef9bd0e80c2e25b7dab47b3 Mon Sep 17 00:00:00 2001 From: boombuler Date: Sun, 11 Mar 2018 18:23:41 +0100 Subject: [PATCH 25/59] drag file urls to finder. --- src/OSX/Avalonia.MonoMac/DragSource.cs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/OSX/Avalonia.MonoMac/DragSource.cs b/src/OSX/Avalonia.MonoMac/DragSource.cs index 77bbee86f1d..2275c7505f6 100644 --- a/src/OSX/Avalonia.MonoMac/DragSource.cs +++ b/src/OSX/Avalonia.MonoMac/DragSource.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; @@ -24,6 +25,7 @@ namespace Avalonia.MonoMac public class DragSource : NSDraggingSource, IPlatformDragSource { private const string NSPasteboardTypeString = "public.utf8-plain-text"; + private const string NSPasteboardTypeFileUrl = "public.file-url"; private readonly Subject _result = new Subject(); private readonly IInputManager _inputManager; @@ -39,7 +41,7 @@ public DragSource() private string DataFormatToUTI(string s) { if (s == DataFormats.FileNames) - throw new NotImplementedException(); + return NSPasteboardTypeFileUrl; if (s == DataFormats.Text) return NSPasteboardTypeString; return s; @@ -47,7 +49,6 @@ private string DataFormatToUTI(string s) private NSDraggingItem CreateDraggingItem(string format, object data) { - var pasteboardItem = new NSPasteboardItem(); NSData nsData; if(data is string s) @@ -72,6 +73,19 @@ private NSDraggingItem CreateDraggingItem(string format, object data) return new NSDraggingItem(writing); } + + public IEnumerable CreateDraggingItems(string format, object data) + { + if (format == DataFormats.FileNames && data is IEnumerable files) + { + foreach (var file in files) + yield return CreateDraggingItem(format, file); + + yield break; + } + + yield return CreateDraggingItem(format, data); + } public async Task DoDragDrop(IDataObject data, DragDropEffects allowedEffects) @@ -87,7 +101,7 @@ public async Task DoDragDrop(IDataObject data, DragDropEffects var ev = NSEvent.MouseEvent(NSEventType.LeftMouseDown, pt, 0, 0, 0, null, 0, 0, 0); _allowedEffects = allowedEffects; - var items = data.GetDataFormats().Select(fmt => CreateDraggingItem(fmt, data.Get(fmt))).ToArray(); + var items = data.GetDataFormats().SelectMany(fmt => CreateDraggingItems(fmt, data.Get(fmt))).ToArray(); view.BeginDraggingSession(items ,ev, this); return await _result; From 52422afe8bce50558ff8a8a2d7397bfa44b9f5e2 Mon Sep 17 00:00:00 2001 From: boombuler Date: Sun, 11 Mar 2018 18:28:00 +0100 Subject: [PATCH 26/59] automatically ensure dragged filenames are uris --- src/OSX/Avalonia.MonoMac/DragSource.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/OSX/Avalonia.MonoMac/DragSource.cs b/src/OSX/Avalonia.MonoMac/DragSource.cs index 2275c7505f6..07f2e186aa5 100644 --- a/src/OSX/Avalonia.MonoMac/DragSource.cs +++ b/src/OSX/Avalonia.MonoMac/DragSource.cs @@ -51,8 +51,12 @@ private NSDraggingItem CreateDraggingItem(string format, object data) { var pasteboardItem = new NSPasteboardItem(); NSData nsData; - if(data is string s) + if (data is string s) + { + if (format == DataFormats.FileNames) + s = new Uri(s).AbsoluteUri; // Ensure file uris... nsData = NSData.FromString(s); + } else if (data is Stream strm) nsData = NSData.FromStream(strm); else if (data is byte[] bytes) From 6b231ba3359c1a60f68ae77f310fdc9d650b1190 Mon Sep 17 00:00:00 2001 From: lindexi Date: Wed, 4 Apr 2018 15:52:48 +0800 Subject: [PATCH 27/59] fix comment --- src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs index c76a5b5da59..b2b5ecdf8fd 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -103,7 +103,7 @@ public static Guid ToWic(this Platform.PixelFormat format) /// Converts a pen to a Direct2D stroke style. /// /// The pen to convert. - /// The render target. + /// The render target. /// The Direct2D brush. public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.Pen pen, SharpDX.Direct2D1.RenderTarget renderTarget) { @@ -114,7 +114,7 @@ public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.Pen pen, Sha /// Converts a pen to a Direct2D stroke style. /// /// The pen to convert. - /// The render target. + /// The factory associated with this resource. /// The Direct2D brush. public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.Pen pen, Factory factory) { From 3e43fc221bf2056520f941cd8995c554551b9466 Mon Sep 17 00:00:00 2001 From: lindexi Date: Wed, 4 Apr 2018 15:54:32 +0800 Subject: [PATCH 28/59] for this code in `pen.DashStyle?.Dashes != null` that pen.DashStyle isnt null and we dont need add `?` --- src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs index b2b5ecdf8fd..959bf118f04 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -132,7 +132,7 @@ public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.Pen pen, Fac { properties.DashStyle = DashStyle.Custom; properties.DashOffset = (float)pen.DashStyle.Offset; - dashes = pen.DashStyle?.Dashes.Select(x => (float)x).ToArray(); + dashes = pen.DashStyle.Dashes.Select(x => (float)x).ToArray(); } return new StrokeStyle(factory, properties, dashes); } From c064d05e8c64b871f0d657ac628041db9696e9cf Mon Sep 17 00:00:00 2001 From: lindexi Date: Wed, 4 Apr 2018 16:03:00 +0800 Subject: [PATCH 29/59] Lasy new the dashes for if we new an array first that we may waste it when we use `dashes = pen.DashStyle.Dashes.Select(x => (float)x).ToArray()`. As a frequently used code I think this optimize is necessary. --- .../Avalonia.Direct2D1/PrimitiveExtensions.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs index 959bf118f04..56d0624f52b 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -127,13 +127,21 @@ public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.Pen pen, Fac EndCap = pen.EndLineCap.ToDirect2D(), DashCap = pen.DashCap.ToDirect2D() }; - var dashes = new float[0]; + float[] dashes = null;//If we new an array first that we may waste it when we use `dashes = pen.DashStyle.Dashes.Select(x => (float)x).ToArray()`. As a frequently used code I think this optimize is necessary. if (pen.DashStyle?.Dashes != null && pen.DashStyle.Dashes.Count > 0) { properties.DashStyle = DashStyle.Custom; - properties.DashOffset = (float)pen.DashStyle.Offset; + properties.DashOffset = (float) pen.DashStyle.Offset; + //for `pen.DashStyle?.Dashes != null` that you shouldnt add `?` in `pen.DashStyle.Dashes` dashes = pen.DashStyle.Dashes.Select(x => (float)x).ToArray(); } + + //If we don't enter the code above that it is null. We should set it a default value to solve the null exception. + if (dashes == null) + { + dashes = new float[0]; + } + return new StrokeStyle(factory, properties, dashes); } From 8713cd5c9f13e1e6e6b0b662cc9151350cb6de3b Mon Sep 17 00:00:00 2001 From: boombuler Date: Wed, 11 Apr 2018 07:34:16 +0200 Subject: [PATCH 30/59] code cleanup --- src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs b/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs index e9c1119e444..c2bc1d57125 100644 --- a/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs +++ b/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs @@ -14,7 +14,7 @@ public class RawDragEvent : RawInputEventArgs public RawDragEvent(IDragDropDevice inputDevice, RawDragEventType type, IInputElement inputRoot, Point location, IDataObject data, DragDropEffects effects) - :base(inputDevice, GetTimeStamp()) + :base(inputDevice, 0) { Type = type; InputRoot = inputRoot; @@ -22,10 +22,5 @@ public RawDragEvent(IDragDropDevice inputDevice, RawDragEventType type, Data = data; Effects = effects; } - - private static uint GetTimeStamp() - { - return (uint)0; - } } } \ No newline at end of file From 7032b9b8eb64ce7ffddbccb5f161d45429e43a86 Mon Sep 17 00:00:00 2001 From: boombuler Date: Wed, 11 Apr 2018 08:11:33 +0200 Subject: [PATCH 31/59] moved Drag+Drop sources to Avalonia.Input Also the windows specific DragSource is only used when the application runs as STA --- src/Avalonia.Controls/Application.cs | 5 +++-- .../{DragDrop => Platform}/DragSource.cs | 10 +++++----- .../DragDrop/DataFormats.cs | 2 +- .../DragDrop/DataObject.cs | 2 +- .../DragDrop/DragDrop.cs | 4 ++-- .../DragDrop/DragDropDevice.cs | 9 ++++----- .../DragDrop/DragDropEffects.cs | 2 +- .../DragDrop/DragEventArgs.cs | 2 +- .../DragDrop/IDataObject.cs | 2 +- .../DragDrop/Raw/IDragDropDevice.cs | 2 +- .../DragDrop/Raw/RawDragEvent.cs | 2 +- .../DragDrop/Raw/RawDragEventType.cs | 2 +- .../Platform/IPlatformDragSource.cs | 4 ++-- src/OSX/Avalonia.MonoMac/DragSource.cs | 4 ++-- src/OSX/Avalonia.MonoMac/DraggingInfo.cs | 2 +- src/OSX/Avalonia.MonoMac/TopLevelImpl.cs | 4 ++-- src/Windows/Avalonia.Win32/ClipboardFormats.cs | 2 +- src/Windows/Avalonia.Win32/DataObject.cs | 4 ++-- src/Windows/Avalonia.Win32/DragSource.cs | 5 ++--- src/Windows/Avalonia.Win32/OleDataObject.cs | 4 ++-- src/Windows/Avalonia.Win32/OleDropTarget.cs | 6 +++--- src/Windows/Avalonia.Win32/Win32Platform.cs | 6 ++++-- 22 files changed, 43 insertions(+), 42 deletions(-) rename src/Avalonia.Controls/{DragDrop => Platform}/DragSource.cs (98%) rename src/{Avalonia.Controls => Avalonia.Input}/DragDrop/DataFormats.cs (89%) rename src/{Avalonia.Controls => Avalonia.Input}/DragDrop/DataObject.cs (96%) rename src/{Avalonia.Controls => Avalonia.Input}/DragDrop/DragDrop.cs (96%) rename src/{Avalonia.Controls => Avalonia.Input}/DragDrop/DragDropDevice.cs (95%) rename src/{Avalonia.Controls => Avalonia.Input}/DragDrop/DragDropEffects.cs (80%) rename src/{Avalonia.Controls => Avalonia.Input}/DragDrop/DragEventArgs.cs (91%) rename src/{Avalonia.Controls => Avalonia.Input}/DragDrop/IDataObject.cs (88%) rename src/{Avalonia.Controls => Avalonia.Input}/DragDrop/Raw/IDragDropDevice.cs (69%) rename src/{Avalonia.Controls => Avalonia.Input}/DragDrop/Raw/RawDragEvent.cs (94%) rename src/{Avalonia.Controls => Avalonia.Input}/DragDrop/Raw/RawDragEventType.cs (72%) rename src/{Avalonia.Controls => Avalonia.Input}/Platform/IPlatformDragSource.cs (79%) diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 6ed797dc513..f94a85f31d1 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -12,9 +12,10 @@ using Avalonia.Styling; using Avalonia.Threading; using System.Reactive.Concurrency; -using Avalonia.Controls.DragDrop; -using Avalonia.Controls.DragDrop.Raw; +using Avalonia.Input.DragDrop.Raw; using Avalonia.Controls.Platform; +using Avalonia.Platform; +using Avalonia.Input.DragDrop; namespace Avalonia { diff --git a/src/Avalonia.Controls/DragDrop/DragSource.cs b/src/Avalonia.Controls/Platform/DragSource.cs similarity index 98% rename from src/Avalonia.Controls/DragDrop/DragSource.cs rename to src/Avalonia.Controls/Platform/DragSource.cs index ceb962ad986..ed873ec961f 100644 --- a/src/Avalonia.Controls/DragDrop/DragSource.cs +++ b/src/Avalonia.Controls/Platform/DragSource.cs @@ -2,17 +2,17 @@ using System.Linq; using System.Reactive.Linq; using System.Reactive.Subjects; -using System.Text; using System.Threading.Tasks; -using Avalonia.Controls.DragDrop.Raw; -using Avalonia.Controls.Platform; +using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Input.DragDrop; +using Avalonia.Input.DragDrop.Raw; +using Avalonia.Input.Platform; using Avalonia.Input.Raw; -using Avalonia.Interactivity; using Avalonia.Threading; using Avalonia.VisualTree; -namespace Avalonia.Controls.DragDrop +namespace Avalonia.Platform { class DragSource : IPlatformDragSource { diff --git a/src/Avalonia.Controls/DragDrop/DataFormats.cs b/src/Avalonia.Input/DragDrop/DataFormats.cs similarity index 89% rename from src/Avalonia.Controls/DragDrop/DataFormats.cs rename to src/Avalonia.Input/DragDrop/DataFormats.cs index f46651ed3bd..4115701478e 100644 --- a/src/Avalonia.Controls/DragDrop/DataFormats.cs +++ b/src/Avalonia.Input/DragDrop/DataFormats.cs @@ -1,4 +1,4 @@ -namespace Avalonia.Controls.DragDrop +namespace Avalonia.Input.DragDrop { public static class DataFormats { diff --git a/src/Avalonia.Controls/DragDrop/DataObject.cs b/src/Avalonia.Input/DragDrop/DataObject.cs similarity index 96% rename from src/Avalonia.Controls/DragDrop/DataObject.cs rename to src/Avalonia.Input/DragDrop/DataObject.cs index e0e7b6d069f..0db1008a6c4 100644 --- a/src/Avalonia.Controls/DragDrop/DataObject.cs +++ b/src/Avalonia.Input/DragDrop/DataObject.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace Avalonia.Controls.DragDrop +namespace Avalonia.Input.DragDrop { public class DataObject : IDataObject { diff --git a/src/Avalonia.Controls/DragDrop/DragDrop.cs b/src/Avalonia.Input/DragDrop/DragDrop.cs similarity index 96% rename from src/Avalonia.Controls/DragDrop/DragDrop.cs rename to src/Avalonia.Input/DragDrop/DragDrop.cs index bbac80e38fa..9d68deb62b1 100644 --- a/src/Avalonia.Controls/DragDrop/DragDrop.cs +++ b/src/Avalonia.Input/DragDrop/DragDrop.cs @@ -1,8 +1,8 @@ using System.Threading.Tasks; -using Avalonia.Controls.Platform; using Avalonia.Interactivity; +using Avalonia.Input.Platform; -namespace Avalonia.Controls.DragDrop +namespace Avalonia.Input.DragDrop { public static class DragDrop { diff --git a/src/Avalonia.Controls/DragDrop/DragDropDevice.cs b/src/Avalonia.Input/DragDrop/DragDropDevice.cs similarity index 95% rename from src/Avalonia.Controls/DragDrop/DragDropDevice.cs rename to src/Avalonia.Input/DragDrop/DragDropDevice.cs index 0aadda79f42..0a64800346b 100644 --- a/src/Avalonia.Controls/DragDrop/DragDropDevice.cs +++ b/src/Avalonia.Input/DragDrop/DragDropDevice.cs @@ -1,13 +1,12 @@ -using Avalonia.Input; -using Avalonia.Interactivity; +using Avalonia.Interactivity; using Avalonia.VisualTree; using System.Linq; -using Avalonia.Controls.DragDrop.Raw; +using Avalonia.Input.DragDrop.Raw; using Avalonia.Input.Raw; -namespace Avalonia.Controls.DragDrop +namespace Avalonia.Input.DragDrop { - class DragDropDevice : IDragDropDevice + public class DragDropDevice : IDragDropDevice { public static readonly DragDropDevice Instance = new DragDropDevice(); diff --git a/src/Avalonia.Controls/DragDrop/DragDropEffects.cs b/src/Avalonia.Input/DragDrop/DragDropEffects.cs similarity index 80% rename from src/Avalonia.Controls/DragDrop/DragDropEffects.cs rename to src/Avalonia.Input/DragDrop/DragDropEffects.cs index cec3dab89d9..7f093bc89c9 100644 --- a/src/Avalonia.Controls/DragDrop/DragDropEffects.cs +++ b/src/Avalonia.Input/DragDrop/DragDropEffects.cs @@ -1,6 +1,6 @@ using System; -namespace Avalonia.Controls.DragDrop +namespace Avalonia.Input.DragDrop { [Flags] public enum DragDropEffects diff --git a/src/Avalonia.Controls/DragDrop/DragEventArgs.cs b/src/Avalonia.Input/DragDrop/DragEventArgs.cs similarity index 91% rename from src/Avalonia.Controls/DragDrop/DragEventArgs.cs rename to src/Avalonia.Input/DragDrop/DragEventArgs.cs index f31bfd48201..bff19c760c5 100644 --- a/src/Avalonia.Controls/DragDrop/DragEventArgs.cs +++ b/src/Avalonia.Input/DragDrop/DragEventArgs.cs @@ -1,6 +1,6 @@ using Avalonia.Interactivity; -namespace Avalonia.Controls.DragDrop +namespace Avalonia.Input.DragDrop { public class DragEventArgs : RoutedEventArgs { diff --git a/src/Avalonia.Controls/DragDrop/IDataObject.cs b/src/Avalonia.Input/DragDrop/IDataObject.cs similarity index 88% rename from src/Avalonia.Controls/DragDrop/IDataObject.cs rename to src/Avalonia.Input/DragDrop/IDataObject.cs index c8fd720c480..5d26a20ceeb 100644 --- a/src/Avalonia.Controls/DragDrop/IDataObject.cs +++ b/src/Avalonia.Input/DragDrop/IDataObject.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Avalonia.Controls.DragDrop +namespace Avalonia.Input.DragDrop { public interface IDataObject { diff --git a/src/Avalonia.Controls/DragDrop/Raw/IDragDropDevice.cs b/src/Avalonia.Input/DragDrop/Raw/IDragDropDevice.cs similarity index 69% rename from src/Avalonia.Controls/DragDrop/Raw/IDragDropDevice.cs rename to src/Avalonia.Input/DragDrop/Raw/IDragDropDevice.cs index 6e2db67ac33..2022e842aeb 100644 --- a/src/Avalonia.Controls/DragDrop/Raw/IDragDropDevice.cs +++ b/src/Avalonia.Input/DragDrop/Raw/IDragDropDevice.cs @@ -1,6 +1,6 @@ using Avalonia.Input; -namespace Avalonia.Controls.DragDrop.Raw +namespace Avalonia.Input.DragDrop.Raw { public interface IDragDropDevice : IInputDevice { diff --git a/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs b/src/Avalonia.Input/DragDrop/Raw/RawDragEvent.cs similarity index 94% rename from src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs rename to src/Avalonia.Input/DragDrop/Raw/RawDragEvent.cs index c2bc1d57125..af18186d4b4 100644 --- a/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs +++ b/src/Avalonia.Input/DragDrop/Raw/RawDragEvent.cs @@ -2,7 +2,7 @@ using Avalonia.Input; using Avalonia.Input.Raw; -namespace Avalonia.Controls.DragDrop.Raw +namespace Avalonia.Input.DragDrop.Raw { public class RawDragEvent : RawInputEventArgs { diff --git a/src/Avalonia.Controls/DragDrop/Raw/RawDragEventType.cs b/src/Avalonia.Input/DragDrop/Raw/RawDragEventType.cs similarity index 72% rename from src/Avalonia.Controls/DragDrop/Raw/RawDragEventType.cs rename to src/Avalonia.Input/DragDrop/Raw/RawDragEventType.cs index 44c4bbd5955..31450efb51b 100644 --- a/src/Avalonia.Controls/DragDrop/Raw/RawDragEventType.cs +++ b/src/Avalonia.Input/DragDrop/Raw/RawDragEventType.cs @@ -1,4 +1,4 @@ -namespace Avalonia.Controls.DragDrop.Raw +namespace Avalonia.Input.DragDrop.Raw { public enum RawDragEventType { diff --git a/src/Avalonia.Controls/Platform/IPlatformDragSource.cs b/src/Avalonia.Input/Platform/IPlatformDragSource.cs similarity index 79% rename from src/Avalonia.Controls/Platform/IPlatformDragSource.cs rename to src/Avalonia.Input/Platform/IPlatformDragSource.cs index 667e1f005c1..44b4d30760a 100644 --- a/src/Avalonia.Controls/Platform/IPlatformDragSource.cs +++ b/src/Avalonia.Input/Platform/IPlatformDragSource.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -using Avalonia.Controls.DragDrop; +using Avalonia.Input.DragDrop; using Avalonia.Interactivity; -namespace Avalonia.Controls.Platform +namespace Avalonia.Input.Platform { public interface IPlatformDragSource { diff --git a/src/OSX/Avalonia.MonoMac/DragSource.cs b/src/OSX/Avalonia.MonoMac/DragSource.cs index 07f2e186aa5..96d650ab3a5 100644 --- a/src/OSX/Avalonia.MonoMac/DragSource.cs +++ b/src/OSX/Avalonia.MonoMac/DragSource.cs @@ -10,8 +10,8 @@ using System.Runtime.Serialization.Formatters.Binary; using System.Threading.Tasks; using Avalonia.Controls; -using Avalonia.Controls.DragDrop; -using Avalonia.Controls.Platform; +using Avalonia.Input.DragDrop; +using Avalonia.Input.Platform; using Avalonia.Input; using Avalonia.Input.Raw; using MonoMac; diff --git a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs index 2fb28a50f52..ca8a24ba827 100644 --- a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs +++ b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Avalonia.Controls.DragDrop; +using Avalonia.Input.DragDrop; using MonoMac.AppKit; using MonoMac.Foundation; diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs index 97c4302c57a..34d0949b574 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using Avalonia.Controls.DragDrop; -using Avalonia.Controls.DragDrop.Raw; +using Avalonia.Input.DragDrop; +using Avalonia.Input.DragDrop.Raw; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Platform; diff --git a/src/Windows/Avalonia.Win32/ClipboardFormats.cs b/src/Windows/Avalonia.Win32/ClipboardFormats.cs index a59c6a1664d..a604f634f64 100644 --- a/src/Windows/Avalonia.Win32/ClipboardFormats.cs +++ b/src/Windows/Avalonia.Win32/ClipboardFormats.cs @@ -3,7 +3,7 @@ using System.ComponentModel; using System.Linq; using System.Text; -using Avalonia.Controls.DragDrop; +using Avalonia.Input.DragDrop; using Avalonia.Win32.Interop; namespace Avalonia.Win32 diff --git a/src/Windows/Avalonia.Win32/DataObject.cs b/src/Windows/Avalonia.Win32/DataObject.cs index aa3ee91ef95..2a990419e74 100644 --- a/src/Windows/Avalonia.Win32/DataObject.cs +++ b/src/Windows/Avalonia.Win32/DataObject.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using System.Text; -using Avalonia.Controls.DragDrop; +using Avalonia.Input.DragDrop; using Avalonia.Win32.Interop; -using IDataObject = Avalonia.Controls.DragDrop.IDataObject; +using IDataObject = Avalonia.Input.DragDrop.IDataObject; using IOleDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; using System.IO; using System.Runtime.Serialization.Formatters.Binary; diff --git a/src/Windows/Avalonia.Win32/DragSource.cs b/src/Windows/Avalonia.Win32/DragSource.cs index 937471f9ad4..81dd7900667 100644 --- a/src/Windows/Avalonia.Win32/DragSource.cs +++ b/src/Windows/Avalonia.Win32/DragSource.cs @@ -2,9 +2,8 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -using Avalonia.Controls.DragDrop; -using Avalonia.Controls.Platform; -using Avalonia.Interactivity; +using Avalonia.Input.DragDrop; +using Avalonia.Input.Platform; using Avalonia.Threading; using Avalonia.Win32.Interop; diff --git a/src/Windows/Avalonia.Win32/OleDataObject.cs b/src/Windows/Avalonia.Win32/OleDataObject.cs index 60527bb7d65..ee1c3046ebc 100644 --- a/src/Windows/Avalonia.Win32/OleDataObject.cs +++ b/src/Windows/Avalonia.Win32/OleDataObject.cs @@ -6,13 +6,13 @@ using System.Runtime.InteropServices.ComTypes; using System.Runtime.Serialization.Formatters.Binary; using System.Text; -using Avalonia.Controls.DragDrop; +using Avalonia.Input.DragDrop; using Avalonia.Win32.Interop; using IDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; namespace Avalonia.Win32 { - class OleDataObject : Avalonia.Controls.DragDrop.IDataObject + class OleDataObject : Avalonia.Input.DragDrop.IDataObject { private IDataObject _wrapped; diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs index 747006673aa..e3791747b1c 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -1,9 +1,9 @@ -using Avalonia.Controls.DragDrop; -using Avalonia.Controls.DragDrop.Raw; +using Avalonia.Input.DragDrop; +using Avalonia.Input.DragDrop.Raw; using Avalonia.Input; using Avalonia.Platform; using Avalonia.Win32.Interop; -using IDataObject = Avalonia.Controls.DragDrop.IDataObject; +using IDataObject = Avalonia.Input.DragDrop.IDataObject; using IOleDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; namespace Avalonia.Win32 diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index ef1c4e987d6..902abaf65bb 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -84,8 +84,10 @@ public static void Initialize(bool deferredRendering = true) .Bind().ToConstant(new RenderLoop(60)) .Bind().ToSingleton() .Bind().ToConstant(s_instance) - .Bind().ToConstant(s_instance) - .Bind().ToSingleton(); + .Bind().ToConstant(s_instance); + + if (OleContext.Current != null) + AvaloniaLocator.CurrentMutable.Bind().ToSingleton(); UseDeferredRendering = deferredRendering; _uiThread = UnmanagedMethods.GetCurrentThreadId(); From 15fed263fdbdedbb470a7e4739d564a1aae21b1f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 11 Apr 2018 10:19:05 +0100 Subject: [PATCH 32/59] tidy ControlCatalog.cspoj --- samples/ControlCatalog/ControlCatalog.csproj | 192 +------------------ 1 file changed, 6 insertions(+), 186 deletions(-) diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 862de9d3201..08c9a48e424 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -1,7 +1,6 @@  netstandard2.0 - False false @@ -21,181 +20,17 @@ prompt 4 + - - - - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - - - - - App.xaml - - - MainView.xaml - - - DecoratedWindow.xaml - - - MainWindow.xaml - - - DialogsPage.xaml - - - BorderPage.xaml - - - AutoCompleteBoxPage.xaml - - - ButtonPage.xaml - - - CalendarPage.xaml - - - CanvasPage.xaml - - - CarouselPage.xaml - - - ContextMenuPage.xaml - - - CheckBoxPage.xaml - - - DropDownPage.xaml - - - DatePickerPage.xaml - - - ExpanderPage.xaml - - - ImagePage.xaml - - - LayoutTransformControlPage.xaml + + %(Filename) - - MenuPage.xaml - - - ProgressBarPage.xaml - - - RadioButtonPage.xaml - - - SliderPage.xaml - - - TreeViewPage.xaml - - - TextBoxPage.xaml - - - ToolTipPage.xaml - - - ButtonSpinnerPage.xaml - - - NumericUpDownPage.xaml - - - - - - - - - - - - + Designer + + @@ -212,20 +47,5 @@ - - - - - - Designer - - - - - MSBuild:Compile - - - - \ No newline at end of file From d1b06cb7844b64fd44a56e03c6794fa78cee3da0 Mon Sep 17 00:00:00 2001 From: lindexi Date: Thu, 12 Apr 2018 09:03:03 +0800 Subject: [PATCH 33/59] Comments at this low level are't really necessary --- src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs index 56d0624f52b..caff1e9e0f2 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -132,7 +132,6 @@ public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.Pen pen, Fac { properties.DashStyle = DashStyle.Custom; properties.DashOffset = (float) pen.DashStyle.Offset; - //for `pen.DashStyle?.Dashes != null` that you shouldnt add `?` in `pen.DashStyle.Dashes` dashes = pen.DashStyle.Dashes.Select(x => (float)x).ToArray(); } From 31b88dc58967ce8a53099103d50bfdb10d9a5af8 Mon Sep 17 00:00:00 2001 From: lindexi Date: Thu, 12 Apr 2018 09:10:26 +0800 Subject: [PATCH 34/59] Change code --- src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs index caff1e9e0f2..a0fd3c01726 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -136,10 +136,7 @@ public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.Pen pen, Fac } //If we don't enter the code above that it is null. We should set it a default value to solve the null exception. - if (dashes == null) - { - dashes = new float[0]; - } + dashes = dashes ?? Array.Empty(); return new StrokeStyle(factory, properties, dashes); } From a0b1ad62b36e3806d1bb7da3280814dd956f0181 Mon Sep 17 00:00:00 2001 From: ahopper Date: Thu, 12 Apr 2018 11:30:53 +0100 Subject: [PATCH 35/59] Fix #1484 --- samples/ControlCatalog/ControlCatalog.csproj | 4 ++ samples/ControlCatalog/MainView.xaml | 1 + samples/ControlCatalog/Pages/PopupPage.xaml | 49 +++++++++++++++++++ .../ControlCatalog/Pages/PopupPage.xaml.cs | 19 +++++++ src/Avalonia.Controls/Primitives/Popup.cs | 47 +++++++++++++++--- 5 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 samples/ControlCatalog/Pages/PopupPage.xaml create mode 100644 samples/ControlCatalog/Pages/PopupPage.xaml.cs diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 862de9d3201..4fb0ff20593 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -23,6 +23,8 @@ + + @@ -84,6 +86,7 @@ Designer + Designer @@ -158,6 +161,7 @@ MenuPage.xaml + ProgressBarPage.xaml diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index a0e0df450b0..3a19d8e8ede 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -27,5 +27,6 @@ + diff --git a/samples/ControlCatalog/Pages/PopupPage.xaml b/samples/ControlCatalog/Pages/PopupPage.xaml new file mode 100644 index 00000000000..8469ac480d7 --- /dev/null +++ b/samples/ControlCatalog/Pages/PopupPage.xaml @@ -0,0 +1,49 @@ + + + Popup + A Popup dialog + + Flyout Auto close + Flyout Toggle + + + + + Item A + Item B + Item C + Item D + Item E + Item F + Item G + + Nested Auto close + + + + Nested Content + + + + + + + + + + + Item A + Item B + Item C + Item D + Item E + Item F + Item G + + + + + + + \ No newline at end of file diff --git a/samples/ControlCatalog/Pages/PopupPage.xaml.cs b/samples/ControlCatalog/Pages/PopupPage.xaml.cs new file mode 100644 index 00000000000..08aca56badf --- /dev/null +++ b/samples/ControlCatalog/Pages/PopupPage.xaml.cs @@ -0,0 +1,19 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace ControlCatalog.Pages +{ + public class PopupPage : UserControl + { + public PopupPage() + { + this.InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 5cd3b22fc94..b8817d28f3d 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -215,7 +215,17 @@ public void Open() { var window = _topLevel as Window; if (window != null) - window.Deactivated += WindowDeactivated; + { + window.Deactivated += WindowDeactivated; + } + else + { + var parentPopuproot = _topLevel as PopupRoot; + if(parentPopuproot != null && parentPopuproot.Parent!=null) + { + ((Popup)(parentPopuproot.Parent)).Closed += ParentClosed; + } + } _topLevel.AddHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel); _nonClientListener = InputManager.Instance.Process.Subscribe(ListenForNonClientClick); } @@ -230,7 +240,7 @@ public void Open() Opened?.Invoke(this, EventArgs.Empty); } - + /// /// Closes the popup. /// @@ -244,6 +254,14 @@ public void Close() var window = _topLevel as Window; if (window != null) window.Deactivated -= WindowDeactivated; + else + { + var parentPopuproot = _topLevel as PopupRoot; + if (parentPopuproot != null && parentPopuproot.Parent != null) + { + ((Popup)parentPopuproot.Parent).Closed -= ParentClosed; + } + } _nonClientListener?.Dispose(); _nonClientListener = null; } @@ -381,16 +399,25 @@ private void PointerPressedOutside(object sender, PointerPressedEventArgs e) { if (!StaysOpen) { - var root = ((IVisual)e.Source).GetVisualRoot(); - - if (root != this.PopupRoot) - { + if(!IsChildOrThis((IVisual)e.Source)) + { Close(); e.Handled = true; } } } + private bool IsChildOrThis(IVisual child) + { + IVisual root = child.GetVisualRoot(); + while (root is PopupRoot) + { + if (root == PopupRoot) return true; + root = ((PopupRoot)root).Parent.GetVisualRoot(); + } + return false; + } + private void WindowDeactivated(object sender, EventArgs e) { if (!StaysOpen) @@ -398,5 +425,13 @@ private void WindowDeactivated(object sender, EventArgs e) Close(); } } + + private void ParentClosed(object sender, EventArgs e) + { + if (!StaysOpen) + { + Close(); + } + } } } \ No newline at end of file From 85339e4585084d4b3e301bd73940c8ad9d1b62f0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 12 Apr 2018 22:44:19 +0100 Subject: [PATCH 36/59] remove other unnecesary stuff from ControlCatalog project file. --- samples/ControlCatalog/ControlCatalog.csproj | 25 ++++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 08c9a48e424..63b0f4a9615 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -1,26 +1,8 @@  - netstandard2.0 - false + netstandard2.0 - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - + %(Filename) @@ -28,7 +10,7 @@ Designer - + @@ -47,5 +29,6 @@ + \ No newline at end of file From 5cba2f4aac7c86eaa99a1b62c6fae3ae107c8aef Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 12 Apr 2018 22:48:24 +0100 Subject: [PATCH 37/59] auto generate assembly info for control catalog. --- .../ControlCatalog/Properties/AssemblyInfo.cs | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 samples/ControlCatalog/Properties/AssemblyInfo.cs diff --git a/samples/ControlCatalog/Properties/AssemblyInfo.cs b/samples/ControlCatalog/Properties/AssemblyInfo.cs deleted file mode 100644 index 30c069d7d83..00000000000 --- a/samples/ControlCatalog/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ControlCatalog")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ControlCatalog")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("61bec86c-f307-4295-b5b8-9428610d7d55")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] From f03d0e9a4fe5ed570b97d42aadcba54716dedd2e Mon Sep 17 00:00:00 2001 From: lindexi Date: Fri, 13 Apr 2018 08:54:05 +0800 Subject: [PATCH 38/59] remove comment --- src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs index a0fd3c01726..124e33c5a3b 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -127,15 +127,14 @@ public static StrokeStyle ToDirect2DStrokeStyle(this Avalonia.Media.Pen pen, Fac EndCap = pen.EndLineCap.ToDirect2D(), DashCap = pen.DashCap.ToDirect2D() }; - float[] dashes = null;//If we new an array first that we may waste it when we use `dashes = pen.DashStyle.Dashes.Select(x => (float)x).ToArray()`. As a frequently used code I think this optimize is necessary. + float[] dashes = null; if (pen.DashStyle?.Dashes != null && pen.DashStyle.Dashes.Count > 0) { properties.DashStyle = DashStyle.Custom; - properties.DashOffset = (float) pen.DashStyle.Offset; + properties.DashOffset = (float)pen.DashStyle.Offset; dashes = pen.DashStyle.Dashes.Select(x => (float)x).ToArray(); } - //If we don't enter the code above that it is null. We should set it a default value to solve the null exception. dashes = dashes ?? Array.Empty(); return new StrokeStyle(factory, properties, dashes); From c09233e4b462271ee667bd2b5312497501fdc126 Mon Sep 17 00:00:00 2001 From: boombuler Date: Fri, 13 Apr 2018 13:28:49 +0200 Subject: [PATCH 39/59] renamed DragSource to InProcessDragSource --- src/Avalonia.Controls/Application.cs | 2 +- .../Platform/{DragSource.cs => InProcessDragSource.cs} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/Avalonia.Controls/Platform/{DragSource.cs => InProcessDragSource.cs} (98%) diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index f94a85f31d1..3dc07f81e66 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -240,7 +240,7 @@ public virtual void RegisterServices() .Bind().ToConstant(this) .Bind().ToConstant(AvaloniaScheduler.Instance) .Bind().ToConstant(DragDropDevice.Instance) - .Bind().ToTransient(); + .Bind().ToTransient(); } } } diff --git a/src/Avalonia.Controls/Platform/DragSource.cs b/src/Avalonia.Controls/Platform/InProcessDragSource.cs similarity index 98% rename from src/Avalonia.Controls/Platform/DragSource.cs rename to src/Avalonia.Controls/Platform/InProcessDragSource.cs index ed873ec961f..1e97c7bf967 100644 --- a/src/Avalonia.Controls/Platform/DragSource.cs +++ b/src/Avalonia.Controls/Platform/InProcessDragSource.cs @@ -14,7 +14,7 @@ namespace Avalonia.Platform { - class DragSource : IPlatformDragSource + class InProcessDragSource : IPlatformDragSource { private const InputModifiers MOUSE_INPUTMODIFIERS = InputModifiers.LeftMouseButton|InputModifiers.MiddleMouseButton|InputModifiers.RightMouseButton; private readonly IDragDropDevice _dragDrop; @@ -29,7 +29,7 @@ class DragSource : IPlatformDragSource private object _originalCursor; private InputModifiers? _initialInputModifiers; - public DragSource() + public InProcessDragSource() { _inputManager = AvaloniaLocator.Current.GetService(); _dragDrop = AvaloniaLocator.Current.GetService(); From d5d8d049a415944f1ba5e851f65774d039c24448 Mon Sep 17 00:00:00 2001 From: boombuler Date: Fri, 13 Apr 2018 13:30:42 +0200 Subject: [PATCH 40/59] Renamed AcceptDrag to AllowDrop and added docs --- src/Avalonia.Input/DragDrop/DragDrop.cs | 30 +++++++++++++++---- src/Avalonia.Input/DragDrop/DragDropDevice.cs | 2 +- src/Avalonia.Input/DragDrop/IDataObject.cs | 22 ++++++++++++++ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Input/DragDrop/DragDrop.cs b/src/Avalonia.Input/DragDrop/DragDrop.cs index 9d68deb62b1..0cbbfb1dbe1 100644 --- a/src/Avalonia.Input/DragDrop/DragDrop.cs +++ b/src/Avalonia.Input/DragDrop/DragDrop.cs @@ -6,21 +6,39 @@ namespace Avalonia.Input.DragDrop { public static class DragDrop { + /// + /// Event which is raised, when a drag-and-drop operation enters the element. + /// public static RoutedEvent DragEnterEvent = RoutedEvent.Register("DragEnter", RoutingStrategies.Bubble, typeof(DragDrop)); + /// + /// Event which is raised, when a drag-and-drop operation leaves the element. + /// public static RoutedEvent DragLeaveEvent = RoutedEvent.Register("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop)); + /// + /// Event which is raised, when a drag-and-drop operation is updated while over the element. + /// public static RoutedEvent DragOverEvent = RoutedEvent.Register("DragOver", RoutingStrategies.Bubble, typeof(DragDrop)); + /// + /// Event which is raised, when a drag-and-drop operation should complete over the element. + /// public static RoutedEvent DropEvent = RoutedEvent.Register("Drop", RoutingStrategies.Bubble, typeof(DragDrop)); - public static AvaloniaProperty AcceptDragProperty = AvaloniaProperty.RegisterAttached("AcceptDrag", typeof(DragDrop), inherits: true); + public static AvaloniaProperty AllowDropProperty = AvaloniaProperty.RegisterAttached("AllowDrop", typeof(DragDrop), inherits: true); - public static bool GetAcceptDrag(Interactive interactive) + /// + /// Gets a value indicating whether the given element can be used as the target of a drag-and-drop operation. + /// + public static bool GetAllowDrop(Interactive interactive) { - return interactive.GetValue(AcceptDragProperty); + return interactive.GetValue(AllowDropProperty); } - - public static void SetAcceptDrag(Interactive interactive, bool value) + + /// + /// Sets a value indicating whether the given interactive can be used as the target of a drag-and-drop operation. + /// + public static void SetAllowDrop(Interactive interactive, bool value) { - interactive.SetValue(AcceptDragProperty, value); + interactive.SetValue(AllowDropProperty, value); } /// diff --git a/src/Avalonia.Input/DragDrop/DragDropDevice.cs b/src/Avalonia.Input/DragDrop/DragDropDevice.cs index 0a64800346b..7ef6f212f82 100644 --- a/src/Avalonia.Input/DragDrop/DragDropDevice.cs +++ b/src/Avalonia.Input/DragDrop/DragDropDevice.cs @@ -15,7 +15,7 @@ public class DragDropDevice : IDragDropDevice private Interactive GetTarget(IInputElement root, Point local) { var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType()?.FirstOrDefault(); - if (target != null && DragDrop.GetAcceptDrag(target)) + if (target != null && DragDrop.GetAllowDrop(target)) return target; return null; } diff --git a/src/Avalonia.Input/DragDrop/IDataObject.cs b/src/Avalonia.Input/DragDrop/IDataObject.cs index 5d26a20ceeb..bf2d00f529d 100644 --- a/src/Avalonia.Input/DragDrop/IDataObject.cs +++ b/src/Avalonia.Input/DragDrop/IDataObject.cs @@ -2,16 +2,38 @@ namespace Avalonia.Input.DragDrop { + /// + /// Interface to access information about the data of a drag-and-drop operation. + /// public interface IDataObject { + /// + /// Lists all formats which are present in the DataObject. + /// + /// IEnumerable GetDataFormats(); + /// + /// Checks wether a given DataFormat is present in this object + /// + /// bool Contains(string dataFormat); + /// + /// Returns the dragged text if the DataObject contains any text. + /// + /// string GetText(); + /// + /// Returns a list of filenames if the DataObject contains filenames. + /// + /// IEnumerable GetFileNames(); + /// + /// Tries to get the data of the given DataFormat. + /// object Get(string dataFormat); } } \ No newline at end of file From 36186b31078aa911ecbd3c931f1514b074d24627 Mon Sep 17 00:00:00 2001 From: boombuler Date: Fri, 13 Apr 2018 13:31:53 +0200 Subject: [PATCH 41/59] added Drag+Drop sample --- samples/ControlCatalog/ControlCatalog.csproj | 8 +++ samples/ControlCatalog/MainView.xaml | 1 + .../ControlCatalog/Pages/DragAndDropPage.cs | 71 +++++++++++++++++++ .../ControlCatalog/Pages/DragAndDropPage.xaml | 19 +++++ 4 files changed, 99 insertions(+) create mode 100644 samples/ControlCatalog/Pages/DragAndDropPage.cs create mode 100644 samples/ControlCatalog/Pages/DragAndDropPage.xaml diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 862de9d3201..5cdf5c59de6 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -23,6 +23,8 @@ + + @@ -35,6 +37,9 @@ Designer + + Designer + Designer @@ -140,6 +145,9 @@ CheckBoxPage.xaml + + DragAndDropPage.xaml + DropDownPage.xaml diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index a0e0df450b0..1107d34b3e6 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -15,6 +15,7 @@ + diff --git a/samples/ControlCatalog/Pages/DragAndDropPage.cs b/samples/ControlCatalog/Pages/DragAndDropPage.cs new file mode 100644 index 00000000000..172331204f0 --- /dev/null +++ b/samples/ControlCatalog/Pages/DragAndDropPage.cs @@ -0,0 +1,71 @@ +using Avalonia.Controls; +using Avalonia.Input.DragDrop; +using Avalonia.Markup.Xaml; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ControlCatalog.Pages +{ + public class DragAndDropPage : UserControl + { + private TextBlock _DropState; + private TextBlock _DragState; + private Border _DragMe; + private int DragCount = 0; + + public DragAndDropPage() + { + this.InitializeComponent(); + + _DragMe.PointerPressed += DoDrag; + + AddHandler(DragDrop.DropEvent, Drop); + AddHandler(DragDrop.DragOverEvent, DragOver); + } + + private async void DoDrag(object sender, Avalonia.Input.PointerPressedEventArgs e) + { + DataObject dragData = new DataObject(); + dragData.Set(DataFormats.Text, $"You have dragged text {++DragCount} times"); + + var result = await DragDrop.DoDragDrop(dragData, DragDropEffects.Copy); + switch(result) + { + case DragDropEffects.Copy: + _DragState.Text = "The text was copied"; break; + case DragDropEffects.Link: + _DragState.Text = "The text was linked"; break; + case DragDropEffects.None: + _DragState.Text = "The drag operation was canceled"; break; + } + } + + private void DragOver(object sender, DragEventArgs e) + { + // Only allow Copy or Link as Drop Operations. + e.DragEffects = e.DragEffects & (DragDropEffects.Copy | DragDropEffects.Link); + + // Only allow if the dragged data contains text or filenames. + if (!e.Data.Contains(DataFormats.Text) && !e.Data.Contains(DataFormats.FileNames)) + e.DragEffects = DragDropEffects.None; + } + + private void Drop(object sender, DragEventArgs e) + { + if (e.Data.Contains(DataFormats.Text)) + _DropState.Text = e.Data.GetText(); + else if (e.Data.Contains(DataFormats.FileNames)) + _DropState.Text = string.Join(Environment.NewLine, e.Data.GetFileNames()); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + + _DropState = this.Find("DropState"); + _DragState = this.Find("DragState"); + _DragMe = this.Find("DragMe"); + } + } +} diff --git a/samples/ControlCatalog/Pages/DragAndDropPage.xaml b/samples/ControlCatalog/Pages/DragAndDropPage.xaml new file mode 100644 index 00000000000..af679d2f9aa --- /dev/null +++ b/samples/ControlCatalog/Pages/DragAndDropPage.xaml @@ -0,0 +1,19 @@ + + + Drag+Drop + Example of Drag+Drop capabilities + + + + Drag Me + + + Drop some text or files here + + + + \ No newline at end of file From 6bb1cb471ef37da8b756534cb241056982f42aab Mon Sep 17 00:00:00 2001 From: boombuler Date: Fri, 13 Apr 2018 13:33:33 +0200 Subject: [PATCH 42/59] don't use OSX-Platform DragSource this should be used when the DragSource does work with any object. For now only strings and filenames work. --- src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs index 3db02de209c..ba45ad8403e 100644 --- a/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs +++ b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs @@ -36,7 +36,7 @@ void DoInitialize() .Bind().ToSingleton() .Bind().ToConstant(s_renderLoop) .Bind().ToConstant(PlatformThreadingInterface.Instance) - .Bind().ToTransient(); + /*.Bind().ToTransient()*/; } public static void Initialize() From 16ea6a31fcaf69e2ed3702a1c48bc6e076481e01 Mon Sep 17 00:00:00 2001 From: achal7 Date: Fri, 13 Apr 2018 17:56:25 +0530 Subject: [PATCH 43/59] 1) changed UpdateLastState to call UpdateLastState(TState) so that its the only one that has last update control 2) Clear function clean up the states, however currentNode by definition is depends upon one of the state value, hence made that also null, since there is nothing now to link --- src/Avalonia.Controls/Utils/UndoRedoHelper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs index c76555e5545..17cf681f15f 100644 --- a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs +++ b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs @@ -59,7 +59,7 @@ public void UpdateLastState(TState state) public void UpdateLastState() { - _states.Last.Value = _host.UndoRedoState; + UpdateLastState(_host.UndoRedoState); } public void DiscardRedo() @@ -94,6 +94,7 @@ public void Snapshot() public void Clear() { _states.Clear(); + _currentNode = null; } bool WeakTimer.IWeakTimerSubscriber.Tick() From 3b2012dcd8f1babd4b96490d5a1d49e88d98ad8c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 15 Mar 2018 23:16:05 +0100 Subject: [PATCH 44/59] Fixed focused control display in DevTools. --- src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs b/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs index 2d3f978462a..555a0b23548 100644 --- a/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs +++ b/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs @@ -86,7 +86,7 @@ public void SelectControl(IControl control) private void UpdateFocusedControl() { - _focusedControl = KeyboardDevice.Instance.FocusedElement?.GetType().Name; + FocusedControl = KeyboardDevice.Instance.FocusedElement?.GetType().Name; } } } From cea86f36474d85b9cee079f477681669bbf301fe Mon Sep 17 00:00:00 2001 From: ahopper Date: Tue, 17 Apr 2018 07:08:58 +0100 Subject: [PATCH 45/59] removed popup from control catalog --- samples/ControlCatalog/ControlCatalog.csproj | 4 -- samples/ControlCatalog/MainView.xaml | 1 - samples/ControlCatalog/Pages/PopupPage.xaml | 49 ------------------- .../ControlCatalog/Pages/PopupPage.xaml.cs | 19 ------- 4 files changed, 73 deletions(-) delete mode 100644 samples/ControlCatalog/Pages/PopupPage.xaml delete mode 100644 samples/ControlCatalog/Pages/PopupPage.xaml.cs diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 4fb0ff20593..862de9d3201 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -23,8 +23,6 @@ - - @@ -86,7 +84,6 @@ Designer - Designer @@ -161,7 +158,6 @@ MenuPage.xaml - ProgressBarPage.xaml diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 3a19d8e8ede..a0e0df450b0 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -27,6 +27,5 @@ - diff --git a/samples/ControlCatalog/Pages/PopupPage.xaml b/samples/ControlCatalog/Pages/PopupPage.xaml deleted file mode 100644 index 8469ac480d7..00000000000 --- a/samples/ControlCatalog/Pages/PopupPage.xaml +++ /dev/null @@ -1,49 +0,0 @@ - - - Popup - A Popup dialog - - Flyout Auto close - Flyout Toggle - - - - - Item A - Item B - Item C - Item D - Item E - Item F - Item G - - Nested Auto close - - - - Nested Content - - - - - - - - - - - Item A - Item B - Item C - Item D - Item E - Item F - Item G - - - - - - - \ No newline at end of file diff --git a/samples/ControlCatalog/Pages/PopupPage.xaml.cs b/samples/ControlCatalog/Pages/PopupPage.xaml.cs deleted file mode 100644 index 08aca56badf..00000000000 --- a/samples/ControlCatalog/Pages/PopupPage.xaml.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; - -namespace ControlCatalog.Pages -{ - public class PopupPage : UserControl - { - public PopupPage() - { - this.InitializeComponent(); - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } - } -} From 0561ee2cb2484bd3733b0d7c73a37e09c7ac5c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Tue, 17 Apr 2018 16:38:09 +0200 Subject: [PATCH 46/59] Fix DragAndDropPage file name --- .../Pages/{DragAndDropPage.cs => DragAndDropPage.xaml.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename samples/ControlCatalog/Pages/{DragAndDropPage.cs => DragAndDropPage.xaml.cs} (100%) diff --git a/samples/ControlCatalog/Pages/DragAndDropPage.cs b/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs similarity index 100% rename from samples/ControlCatalog/Pages/DragAndDropPage.cs rename to samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs From 68f5cd6dd62ccc56735857583a87a788eec4fd12 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 18 Apr 2018 01:06:44 +0200 Subject: [PATCH 47/59] Move DnD classes to Avalonia.Input. Fixes #1506. --- samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs | 2 +- src/Avalonia.Controls/Application.cs | 9 +++------ src/Avalonia.Controls/Platform/InProcessDragSource.cs | 2 -- src/Avalonia.Input/{DragDrop => }/DataFormats.cs | 2 +- src/Avalonia.Input/{DragDrop => }/DataObject.cs | 2 +- src/Avalonia.Input/{DragDrop => }/DragDrop.cs | 2 +- src/Avalonia.Input/{DragDrop => }/DragDropDevice.cs | 3 +-- src/Avalonia.Input/{DragDrop => }/DragDropEffects.cs | 2 +- src/Avalonia.Input/{DragDrop => }/DragEventArgs.cs | 2 +- src/Avalonia.Input/{DragDrop => }/IDataObject.cs | 2 +- src/Avalonia.Input/Platform/IPlatformDragSource.cs | 2 +- src/Avalonia.Input/{DragDrop => }/Raw/IDragDropDevice.cs | 2 +- src/Avalonia.Input/{DragDrop => }/Raw/RawDragEvent.cs | 2 +- .../{DragDrop => }/Raw/RawDragEventType.cs | 2 +- src/OSX/Avalonia.MonoMac/DragSource.cs | 3 +-- src/OSX/Avalonia.MonoMac/DraggingInfo.cs | 2 +- src/OSX/Avalonia.MonoMac/TopLevelImpl.cs | 2 -- src/Windows/Avalonia.Win32/ClipboardFormats.cs | 2 +- src/Windows/Avalonia.Win32/DataObject.cs | 4 ++-- src/Windows/Avalonia.Win32/DragSource.cs | 2 +- src/Windows/Avalonia.Win32/OleDataObject.cs | 4 ++-- src/Windows/Avalonia.Win32/OleDropTarget.cs | 7 +++---- 22 files changed, 26 insertions(+), 36 deletions(-) rename src/Avalonia.Input/{DragDrop => }/DataFormats.cs (90%) rename src/Avalonia.Input/{DragDrop => }/DataObject.cs (96%) rename src/Avalonia.Input/{DragDrop => }/DragDrop.cs (98%) rename src/Avalonia.Input/{DragDrop => }/DragDropDevice.cs (98%) rename src/Avalonia.Input/{DragDrop => }/DragDropEffects.cs (81%) rename src/Avalonia.Input/{DragDrop => }/DragEventArgs.cs (91%) rename src/Avalonia.Input/{DragDrop => }/IDataObject.cs (97%) rename src/Avalonia.Input/{DragDrop => }/Raw/IDragDropDevice.cs (70%) rename src/Avalonia.Input/{DragDrop => }/Raw/RawDragEvent.cs (95%) rename src/Avalonia.Input/{DragDrop => }/Raw/RawDragEventType.cs (74%) diff --git a/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs b/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs index 172331204f0..718f21314e1 100644 --- a/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs @@ -1,5 +1,5 @@ using Avalonia.Controls; -using Avalonia.Input.DragDrop; +using Avalonia.Input; using Avalonia.Markup.Xaml; using System; using System.Collections.Generic; diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 3dc07f81e66..6fdca557ebc 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -2,20 +2,17 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Reactive.Concurrency; using System.Threading; using Avalonia.Controls; using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Input.Platform; +using Avalonia.Input.Raw; using Avalonia.Layout; -using Avalonia.Rendering; +using Avalonia.Platform; using Avalonia.Styling; using Avalonia.Threading; -using System.Reactive.Concurrency; -using Avalonia.Input.DragDrop.Raw; -using Avalonia.Controls.Platform; -using Avalonia.Platform; -using Avalonia.Input.DragDrop; namespace Avalonia { diff --git a/src/Avalonia.Controls/Platform/InProcessDragSource.cs b/src/Avalonia.Controls/Platform/InProcessDragSource.cs index 1e97c7bf967..e136efe2a9a 100644 --- a/src/Avalonia.Controls/Platform/InProcessDragSource.cs +++ b/src/Avalonia.Controls/Platform/InProcessDragSource.cs @@ -5,8 +5,6 @@ using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Input; -using Avalonia.Input.DragDrop; -using Avalonia.Input.DragDrop.Raw; using Avalonia.Input.Platform; using Avalonia.Input.Raw; using Avalonia.Threading; diff --git a/src/Avalonia.Input/DragDrop/DataFormats.cs b/src/Avalonia.Input/DataFormats.cs similarity index 90% rename from src/Avalonia.Input/DragDrop/DataFormats.cs rename to src/Avalonia.Input/DataFormats.cs index 4115701478e..559d2cb6433 100644 --- a/src/Avalonia.Input/DragDrop/DataFormats.cs +++ b/src/Avalonia.Input/DataFormats.cs @@ -1,4 +1,4 @@ -namespace Avalonia.Input.DragDrop +namespace Avalonia.Input { public static class DataFormats { diff --git a/src/Avalonia.Input/DragDrop/DataObject.cs b/src/Avalonia.Input/DataObject.cs similarity index 96% rename from src/Avalonia.Input/DragDrop/DataObject.cs rename to src/Avalonia.Input/DataObject.cs index 0db1008a6c4..cb642f4d65f 100644 --- a/src/Avalonia.Input/DragDrop/DataObject.cs +++ b/src/Avalonia.Input/DataObject.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace Avalonia.Input.DragDrop +namespace Avalonia.Input { public class DataObject : IDataObject { diff --git a/src/Avalonia.Input/DragDrop/DragDrop.cs b/src/Avalonia.Input/DragDrop.cs similarity index 98% rename from src/Avalonia.Input/DragDrop/DragDrop.cs rename to src/Avalonia.Input/DragDrop.cs index 0cbbfb1dbe1..c312732afa8 100644 --- a/src/Avalonia.Input/DragDrop/DragDrop.cs +++ b/src/Avalonia.Input/DragDrop.cs @@ -2,7 +2,7 @@ using Avalonia.Interactivity; using Avalonia.Input.Platform; -namespace Avalonia.Input.DragDrop +namespace Avalonia.Input { public static class DragDrop { diff --git a/src/Avalonia.Input/DragDrop/DragDropDevice.cs b/src/Avalonia.Input/DragDropDevice.cs similarity index 98% rename from src/Avalonia.Input/DragDrop/DragDropDevice.cs rename to src/Avalonia.Input/DragDropDevice.cs index 7ef6f212f82..2615e3a2125 100644 --- a/src/Avalonia.Input/DragDrop/DragDropDevice.cs +++ b/src/Avalonia.Input/DragDropDevice.cs @@ -1,10 +1,9 @@ using Avalonia.Interactivity; using Avalonia.VisualTree; using System.Linq; -using Avalonia.Input.DragDrop.Raw; using Avalonia.Input.Raw; -namespace Avalonia.Input.DragDrop +namespace Avalonia.Input { public class DragDropDevice : IDragDropDevice { diff --git a/src/Avalonia.Input/DragDrop/DragDropEffects.cs b/src/Avalonia.Input/DragDropEffects.cs similarity index 81% rename from src/Avalonia.Input/DragDrop/DragDropEffects.cs rename to src/Avalonia.Input/DragDropEffects.cs index 7f093bc89c9..bcda1091d59 100644 --- a/src/Avalonia.Input/DragDrop/DragDropEffects.cs +++ b/src/Avalonia.Input/DragDropEffects.cs @@ -1,6 +1,6 @@ using System; -namespace Avalonia.Input.DragDrop +namespace Avalonia.Input { [Flags] public enum DragDropEffects diff --git a/src/Avalonia.Input/DragDrop/DragEventArgs.cs b/src/Avalonia.Input/DragEventArgs.cs similarity index 91% rename from src/Avalonia.Input/DragDrop/DragEventArgs.cs rename to src/Avalonia.Input/DragEventArgs.cs index bff19c760c5..12d5a8941ee 100644 --- a/src/Avalonia.Input/DragDrop/DragEventArgs.cs +++ b/src/Avalonia.Input/DragEventArgs.cs @@ -1,6 +1,6 @@ using Avalonia.Interactivity; -namespace Avalonia.Input.DragDrop +namespace Avalonia.Input { public class DragEventArgs : RoutedEventArgs { diff --git a/src/Avalonia.Input/DragDrop/IDataObject.cs b/src/Avalonia.Input/IDataObject.cs similarity index 97% rename from src/Avalonia.Input/DragDrop/IDataObject.cs rename to src/Avalonia.Input/IDataObject.cs index bf2d00f529d..1b12323d997 100644 --- a/src/Avalonia.Input/DragDrop/IDataObject.cs +++ b/src/Avalonia.Input/IDataObject.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Avalonia.Input.DragDrop +namespace Avalonia.Input { /// /// Interface to access information about the data of a drag-and-drop operation. diff --git a/src/Avalonia.Input/Platform/IPlatformDragSource.cs b/src/Avalonia.Input/Platform/IPlatformDragSource.cs index 44b4d30760a..669251c50b9 100644 --- a/src/Avalonia.Input/Platform/IPlatformDragSource.cs +++ b/src/Avalonia.Input/Platform/IPlatformDragSource.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -using Avalonia.Input.DragDrop; +using Avalonia.Input; using Avalonia.Interactivity; namespace Avalonia.Input.Platform diff --git a/src/Avalonia.Input/DragDrop/Raw/IDragDropDevice.cs b/src/Avalonia.Input/Raw/IDragDropDevice.cs similarity index 70% rename from src/Avalonia.Input/DragDrop/Raw/IDragDropDevice.cs rename to src/Avalonia.Input/Raw/IDragDropDevice.cs index 2022e842aeb..6aab1b868db 100644 --- a/src/Avalonia.Input/DragDrop/Raw/IDragDropDevice.cs +++ b/src/Avalonia.Input/Raw/IDragDropDevice.cs @@ -1,6 +1,6 @@ using Avalonia.Input; -namespace Avalonia.Input.DragDrop.Raw +namespace Avalonia.Input.Raw { public interface IDragDropDevice : IInputDevice { diff --git a/src/Avalonia.Input/DragDrop/Raw/RawDragEvent.cs b/src/Avalonia.Input/Raw/RawDragEvent.cs similarity index 95% rename from src/Avalonia.Input/DragDrop/Raw/RawDragEvent.cs rename to src/Avalonia.Input/Raw/RawDragEvent.cs index af18186d4b4..49125b4c07d 100644 --- a/src/Avalonia.Input/DragDrop/Raw/RawDragEvent.cs +++ b/src/Avalonia.Input/Raw/RawDragEvent.cs @@ -2,7 +2,7 @@ using Avalonia.Input; using Avalonia.Input.Raw; -namespace Avalonia.Input.DragDrop.Raw +namespace Avalonia.Input.Raw { public class RawDragEvent : RawInputEventArgs { diff --git a/src/Avalonia.Input/DragDrop/Raw/RawDragEventType.cs b/src/Avalonia.Input/Raw/RawDragEventType.cs similarity index 74% rename from src/Avalonia.Input/DragDrop/Raw/RawDragEventType.cs rename to src/Avalonia.Input/Raw/RawDragEventType.cs index 31450efb51b..9635f774676 100644 --- a/src/Avalonia.Input/DragDrop/Raw/RawDragEventType.cs +++ b/src/Avalonia.Input/Raw/RawDragEventType.cs @@ -1,4 +1,4 @@ -namespace Avalonia.Input.DragDrop.Raw +namespace Avalonia.Input.Raw { public enum RawDragEventType { diff --git a/src/OSX/Avalonia.MonoMac/DragSource.cs b/src/OSX/Avalonia.MonoMac/DragSource.cs index 96d650ab3a5..41a206b5805 100644 --- a/src/OSX/Avalonia.MonoMac/DragSource.cs +++ b/src/OSX/Avalonia.MonoMac/DragSource.cs @@ -10,9 +10,8 @@ using System.Runtime.Serialization.Formatters.Binary; using System.Threading.Tasks; using Avalonia.Controls; -using Avalonia.Input.DragDrop; -using Avalonia.Input.Platform; using Avalonia.Input; +using Avalonia.Input.Platform; using Avalonia.Input.Raw; using MonoMac; using MonoMac.AppKit; diff --git a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs index ca8a24ba827..fc5f52713e6 100644 --- a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs +++ b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Avalonia.Input.DragDrop; +using Avalonia.Input; using MonoMac.AppKit; using MonoMac.Foundation; diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs index 34d0949b574..db7f29f05b8 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Avalonia.Input.DragDrop; -using Avalonia.Input.DragDrop.Raw; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Platform; diff --git a/src/Windows/Avalonia.Win32/ClipboardFormats.cs b/src/Windows/Avalonia.Win32/ClipboardFormats.cs index a604f634f64..5e0bbab9751 100644 --- a/src/Windows/Avalonia.Win32/ClipboardFormats.cs +++ b/src/Windows/Avalonia.Win32/ClipboardFormats.cs @@ -3,7 +3,7 @@ using System.ComponentModel; using System.Linq; using System.Text; -using Avalonia.Input.DragDrop; +using Avalonia.Input; using Avalonia.Win32.Interop; namespace Avalonia.Win32 diff --git a/src/Windows/Avalonia.Win32/DataObject.cs b/src/Windows/Avalonia.Win32/DataObject.cs index 2a990419e74..34867765e5b 100644 --- a/src/Windows/Avalonia.Win32/DataObject.cs +++ b/src/Windows/Avalonia.Win32/DataObject.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using System.Text; -using Avalonia.Input.DragDrop; +using Avalonia.Input; using Avalonia.Win32.Interop; -using IDataObject = Avalonia.Input.DragDrop.IDataObject; +using IDataObject = Avalonia.Input.IDataObject; using IOleDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; using System.IO; using System.Runtime.Serialization.Formatters.Binary; diff --git a/src/Windows/Avalonia.Win32/DragSource.cs b/src/Windows/Avalonia.Win32/DragSource.cs index 81dd7900667..ea124e5f291 100644 --- a/src/Windows/Avalonia.Win32/DragSource.cs +++ b/src/Windows/Avalonia.Win32/DragSource.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -using Avalonia.Input.DragDrop; +using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Threading; using Avalonia.Win32.Interop; diff --git a/src/Windows/Avalonia.Win32/OleDataObject.cs b/src/Windows/Avalonia.Win32/OleDataObject.cs index ee1c3046ebc..85d1daadebb 100644 --- a/src/Windows/Avalonia.Win32/OleDataObject.cs +++ b/src/Windows/Avalonia.Win32/OleDataObject.cs @@ -6,13 +6,13 @@ using System.Runtime.InteropServices.ComTypes; using System.Runtime.Serialization.Formatters.Binary; using System.Text; -using Avalonia.Input.DragDrop; +using Avalonia.Input; using Avalonia.Win32.Interop; using IDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; namespace Avalonia.Win32 { - class OleDataObject : Avalonia.Input.DragDrop.IDataObject + class OleDataObject : Avalonia.Input.IDataObject { private IDataObject _wrapped; diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs index e3791747b1c..500c03e3176 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -1,9 +1,8 @@ -using Avalonia.Input.DragDrop; -using Avalonia.Input.DragDrop.Raw; -using Avalonia.Input; +using Avalonia.Input; +using Avalonia.Input.Raw; using Avalonia.Platform; using Avalonia.Win32.Interop; -using IDataObject = Avalonia.Input.DragDrop.IDataObject; +using IDataObject = Avalonia.Input.IDataObject; using IOleDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; namespace Avalonia.Win32 From 9ff123cc942e77047bdb7b75d91cd53418501a7a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 18 Apr 2018 01:51:28 +0200 Subject: [PATCH 48/59] Moved StringTokenizer to Avalonia.Base. And make it public instead of linking it from Avalonia.Visuals to Avalonia.Controls. --- .../Utilities/StringTokenizer.cs | 2 +- src/Avalonia.Controls/Avalonia.Controls.csproj | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/StringTokenizer.cs (99%) diff --git a/src/Avalonia.Visuals/Utilities/StringTokenizer.cs b/src/Avalonia.Base/Utilities/StringTokenizer.cs similarity index 99% rename from src/Avalonia.Visuals/Utilities/StringTokenizer.cs rename to src/Avalonia.Base/Utilities/StringTokenizer.cs index 2f378f44df4..1c9f475eb71 100644 --- a/src/Avalonia.Visuals/Utilities/StringTokenizer.cs +++ b/src/Avalonia.Base/Utilities/StringTokenizer.cs @@ -4,7 +4,7 @@ namespace Avalonia.Utilities { - internal struct StringTokenizer : IDisposable + public struct StringTokenizer : IDisposable { private const char DefaultSeparatorChar = ','; diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 1490afb5e30..997e15050f5 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -26,7 +26,6 @@ true - Properties\SharedAssemblyInfo.cs From f83dd046a2dd89cca73219fcc6190a9a6124273d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 18 Apr 2018 01:59:46 +0200 Subject: [PATCH 49/59] Use Contract.Requires for preconditions. --- src/Avalonia.Visuals/Media/Brush.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Brush.cs b/src/Avalonia.Visuals/Media/Brush.cs index abceb399616..d6b0e43cdcf 100644 --- a/src/Avalonia.Visuals/Media/Brush.cs +++ b/src/Avalonia.Visuals/Media/Brush.cs @@ -34,8 +34,8 @@ public double Opacity /// The . public static IBrush Parse(string s) { - if (s == null) throw new ArgumentNullException(nameof(s)); - if (s.Length == 0) throw new FormatException(); + Contract.Requires(s != null); + Contract.Requires(s.Length > 0); if (s[0] == '#') { From 5491f4818e883032c28a90ab8559dce744dc67d9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 18 Apr 2018 02:03:23 +0200 Subject: [PATCH 50/59] Use standard naming for Read methods. --- .../Utilities/StringTokenizer.cs | 26 +++++++++---------- src/Avalonia.Controls/GridLength.cs | 2 +- src/Avalonia.Visuals/Matrix.cs | 12 ++++----- src/Avalonia.Visuals/Point.cs | 4 +-- src/Avalonia.Visuals/Rect.cs | 8 +++--- src/Avalonia.Visuals/RelativePoint.cs | 4 +-- src/Avalonia.Visuals/RelativeRect.cs | 8 +++--- src/Avalonia.Visuals/Size.cs | 4 +-- src/Avalonia.Visuals/Thickness.cs | 8 +++--- 9 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/Avalonia.Base/Utilities/StringTokenizer.cs b/src/Avalonia.Base/Utilities/StringTokenizer.cs index 1c9f475eb71..2559e529320 100644 --- a/src/Avalonia.Base/Utilities/StringTokenizer.cs +++ b/src/Avalonia.Base/Utilities/StringTokenizer.cs @@ -50,16 +50,16 @@ public void Dispose() } } - public bool NextInt32(out Int32 result, char? separator = null) + public bool TryReadInt32(out Int32 result, char? separator = null) { - var success = NextString(out var stringResult, separator); + var success = TryReadString(out var stringResult, separator); result = success ? int.Parse(stringResult, _formatProvider) : 0; return success; } - public int NextInt32Required(char? separator = null) + public int ReadInt32(char? separator = null) { - if (!NextInt32(out var result, separator)) + if (!TryReadInt32(out var result, separator)) { throw GetFormatException(); } @@ -67,16 +67,16 @@ public int NextInt32Required(char? separator = null) return result; } - public bool NextDouble(out double result, char? separator = null) + public bool TryReadDouble(out double result, char? separator = null) { - var success = NextString(out var stringResult, separator); + var success = TryReadString(out var stringResult, separator); result = success ? double.Parse(stringResult, _formatProvider) : 0; return success; } - public double NextDoubleRequired(char? separator = null) + public double ReadDouble(char? separator = null) { - if (!NextDouble(out var result, separator)) + if (!TryReadDouble(out var result, separator)) { throw GetFormatException(); } @@ -84,16 +84,16 @@ public double NextDoubleRequired(char? separator = null) return result; } - public bool NextString(out string result, char? separator = null) + public bool TryReadString(out string result, char? separator = null) { - var success = NextToken(separator ?? _separator); + var success = TryReadToken(separator ?? _separator); result = CurrentToken; return success; } - public string NextStringRequired(char? separator = null) + public string ReadString(char? separator = null) { - if (!NextString(out var result, separator)) + if (!TryReadString(out var result, separator)) { throw GetFormatException(); } @@ -101,7 +101,7 @@ public string NextStringRequired(char? separator = null) return result; } - private bool NextToken(char separator) + private bool TryReadToken(char separator) { _tokenIndex = -1; diff --git a/src/Avalonia.Controls/GridLength.cs b/src/Avalonia.Controls/GridLength.cs index b17dc584bd1..789953a249a 100644 --- a/src/Avalonia.Controls/GridLength.cs +++ b/src/Avalonia.Controls/GridLength.cs @@ -213,7 +213,7 @@ public static IEnumerable ParseLengths(string s, CultureInfo culture { using (var tokenizer = new StringTokenizer(s, culture)) { - while (tokenizer.NextString(out var item)) + while (tokenizer.TryReadString(out var item)) { yield return Parse(item, culture); } diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 6013cf94c6a..70804ee04f4 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -321,12 +321,12 @@ public static Matrix Parse(string s, CultureInfo culture) using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Matrix")) { return new Matrix( - tokenizer.NextDoubleRequired(), - tokenizer.NextDoubleRequired(), - tokenizer.NextDoubleRequired(), - tokenizer.NextDoubleRequired(), - tokenizer.NextDoubleRequired(), - tokenizer.NextDoubleRequired() + tokenizer.ReadDouble(), + tokenizer.ReadDouble(), + tokenizer.ReadDouble(), + tokenizer.ReadDouble(), + tokenizer.ReadDouble(), + tokenizer.ReadDouble() ); } } diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Visuals/Point.cs index 49d2a401bfa..d0c3edfeb2f 100644 --- a/src/Avalonia.Visuals/Point.cs +++ b/src/Avalonia.Visuals/Point.cs @@ -177,8 +177,8 @@ public static Point Parse(string s, CultureInfo culture) using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Point")) { return new Point( - tokenizer.NextDoubleRequired(), - tokenizer.NextDoubleRequired() + tokenizer.ReadDouble(), + tokenizer.ReadDouble() ); } } diff --git a/src/Avalonia.Visuals/Rect.cs b/src/Avalonia.Visuals/Rect.cs index 11f3db31da4..748928ada3b 100644 --- a/src/Avalonia.Visuals/Rect.cs +++ b/src/Avalonia.Visuals/Rect.cs @@ -494,10 +494,10 @@ public static Rect Parse(string s, CultureInfo culture) using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Rect")) { return new Rect( - tokenizer.NextDoubleRequired(), - tokenizer.NextDoubleRequired(), - tokenizer.NextDoubleRequired(), - tokenizer.NextDoubleRequired() + tokenizer.ReadDouble(), + tokenizer.ReadDouble(), + tokenizer.ReadDouble(), + tokenizer.ReadDouble() ); } } diff --git a/src/Avalonia.Visuals/RelativePoint.cs b/src/Avalonia.Visuals/RelativePoint.cs index 625ee614392..a2ef0e6725b 100644 --- a/src/Avalonia.Visuals/RelativePoint.cs +++ b/src/Avalonia.Visuals/RelativePoint.cs @@ -160,8 +160,8 @@ public static RelativePoint Parse(string s, CultureInfo culture) { using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid RelativePoint")) { - var x = tokenizer.NextStringRequired(); - var y = tokenizer.NextStringRequired(); + var x = tokenizer.ReadString(); + var y = tokenizer.ReadString(); var unit = RelativeUnit.Absolute; var scale = 1.0; diff --git a/src/Avalonia.Visuals/RelativeRect.cs b/src/Avalonia.Visuals/RelativeRect.cs index 05e344f42b1..c13c3282dbb 100644 --- a/src/Avalonia.Visuals/RelativeRect.cs +++ b/src/Avalonia.Visuals/RelativeRect.cs @@ -173,10 +173,10 @@ public static RelativeRect Parse(string s, CultureInfo culture) { using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid RelativeRect")) { - var x = tokenizer.NextStringRequired(); - var y = tokenizer.NextStringRequired(); - var width = tokenizer.NextStringRequired(); - var height = tokenizer.NextStringRequired(); + var x = tokenizer.ReadString(); + var y = tokenizer.ReadString(); + var width = tokenizer.ReadString(); + var height = tokenizer.ReadString(); var unit = RelativeUnit.Absolute; var scale = 1.0; diff --git a/src/Avalonia.Visuals/Size.cs b/src/Avalonia.Visuals/Size.cs index ff734146c21..c5eaa33b411 100644 --- a/src/Avalonia.Visuals/Size.cs +++ b/src/Avalonia.Visuals/Size.cs @@ -157,8 +157,8 @@ public static Size Parse(string s, CultureInfo culture) using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Size")) { return new Size( - tokenizer.NextDoubleRequired(), - tokenizer.NextDoubleRequired()); + tokenizer.ReadDouble(), + tokenizer.ReadDouble()); } } diff --git a/src/Avalonia.Visuals/Thickness.cs b/src/Avalonia.Visuals/Thickness.cs index 4609cc3ced7..a5ca0a04a84 100644 --- a/src/Avalonia.Visuals/Thickness.cs +++ b/src/Avalonia.Visuals/Thickness.cs @@ -171,13 +171,13 @@ public static Thickness Parse(string s, CultureInfo culture) { using (var tokenizer = new StringTokenizer(s, culture, exceptionMessage: "Invalid Thickness")) { - var a = tokenizer.NextDoubleRequired(); + var a = tokenizer.ReadDouble(); - if (tokenizer.NextDouble(out var b)) + if (tokenizer.TryReadDouble(out var b)) { - if (tokenizer.NextDouble(out var c)) + if (tokenizer.TryReadDouble(out var c)) { - return new Thickness(a, b, c, tokenizer.NextDoubleRequired()); + return new Thickness(a, b, c, tokenizer.ReadDouble()); } return new Thickness(a, b); From bdc81009613a69a3aa238dcb4132897bc3cb93eb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 18 Apr 2018 02:13:58 +0200 Subject: [PATCH 51/59] Fix failing tests. --- tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs index fb432c30d46..ba68838382c 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs @@ -8,6 +8,7 @@ using Avalonia.Markup.Xaml.Styling; using Avalonia.Markup.Xaml.Templates; using Avalonia.Media; +using Avalonia.Media.Immutable; using Avalonia.Styling; using Avalonia.UnitTests; using System.Collections; @@ -359,8 +360,8 @@ public void Avalonia_TypeConverter_Is_Used() var control = AvaloniaXamlLoader.Parse(xaml); var bk = control.Background; - Assert.IsType(bk); - Assert.Equal(Colors.White, (bk as SolidColorBrush).Color); + Assert.IsType(bk); + Assert.Equal(Colors.White, (bk as ISolidColorBrush).Color); } [Fact] @@ -496,7 +497,7 @@ public void Style_Resources_Are_Built() Assert.NotNull(brush); - Assert.Equal(Colors.White, ((SolidColorBrush)brush).Color); + Assert.Equal(Colors.White, ((ISolidColorBrush)brush).Color); style.TryGetResource("Double", out var d); From bcdc940cd91bed07e1e6e9b16282b6fc9c6a47a6 Mon Sep 17 00:00:00 2001 From: boombuler Date: Wed, 18 Apr 2018 07:10:59 +0200 Subject: [PATCH 52/59] removed unnecessary XmlnsDefinition --- src/Avalonia.Controls/Properties/AssemblyInfo.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Controls/Properties/AssemblyInfo.cs b/src/Avalonia.Controls/Properties/AssemblyInfo.cs index b0877e0fb7f..ae8c88f7e8c 100644 --- a/src/Avalonia.Controls/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Controls/Properties/AssemblyInfo.cs @@ -11,7 +11,6 @@ [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.DragDrop")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Embedding")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Presenters")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")] From ad675ded5efb8a3e1c00e00c33f8de47b894ef2f Mon Sep 17 00:00:00 2001 From: temporaryuser0 <38496811+temporaryuser0@users.noreply.github.com> Date: Wed, 18 Apr 2018 14:04:13 +0200 Subject: [PATCH 53/59] Check if event was handled before in OnTextInput --- src/Avalonia.Controls/TextBox.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 34c6b1cfd60..890926db546 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -275,8 +275,11 @@ protected override void OnLostFocus(RoutedEventArgs e) protected override void OnTextInput(TextInputEventArgs e) { - HandleTextInput(e.Text); - e.Handled = true; + if (!e.Handled) + { + HandleTextInput(e.Text); + e.Handled = true; + } } private void HandleTextInput(string input) From 8b9aace2e451cae83b7cd7b8fdad7a1afad85dd0 Mon Sep 17 00:00:00 2001 From: achal7 Date: Fri, 13 Apr 2018 17:56:25 +0530 Subject: [PATCH 54/59] 1) changed UpdateLastState to call UpdateLastState(TState) so that its the only one that has last update control 2) Clear function clean up the states, however currentNode by definition is depends upon one of the state value, hence made that also null, since there is nothing now to link --- src/Avalonia.Controls/Utils/UndoRedoHelper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs index c76555e5545..17cf681f15f 100644 --- a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs +++ b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs @@ -59,7 +59,7 @@ public void UpdateLastState(TState state) public void UpdateLastState() { - _states.Last.Value = _host.UndoRedoState; + UpdateLastState(_host.UndoRedoState); } public void DiscardRedo() @@ -94,6 +94,7 @@ public void Snapshot() public void Clear() { _states.Clear(); + _currentNode = null; } bool WeakTimer.IWeakTimerSubscriber.Tick() From 802729bd2d123d7d6529381cd31733be85763833 Mon Sep 17 00:00:00 2001 From: achal7 Date: Tue, 17 Apr 2018 02:29:13 +0530 Subject: [PATCH 55/59] Fix of issue #1448 InvalidateFormattedText was doing mainly 2 activities, resets formatted text and resets measure. Splited it and extracted reset of formatted text in to seprate function and using it over there. TextPresenter usis obervable stream for SelectionStart/End property, over there inside subscriber instead of calling InvalidateFormatedText now we use only ClearFormatedText. The reason is SelectionStart/End is not going to make any changes regarding measurement and hence reseting formatted text alone is sufficient enough --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 2 +- src/Avalonia.Controls/TextBlock.cs | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index d2d4151e3dd..4a4a9fa8d17 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -43,7 +43,7 @@ public TextPresenter() Observable.Merge( this.GetObservable(SelectionStartProperty), this.GetObservable(SelectionEndProperty)) - .Subscribe(_ => InvalidateFormattedText()); + .Subscribe(_ => ClearFormattedText()); this.GetObservable(CaretIndexProperty) .Subscribe(CaretIndexChanged); diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index c7a77bdf0ed..cddc317df7f 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -364,14 +364,21 @@ protected virtual FormattedText CreateFormattedText(Size constraint) /// Invalidates . /// protected void InvalidateFormattedText() + { + ClearFormattedText(); + InvalidateMeasure(); + } + + /// + /// Invalidates . + /// + protected void ClearFormattedText() { if (_formattedText != null) { _constraint = _formattedText.Constraint; _formattedText = null; } - - InvalidateMeasure(); } /// From 7623a18a53350fdedbf6af7fffe77138e3ccde49 Mon Sep 17 00:00:00 2001 From: Achal Shah Date: Thu, 19 Apr 2018 00:30:26 +0530 Subject: [PATCH 56/59] Revert "1) changed UpdateLastState to call UpdateLastState(TState) so that its the only one that has last update control" This reverts commit 16ea6a31fcaf69e2ed3702a1c48bc6e076481e01. --- src/Avalonia.Controls/Utils/UndoRedoHelper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs index 17cf681f15f..c76555e5545 100644 --- a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs +++ b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs @@ -59,7 +59,7 @@ public void UpdateLastState(TState state) public void UpdateLastState() { - UpdateLastState(_host.UndoRedoState); + _states.Last.Value = _host.UndoRedoState; } public void DiscardRedo() @@ -94,7 +94,6 @@ public void Snapshot() public void Clear() { _states.Clear(); - _currentNode = null; } bool WeakTimer.IWeakTimerSubscriber.Tick() From 16c2a181b27f8682bf9b9fce2b74b584bc9214e8 Mon Sep 17 00:00:00 2001 From: achal7 Date: Thu, 19 Apr 2018 01:22:48 +0530 Subject: [PATCH 57/59] Revert "Fix of issue #1448" This reverts commit 1ed14986f1a94aef840f0505902c7ef237bb3825. --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 2 +- src/Avalonia.Controls/TextBlock.cs | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 4a4a9fa8d17..d2d4151e3dd 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -43,7 +43,7 @@ public TextPresenter() Observable.Merge( this.GetObservable(SelectionStartProperty), this.GetObservable(SelectionEndProperty)) - .Subscribe(_ => ClearFormattedText()); + .Subscribe(_ => InvalidateFormattedText()); this.GetObservable(CaretIndexProperty) .Subscribe(CaretIndexChanged); diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index cddc317df7f..c7a77bdf0ed 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -364,21 +364,14 @@ protected virtual FormattedText CreateFormattedText(Size constraint) /// Invalidates . /// protected void InvalidateFormattedText() - { - ClearFormattedText(); - InvalidateMeasure(); - } - - /// - /// Invalidates . - /// - protected void ClearFormattedText() { if (_formattedText != null) { _constraint = _formattedText.Constraint; _formattedText = null; } + + InvalidateMeasure(); } /// From a38fe5c9157541cc692e0edd601f53353e4290cc Mon Sep 17 00:00:00 2001 From: achal7 Date: Thu, 19 Apr 2018 02:37:15 +0530 Subject: [PATCH 58/59] Seprated InvalidateMeasure out from InvalidateFormattedText. Whoever needs both the feature, calls then explicitly. In this way TextPresenter's subscriber for SelectionStart/End doent invalidate measure, since it should only focus on invalidate formatted text Fixes #1448 --- src/Avalonia.Controls/TextBlock.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index c7a77bdf0ed..88a9fe077d5 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -120,6 +120,7 @@ public TextBlock() .Subscribe(_ => { InvalidateFormattedText(); + InvalidateMeasure(); }); } @@ -370,8 +371,6 @@ protected void InvalidateFormattedText() _constraint = _formattedText.Constraint; _formattedText = null; } - - InvalidateMeasure(); } /// @@ -402,6 +401,7 @@ protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e { base.OnAttachedToLogicalTree(e); InvalidateFormattedText(); + InvalidateMeasure(); } } } \ No newline at end of file From 648ce074bb57c8eddd9d8cf550d602a76cf1f792 Mon Sep 17 00:00:00 2001 From: Nat Elkins Date: Fri, 20 Apr 2018 16:00:25 -0400 Subject: [PATCH 59/59] Updating readme to reflect beta status --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 96cfde3eb2c..2b26cbdd1a2 100644 --- a/readme.md +++ b/readme.md @@ -35,7 +35,7 @@ https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master/artifacts ## Documentation -As mentioned above, Avalonia is still in alpha and as such there's not much documentation yet. You can take a look at the [getting started page](http://avaloniaui.net/docs/quickstart/) for an overview of how to get started but probably the best thing to do for now is to already know a little bit about WPF/Silverlight/UWP/XAML and ask questions in our [Gitter room](https://gitter.im/AvaloniaUI/Avalonia). +As mentioned above, Avalonia is still in beta and as such there's not much documentation yet. You can take a look at the [getting started page](http://avaloniaui.net/docs/quickstart/) for an overview of how to get started but probably the best thing to do for now is to already know a little bit about WPF/Silverlight/UWP/XAML and ask questions in our [Gitter room](https://gitter.im/AvaloniaUI/Avalonia). There's also a high-level [architecture document](http://avaloniaui.net/architecture/project-structure) that is currently a little bit out of date, and I've also started writing blog posts on Avalonia at http://grokys.github.io/.