OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 import 'dart:math' as math; | 5 import 'dart:math' as math; |
6 import 'box.dart'; | 6 import 'box.dart'; |
7 import 'object.dart'; | 7 import 'object.dart'; |
8 | 8 |
9 class FlexBoxParentData extends BoxParentData with ContainerParentDataMixin<Rend erBox> { | 9 class FlexBoxParentData extends BoxParentData with ContainerParentDataMixin<Rend erBox> { |
10 int flex; | 10 int flex; |
11 | 11 |
12 void merge(FlexBoxParentData other) { | 12 void merge(FlexBoxParentData other) { |
13 if (other.flex != null) | 13 if (other.flex != null) |
14 flex = other.flex; | 14 flex = other.flex; |
15 super.merge(other); | 15 super.merge(other); |
16 } | 16 } |
17 | 17 |
18 String toString() => '${super.toString()}; flex=$flex'; | 18 String toString() => '${super.toString()}; flex=$flex'; |
19 } | 19 } |
20 | 20 |
21 enum FlexDirection { horizontal, vertical } | 21 enum FlexDirection { horizontal, vertical } |
22 enum FlexJustifyContent { | 22 enum FlexJustifyContent { |
23 flexStart, | 23 flexStart, |
24 flexEnd, | 24 flexEnd, |
25 center, | 25 center, |
26 spaceBetween, | 26 spaceBetween, |
27 spaceAround, | 27 spaceAround, |
28 } | 28 } |
29 | 29 |
30 typedef double ChildSizingFunction(RenderBox child, BoxConstraints constraints); | |
abarth-chromium
2015/06/10 04:23:20
_ChildSizingFunction
The _ scopes the definition
jackson
2015/06/10 04:44:46
Acknowledged.
kulakowski
2015/06/10 16:07:14
Pedantry hopefully useful in the future: There is
| |
31 | |
30 class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl exBoxParentData>, | 32 class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl exBoxParentData>, |
31 RenderBoxContainerDefaultsMixin<RenderBo x, FlexBoxParentData> { | 33 RenderBoxContainerDefaultsMixin<RenderBo x, FlexBoxParentData> { |
32 // lays out RenderBox children using flexible layout | 34 // lays out RenderBox children using flexible layout |
33 | 35 |
34 RenderFlex({ | 36 RenderFlex({ |
35 FlexDirection direction: FlexDirection.horizontal, | 37 FlexDirection direction: FlexDirection.horizontal, |
36 FlexJustifyContent justifyContent: FlexJustifyContent.flexStart | 38 FlexJustifyContent justifyContent: FlexJustifyContent.flexStart |
37 }) : _direction = direction, _justifyContent = justifyContent; | 39 }) : _direction = direction, _justifyContent = justifyContent; |
38 | 40 |
39 FlexDirection _direction; | 41 FlexDirection _direction; |
(...skipping 12 matching lines...) Expand all Loading... | |
52 _justifyContent = value; | 54 _justifyContent = value; |
53 markNeedsLayout(); | 55 markNeedsLayout(); |
54 } | 56 } |
55 } | 57 } |
56 | 58 |
57 void setParentData(RenderBox child) { | 59 void setParentData(RenderBox child) { |
58 if (child.parentData is! FlexBoxParentData) | 60 if (child.parentData is! FlexBoxParentData) |
59 child.parentData = new FlexBoxParentData(); | 61 child.parentData = new FlexBoxParentData(); |
60 } | 62 } |
61 | 63 |
62 // We don't currently support this for RenderFlex | 64 double _getIntrinsicSize(BoxConstraints constraints, |
63 double getMinIntrinsicWidth(BoxConstraints constraints) { | 65 FlexDirection sizingDirection, |
64 assert(false); | 66 ChildSizingFunction childSize) { |
65 return constraints.constrainWidth(0.0); | 67 // http://www.w3.org/TR/2015/WD-css-flexbox-1-20150514/#intrinsic-sizes |
68 if (_direction == sizingDirection) { | |
69 // INTRINSIC MAIN SIZE | |
70 // Intrinsic main size is the smallest size the flex container can take | |
71 // while maintaining the min/max-content contributions of its flex items. | |
72 BoxConstraints childConstraints; | |
73 switch(_direction) { | |
74 case FlexDirection.horizontal: | |
75 childConstraints = new BoxConstraints(maxHeight: constraints.maxHeight ); | |
76 break; | |
77 case FlexDirection.vertical: | |
78 childConstraints = new BoxConstraints(maxWidth: constraints.maxWidth); | |
79 break; | |
80 } | |
81 | |
82 double totalFlex = 0.0; | |
83 double inflexibleSpace = 0.0; | |
84 double maxFlexFractionSoFar = 0.0; | |
85 RenderBox child = firstChild; | |
86 while (child != null) { | |
87 int flex = _getFlex(child); | |
88 totalFlex += flex; | |
89 if (flex > 0) { | |
90 double flexFraction = childSize(child, childConstraints) / _getFlex(ch ild); | |
91 maxFlexFractionSoFar = math.max(maxFlexFractionSoFar, flexFraction); | |
92 } else { | |
93 inflexibleSpace += childSize(child, childConstraints); | |
94 } | |
95 child = child.parentData.nextSibling; | |
96 } | |
97 double mainSize = maxFlexFractionSoFar * totalFlex + inflexibleSpace; | |
98 | |
99 // Ensure that we don't violate the given constraints with our result | |
100 switch(_direction) { | |
101 case FlexDirection.horizontal: | |
102 return constraints.constrainWidth(mainSize); | |
103 case FlexDirection.vertical: | |
104 return constraints.constrainHeight(mainSize); | |
105 } | |
106 } else { | |
107 // INTRINSIC CROSS SIZE | |
108 // The spec wants us to perform layout into the given available main-axis | |
109 // space and return the cross size. That's too expensive, so instead we | |
110 // size inflexible children according to their max intrinsic size in the | |
111 // main direction and use those constraints to determine their max | |
112 // intrinsic size in the cross direction. We don't care if the caller | |
113 // asked for max or min -- the answer is always computed using the | |
114 // max size in the main direction. | |
115 | |
116 double availableMainSpace; | |
117 BoxConstraints childConstraints; | |
118 switch(_direction) { | |
119 case FlexDirection.horizontal: | |
120 childConstraints = new BoxConstraints(maxWidth: constraints.maxWidth); | |
121 availableMainSpace = innerConstraints.maxWidth; | |
122 break; | |
123 case FlexDirection.vertical: | |
124 childConstraints = new BoxConstraints(maxHeight: constraints.maxHeight ); | |
125 availableMainSpace = innerConstraints.maxHeight; | |
126 break; | |
127 } | |
128 | |
129 // Get inflexible space using the max in the main direction | |
130 int totalFlex = 0; | |
131 double inflexibleSpace = 0.0; | |
132 double maxCrossSize = 0.0; | |
133 RenderBox child = firstChild; | |
134 while (child != null) { | |
135 int flex = _getFlex(child); | |
136 totalFlex += flex; | |
137 double mainSize; | |
138 double crossSize; | |
139 if (flex == 0) { | |
140 switch (_direction) { | |
141 case FlexDirection.horizontal: | |
142 mainSize = child.getMaxIntrinsicWidth(childConstraints); | |
143 BoxConstraints widthConstraints = | |
144 new BoxConstraints(minWidth: mainSize, maxWidth: mainSize); | |
145 crossSize = child.getMinIntrinsicHeight(widthConstraints); | |
146 break; | |
147 case FlexDirection.vertical: | |
148 mainSize = child.getMaxIntrinsicHeight(childConstraints); | |
149 BoxConstraints heightConstraints = | |
150 new BoxConstraints(minWidth: mainSize, maxWidth: mainSize); | |
151 crossSize = child.getMinIntrinsicWidth(heightConstraints); | |
152 inflexibleSpace += mainSize; | |
abarth-chromium
2015/06/10 04:23:20
Is this line missing from the other case?
jackson
2015/06/10 04:44:46
Acknowledged.
| |
153 break; | |
154 } | |
155 inflexibleSpace += mainSize; | |
abarth-chromium
2015/06/10 04:23:20
Wait, it seems like we're doing this twice in the
jackson
2015/06/10 04:44:46
Acknowledged.
| |
156 maxCrossSize = math.max(maxCrossSize, crossSize); | |
157 } | |
158 child = child.parentData.nextSibling; | |
159 } | |
160 | |
161 // Determine the spacePerFlex by allocating the remaining available space | |
162 double spacePerFlex = (availableMainSpace - inflexibleSpace) / totalFlex; | |
163 | |
164 // Size remaining items, take the maximum | |
165 child = firstChild; | |
166 while (child != null) { | |
167 int flex = _getFlex(child); | |
168 if (flex > 0) { | |
169 double childMainSize = spacePerFlex * flex; | |
170 double crossSize; | |
171 switch (_direction) { | |
172 case FlexDirection.horizontal: | |
173 BoxConstraints childConstraints = | |
174 new BoxConstraints(minWidth: childMainSize, maxWidth: childMainS ize); | |
175 crossSize = child.getMaxIntrinsicHeight(childConstraints); | |
abarth-chromium
2015/06/10 04:23:20
It's odd that we use getMaxIntrinsicHeight here bu
jackson
2015/06/10 04:44:46
Acknowledged.
| |
176 maxCrossSize = math.max(maxCrossSize, crossSize); | |
177 break; | |
178 case FlexDirection.vertical: | |
179 BoxConstraints childConstraints = | |
180 new BoxConstraints(minHeight: childMainSize, maxHeight: childMai nSize); | |
181 crossSize = child.getMaxIntrinsicWidth(childConstraints); | |
182 break; | |
183 } | |
184 maxCrossSize = math.max(maxCrossSize, crossSize); | |
abarth-chromium
2015/06/10 04:23:20
Again, we seem to have the problem with this line
jackson
2015/06/10 04:44:46
Acknowledged.
| |
185 } | |
186 child = child.parentData.nextSibling; | |
187 } | |
188 | |
189 // Ensure that we don't violate the given constraints with our result | |
190 switch(_direction) { | |
191 case FlexDirection.horizontal: | |
192 return innerConstraints.constrainHeight(maxCrossSize); | |
193 case FlexDirection.vertical: | |
194 return innerConstraints.constrainWidth(maxCrossSize); | |
195 } | |
196 } | |
66 } | 197 } |
67 | 198 |
68 // We don't currently support this for RenderFlex | 199 double getMinIntrinsicWidth(BoxConstraints constraints) { |
69 double getMaxIntrinsicWidth(BoxConstraints constraints) { | 200 return _getIntrinsicSize( |
70 assert(false); | 201 constraints, |
71 return constraints.constrainWidth(0.0); | 202 FlexDirection.horizontal, |
abarth-chromium
2015/06/10 04:23:20
Shouldn't we use the flex direction of the contain
jackson
2015/06/10 04:44:46
This argument (sizingDirection) is compared with t
abarth-chromium
2015/06/10 04:52:07
Ah, I see. I think it's fine this way. Another c
| |
203 (c, innerConstraints) => c.getMinIntrinsicWidth(innerConstraints) | |
204 ); | |
72 } | 205 } |
73 | 206 |
74 // We don't currently support this for RenderFlex | 207 double getMaxIntrinsicWidth(BoxConstraints constraints) { |
75 double getMinIntrinsicHeight(BoxConstraints constraints) { | 208 return _getIntrinsicSize( |
76 assert(false); | 209 constraints, |
77 return constraints.constrainHeight(0.0); | 210 FlexDirection.horizontal, |
abarth-chromium
2015/06/10 04:23:20
ditto
| |
211 (c, innerConstraints) => c.getMaxIntrinsicWidth(innerConstraints) | |
212 ); | |
78 } | 213 } |
79 | 214 |
80 // We don't currently support this for RenderFlex | 215 double getMinIntrinsicHeight(BoxConstraints constraints) { |
81 double getMaxIntrinsicHeight(BoxConstraints constraints) { | 216 return _getIntrinsicSize( |
82 assert(false); | 217 constraints, |
83 return constraints.constrainHeight(0.0); | 218 FlexDirection.vertical, |
abarth-chromium
2015/06/10 04:23:20
ditto
| |
219 (c, innerConstraints) => c.getMinIntrinsicHeight(innerConstraints) | |
220 ); | |
84 } | 221 } |
85 | 222 |
86 bool get sizedByParent => true; | 223 double getMaxIntrinsicHeight(BoxConstraints constraints) { |
87 void performResize() { | 224 return _getIntrinsicSize( |
88 size = constraints.constrain(new Size(constraints.maxWidth, constraints.maxH eight)); | 225 constraints, |
89 assert(size.height < double.INFINITY); | 226 FlexDirection.vertical, |
abarth-chromium
2015/06/10 04:23:20
ditto
| |
90 assert(size.width < double.INFINITY); | 227 (c, innerConstraints) => c.getMaxIntrinsicHeight(innerConstraints)); |
91 } | 228 } |
92 | 229 |
93 int _getFlex(RenderBox child) { | 230 int _getFlex(RenderBox child) { |
94 assert(child.parentData is FlexBoxParentData); | 231 assert(child.parentData is FlexBoxParentData); |
95 return child.parentData.flex != null ? child.parentData.flex : 0; | 232 return child.parentData.flex != null ? child.parentData.flex : 0; |
96 } | 233 } |
97 | 234 |
98 void performLayout() { | 235 void performLayout() { |
99 // Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexib le Lengths | 236 // Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexib le Lengths |
100 // Steps 1-3. Determine used flex factor, size inflexible items, calculate f ree space | 237 // Steps 1-3. Determine used flex factor, size inflexible items, calculate f ree space |
101 int totalFlex = 0; | 238 int totalFlex = 0; |
102 int totalChildren = 0; | 239 int totalChildren = 0; |
103 assert(constraints != null); | 240 assert(constraints != null); |
104 double freeSpace = (_direction == FlexDirection.horizontal) ? constraints.ma xWidth : constraints.maxHeight; | 241 final double mainSize = (_direction == FlexDirection.horizontal) ? constrain ts.maxWidth : constraints.maxHeight; |
242 double crossSize = 0.0; // This will be determined after laying out the chi ldren | |
243 double freeSpace = mainSize; | |
105 RenderBox child = firstChild; | 244 RenderBox child = firstChild; |
106 while (child != null) { | 245 while (child != null) { |
107 totalChildren++; | 246 totalChildren++; |
108 int flex = _getFlex(child); | 247 int flex = _getFlex(child); |
109 if (flex > 0) { | 248 if (flex > 0) { |
110 totalFlex += child.parentData.flex; | 249 totalFlex += child.parentData.flex; |
111 } else { | 250 } else { |
112 BoxConstraints innerConstraints = new BoxConstraints(maxHeight: constrai nts.maxHeight, | 251 BoxConstraints innerConstraints = new BoxConstraints(maxHeight: constrai nts.maxHeight, |
113 maxWidth: constrain ts.maxWidth); | 252 maxWidth: constrain ts.maxWidth); |
114 child.layout(innerConstraints, parentUsesSize: true); | 253 child.layout(innerConstraints, parentUsesSize: true); |
115 freeSpace -= (_direction == FlexDirection.horizontal) ? child.size.width : child.size.height; | 254 freeSpace -= (_direction == FlexDirection.horizontal) ? child.size.width : child.size.height; |
116 } | 255 } |
117 child = child.parentData.nextSibling; | 256 child = child.parentData.nextSibling; |
118 } | 257 } |
119 | 258 |
120 // Steps 4-5. Distribute remaining space to flexible children. | 259 // Steps 4-5. Distribute remaining space to flexible children. |
121 double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0; | 260 double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0; |
122 double usedSpace = 0.0; | 261 double usedSpace = 0.0; |
123 child = firstChild; | 262 child = firstChild; |
124 while (child != null) { | 263 while (child != null) { |
125 int flex = _getFlex(child); | 264 int flex = _getFlex(child); |
126 if (flex > 0) { | 265 if (flex > 0) { |
127 double spaceForChild = spacePerFlex * flex; | 266 double spaceForChild = spacePerFlex * flex; |
128 BoxConstraints innerConstraints; | 267 BoxConstraints innerConstraints; |
129 switch (_direction) { | 268 switch (_direction) { |
130 case FlexDirection.horizontal: | 269 case FlexDirection.horizontal: |
131 innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeig ht, | 270 innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeig ht, |
132 minWidth: spaceForChild, | 271 minWidth: spaceForChild, |
133 maxWidth: spaceForChild); | 272 maxWidth: spaceForChild); |
273 child.layout(innerConstraints, parentUsesSize: true); | |
274 usedSpace += child.size.width; | |
275 crossSize = math.max(crossSize, child.size.height); | |
134 break; | 276 break; |
135 case FlexDirection.vertical: | 277 case FlexDirection.vertical: |
136 innerConstraints = new BoxConstraints(minHeight: spaceForChild, | 278 innerConstraints = new BoxConstraints(minHeight: spaceForChild, |
137 maxHeight: spaceForChild, | 279 maxHeight: spaceForChild, |
138 maxWidth: constraints.maxWidth ); | 280 maxWidth: constraints.maxWidth ); |
281 child.layout(innerConstraints, parentUsesSize: true); | |
282 usedSpace += child.size.height; | |
283 crossSize = math.max(crossSize, child.size.width); | |
139 break; | 284 break; |
140 } | 285 } |
141 child.layout(innerConstraints, parentUsesSize: true); | |
142 usedSpace += _direction == FlexDirection.horizontal ? child.size.width : child.size.height; | |
143 } | 286 } |
144 child = child.parentData.nextSibling; | 287 child = child.parentData.nextSibling; |
145 } | 288 } |
146 | 289 |
147 // Section 8.2: Axis Alignment using the justify-content property | 290 // Section 8.2: Axis Alignment using the justify-content property |
148 double remainingSpace = math.max(0.0, freeSpace - usedSpace); | 291 double remainingSpace = math.max(0.0, freeSpace - usedSpace); |
149 double leadingSpace; | 292 double leadingSpace; |
150 double betweenSpace; | 293 double betweenSpace; |
151 child = firstChild; | 294 child = firstChild; |
152 switch (_justifyContent) { | 295 switch (_justifyContent) { |
(...skipping 12 matching lines...) Expand all Loading... | |
165 case FlexJustifyContent.spaceBetween: | 308 case FlexJustifyContent.spaceBetween: |
166 leadingSpace = 0.0; | 309 leadingSpace = 0.0; |
167 betweenSpace = totalChildren > 1 ? remainingSpace / (totalChildren - 1) : 0.0; | 310 betweenSpace = totalChildren > 1 ? remainingSpace / (totalChildren - 1) : 0.0; |
168 break; | 311 break; |
169 case FlexJustifyContent.spaceAround: | 312 case FlexJustifyContent.spaceAround: |
170 betweenSpace = totalChildren > 0 ? remainingSpace / totalChildren : 0.0; | 313 betweenSpace = totalChildren > 0 ? remainingSpace / totalChildren : 0.0; |
171 leadingSpace = betweenSpace / 2.0; | 314 leadingSpace = betweenSpace / 2.0; |
172 break; | 315 break; |
173 } | 316 } |
174 | 317 |
318 switch (_direction) { | |
319 case FlexDirection.horizontal: | |
320 size = constraints.constrain(new Size(mainSize, crossSize)); | |
321 break; | |
322 case FlexDirection.vertical: | |
323 size = constraints.constrain(new Size(crossSize, mainSize)); | |
324 break; | |
325 } | |
326 | |
175 // Position elements. For now, center the flex items in the cross direction | 327 // Position elements. For now, center the flex items in the cross direction |
176 double mainDimPosition = leadingSpace; | 328 double mainDimPosition = leadingSpace; |
177 child = firstChild; | 329 child = firstChild; |
178 while (child != null) { | 330 while (child != null) { |
179 switch (_direction) { | 331 switch (_direction) { |
180 case FlexDirection.horizontal: | 332 case FlexDirection.horizontal: |
181 child.parentData.position = new Point(mainDimPosition, size.height / 2 .0 - child.size.height / 2.0); | 333 child.parentData.position = new Point(mainDimPosition, size.height / 2 .0 - child.size.height / 2.0); |
182 mainDimPosition += child.size.width; | 334 mainDimPosition += child.size.width; |
183 break; | 335 break; |
184 case FlexDirection.vertical: | 336 case FlexDirection.vertical: |
185 child.parentData.position = new Point(size.width / 2.0 - child.size.wi dth / 2.0, mainDimPosition); | 337 child.parentData.position = new Point(size.width / 2.0 - child.size.wi dth / 2.0, mainDimPosition); |
186 mainDimPosition += child.size.height; | 338 mainDimPosition += child.size.height; |
187 break; | 339 break; |
188 } | 340 } |
189 mainDimPosition += betweenSpace; | 341 mainDimPosition += betweenSpace; |
190 child = child.parentData.nextSibling; | 342 child = child.parentData.nextSibling; |
191 } | 343 } |
192 } | 344 } |
193 | 345 |
194 void hitTestChildren(HitTestResult result, { Point position }) { | 346 void hitTestChildren(HitTestResult result, { Point position }) { |
195 defaultHitTestChildren(result, position: position); | 347 defaultHitTestChildren(result, position: position); |
196 } | 348 } |
197 | 349 |
198 void paint(RenderObjectDisplayList canvas) { | 350 void paint(RenderObjectDisplayList canvas) { |
199 defaultPaint(canvas); | 351 defaultPaint(canvas); |
200 } | 352 } |
201 } | 353 } |
OLD | NEW |