From da25b2f61f8497dd2dec235ed063714fb4181c1f Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Sun, 15 Apr 2018 18:52:34 +0200 Subject: [PATCH 01/12] LayoutHelper for controls with single content BorderRenderHelper refactoring --- src/Avalonia.Controls/Border.cs | 28 +--- src/Avalonia.Controls/Decorator.cs | 17 +- .../Presenters/ContentPresenter.cs | 118 +++++++------ .../Utils/BorderRenderHelper.cs | 156 ++++++++++-------- src/Avalonia.Layout/LayoutHelper.cs | 29 ++++ 5 files changed, 175 insertions(+), 173 deletions(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index 8acb3603c9..0382c8d675 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -3,6 +3,7 @@ using Avalonia; using Avalonia.Controls.Utils; +using Avalonia.Layout; using Avalonia.Media; namespace Avalonia.Controls @@ -99,7 +100,7 @@ namespace Avalonia.Controls /// The desired size of the control. protected override Size MeasureOverride(Size availableSize) { - return MeasureOverrideImpl(availableSize, Child, Padding, BorderThickness); + return LayoutHelper.MeasureChild(Child, availableSize, Padding, BorderThickness); } /// @@ -109,32 +110,9 @@ namespace Avalonia.Controls /// The space taken. protected override Size ArrangeOverride(Size finalSize) { - if (Child != null) - { - var padding = Padding + BorderThickness; - Child.Arrange(new Rect(finalSize).Deflate(padding)); - } - _borderRenderHelper.Update(finalSize, BorderThickness, CornerRadius); - return finalSize; - } - - internal static Size MeasureOverrideImpl( - Size availableSize, - IControl child, - Thickness padding, - Thickness borderThickness) - { - padding += borderThickness; - - if (child != null) - { - child.Measure(availableSize.Deflate(padding)); - return child.DesiredSize.Inflate(padding); - } - - return new Size(padding.Left + padding.Right, padding.Bottom + padding.Top); + return LayoutHelper.ArrangeChild(Child, finalSize, Padding, BorderThickness); } } } \ No newline at end of file diff --git a/src/Avalonia.Controls/Decorator.cs b/src/Avalonia.Controls/Decorator.cs index 4040e4b8b7..389cf66d34 100644 --- a/src/Avalonia.Controls/Decorator.cs +++ b/src/Avalonia.Controls/Decorator.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.Layout; using Avalonia.Metadata; namespace Avalonia.Controls @@ -53,25 +54,13 @@ namespace Avalonia.Controls /// protected override Size MeasureOverride(Size availableSize) { - var content = Child; - var padding = Padding; - - if (content != null) - { - content.Measure(availableSize.Deflate(padding)); - return content.DesiredSize.Inflate(padding); - } - else - { - return new Size(padding.Left + padding.Right, padding.Bottom + padding.Top); - } + return LayoutHelper.MeasureChild(Child, availableSize, Padding); } /// protected override Size ArrangeOverride(Size finalSize) { - Child?.Arrange(new Rect(finalSize).Deflate(Padding)); - return finalSize; + return LayoutHelper.ArrangeChild(Child, finalSize, Padding); } /// diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 3cc750e20d..97c85b0c6c 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -35,6 +35,13 @@ namespace Avalonia.Controls.Presenters public static readonly StyledProperty BorderThicknessProperty = Border.BorderThicknessProperty.AddOwner(); + /// + /// Defines the property. + /// + public static readonly StyledProperty CornerRadiusProperty = + Border.CornerRadiusProperty.AddOwner(); + + /// /// Defines the property. /// @@ -55,12 +62,6 @@ namespace Avalonia.Controls.Presenters public static readonly StyledProperty ContentTemplateProperty = ContentControl.ContentTemplateProperty.AddOwner(); - /// - /// Defines the property. - /// - public static readonly StyledProperty CornerRadiusProperty = - Border.CornerRadiusProperty.AddOwner(); - /// /// Defines the property. /// @@ -96,13 +97,6 @@ namespace Avalonia.Controls.Presenters TemplatedParentProperty.Changed.AddClassHandler(x => x.TemplatedParentChanged); } - /// - /// Initializes a new instance of the class. - /// - public ContentPresenter() - { - } - /// /// Gets or sets a brush with which to paint the background. /// @@ -130,6 +124,15 @@ namespace Avalonia.Controls.Presenters set { SetValue(BorderThicknessProperty, value); } } + /// + /// Gets or sets the radius of the border rounded corners. + /// + public CornerRadius CornerRadius + { + get { return GetValue(CornerRadiusProperty); } + set { SetValue(CornerRadiusProperty, value); } + } + /// /// Gets the control displayed by the presenter. /// @@ -159,16 +162,7 @@ namespace Avalonia.Controls.Presenters } /// - /// Gets or sets the radius of the border rounded corners. - /// - public CornerRadius CornerRadius - { - get { return GetValue(CornerRadiusProperty); } - set { SetValue(CornerRadiusProperty, value); } - } - - /// - /// Gets or sets the horizontal alignment of the content within the control. + /// Gets or sets the horizontal alignment of the content within the border the control. /// public HorizontalAlignment HorizontalContentAlignment { @@ -177,7 +171,7 @@ namespace Avalonia.Controls.Presenters } /// - /// Gets or sets the vertical alignment of the content within the control. + /// Gets or sets the vertical alignment of the content within the border of the control. /// public VerticalAlignment VerticalContentAlignment { @@ -186,7 +180,7 @@ namespace Avalonia.Controls.Presenters } /// - /// Gets or sets the padding to place around the control. + /// Gets or sets the space between the border and the control. /// public Thickness Padding { @@ -195,7 +189,7 @@ namespace Avalonia.Controls.Presenters } /// - public override sealed void ApplyTemplate() + public sealed override void ApplyTemplate() { if (!_createdChild && ((ILogical)this).IsAttachedToLogicalTree) { @@ -328,67 +322,41 @@ namespace Avalonia.Controls.Presenters /// protected override Size MeasureOverride(Size availableSize) { - return Border.MeasureOverrideImpl(availableSize, Child, Padding, BorderThickness); + return LayoutHelper.MeasureChild(Child, availableSize, Padding, BorderThickness); } /// protected override Size ArrangeOverride(Size finalSize) { - finalSize = ArrangeOverrideImpl(finalSize, new Vector()); - _borderRenderer.Update(finalSize, BorderThickness, CornerRadius); - return finalSize; - } - - /// - /// Called when the property changes. - /// - /// The event args. - private void ContentChanged(AvaloniaPropertyChangedEventArgs e) - { - _createdChild = false; - - if (((ILogical)this).IsAttachedToLogicalTree) - { - UpdateChild(); - } - else if (Child != null) - { - VisualChildren.Remove(Child); - LogicalChildren.Remove(Child); - Child = null; - _dataTemplate = null; - } - - InvalidateMeasure(); + return ArrangeOverrideImpl(finalSize, new Vector()); } internal Size ArrangeOverrideImpl(Size finalSize, Vector offset) { if (Child == null) return finalSize; - var padding = Padding; - var borderThickness = BorderThickness; + var padding = Padding + BorderThickness; var horizontalContentAlignment = HorizontalContentAlignment; var verticalContentAlignment = VerticalContentAlignment; var useLayoutRounding = UseLayoutRounding; var availableSizeMinusMargins = new Size( - Math.Max(0, finalSize.Width - padding.Left - padding.Right - borderThickness.Left - borderThickness.Right), - Math.Max(0, finalSize.Height - padding.Top - padding.Bottom - borderThickness.Top - borderThickness.Bottom)); + Math.Max(0, finalSize.Width), + Math.Max(0, finalSize.Height)); var size = availableSizeMinusMargins; var scale = GetLayoutScale(); - var originX = offset.X + padding.Left + borderThickness.Left; - var originY = offset.Y + padding.Top + borderThickness.Top; + var originX = offset.X; + var originY = offset.Y; if (horizontalContentAlignment != HorizontalAlignment.Stretch) { - size = size.WithWidth(Math.Min(size.Width, DesiredSize.Width - padding.Left - padding.Right)); + size = size.WithWidth(Math.Min(size.Width, DesiredSize.Width)); } 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)); } if (useLayoutRounding) @@ -427,11 +395,37 @@ namespace Avalonia.Controls.Presenters originY = Math.Floor(originY * scale) / scale; } - Child.Arrange(new Rect(originX, originY, Math.Max(0, size.Width), Math.Max(0, size.Height))); + var boundsForChild = + new Rect(originX, originY, Math.Max(0, size.Width), Math.Max(0, size.Height)).Deflate(padding); + + Child.Arrange(boundsForChild); return finalSize; } + /// + /// Called when the property changes. + /// + /// The event args. + private void ContentChanged(AvaloniaPropertyChangedEventArgs e) + { + _createdChild = false; + + if (((ILogical)this).IsAttachedToLogicalTree) + { + UpdateChild(); + } + else if (Child != null) + { + VisualChildren.Remove(Child); + LogicalChildren.Remove(Child); + Child = null; + _dataTemplate = null; + } + + InvalidateMeasure(); + } + private double GetLayoutScale() { var result = (VisualRoot as ILayoutRoot)?.LayoutScaling ?? 1.0; diff --git a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs index d9169e51f3..e5b1b1961f 100644 --- a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs +++ b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs @@ -26,7 +26,7 @@ namespace Avalonia.Controls.Utils var boundRect = new Rect(finalSize); var innerRect = boundRect.Deflate(borderThickness); - var innerCoordinates = new BorderCoordinates(cornerRadius, borderThickness, false); + var innerCoordinates = GeometryCoordinates.CreateBackgroundCoordinates(cornerRadius, borderThickness); StreamGeometry backgroundGeometry = null; @@ -48,7 +48,7 @@ namespace Avalonia.Controls.Utils if (boundRect.Width != 0 && innerRect.Height != 0) { - var outerCoordinates = new BorderCoordinates(cornerRadius, borderThickness, true); + var outerCoordinates = GeometryCoordinates.CreateBorderCoordinates(cornerRadius, borderThickness); var borderGeometry = new StreamGeometry(); using (var ctx = borderGeometry.Open()) @@ -104,42 +104,41 @@ namespace Avalonia.Controls.Utils } } - private static void CreateGeometry(StreamGeometryContext context, Rect boundRect, BorderCoordinates borderCoordinates) + private static void CreateGeometry(StreamGeometryContext context, Rect boundRect, GeometryCoordinates geometryCoordinates) { - var topLeft = new Point(borderCoordinates.LeftTop, 0); - var topRight = new Point(boundRect.Width - borderCoordinates.RightTop, 0); - var rightTop = new Point(boundRect.Width, borderCoordinates.TopRight); - var rightBottom = new Point(boundRect.Width, boundRect.Height - borderCoordinates.BottomRight); - var bottomRight = new Point(boundRect.Width - borderCoordinates.RightBottom, boundRect.Height); - var bottomLeft = new Point(borderCoordinates.LeftBottom, boundRect.Height); - var leftBottom = new Point(0, boundRect.Height - borderCoordinates.BottomLeft); - var leftTop = new Point(0, borderCoordinates.TopLeft); - + var topLeft = new Point(geometryCoordinates.LeftTop, 0); + var topRight = new Point(boundRect.Width - geometryCoordinates.RightTop, 0); + var rightTop = new Point(boundRect.Width, geometryCoordinates.TopRight); + var rightBottom = new Point(boundRect.Width, boundRect.Height - geometryCoordinates.BottomRight); + var bottomRight = new Point(boundRect.Width - geometryCoordinates.RightBottom, boundRect.Height); + var bottomLeft = new Point(geometryCoordinates.LeftBottom, boundRect.Height); + var leftBottom = new Point(0, boundRect.Height - geometryCoordinates.BottomLeft); + var leftTop = new Point(0, geometryCoordinates.TopLeft); if (topLeft.X > topRight.X) { - var scaledX = borderCoordinates.LeftTop / (borderCoordinates.LeftTop + borderCoordinates.RightTop) * boundRect.Width; + var scaledX = geometryCoordinates.LeftTop / (geometryCoordinates.LeftTop + geometryCoordinates.RightTop) * boundRect.Width; topLeft = new Point(scaledX, topLeft.Y); topRight = new Point(scaledX, topRight.Y); } if (rightTop.Y > rightBottom.Y) { - var scaledY = borderCoordinates.TopRight / (borderCoordinates.TopRight + borderCoordinates.BottomRight) * boundRect.Height; + var scaledY = geometryCoordinates.TopRight / (geometryCoordinates.TopRight + geometryCoordinates.BottomRight) * boundRect.Height; rightTop = new Point(rightTop.X, scaledY); rightBottom = new Point(rightBottom.X, scaledY); } if (bottomRight.X < bottomLeft.X) { - var scaledX = borderCoordinates.LeftBottom / (borderCoordinates.LeftBottom + borderCoordinates.RightBottom) * boundRect.Width; + var scaledX = geometryCoordinates.LeftBottom / (geometryCoordinates.LeftBottom + geometryCoordinates.RightBottom) * boundRect.Width; bottomRight = new Point(scaledX, bottomRight.Y); bottomLeft = new Point(scaledX, bottomLeft.Y); } if (leftBottom.Y < leftTop.Y) { - var scaledY = borderCoordinates.TopLeft / (borderCoordinates.TopLeft + borderCoordinates.BottomLeft) * boundRect.Height; + var scaledY = geometryCoordinates.TopLeft / (geometryCoordinates.TopLeft + geometryCoordinates.BottomLeft) * boundRect.Height; leftBottom = new Point(leftBottom.X, scaledY); leftTop = new Point(leftTop.X, scaledY); } @@ -204,75 +203,88 @@ namespace Avalonia.Controls.Utils context.EndFigure(true); } - private struct BorderCoordinates + private struct GeometryCoordinates { - internal BorderCoordinates(CornerRadius cornerRadius, Thickness borderThickness, bool isOuter) + internal static GeometryCoordinates CreateBorderCoordinates(CornerRadius cornerRadius, Thickness borderThickness) { var left = 0.5 * borderThickness.Left; var top = 0.5 * borderThickness.Top; var right = 0.5 * borderThickness.Right; var bottom = 0.5 * borderThickness.Bottom; - if (isOuter) + var leftTop = 0.0; + var topLeft = 0.0; + if (cornerRadius.TopLeft != 0) { - if (cornerRadius.TopLeft == 0) - { - LeftTop = TopLeft = 0.0; - } - else - { - LeftTop = cornerRadius.TopLeft + left; - TopLeft = cornerRadius.TopLeft + top; - } - if (cornerRadius.TopRight == 0) - { - TopRight = RightTop = 0; - } - else - { - TopRight = cornerRadius.TopRight + top; - RightTop = cornerRadius.TopRight + right; - } - if (cornerRadius.BottomRight == 0) - { - RightBottom = BottomRight = 0; - } - else - { - RightBottom = cornerRadius.BottomRight + right; - BottomRight = cornerRadius.BottomRight + bottom; - } - if (cornerRadius.BottomLeft == 0) - { - BottomLeft = LeftBottom = 0; - } - else - { - BottomLeft = cornerRadius.BottomLeft + bottom; - LeftBottom = cornerRadius.BottomLeft + left; - } + leftTop = cornerRadius.TopLeft + left; + topLeft = cornerRadius.TopLeft + top; } - else + + var topRight = 0.0; + var rightTop = 0.0; + if (cornerRadius.TopRight != 0) { - LeftTop = Math.Max(0, cornerRadius.TopLeft - left); - TopLeft = Math.Max(0, cornerRadius.TopLeft - top); - TopRight = Math.Max(0, cornerRadius.TopRight - top); - RightTop = Math.Max(0, cornerRadius.TopRight - right); - RightBottom = Math.Max(0, cornerRadius.BottomRight - right); - BottomRight = Math.Max(0, cornerRadius.BottomRight - bottom); - BottomLeft = Math.Max(0, cornerRadius.BottomLeft - bottom); - LeftBottom = Math.Max(0, cornerRadius.BottomLeft - left); + topRight = cornerRadius.TopRight + top; + rightTop = cornerRadius.TopRight + right; } + + var rightBottom = 0.0; + var bottomRight = 0.0; + if (cornerRadius.BottomRight != 0) + { + rightBottom = cornerRadius.BottomRight + right; + bottomRight = cornerRadius.BottomRight + bottom; + } + + var bottomLeft = 0.0; + var leftBottom = 0.0; + if (cornerRadius.BottomLeft != 0) + { + bottomLeft = cornerRadius.BottomLeft + bottom; + leftBottom = cornerRadius.BottomLeft + left; + } + + return new GeometryCoordinates + { + LeftTop = leftTop, + TopLeft = topLeft, + TopRight = topRight, + RightTop = rightTop, + RightBottom = rightBottom, + BottomRight = bottomRight, + BottomLeft = bottomLeft, + LeftBottom = leftBottom, + }; + } + + internal static GeometryCoordinates CreateBackgroundCoordinates(CornerRadius cornerRadius, Thickness borderThickness) + { + var left = 0.5 * borderThickness.Left; + var top = 0.5 * borderThickness.Top; + var right = 0.5 * borderThickness.Right; + var bottom = 0.5 * borderThickness.Bottom; + + return new GeometryCoordinates + { + LeftTop = Math.Max(0, cornerRadius.TopLeft - left), + TopLeft = Math.Max(0, cornerRadius.TopLeft - top), + TopRight = Math.Max(0, cornerRadius.TopRight - top), + RightTop = Math.Max(0, cornerRadius.TopRight - right), + RightBottom = Math.Max(0, cornerRadius.BottomRight - right), + BottomRight = Math.Max(0, cornerRadius.BottomRight - bottom), + BottomLeft = Math.Max(0, cornerRadius.BottomLeft - bottom), + LeftBottom = Math.Max(0, cornerRadius.BottomLeft - left), + }; } - internal readonly double LeftTop; - internal readonly double TopLeft; - internal readonly double TopRight; - internal readonly double RightTop; - internal readonly double RightBottom; - internal readonly double BottomRight; - internal readonly double BottomLeft; - internal readonly double LeftBottom; + internal double LeftTop { get; private set; } + internal double TopLeft { get; private set; } + internal double TopRight { get; private set; } + internal double RightTop { get; private set; } + internal double RightBottom { get; private set; } + internal double BottomRight { get; private set; } + internal double BottomLeft { get; private set; } + internal double LeftBottom { get; private set; } } } diff --git a/src/Avalonia.Layout/LayoutHelper.cs b/src/Avalonia.Layout/LayoutHelper.cs index 8c0a6a579b..d77cc269f9 100644 --- a/src/Avalonia.Layout/LayoutHelper.cs +++ b/src/Avalonia.Layout/LayoutHelper.cs @@ -29,5 +29,34 @@ namespace Avalonia.Layout height = Math.Max(height, control.MinHeight); return new Size(width, height); } + + public static Size MeasureChild(ILayoutable control, Size availableSize, Thickness padding, + Thickness borderThickness) + { + return MeasureChild(control, availableSize, padding + borderThickness); + } + + public static Size MeasureChild(ILayoutable control, Size availableSize, Thickness padding) + { + if (control != null) + { + control.Measure(availableSize.Deflate(padding)); + return control.DesiredSize.Inflate(padding); + } + + return new Size(padding.Left + padding.Right, padding.Bottom + padding.Top); + } + + public static Size ArrangeChild(ILayoutable child, Size availableSize, Thickness padding, Thickness borderThickness) + { + return ArrangeChild(child, availableSize, padding + borderThickness); + } + + public static Size ArrangeChild(ILayoutable child, Size availableSize, Thickness padding) + { + child?.Arrange(new Rect(availableSize).Deflate(padding)); + + return availableSize; + } } } From dbbf36e87276bd855b49d02760c6f77058826473 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Mon, 16 Apr 2018 16:58:58 +0200 Subject: [PATCH 02/12] fix default templates --- .../Presenters/ContentPresenter.cs | 26 +++++++++---------- .../ButtonSpinner.xaml | 8 +++--- .../ContentControl.xaml | 5 +++- src/Avalonia.Themes.Default/ListBoxItem.xaml | 5 +++- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 97c85b0c6c..106a63feb3 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -341,29 +341,27 @@ namespace Avalonia.Controls.Presenters var horizontalContentAlignment = HorizontalContentAlignment; var verticalContentAlignment = VerticalContentAlignment; var useLayoutRounding = UseLayoutRounding; - var availableSizeMinusMargins = new Size( - Math.Max(0, finalSize.Width), - Math.Max(0, finalSize.Height)); - var size = availableSizeMinusMargins; + var availableSizeMinusMargins = finalSize; + var sizeForChild = availableSizeMinusMargins; var scale = GetLayoutScale(); var originX = offset.X; var originY = offset.Y; if (horizontalContentAlignment != HorizontalAlignment.Stretch) { - size = size.WithWidth(Math.Min(size.Width, DesiredSize.Width)); + sizeForChild = sizeForChild.WithWidth(Math.Min(sizeForChild.Width, DesiredSize.Width)); } if (verticalContentAlignment != VerticalAlignment.Stretch) { - size = size.WithHeight(Math.Min(size.Height, DesiredSize.Height)); + sizeForChild = sizeForChild.WithHeight(Math.Min(sizeForChild.Height, DesiredSize.Height)); } if (useLayoutRounding) { - size = new Size( - Math.Ceiling(size.Width * scale) / scale, - Math.Ceiling(size.Height * scale) / scale); + sizeForChild = new Size( + Math.Ceiling(sizeForChild.Width * scale) / scale, + Math.Ceiling(sizeForChild.Height * scale) / scale); availableSizeMinusMargins = new Size( Math.Ceiling(availableSizeMinusMargins.Width * scale) / scale, Math.Ceiling(availableSizeMinusMargins.Height * scale) / scale); @@ -372,20 +370,20 @@ namespace Avalonia.Controls.Presenters switch (horizontalContentAlignment) { case HorizontalAlignment.Center: - originX += (availableSizeMinusMargins.Width - size.Width) / 2; + originX += (availableSizeMinusMargins.Width - sizeForChild.Width) / 2; break; case HorizontalAlignment.Right: - originX += availableSizeMinusMargins.Width - size.Width; + originX += availableSizeMinusMargins.Width - sizeForChild.Width; break; } switch (verticalContentAlignment) { case VerticalAlignment.Center: - originY += (availableSizeMinusMargins.Height - size.Height) / 2; + originY += (availableSizeMinusMargins.Height - sizeForChild.Height) / 2; break; case VerticalAlignment.Bottom: - originY += availableSizeMinusMargins.Height - size.Height; + originY += availableSizeMinusMargins.Height - sizeForChild.Height; break; } @@ -396,7 +394,7 @@ namespace Avalonia.Controls.Presenters } var boundsForChild = - new Rect(originX, originY, Math.Max(0, size.Width), Math.Max(0, size.Height)).Deflate(padding); + new Rect(originX, originY, sizeForChild.Width, sizeForChild.Height).Deflate(padding); Child.Arrange(boundsForChild); diff --git a/src/Avalonia.Themes.Default/ButtonSpinner.xaml b/src/Avalonia.Themes.Default/ButtonSpinner.xaml index a08f5b5e9b..17a2c5525d 100644 --- a/src/Avalonia.Themes.Default/ButtonSpinner.xaml +++ b/src/Avalonia.Themes.Default/ButtonSpinner.xaml @@ -46,12 +46,14 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Margin="{TemplateBinding Padding}" - HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" - VerticalAlignment="{TemplateBinding VerticalContentAlignment}"> + HorizontalAlignment="{TemplateBinding HorizontalAlignment}" + VerticalAlignment="{TemplateBinding VerticalAlignment}"> + Content="{TemplateBinding Content}" + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> diff --git a/src/Avalonia.Themes.Default/ContentControl.xaml b/src/Avalonia.Themes.Default/ContentControl.xaml index 7584ae0dfe..f4cdd87ad0 100644 --- a/src/Avalonia.Themes.Default/ContentControl.xaml +++ b/src/Avalonia.Themes.Default/ContentControl.xaml @@ -7,7 +7,10 @@ BorderThickness="{TemplateBinding BorderThickness}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" - Padding="{TemplateBinding Padding}"/> + Padding="{TemplateBinding Padding}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"> + \ No newline at end of file diff --git a/src/Avalonia.Themes.Default/ListBoxItem.xaml b/src/Avalonia.Themes.Default/ListBoxItem.xaml index a0405f2875..c591fa424f 100644 --- a/src/Avalonia.Themes.Default/ListBoxItem.xaml +++ b/src/Avalonia.Themes.Default/ListBoxItem.xaml @@ -9,7 +9,10 @@ BorderThickness="{TemplateBinding BorderThickness}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" - Padding="{TemplateBinding Padding}"/> + Padding="{TemplateBinding Padding}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"> + From 969265fbed1e5e37324fda52df3a476ba0e78abe Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Mon, 16 Apr 2018 17:13:13 +0200 Subject: [PATCH 03/12] fix additional default templates --- .../ButtonSpinner.xaml | 12 ++++++--- .../ContentControl.xaml | 3 +-- src/Avalonia.Themes.Default/Expander.xaml | 25 ++++++++++++------- src/Avalonia.Themes.Default/ListBoxItem.xaml | 3 +-- src/Avalonia.Themes.Default/RepeatButton.xaml | 2 +- src/Avalonia.Themes.Default/TabStripItem.xaml | 2 ++ src/Avalonia.Themes.Default/TreeViewItem.xaml | 2 ++ src/Avalonia.Themes.Default/Window.xaml | 8 +++--- 8 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/Avalonia.Themes.Default/ButtonSpinner.xaml b/src/Avalonia.Themes.Default/ButtonSpinner.xaml index 17a2c5525d..2558b7c2f8 100644 --- a/src/Avalonia.Themes.Default/ButtonSpinner.xaml +++ b/src/Avalonia.Themes.Default/ButtonSpinner.xaml @@ -53,7 +53,8 @@ ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" - VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" + Padding="{TemplateBinding Padding}"/> @@ -70,8 +71,8 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Margin="{TemplateBinding Padding}" - HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" - VerticalAlignment="{TemplateBinding VerticalContentAlignment}"> + HorizontalAlignment="{TemplateBinding HorizontalAlignment}" + VerticalAlignment="{TemplateBinding VerticalAlignment}"> @@ -79,7 +80,10 @@ + Content="{TemplateBinding Content}" + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" + Padding="{TemplateBinding Padding}"/> diff --git a/src/Avalonia.Themes.Default/ContentControl.xaml b/src/Avalonia.Themes.Default/ContentControl.xaml index f4cdd87ad0..d76eecee84 100644 --- a/src/Avalonia.Themes.Default/ContentControl.xaml +++ b/src/Avalonia.Themes.Default/ContentControl.xaml @@ -9,8 +9,7 @@ Content="{TemplateBinding Content}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" - HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"> - + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"/> \ No newline at end of file diff --git a/src/Avalonia.Themes.Default/Expander.xaml b/src/Avalonia.Themes.Default/Expander.xaml index 5e05dcf608..0bea0c9763 100644 --- a/src/Avalonia.Themes.Default/Expander.xaml +++ b/src/Avalonia.Themes.Default/Expander.xaml @@ -18,8 +18,9 @@ IsVisible="{TemplateBinding IsExpanded}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" - HorizontalAlignment="Stretch" - VerticalAlignment="Stretch" /> + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" + Padding="{TemplateBinding Padding}" /> @@ -36,8 +37,9 @@ IsVisible="{TemplateBinding IsExpanded}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" - HorizontalAlignment="Stretch" - VerticalAlignment="Stretch" /> + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" + Padding="{TemplateBinding Padding}" /> @@ -54,8 +56,9 @@ IsVisible="{TemplateBinding IsExpanded}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" - HorizontalAlignment="Stretch" - VerticalAlignment="Stretch" /> + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" + Padding="{TemplateBinding Padding}" /> @@ -72,8 +75,9 @@ IsVisible="{TemplateBinding IsExpanded}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" - HorizontalAlignment="Stretch" - VerticalAlignment="Stretch" /> + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" + Padding="{TemplateBinding Padding}" /> @@ -94,7 +98,10 @@ Grid.Column="1" Background="Transparent" Content="{TemplateBinding Content}" - VerticalAlignment="Center" /> + VerticalAlignment="Center" + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" + Padding="{TemplateBinding Padding}"/> diff --git a/src/Avalonia.Themes.Default/ListBoxItem.xaml b/src/Avalonia.Themes.Default/ListBoxItem.xaml index c591fa424f..fc2600c1a9 100644 --- a/src/Avalonia.Themes.Default/ListBoxItem.xaml +++ b/src/Avalonia.Themes.Default/ListBoxItem.xaml @@ -11,8 +11,7 @@ Content="{TemplateBinding Content}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" - HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"> - + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"/> diff --git a/src/Avalonia.Themes.Default/RepeatButton.xaml b/src/Avalonia.Themes.Default/RepeatButton.xaml index 872f390ac5..001c5c2f23 100644 --- a/src/Avalonia.Themes.Default/RepeatButton.xaml +++ b/src/Avalonia.Themes.Default/RepeatButton.xaml @@ -25,7 +25,7 @@ Padding="{TemplateBinding Padding}" TextBlock.Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" - VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" /> + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> diff --git a/src/Avalonia.Themes.Default/TabStripItem.xaml b/src/Avalonia.Themes.Default/TabStripItem.xaml index 408cfaa3d7..8aa03d5bd2 100644 --- a/src/Avalonia.Themes.Default/TabStripItem.xaml +++ b/src/Avalonia.Themes.Default/TabStripItem.xaml @@ -11,6 +11,8 @@ BorderThickness="{TemplateBinding BorderThickness}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" Padding="{TemplateBinding Padding}"/> diff --git a/src/Avalonia.Themes.Default/TreeViewItem.xaml b/src/Avalonia.Themes.Default/TreeViewItem.xaml index 5fb1dfb73d..3a8fe344c5 100644 --- a/src/Avalonia.Themes.Default/TreeViewItem.xaml +++ b/src/Avalonia.Themes.Default/TreeViewItem.xaml @@ -13,6 +13,8 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Content="{TemplateBinding Header}" + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" Padding="{TemplateBinding Padding}" TemplatedControl.IsTemplateFocusTarget="True" Grid.Column="1"/> diff --git a/src/Avalonia.Themes.Default/Window.xaml b/src/Avalonia.Themes.Default/Window.xaml index d19bf41c4d..f36e091e55 100644 --- a/src/Avalonia.Themes.Default/Window.xaml +++ b/src/Avalonia.Themes.Default/Window.xaml @@ -6,10 +6,12 @@ - + Content="{TemplateBinding Content}" + Margin="{TemplateBinding Padding}" + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> From 5190a6c91579bbbe6699a92199446445fd809b11 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Mon, 16 Apr 2018 17:23:59 +0200 Subject: [PATCH 04/12] fix missing measure call if the padding changes --- src/Avalonia.Controls/Presenters/ContentPresenter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 106a63feb3..e57928d59b 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -91,7 +91,7 @@ namespace Avalonia.Controls.Presenters static ContentPresenter() { AffectsRender(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty); - AffectsMeasure(BorderThicknessProperty); + AffectsMeasure(BorderThicknessProperty, PaddingProperty); ContentProperty.Changed.AddClassHandler(x => x.ContentChanged); ContentTemplateProperty.Changed.AddClassHandler(x => x.ContentChanged); TemplatedParentProperty.Changed.AddClassHandler(x => x.TemplatedParentChanged); From 87cc247100a1e47348adccd0e6200620803b0ecc Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Mon, 16 Apr 2018 18:45:42 +0200 Subject: [PATCH 05/12] fix tests --- .../ContentPresenterTests_Layout.cs | 2 +- .../Data/BindingExpressionTests.cs | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs index 2c1074aa9a..98e57601a4 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs @@ -233,7 +233,7 @@ namespace Avalonia.Controls.UnitTests.Presenters target.Arrange(new Rect(0, 0, 100, 100)); - Assert.Equal(new Rect(48, 48, 0, 0), content.Bounds); + Assert.Equal(new Rect(32, 32, 0, 0), content.Bounds); } } } \ No newline at end of file diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs index f5c0c6ec15..a6fb2fa722 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs @@ -58,7 +58,7 @@ namespace Avalonia.Markup.UnitTests.Data [Fact] public async Task Should_Convert_Get_String_To_Double() { - var data = new Class1 { StringValue = "5.6" }; + var data = new Class1 { StringValue = 5.6.ToString(CultureInfo.CurrentCulture) }; var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double)); var result = await target.Take(1); @@ -94,12 +94,12 @@ namespace Avalonia.Markup.UnitTests.Data [Fact] public void Should_Convert_Set_String_To_Double() { - var data = new Class1 { StringValue = (5.6).ToString() }; + var data = new Class1 { StringValue = 5.6.ToString(CultureInfo.CurrentCulture) }; var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double)); target.OnNext(6.7); - Assert.Equal((6.7).ToString(), data.StringValue); + Assert.Equal(6.7.ToString(CultureInfo.CurrentCulture), data.StringValue); GC.KeepAlive(data); } @@ -111,7 +111,7 @@ namespace Avalonia.Markup.UnitTests.Data var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string)); var result = await target.Take(1); - Assert.Equal((5.6).ToString(), result); + Assert.Equal((5.6).ToString(CultureInfo.CurrentCulture), result); GC.KeepAlive(data); } @@ -122,7 +122,7 @@ namespace Avalonia.Markup.UnitTests.Data var data = new Class1 { DoubleValue = 5.6 }; var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string)); - target.OnNext("6.7"); + target.OnNext(6.7.ToString(CultureInfo.CurrentCulture)); Assert.Equal(6.7, data.DoubleValue); @@ -296,7 +296,7 @@ namespace Avalonia.Markup.UnitTests.Data var data = new Class1 { DoubleValue = 5.6 }; var converter = new Mock(); var target = new BindingExpression( - new ExpressionObserver(data, "DoubleValue"), + new ExpressionObserver(data, "DoubleValue"), typeof(string), converter.Object, converterParameter: "foo"); @@ -318,15 +318,15 @@ namespace Avalonia.Markup.UnitTests.Data target.Subscribe(x => result.Add(x)); target.OnNext(1.2); - target.OnNext("3.4"); + target.OnNext(3.4.ToString(CultureInfo.CurrentCulture)); target.OnNext("bar"); Assert.Equal( new[] { - new BindingNotification("5.6"), - new BindingNotification("1.2"), - new BindingNotification("3.4"), + new BindingNotification(5.6.ToString(CultureInfo.CurrentCulture)), + new BindingNotification(1.2.ToString(CultureInfo.CurrentCulture)), + new BindingNotification(3.4.ToString(CultureInfo.CurrentCulture)), new BindingNotification( new InvalidCastException("'bar' is not a valid number."), BindingErrorType.Error) From 77bd8c89ca5dc34680f052ba40aaef4f0f04d415 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 18 Apr 2018 16:51:07 +0200 Subject: [PATCH 06/12] Cleanup BorderRenderHelper Name fix in ContentPresenter --- .../Presenters/ContentPresenter.cs | 18 +- .../Utils/BorderRenderHelper.cs | 268 ++++++++---------- 2 files changed, 127 insertions(+), 159 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index e57928d59b..6badf91367 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -341,8 +341,8 @@ namespace Avalonia.Controls.Presenters var horizontalContentAlignment = HorizontalContentAlignment; var verticalContentAlignment = VerticalContentAlignment; var useLayoutRounding = UseLayoutRounding; - var availableSizeMinusMargins = finalSize; - var sizeForChild = availableSizeMinusMargins; + var availableSize = finalSize; + var sizeForChild = availableSize; var scale = GetLayoutScale(); var originX = offset.X; var originY = offset.Y; @@ -362,28 +362,28 @@ namespace Avalonia.Controls.Presenters sizeForChild = new Size( Math.Ceiling(sizeForChild.Width * scale) / scale, Math.Ceiling(sizeForChild.Height * scale) / scale); - availableSizeMinusMargins = new Size( - Math.Ceiling(availableSizeMinusMargins.Width * scale) / scale, - Math.Ceiling(availableSizeMinusMargins.Height * scale) / scale); + availableSize = new Size( + Math.Ceiling(availableSize.Width * scale) / scale, + Math.Ceiling(availableSize.Height * scale) / scale); } switch (horizontalContentAlignment) { case HorizontalAlignment.Center: - originX += (availableSizeMinusMargins.Width - sizeForChild.Width) / 2; + originX += (availableSize.Width - sizeForChild.Width) / 2; break; case HorizontalAlignment.Right: - originX += availableSizeMinusMargins.Width - sizeForChild.Width; + originX += availableSize.Width - sizeForChild.Width; break; } switch (verticalContentAlignment) { case VerticalAlignment.Center: - originY += (availableSizeMinusMargins.Height - sizeForChild.Height) / 2; + originY += (availableSize.Height - sizeForChild.Height) / 2; break; case VerticalAlignment.Bottom: - originY += availableSizeMinusMargins.Height - sizeForChild.Height; + originY += availableSize.Height - sizeForChild.Height; break; } diff --git a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs index e5b1b1961f..0fd6593fbe 100644 --- a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs +++ b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs @@ -26,17 +26,17 @@ namespace Avalonia.Controls.Utils var boundRect = new Rect(finalSize); var innerRect = boundRect.Deflate(borderThickness); - var innerCoordinates = GeometryCoordinates.CreateBackgroundCoordinates(cornerRadius, borderThickness); - + BorderGeometryKeypoints backgroundKeypoints = null; StreamGeometry backgroundGeometry = null; if (innerRect.Width != 0 && innerRect.Height != 0) { backgroundGeometry = new StreamGeometry(); + backgroundKeypoints = new BorderGeometryKeypoints(innerRect, borderThickness, cornerRadius, true); using (var ctx = backgroundGeometry.Open()) { - CreateGeometry(ctx, innerRect, innerCoordinates); + CreateGeometry(ctx, innerRect, backgroundKeypoints); } _backgroundGeometryCache = backgroundGeometry; @@ -48,16 +48,16 @@ namespace Avalonia.Controls.Utils if (boundRect.Width != 0 && innerRect.Height != 0) { - var outerCoordinates = GeometryCoordinates.CreateBorderCoordinates(cornerRadius, borderThickness); + var borderGeometryKeypoints = new BorderGeometryKeypoints(boundRect, borderThickness, cornerRadius, false); var borderGeometry = new StreamGeometry(); using (var ctx = borderGeometry.Open()) { - CreateGeometry(ctx, boundRect, outerCoordinates); + CreateGeometry(ctx, boundRect, borderGeometryKeypoints); if (backgroundGeometry != null) { - CreateGeometry(ctx, innerRect, innerCoordinates); + CreateGeometry(ctx, innerRect, backgroundKeypoints); } } @@ -104,188 +104,156 @@ namespace Avalonia.Controls.Utils } } - private static void CreateGeometry(StreamGeometryContext context, Rect boundRect, GeometryCoordinates geometryCoordinates) - { - var topLeft = new Point(geometryCoordinates.LeftTop, 0); - var topRight = new Point(boundRect.Width - geometryCoordinates.RightTop, 0); - var rightTop = new Point(boundRect.Width, geometryCoordinates.TopRight); - var rightBottom = new Point(boundRect.Width, boundRect.Height - geometryCoordinates.BottomRight); - var bottomRight = new Point(boundRect.Width - geometryCoordinates.RightBottom, boundRect.Height); - var bottomLeft = new Point(geometryCoordinates.LeftBottom, boundRect.Height); - var leftBottom = new Point(0, boundRect.Height - geometryCoordinates.BottomLeft); - var leftTop = new Point(0, geometryCoordinates.TopLeft); - - if (topLeft.X > topRight.X) + private class BorderGeometryKeypoints + { + internal BorderGeometryKeypoints(Rect boundRect, Thickness borderThickness, CornerRadius cornerRadius, bool inner) { - var scaledX = geometryCoordinates.LeftTop / (geometryCoordinates.LeftTop + geometryCoordinates.RightTop) * boundRect.Width; - topLeft = new Point(scaledX, topLeft.Y); - topRight = new Point(scaledX, topRight.Y); - } + var left = 0.5 * borderThickness.Left; + var top = 0.5 * borderThickness.Top; + var right = 0.5 * borderThickness.Right; + var bottom = 0.5 * borderThickness.Bottom; - if (rightTop.Y > rightBottom.Y) - { - var scaledY = geometryCoordinates.TopRight / (geometryCoordinates.TopRight + geometryCoordinates.BottomRight) * boundRect.Height; - rightTop = new Point(rightTop.X, scaledY); - rightBottom = new Point(rightBottom.X, scaledY); - } + double leftTopY; + double topLeftX; + double topRightX; + double rightTopY; + double rightBottomY; + double bottomRightX; + double bottomLeftX; + double leftBottomY; - if (bottomRight.X < bottomLeft.X) - { - var scaledX = geometryCoordinates.LeftBottom / (geometryCoordinates.LeftBottom + geometryCoordinates.RightBottom) * boundRect.Width; - bottomRight = new Point(scaledX, bottomRight.Y); - bottomLeft = new Point(scaledX, bottomLeft.Y); - } + if (inner) + { + leftTopY = Math.Max(0, cornerRadius.TopLeft - top) + boundRect.TopLeft.Y; + topLeftX = Math.Max(0, cornerRadius.TopLeft - left) + boundRect.TopLeft.X; + topRightX = boundRect.Width - Math.Max(0, cornerRadius.TopRight - top) + boundRect.TopLeft.X; + rightTopY = Math.Max(0, cornerRadius.TopRight - right) + boundRect.TopLeft.Y; + rightBottomY = boundRect.Height - Math.Max(0, cornerRadius.BottomRight - bottom) + boundRect.TopLeft.Y; + bottomRightX = boundRect.Width - Math.Max(0, cornerRadius.BottomRight - right) + boundRect.TopLeft.X; + bottomLeftX = Math.Max(0, cornerRadius.BottomLeft - left) + boundRect.TopLeft.X; + leftBottomY = boundRect.Height - Math.Max(0, cornerRadius.BottomLeft - bottom) + boundRect.TopLeft.Y; + } + else + { + + leftTopY = cornerRadius.TopLeft + top + boundRect.TopLeft.Y; + topLeftX = cornerRadius.TopLeft + left + boundRect.TopLeft.X; + topRightX = boundRect.Width - (cornerRadius.TopRight + right) + boundRect.TopLeft.X; + rightTopY = cornerRadius.TopRight + top + boundRect.TopLeft.Y; + rightBottomY = boundRect.Height - (cornerRadius.BottomRight + bottom) + boundRect.TopLeft.Y; + bottomRightX = boundRect.Width - (cornerRadius.BottomRight + right) + boundRect.TopLeft.X; + bottomLeftX = cornerRadius.BottomLeft + left + boundRect.TopLeft.X; + leftBottomY = boundRect.Height - (cornerRadius.BottomLeft + bottom) + boundRect.TopLeft.Y; + } + + var leftTopX = boundRect.TopLeft.X; + var topLeftY = boundRect.TopLeft.Y; + var topRightY = boundRect.TopLeft.Y; + var rightTopX = boundRect.Width + boundRect.TopLeft.X; + var rightBottomX = boundRect.Width + boundRect.TopLeft.X; + var bottomRightY = boundRect.Height + boundRect.TopLeft.Y; + var bottomLeftY = boundRect.Height + boundRect.TopLeft.Y; + var leftBottomX = boundRect.TopLeft.X; + + LeftTop = new Point(leftTopX, leftTopY); + TopLeft = new Point(topLeftX, topLeftY); + TopRight = new Point(topRightX, topRightY); + RightTop = new Point(rightTopX, rightTopY); + RightBottom = new Point(rightBottomX, rightBottomY); + BottomRight = new Point(bottomRightX, bottomRightY); + BottomLeft = new Point(bottomLeftX, bottomLeftY); + LeftBottom = new Point(leftBottomX, leftBottomY); + + //Fix overlap + if (TopLeft.X > TopRight.X) + { + var scaledX = topLeftX / (topLeftX + topRightX) * boundRect.Width; + TopLeft = new Point(scaledX, TopLeft.Y); + TopRight = new Point(scaledX, TopRight.Y); + } - if (leftBottom.Y < leftTop.Y) - { - var scaledY = geometryCoordinates.TopLeft / (geometryCoordinates.TopLeft + geometryCoordinates.BottomLeft) * boundRect.Height; - leftBottom = new Point(leftBottom.X, scaledY); - leftTop = new Point(leftTop.X, scaledY); + if (RightTop.Y > RightBottom.Y) + { + var scaledY = rightBottomY / (rightTopY + rightBottomY) * boundRect.Height; + RightTop = new Point(RightTop.X, scaledY); + RightBottom = new Point(RightBottom.X, scaledY); + } + + if (BottomRight.X < BottomLeft.X) + { + var scaledX = bottomLeftX / (bottomLeftX + bottomRightX) * boundRect.Width; + BottomRight = new Point(scaledX, BottomRight.Y); + BottomLeft = new Point(scaledX, BottomLeft.Y); + } + + if (LeftBottom.Y < LeftTop.Y) + { + var scaledY = leftTopY / (leftTopY + leftBottomY) * boundRect.Height; + LeftBottom = new Point(LeftBottom.X, scaledY); + LeftTop = new Point(LeftTop.X, scaledY); + } } - var offset = new Vector(boundRect.TopLeft.X, boundRect.TopLeft.Y); - topLeft += offset; - topRight += offset; - rightTop += offset; - rightBottom += offset; - bottomRight += offset; - bottomLeft += offset; - leftBottom += offset; - leftTop += offset; + internal Point LeftTop { get; private set; } + internal Point TopLeft { get; private set; } + internal Point TopRight { get; private set; } + internal Point RightTop { get; private set; } + internal Point RightBottom { get; private set; } + internal Point BottomRight { get; private set; } + internal Point BottomLeft { get; private set; } + internal Point LeftBottom { get; private set; } + } - context.BeginFigure(topLeft, true); + private static void CreateGeometry(StreamGeometryContext context, Rect boundRect, BorderGeometryKeypoints keypoints) + { + context.BeginFigure(keypoints.TopLeft, true); //Top - context.LineTo(topRight); + context.LineTo(keypoints.TopRight); //TopRight corner - var radiusX = boundRect.TopRight.X - topRight.X; - var radiusY = rightTop.Y - boundRect.TopRight.Y; + var radiusX = boundRect.TopRight.X - keypoints.TopRight.X; + var radiusY = keypoints.RightTop.Y - boundRect.TopRight.Y; if (radiusX != 0 || radiusY != 0) { - context.ArcTo(rightTop, new Size(radiusY, radiusY), 0, false, SweepDirection.Clockwise); + context.ArcTo(keypoints.RightTop, new Size(radiusY, radiusY), 0, false, SweepDirection.Clockwise); } //Right - context.LineTo(rightBottom); + context.LineTo(keypoints.RightBottom); //BottomRight corner - radiusX = boundRect.BottomRight.X - bottomRight.X; - radiusY = boundRect.BottomRight.Y - rightBottom.Y; + radiusX = boundRect.BottomRight.X - keypoints.BottomRight.X; + radiusY = boundRect.BottomRight.Y - keypoints.RightBottom.Y; if (radiusX != 0 || radiusY != 0) { - context.ArcTo(bottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); + context.ArcTo(keypoints.BottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); } //Bottom - context.LineTo(bottomLeft); + context.LineTo(keypoints.BottomLeft); //BottomLeft corner - radiusX = bottomLeft.X - boundRect.BottomLeft.X; - radiusY = boundRect.BottomLeft.Y - leftBottom.Y; + radiusX = keypoints.BottomLeft.X - boundRect.BottomLeft.X; + radiusY = boundRect.BottomLeft.Y - keypoints.LeftBottom.Y; if (radiusX != 0 || radiusY != 0) { - context.ArcTo(leftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); + context.ArcTo(keypoints.LeftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); } //Left - context.LineTo(leftTop); + context.LineTo(keypoints.LeftTop); //TopLeft corner - radiusX = topLeft.X - boundRect.TopLeft.X; - radiusY = leftTop.Y - boundRect.TopLeft.Y; + radiusX = keypoints.TopLeft.X - boundRect.TopLeft.X; + radiusY = keypoints.LeftTop.Y - boundRect.TopLeft.Y; if (radiusX != 0 || radiusY != 0) { - context.ArcTo(topLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); + context.ArcTo(keypoints.TopLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); } context.EndFigure(true); } - - private struct GeometryCoordinates - { - internal static GeometryCoordinates CreateBorderCoordinates(CornerRadius cornerRadius, Thickness borderThickness) - { - var left = 0.5 * borderThickness.Left; - var top = 0.5 * borderThickness.Top; - var right = 0.5 * borderThickness.Right; - var bottom = 0.5 * borderThickness.Bottom; - - var leftTop = 0.0; - var topLeft = 0.0; - if (cornerRadius.TopLeft != 0) - { - leftTop = cornerRadius.TopLeft + left; - topLeft = cornerRadius.TopLeft + top; - } - - var topRight = 0.0; - var rightTop = 0.0; - if (cornerRadius.TopRight != 0) - { - topRight = cornerRadius.TopRight + top; - rightTop = cornerRadius.TopRight + right; - } - - var rightBottom = 0.0; - var bottomRight = 0.0; - if (cornerRadius.BottomRight != 0) - { - rightBottom = cornerRadius.BottomRight + right; - bottomRight = cornerRadius.BottomRight + bottom; - } - - var bottomLeft = 0.0; - var leftBottom = 0.0; - if (cornerRadius.BottomLeft != 0) - { - bottomLeft = cornerRadius.BottomLeft + bottom; - leftBottom = cornerRadius.BottomLeft + left; - } - - return new GeometryCoordinates - { - LeftTop = leftTop, - TopLeft = topLeft, - TopRight = topRight, - RightTop = rightTop, - RightBottom = rightBottom, - BottomRight = bottomRight, - BottomLeft = bottomLeft, - LeftBottom = leftBottom, - }; - } - - internal static GeometryCoordinates CreateBackgroundCoordinates(CornerRadius cornerRadius, Thickness borderThickness) - { - var left = 0.5 * borderThickness.Left; - var top = 0.5 * borderThickness.Top; - var right = 0.5 * borderThickness.Right; - var bottom = 0.5 * borderThickness.Bottom; - - return new GeometryCoordinates - { - LeftTop = Math.Max(0, cornerRadius.TopLeft - left), - TopLeft = Math.Max(0, cornerRadius.TopLeft - top), - TopRight = Math.Max(0, cornerRadius.TopRight - top), - RightTop = Math.Max(0, cornerRadius.TopRight - right), - RightBottom = Math.Max(0, cornerRadius.BottomRight - right), - BottomRight = Math.Max(0, cornerRadius.BottomRight - bottom), - BottomLeft = Math.Max(0, cornerRadius.BottomLeft - bottom), - LeftBottom = Math.Max(0, cornerRadius.BottomLeft - left), - }; - } - - internal double LeftTop { get; private set; } - internal double TopLeft { get; private set; } - internal double TopRight { get; private set; } - internal double RightTop { get; private set; } - internal double RightBottom { get; private set; } - internal double BottomRight { get; private set; } - internal double BottomLeft { get; private set; } - internal double LeftBottom { get; private set; } - } - } } From eafb8cde9e5c6dd6d9e65bb7b7abe94e615e9f9b Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Thu, 26 Apr 2018 18:17:50 +0200 Subject: [PATCH 07/12] revert BindingExpressionTests --- .../Data/BindingExpressionTests.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs index a6fb2fa722..6eeaa029de 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs @@ -58,7 +58,7 @@ namespace Avalonia.Markup.UnitTests.Data [Fact] public async Task Should_Convert_Get_String_To_Double() { - var data = new Class1 { StringValue = 5.6.ToString(CultureInfo.CurrentCulture) }; + var data = new Class1 { StringValue = "5.6" }; var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double)); var result = await target.Take(1); @@ -94,12 +94,12 @@ namespace Avalonia.Markup.UnitTests.Data [Fact] public void Should_Convert_Set_String_To_Double() { - var data = new Class1 { StringValue = 5.6.ToString(CultureInfo.CurrentCulture) }; + var data = new Class1 { StringValue = (5.6).ToString() }; var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double)); target.OnNext(6.7); - Assert.Equal(6.7.ToString(CultureInfo.CurrentCulture), data.StringValue); + Assert.Equal((6.7).ToString(), data.StringValue); GC.KeepAlive(data); } @@ -111,7 +111,7 @@ namespace Avalonia.Markup.UnitTests.Data var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string)); var result = await target.Take(1); - Assert.Equal((5.6).ToString(CultureInfo.CurrentCulture), result); + Assert.Equal((5.6).ToString(), result); GC.KeepAlive(data); } @@ -122,7 +122,7 @@ namespace Avalonia.Markup.UnitTests.Data var data = new Class1 { DoubleValue = 5.6 }; var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string)); - target.OnNext(6.7.ToString(CultureInfo.CurrentCulture)); + target.OnNext("6.7"); Assert.Equal(6.7, data.DoubleValue); @@ -318,15 +318,15 @@ namespace Avalonia.Markup.UnitTests.Data target.Subscribe(x => result.Add(x)); target.OnNext(1.2); - target.OnNext(3.4.ToString(CultureInfo.CurrentCulture)); + target.OnNext("3.4"); target.OnNext("bar"); Assert.Equal( new[] { - new BindingNotification(5.6.ToString(CultureInfo.CurrentCulture)), - new BindingNotification(1.2.ToString(CultureInfo.CurrentCulture)), - new BindingNotification(3.4.ToString(CultureInfo.CurrentCulture)), + new BindingNotification("5.6"), + new BindingNotification("1.2"), + new BindingNotification("3.4"), new BindingNotification( new InvalidCastException("'bar' is not a valid number."), BindingErrorType.Error) From 2af15cbee471984b75df9e64912cf14abb321963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Wed, 4 Oct 2017 23:15:54 +0100 Subject: [PATCH 08/12] Make WindowState bindable. Requested changes. --- src/Avalonia.Controls/Platform/IWindowImpl.cs | 5 ++++ src/Avalonia.Controls/Window.cs | 26 ++++++++++++------- .../Remote/PreviewerWindowImpl.cs | 1 + src/Avalonia.DesignerSupport/Remote/Stubs.cs | 1 + src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs | 2 +- src/Gtk/Avalonia.Gtk3/WindowImpl.cs | 24 ++++++++++++++--- src/OSX/Avalonia.MonoMac/WindowImpl.cs | 16 ++++++++++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 19 ++++++++++++-- 8 files changed, 79 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs index 3f2c977718..f1f3925133 100644 --- a/src/Avalonia.Controls/Platform/IWindowImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs @@ -16,6 +16,11 @@ namespace Avalonia.Platform /// WindowState WindowState { get; set; } + /// + /// Gets or sets a method called when the minimized/maximized state of the window changes. + /// + Action WindowStateChanged { get; set; } + /// /// Sets the title of the window. /// diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 16ee3a46b3..689b664289 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -67,13 +67,19 @@ namespace Avalonia.Controls /// public static readonly StyledProperty HasSystemDecorationsProperty = AvaloniaProperty.Register(nameof(HasSystemDecorations), true); - + /// /// Enables or disables the taskbar icon /// public static readonly StyledProperty ShowInTaskbarProperty = AvaloniaProperty.Register(nameof(ShowInTaskbar), true); - + + /// + /// Enables or disables the taskbar icon + /// + public static readonly StyledProperty WindowStateProperty = + AvaloniaProperty.Register(nameof(WindowState)); + /// /// Defines the property. /// @@ -118,6 +124,9 @@ namespace Avalonia.Controls IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); CanResizeProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.CanResize((bool)e.NewValue)); + + WindowStateProperty.Changed.AddClassHandler( + (w, e) => { if (w.PlatformImpl != null) w.PlatformImpl.WindowState = (WindowState)e.NewValue; }); } /// @@ -138,8 +147,11 @@ namespace Avalonia.Controls impl.Closing = HandleClosing; _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size); Screens = new Screens(PlatformImpl?.Screen); - } + if (PlatformImpl != null) + PlatformImpl.WindowStateChanged = s => WindowState = s; + } + /// event EventHandler INameScope.Registered { @@ -205,12 +217,8 @@ namespace Avalonia.Controls /// public WindowState WindowState { - get { return PlatformImpl?.WindowState ?? WindowState.Normal; } - set - { - if (PlatformImpl != null) - PlatformImpl.WindowState = value; - } + get { return GetValue(WindowStateProperty); } + set { SetValue(WindowStateProperty, value); } } /// diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs index 9750b46aa2..ef16d06b60 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs @@ -42,6 +42,7 @@ namespace Avalonia.DesignerSupport.Remote public Func Closing { get; set; } public IPlatformHandle Handle { get; } public WindowState WindowState { get; set; } + public Action WindowStateChanged { get; set; } public Size MaxClientSize { get; } = new Size(4096, 4096); public event Action LostFocus; diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index ee8569d748..e749d10468 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -32,6 +32,7 @@ namespace Avalonia.DesignerSupport.Remote public Point Position { get; set; } public Action PositionChanged { get; set; } public WindowState WindowState { get; set; } + public Action WindowStateChanged { get; set; } public IRenderer CreateRenderer(IRenderRoot root) => new ImmediateRenderer(root); public void Dispose() { diff --git a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs index 0ebfea998a..94537d3475 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs @@ -241,7 +241,7 @@ namespace Avalonia.Gtk3 return true; } - void ConnectEvent(string name, Native.D.signal_onevent handler) + protected void ConnectEvent(string name, Native.D.signal_onevent handler) => Disposables.Add(Signal.Connect(GtkWidget, name, handler)); void Connect(string name, T handler) => Disposables.Add(Signal.Connect(GtkWidget, name, handler)); diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs index 2d309e19d4..bae34db6f3 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs @@ -1,18 +1,34 @@ using System; -using System.Linq.Expressions; using Avalonia.Controls; using Avalonia.Gtk3.Interop; using Avalonia.Platform; -using System.Runtime.InteropServices; namespace Avalonia.Gtk3 { class WindowImpl : WindowBaseImpl, IWindowImpl { + private WindowState _lastWindowState; + public WindowImpl() : base(Native.GtkWindowNew(GtkWindowType.TopLevel)) { } + protected unsafe override bool OnStateChanged(IntPtr w, IntPtr pev, IntPtr userData) + { + var windowStateEvent = (GdkEventWindowState*)pev; + var newWindowState = windowStateEvent->new_window_state; + var windowState = newWindowState.HasFlag(GdkWindowState.Iconified) ? WindowState.Minimized + : (newWindowState.HasFlag(GdkWindowState.Maximized) ? WindowState.Maximized : WindowState.Normal); + + if (windowState != _lastWindowState) + { + _lastWindowState = windowState; + WindowStateChanged?.Invoke(windowState); + } + + return base.OnStateChanged(w, pev, userData); + } + public void SetTitle(string title) { using (var t = new Utf8Buffer(title)) @@ -43,7 +59,9 @@ namespace Avalonia.Gtk3 } } } - + + public Action WindowStateChanged { get; set; } + public IDisposable ShowDialog() { Native.GtkWindowSetModal(GtkWidget, true); diff --git a/src/OSX/Avalonia.MonoMac/WindowImpl.cs b/src/OSX/Avalonia.MonoMac/WindowImpl.cs index d01cbd6ae3..a2f8df6791 100644 --- a/src/OSX/Avalonia.MonoMac/WindowImpl.cs +++ b/src/OSX/Avalonia.MonoMac/WindowImpl.cs @@ -12,10 +12,24 @@ namespace Avalonia.MonoMac public bool IsResizable = true; public CGRect? UndecoratedLastUnmaximizedFrame; + private WindowState _lastWindowState; + public WindowImpl() { UpdateStyle(); Window.SetCanBecomeKeyAndMain(); + + Window.DidResize += delegate + { + var windowState = Window.IsMiniaturized ? WindowState.Minimized + : (IsZoomed ? WindowState.Maximized : WindowState.Normal); + + if (windowState != _lastWindowState) + { + _lastWindowState = windowState; + WindowStateChanged?.Invoke(windowState); + } + }; } public WindowState WindowState @@ -49,6 +63,8 @@ namespace Avalonia.MonoMac } } + public Action WindowStateChanged { get; set; } + bool IsZoomed => IsDecorated ? Window.IsZoomed : UndecoratedIsMaximized; public bool UndecoratedIsMaximized => Window.Frame == Window.Screen.VisibleFrame; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index cf6cb40e58..5aa1b30512 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -34,6 +34,7 @@ namespace Avalonia.Win32 private bool _resizable = true; private double _scaling = 1; private WindowState _showWindowState; + private WindowState _lastWindowState; private FramebufferManager _framebuffer; private OleDropTarget _dropTarget; private Size _minSize; @@ -77,6 +78,8 @@ namespace Avalonia.Win32 public Action PositionChanged { get; set; } + public Action WindowStateChanged { get; set; } + public Thickness BorderThickness { get @@ -617,14 +620,25 @@ namespace Avalonia.Win32 return IntPtr.Zero; case UnmanagedMethods.WindowsMessage.WM_SIZE: + var size = (UnmanagedMethods.SizeCommand)wParam; + if (Resized != null && - (wParam == (IntPtr)UnmanagedMethods.SizeCommand.Restored || - wParam == (IntPtr)UnmanagedMethods.SizeCommand.Maximized)) + (size == UnmanagedMethods.SizeCommand.Restored || + size == UnmanagedMethods.SizeCommand.Maximized)) { var clientSize = new Size(ToInt32(lParam) & 0xffff, ToInt32(lParam) >> 16); Resized(clientSize / Scaling); } + var windowState = size == SizeCommand.Maximized ? WindowState.Maximized + : (size == SizeCommand.Minimized ? WindowState.Minimized : WindowState.Normal); + + if (windowState != _lastWindowState) + { + _lastWindowState = windowState; + WindowStateChanged?.Invoke(windowState); + } + return IntPtr.Zero; case UnmanagedMethods.WindowsMessage.WM_MOVE: @@ -654,6 +668,7 @@ namespace Avalonia.Win32 (Screen as ScreenImpl)?.InvalidateScreensCache(); return IntPtr.Zero; } + #if USE_MANAGED_DRAG if (_managedDrag.PreprocessInputEvent(ref e)) From 0b055771f54618b02d6ccaca9939cad958bda452 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 16 May 2018 00:40:38 +0200 Subject: [PATCH 09/12] Generate version numbers for assemblies. Don't set `false` because this means that assemblies won't get versions. --- src/Avalonia.Controls/Avalonia.Controls.csproj | 2 -- .../Avalonia.DotNetCoreRuntime.csproj | 1 - .../Avalonia.DotNetFrameworkRuntime.csproj | 1 - src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj | 1 - src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj | 1 - src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs | 1 - src/Markup/Avalonia.Markup/Avalonia.Markup.csproj | 1 - src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj | 1 - src/Skia/Avalonia.Skia/Avalonia.Skia.csproj | 1 - src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj | 4 ---- src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs | 1 - 11 files changed, 15 deletions(-) diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index ae777115f4..044ed47920 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -1,8 +1,6 @@  netstandard2.0 - latest - false diff --git a/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj b/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj index 5f40d347b7..f80462e958 100644 --- a/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj +++ b/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj @@ -1,7 +1,6 @@  netcoreapp2.0 - false $(DefineConstants);DOTNETCORE diff --git a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj index eca5b89419..34022a87f6 100644 --- a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj +++ b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj @@ -1,7 +1,6 @@  net461 - false bin\$(Configuration)\Avalonia.DotNetFrameworkRuntime.xml $(DefineConstants);FULLDOTNET true diff --git a/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj b/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj index 520687c3ef..6e65264db0 100644 --- a/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj +++ b/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj @@ -3,7 +3,6 @@ netstandard2.0 False False - false CS0436 diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 49f6ca9eeb..2d3a43a909 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -3,7 +3,6 @@ netstandard2.0 False false - false true diff --git a/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs b/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs index 0b42bedfa8..1b0e056baf 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs @@ -5,7 +5,6 @@ using System.Reflection; using Avalonia.Metadata; using System.Runtime.CompilerServices; -[assembly: AssemblyTitle("Avalonia.Markup.Xaml")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Xaml.Data")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Xaml.MarkupExtensions")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Xaml.Styling")] diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj index 9707662701..a3b7d07784 100644 --- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj +++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj @@ -1,7 +1,6 @@  netstandard2.0 - false diff --git a/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj index cbf91e818a..e675b43389 100644 --- a/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj +++ b/src/OSX/Avalonia.MonoMac/Avalonia.MonoMac.csproj @@ -2,7 +2,6 @@ netstandard2.0 True - false diff --git a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj index ade7f8778a..03618e00f5 100644 --- a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj +++ b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj @@ -1,7 +1,6 @@  netstandard2.0 - false Avalonia.Skia Avalonia.Skia true diff --git a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj index a84c373886..426a9a85a3 100644 --- a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj +++ b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj @@ -1,12 +1,8 @@  netstandard2.0 - false - - Properties\SharedAssemblyInfo.cs - UnmanagedMethods.cs diff --git a/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs b/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs index c1c5b24048..a09d5c2d1c 100644 --- a/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs +++ b/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs @@ -5,7 +5,6 @@ using System.Reflection; using Avalonia.Platform; using Avalonia.Direct2D1; -[assembly: AssemblyTitle("Avalonia.Direct2D1")] [assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 1, "Direct2D1", typeof(Direct2D1Platform), nameof(Direct2D1Platform.Initialize), typeof(Direct2DChecker))] From ffeefabf1c210cbbae6e23b56ca7bb5074afd44e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 16 May 2018 00:46:48 +0200 Subject: [PATCH 10/12] Update ControlCatalog.Desktop output dir. The output path has changed from `bin\Release` to `bin\Release\net461`. --- parameters.cake | 2 +- tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/parameters.cake b/parameters.cake index e224cce151..06cb9ba2b2 100644 --- a/parameters.cake +++ b/parameters.cake @@ -115,7 +115,7 @@ public class Parameters FileZipSuffix = Version + ".zip"; ZipCoreArtifacts = ZipRoot.CombineWithFilePath("Avalonia-" + FileZipSuffix); - ZipSourceControlCatalogDesktopDirs = (DirectoryPath)context.Directory("./samples/ControlCatalog.Desktop/bin/" + DirSuffix); + ZipSourceControlCatalogDesktopDirs = (DirectoryPath)context.Directory("./samples/ControlCatalog.Desktop/bin/" + DirSuffix + "/net461); ZipTargetControlCatalogDesktopDirs = ZipRoot.CombineWithFilePath("ControlCatalog.Desktop-" + FileZipSuffix); } } diff --git a/tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs b/tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs index 32da700acc..f320fa6fe8 100644 --- a/tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs +++ b/tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs @@ -11,9 +11,9 @@ namespace Avalonia.DesignerSupport.Tests { public class DesignerSupportTests { - [Theory(Skip = "Skipping for now as failing on AppVeyor"), + [Theory, InlineData(@"Avalonia.DesignerSupport.TestApp.exe", @"..\..\tests\Avalonia.DesignerSupport.TestApp\MainWindow.xaml"), - InlineData(@"..\..\samples\ControlCatalog.Desktop\bin\$BUILD\ControlCatalog.dll", @"..\..\samples\ControlCatalog\MainWindow.xaml")] + InlineData(@"..\..\samples\ControlCatalog.Desktop\bin\$BUILD\net461\ControlCatalog.dll", @"..\..\samples\ControlCatalog\MainWindow.xaml")] public void DesignerApiShoudBeOperational(string outputDir, string xamlFile) { var xaml = File.ReadAllText(xamlFile); From 6c3305d1b8df3af75dc3eb9e3f97a63a91ed8dc4 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 16 May 2018 01:16:55 +0200 Subject: [PATCH 11/12] Missing quote. --- parameters.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parameters.cake b/parameters.cake index 06cb9ba2b2..9e6eaa9d27 100644 --- a/parameters.cake +++ b/parameters.cake @@ -115,7 +115,7 @@ public class Parameters FileZipSuffix = Version + ".zip"; ZipCoreArtifacts = ZipRoot.CombineWithFilePath("Avalonia-" + FileZipSuffix); - ZipSourceControlCatalogDesktopDirs = (DirectoryPath)context.Directory("./samples/ControlCatalog.Desktop/bin/" + DirSuffix + "/net461); + ZipSourceControlCatalogDesktopDirs = (DirectoryPath)context.Directory("./samples/ControlCatalog.Desktop/bin/" + DirSuffix + "/net461"); ZipTargetControlCatalogDesktopDirs = ZipRoot.CombineWithFilePath("ControlCatalog.Desktop-" + FileZipSuffix); } } From 25ed770790b53be03b64b6923e6c5a77a1db2745 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 16 May 2018 17:51:14 +0200 Subject: [PATCH 12/12] Fix raising property changed for attached property. Raise a property changed event for attached properties when `InheritanceParent` is set. This was broken by #1499. Fixes #1576 --- src/Avalonia.Base/AvaloniaObject.cs | 5 +- .../AvaloniaObjectTests_Inheritance.cs | 46 +++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 68b9871fd1..1a2db9fc3d 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -139,8 +139,9 @@ namespace Avalonia { _inheritanceParent.PropertyChanged -= ParentPropertyChanged; } - - var inherited = (from property in AvaloniaPropertyRegistry.Instance.GetRegistered(this) + var properties = AvaloniaPropertyRegistry.Instance.GetRegistered(this) + .Concat(AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(this.GetType())); + var inherited = (from property in properties where property.Inherits select new { diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs index 5bd888eeb4..ef6e03a60b 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs @@ -38,6 +38,26 @@ namespace Avalonia.Base.UnitTests Assert.True(raised); } + [Fact] + public void Setting_InheritanceParent_Raises_PropertyChanged_For_Attached_Property_When_Value_Changed_In_Parent() + { + bool raised = false; + + Class1 parent = new Class1(); + parent.SetValue(AttachedOwner.AttachedProperty, "changed"); + + Class2 child = new Class2(); + child.PropertyChanged += (s, e) => + raised = s == child && + e.Property == AttachedOwner.AttachedProperty && + (string)e.OldValue == null && + (string)e.NewValue == "changed"; + + child.Parent = parent; + + Assert.True(raised); + } + [Fact] public void Setting_InheritanceParent_Doesnt_Raise_PropertyChanged_When_Local_Value_Set() { @@ -75,6 +95,26 @@ namespace Avalonia.Base.UnitTests Assert.True(raised); } + [Fact] + public void Setting_Value_Of_Attached_Property_In_InheritanceParent_Raises_PropertyChanged() + { + bool raised = false; + + Class1 parent = new Class1(); + + Class2 child = new Class2(); + child.PropertyChanged += (s, e) => + raised = s == child && + e.Property == AttachedOwner.AttachedProperty && + (string)e.OldValue == null && + (string)e.NewValue == "changed"; + child.Parent = parent; + + parent.SetValue(AttachedOwner.AttachedProperty, "changed"); + + Assert.True(raised); + } + private class Class1 : AvaloniaObject { public static readonly StyledProperty FooProperty = @@ -97,5 +137,11 @@ namespace Avalonia.Base.UnitTests set { InheritanceParent = value; } } } + + private class AttachedOwner : AvaloniaObject + { + public static readonly AttachedProperty AttachedProperty = + AvaloniaProperty.RegisterAttached("Attached", inherits: true); + } } }