/
AbsoluteLayoutManager.cs
157 lines (120 loc) · 5.05 KB
/
AbsoluteLayoutManager.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
using System;
using Microsoft.Maui.Graphics;
namespace Microsoft.Maui.Layouts
{
public class AbsoluteLayoutManager : LayoutManager
{
public IAbsoluteLayout AbsoluteLayout { get; }
const double AutoSize = -1;
public AbsoluteLayoutManager(IAbsoluteLayout absoluteLayout) : base(absoluteLayout)
{
AbsoluteLayout = absoluteLayout;
}
public override Size Measure(double widthConstraint, double heightConstraint)
{
var padding = AbsoluteLayout.Padding;
var availableWidth = widthConstraint - padding.HorizontalThickness;
var availableHeight = heightConstraint - padding.VerticalThickness;
double measuredHeight = 0;
double measuredWidth = 0;
for (int n = 0; n < AbsoluteLayout.Count; n++)
{
var child = AbsoluteLayout[n];
if (child.Visibility == Visibility.Collapsed)
{
continue;
}
var bounds = AbsoluteLayout.GetLayoutBounds(child);
var flags = AbsoluteLayout.GetLayoutFlags(child);
bool isWidthProportional = HasFlag(flags, AbsoluteLayoutFlags.WidthProportional);
bool isHeightProportional = HasFlag(flags, AbsoluteLayoutFlags.HeightProportional);
var measureWidth = ResolveChildMeasureConstraint(bounds.Width, isWidthProportional, widthConstraint);
var measureHeight = ResolveChildMeasureConstraint(bounds.Height, isHeightProportional, heightConstraint);
var measure = child.Measure(measureWidth, measureHeight);
var width = ResolveDimension(isWidthProportional, bounds.Width, availableWidth, measure.Width);
var height = ResolveDimension(isHeightProportional, bounds.Height, availableHeight, measure.Height);
measuredHeight = Math.Max(measuredHeight, bounds.Top + height);
measuredWidth = Math.Max(measuredWidth, bounds.Left + width);
}
var finalHeight = ResolveConstraints(heightConstraint, AbsoluteLayout.Height, measuredHeight, AbsoluteLayout.MinimumHeight, AbsoluteLayout.MaximumHeight);
var finalWidth = ResolveConstraints(widthConstraint, AbsoluteLayout.Width, measuredWidth, AbsoluteLayout.MinimumWidth, AbsoluteLayout.MaximumWidth);
return new Size(finalWidth, finalHeight);
}
public override Size ArrangeChildren(Rect bounds)
{
var padding = AbsoluteLayout.Padding;
double top = padding.Top + bounds.Top;
double left = padding.Left + bounds.Left;
double right = bounds.Right - padding.Right;
double availableWidth = bounds.Width - padding.HorizontalThickness;
double availableHeight = bounds.Height - padding.VerticalThickness;
bool leftToRight = AbsoluteLayout.ShouldArrangeLeftToRight();
// Figure out where we're starting from (the left edge of the padded area, or the right edge)
double xPosition = leftToRight ? left : right;
for (int n = 0; n < AbsoluteLayout.Count; n++)
{
var child = AbsoluteLayout[n];
if (child.Visibility == Visibility.Collapsed)
{
continue;
}
var destination = AbsoluteLayout.GetLayoutBounds(child);
var flags = AbsoluteLayout.GetLayoutFlags(child);
bool isWidthProportional = HasFlag(flags, AbsoluteLayoutFlags.WidthProportional);
bool isHeightProportional = HasFlag(flags, AbsoluteLayoutFlags.HeightProportional);
destination.Width = ResolveDimension(isWidthProportional, destination.Width, availableWidth, child.DesiredSize.Width);
destination.Height = ResolveDimension(isHeightProportional, destination.Height, availableHeight, child.DesiredSize.Height);
if (HasFlag(flags, AbsoluteLayoutFlags.XProportional))
{
destination.X = (availableWidth - destination.Width) * destination.X;
}
if (HasFlag(flags, AbsoluteLayoutFlags.YProportional))
{
destination.Y = (availableHeight - destination.Height) * destination.Y;
}
if (leftToRight)
destination.X += left;
else
destination.X = right - destination.X - destination.Width;
destination.Y += top;
child.Arrange(destination);
}
return new Size(availableWidth, availableHeight);
}
static bool HasFlag(AbsoluteLayoutFlags a, AbsoluteLayoutFlags b)
{
// Avoiding Enum.HasFlag here for performance reasons; we don't need the type check
return (a & b) == b;
}
static double ResolveDimension(bool isProportional, double fromBounds, double available, double measured)
{
// By default, we use the absolute value from LayoutBounds
var value = fromBounds;
if (isProportional && !double.IsInfinity(available))
{
// If this dimension is marked proportional, then the value is a percentage of the available space
// Multiple it by the available space to figure out the final value
value *= available;
}
else if (value == AutoSize)
{
// No absolute or proportional value specified, so we use the measured value
value = measured;
}
return value;
}
static double ResolveChildMeasureConstraint(double boundsValue, bool proportional, double constraint)
{
if (boundsValue < 0)
{
// If the child view doesn't have bounds set by the AbsoluteLayout, then we'll let it auto-size
return double.PositiveInfinity;
}
if (proportional)
{
return boundsValue * constraint;
}
return boundsValue;
}
}
}