| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 import 'dart:math' as math; | |
| 6 import 'box.dart'; | |
| 7 import 'object.dart'; | |
| 8 | |
| 9 class FlexBoxParentData extends BoxParentData with ContainerParentDataMixin<Rend
erBox> { | |
| 10 int flex; | |
| 11 | |
| 12 void merge(FlexBoxParentData other) { | |
| 13 if (other.flex != null) | |
| 14 flex = other.flex; | |
| 15 super.merge(other); | |
| 16 } | |
| 17 | |
| 18 String toString() => '${super.toString()}; flex=$flex'; | |
| 19 } | |
| 20 | |
| 21 enum FlexDirection { horizontal, vertical } | |
| 22 | |
| 23 enum FlexJustifyContent { | |
| 24 flexStart, | |
| 25 flexEnd, | |
| 26 center, | |
| 27 spaceBetween, | |
| 28 spaceAround, | |
| 29 } | |
| 30 | |
| 31 enum FlexAlignItems { | |
| 32 flexStart, | |
| 33 flexEnd, | |
| 34 center, | |
| 35 } | |
| 36 | |
| 37 typedef double _ChildSizingFunction(RenderBox child, BoxConstraints constraints)
; | |
| 38 | |
| 39 class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
exBoxParentData>, | |
| 40 RenderBoxContainerDefaultsMixin<RenderBo
x, FlexBoxParentData> { | |
| 41 // lays out RenderBox children using flexible layout | |
| 42 | |
| 43 RenderFlex({ | |
| 44 FlexDirection direction: FlexDirection.horizontal, | |
| 45 FlexJustifyContent justifyContent: FlexJustifyContent.flexStart, | |
| 46 FlexAlignItems alignItems: FlexAlignItems.center | |
| 47 }) : _direction = direction, _justifyContent = justifyContent, _alignItems = a
lignItems; | |
| 48 | |
| 49 FlexDirection _direction; | |
| 50 FlexDirection get direction => _direction; | |
| 51 void set direction (FlexDirection value) { | |
| 52 if (_direction != value) { | |
| 53 _direction = value; | |
| 54 markNeedsLayout(); | |
| 55 } | |
| 56 } | |
| 57 | |
| 58 FlexJustifyContent _justifyContent; | |
| 59 FlexJustifyContent get justifyContent => _justifyContent; | |
| 60 void set justifyContent (FlexJustifyContent value) { | |
| 61 if (_justifyContent != value) { | |
| 62 _justifyContent = value; | |
| 63 markNeedsLayout(); | |
| 64 } | |
| 65 } | |
| 66 | |
| 67 FlexAlignItems _alignItems; | |
| 68 FlexAlignItems get alignItems => _alignItems; | |
| 69 void set alignItems (FlexAlignItems value) { | |
| 70 if (_alignItems != value) { | |
| 71 _alignItems = value; | |
| 72 markNeedsLayout(); | |
| 73 } | |
| 74 } | |
| 75 | |
| 76 void setParentData(RenderBox child) { | |
| 77 if (child.parentData is! FlexBoxParentData) | |
| 78 child.parentData = new FlexBoxParentData(); | |
| 79 } | |
| 80 | |
| 81 double _getIntrinsicSize({ BoxConstraints constraints, | |
| 82 FlexDirection sizingDirection, | |
| 83 _ChildSizingFunction childSize }) { | |
| 84 // http://www.w3.org/TR/2015/WD-css-flexbox-1-20150514/#intrinsic-sizes | |
| 85 if (_direction == sizingDirection) { | |
| 86 // INTRINSIC MAIN SIZE | |
| 87 // Intrinsic main size is the smallest size the flex container can take | |
| 88 // while maintaining the min/max-content contributions of its flex items. | |
| 89 BoxConstraints childConstraints; | |
| 90 switch(_direction) { | |
| 91 case FlexDirection.horizontal: | |
| 92 childConstraints = new BoxConstraints(maxHeight: constraints.maxHeight
); | |
| 93 break; | |
| 94 case FlexDirection.vertical: | |
| 95 childConstraints = new BoxConstraints(maxWidth: constraints.maxWidth); | |
| 96 break; | |
| 97 } | |
| 98 | |
| 99 double totalFlex = 0.0; | |
| 100 double inflexibleSpace = 0.0; | |
| 101 double maxFlexFractionSoFar = 0.0; | |
| 102 RenderBox child = firstChild; | |
| 103 while (child != null) { | |
| 104 int flex = _getFlex(child); | |
| 105 totalFlex += flex; | |
| 106 if (flex > 0) { | |
| 107 double flexFraction = childSize(child, childConstraints) / _getFlex(ch
ild); | |
| 108 maxFlexFractionSoFar = math.max(maxFlexFractionSoFar, flexFraction); | |
| 109 } else { | |
| 110 inflexibleSpace += childSize(child, childConstraints); | |
| 111 } | |
| 112 child = child.parentData.nextSibling; | |
| 113 } | |
| 114 double mainSize = maxFlexFractionSoFar * totalFlex + inflexibleSpace; | |
| 115 | |
| 116 // Ensure that we don't violate the given constraints with our result | |
| 117 switch(_direction) { | |
| 118 case FlexDirection.horizontal: | |
| 119 return constraints.constrainWidth(mainSize); | |
| 120 case FlexDirection.vertical: | |
| 121 return constraints.constrainHeight(mainSize); | |
| 122 } | |
| 123 } else { | |
| 124 // INTRINSIC CROSS SIZE | |
| 125 // The spec wants us to perform layout into the given available main-axis | |
| 126 // space and return the cross size. That's too expensive, so instead we | |
| 127 // size inflexible children according to their max intrinsic size in the | |
| 128 // main direction and use those constraints to determine their max | |
| 129 // intrinsic size in the cross direction. We don't care if the caller | |
| 130 // asked for max or min -- the answer is always computed using the | |
| 131 // max size in the main direction. | |
| 132 | |
| 133 double availableMainSpace; | |
| 134 BoxConstraints childConstraints; | |
| 135 switch(_direction) { | |
| 136 case FlexDirection.horizontal: | |
| 137 childConstraints = new BoxConstraints(maxWidth: constraints.maxWidth); | |
| 138 availableMainSpace = constraints.maxWidth; | |
| 139 break; | |
| 140 case FlexDirection.vertical: | |
| 141 childConstraints = new BoxConstraints(maxHeight: constraints.maxHeight
); | |
| 142 availableMainSpace = constraints.maxHeight; | |
| 143 break; | |
| 144 } | |
| 145 | |
| 146 // Get inflexible space using the max in the main direction | |
| 147 int totalFlex = 0; | |
| 148 double inflexibleSpace = 0.0; | |
| 149 double maxCrossSize = 0.0; | |
| 150 RenderBox child = firstChild; | |
| 151 while (child != null) { | |
| 152 int flex = _getFlex(child); | |
| 153 totalFlex += flex; | |
| 154 double mainSize; | |
| 155 double crossSize; | |
| 156 if (flex == 0) { | |
| 157 switch (_direction) { | |
| 158 case FlexDirection.horizontal: | |
| 159 mainSize = child.getMaxIntrinsicWidth(childConstraints); | |
| 160 BoxConstraints widthConstraints = | |
| 161 new BoxConstraints(minWidth: mainSize, maxWidth: mainSize); | |
| 162 crossSize = child.getMaxIntrinsicHeight(widthConstraints); | |
| 163 break; | |
| 164 case FlexDirection.vertical: | |
| 165 mainSize = child.getMaxIntrinsicHeight(childConstraints); | |
| 166 BoxConstraints heightConstraints = | |
| 167 new BoxConstraints(minWidth: mainSize, maxWidth: mainSize); | |
| 168 crossSize = child.getMaxIntrinsicWidth(heightConstraints); | |
| 169 break; | |
| 170 } | |
| 171 inflexibleSpace += mainSize; | |
| 172 maxCrossSize = math.max(maxCrossSize, crossSize); | |
| 173 } | |
| 174 child = child.parentData.nextSibling; | |
| 175 } | |
| 176 | |
| 177 // Determine the spacePerFlex by allocating the remaining available space | |
| 178 double spacePerFlex = (availableMainSpace - inflexibleSpace) / totalFlex; | |
| 179 | |
| 180 // Size remaining items, find the maximum cross size | |
| 181 child = firstChild; | |
| 182 while (child != null) { | |
| 183 int flex = _getFlex(child); | |
| 184 if (flex > 0) { | |
| 185 double childMainSize = spacePerFlex * flex; | |
| 186 double crossSize; | |
| 187 switch (_direction) { | |
| 188 case FlexDirection.horizontal: | |
| 189 BoxConstraints childConstraints = | |
| 190 new BoxConstraints(minWidth: childMainSize, maxWidth: childMainS
ize); | |
| 191 crossSize = child.getMaxIntrinsicHeight(childConstraints); | |
| 192 break; | |
| 193 case FlexDirection.vertical: | |
| 194 BoxConstraints childConstraints = | |
| 195 new BoxConstraints(minHeight: childMainSize, maxHeight: childMai
nSize); | |
| 196 crossSize = child.getMaxIntrinsicWidth(childConstraints); | |
| 197 break; | |
| 198 } | |
| 199 maxCrossSize = math.max(maxCrossSize, crossSize); | |
| 200 } | |
| 201 child = child.parentData.nextSibling; | |
| 202 } | |
| 203 | |
| 204 // Ensure that we don't violate the given constraints with our result | |
| 205 switch(_direction) { | |
| 206 case FlexDirection.horizontal: | |
| 207 return constraints.constrainHeight(maxCrossSize); | |
| 208 case FlexDirection.vertical: | |
| 209 return constraints.constrainWidth(maxCrossSize); | |
| 210 } | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 double getMinIntrinsicWidth(BoxConstraints constraints) { | |
| 215 return _getIntrinsicSize( | |
| 216 constraints: constraints, | |
| 217 sizingDirection: FlexDirection.horizontal, | |
| 218 childSize: (c, innerConstraints) => c.getMinIntrinsicWidth(innerConstraint
s) | |
| 219 ); | |
| 220 } | |
| 221 | |
| 222 double getMaxIntrinsicWidth(BoxConstraints constraints) { | |
| 223 return _getIntrinsicSize( | |
| 224 constraints: constraints, | |
| 225 sizingDirection: FlexDirection.horizontal, | |
| 226 childSize: (c, innerConstraints) => c.getMaxIntrinsicWidth(innerConstraint
s) | |
| 227 ); | |
| 228 } | |
| 229 | |
| 230 double getMinIntrinsicHeight(BoxConstraints constraints) { | |
| 231 return _getIntrinsicSize( | |
| 232 constraints: constraints, | |
| 233 sizingDirection: FlexDirection.vertical, | |
| 234 childSize: (c, innerConstraints) => c.getMinIntrinsicHeight(innerConstrain
ts) | |
| 235 ); | |
| 236 } | |
| 237 | |
| 238 double getMaxIntrinsicHeight(BoxConstraints constraints) { | |
| 239 return _getIntrinsicSize( | |
| 240 constraints: constraints, | |
| 241 sizingDirection: FlexDirection.vertical, | |
| 242 childSize: (c, innerConstraints) => c.getMaxIntrinsicHeight(innerConstrain
ts)); | |
| 243 } | |
| 244 | |
| 245 int _getFlex(RenderBox child) { | |
| 246 assert(child.parentData is FlexBoxParentData); | |
| 247 return child.parentData.flex != null ? child.parentData.flex : 0; | |
| 248 } | |
| 249 | |
| 250 double _getCrossSize(RenderBox child) { | |
| 251 return (_direction == FlexDirection.horizontal) ? child.size.height : child.
size.width; | |
| 252 } | |
| 253 | |
| 254 double _getMainSize(RenderBox child) { | |
| 255 return (_direction == FlexDirection.horizontal) ? child.size.width : child.s
ize.height; | |
| 256 } | |
| 257 | |
| 258 void performLayout() { | |
| 259 // Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexib
le Lengths | |
| 260 // Steps 1-3. Determine used flex factor, size inflexible items, calculate f
ree space | |
| 261 int totalFlex = 0; | |
| 262 int totalChildren = 0; | |
| 263 assert(constraints != null); | |
| 264 final double mainSize = (_direction == FlexDirection.horizontal) ? constrain
ts.maxWidth : constraints.maxHeight; | |
| 265 double crossSize = 0.0; // This will be determined after laying out the chi
ldren | |
| 266 double freeSpace = mainSize; | |
| 267 RenderBox child = firstChild; | |
| 268 while (child != null) { | |
| 269 totalChildren++; | |
| 270 int flex = _getFlex(child); | |
| 271 if (flex > 0) { | |
| 272 totalFlex += child.parentData.flex; | |
| 273 } else { | |
| 274 BoxConstraints innerConstraints = new BoxConstraints(maxHeight: constrai
nts.maxHeight, | |
| 275 maxWidth: constrain
ts.maxWidth); | |
| 276 child.layout(innerConstraints, parentUsesSize: true); | |
| 277 freeSpace -= _getMainSize(child); | |
| 278 crossSize = math.max(crossSize, _getCrossSize(child)); | |
| 279 } | |
| 280 child = child.parentData.nextSibling; | |
| 281 } | |
| 282 | |
| 283 // Steps 4-5. Distribute remaining space to flexible children. | |
| 284 double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0; | |
| 285 double usedSpace = 0.0; | |
| 286 child = firstChild; | |
| 287 while (child != null) { | |
| 288 int flex = _getFlex(child); | |
| 289 if (flex > 0) { | |
| 290 double spaceForChild = spacePerFlex * flex; | |
| 291 BoxConstraints innerConstraints; | |
| 292 switch (_direction) { | |
| 293 case FlexDirection.horizontal: | |
| 294 innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeig
ht, | |
| 295 minWidth: spaceForChild, | |
| 296 maxWidth: spaceForChild); | |
| 297 break; | |
| 298 case FlexDirection.vertical: | |
| 299 innerConstraints = new BoxConstraints(minHeight: spaceForChild, | |
| 300 maxHeight: spaceForChild, | |
| 301 maxWidth: constraints.maxWidth
); | |
| 302 break; | |
| 303 } | |
| 304 child.layout(innerConstraints, parentUsesSize: true); | |
| 305 usedSpace += _getMainSize(child); | |
| 306 crossSize = math.max(crossSize, _getCrossSize(child)); | |
| 307 } | |
| 308 child = child.parentData.nextSibling; | |
| 309 } | |
| 310 | |
| 311 // Section 8.2: Axis Alignment using the justify-content property | |
| 312 double remainingSpace = math.max(0.0, freeSpace - usedSpace); | |
| 313 double leadingSpace; | |
| 314 double betweenSpace; | |
| 315 child = firstChild; | |
| 316 switch (_justifyContent) { | |
| 317 case FlexJustifyContent.flexStart: | |
| 318 leadingSpace = 0.0; | |
| 319 betweenSpace = 0.0; | |
| 320 break; | |
| 321 case FlexJustifyContent.flexEnd: | |
| 322 leadingSpace = remainingSpace; | |
| 323 betweenSpace = 0.0; | |
| 324 break; | |
| 325 case FlexJustifyContent.center: | |
| 326 leadingSpace = remainingSpace / 2.0; | |
| 327 betweenSpace = 0.0; | |
| 328 break; | |
| 329 case FlexJustifyContent.spaceBetween: | |
| 330 leadingSpace = 0.0; | |
| 331 betweenSpace = totalChildren > 1 ? remainingSpace / (totalChildren - 1)
: 0.0; | |
| 332 break; | |
| 333 case FlexJustifyContent.spaceAround: | |
| 334 betweenSpace = totalChildren > 0 ? remainingSpace / totalChildren : 0.0; | |
| 335 leadingSpace = betweenSpace / 2.0; | |
| 336 break; | |
| 337 } | |
| 338 | |
| 339 switch (_direction) { | |
| 340 case FlexDirection.horizontal: | |
| 341 size = constraints.constrain(new Size(mainSize, crossSize)); | |
| 342 crossSize = size.height; | |
| 343 break; | |
| 344 case FlexDirection.vertical: | |
| 345 size = constraints.constrain(new Size(crossSize, mainSize)); | |
| 346 crossSize = size.width; | |
| 347 break; | |
| 348 } | |
| 349 | |
| 350 // Position elements. For now, center the flex items in the cross direction | |
| 351 double childMainPosition = leadingSpace; | |
| 352 child = firstChild; | |
| 353 while (child != null) { | |
| 354 double childCrossPosition; | |
| 355 switch (_alignItems) { | |
| 356 case FlexAlignItems.flexStart: | |
| 357 childCrossPosition = 0.0; | |
| 358 break; | |
| 359 case FlexAlignItems.flexEnd: | |
| 360 childCrossPosition = crossSize - _getCrossSize(child); | |
| 361 break; | |
| 362 case FlexAlignItems.center: | |
| 363 childCrossPosition = crossSize / 2.0 - _getCrossSize(child) / 2.0; | |
| 364 break; | |
| 365 } | |
| 366 switch (_direction) { | |
| 367 case FlexDirection.horizontal: | |
| 368 child.parentData.position = new Point(childMainPosition, childCrossPos
ition); | |
| 369 break; | |
| 370 case FlexDirection.vertical: | |
| 371 child.parentData.position = new Point(childCrossPosition, childMainPos
ition); | |
| 372 break; | |
| 373 } | |
| 374 childMainPosition += _getMainSize(child) + betweenSpace; | |
| 375 child = child.parentData.nextSibling; | |
| 376 } | |
| 377 } | |
| 378 | |
| 379 void hitTestChildren(HitTestResult result, { Point position }) { | |
| 380 defaultHitTestChildren(result, position: position); | |
| 381 } | |
| 382 | |
| 383 void paint(RenderObjectDisplayList canvas) { | |
| 384 defaultPaint(canvas); | |
| 385 } | |
| 386 } | |
| OLD | NEW |