From 687037ab50ad9d0cc62e0272a3b4468f002c631e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Mon, 10 Jun 2019 19:35:09 +0200 Subject: [PATCH 01/12] Initial port of WPF DockPanel --- src/Avalonia.Controls/DockPanel.cs | 164 +++++++++++++++++------------ 1 file changed, 97 insertions(+), 67 deletions(-) diff --git a/src/Avalonia.Controls/DockPanel.cs b/src/Avalonia.Controls/DockPanel.cs index e147fe1a52..e580283d85 100644 --- a/src/Avalonia.Controls/DockPanel.cs +++ b/src/Avalonia.Controls/DockPanel.cs @@ -70,107 +70,137 @@ namespace Avalonia.Controls set { SetValue(LastChildFillProperty, value); } } - /// + /// + /// Updates DesiredSize of the DockPanel. Called by parent Control. This is the first pass of layout. + /// + /// + /// Children are measured based on their sizing properties and . + /// Each child is allowed to consume all of the space on the side on which it is docked; Left/Right docked + /// children are granted all vertical space for their entire width, and Top/Bottom docked children are + /// granted all horizontal space for their entire height. + /// + /// Constraint size is an "upper limit" that the return value should not exceed. + /// The Panel's desired size. protected override Size MeasureOverride(Size constraint) { - double usedWidth = 0.0; - double usedHeight = 0.0; - double maximumWidth = 0.0; - double maximumHeight = 0.0; + var children = Children; - // Measure each of the Children - foreach (Control element in Children) + double parentWidth = 0; // Our current required width due to children thus far. + double parentHeight = 0; // Our current required height due to children thus far. + double accumulatedWidth = 0; // Total width consumed by children. + double accumulatedHeight = 0; // Total height consumed by children. + + for (int i = 0, count = children.Count; i < count; ++i) { - // Get the child's desired size - Size remainingSize = new Size( - Math.Max(0.0, constraint.Width - usedWidth), - Math.Max(0.0, constraint.Height - usedHeight)); - element.Measure(remainingSize); - Size desiredSize = element.DesiredSize; - - // Decrease the remaining space for the rest of the children - switch (GetDock(element)) + var child = children[i]; + Size childConstraint; // Contains the suggested input constraint for this child. + Size childDesiredSize; // Contains the return size from child measure. + + if (child == null) + { continue; } + + // Child constraint is the remaining size; this is total size minus size consumed by previous children. + childConstraint = new Size(Math.Max(0.0, constraint.Width - accumulatedWidth), + Math.Max(0.0, constraint.Height - accumulatedHeight)); + + // Measure child. + child.Measure(childConstraint); + childDesiredSize = child.DesiredSize; + + // Now, we adjust: + // 1. Size consumed by children (accumulatedSize). This will be used when computing subsequent + // children to determine how much space is remaining for them. + // 2. Parent size implied by this child (parentSize) when added to the current children (accumulatedSize). + // This is different from the size above in one respect: A Dock.Left child implies a height, but does + // not actually consume any height for subsequent children. + // If we accumulate size in a given dimension, the next child (or the end conditions after the child loop) + // will deal with computing our minimum size (parentSize) due to that accumulation. + // Therefore, we only need to compute our minimum size (parentSize) in dimensions that this child does + // not accumulate: Width for Top/Bottom, Height for Left/Right. + switch (DockPanel.GetDock((Control)child)) { case Dock.Left: case Dock.Right: - maximumHeight = Math.Max(maximumHeight, usedHeight + desiredSize.Height); - usedWidth += desiredSize.Width; + parentHeight = Math.Max(parentHeight, accumulatedHeight + childDesiredSize.Height); + accumulatedWidth += childDesiredSize.Width; break; + case Dock.Top: case Dock.Bottom: - maximumWidth = Math.Max(maximumWidth, usedWidth + desiredSize.Width); - usedHeight += desiredSize.Height; + parentWidth = Math.Max(parentWidth, accumulatedWidth + childDesiredSize.Width); + accumulatedHeight += childDesiredSize.Height; break; } } - maximumWidth = Math.Max(maximumWidth, usedWidth); - maximumHeight = Math.Max(maximumHeight, usedHeight); - return new Size(maximumWidth, maximumHeight); + // Make sure the final accumulated size is reflected in parentSize. + parentWidth = Math.Max(parentWidth, accumulatedWidth); + parentHeight = Math.Max(parentHeight, accumulatedHeight); + + return (new Size(parentWidth, parentHeight)); } - /// + /// + /// DockPanel computes a position and final size for each of its children based upon their + /// enum and sizing properties. + /// + /// Size that DockPanel will assume to position children. protected override Size ArrangeOverride(Size arrangeSize) { - double left = 0.0; - double top = 0.0; - double right = 0.0; - double bottom = 0.0; - - // Arrange each of the Children var children = Children; - int dockedCount = children.Count - (LastChildFill ? 1 : 0); - int index = 0; + int totalChildrenCount = children.Count; + int nonFillChildrenCount = totalChildrenCount - (LastChildFill ? 1 : 0); - foreach (Control element in children) + double accumulatedLeft = 0; + double accumulatedTop = 0; + double accumulatedRight = 0; + double accumulatedBottom = 0; + + for (int i = 0; i < totalChildrenCount; ++i) { - // Determine the remaining space left to arrange the element - Rect remainingRect = new Rect( - left, - top, - Math.Max(0.0, arrangeSize.Width - left - right), - Math.Max(0.0, arrangeSize.Height - top - bottom)); - - // Trim the remaining Rect to the docked size of the element - // (unless the element should fill the remaining space because - // of LastChildFill) - if (index < dockedCount) + var child = children[i]; + if (child == null) + { continue; } + + Size childDesiredSize = child.DesiredSize; + Rect rcChild = new Rect( + accumulatedLeft, + accumulatedTop, + Math.Max(0.0, arrangeSize.Width - (accumulatedLeft + accumulatedRight)), + Math.Max(0.0, arrangeSize.Height - (accumulatedTop + accumulatedBottom))); + + if (i < nonFillChildrenCount) { - Size desiredSize = element.DesiredSize; - switch (GetDock(element)) + switch (DockPanel.GetDock((Control)child)) { case Dock.Left: - left += desiredSize.Width; - remainingRect = remainingRect.WithWidth(desiredSize.Width); - break; - case Dock.Top: - top += desiredSize.Height; - remainingRect = remainingRect.WithHeight(desiredSize.Height); + accumulatedLeft += childDesiredSize.Width; + rcChild = rcChild.WithWidth(childDesiredSize.Width); break; + case Dock.Right: - right += desiredSize.Width; - remainingRect = new Rect( - Math.Max(0.0, arrangeSize.Width - right), - remainingRect.Y, - desiredSize.Width, - remainingRect.Height); + accumulatedRight += childDesiredSize.Width; + rcChild = rcChild.WithX(Math.Max(0.0, arrangeSize.Width - accumulatedRight)); + rcChild = rcChild.WithWidth(childDesiredSize.Width); break; + + case Dock.Top: + accumulatedTop += childDesiredSize.Height; + rcChild = rcChild.WithHeight(childDesiredSize.Height); + break; + case Dock.Bottom: - bottom += desiredSize.Height; - remainingRect = new Rect( - remainingRect.X, - Math.Max(0.0, arrangeSize.Height - bottom), - remainingRect.Width, - desiredSize.Height); + accumulatedBottom += childDesiredSize.Height; + rcChild = rcChild.WithY(Math.Max(0.0, arrangeSize.Height - accumulatedBottom)); + rcChild = rcChild.WithHeight(childDesiredSize.Height); break; } } - element.Arrange(remainingRect); - index++; + child.Arrange(rcChild); } - return arrangeSize; + return (arrangeSize); } } } From 1c2e27015523cbe13ce0aa40a02340d32d27d9c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Mon, 10 Jun 2019 21:25:27 +0200 Subject: [PATCH 02/12] Initial port of WPF StackPanel --- src/Avalonia.Controls/StackPanel.cs | 136 +++++++++++++++------------- 1 file changed, 73 insertions(+), 63 deletions(-) diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index c29faa1b4d..d456e462f5 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -2,9 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Linq; -using Avalonia.Input; -using Avalonia.Layout; +using Avalonia.Input namespace Avalonia.Controls { @@ -155,106 +153,118 @@ namespace Avalonia.Controls } /// - /// Measures the control. + /// General StackPanel layout behavior is to grow unbounded in the "stacking" direction (Size To Content). + /// Children in this dimension are encouraged to be as large as they like. In the other dimension, + /// StackPanel will assume the maximum size of its children. /// - /// The available size. - /// The desired size of the control. - protected override Size MeasureOverride(Size availableSize) + /// Constraint + /// Desired size + protected override Size MeasureOverride(Size constraint) { - double childAvailableWidth = double.PositiveInfinity; - double childAvailableHeight = double.PositiveInfinity; + Size stackDesiredSize = new Size(); + var children = Children; + Size layoutSlotSize = constraint; + bool fHorizontal = (Orientation == Orientation.Horizontal); + double spacing = Spacing; + bool hasVisibleChild = false; - if (Orientation == Orientation.Vertical) + // + // Initialize child sizing and iterator data + // Allow children as much size as they want along the stack. + // + if (fHorizontal) { - childAvailableWidth = availableSize.Width; - - if (!double.IsNaN(Width)) - { - childAvailableWidth = Width; - } - - childAvailableWidth = Math.Min(childAvailableWidth, MaxWidth); - childAvailableWidth = Math.Max(childAvailableWidth, MinWidth); + layoutSlotSize = layoutSlotSize.WithWidth(Double.PositiveInfinity); } else { - childAvailableHeight = availableSize.Height; + layoutSlotSize = layoutSlotSize.WithHeight(Double.PositiveInfinity); + } - if (!double.IsNaN(Height)) - { - childAvailableHeight = Height; - } + // + // Iterate through children. + // While we still supported virtualization, this was hidden in a child iterator (see source history). + // + for (int i = 0, count = children.Count; i < count; ++i) + { + // Get next child. + var child = children[i]; - childAvailableHeight = Math.Min(childAvailableHeight, MaxHeight); - childAvailableHeight = Math.Max(childAvailableHeight, MinHeight); - } + if (child == null) + { continue; } - double measuredWidth = 0; - double measuredHeight = 0; - double spacing = Spacing; - bool hasVisibleChild = Children.Any(c => c.IsVisible); + if (child.IsVisible) + { + hasVisibleChild = true; + } - foreach (Control child in Children) - { - child.Measure(new Size(childAvailableWidth, childAvailableHeight)); - Size size = child.DesiredSize; + // Measure the child. + child.Measure(layoutSlotSize); + Size childDesiredSize = child.DesiredSize; - if (Orientation == Orientation.Vertical) + // Accumulate child size. + if (fHorizontal) { - measuredHeight += size.Height + (child.IsVisible ? spacing : 0); - measuredWidth = Math.Max(measuredWidth, size.Width); + stackDesiredSize = stackDesiredSize.WithWidth(stackDesiredSize.Width + childDesiredSize.Width + (child.IsVisible ? spacing : 0)); + stackDesiredSize = stackDesiredSize.WithHeight(Math.Max(stackDesiredSize.Height, childDesiredSize.Height)); } else { - measuredWidth += size.Width + (child.IsVisible ? spacing : 0); - measuredHeight = Math.Max(measuredHeight, size.Height); + stackDesiredSize = stackDesiredSize.WithWidth(Math.Max(stackDesiredSize.Width, childDesiredSize.Width)); + stackDesiredSize = stackDesiredSize.WithHeight(stackDesiredSize.Height + childDesiredSize.Height + (child.IsVisible ? spacing : 0)); } } if (Orientation == Orientation.Vertical) { - measuredHeight -= (hasVisibleChild ? spacing : 0); + stackDesiredSize = stackDesiredSize.WithHeight(stackDesiredSize.Height - (hasVisibleChild ? spacing : 0)); } else { - measuredWidth -= (hasVisibleChild ? spacing : 0); + stackDesiredSize = stackDesiredSize.WithWidth(stackDesiredSize.Width - (hasVisibleChild ? spacing : 0)); } - return new Size(measuredWidth, measuredHeight).Constrain(availableSize); + return stackDesiredSize; } - /// + /// + /// Content arrangement. + /// + /// Arrange size protected override Size ArrangeOverride(Size finalSize) { - var orientation = Orientation; + var children = Children; + bool fHorizontal = (Orientation == Orientation.Horizontal); + Rect rcChild = new Rect(finalSize); + double previousChildSize = 0.0; var spacing = Spacing; - var finalRect = new Rect(finalSize); - var pos = 0.0; - foreach (Control child in Children) + // + // Arrange and Position Children. + // + for (int i = 0, count = children.Count; i < count; ++i) { - if (!child.IsVisible) - { - continue; - } + var child = children[i]; - double childWidth = child.DesiredSize.Width; - double childHeight = child.DesiredSize.Height; + if (child == null) + { continue; } - if (orientation == Orientation.Vertical) + if (fHorizontal) { - var rect = new Rect(0, pos, childWidth, childHeight) - .Align(finalRect, child.HorizontalAlignment, VerticalAlignment.Top); - ArrangeChild(child, rect, finalSize, orientation); - pos += childHeight + spacing; + rcChild = rcChild.WithX(rcChild.X + previousChildSize); + previousChildSize = child.DesiredSize.Width; + rcChild = rcChild.WithWidth(previousChildSize); + rcChild = rcChild.WithHeight(Math.Max(finalSize.Height, child.DesiredSize.Height) + (child.IsVisible ? spacing : 0)); } else { - var rect = new Rect(pos, 0, childWidth, childHeight) - .Align(finalRect, HorizontalAlignment.Left, child.VerticalAlignment); - ArrangeChild(child, rect, finalSize, orientation); - pos += childWidth + spacing; + rcChild = rcChild.WithY(rcChild.Y + previousChildSize); + previousChildSize = child.DesiredSize.Height; + rcChild = rcChild.WithHeight(previousChildSize); + rcChild = rcChild.WithWidth(Math.Max(finalSize.Width, child.DesiredSize.Width) + (child.IsVisible ? spacing : 0)); } + + child.Arrange(rcChild); } return finalSize; From 8304eaab8502ac0e288b7c31bd40e721df7f8a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Mon, 10 Jun 2019 21:26:14 +0200 Subject: [PATCH 03/12] Fix --- src/Avalonia.Controls/StackPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index d456e462f5..6caffb8686 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using Avalonia.Input +using Avalonia.Input; namespace Avalonia.Controls { From 39a696b9851e46dacd5c6b6293ec0da14f0169fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Mon, 10 Jun 2019 22:12:36 +0200 Subject: [PATCH 04/12] Fix tests --- src/Avalonia.Controls/StackPanel.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index 6caffb8686..0a64096ee9 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -215,13 +215,13 @@ namespace Avalonia.Controls } } - if (Orientation == Orientation.Vertical) + if (fHorizontal) { - stackDesiredSize = stackDesiredSize.WithHeight(stackDesiredSize.Height - (hasVisibleChild ? spacing : 0)); + stackDesiredSize = stackDesiredSize.WithWidth(stackDesiredSize.Width - (hasVisibleChild ? spacing : 0)); } else - { - stackDesiredSize = stackDesiredSize.WithWidth(stackDesiredSize.Width - (hasVisibleChild ? spacing : 0)); + { + stackDesiredSize = stackDesiredSize.WithHeight(stackDesiredSize.Height - (hasVisibleChild ? spacing : 0)); } return stackDesiredSize; @@ -252,16 +252,16 @@ namespace Avalonia.Controls if (fHorizontal) { rcChild = rcChild.WithX(rcChild.X + previousChildSize); - previousChildSize = child.DesiredSize.Width; + previousChildSize = child.DesiredSize.Width + (child.IsVisible ? spacing : 0); rcChild = rcChild.WithWidth(previousChildSize); - rcChild = rcChild.WithHeight(Math.Max(finalSize.Height, child.DesiredSize.Height) + (child.IsVisible ? spacing : 0)); + rcChild = rcChild.WithHeight(Math.Max(finalSize.Height, child.DesiredSize.Height)); } else { rcChild = rcChild.WithY(rcChild.Y + previousChildSize); - previousChildSize = child.DesiredSize.Height; + previousChildSize = child.DesiredSize.Height + (child.IsVisible ? spacing : 0); rcChild = rcChild.WithHeight(previousChildSize); - rcChild = rcChild.WithWidth(Math.Max(finalSize.Width, child.DesiredSize.Width) + (child.IsVisible ? spacing : 0)); + rcChild = rcChild.WithWidth(Math.Max(finalSize.Width, child.DesiredSize.Width)); } child.Arrange(rcChild); From 895f458fa9b5fb23c6c86389cee8abad3e8fe677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Mon, 10 Jun 2019 22:20:21 +0200 Subject: [PATCH 05/12] Update StackPanel.cs --- src/Avalonia.Controls/StackPanel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index 0a64096ee9..c2f36b94d7 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -157,13 +157,13 @@ namespace Avalonia.Controls /// Children in this dimension are encouraged to be as large as they like. In the other dimension, /// StackPanel will assume the maximum size of its children. /// - /// Constraint + /// Constraint /// Desired size - protected override Size MeasureOverride(Size constraint) + protected override Size MeasureOverride(Size availableSize) { Size stackDesiredSize = new Size(); var children = Children; - Size layoutSlotSize = constraint; + Size layoutSlotSize = availableSize; bool fHorizontal = (Orientation == Orientation.Horizontal); double spacing = Spacing; bool hasVisibleChild = false; @@ -224,7 +224,7 @@ namespace Avalonia.Controls stackDesiredSize = stackDesiredSize.WithHeight(stackDesiredSize.Height - (hasVisibleChild ? spacing : 0)); } - return stackDesiredSize; + return stackDesiredSize.Constrain(availableSize); // TODO: In WPF `.Constrain(availableSize)` is not used. } /// From 705ebc26752a1b9e956cda8ac5dd908eb1883e59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Mon, 10 Jun 2019 22:30:37 +0200 Subject: [PATCH 06/12] Update StackPanel.cs --- src/Avalonia.Controls/StackPanel.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index c2f36b94d7..fd75353364 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -205,13 +205,13 @@ namespace Avalonia.Controls // Accumulate child size. if (fHorizontal) { - stackDesiredSize = stackDesiredSize.WithWidth(stackDesiredSize.Width + childDesiredSize.Width + (child.IsVisible ? spacing : 0)); + stackDesiredSize = stackDesiredSize.WithWidth(stackDesiredSize.Width + (child.IsVisible ? spacing : 0) + childDesiredSize.Width); stackDesiredSize = stackDesiredSize.WithHeight(Math.Max(stackDesiredSize.Height, childDesiredSize.Height)); } else { stackDesiredSize = stackDesiredSize.WithWidth(Math.Max(stackDesiredSize.Width, childDesiredSize.Width)); - stackDesiredSize = stackDesiredSize.WithHeight(stackDesiredSize.Height + childDesiredSize.Height + (child.IsVisible ? spacing : 0)); + stackDesiredSize = stackDesiredSize.WithHeight(stackDesiredSize.Height + (child.IsVisible ? spacing : 0) + childDesiredSize.Height); } } @@ -224,7 +224,9 @@ namespace Avalonia.Controls stackDesiredSize = stackDesiredSize.WithHeight(stackDesiredSize.Height - (hasVisibleChild ? spacing : 0)); } - return stackDesiredSize.Constrain(availableSize); // TODO: In WPF `.Constrain(availableSize)` is not used. + return stackDesiredSize; + // TODO: In WPF `.Constrain(availableSize)` is not used. + //return stackDesiredSize.Constrain(availableSize); } /// @@ -251,15 +253,15 @@ namespace Avalonia.Controls if (fHorizontal) { - rcChild = rcChild.WithX(rcChild.X + previousChildSize); - previousChildSize = child.DesiredSize.Width + (child.IsVisible ? spacing : 0); + rcChild = rcChild.WithX(rcChild.X + previousChildSize + spacing); + previousChildSize = child.DesiredSize.Width;//+ (child.IsVisible ? spacing : 0); rcChild = rcChild.WithWidth(previousChildSize); rcChild = rcChild.WithHeight(Math.Max(finalSize.Height, child.DesiredSize.Height)); } else { - rcChild = rcChild.WithY(rcChild.Y + previousChildSize); - previousChildSize = child.DesiredSize.Height + (child.IsVisible ? spacing : 0); + rcChild = rcChild.WithY(rcChild.Y + previousChildSize + spacing); + previousChildSize = child.DesiredSize.Height;//+ (child.IsVisible ? spacing : 0); rcChild = rcChild.WithHeight(previousChildSize); rcChild = rcChild.WithWidth(Math.Max(finalSize.Width, child.DesiredSize.Width)); } From af69f801204435e46ea7312f334f46ea79e7f652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Mon, 10 Jun 2019 23:08:21 +0200 Subject: [PATCH 07/12] Update StackPanel.cs --- src/Avalonia.Controls/StackPanel.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index fd75353364..a4e711bbd3 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -193,7 +193,9 @@ namespace Avalonia.Controls if (child == null) { continue; } - if (child.IsVisible) + bool isVisible = child.IsVisible; + + if (isVisible && !hasVisibleChild) { hasVisibleChild = true; } @@ -205,13 +207,13 @@ namespace Avalonia.Controls // Accumulate child size. if (fHorizontal) { - stackDesiredSize = stackDesiredSize.WithWidth(stackDesiredSize.Width + (child.IsVisible ? spacing : 0) + childDesiredSize.Width); + stackDesiredSize = stackDesiredSize.WithWidth(stackDesiredSize.Width + (isVisible ? spacing : 0) + childDesiredSize.Width); stackDesiredSize = stackDesiredSize.WithHeight(Math.Max(stackDesiredSize.Height, childDesiredSize.Height)); } else { stackDesiredSize = stackDesiredSize.WithWidth(Math.Max(stackDesiredSize.Width, childDesiredSize.Width)); - stackDesiredSize = stackDesiredSize.WithHeight(stackDesiredSize.Height + (child.IsVisible ? spacing : 0) + childDesiredSize.Height); + stackDesiredSize = stackDesiredSize.WithHeight(stackDesiredSize.Height + (isVisible ? spacing : 0) + childDesiredSize.Height); } } From 4cf502e61b5ccf409d84f72cc5f0acd50cee5bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Mon, 10 Jun 2019 23:15:54 +0200 Subject: [PATCH 08/12] Update StackPanel.cs --- src/Avalonia.Controls/StackPanel.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index a4e711bbd3..8abc82f747 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -255,17 +255,19 @@ namespace Avalonia.Controls if (fHorizontal) { - rcChild = rcChild.WithX(rcChild.X + previousChildSize + spacing); - previousChildSize = child.DesiredSize.Width;//+ (child.IsVisible ? spacing : 0); + rcChild = rcChild.WithX(rcChild.X + previousChildSize); + previousChildSize = child.DesiredSize.Width; rcChild = rcChild.WithWidth(previousChildSize); rcChild = rcChild.WithHeight(Math.Max(finalSize.Height, child.DesiredSize.Height)); + previousChildSize += spacing; } else { - rcChild = rcChild.WithY(rcChild.Y + previousChildSize + spacing); - previousChildSize = child.DesiredSize.Height;//+ (child.IsVisible ? spacing : 0); + rcChild = rcChild.WithY(rcChild.Y + previousChildSize); + previousChildSize = child.DesiredSize.Height; rcChild = rcChild.WithHeight(previousChildSize); rcChild = rcChild.WithWidth(Math.Max(finalSize.Width, child.DesiredSize.Width)); + previousChildSize += spacing; } child.Arrange(rcChild); From ebb8d43727f6688db9c69851b033600e46f75715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Mon, 10 Jun 2019 23:15:58 +0200 Subject: [PATCH 09/12] Update StackPanel.cs --- src/Avalonia.Controls/StackPanel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index 8abc82f747..8b002dc64d 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -226,9 +226,9 @@ namespace Avalonia.Controls stackDesiredSize = stackDesiredSize.WithHeight(stackDesiredSize.Height - (hasVisibleChild ? spacing : 0)); } - return stackDesiredSize; // TODO: In WPF `.Constrain(availableSize)` is not used. - //return stackDesiredSize.Constrain(availableSize); + //return stackDesiredSize; + return stackDesiredSize.Constrain(availableSize); } /// From 4ae22f579e47df6c7edf2d7ec93fc3b099f64d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Wed, 12 Jun 2019 17:28:57 +0200 Subject: [PATCH 10/12] Fix license headers --- src/Avalonia.Controls/DockPanel.cs | 9 +++++++-- src/Avalonia.Controls/StackPanel.cs | 6 ++++-- src/Avalonia.Controls/WrapPanel.cs | 10 ++++------ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Controls/DockPanel.cs b/src/Avalonia.Controls/DockPanel.cs index e580283d85..8e23555c2d 100644 --- a/src/Avalonia.Controls/DockPanel.cs +++ b/src/Avalonia.Controls/DockPanel.cs @@ -1,7 +1,12 @@ +// This source file is adapted from the Windows Presentation Foundation project. +// (https://github.com/dotnet/wpf/) +// +// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. + +using System; + namespace Avalonia.Controls { - using System; - /// /// Defines the available docking modes for a control in a . /// diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index 8b002dc64d..59c3c33942 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -1,5 +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. +// This source file is adapted from the Windows Presentation Foundation project. +// (https://github.com/dotnet/wpf/) +// +// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. using System; using Avalonia.Input; diff --git a/src/Avalonia.Controls/WrapPanel.cs b/src/Avalonia.Controls/WrapPanel.cs index 4df1b39400..6f53d853c7 100644 --- a/src/Avalonia.Controls/WrapPanel.cs +++ b/src/Avalonia.Controls/WrapPanel.cs @@ -1,9 +1,7 @@ -// Copyright (c) The Avalonia Project. All rights reserved. -// Licensed under the MIT license. See licence.md file in the project root for full license information. - -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; +// This source file is adapted from the Windows Presentation Foundation project. +// (https://github.com/dotnet/wpf/) +// +// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. using Avalonia.Input; using Avalonia.Utilities; From 710215ddb2558fe927880e95bae68485fd944cc1 Mon Sep 17 00:00:00 2001 From: wieslawsoltes Date: Tue, 25 Jun 2019 13:26:35 +0200 Subject: [PATCH 11/12] Revert StackPanel --- src/Avalonia.Controls/StackPanel.cs | 148 ++++++++++++---------------- 1 file changed, 65 insertions(+), 83 deletions(-) diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index 59c3c33942..c29faa1b4d 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -1,10 +1,10 @@ -// This source file is adapted from the Windows Presentation Foundation project. -// (https://github.com/dotnet/wpf/) -// -// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Linq; using Avalonia.Input; +using Avalonia.Layout; namespace Avalonia.Controls { @@ -155,124 +155,106 @@ namespace Avalonia.Controls } /// - /// General StackPanel layout behavior is to grow unbounded in the "stacking" direction (Size To Content). - /// Children in this dimension are encouraged to be as large as they like. In the other dimension, - /// StackPanel will assume the maximum size of its children. + /// Measures the control. /// - /// Constraint - /// Desired size + /// The available size. + /// The desired size of the control. protected override Size MeasureOverride(Size availableSize) { - Size stackDesiredSize = new Size(); - var children = Children; - Size layoutSlotSize = availableSize; - bool fHorizontal = (Orientation == Orientation.Horizontal); - double spacing = Spacing; - bool hasVisibleChild = false; + double childAvailableWidth = double.PositiveInfinity; + double childAvailableHeight = double.PositiveInfinity; - // - // Initialize child sizing and iterator data - // Allow children as much size as they want along the stack. - // - if (fHorizontal) + if (Orientation == Orientation.Vertical) { - layoutSlotSize = layoutSlotSize.WithWidth(Double.PositiveInfinity); + childAvailableWidth = availableSize.Width; + + if (!double.IsNaN(Width)) + { + childAvailableWidth = Width; + } + + childAvailableWidth = Math.Min(childAvailableWidth, MaxWidth); + childAvailableWidth = Math.Max(childAvailableWidth, MinWidth); } else { - layoutSlotSize = layoutSlotSize.WithHeight(Double.PositiveInfinity); - } + childAvailableHeight = availableSize.Height; - // - // Iterate through children. - // While we still supported virtualization, this was hidden in a child iterator (see source history). - // - for (int i = 0, count = children.Count; i < count; ++i) - { - // Get next child. - var child = children[i]; - - if (child == null) - { continue; } - - bool isVisible = child.IsVisible; - - if (isVisible && !hasVisibleChild) + if (!double.IsNaN(Height)) { - hasVisibleChild = true; + childAvailableHeight = Height; } - // Measure the child. - child.Measure(layoutSlotSize); - Size childDesiredSize = child.DesiredSize; + childAvailableHeight = Math.Min(childAvailableHeight, MaxHeight); + childAvailableHeight = Math.Max(childAvailableHeight, MinHeight); + } + + double measuredWidth = 0; + double measuredHeight = 0; + double spacing = Spacing; + bool hasVisibleChild = Children.Any(c => c.IsVisible); + + foreach (Control child in Children) + { + child.Measure(new Size(childAvailableWidth, childAvailableHeight)); + Size size = child.DesiredSize; - // Accumulate child size. - if (fHorizontal) + if (Orientation == Orientation.Vertical) { - stackDesiredSize = stackDesiredSize.WithWidth(stackDesiredSize.Width + (isVisible ? spacing : 0) + childDesiredSize.Width); - stackDesiredSize = stackDesiredSize.WithHeight(Math.Max(stackDesiredSize.Height, childDesiredSize.Height)); + measuredHeight += size.Height + (child.IsVisible ? spacing : 0); + measuredWidth = Math.Max(measuredWidth, size.Width); } else { - stackDesiredSize = stackDesiredSize.WithWidth(Math.Max(stackDesiredSize.Width, childDesiredSize.Width)); - stackDesiredSize = stackDesiredSize.WithHeight(stackDesiredSize.Height + (isVisible ? spacing : 0) + childDesiredSize.Height); + measuredWidth += size.Width + (child.IsVisible ? spacing : 0); + measuredHeight = Math.Max(measuredHeight, size.Height); } } - if (fHorizontal) + if (Orientation == Orientation.Vertical) { - stackDesiredSize = stackDesiredSize.WithWidth(stackDesiredSize.Width - (hasVisibleChild ? spacing : 0)); + measuredHeight -= (hasVisibleChild ? spacing : 0); } else - { - stackDesiredSize = stackDesiredSize.WithHeight(stackDesiredSize.Height - (hasVisibleChild ? spacing : 0)); + { + measuredWidth -= (hasVisibleChild ? spacing : 0); } - // TODO: In WPF `.Constrain(availableSize)` is not used. - //return stackDesiredSize; - return stackDesiredSize.Constrain(availableSize); + return new Size(measuredWidth, measuredHeight).Constrain(availableSize); } - /// - /// Content arrangement. - /// - /// Arrange size + /// protected override Size ArrangeOverride(Size finalSize) { - var children = Children; - bool fHorizontal = (Orientation == Orientation.Horizontal); - Rect rcChild = new Rect(finalSize); - double previousChildSize = 0.0; + var orientation = Orientation; var spacing = Spacing; + var finalRect = new Rect(finalSize); + var pos = 0.0; - // - // Arrange and Position Children. - // - for (int i = 0, count = children.Count; i < count; ++i) + foreach (Control child in Children) { - var child = children[i]; + if (!child.IsVisible) + { + continue; + } - if (child == null) - { continue; } + double childWidth = child.DesiredSize.Width; + double childHeight = child.DesiredSize.Height; - if (fHorizontal) + if (orientation == Orientation.Vertical) { - rcChild = rcChild.WithX(rcChild.X + previousChildSize); - previousChildSize = child.DesiredSize.Width; - rcChild = rcChild.WithWidth(previousChildSize); - rcChild = rcChild.WithHeight(Math.Max(finalSize.Height, child.DesiredSize.Height)); - previousChildSize += spacing; + var rect = new Rect(0, pos, childWidth, childHeight) + .Align(finalRect, child.HorizontalAlignment, VerticalAlignment.Top); + ArrangeChild(child, rect, finalSize, orientation); + pos += childHeight + spacing; } else { - rcChild = rcChild.WithY(rcChild.Y + previousChildSize); - previousChildSize = child.DesiredSize.Height; - rcChild = rcChild.WithHeight(previousChildSize); - rcChild = rcChild.WithWidth(Math.Max(finalSize.Width, child.DesiredSize.Width)); - previousChildSize += spacing; + var rect = new Rect(pos, 0, childWidth, childHeight) + .Align(finalRect, HorizontalAlignment.Left, child.VerticalAlignment); + ArrangeChild(child, rect, finalSize, orientation); + pos += childWidth + spacing; } - - child.Arrange(rcChild); } return finalSize; From 1e115b89c8e980c9288bfcd8d0375a5fbce00c98 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 25 Jun 2019 19:55:09 +0300 Subject: [PATCH 12/12] Fixed Start() + obsoleted Start() --- src/Avalonia.Controls/AppBuilderBase.cs | 24 +++++++++++++------ .../DesktopApplicationExtensions.cs | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 0f0b55e9e8..59ff35be76 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -4,6 +4,7 @@ using System; using System.Reflection; using System.Linq; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Platform; namespace Avalonia.Controls @@ -106,19 +107,28 @@ namespace Avalonia.Controls public void Start(Func dataContextProvider = null) where TMainWindow : Window, new() { - var window = new TMainWindow(); - if (dataContextProvider != null) - window.DataContext = dataContextProvider(); - Instance.Run(window); + AfterSetup(builder => + { + var window = new TMainWindow(); + if (dataContextProvider != null) + window.DataContext = dataContextProvider(); + ((IClassicDesktopStyleApplicationLifetime)builder.Instance.ApplicationLifetime) + .MainWindow = window; + }); + + // Copy-pasted because we can't call extension methods due to generic constraints + var lifetime = new ClassicDesktopStyleApplicationLifetime(Instance) {ShutdownMode = ShutdownMode.OnMainWindowClose}; + Instance.ApplicationLifetime = lifetime; + SetupWithoutStarting(); + lifetime.Start(Array.Empty()); } public delegate void AppMainDelegate(Application app, string[] args); - [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details")] + [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details", true)] public void Start() { - Setup(); - Instance.Run(); + throw new NotSupportedException(); } public void Start(AppMainDelegate main, string[] args) diff --git a/src/Avalonia.Controls/DesktopApplicationExtensions.cs b/src/Avalonia.Controls/DesktopApplicationExtensions.cs index 9b81bc94d1..ff6705cdc0 100644 --- a/src/Avalonia.Controls/DesktopApplicationExtensions.cs +++ b/src/Avalonia.Controls/DesktopApplicationExtensions.cs @@ -8,7 +8,7 @@ namespace Avalonia.Controls { public static class DesktopApplicationExtensions { - [Obsolete("Running application without a cancellation token and a lifetime is no longer supported, see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details")] + [Obsolete("Running application without a cancellation token and a lifetime is no longer supported, see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details", true)] public static void Run(this Application app) => throw new NotSupportedException(); ///