diff options
author | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 13:02:25 -0700 |
---|---|---|
committer | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 16:13:41 -0700 |
commit | 17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch) | |
tree | b5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Core/AbsoluteLayout.cs | |
download | xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.gz xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.bz2 xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.zip |
Initial import
Diffstat (limited to 'Xamarin.Forms.Core/AbsoluteLayout.cs')
-rw-r--r-- | Xamarin.Forms.Core/AbsoluteLayout.cs | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/Xamarin.Forms.Core/AbsoluteLayout.cs b/Xamarin.Forms.Core/AbsoluteLayout.cs new file mode 100644 index 00000000..51b5ca12 --- /dev/null +++ b/Xamarin.Forms.Core/AbsoluteLayout.cs @@ -0,0 +1,313 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace Xamarin.Forms +{ + public class AbsoluteLayout : Layout<View> + { + public static readonly BindableProperty LayoutFlagsProperty = BindableProperty.CreateAttached("LayoutFlags", typeof(AbsoluteLayoutFlags), typeof(AbsoluteLayout), AbsoluteLayoutFlags.None); + + public static readonly BindableProperty LayoutBoundsProperty = BindableProperty.CreateAttached("LayoutBounds", typeof(Rectangle), typeof(AbsoluteLayout), new Rectangle(0, 0, AutoSize, AutoSize)); + + readonly AbsoluteElementCollection _children; + + public AbsoluteLayout() + { + _children = new AbsoluteElementCollection(InternalChildren, this); + } + + public static double AutoSize + { + get { return -1; } + } + + public new IAbsoluteList<View> Children + { + get { return _children; } + } + + [TypeConverter(typeof(BoundsTypeConverter))] + public static Rectangle GetLayoutBounds(BindableObject bindable) + { + return (Rectangle)bindable.GetValue(LayoutBoundsProperty); + } + + public static AbsoluteLayoutFlags GetLayoutFlags(BindableObject bindable) + { + return (AbsoluteLayoutFlags)bindable.GetValue(LayoutFlagsProperty); + } + + public static void SetLayoutBounds(BindableObject bindable, Rectangle bounds) + { + bindable.SetValue(LayoutBoundsProperty, bounds); + } + + public static void SetLayoutFlags(BindableObject bindable, AbsoluteLayoutFlags flags) + { + bindable.SetValue(LayoutFlagsProperty, flags); + } + + protected override void LayoutChildren(double x, double y, double width, double height) + { + foreach (View child in LogicalChildren) + { + Rectangle rect = ComputeLayoutForRegion(child, new Size(width, height)); + rect.X += x; + rect.Y += y; + + LayoutChildIntoBoundingRegion(child, rect); + } + } + + protected override void OnChildAdded(Element child) + { + base.OnChildAdded(child); + child.PropertyChanged += ChildOnPropertyChanged; + } + + protected override void OnChildRemoved(Element child) + { + child.PropertyChanged -= ChildOnPropertyChanged; + base.OnChildRemoved(child); + } + + [Obsolete("Use OnMeasure")] + protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint) + { + var bestFitSize = new Size(); + var minimum = new Size(); + foreach (View child in LogicalChildren) + { + SizeRequest desiredSize = ComputeBoundingRegionDesiredSize(child); + + bestFitSize.Width = Math.Max(bestFitSize.Width, desiredSize.Request.Width); + bestFitSize.Height = Math.Max(bestFitSize.Height, desiredSize.Request.Height); + minimum.Width = Math.Max(minimum.Width, desiredSize.Minimum.Width); + minimum.Height = Math.Max(minimum.Height, desiredSize.Minimum.Height); + } + + return new SizeRequest(bestFitSize, minimum); + } + + internal override void ComputeConstraintForView(View view) + { + AbsoluteLayoutFlags layoutFlags = GetLayoutFlags(view); + + if ((layoutFlags & AbsoluteLayoutFlags.SizeProportional) == AbsoluteLayoutFlags.SizeProportional) + { + view.ComputedConstraint = Constraint; + return; + } + + var result = LayoutConstraint.None; + Rectangle layoutBounds = GetLayoutBounds(view); + if ((layoutFlags & AbsoluteLayoutFlags.HeightProportional) != 0) + { + bool widthLocked = layoutBounds.Width != AutoSize; + result = Constraint & LayoutConstraint.VerticallyFixed; + if (widthLocked) + result |= LayoutConstraint.HorizontallyFixed; + } + else if ((layoutFlags & AbsoluteLayoutFlags.WidthProportional) != 0) + { + bool heightLocked = layoutBounds.Height != AutoSize; + result = Constraint & LayoutConstraint.HorizontallyFixed; + if (heightLocked) + result |= LayoutConstraint.VerticallyFixed; + } + else + { + if (layoutBounds.Width != AutoSize) + result |= LayoutConstraint.HorizontallyFixed; + if (layoutBounds.Height != AutoSize) + result |= LayoutConstraint.VerticallyFixed; + } + + view.ComputedConstraint = result; + } + + void ChildOnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == LayoutFlagsProperty.PropertyName || e.PropertyName == LayoutBoundsProperty.PropertyName) + { + InvalidateMeasure(InvalidationTrigger.MeasureChanged); + UpdateChildrenLayout(); + } + } + + static SizeRequest ComputeBoundingRegionDesiredSize(View view) + { + var width = 0.0; + var height = 0.0; + + var sizeRequest = new Lazy<SizeRequest>(() => view.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins)); + + Rectangle bounds = GetLayoutBounds(view); + AbsoluteLayoutFlags absFlags = GetLayoutFlags(view); + bool widthIsProportional = (absFlags & AbsoluteLayoutFlags.WidthProportional) != 0; + bool heightIsProportional = (absFlags & AbsoluteLayoutFlags.HeightProportional) != 0; + bool xIsProportional = (absFlags & AbsoluteLayoutFlags.XProportional) != 0; + bool yIsProportional = (absFlags & AbsoluteLayoutFlags.YProportional) != 0; + + // add in required x values + if (!xIsProportional) + { + width += bounds.X; + } + + if (!yIsProportional) + { + height += bounds.Y; + } + + double minWidth = width; + double minHeight = height; + + if (!widthIsProportional && bounds.Width != AutoSize) + { + // fixed size + width += bounds.Width; + minWidth += bounds.Width; + } + else if (!widthIsProportional) + { + // auto size + width += sizeRequest.Value.Request.Width; + minWidth += sizeRequest.Value.Minimum.Width; + } + else + { + // proportional size + width += sizeRequest.Value.Request.Width / Math.Max(0.25, bounds.Width); + //minWidth += 0; + } + + if (!heightIsProportional && bounds.Height != AutoSize) + { + // fixed size + height += bounds.Height; + minHeight += bounds.Height; + } + else if (!heightIsProportional) + { + // auto size + height += sizeRequest.Value.Request.Height; + minHeight += sizeRequest.Value.Minimum.Height; + } + else + { + // proportional size + height += sizeRequest.Value.Request.Height / Math.Max(0.25, bounds.Height); + //minHeight += 0; + } + + return new SizeRequest(new Size(width, height), new Size(minWidth, minHeight)); + } + + static Rectangle ComputeLayoutForRegion(View view, Size region) + { + var result = new Rectangle(); + + SizeRequest sizeRequest; + Rectangle bounds = GetLayoutBounds(view); + AbsoluteLayoutFlags absFlags = GetLayoutFlags(view); + bool widthIsProportional = (absFlags & AbsoluteLayoutFlags.WidthProportional) != 0; + bool heightIsProportional = (absFlags & AbsoluteLayoutFlags.HeightProportional) != 0; + bool xIsProportional = (absFlags & AbsoluteLayoutFlags.XProportional) != 0; + bool yIsProportional = (absFlags & AbsoluteLayoutFlags.YProportional) != 0; + + if (widthIsProportional) + { + result.Width = Math.Round(region.Width * bounds.Width); + } + else if (bounds.Width != AutoSize) + { + result.Width = bounds.Width; + } + + if (heightIsProportional) + { + result.Height = Math.Round(region.Height * bounds.Height); + } + else if (bounds.Height != AutoSize) + { + result.Height = bounds.Height; + } + + if (!widthIsProportional && bounds.Width == AutoSize) + { + if (!heightIsProportional && bounds.Width == AutoSize) + { + // Width and Height are auto + sizeRequest = view.Measure(region.Width, region.Height, MeasureFlags.IncludeMargins); + result.Width = sizeRequest.Request.Width; + result.Height = sizeRequest.Request.Height; + } + else + { + // Only width is auto + sizeRequest = view.Measure(region.Width, result.Height, MeasureFlags.IncludeMargins); + result.Width = sizeRequest.Request.Width; + } + } + else if (!heightIsProportional && bounds.Height == AutoSize) + { + // Only height is auto + sizeRequest = view.Measure(result.Width, region.Height, MeasureFlags.IncludeMargins); + result.Height = sizeRequest.Request.Height; + } + + if (xIsProportional) + { + result.X = Math.Round((region.Width - result.Width) * bounds.X); + } + else + { + result.X = bounds.X; + } + + if (yIsProportional) + { + result.Y = Math.Round((region.Height - result.Height) * bounds.Y); + } + else + { + result.Y = bounds.Y; + } + + return result; + } + + public interface IAbsoluteList<T> : IList<T> where T : View + { + void Add(View view, Rectangle bounds, AbsoluteLayoutFlags flags = AbsoluteLayoutFlags.None); + + void Add(View view, Point position); + } + + class AbsoluteElementCollection : ElementCollection<View>, IAbsoluteList<View> + { + public AbsoluteElementCollection(ObservableCollection<Element> inner, AbsoluteLayout parent) : base(inner) + { + Parent = parent; + } + + internal AbsoluteLayout Parent { get; set; } + + public void Add(View view, Rectangle bounds, AbsoluteLayoutFlags flags = AbsoluteLayoutFlags.None) + { + SetLayoutBounds(view, bounds); + SetLayoutFlags(view, flags); + Add(view); + } + + public void Add(View view, Point position) + { + SetLayoutBounds(view, new Rectangle(position.X, position.Y, AutoSize, AutoSize)); + Add(view); + } + } + } +}
\ No newline at end of file |