diff --git a/parameters.cake b/parameters.cake index e224cce151..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); + ZipSourceControlCatalogDesktopDirs = (DirectoryPath)context.Directory("./samples/ControlCatalog.Desktop/bin/" + DirSuffix + "/net461"); ZipTargetControlCatalogDesktopDirs = ZipRoot.CombineWithFilePath("ControlCatalog.Desktop-" + FileZipSuffix); } } 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/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.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/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/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 3cc750e20d..6badf91367 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. /// @@ -90,19 +91,12 @@ 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); } - /// - /// 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,96 +322,68 @@ 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)); - var size = availableSizeMinusMargins; + var availableSize = finalSize; + var sizeForChild = availableSize; 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)); + sizeForChild = sizeForChild.WithWidth(Math.Min(sizeForChild.Width, DesiredSize.Width)); } if (verticalContentAlignment != VerticalAlignment.Stretch) { - size = size.WithHeight(Math.Min(size.Height, DesiredSize.Height - padding.Top - padding.Bottom)); + 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); - availableSizeMinusMargins = new Size( - Math.Ceiling(availableSizeMinusMargins.Width * scale) / scale, - Math.Ceiling(availableSizeMinusMargins.Height * scale) / scale); + sizeForChild = new Size( + Math.Ceiling(sizeForChild.Width * scale) / scale, + Math.Ceiling(sizeForChild.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 - size.Width) / 2; + originX += (availableSize.Width - sizeForChild.Width) / 2; break; case HorizontalAlignment.Right: - originX += availableSizeMinusMargins.Width - size.Width; + originX += availableSize.Width - sizeForChild.Width; break; } switch (verticalContentAlignment) { case VerticalAlignment.Center: - originY += (availableSizeMinusMargins.Height - size.Height) / 2; + originY += (availableSize.Height - sizeForChild.Height) / 2; break; case VerticalAlignment.Bottom: - originY += availableSizeMinusMargins.Height - size.Height; + originY += availableSize.Height - sizeForChild.Height; break; } @@ -427,11 +393,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, sizeForChild.Width, sizeForChild.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..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 = new BorderCoordinates(cornerRadius, borderThickness, false); - + 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 = new BorderCoordinates(cornerRadius, borderThickness, true); + 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,176 +104,156 @@ namespace Avalonia.Controls.Utils } } - private static void CreateGeometry(StreamGeometryContext context, Rect boundRect, BorderCoordinates borderCoordinates) - { - 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); + private class BorderGeometryKeypoints + { + internal BorderGeometryKeypoints(Rect boundRect, Thickness borderThickness, CornerRadius cornerRadius, bool inner) + { + var left = 0.5 * borderThickness.Left; + var top = 0.5 * borderThickness.Top; + var right = 0.5 * borderThickness.Right; + var bottom = 0.5 * borderThickness.Bottom; + double leftTopY; + double topLeftX; + double topRightX; + double rightTopY; + double rightBottomY; + double bottomRightX; + double bottomLeftX; + double leftBottomY; - if (topLeft.X > topRight.X) - { - var scaledX = borderCoordinates.LeftTop / (borderCoordinates.LeftTop + borderCoordinates.RightTop) * boundRect.Width; - topLeft = new Point(scaledX, topLeft.Y); - topRight = new Point(scaledX, topRight.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 (rightTop.Y > rightBottom.Y) - { - var scaledY = borderCoordinates.TopRight / (borderCoordinates.TopRight + borderCoordinates.BottomRight) * boundRect.Height; - rightTop = new Point(rightTop.X, scaledY); - rightBottom = new Point(rightBottom.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 = borderCoordinates.LeftBottom / (borderCoordinates.LeftBottom + borderCoordinates.RightBottom) * boundRect.Width; - bottomRight = new Point(scaledX, bottomRight.Y); - bottomLeft = new Point(scaledX, bottomLeft.Y); - } + 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 = borderCoordinates.TopLeft / (borderCoordinates.TopLeft + borderCoordinates.BottomLeft) * boundRect.Height; - leftBottom = new Point(leftBottom.X, scaledY); - leftTop = new Point(leftTop.X, scaledY); + 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 BorderCoordinates - { - internal BorderCoordinates(CornerRadius cornerRadius, Thickness borderThickness, bool isOuter) - { - 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) - { - 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; - } - } - else - { - 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; - } - } } diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 91d6c874d3..c4a09ac043 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/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/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; + } } } diff --git a/src/Avalonia.Themes.Default/ButtonSpinner.xaml b/src/Avalonia.Themes.Default/ButtonSpinner.xaml index a08f5b5e9b..2558b7c2f8 100644 --- a/src/Avalonia.Themes.Default/ButtonSpinner.xaml +++ b/src/Avalonia.Themes.Default/ButtonSpinner.xaml @@ -46,12 +46,15 @@ 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}" + Padding="{TemplateBinding Padding}"/> @@ -68,8 +71,8 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Margin="{TemplateBinding Padding}" - HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" - VerticalAlignment="{TemplateBinding VerticalContentAlignment}"> + HorizontalAlignment="{TemplateBinding HorizontalAlignment}" + VerticalAlignment="{TemplateBinding VerticalAlignment}"> @@ -77,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 7584ae0dfe..d76eecee84 100644 --- a/src/Avalonia.Themes.Default/ContentControl.xaml +++ b/src/Avalonia.Themes.Default/ContentControl.xaml @@ -7,7 +7,9 @@ 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/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 a0405f2875..fc2600c1a9 100644 --- a/src/Avalonia.Themes.Default/ListBoxItem.xaml +++ b/src/Avalonia.Themes.Default/ListBoxItem.xaml @@ -9,7 +9,9 @@ BorderThickness="{TemplateBinding BorderThickness}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" - Padding="{TemplateBinding Padding}"/> + Padding="{TemplateBinding Padding}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" + 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}"/> 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/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/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/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))] diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index be28b64c5a..7b9f8ee066 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 @@ -626,14 +629,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: @@ -663,6 +677,7 @@ namespace Avalonia.Win32 (Screen as ScreenImpl)?.InvalidateScreensCache(); return IntPtr.Zero; } + #if USE_MANAGED_DRAG if (_managedDrag.PreprocessInputEvent(ref e)) 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); + } } } 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.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); diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs index f42e0daf2a..2e0f048e21 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs @@ -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");