Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(344)

Side by Side Diff: sky/framework/layout.dart

Issue 1093633002: Layout API prototype, for discussion (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Abstract out some logic from RenderNode since it's not really RenderNode-specific, it could be used… Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | sky/framework/node.dart » ('j') | sky/framework/node.dart » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 library layout;
2
3 import 'node.dart';
4
5 // VALUE TYPES
6
7 class EdgeDims {
8 // used for e.g. padding
9 const EdgeDims(this.top, this.right, this.bottom, this.left);
10 final double top;
11 final double right;
12 final double bottom;
13 final double left;
14 operator ==(EdgeDims other) => (top == other.top) ||
15 (right == other.right) ||
16 (bottom == other.bottom) ||
17 (left == other.left);
18 }
19
20 class Matrix {
21 external static Matrix get IDENTITY;
22 external void rotate(double cx, double cy, double theta); // rotate theta radi ans clockwise around cx,cy
23 }
24
25
26 // PAINTING
27
28 class DisplayList {
29 List<RenderNode> _children;
30 void paintChild(RenderNode child, double x, double y) {
31 _addChildToDisplayList(child, x, y);
32 if (_children == null)
33 _children = new List<RenderNode>();
34 assert(!_children.contains(child));
35 _children.add(child);
ojan 2015/04/23 20:28:32 Why add the children to a list instead of calling
Hixie 2015/04/23 20:45:13 If it's not dirty, you don't need to paint it. We
ojan 2015/04/23 21:12:36 Extra lists == moar slow. So we would only want to
36 }
37 external void _addChildToDisplayList(RenderNode child, double x, double y);
38 }
39
40
41 // ABSTRACT LAYOUT
42
43 class ParentData {
44 void detach() {
45 detachSiblings();
46 }
47 void detachSiblings() { } // workaround for lack of inter-class mixins in Dart
48 }
49
50 class PaintOptions {
51 Matrix transform = Matrix.IDENTITY;
52 }
53
54 const kLayoutDirections = 4;
ojan 2015/04/23 20:28:32 FYI, this is dead
Hixie 2015/04/23 20:45:13 Acknowledged.
55
56 double clamp({double min: 0.0, double value: 0.0, double max: double.INFINITY}) {
57 if (value > max)
58 value = max;
59 if (value < min)
60 value = min;
61 return value;
62 }
63
64 abstract class RenderNode extends Node {
65
66 // LAYOUT
67
68 // pos is only for use by the RenderNode that actually lays this
69 // node out, and any other nodes who happen to know exactly what
70 // kind of node that is.
71 ParentData pos;
72 void setupPos(RenderNode child) {
73 // override this to setup .pos correctly for your class
74 if (child.pos is! ParentData)
75 child.pos = new ParentData();
76 }
77
78 void setAsChild(RenderNode child) { // only for use by subclasses
79 // call this whenever you decide a node is a child
80 assert(child != null);
81 setupPos(child);
82 super.setAsChild(child);
83 }
84 void dropChild(RenderNode child) { // only for use by subclasses
85 assert(child != null);
86 assert(child.pos != null);
87 child.pos.detach();
88 super.dropChild(child);
89 }
90
91 static List<RenderNode> _nodesNeedingLayout = new List<RenderNode>();
92 static bool _debugDoingLayout = false;
93 bool _needsLayout = true;
94 bool get needsLayout => _needsLayout;
95 RenderNode _relayoutSubtreeRoot;
96 void saveRelayoutSubtreeRoot(RenderNode relayoutSubtreeRoot) {
97 assert(_relayoutSubtreeRoot._relayoutSubtreeRoot == null);
98 _relayoutSubtreeRoot = relayoutSubtreeRoot;
99 }
100 void markNeedsLayout() {
101 assert(!_debugDoingLayout);
102 assert(!_debugDoingPaint);
103 if (_needsLayout) return;
104 _needsLayout = true;
105 _nodesNeedingLayout.add(this);
106 }
107 static void flushLayout() {
108 _debugDoingLayout = true;
109 List<RenderNode> dirtyNodes = _nodesNeedingLayout;
110 _nodesNeedingLayout = new List<RenderNode>();
111 dirtyNodes..sort((a, b) => a.order - b.order)..forEach((node) {
112 if (node._needsLayout && node.attached)
113 node._doLayout();
114 });
115 _debugDoingLayout = false;
116 }
117 void _doLayout() {
118 try {
119 if (_relayoutSubtreeRoot != null) {
120 assert(_relayoutSubtreeRoot._relayoutSubtreeRoot == null);
121 _relayoutSubtreeRoot._doLayout();
122 } else {
123 relayout();
124 }
125 } catch (e, stack) {
126 print('Exception raised during layout of ${this}: ${e}');
127 print(stack);
128 return;
129 }
130 assert(!_needsLayout); // check that the relayout() method marked us "not di rty"
131 }
132 /* // this method's signature is subclass-specific, but will exist in
133 // some form in all subclasses:
134 void layout({arguments..., RenderNode relayoutSubtreeRoot}) {
135 if (this node has an opinion about its size, e.g. because it autosizes ba sed on kids, or has an intrinsic dimension) {
136 if (relayoutSubtreeRoot != null) {
137 saveRelayoutSubtreeRoot(relayoutSubtreeRoot);
138 // for each child, if we are going to size ourselves around them:
139 child.layout(... relayoutSubtreeRoot: relayoutSubtreeRoot);
140 width = ...;
141 height = ...;
142 } else {
143 saveRelayoutSubtreeRoot(null); // you can skip this if there's no way you would ever have called saveRelayoutSubtreeRoot() before
144 // we're the root of the relayout subtree
145 // for each child, if we are going to size ourselves around them:
146 child.layout(... relayoutSubtreeRoot: this);
147 width = ...;
148 height = ...;
149 }
150 } else {
151 // we're sizing ourselves exclusively on input from the parent (argumen ts to this function)
152 // ignore relayoutSubtreeRoot
153 saveRelayoutSubtreeRoot(null); // you can skip this if there's no way y ou would ever have called saveRelayoutSubtreeRoot() before
154 width = ...; // based on input from arguments only
155 height = ...; // based on input from arguments only
156 }
157 // for each child whose size we'll ignore when deciding ours:
158 child.layout(... relayoutSubtreeRoot: null); // or just omit relayoutSubt reeRoot
159 layoutDone();
160 return result;
161 }
162 */
163 void relayout() {
164 // Override this to perform relayout without your parent's
165 // involvement.
166 //
167 // This is what is called after the first layout(), if you mark
168 // yourself dirty and don't have a _relayoutSubtreeRoot set; in
169 // other words, either if your parent doesn't care what size you
170 // are (and thus didn't pass a relayoutSubtreeRoot to your
171 // layout() method) or if you sized yourself entirely based on
172 // what your parents told you, and not based on your children (and
173 // thus you never called saveRelayoutSubtreeRoot()).
174 //
175 // In the former case, you can resize yourself here at will. In
176 // the latter case, just leave your dimensions unchanged.
177 //
178 // If _relayoutSubtreeRoot is set (i.e. you called saveRelayout-
179 // SubtreeRoot() in your layout(), with a relayoutSubtreeRoot
180 // argument that was non-null), then if you mark yourself as dirty
181 // then we'll tell that subtree root instead, and the layout will
182 // occur via the layout() tree rather than starting from this
183 // relayout() method.
184 assert(_relayoutSubtreeRoot == null);
185 layoutDone();
186 }
187 void layoutDone({bool needsPaint: true}) {
188 // make sure to call this at the end of your layout() or relayout()
189 _needsLayout = false;
190 if (needsPaint)
191 markNeedsPaint();
192 }
193
194 // when the parent has rotated (e.g. when the screen has been turned
195 // 90 degrees), immediately prior to layout() being called for the
196 // new dimensions, rotate() is called with the old and new angles.
197 // The next time paint() is called, the coordinate space will have
198 // been rotated N quarter-turns clockwise, where:
199 // N = newAngle-oldAngle
200 // ...but the rendering is expected to remain the same, pixel for
201 // pixel, on the output device. Then, the layout() method or
202 // equivalent will be invoked.
203
204 void rotate({
205 int oldAngle, // 0..3
206 int newAngle, // 0..3
207 Duration time
208 }) { }
209
210
211 // HIT TESTING
212
213 void hitTest(double x, double y, List<RenderNode> targets) {
214 // override this if you have children
215 // if any of your children cover x,y, call the top-most such
216 // child's hitTest().
217 // then, call this superclass hitTest().
218 targets.add(this);
219 }
220
221
222 // PAINTING
223
224 PaintOptions paintOptions;
225 DisplayList _cachedPaint;
226 // only set this to a subclass of PaintOptions that this class of RenderNode k nows how to deal with
227
228 static List<RenderNode> _nodesNeedingPaint = new List<RenderNode>();
229 static bool _debugDoingPaint = false;
230 bool _needsPaint = true;
231 bool get needsPaint => _needsPaint;
232 void markNeedsPaint() {
233 assert(!_debugDoingPaint);
234 if (_needsPaint) return;
235 _needsPaint = true;
236 _nodesNeedingPaint.add(this);
237 }
238 static void flushPaint() {
239 List<RenderNode> dirtyNodes = _nodesNeedingPaint;
240 _nodesNeedingPaint = new List<RenderNode>();
241 dirtyNodes..sort((a, b) => a.order - b.order)..forEach((node) {
242 if (node._needsPaint && node.attached)
243 node._doPaint();
244 });
245 }
246 void _doPaint() {
247 assert(!_needsLayout);
248 DisplayList newPaint = new DisplayList();
249 _needsPaint = false;
250 try {
251 paint(newPaint);
252 } catch (e, stack) {
253 print('Exception raised during paint of ${this}: ${e}');
254 print(stack);
255 return;
256 }
257 assert(!_needsLayout); // check that the paint() method didn't mark us dirty again
258 assert(!_needsPaint); // check that the paint() method didn't mark us dirty again
259 if (newPaint._children != null)
260 newPaint._children.forEach((node) {
261 assert(node.attached == attached);
262 if (node._needsPaint)
263 node._doPaint();
264 });
265 _cachedPaint = newPaint;
266 }
267
268 void paint(DisplayList canvas) { }
269
270 }
271
272
273 // GENERIC MIXIN FOR RENDER NODES THAT TAKE A LIST OF CHILDREN
274
275 abstract class ContainerParentDataMixin<ChildType extends RenderNode> {
276 ChildType previousSibling;
277 ChildType nextSibling;
278 void detachSiblings() {
279 if (previousSibling != null)
280 (previousSibling.pos as ContainerParentDataMixin<ChildType>).nextSibling = nextSibling;
281 if (nextSibling != null)
282 (nextSibling.pos as ContainerParentDataMixin<ChildType>).previousSibling = previousSibling;
283 previousSibling = null;
284 nextSibling = null;
285 }
286 }
287
288 abstract class ContainerRenderNodeMixin<ChildType extends RenderNode, ParentData Type extends ContainerParentDataMixin<ChildType>> {
289 // abstract class that has only InlineNode children
290
291 bool _debugUltimatePreviousSiblingOf(ChildType child, { ChildType equals }) {
292 assert(child.pos is ParentDataType);
293 while ((child.pos as ParentDataType).previousSibling != null) {
294 child = (child.pos as ParentDataType).previousSibling;
295 assert(child.pos is ParentDataType);
296 }
297 return child == equals;
298 }
299 bool _debugUltimateNextSiblingOf(ChildType child, { ChildType equals }) {
300 assert(child.pos is ParentDataType);
301 while ((child.pos as ParentDataType).nextSibling != null) {
302 child = (child.pos as ParentDataType).nextSibling;
303 assert(child.pos is ParentDataType);
304 }
305 return child == equals;
306 }
307
308 ChildType _firstChild;
309 ChildType _lastChild;
310 void add(ChildType child, { ChildType before }) {
311 setAsChild(child);
312 if (before == null) {
313 // append at the end (_lastChild)
314 (child.pos as ParentDataType).previousSibling = _lastChild;
315 if (_lastChild != null)
316 (_lastChild.pos as ParentDataType).nextSibling = child;
317 (child.pos as ParentDataType).previousSibling = _lastChild;
318 _lastChild = child;
319 if (_firstChild == null)
320 _firstChild = child;
321 } else {
322 assert(_firstChild != null);
323 assert(_lastChild != null);
324 assert(_debugUltimatePreviousSiblingOf(before, equals: _firstChild));
325 assert(_debugUltimateNextSiblingOf(before, equals: _lastChild));
326 if ((before.pos as ParentDataType).previousSibling == null) {
327 // insert at the start (_firstChild); there's two or more children
328 assert(before == _firstChild);
329 (child.pos as ParentDataType).nextSibling = before;
330 (before.pos as ParentDataType).previousSibling = child;
331 _firstChild = child;
332 } else {
333 // insert in the middle; there's two or more children
334 // set up links from child to siblings
335 (child.pos as ParentDataType).previousSibling = (before.pos as ParentDat aType).previousSibling;
336 (child.pos as ParentDataType).nextSibling = before;
337 // set up links from siblings to child
338 ((child.pos as ParentDataType).previousSibling.pos as ParentDataType).ne xtSibling = child;
339 ((child.pos as ParentDataType).nextSibling.pos as ParentDataType).previo usSibling = child;
340 }
341 }
342 markNeedsLayout();
343 }
344 void remove(ChildType child) {
345 assert(child.pos is ParentDataType);
346 assert(_debugUltimatePreviousSiblingOf(child, equals: _firstChild));
347 assert(_debugUltimateNextSiblingOf(child, equals: _lastChild));
348 if ((child.pos as ParentDataType).previousSibling == null)
349 _firstChild = (child.pos as ParentDataType).nextSibling;
350 else
351 ((child.pos as ParentDataType).previousSibling.pos as ParentDataType).next Sibling = (child.pos as ParentDataType).nextSibling;
352 if ((child.pos as ParentDataType).nextSibling == null)
353 _lastChild = (child.pos as ParentDataType).previousSibling;
354 else
355 ((child.pos as ParentDataType).nextSibling.pos as ParentDataType).previous Sibling = (child.pos as ParentDataType).previousSibling;
356 dropChild(child);
357 markNeedsLayout();
358 }
359 void reorderChildren() {
360 ChildType child = _firstChild;
361 while (child != null) {
362 reorderChild(child);
363 child = (child.pos as ParentDataType).nextSibling;
364 }
365 }
366 void attachChildren() {
367 ChildType child = _firstChild;
368 while (child != null) {
369 child.attach();
370 child = (child.pos as ParentDataType).nextSibling;
371 }
372 }
373 void detachChildren() {
374 ChildType child = _firstChild;
375 while (child != null) {
376 child.detach();
377 child = (child.pos as ParentDataType).nextSibling;
378 }
379 }
380
381 }
382
383
384 // GENERIC BOX RENDERING
385 // Anything that has a concept of x, y, width, height is going to derive from th is
386
387 class BoxDimensions {
388 const BoxDimensions({this.width, this.height});
389 final double width;
390 final double height;
391 }
392
393 class BoxParentData extends ParentData {
394 double x = 0.0;
395 double y = 0.0;
396 }
397
398 abstract class RenderBox extends RenderNode {
399
400 void setupPos(RenderNode child) {
401 if (child.pos is! BoxParentData)
402 child.pos = new BoxParentData();
403 }
404
405 // override this to report what dimensions you would have if you
406 // were laid out with the given constraints this can walk the tree
407 // if it must, but it should be as cheap as possible; just get the
408 // dimensions and nothing else (e.g. don't calculate hypothetical
409 // child positions if they're not needed to determine dimensions)
410 BoxDimensions getIntrinsicDimensions({
411 double minWidth: 0.0,
412 double maxWidth: double.INFINITY,
413 double minHeight: 0.0,
414 double maxHeight: double.INFINITY
415 }) {
416 return new BoxDimensions(
417 width: clamp(min: minWidth, max: maxWidth),
418 height: clamp(min: minHeight, max: maxHeight)
419 );
420 }
421
422 void layout({
423 double minWidth: 0.0,
424 double maxWidth: double.INFINITY,
425 double minHeight: 0.0,
426 double maxHeight: double.INFINITY,
427 RenderNode relayoutSubtreeRoot
428 }) {
429 width = clamp(min: minWidth, max: maxWidth);
430 height = clamp(min: minHeight, max: maxHeight);
431 layoutDone();
432 }
433
434 double width;
435 double height;
436
437 void rotate({
438 int oldAngle, // 0..3
439 int newAngle, // 0..3
440 Duration time
441 }) { }
442
443 }
444
445
446 // SCREEN LAYOUT MANAGER
447
448 class Screen extends RenderNode {
449
450 Screen({
451 RenderBox root,
452 this.timeForRotation: const Duration(microseconds: 83333)
453 }) {
454 assert(root != null);
455 this.root = root;
456 }
457
458 double _width;
459 double get width => _width;
460 double _height;
461 double get height => _height;
462
463 int _orientation; // 0..3
464 int get orientation => _orientation;
465 Duration timeForRotation;
466
467 RenderBox _root;
468 RenderBox get root => _root;
469 void set root (RenderBox value) {
470 assert(root != null);
471 _root = value;
472 setAsChild(_root);
473 markNeedsLayout();
474 }
475
476 void layout({
477 double newWidth,
478 double newHeight,
479 int newOrientation
480 }) {
481 assert(root != null);
482 if (newOrientation != orientation) {
483 if (orientation != null)
484 root.rotate(oldAngle: orientation, newAngle: newOrientation, time: timeF orRotation);
485 _orientation = newOrientation;
486 }
487 if ((newWidth != width) || (newHeight != height)) {
488 _width = newWidth;
489 _height = newHeight;
490 relayout();
491 }
492 }
493
494 void relayout() {
495 assert(root != null);
496 root.layout(
497 minWidth: width,
498 maxWidth: width,
499 minHeight: height,
500 maxHeight: height
501 );
502 assert(root.width == width);
503 assert(root.height == height);
504 }
505
506 void rotate({ int oldAngle, int newAngle, Duration time }) {
507 assert(false); // nobody tells the screen to rotate, the whole rotate() danc e is started from our layout()
508 }
509
510 void paint(DisplayList canvas) {
511 canvas.paintChild(root, 0, 0);
512 }
513
514 }
515
516
517 // BLOCK LAYOUT MANAGER
518
519 class BlockParentData extends BoxParentData with ContainerParentDataMixin<Render Box> { }
520
521 class BlockBox extends RenderBox with ContainerRenderNodeMixin<RenderBox, BlockP arentData> {
522 // lays out RenderBox children in a vertical stack
523 // uses the maximum width provided by the parent
524 // sizes itself to the height of its child stack
525
526 BlockBox({
527 EdgeDims padding: const EdgeDims(0.0, 0.0, 0.0, 0.0)
528 }) {
529 _padding = padding;
530 }
531
532 EdgeDims _padding;
533 EdgeDims get padding => _padding;
534 void set padding(EdgeDims value) {
535 assert(value != null);
536 if (_padding != value) {
537 _padding = value;
538 markNeedsLayout();
539 }
540 }
541
542 void setupPos(RenderBox child) {
543 if (child.pos is! BlockParentData)
544 child.pos = new BlockParentData();
545 }
546
547 // override this to report what dimensions you would have if you
548 // were laid out with the given constraints this can walk the tree
549 // if it must, but it should be as cheap as possible; just get the
550 // dimensions and nothing else (e.g. don't calculate hypothetical
551 // child positions if they're not needed to determine dimensions)
552 BoxDimensions getIntrinsicDimensions({
553 double minWidth: 0.0,
554 double maxWidth: double.INFINITY,
555 double minHeight: 0.0,
556 double maxHeight: double.INFINITY
557 }) {
558 double outerHeight = _padding.top + _padding.bottom;
559 double outerWidth = clamp(min: minWidth, max: maxWidth);
560 double innerWidth = outerWidth - (_padding.left + _padding.right);
561 RenderBox child = _firstChild;
562 while (child != null) {
563 outerHeight += child.getIntrinsicDimensions(minWidth: innerWidth, maxWidth : innerWidth).height;
564 child = (child.pos as BlockParentData).nextSibling;
565 }
566 return new BoxDimensions(
567 width: outerWidth,
568 height: clamp(min: minHeight, max: maxHeight, value: outerHeight)
569 );
570 }
571
572 double _minHeight; // value cached from parent for relayout call
573 double _maxHeight; // value cached from parent for relayout call
574 void layout({
575 double minWidth: 0.0,
576 double maxWidth: double.INFINITY,
577 double minHeight: 0.0,
578 double maxHeight: double.INFINITY,
579 RenderNode relayoutSubtreeRoot
580 }) {
581 if (relayoutSubtreeRoot != null)
582 saveRelayoutSubtreeRoot(relayoutSubtreeRoot);
583 relayoutSubtreeRoot = relayoutSubtreeRoot == null ? this : relayoutSubtreeRo ot;
584 width = clamp(min: minWidth, max: maxWidth);
585 _minHeight = minHeight;
586 _maxHeight = maxHeight;
587 internalLayout(relayoutSubtreeRoot);
588 }
589
590 void relayout() {
591 internalLayout(this);
592 }
593
594 void internalLayout(RenderNode relayoutSubtreeRoot) {
595 assert(_minHeight != null);
596 assert(_maxHeight != null);
597 double y = _padding.top;
598 double innerWidth = width - (_padding.left + _padding.right);
599 RenderBox child = _firstChild;
600 while (child != null) {
601 child.layout(minWidth: innerWidth, maxWidth: innerWidth, relayoutSubtreeRo ot: relayoutSubtreeRoot);
602 assert(child.pos is BlockParentData);
603 (child.pos as BlockParentData).x = 0.0;
604 (child.pos as BlockParentData).y = y;
605 y += child.height;
606 child = (child.pos as BlockParentData).nextSibling;
607 }
608 height = clamp(min: _minHeight, value: y + _padding.bottom, max: _maxHeight) ;
609 layoutDone();
610 }
611
612 void hitTest(double x, double y, List<RenderNode> targets) {
613 RenderBox child = _lastChild;
614 while (child != null) {
615 if ((x >= (child.pos as BlockParentData).x) && (x < (child.pos as BlockPar entData).x + child.width) &&
616 (y >= (child.pos as BlockParentData).y) && (y < (child.pos as BlockPar entData).y + child.height)) {
617 child.hitTest(x, y, targets);
618 break;
619 }
620 child = (child.pos as BlockParentData).previousSibling;
621 }
622 super.hitTest(x, y, targets);
623 }
624
625 void paint(DisplayList canvas) {
626 RenderBox child = _firstChild;
627 while (child != null) {
628 canvas.paintChild(child, (child.pos as BlockParentData).x, (child.pos as B lockParentData).y);
629 child = (child.pos as BlockParentData).nextSibling;
630 }
631 }
632
633 }
634
635
636 // PARAGRAPH LAYOUT MANAGER
637
638 class InlineParentData extends BoxParentData with ContainerParentDataMixin<Inlin eNode> { }
639
640 class ParagraphBox extends RenderBox with ContainerRenderNodeMixin<InlineNode, I nlineParentData> {
641
642 void setupPos(InlineNode child) {
643 if (child.pos is! InlineParentData)
644 child.pos = new InlineParentData();
645 }
646
647 BoxDimensions getIntrinsicDimensions({
648 double minWidth: 0.0,
649 double maxWidth: double.INFINITY,
650 double minHeight: 0.0,
651 double maxHeight: double.INFINITY
652 }) {
653 // ...compute intrinsic dimension given these constraints...
654 }
655
656 double _minWidth; // value cached from parent for relayout call
657 double _maxWidth; // value cached from parent for relayout call
658 double _minHeight; // value cached from parent for relayout call
659 double _maxHeight; // value cached from parent for relayout call
660 void layout({
661 double minWidth: 0.0,
662 double maxWidth: double.INFINITY,
663 double minHeight: 0.0,
664 double maxHeight: double.INFINITY,
665 RenderNode relayoutSubtreeRoot
666 }) {
667 if (relayoutSubtreeRoot != null)
668 saveRelayoutSubtreeRoot(relayoutSubtreeRoot);
669 relayoutSubtreeRoot = relayoutSubtreeRoot == null ? this : relayoutSubtreeRo ot;
670 _minWidth = minWidth;
671 _maxWidth = maxWidth;
672 _minHeight = minHeight;
673 _maxHeight = maxHeight;
674 internalLayout(relayoutSubtreeRoot);
675 layoutDone();
676 }
677
678 void relayout() {
679 internalLayout(this);
680 layoutDone();
681 }
682
683 external void internalLayout(RenderNode relayoutSubtreeRoot);
684 external void hitTest(double x, double y, List<RenderNode> targets);
685 external void paint(DisplayList canvas);
686
687 }
688
689 class InlineNode extends RenderNode with ContainerRenderNodeMixin<InlineNode, In lineParentData> {
690 // ...
691 }
692
693
694 // InlineBox wraps a RenderBox, i.e. it's a box that fits into inline
695 // layout without breaking into multiple lines. This allows you to put
696 // images, form controls, inline blocks, etc, into paragraphs
697 class InlineBox extends InlineNode {
698 InlineBox(this.child);
699 final RenderBox child;
700
701 // ...
702 }
703
704
705 // SCAFFOLD LAYOUT MANAGER
706
707 // a sample special-purpose layout manager
708
709 class ScaffoldBox extends RenderBox {
710
711 ScaffoldBox(this.toolbar, this.body, this.statusbar, this.drawer) {
712 assert(body != null);
713 }
714
715 final RenderBox toolbar;
716 final RenderBox body;
717 final RenderBox statusbar;
718 final RenderBox drawer;
719
720 void layout({
721 double minWidth: 0.0,
722 double maxWidth: double.INFINITY,
723 double minHeight: 0.0,
724 double maxHeight: double.INFINITY,
725 RenderNode relayoutSubtreeRoot
726 }) {
727 width = clamp(min: minWidth, max: maxWidth);
728 height = clamp(min: minHeight, max: maxHeight);
729 relayout();
730 }
731
732 static const kToolbarHeight = 100.0;
733 static const kStatusbarHeight = 50.0;
734
735 void relayout() {
736 double bodyHeight = height;
737 if (toolbar != null) {
738 toolbar.layout(minWidth: width, maxWidth: width, minHeight: kToolbarHeight , maxHeight: kToolbarHeight);
739 (toolbar.pos as BoxParentData).x = 0.0;
740 (toolbar.pos as BoxParentData).y = 0.0;
741 bodyHeight -= kToolbarHeight;
742 }
743 if (statusbar != null) {
744 statusbar.layout(minWidth: width, maxWidth: width, minHeight: kStatusbarHe ight, maxHeight: kStatusbarHeight);
745 (statusbar.pos as BoxParentData).x = 0.0;
746 (statusbar.pos as BoxParentData).y = height - kStatusbarHeight;
747 bodyHeight -= kStatusbarHeight;
748 }
749 body.layout(minWidth: width, maxWidth: width, minHeight: bodyHeight, maxHeig ht: bodyHeight);
750 if (drawer != null)
751 drawer.layout(minWidth: 0.0, maxWidth: width, minHeight: height, maxHeight : height);
752 layoutDone();
753 }
754
755 void hitTest(double x, double y, List<RenderNode> targets) {
756 if ((drawer != null) && (x < drawer.width)) {
757 drawer.hitTest(x, y, targets);
758 } else if ((toolbar != null) && (y < toolbar.height)) {
759 toolbar.hitTest(x, y, targets);
760 } else if ((statusbar != null) && (y > (statusbar.pos as BoxParentData).y)) {
761 statusbar.hitTest(x, y, targets);
762 } else {
763 body.hitTest(x, y, targets);
764 }
765 super.hitTest(x, y, targets);
766 }
767
768 void paint(DisplayList canvas) {
769 canvas.paintChild(body, (body.pos as BoxParentData).x, (body.pos as BoxParen tData).y);
770 if (statusbar != null)
771 canvas.paintChild(statusbar, (statusbar.pos as BoxParentData).x, (statusba r.pos as BoxParentData).y);
772 if (toolbar != null)
773 canvas.paintChild(toolbar, (toolbar.pos as BoxParentData).x, (toolbar.pos as BoxParentData).y);
774 if (drawer != null)
775 canvas.paintChild(drawer, (drawer.pos as BoxParentData).x, (drawer.pos as BoxParentData).y);
776 }
777
778 }
OLDNEW
« no previous file with comments | « no previous file | sky/framework/node.dart » ('j') | sky/framework/node.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698