From 898e7e4747f0d0b56092c5e7687415753fae8b54 Mon Sep 17 00:00:00 2001 From: Eli Arbel Date: Tue, 22 Aug 2017 09:55:24 +0300 Subject: [PATCH 01/62] 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 037546b186..0b1e778cf0 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 c711553e05..b17dc584bd 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 @@ namespace Avalonia.Controls /// 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 10549b967d..6c7d104680 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 @@ namespace Avalonia /// 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 5fbd082967..49d2a401bf 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 @@ namespace Avalonia /// 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 d562429fc7..11f3db31da 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 @@ namespace Avalonia /// 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 cc34feb5f3..625ee61439 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 @@ namespace Avalonia /// 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 a11f080e94..05e344f42b 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 @@ namespace Avalonia Rect.Width * size.Width, Rect.Height * size.Height); } - + /// /// Parses a string. /// @@ -168,43 +171,43 @@ namespace Avalonia /// 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 6ad87c6120..ff734146c2 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 @@ namespace Avalonia /// 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 dc9be7341d..2a1cf6ac57 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 @@ namespace Avalonia /// 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 0000000000..2f378f44df --- /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 8ba4f3b739..9f25dcd413 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 @@ namespace Avalonia.Visuals.UnitTests 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/62] 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 40ac24b605..abceb39961 100644 --- a/src/Avalonia.Visuals/Media/Brush.cs +++ b/src/Avalonia.Visuals/Media/Brush.cs @@ -34,26 +34,21 @@ namespace Avalonia.Media /// 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 4c89c97b49..83ff043397 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 cbf5a86fd6..82cc19347a 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -88,6 +88,9 @@ namespace Avalonia.Media /// 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 @@ namespace Avalonia.Media 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 @@ namespace Avalonia.Media /// 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 7296d7dd39..1067cf66aa 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 @@ namespace Avalonia.Media /// /// 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 0000000000..0887d2c913 --- /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 ae88a94073..a6015c52e5 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs @@ -12,7 +12,7 @@ namespace Avalonia.Visuals.UnitTests.Media [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 @@ namespace Avalonia.Visuals.UnitTests.Media [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 @@ namespace Avalonia.Visuals.UnitTests.Media [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 @@ namespace Avalonia.Visuals.UnitTests.Media [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 @@ namespace Avalonia.Visuals.UnitTests.Media 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/62] 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 0000000000..f46651ed3b --- /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 0000000000..ffc6f666f7 --- /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 0000000000..b6dc53d32d --- /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 0000000000..5c39635dd0 --- /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 5f6be91571..c31c131ea9 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 0000000000..3abcb8c68c --- /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 667ee12fa0..91e822f802 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 @@ namespace Avalonia.MonoMac 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 @@ namespace Avalonia.MonoMac _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 @@ namespace Avalonia.MonoMac 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/62] 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 06c1a8b4cc..63b17530ff 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -12,6 +12,7 @@ using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.Threading; using System.Reactive.Concurrency; +using Avalonia.Controls.DragDrop; namespace Avalonia { @@ -234,7 +235,8 @@ namespace Avalonia .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 0000000000..2ce5a32dc5 --- /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 0000000000..3645409a5a --- /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 0000000000..e8bd2528c7 --- /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 5c39635dd0..05a211ff76 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 ae8c88f7e8..b0877e0fb7 100644 --- a/src/Avalonia.Controls/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Controls/Properties/AssemblyInfo.cs @@ -11,6 +11,7 @@ using Avalonia.Metadata; [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 91e822f802..711f6d2787 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -201,7 +201,7 @@ namespace Avalonia.MonoMac 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 @@ namespace Avalonia.MonoMac 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/62] 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 3abcb8c68c..8164470548 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 @@ namespace Avalonia.MonoMac 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/62] 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 0000000000..254facf81a --- /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 f13dd3272c..dada2eb7e6 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -5,6 +5,7 @@ using System; 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 @@ namespace Avalonia.Win32.Interop [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 @@ namespace Avalonia.Win32.Interop public enum ClipboardFormat { CF_TEXT = 1, - CF_UNICODETEXT = 13 + CF_UNICODETEXT = 13, + CF_HDROP = 15 } public struct MSG @@ -1300,4 +1324,49 @@ namespace Avalonia.Win32.Interop 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 0000000000..0c597e3d17 --- /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 0000000000..6c86cd03e0 --- /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 0000000000..b0f592403b --- /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 3ba926b42a..2fca0baac7 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -14,6 +14,7 @@ using Avalonia.Platform; 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 @@ namespace Avalonia.Win32 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 @@ namespace Avalonia.Win32 public void SetInputRoot(IInputRoot inputRoot) { _owner = inputRoot; + CreateDropTarget(); } public void SetTitle(string title) @@ -689,6 +692,13 @@ namespace Avalonia.Win32 } } + 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/62] 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 3645409a5a..317a903d3c 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/62] 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 2ce5a32dc5..68fa6cad75 100644 --- a/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs +++ b/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs @@ -23,37 +23,37 @@ namespace Avalonia.Controls.DragDrop 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 @@ namespace Avalonia.Controls.DragDrop } } - 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 ffc6f666f7..cec3dab89d 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 e8bd2528c7..2b6658d395 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 05a211ff76..84c023ddbc 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 8164470548..dccc39e861 100644 --- a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs +++ b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs @@ -17,27 +17,27 @@ namespace Avalonia.MonoMac } - 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 711f6d2787..4398b6bddb 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -201,7 +201,7 @@ namespace Avalonia.MonoMac 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 @@ namespace Avalonia.MonoMac 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 b0f592403b..e3725d3bbf 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -22,27 +22,27 @@ namespace Avalonia.Win32 _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/62] 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 0c597e3d17..085c0f8ea9 100644 --- a/src/Windows/Avalonia.Win32/OleContext.cs +++ b/src/Windows/Avalonia.Win32/OleContext.cs @@ -4,7 +4,7 @@ using Avalonia.Platform; 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 2fca0baac7..ed224a6525 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -14,7 +14,6 @@ using Avalonia.Platform; 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/62] 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 68fa6cad75..2db227248b 100644 --- a/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs +++ b/src/Avalonia.Controls/DragDrop/DefaultDragDispatcher.cs @@ -23,7 +23,7 @@ namespace Avalonia.Controls.DragDrop 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 @@ namespace Avalonia.Controls.DragDrop return args.DragEffects; } - public DragDropEffects DragEnter(IInputElement inputRoot, Point point, IDragData data, DragDropEffects effects) + public DragDropEffects DragEnter(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects) { _lastTarget = GetTarget(inputRoot, point); return RaiseDragEvent(_lastTarget, DragDrop.DragEnterEvent, effects, data); } - public DragDropEffects DragOver(IInputElement inputRoot, Point point, IDragData data, DragDropEffects effects) + public DragDropEffects DragOver(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects) { var target = GetTarget(inputRoot, point); @@ -75,7 +75,7 @@ namespace Avalonia.Controls.DragDrop } } - public DragDropEffects Drop(IInputElement inputRoot, Point point, IDragData data, DragDropEffects effects) + public DragDropEffects Drop(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects) { try { diff --git a/src/Avalonia.Controls/DragDrop/DragEventArgs.cs b/src/Avalonia.Controls/DragDrop/DragEventArgs.cs index 2b6658d395..f31bfd4820 100644 --- a/src/Avalonia.Controls/DragDrop/DragEventArgs.cs +++ b/src/Avalonia.Controls/DragDrop/DragEventArgs.cs @@ -6,9 +6,9 @@ namespace Avalonia.Controls.DragDrop { public DragDropEffects DragEffects { get; set; } - public IDragData Data { get; private set; } + public IDataObject Data { get; private set; } - public DragEventArgs(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 b6dc53d32d..5ffecc13ed 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 84c023ddbc..538ffa171b 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 dccc39e861..ef4d9d99f6 100644 --- a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs +++ b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs @@ -7,7 +7,7 @@ using MonoMac.Foundation; 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 dada2eb7e6..f43cdb98cc 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1335,25 +1335,7 @@ namespace Avalonia.Win32.Interop 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 @@ namespace Avalonia.Win32.Interop 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 6c86cd03e0..bf6f3d98c3 100644 --- a/src/Windows/Avalonia.Win32/OleDataObject.cs +++ b/src/Windows/Avalonia.Win32/OleDataObject.cs @@ -6,14 +6,15 @@ using System.Runtime.InteropServices.ComTypes; 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 e3725d3bbf..925f9b7046 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 @@ namespace Avalonia.Win32 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/62] 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 63b17530ff..b751aeca60 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -13,6 +13,7 @@ using Avalonia.Styling; using Avalonia.Threading; using System.Reactive.Concurrency; using Avalonia.Controls.DragDrop; +using Avalonia.Controls.DragDrop.Raw; namespace Avalonia { @@ -236,7 +237,7 @@ namespace Avalonia .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 2db227248b..25f82ca3bc 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 @@ namespace Avalonia.Controls.DragDrop return args.DragEffects; } - public DragDropEffects DragEnter(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects) + private DragDropEffects DragEnter(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects) { _lastTarget = GetTarget(inputRoot, point); return RaiseDragEvent(_lastTarget, DragDrop.DragEnterEvent, effects, data); } - public DragDropEffects DragOver(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects) + private DragDropEffects DragOver(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects) { var target = GetTarget(inputRoot, point); @@ -61,7 +59,7 @@ namespace Avalonia.Controls.DragDrop } } - public void DragLeave(IInputElement inputRoot) + private void DragLeave(IInputElement inputRoot) { if (_lastTarget == null) return; @@ -75,7 +73,7 @@ namespace Avalonia.Controls.DragDrop } } - public DragDropEffects Drop(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects) + private DragDropEffects Drop(IInputElement inputRoot, Point point, IDataObject data, DragDropEffects effects) { try { @@ -86,5 +84,30 @@ namespace Avalonia.Controls.DragDrop _lastTarget = null; } } + + public void ProcessRawEvent(RawInputEventArgs e) + { + if (!e.Handled && e is RawDragEvent margs) + ProcessRawEvent(margs); + } + + private void ProcessRawEvent(RawDragEvent e) + { + switch (e.Type) + { + case RawDragEventType.DragEnter: + e.Effects = DragEnter(e.InputRoot, e.Location, e.Data, e.Effects); + break; + case RawDragEventType.DragOver: + e.Effects = DragOver(e.InputRoot, e.Location, e.Data, e.Effects); + break; + case RawDragEventType.DragLeave: + DragLeave(e.InputRoot); + break; + case RawDragEventType.Drop: + e.Effects = Drop(e.InputRoot, e.Location, e.Data, e.Effects); + break; + } + } } } \ No newline at end of file diff --git a/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs b/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs deleted file mode 100644 index 538ffa171b..0000000000 --- a/src/Avalonia.Controls/DragDrop/IDragDispatcher.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Avalonia.Input; - -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 - { - 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 0000000000..d1c20fae55 --- /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 0000000000..76a2c16b3b --- /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 0000000000..44c4bbd595 --- /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 4398b6bddb..b767a86490 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 @@ namespace Avalonia.MonoMac { 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 @@ namespace Avalonia.MonoMac 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 @@ namespace Avalonia.MonoMac _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 @@ namespace Avalonia.MonoMac 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 925f9b7046..c16c5058f3 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 @@ namespace Avalonia.Win32 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 @@ namespace Avalonia.Win32 { 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 @@ namespace Avalonia.Win32 { 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 ed224a6525..85e5b5b4b9 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -693,7 +693,7 @@ namespace Avalonia.Win32 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/62] 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 5ffecc13ed..c8fd720c48 100644 --- a/src/Avalonia.Controls/DragDrop/IDataObject.cs +++ b/src/Avalonia.Controls/DragDrop/IDataObject.cs @@ -11,5 +11,7 @@ namespace Avalonia.Controls.DragDrop 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 ef4d9d99f6..2fb28a50f5 100644 --- a/src/OSX/Avalonia.MonoMac/DraggingInfo.cs +++ b/src/OSX/Avalonia.MonoMac/DraggingInfo.cs @@ -15,8 +15,7 @@ namespace Avalonia.MonoMac { _info = info; } - - + internal static NSDragOperation ConvertDragOperation(DragDropEffects d) { NSDragOperation result = NSDragOperation.None; @@ -77,5 +76,15 @@ namespace Avalonia.MonoMac { 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 254facf81a..a59c6a1664 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 @@ namespace Avalonia.Win32 { 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 0000000000..6248afd7bb --- /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 f43cdb98cc..aa86ab0f8d 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -973,7 +973,11 @@ namespace Avalonia.Win32.Interop [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 @@ namespace Avalonia.Win32.Interop 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 @@ namespace Avalonia.Win32.Interop 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 @@ namespace Avalonia.Win32.Interop [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 bf6f3d98c3..fe6faa162c 100644 --- a/src/Windows/Avalonia.Win32/OleDataObject.cs +++ b/src/Windows/Avalonia.Win32/OleDataObject.cs @@ -39,6 +39,11 @@ namespace Avalonia.Win32 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 0000000000..1bff984087 --- /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 c16c5058f3..6bc298951e 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -55,8 +55,9 @@ namespace Avalonia.Win32 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 @@ namespace Avalonia.Win32 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/62] 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} (60%) 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 25f82ca3bc..0aadda79f4 100644 --- a/src/Avalonia.Controls/DragDrop/DragDevice.cs +++ b/src/Avalonia.Controls/DragDrop/DragDropDevice.cs @@ -7,9 +7,9 @@ using Avalonia.Input.Raw; 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 60% rename from src/Avalonia.Controls/DragDrop/Raw/IDragDevice.cs rename to src/Avalonia.Controls/DragDrop/Raw/IDragDropDevice.cs index d1c20fae55..6e2db67ac3 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 76a2c16b3b..e9c1119e44 100644 --- a/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs +++ b/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs @@ -12,7 +12,7 @@ namespace Avalonia.Controls.DragDrop.Raw 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 b767a86490..20d255a608 100644 --- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs +++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs @@ -20,7 +20,7 @@ namespace Avalonia.MonoMac { 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 @@ namespace Avalonia.MonoMac 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 6bc298951e..31db5f88a9 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -12,13 +12,13 @@ namespace Avalonia.Win32 { 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/62] 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 b751aeca60..0a9bf4eab2 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -237,7 +237,7 @@ namespace Avalonia .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/62] 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 b151cabf43..a2048005a4 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 346535d39d..b45a93455e 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/62] 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 0000000000..e0e7b6d069 --- /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 317a903d3c..af003516b1 100644 --- a/src/Avalonia.Controls/DragDrop/DragDrop.cs +++ b/src/Avalonia.Controls/DragDrop/DragDrop.cs @@ -20,5 +20,15 @@ namespace Avalonia.Controls.DragDrop { 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/62] 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 0a9bf4eab2..6ed797dc51 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -14,6 +14,7 @@ using Avalonia.Threading; using System.Reactive.Concurrency; using Avalonia.Controls.DragDrop; using Avalonia.Controls.DragDrop.Raw; +using Avalonia.Controls.Platform; namespace Avalonia { @@ -237,7 +238,8 @@ namespace Avalonia .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 af003516b1..bbac80e38f 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 @@ namespace Avalonia.Controls.DragDrop /// 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 0000000000..f899eabfbd --- /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 0000000000..667e1f005c --- /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 0000000000..937471f9ad --- /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 1bff984087..522014abc0 100644 --- a/src/Windows/Avalonia.Win32/OleDragSource.cs +++ b/src/Windows/Avalonia.Win32/OleDragSource.cs @@ -33,9 +33,7 @@ namespace Avalonia.Win32 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 31db5f88a9..747006673a 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -23,7 +23,7 @@ namespace Avalonia.Win32 _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 @@ namespace Avalonia.Win32 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 4e1ba618a8..ef1c4e987d 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -84,7 +84,8 @@ namespace Avalonia.Win32 .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/62] 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 f899eabfbd..2048a0c0da 100644 --- a/src/Avalonia.Controls/DragDrop/DragSource.cs +++ b/src/Avalonia.Controls/DragDrop/DragSource.cs @@ -23,8 +23,9 @@ namespace Avalonia.Controls.DragDrop 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 @@ namespace Avalonia.Controls.DragDrop 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 @@ namespace Avalonia.Controls.DragDrop { 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 @@ namespace Avalonia.Controls.DragDrop 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/62] 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 e3860e58e5..02a026c998 100644 --- a/src/Avalonia.Input/Cursors.cs +++ b/src/Avalonia.Input/Cursors.cs @@ -38,7 +38,10 @@ namespace Avalonia.Input 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 ac547b8bc2..d6a3c1f260 100644 --- a/src/Gtk/Avalonia.Gtk3/CursorFactory.cs +++ b/src/Gtk/Avalonia.Gtk3/CursorFactory.cs @@ -32,7 +32,10 @@ namespace Avalonia.Gtk3 {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 10445e62e2..d9370e527b 100644 --- a/src/OSX/Avalonia.MonoMac/Cursor.cs +++ b/src/OSX/Avalonia.MonoMac/Cursor.cs @@ -51,6 +51,10 @@ namespace Avalonia.MonoMac [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 0d529d6b91..fa2fbe4810 100644 --- a/src/Windows/Avalonia.Win32/CursorFactory.cs +++ b/src/Windows/Avalonia.Win32/CursorFactory.cs @@ -9,6 +9,7 @@ using System.Text; using System.Threading.Tasks; using Avalonia.Input; using Avalonia.Platform; +using System.Runtime.InteropServices; namespace Avalonia.Win32 { @@ -20,6 +21,27 @@ namespace Avalonia.Win32 { } + 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 @@ namespace Avalonia.Win32 //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/62] 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 2048a0c0da..fd3d8ef143 100644 --- a/src/Avalonia.Controls/DragDrop/DragSource.cs +++ b/src/Avalonia.Controls/DragDrop/DragSource.cs @@ -19,13 +19,14 @@ namespace Avalonia.Controls.DragDrop 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 @@ namespace Avalonia.Controls.DragDrop { _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 @@ namespace Avalonia.Controls.DragDrop 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 @@ namespace Avalonia.Controls.DragDrop 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/62] 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 6248afd7bb..aa3ee91ef9 100644 --- a/src/Windows/Avalonia.Win32/DataObject.cs +++ b/src/Windows/Avalonia.Win32/DataObject.cs @@ -8,11 +8,16 @@ using Avalonia.Controls.DragDrop; 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 @@ namespace Avalonia.Win32 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 fe6faa162c..60527bb7d6 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 @@ namespace Avalonia.Win32 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 @@ namespace Avalonia.Win32 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/62] 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 fd3d8ef143..ceb962ad98 100644 --- a/src/Avalonia.Controls/DragDrop/DragSource.cs +++ b/src/Avalonia.Controls/DragDrop/DragSource.cs @@ -25,7 +25,8 @@ namespace Avalonia.Controls.DragDrop 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 @@ namespace Avalonia.Controls.DragDrop 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 @@ namespace Avalonia.Controls.DragDrop { 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/62] 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 0000000000..ea1739e33a --- /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 5907459459..3db02de209 100644 --- a/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs +++ b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs @@ -35,7 +35,8 @@ namespace Avalonia.MonoMac .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/62] 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 ea1739e33a..77bbee86f1 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 @@ namespace Avalonia.MonoMac 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/62] 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 77bbee86f1..2275c7505f 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 @@ namespace Avalonia.MonoMac 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 @@ namespace Avalonia.MonoMac private NSDraggingItem CreateDraggingItem(string format, object data) { - var pasteboardItem = new NSPasteboardItem(); NSData nsData; if(data is string s) @@ -72,6 +73,19 @@ namespace Avalonia.MonoMac 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 @@ namespace Avalonia.MonoMac 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/62] 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 2275c7505f..07f2e186aa 100644 --- a/src/OSX/Avalonia.MonoMac/DragSource.cs +++ b/src/OSX/Avalonia.MonoMac/DragSource.cs @@ -51,8 +51,12 @@ namespace Avalonia.MonoMac { 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/62] 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 c76a5b5da5..b2b5ecdf8f 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -103,7 +103,7 @@ namespace Avalonia.Direct2D1 /// 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 @@ namespace Avalonia.Direct2D1 /// 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/62] 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 b2b5ecdf8f..959bf118f0 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -132,7 +132,7 @@ namespace Avalonia.Direct2D1 { 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/62] 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 959bf118f0..56d0624f52 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -127,13 +127,21 @@ namespace Avalonia.Direct2D1 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/62] 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 e9c1119e44..c2bc1d5712 100644 --- a/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs +++ b/src/Avalonia.Controls/DragDrop/Raw/RawDragEvent.cs @@ -14,7 +14,7 @@ namespace Avalonia.Controls.DragDrop.Raw 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 @@ namespace Avalonia.Controls.DragDrop.Raw 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/62] 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 (79%) rename src/{Avalonia.Controls => Avalonia.Input}/DragDrop/DragEventArgs.cs (90%) rename src/{Avalonia.Controls => Avalonia.Input}/DragDrop/IDataObject.cs (88%) rename src/{Avalonia.Controls => Avalonia.Input}/DragDrop/Raw/IDragDropDevice.cs (68%) 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 6ed797dc51..f94a85f31d 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -12,9 +12,10 @@ using Avalonia.Rendering; 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 ceb962ad98..ed873ec961 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 f46651ed3b..4115701478 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 e0e7b6d069..0db1008a6c 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 bbac80e38f..9d68deb62b 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 0aadda79f4..0a64800346 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 79% rename from src/Avalonia.Controls/DragDrop/DragDropEffects.cs rename to src/Avalonia.Input/DragDrop/DragDropEffects.cs index cec3dab89d..7f093bc89c 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 90% rename from src/Avalonia.Controls/DragDrop/DragEventArgs.cs rename to src/Avalonia.Input/DragDrop/DragEventArgs.cs index f31bfd4820..bff19c760c 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 c8fd720c48..5d26a20cee 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 68% rename from src/Avalonia.Controls/DragDrop/Raw/IDragDropDevice.cs rename to src/Avalonia.Input/DragDrop/Raw/IDragDropDevice.cs index 6e2db67ac3..2022e842ae 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 c2bc1d5712..af18186d4b 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 44c4bbd595..31450efb51 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 667e1f005c..44b4d30760 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 07f2e186aa..96d650ab3a 100644 --- a/src/OSX/Avalonia.MonoMac/DragSource.cs +++ b/src/OSX/Avalonia.MonoMac/DragSource.cs @@ -10,8 +10,8 @@ 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.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 2fb28a50f5..ca8a24ba82 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 97c4302c57..34d0949b57 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 a59c6a1664..a604f634f6 100644 --- a/src/Windows/Avalonia.Win32/ClipboardFormats.cs +++ b/src/Windows/Avalonia.Win32/ClipboardFormats.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; 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 aa3ee91ef9..2a990419e7 100644 --- a/src/Windows/Avalonia.Win32/DataObject.cs +++ b/src/Windows/Avalonia.Win32/DataObject.cs @@ -4,9 +4,9 @@ using System.Collections.Generic; 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 937471f9ad..81dd790066 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 60527bb7d6..ee1c3046eb 100644 --- a/src/Windows/Avalonia.Win32/OleDataObject.cs +++ b/src/Windows/Avalonia.Win32/OleDataObject.cs @@ -6,13 +6,13 @@ using System.Runtime.InteropServices; 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 747006673a..e3791747b1 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 ef1c4e987d..902abaf65b 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -84,8 +84,10 @@ namespace Avalonia.Win32 .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/62] 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 862de9d320..08c9a48e42 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 fedae81cb884e3fc9b595c73e633ad44a4757575 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 11 Apr 2018 12:08:33 +0100 Subject: [PATCH 33/62] partial fix for 1436. --- src/Avalonia.Controls/Border.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index 06ad8a4837..2dc42bd923 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -43,7 +43,8 @@ namespace Avalonia.Controls /// static Border() { - AffectsRender(BackgroundProperty, BorderBrushProperty); + AffectsRender(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty); + AffectsMeasure(BorderThicknessProperty); } /// From d1b06cb7844b64fd44a56e03c6794fa78cee3da0 Mon Sep 17 00:00:00 2001 From: lindexi Date: Thu, 12 Apr 2018 09:03:03 +0800 Subject: [PATCH 34/62] 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 56d0624f52..caff1e9e0f 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -132,7 +132,6 @@ namespace Avalonia.Direct2D1 { 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 35/62] 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 caff1e9e0f..a0fd3c0172 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -136,10 +136,7 @@ namespace Avalonia.Direct2D1 } //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 36/62] 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 862de9d320..4fb0ff2059 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 a0e0df450b..3a19d8e8ed 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 0000000000..8469ac480d --- /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 0000000000..08aca56bad --- /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 5cd3b22fc9..b8817d28f3 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -215,7 +215,17 @@ namespace Avalonia.Controls.Primitives { 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 @@ namespace Avalonia.Controls.Primitives Opened?.Invoke(this, EventArgs.Empty); } - + /// /// Closes the popup. /// @@ -244,6 +254,14 @@ namespace Avalonia.Controls.Primitives 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 @@ namespace Avalonia.Controls.Primitives { 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 @@ namespace Avalonia.Controls.Primitives 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 37/62] 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 08c9a48e42..63b0f4a961 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 38/62] 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 30c069d7d8..0000000000 --- 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 27680c35e9355abe3512890e40d36cd6eebb1a28 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 12 Apr 2018 23:25:26 +0100 Subject: [PATCH 39/62] [Border] CornerRadius affects render. --- src/Avalonia.Controls/Border.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index 2dc42bd923..d90f292183 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -43,7 +43,7 @@ namespace Avalonia.Controls /// static Border() { - AffectsRender(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty); + AffectsRender(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty); AffectsMeasure(BorderThicknessProperty); } From 653372009aa77e6e05d84ac885cd48106790cc89 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 12 Apr 2018 23:26:06 +0100 Subject: [PATCH 40/62] [ContentPresenter] BorderThickness affects render and measure. --- src/Avalonia.Controls/Presenters/ContentPresenter.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 6acbc047ae..d2fb838657 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -92,6 +92,9 @@ namespace Avalonia.Controls.Presenters ContentProperty.Changed.AddClassHandler(x => x.ContentChanged); ContentTemplateProperty.Changed.AddClassHandler(x => x.ContentChanged); TemplatedParentProperty.Changed.AddClassHandler(x => x.TemplatedParentChanged); + + AffectsRender(BorderThicknessProperty); + AffectsMeasure(BorderThicknessProperty); } /// From f03d0e9a4fe5ed570b97d42aadcba54716dedd2e Mon Sep 17 00:00:00 2001 From: lindexi Date: Fri, 13 Apr 2018 08:54:05 +0800 Subject: [PATCH 41/62] 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 a0fd3c0172..124e33c5a3 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -127,15 +127,14 @@ namespace Avalonia.Direct2D1 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 42/62] 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 f94a85f31d..3dc07f81e6 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -240,7 +240,7 @@ namespace Avalonia .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 ed873ec961..1e97c7bf96 100644 --- a/src/Avalonia.Controls/Platform/DragSource.cs +++ b/src/Avalonia.Controls/Platform/InProcessDragSource.cs @@ -14,7 +14,7 @@ using Avalonia.VisualTree; 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 @@ namespace Avalonia.Platform 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 43/62] 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 9d68deb62b..0cbbfb1dbe 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 0a64800346..7ef6f212f8 100644 --- a/src/Avalonia.Input/DragDrop/DragDropDevice.cs +++ b/src/Avalonia.Input/DragDrop/DragDropDevice.cs @@ -15,7 +15,7 @@ namespace Avalonia.Input.DragDrop 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 5d26a20cee..bf2d00f529 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 44/62] 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 862de9d320..5cdf5c59de 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 a0e0df450b..1107d34b3e 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 0000000000..172331204f --- /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 0000000000..af679d2f9a --- /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 45/62] 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 3db02de209..ba45ad8403 100644 --- a/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs +++ b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs @@ -36,7 +36,7 @@ namespace Avalonia.MonoMac .Bind().ToSingleton() .Bind().ToConstant(s_renderLoop) .Bind().ToConstant(PlatformThreadingInterface.Instance) - .Bind().ToTransient(); + /*.Bind().ToTransient()*/; } public static void Initialize() From 2d1ab0a106f180f154ad5d680320f60950eaf19f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 13 Apr 2018 14:36:46 +0200 Subject: [PATCH 46/62] Only update clip bounds when ClipToBounds = true. Fixes #1436 --- .../Rendering/SceneGraph/SceneBuilder.cs | 4 +- .../Rendering/SceneGraph/SceneBuilderTests.cs | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 6005ee8b8f..799380cb85 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -167,7 +167,9 @@ namespace Avalonia.Rendering.SceneGraph using (context.PushPostTransform(m)) using (context.PushTransformContainer()) { - var clipBounds = bounds.TransformToAABB(contextImpl.Transform).Intersect(clip); + var clipBounds = clipToBounds ? + bounds.TransformToAABB(contextImpl.Transform).Intersect(clip) : + clip; forceRecurse = forceRecurse || node.ClipBounds != clipBounds || diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs index 2ada7bdbba..df4584518e 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs @@ -83,6 +83,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph Margin = new Thickness(10, 20, 30, 40), Child = canvas = new Canvas { + ClipToBounds = true, Background = Brushes.AliceBlue, } } @@ -129,6 +130,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph (border = new Border { Background = Brushes.AliceBlue, + ClipToBounds = true, Width = 100, Height = 100, [Canvas.LeftProperty] = 50, @@ -173,6 +175,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph (border = new Border { Background = Brushes.AliceBlue, + ClipToBounds = true, Width = 100, Height = 100, [Canvas.LeftProperty] = 50, @@ -254,6 +257,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph Margin = new Thickness(24, 26), Child = target = new Border { + ClipToBounds = true, Margin = new Thickness(26, 24), Width = 100, Height = 100, @@ -515,6 +519,50 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph } } + [Fact] + public void Should_Update_ClipBounds_For_Negative_Margin() + { + using (TestApplication()) + { + Decorator decorator; + Border border; + var tree = new TestRoot + { + Width = 100, + Height = 100, + Child = decorator = new Decorator + { + Margin = new Thickness(0, 10, 0, 0), + Child = border = new Border + { + Background = Brushes.Red, + ClipToBounds = true, + Margin = new Thickness(0, -5, 0, 0), + } + } + }; + + var layout = AvaloniaLocator.Current.GetService(); + layout.ExecuteInitialLayoutPass(tree); + + var scene = new Scene(tree); + var sceneBuilder = new SceneBuilder(); + sceneBuilder.UpdateAll(scene); + + var borderNode = scene.FindNode(border); + Assert.Equal(new Rect(0, 5, 100, 95), borderNode.ClipBounds); + + border.Margin = new Thickness(0, -8, 0, 0); + layout.ExecuteLayoutPass(); + + scene = scene.CloneScene(); + sceneBuilder.Update(scene, border); + + borderNode = scene.FindNode(border); + Assert.Equal(new Rect(0, 2, 100, 98), borderNode.ClipBounds); + } + } + [Fact] public void Should_Update_Descendent_Tranform_When_Margin_Changed() { From 67003577a0dc9c7aac2326c527f001d075ef9823 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 13 Apr 2018 13:40:56 +0100 Subject: [PATCH 47/62] Add failing unit tests for border and content presenter. --- .../BorderTests.cs | 23 +++++++++++++++++++ .../ContentPresenterTests_Layout.cs | 23 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/BorderTests.cs b/tests/Avalonia.Controls.UnitTests/BorderTests.cs index 3660a7b4ca..4f58e5e780 100644 --- a/tests/Avalonia.Controls.UnitTests/BorderTests.cs +++ b/tests/Avalonia.Controls.UnitTests/BorderTests.cs @@ -20,5 +20,28 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new Size(20, 20), target.DesiredSize); } + + [Fact] + public void Child_Should_Arrange_With_Zero_Height_Width_If_Padding_Greater_Than_Child_Size() + { + Border content; + + var target = new Border + { + Padding = new Thickness(6), + MaxHeight = 12, + MaxWidth = 12, + Content = content = new Border + { + Height = 0, + Width = 0 + } + }; + + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + + Assert.Equal(new Rect(0, 0, 0, 0), content.Bounds); + } } } diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs index b3c617c4ab..d88716279a 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs @@ -210,5 +210,28 @@ namespace Avalonia.Controls.UnitTests.Presenters Assert.Equal(new Rect(84, 0, 16, 16), content.Bounds); } + + [Fact] + public void Child_Arrange_With_Zero_Height_When_Padding_Height_Greater_Than_Child_Height() + { + Border content; + var target = new ContentPresenter + { + Padding = 32, + MaxHeight = 32, + MaxWidth = 32, + Content = content = new Border + { + MinWidth = 16, + MinHeight = 16, + }, + }; + + target.UpdateChild(); + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + + Assert.Equal(new Rect(0, 0, 0, 0), content.Bounds); + } } } \ No newline at end of file From a98d289af8abcf50e3b29bdd589b92a6e2ccd72a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 13 Apr 2018 13:42:36 +0100 Subject: [PATCH 48/62] Add arrange fix for border and content presenter. --- src/Avalonia.Controls/Border.cs | 3 ++- src/Avalonia.Controls/Presenters/ContentPresenter.cs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index 06ad8a4837..6bceba3401 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -111,7 +111,8 @@ namespace Avalonia.Controls if (Child != null) { var padding = Padding + BorderThickness; - Child.Arrange(new Rect(finalSize).Deflate(padding)); + var arrangeRect = new Rect(finalSize).Deflate(padding); + Child.Arrange(new Rect(arrangeRect.X, arrangeRect.Y, Math.Max(0, arrangeRect.Width), Math.Max(0, arrangeRect.Height))); } _borderRenderHelper.Update(finalSize, BorderThickness, CornerRadius); diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 6acbc047ae..1c5883d980 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -386,7 +386,7 @@ namespace Avalonia.Controls.Presenters if (verticalContentAlignment != VerticalAlignment.Stretch) { - size = size.WithHeight(Math.Min(size.Height, DesiredSize.Height - padding.Top - padding.Bottom)); + size = size.WithHeight(Math.Min(size.Height, DesiredSize.Height - padding.Top - padding.Bottom)); } if (useLayoutRounding) @@ -425,7 +425,7 @@ namespace Avalonia.Controls.Presenters originY = Math.Floor(originY * scale) / scale; } - Child.Arrange(new Rect(originX, originY, size.Width, size.Height)); + Child.Arrange(new Rect(originX, originY, Math.Max(0, size.Width), Math.Max(0, size.Height))); return finalSize; } From 2c9f3ede54cf7aca8c2fdd8b1e1af437c81aa805 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 13 Apr 2018 14:12:51 +0100 Subject: [PATCH 49/62] fix tests and and fixes. --- src/Avalonia.Controls/Border.cs | 5 ++--- tests/Avalonia.Controls.UnitTests/BorderTests.cs | 7 +++---- .../Presenters/ContentPresenterTests_Layout.cs | 12 +++++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index 6bceba3401..e6846978dc 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -110,9 +110,8 @@ namespace Avalonia.Controls { if (Child != null) { - var padding = Padding + BorderThickness; - var arrangeRect = new Rect(finalSize).Deflate(padding); - Child.Arrange(new Rect(arrangeRect.X, arrangeRect.Y, Math.Max(0, arrangeRect.Width), Math.Max(0, arrangeRect.Height))); + var padding = Padding + BorderThickness; + Child.Arrange(new Rect(finalSize).Deflate(padding)); } _borderRenderHelper.Update(finalSize, BorderThickness, CornerRadius); diff --git a/tests/Avalonia.Controls.UnitTests/BorderTests.cs b/tests/Avalonia.Controls.UnitTests/BorderTests.cs index 4f58e5e780..9d6c838bc3 100644 --- a/tests/Avalonia.Controls.UnitTests/BorderTests.cs +++ b/tests/Avalonia.Controls.UnitTests/BorderTests.cs @@ -31,17 +31,16 @@ namespace Avalonia.Controls.UnitTests Padding = new Thickness(6), MaxHeight = 12, MaxWidth = 12, - Content = content = new Border + Child = content = new Border { Height = 0, Width = 0 } }; - - target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); - Assert.Equal(new Rect(0, 0, 0, 0), content.Bounds); + Assert.Equal(new Rect(6, 6, 0, 0), content.Bounds); } } } diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs index d88716279a..2c1074aa9a 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs @@ -217,21 +217,23 @@ namespace Avalonia.Controls.UnitTests.Presenters Border content; var target = new ContentPresenter { - Padding = 32, + Padding = new Thickness(32), MaxHeight = 32, MaxWidth = 32, + HorizontalContentAlignment = HorizontalAlignment.Center, + VerticalContentAlignment = VerticalAlignment.Center, Content = content = new Border { - MinWidth = 16, - MinHeight = 16, + Height = 0, + Width = 0, }, }; target.UpdateChild(); - target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); - Assert.Equal(new Rect(0, 0, 0, 0), content.Bounds); + Assert.Equal(new Rect(48, 48, 0, 0), content.Bounds); } } } \ No newline at end of file From 8d07861e5976fa1930d2da40271031019b242d05 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 13 Apr 2018 14:14:24 +0100 Subject: [PATCH 50/62] white space. --- src/Avalonia.Controls/Border.cs | 4 ++-- src/Avalonia.Controls/Presenters/ContentPresenter.cs | 4 ++-- tests/Avalonia.Controls.UnitTests/BorderTests.cs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index e6846978dc..93a6c203c9 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -110,11 +110,11 @@ namespace Avalonia.Controls { if (Child != null) { - var padding = Padding + BorderThickness; + var padding = Padding + BorderThickness; Child.Arrange(new Rect(finalSize).Deflate(padding)); } - _borderRenderHelper.Update(finalSize, BorderThickness, CornerRadius); + _borderRenderHelper.Update(finalSize, BorderThickness, CornerRadius); return finalSize; } diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 1c5883d980..ce8fe52c22 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -383,10 +383,10 @@ namespace Avalonia.Controls.Presenters { size = size.WithWidth(Math.Min(size.Width, DesiredSize.Width - padding.Left - padding.Right)); } - + if (verticalContentAlignment != VerticalAlignment.Stretch) { - size = size.WithHeight(Math.Min(size.Height, DesiredSize.Height - padding.Top - padding.Bottom)); + size = size.WithHeight(Math.Min(size.Height, DesiredSize.Height - padding.Top - padding.Bottom)); } if (useLayoutRounding) diff --git a/tests/Avalonia.Controls.UnitTests/BorderTests.cs b/tests/Avalonia.Controls.UnitTests/BorderTests.cs index 9d6c838bc3..9a6a041ec7 100644 --- a/tests/Avalonia.Controls.UnitTests/BorderTests.cs +++ b/tests/Avalonia.Controls.UnitTests/BorderTests.cs @@ -33,11 +33,11 @@ namespace Avalonia.Controls.UnitTests MaxWidth = 12, Child = content = new Border { - Height = 0, - Width = 0 + Height = 0, + Width = 0 } }; - + target.Arrange(new Rect(0, 0, 100, 100)); Assert.Equal(new Rect(6, 6, 0, 0), content.Bounds); From 4ca13f2e3f7bde9e2735063318701365fb8362c4 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 13 Apr 2018 15:25:52 +0200 Subject: [PATCH 51/62] Added additional invalidation properties... ...to `ContentPresenter` --- src/Avalonia.Controls/Presenters/ContentPresenter.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index d2fb838657..408e777ef8 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -89,12 +89,11 @@ namespace Avalonia.Controls.Presenters /// static ContentPresenter() { + AffectsRender(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty); + AffectsMeasure(BorderThicknessProperty); ContentProperty.Changed.AddClassHandler(x => x.ContentChanged); ContentTemplateProperty.Changed.AddClassHandler(x => x.ContentChanged); TemplatedParentProperty.Changed.AddClassHandler(x => x.TemplatedParentChanged); - - AffectsRender(BorderThicknessProperty); - AffectsMeasure(BorderThicknessProperty); } /// From 195c2bb57a696d7e8d600d43b6b17a13cd620863 Mon Sep 17 00:00:00 2001 From: factormystic Date: Fri, 13 Apr 2018 17:47:56 -0400 Subject: [PATCH 52/62] Fix broken quickstart link --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index eeee39dabe..96cfde3eb2 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/guides/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 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). 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/. From 3b2012dcd8f1babd4b96490d5a1d49e88d98ad8c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 15 Mar 2018 23:16:05 +0100 Subject: [PATCH 53/62] 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 2d3f978462..555a0b2354 100644 --- a/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs +++ b/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs @@ -86,7 +86,7 @@ namespace Avalonia.Diagnostics.ViewModels 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 54/62] 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 4fb0ff2059..862de9d320 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 3a19d8e8ed..a0e0df450b 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 8469ac480d..0000000000 --- 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 08aca56bad..0000000000 --- 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 55/62] 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 56/62] 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 (89%) 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 (80%) 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 (94%) rename src/Avalonia.Input/{DragDrop => }/Raw/RawDragEventType.cs (73%) diff --git a/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs b/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs index 172331204f..718f21314e 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 3dc07f81e6..6fdca557eb 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 1e97c7bf96..e136efe2a9 100644 --- a/src/Avalonia.Controls/Platform/InProcessDragSource.cs +++ b/src/Avalonia.Controls/Platform/InProcessDragSource.cs @@ -5,8 +5,6 @@ using System.Reactive.Subjects; 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 89% rename from src/Avalonia.Input/DragDrop/DataFormats.cs rename to src/Avalonia.Input/DataFormats.cs index 4115701478..559d2cb643 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 0db1008a6c..cb642f4d65 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 0cbbfb1dbe..c312732afa 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 7ef6f212f8..2615e3a212 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 80% rename from src/Avalonia.Input/DragDrop/DragDropEffects.cs rename to src/Avalonia.Input/DragDropEffects.cs index 7f093bc89c..bcda1091d5 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 bff19c760c..12d5a8941e 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 bf2d00f529..1b12323d99 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 44b4d30760..669251c50b 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 2022e842ae..6aab1b868d 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 94% rename from src/Avalonia.Input/DragDrop/Raw/RawDragEvent.cs rename to src/Avalonia.Input/Raw/RawDragEvent.cs index af18186d4b..49125b4c07 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 73% rename from src/Avalonia.Input/DragDrop/Raw/RawDragEventType.cs rename to src/Avalonia.Input/Raw/RawDragEventType.cs index 31450efb51..9635f77467 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 96d650ab3a..41a206b580 100644 --- a/src/OSX/Avalonia.MonoMac/DragSource.cs +++ b/src/OSX/Avalonia.MonoMac/DragSource.cs @@ -10,9 +10,8 @@ using System.Runtime.InteropServices; 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 ca8a24ba82..fc5f52713e 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 34d0949b57..db7f29f05b 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 a604f634f6..5e0bbab975 100644 --- a/src/Windows/Avalonia.Win32/ClipboardFormats.cs +++ b/src/Windows/Avalonia.Win32/ClipboardFormats.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; 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 2a990419e7..34867765e5 100644 --- a/src/Windows/Avalonia.Win32/DataObject.cs +++ b/src/Windows/Avalonia.Win32/DataObject.cs @@ -4,9 +4,9 @@ using System.Collections.Generic; 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 81dd790066..ea124e5f29 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 ee1c3046eb..85d1daadeb 100644 --- a/src/Windows/Avalonia.Win32/OleDataObject.cs +++ b/src/Windows/Avalonia.Win32/OleDataObject.cs @@ -6,13 +6,13 @@ using System.Runtime.InteropServices; 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 e3791747b1..500c03e317 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 57/62] 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 2f378f44df..1c9f475eb7 100644 --- a/src/Avalonia.Visuals/Utilities/StringTokenizer.cs +++ b/src/Avalonia.Base/Utilities/StringTokenizer.cs @@ -4,7 +4,7 @@ using static System.Char; 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 1490afb5e3..997e15050f 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 58/62] 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 abceb39961..d6b0e43cdc 100644 --- a/src/Avalonia.Visuals/Media/Brush.cs +++ b/src/Avalonia.Visuals/Media/Brush.cs @@ -34,8 +34,8 @@ namespace Avalonia.Media /// 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 59/62] 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 1c9f475eb7..2559e52932 100644 --- a/src/Avalonia.Base/Utilities/StringTokenizer.cs +++ b/src/Avalonia.Base/Utilities/StringTokenizer.cs @@ -50,16 +50,16 @@ namespace Avalonia.Utilities } } - 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 @@ namespace Avalonia.Utilities 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 @@ namespace Avalonia.Utilities 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 @@ namespace Avalonia.Utilities 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 b17dc584bd..789953a249 100644 --- a/src/Avalonia.Controls/GridLength.cs +++ b/src/Avalonia.Controls/GridLength.cs @@ -213,7 +213,7 @@ namespace Avalonia.Controls { 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 6013cf94c6..70804ee04f 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -321,12 +321,12 @@ namespace Avalonia 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 49d2a401bf..d0c3edfeb2 100644 --- a/src/Avalonia.Visuals/Point.cs +++ b/src/Avalonia.Visuals/Point.cs @@ -177,8 +177,8 @@ namespace Avalonia 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 11f3db31da..748928ada3 100644 --- a/src/Avalonia.Visuals/Rect.cs +++ b/src/Avalonia.Visuals/Rect.cs @@ -494,10 +494,10 @@ namespace Avalonia 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 625ee61439..a2ef0e6725 100644 --- a/src/Avalonia.Visuals/RelativePoint.cs +++ b/src/Avalonia.Visuals/RelativePoint.cs @@ -160,8 +160,8 @@ namespace Avalonia { 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 05e344f42b..c13c3282db 100644 --- a/src/Avalonia.Visuals/RelativeRect.cs +++ b/src/Avalonia.Visuals/RelativeRect.cs @@ -173,10 +173,10 @@ namespace Avalonia { 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 ff734146c2..c5eaa33b41 100644 --- a/src/Avalonia.Visuals/Size.cs +++ b/src/Avalonia.Visuals/Size.cs @@ -157,8 +157,8 @@ namespace Avalonia 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 4609cc3ced..a5ca0a04a8 100644 --- a/src/Avalonia.Visuals/Thickness.cs +++ b/src/Avalonia.Visuals/Thickness.cs @@ -171,13 +171,13 @@ namespace Avalonia { 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 60/62] 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 fb432c30d4..ba68838382 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.Data; 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 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml 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 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml 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 61/62] 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 b0877e0fb7..ae8c88f7e8 100644 --- a/src/Avalonia.Controls/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Controls/Properties/AssemblyInfo.cs @@ -11,7 +11,6 @@ using Avalonia.Metadata; [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 62/62] 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 34c6b1cfd6..890926db54 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -275,8 +275,11 @@ namespace Avalonia.Controls 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)