| 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 'render_node.dart'; | |
| 6 import 'dart:sky' as sky; | |
| 7 | |
| 8 // GENERIC BOX RENDERING | |
| 9 // Anything that has a concept of x, y, width, height is going to derive from th
is | |
| 10 | |
| 11 class EdgeDims { | |
| 12 // used for e.g. padding | |
| 13 const EdgeDims(this.top, this.right, this.bottom, this.left); | |
| 14 final double top; | |
| 15 final double right; | |
| 16 final double bottom; | |
| 17 final double left; | |
| 18 operator ==(EdgeDims other) => (top == other.top) || | |
| 19 (right == other.right) || | |
| 20 (bottom == other.bottom) || | |
| 21 (left == other.left); | |
| 22 } | |
| 23 | |
| 24 class BoxConstraints { | |
| 25 const BoxConstraints({ | |
| 26 this.minWidth: 0.0, | |
| 27 this.maxWidth: double.INFINITY, | |
| 28 this.minHeight: 0.0, | |
| 29 this.maxHeight: double.INFINITY}); | |
| 30 | |
| 31 BoxConstraints.tight(sky.Size size) | |
| 32 : minWidth = size.width, | |
| 33 maxWidth = size.width, | |
| 34 minHeight = size.height, | |
| 35 maxHeight = size.height; | |
| 36 | |
| 37 BoxConstraints deflate(EdgeDims edges) { | |
| 38 assert(edges != null); | |
| 39 return new BoxConstraints( | |
| 40 minWidth: minWidth, | |
| 41 maxWidth: maxWidth - (edges.left + edges.right), | |
| 42 minHeight: minHeight, | |
| 43 maxHeight: maxHeight - (edges.top + edges.bottom) | |
| 44 ); | |
| 45 } | |
| 46 | |
| 47 final double minWidth; | |
| 48 final double maxWidth; | |
| 49 final double minHeight; | |
| 50 final double maxHeight; | |
| 51 | |
| 52 double constrainWidth(double width) { | |
| 53 return clamp(min: minWidth, max: maxWidth, value: width); | |
| 54 } | |
| 55 | |
| 56 double constrainHeight(double height) { | |
| 57 return clamp(min: minHeight, max: maxHeight, value: height); | |
| 58 } | |
| 59 | |
| 60 sky.Size constrain(sky.Size size) { | |
| 61 return new sky.Size(constrainWidth(size.width), constrainHeight(size.height)
); | |
| 62 } | |
| 63 | |
| 64 bool get isInfinite => maxWidth >= double.INFINITY || maxHeight >= double.INFI
NITY; | |
| 65 } | |
| 66 | |
| 67 class BoxParentData extends ParentData { | |
| 68 sky.Point position = new sky.Point(0.0, 0.0); | |
| 69 } | |
| 70 | |
| 71 abstract class RenderBox extends RenderNode { | |
| 72 | |
| 73 void setParentData(RenderNode child) { | |
| 74 if (child.parentData is! BoxParentData) | |
| 75 child.parentData = new BoxParentData(); | |
| 76 } | |
| 77 | |
| 78 // override this to report what dimensions you would have if you | |
| 79 // were laid out with the given constraints this can walk the tree | |
| 80 // if it must, but it should be as cheap as possible; just get the | |
| 81 // dimensions and nothing else (e.g. don't calculate hypothetical | |
| 82 // child positions if they're not needed to determine dimensions) | |
| 83 sky.Size getIntrinsicDimensions(BoxConstraints constraints) { | |
| 84 return constraints.constrain(new sky.Size(0.0, 0.0)); | |
| 85 } | |
| 86 | |
| 87 BoxConstraints get constraints => super.constraints as BoxConstraints; | |
| 88 void performResize() { | |
| 89 // default behaviour for subclasses that have sizedByParent = true | |
| 90 size = constraints.constrain(new sky.Size(0.0, 0.0)); | |
| 91 assert(size.height < double.INFINITY); | |
| 92 assert(size.width < double.INFINITY); | |
| 93 } | |
| 94 void performLayout() { | |
| 95 // descendants have to either override performLayout() to set both | |
| 96 // width and height and lay out children, or, set sizedByParent to | |
| 97 // true so that performResize()'s logic above does its thing. | |
| 98 assert(sizedByParent); | |
| 99 } | |
| 100 | |
| 101 bool hitTest(HitTestResult result, { sky.Point position }) { | |
| 102 hitTestChildren(result, position: position); | |
| 103 result.add(this); | |
| 104 return true; | |
| 105 } | |
| 106 void hitTestChildren(HitTestResult result, { sky.Point position }) { } | |
| 107 | |
| 108 sky.Size size = new sky.Size(0.0, 0.0); | |
| 109 } | |
| 110 | |
| 111 abstract class RenderProxyBox extends RenderBox with RenderNodeWithChildMixin<Re
nderBox> { | |
| 112 RenderProxyBox(RenderBox child) { | |
| 113 this.child = child; | |
| 114 } | |
| 115 | |
| 116 sky.Size getIntrinsicDimensions(BoxConstraints constraints) { | |
| 117 if (child != null) | |
| 118 return child.getIntrinsicDimensions(constraints); | |
| 119 return super.getIntrinsicDimensions(constraints); | |
| 120 } | |
| 121 | |
| 122 void performLayout() { | |
| 123 if (child != null) { | |
| 124 child.layout(constraints, parentUsesSize: true); | |
| 125 size = child.size; | |
| 126 } else { | |
| 127 performResize(); | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 void hitTestChildren(HitTestResult result, { sky.Point position }) { | |
| 132 if (child != null) | |
| 133 child.hitTest(result, position: position); | |
| 134 else | |
| 135 super.hitTestChildren(result, position: position); | |
| 136 } | |
| 137 | |
| 138 void paint(RenderNodeDisplayList canvas) { | |
| 139 if (child != null) | |
| 140 child.paint(canvas); | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 class RenderSizedBox extends RenderProxyBox { | |
| 145 final sky.Size desiredSize; | |
| 146 | |
| 147 RenderSizedBox({ | |
| 148 RenderBox child, | |
| 149 this.desiredSize: const sky.Size.infinite() | |
| 150 }) : super(child); | |
| 151 | |
| 152 sky.Size getIntrinsicDimensions(BoxConstraints constraints) { | |
| 153 return constraints.constrain(desiredSize); | |
| 154 } | |
| 155 | |
| 156 void performLayout() { | |
| 157 size = constraints.constrain(desiredSize); | |
| 158 child.layout(new BoxConstraints.tight(size)); | |
| 159 } | |
| 160 } | |
| 161 | |
| 162 class RenderPadding extends RenderBox with RenderNodeWithChildMixin<RenderBox> { | |
| 163 | |
| 164 RenderPadding(EdgeDims padding, RenderBox child) { | |
| 165 assert(padding != null); | |
| 166 this.padding = padding; | |
| 167 this.child = child; | |
| 168 } | |
| 169 | |
| 170 EdgeDims _padding; | |
| 171 EdgeDims get padding => _padding; | |
| 172 void set padding (EdgeDims value) { | |
| 173 assert(value != null); | |
| 174 if (_padding != value) { | |
| 175 _padding = value; | |
| 176 markNeedsLayout(); | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 sky.Size getIntrinsicDimensions(BoxConstraints constraints) { | |
| 181 assert(padding != null); | |
| 182 constraints = constraints.deflate(padding); | |
| 183 if (child == null) | |
| 184 return super.getIntrinsicDimensions(constraints); | |
| 185 return child.getIntrinsicDimensions(constraints); | |
| 186 } | |
| 187 | |
| 188 void performLayout() { | |
| 189 assert(padding != null); | |
| 190 BoxConstraints innerConstraints = constraints.deflate(padding); | |
| 191 if (child == null) { | |
| 192 size = innerConstraints.constrain( | |
| 193 new sky.Size(padding.left + padding.right, padding.top + padding.botto
m)); | |
| 194 return; | |
| 195 } | |
| 196 child.layout(innerConstraints, parentUsesSize: true); | |
| 197 assert(child.parentData is BoxParentData); | |
| 198 child.parentData.position = new sky.Point(padding.left, padding.top); | |
| 199 size = constraints.constrain(new sky.Size(padding.left + child.size.width +
padding.right, | |
| 200 padding.top + child.size.height +
padding.bottom)); | |
| 201 } | |
| 202 | |
| 203 void paint(RenderNodeDisplayList canvas) { | |
| 204 if (child != null) | |
| 205 canvas.paintChild(child, child.parentData.position); | |
| 206 } | |
| 207 | |
| 208 void hitTestChildren(HitTestResult result, { sky.Point position }) { | |
| 209 if (child != null) { | |
| 210 assert(child.parentData is BoxParentData); | |
| 211 sky.Rect childBounds = new sky.Rect.fromPointAndSize(child.parentData.posi
tion, child.size); | |
| 212 if (childBounds.contains(position)) { | |
| 213 child.hitTest(result, position: new sky.Point(position.x - child.parentD
ata.position.x, | |
| 214 position.y - child.parentD
ata.position.y)); | |
| 215 } | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 } | |
| 220 | |
| 221 // This must be immutable, because we won't notice when it changes | |
| 222 class BoxDecoration { | |
| 223 // TODO(mpcomplete): go through and change the users of this class to pass | |
| 224 // a Color object. | |
| 225 BoxDecoration({ | |
| 226 backgroundColor | |
| 227 }) : backgroundColor = new sky.Color(backgroundColor); | |
| 228 | |
| 229 final sky.Color backgroundColor; | |
| 230 } | |
| 231 | |
| 232 class RenderDecoratedBox extends RenderProxyBox { | |
| 233 | |
| 234 RenderDecoratedBox({ | |
| 235 BoxDecoration decoration, | |
| 236 RenderBox child | |
| 237 }) : _decoration = decoration, super(child); | |
| 238 | |
| 239 BoxDecoration _decoration; | |
| 240 BoxDecoration get decoration => _decoration; | |
| 241 void set decoration (BoxDecoration value) { | |
| 242 if (value == _decoration) | |
| 243 return; | |
| 244 _decoration = value; | |
| 245 markNeedsPaint(); | |
| 246 } | |
| 247 | |
| 248 void paint(RenderNodeDisplayList canvas) { | |
| 249 assert(size.width != null); | |
| 250 assert(size.height != null); | |
| 251 | |
| 252 if (_decoration == null) | |
| 253 return; | |
| 254 | |
| 255 if (_decoration.backgroundColor != null) { | |
| 256 sky.Paint paint = new sky.Paint()..color = _decoration.backgroundColor; | |
| 257 canvas.drawRect(new sky.Rect.fromLTRB(0.0, 0.0, size.width, size.height),
paint); | |
| 258 } | |
| 259 super.paint(canvas); | |
| 260 } | |
| 261 | |
| 262 } | |
| 263 | |
| 264 | |
| 265 // RENDER VIEW LAYOUT MANAGER | |
| 266 | |
| 267 class ViewConstraints { | |
| 268 | |
| 269 const ViewConstraints({ | |
| 270 this.width: 0.0, this.height: 0.0, this.orientation: null | |
| 271 }); | |
| 272 | |
| 273 final double width; | |
| 274 final double height; | |
| 275 final int orientation; | |
| 276 | |
| 277 } | |
| 278 | |
| 279 class RenderView extends RenderNode with RenderNodeWithChildMixin<RenderBox> { | |
| 280 | |
| 281 RenderView({ | |
| 282 RenderBox child, | |
| 283 this.timeForRotation: const Duration(microseconds: 83333) | |
| 284 }) { | |
| 285 this.child = child; | |
| 286 } | |
| 287 | |
| 288 sky.Size _size = new sky.Size(0.0, 0.0); | |
| 289 double get width => _size.width; | |
| 290 double get height => _size.height; | |
| 291 | |
| 292 int _orientation; // 0..3 | |
| 293 int get orientation => _orientation; | |
| 294 Duration timeForRotation; | |
| 295 | |
| 296 ViewConstraints get constraints => super.constraints as ViewConstraints; | |
| 297 bool get sizedByParent => true; | |
| 298 void performResize() { | |
| 299 if (constraints.orientation != _orientation) { | |
| 300 if (_orientation != null && child != null) | |
| 301 child.rotate(oldAngle: _orientation, newAngle: constraints.orientation,
time: timeForRotation); | |
| 302 _orientation = constraints.orientation; | |
| 303 } | |
| 304 _size = new sky.Size(constraints.width, constraints.height); | |
| 305 assert(_size.height < double.INFINITY); | |
| 306 assert(_size.width < double.INFINITY); | |
| 307 } | |
| 308 void performLayout() { | |
| 309 if (child != null) { | |
| 310 child.layout(new BoxConstraints.tight(_size)); | |
| 311 assert(child.size.width == width); | |
| 312 assert(child.size.height == height); | |
| 313 } | |
| 314 } | |
| 315 | |
| 316 void rotate({ int oldAngle, int newAngle, Duration time }) { | |
| 317 assert(false); // nobody tells the screen to rotate, the whole rotate() danc
e is started from our performResize() | |
| 318 } | |
| 319 | |
| 320 bool hitTest(HitTestResult result, { sky.Point position }) { | |
| 321 if (child != null) { | |
| 322 sky.Rect childBounds = new sky.Rect.fromSize(child.size); | |
| 323 if (childBounds.contains(position)) | |
| 324 child.hitTest(result, position: position); | |
| 325 } | |
| 326 result.add(this); | |
| 327 return true; | |
| 328 } | |
| 329 | |
| 330 void paint(RenderNodeDisplayList canvas) { | |
| 331 if (child != null) | |
| 332 canvas.paintChild(child, new sky.Point(0.0, 0.0)); | |
| 333 } | |
| 334 | |
| 335 void paintFrame() { | |
| 336 RenderNode.debugDoingPaint = true; | |
| 337 var canvas = new RenderNodeDisplayList(sky.view.width, sky.view.height); | |
| 338 paint(canvas); | |
| 339 sky.view.picture = canvas.endRecording(); | |
| 340 RenderNode.debugDoingPaint = false; | |
| 341 } | |
| 342 | |
| 343 } | |
| 344 | |
| 345 // DEFAULT BEHAVIORS FOR RENDERBOX CONTAINERS | |
| 346 abstract class RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, Pare
ntDataType extends ContainerParentDataMixin<ChildType>> implements ContainerRend
erNodeMixin<ChildType, ParentDataType> { | |
| 347 | |
| 348 void defaultHitTestChildren(HitTestResult result, { sky.Point position }) { | |
| 349 // the x, y parameters have the top left of the node's box as the origin | |
| 350 ChildType child = lastChild; | |
| 351 while (child != null) { | |
| 352 assert(child.parentData is ParentDataType); | |
| 353 sky.Rect childBounds = new sky.Rect.fromPointAndSize(child.parentData.posi
tion, child.size); | |
| 354 if (childBounds.contains(position)) { | |
| 355 if (child.hitTest(result, position: new sky.Point(position.x - child.par
entData.position.x, | |
| 356 position.y - child.par
entData.position.y))) | |
| 357 break; | |
| 358 } | |
| 359 child = child.parentData.previousSibling; | |
| 360 } | |
| 361 } | |
| 362 | |
| 363 void defaultPaint(RenderNodeDisplayList canvas) { | |
| 364 RenderBox child = firstChild; | |
| 365 while (child != null) { | |
| 366 assert(child.parentData is ParentDataType); | |
| 367 canvas.paintChild(child, child.parentData.position); | |
| 368 child = child.parentData.nextSibling; | |
| 369 } | |
| 370 } | |
| 371 } | |
| OLD | NEW |