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 const BoxDecoration({ |
| 224 this.backgroundColor |
| 225 }); |
| 226 |
| 227 final int backgroundColor; |
| 228 } |
| 229 |
| 230 class RenderDecoratedBox extends RenderProxyBox { |
| 231 |
| 232 RenderDecoratedBox({ |
| 233 BoxDecoration decoration, |
| 234 RenderBox child |
| 235 }) : _decoration = decoration, super(child); |
| 236 |
| 237 BoxDecoration _decoration; |
| 238 BoxDecoration get decoration => _decoration; |
| 239 void set decoration (BoxDecoration value) { |
| 240 if (value == _decoration) |
| 241 return; |
| 242 _decoration = value; |
| 243 markNeedsPaint(); |
| 244 } |
| 245 |
| 246 void paint(RenderNodeDisplayList canvas) { |
| 247 assert(size.width != null); |
| 248 assert(size.height != null); |
| 249 |
| 250 if (_decoration == null) |
| 251 return; |
| 252 |
| 253 if (_decoration.backgroundColor != null) { |
| 254 sky.Paint paint = new sky.Paint()..color = _decoration.backgroundColor; |
| 255 canvas.drawRect(new sky.Rect.fromLTRB(0.0, 0.0, size.width, size.height),
paint); |
| 256 } |
| 257 super.paint(canvas); |
| 258 } |
| 259 |
| 260 } |
| 261 |
| 262 |
| 263 // RENDER VIEW LAYOUT MANAGER |
| 264 |
| 265 class ViewConstraints { |
| 266 |
| 267 const ViewConstraints({ |
| 268 this.width: 0.0, this.height: 0.0, this.orientation: null |
| 269 }); |
| 270 |
| 271 final double width; |
| 272 final double height; |
| 273 final int orientation; |
| 274 |
| 275 } |
| 276 |
| 277 class RenderView extends RenderNode with RenderNodeWithChildMixin<RenderBox> { |
| 278 |
| 279 RenderView({ |
| 280 RenderBox child, |
| 281 this.timeForRotation: const Duration(microseconds: 83333) |
| 282 }) { |
| 283 this.child = child; |
| 284 } |
| 285 |
| 286 sky.Size _size = new sky.Size(0.0, 0.0); |
| 287 double get width => _size.width; |
| 288 double get height => _size.height; |
| 289 |
| 290 int _orientation; // 0..3 |
| 291 int get orientation => _orientation; |
| 292 Duration timeForRotation; |
| 293 |
| 294 ViewConstraints get constraints => super.constraints as ViewConstraints; |
| 295 bool get sizedByParent => true; |
| 296 void performResize() { |
| 297 if (constraints.orientation != _orientation) { |
| 298 if (_orientation != null && child != null) |
| 299 child.rotate(oldAngle: _orientation, newAngle: constraints.orientation,
time: timeForRotation); |
| 300 _orientation = constraints.orientation; |
| 301 } |
| 302 _size = new sky.Size(constraints.width, constraints.height); |
| 303 assert(_size.height < double.INFINITY); |
| 304 assert(_size.width < double.INFINITY); |
| 305 } |
| 306 void performLayout() { |
| 307 if (child != null) { |
| 308 child.layout(new BoxConstraints.tight(_size)); |
| 309 assert(child.size.width == width); |
| 310 assert(child.size.height == height); |
| 311 } |
| 312 } |
| 313 |
| 314 void rotate({ int oldAngle, int newAngle, Duration time }) { |
| 315 assert(false); // nobody tells the screen to rotate, the whole rotate() danc
e is started from our performResize() |
| 316 } |
| 317 |
| 318 bool hitTest(HitTestResult result, { sky.Point position }) { |
| 319 if (child != null) { |
| 320 sky.Rect childBounds = new sky.Rect.fromSize(child.size); |
| 321 if (childBounds.contains(position)) |
| 322 child.hitTest(result, position: position); |
| 323 } |
| 324 result.add(this); |
| 325 return true; |
| 326 } |
| 327 |
| 328 void paint(RenderNodeDisplayList canvas) { |
| 329 if (child != null) |
| 330 canvas.paintChild(child, new sky.Point(0.0, 0.0)); |
| 331 } |
| 332 |
| 333 void paintFrame() { |
| 334 RenderNode.debugDoingPaint = true; |
| 335 var canvas = new RenderNodeDisplayList(sky.view.width, sky.view.height); |
| 336 paint(canvas); |
| 337 sky.view.picture = canvas.endRecording(); |
| 338 RenderNode.debugDoingPaint = false; |
| 339 } |
| 340 |
| 341 } |
| 342 |
| 343 // DEFAULT BEHAVIORS FOR RENDERBOX CONTAINERS |
| 344 abstract class RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, Pare
ntDataType extends ContainerParentDataMixin<ChildType>> implements ContainerRend
erNodeMixin<ChildType, ParentDataType> { |
| 345 |
| 346 void defaultHitTestChildren(HitTestResult result, { sky.Point position }) { |
| 347 // the x, y parameters have the top left of the node's box as the origin |
| 348 ChildType child = lastChild; |
| 349 while (child != null) { |
| 350 assert(child.parentData is ParentDataType); |
| 351 sky.Rect childBounds = new sky.Rect.fromPointAndSize(child.parentData.posi
tion, child.size); |
| 352 if (childBounds.contains(position)) { |
| 353 if (child.hitTest(result, position: new sky.Point(position.x - child.par
entData.position.x, |
| 354 position.y - child.par
entData.position.y))) |
| 355 break; |
| 356 } |
| 357 child = child.parentData.previousSibling; |
| 358 } |
| 359 } |
| 360 |
| 361 void defaultPaint(RenderNodeDisplayList canvas) { |
| 362 RenderBox child = firstChild; |
| 363 while (child != null) { |
| 364 assert(child.parentData is ParentDataType); |
| 365 canvas.paintChild(child, child.parentData.position); |
| 366 child = child.parentData.nextSibling; |
| 367 } |
| 368 } |
| 369 } |
OLD | NEW |