Chromium Code Reviews| 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 |