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

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: remove some casting for clarity 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') | no next file with comments »
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);
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;
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();
ojan 2015/05/09 01:23:34 I was talking Elliott through this design and we r
Hixie 2015/05/13 20:29:21 Hm, yeah. I don't see a way to work around this.
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 assert(previousSibling.pos is ContainerParentDataMixin<ChildType>);
281 previousSibling.pos.nextSibling = nextSibling;
282 }
283 if (nextSibling != null) {
284 assert(nextSibling.pos is ContainerParentDataMixin<ChildType>);
285 nextSibling.pos.previousSibling = previousSibling;
286 }
287 previousSibling = null;
288 nextSibling = null;
289 }
290 }
291
292 abstract class ContainerRenderNodeMixin<ChildType extends RenderNode, ParentData Type extends ContainerParentDataMixin<ChildType>> {
293 // abstract class that has only InlineNode children
294
295 bool _debugUltimatePreviousSiblingOf(ChildType child, { ChildType equals }) {
296 assert(child.pos is ParentDataType);
297 while (child.pos.previousSibling != null) {
298 child = child.pos.previousSibling;
299 assert(child.pos is ParentDataType);
300 }
301 return child == equals;
302 }
303 bool _debugUltimateNextSiblingOf(ChildType child, { ChildType equals }) {
304 assert(child.pos is ParentDataType);
305 while (child.pos.nextSibling != null) {
306 child = child.pos.nextSibling;
307 assert(child.pos is ParentDataType);
308 }
309 return child == equals;
310 }
311
312 ChildType _firstChild;
313 ChildType _lastChild;
314 void add(ChildType child, { ChildType before }) {
315 setAsChild(child);
316 assert(child.pos is ParentDataType);
317 if (before == null) {
318 // append at the end (_lastChild)
319 child.pos.previousSibling = _lastChild;
320 if (_lastChild != null) {
321 assert(_lastChild.pos is ParentDataType);
322 _lastChild.pos.nextSibling = child;
323 }
324 _lastChild = child;
325 if (_firstChild == null)
326 _firstChild = child;
327 } else {
328 assert(_firstChild != null);
329 assert(_lastChild != null);
330 assert(_debugUltimatePreviousSiblingOf(before, equals: _firstChild));
331 assert(_debugUltimateNextSiblingOf(before, equals: _lastChild));
332 assert(before.pos is ParentDataType);
333 if (before.pos.previousSibling == null) {
334 // insert at the start (_firstChild); we'll end up with two or more chil dren
335 assert(before == _firstChild);
336 child.pos.nextSibling = before;
337 before.pos.previousSibling = child;
338 _firstChild = child;
339 } else {
340 // insert in the middle; we'll end up with three or more children
341 // set up links from child to siblings
342 child.pos.previousSibling = before.pos.previousSibling;
343 child.pos.nextSibling = before;
344 // set up links from siblings to child
345 assert(child.pos.previousSibling.pos is ParentDataType);
346 assert(child.pos.nextSibling.pos is ParentDataType);
347 child.pos.previousSibling.pos.nextSibling = child;
348 child.pos.nextSibling.pos.previousSibling = child;
349 }
350 }
351 markNeedsLayout();
352 }
353 void remove(ChildType child) {
354 assert(child.pos is ParentDataType);
355 assert(_debugUltimatePreviousSiblingOf(child, equals: _firstChild));
356 assert(_debugUltimateNextSiblingOf(child, equals: _lastChild));
357 if (child.pos.previousSibling == null) {
358 _firstChild = child.pos.nextSibling;
359 } else {
360 assert(child.pos.previousSibling.pos is ParentDataType);
361 child.pos.previousSibling.pos.nextSibling = child.pos.nextSibling;
362 }
363 if (child.pos.nextSibling == null) {
364 _lastChild = child.pos.previousSibling;
365 } else {
366 assert(child.pos.nextSibling.pos is ParentDataType);
367 child.pos.nextSibling.pos.previousSibling = child.pos.previousSibling;
368 }
369 dropChild(child);
370 markNeedsLayout();
371 }
372 void reorderChildren() {
373 ChildType child = _firstChild;
374 while (child != null) {
375 reorderChild(child);
376 assert(child.pos is ParentDataType);
377 child = child.pos.nextSibling;
378 }
379 }
380 void attachChildren() {
381 ChildType child = _firstChild;
382 while (child != null) {
383 child.attach();
384 assert(child.pos is ParentDataType);
385 child = child.pos.nextSibling;
386 }
387 }
388 void detachChildren() {
389 ChildType child = _firstChild;
390 while (child != null) {
391 child.detach();
392 assert(child.pos is ParentDataType);
393 child = child.pos.nextSibling;
394 }
395 }
396
397 }
398
399
400 // GENERIC BOX RENDERING
401 // Anything that has a concept of x, y, width, height is going to derive from th is
402
403 class BoxDimensions {
404 const BoxDimensions({this.width, this.height});
405 final double width;
406 final double height;
407 }
408
409 class BoxParentData extends ParentData {
410 double x = 0.0;
411 double y = 0.0;
412 }
413
414 abstract class RenderBox extends RenderNode {
415
416 void setupPos(RenderNode child) {
417 if (child.pos is! BoxParentData)
418 child.pos = new BoxParentData();
419 }
420
421 // override this to report what dimensions you would have if you
422 // were laid out with the given constraints this can walk the tree
423 // if it must, but it should be as cheap as possible; just get the
424 // dimensions and nothing else (e.g. don't calculate hypothetical
425 // child positions if they're not needed to determine dimensions)
426 BoxDimensions getIntrinsicDimensions({
427 double minWidth: 0.0,
428 double maxWidth: double.INFINITY,
429 double minHeight: 0.0,
430 double maxHeight: double.INFINITY
431 }) {
432 return new BoxDimensions(
433 width: clamp(min: minWidth, max: maxWidth),
434 height: clamp(min: minHeight, max: maxHeight)
435 );
436 }
437
438 void layout({
439 double minWidth: 0.0,
440 double maxWidth: double.INFINITY,
441 double minHeight: 0.0,
442 double maxHeight: double.INFINITY,
443 RenderNode relayoutSubtreeRoot
444 }) {
445 width = clamp(min: minWidth, max: maxWidth);
446 height = clamp(min: minHeight, max: maxHeight);
447 layoutDone();
448 }
449
450 double width;
451 double height;
452
453 void rotate({
454 int oldAngle, // 0..3
455 int newAngle, // 0..3
456 Duration time
457 }) { }
458
459 }
460
461
462 // SCREEN LAYOUT MANAGER
463
464 class Screen extends RenderNode {
465
466 Screen({
467 RenderBox root,
468 this.timeForRotation: const Duration(microseconds: 83333)
469 }) {
470 assert(root != null);
471 this.root = root;
472 }
473
474 double _width;
475 double get width => _width;
476 double _height;
477 double get height => _height;
478
479 int _orientation; // 0..3
480 int get orientation => _orientation;
481 Duration timeForRotation;
482
483 RenderBox _root;
484 RenderBox get root => _root;
485 void set root (RenderBox value) {
486 assert(root != null);
487 _root = value;
488 setAsChild(_root);
489 markNeedsLayout();
490 }
491
492 void layout({
493 double newWidth,
494 double newHeight,
495 int newOrientation
496 }) {
497 assert(root != null);
498 if (newOrientation != orientation) {
499 if (orientation != null)
500 root.rotate(oldAngle: orientation, newAngle: newOrientation, time: timeF orRotation);
501 _orientation = newOrientation;
502 }
503 if ((newWidth != width) || (newHeight != height)) {
504 _width = newWidth;
505 _height = newHeight;
506 relayout();
507 }
508 }
509
510 void relayout() {
511 assert(root != null);
512 root.layout(
513 minWidth: width,
514 maxWidth: width,
515 minHeight: height,
516 maxHeight: height
517 );
518 assert(root.width == width);
519 assert(root.height == height);
520 }
521
522 void rotate({ int oldAngle, int newAngle, Duration time }) {
523 assert(false); // nobody tells the screen to rotate, the whole rotate() danc e is started from our layout()
524 }
525
526 void paint(DisplayList canvas) {
527 canvas.paintChild(root, 0.0, 0.0);
528 }
529
530 }
531
532
533 // BLOCK LAYOUT MANAGER
534
535 class BlockParentData extends BoxParentData with ContainerParentDataMixin<Render Box> { }
536
537 class BlockBox extends RenderBox with ContainerRenderNodeMixin<RenderBox, BlockP arentData> {
538 // lays out RenderBox children in a vertical stack
539 // uses the maximum width provided by the parent
540 // sizes itself to the height of its child stack
541
542 BlockBox({
543 EdgeDims padding: const EdgeDims(0.0, 0.0, 0.0, 0.0)
544 }) {
545 _padding = padding;
546 }
547
548 EdgeDims _padding;
549 EdgeDims get padding => _padding;
550 void set padding(EdgeDims value) {
551 assert(value != null);
552 if (_padding != value) {
553 _padding = value;
554 markNeedsLayout();
555 }
556 }
557
558 void setupPos(RenderBox child) {
559 if (child.pos is! BlockParentData)
560 child.pos = new BlockParentData();
561 }
562
563 // override this to report what dimensions you would have if you
564 // were laid out with the given constraints this can walk the tree
565 // if it must, but it should be as cheap as possible; just get the
566 // dimensions and nothing else (e.g. don't calculate hypothetical
567 // child positions if they're not needed to determine dimensions)
568 BoxDimensions getIntrinsicDimensions({
569 double minWidth: 0.0,
570 double maxWidth: double.INFINITY,
571 double minHeight: 0.0,
572 double maxHeight: double.INFINITY
573 }) {
574 double outerHeight = _padding.top + _padding.bottom;
575 double outerWidth = clamp(min: minWidth, max: maxWidth);
576 double innerWidth = outerWidth - (_padding.left + _padding.right);
577 RenderBox child = _firstChild;
578 while (child != null) {
579 outerHeight += child.getIntrinsicDimensions(minWidth: innerWidth, maxWidth : innerWidth).height;
580 assert(child.pos is BlockParentData);
581 child = child.pos.nextSibling;
582 }
583 return new BoxDimensions(
584 width: outerWidth,
585 height: clamp(min: minHeight, max: maxHeight, value: outerHeight)
586 );
587 }
588
589 double _minHeight; // value cached from parent for relayout call
590 double _maxHeight; // value cached from parent for relayout call
591 void layout({
592 double minWidth: 0.0,
593 double maxWidth: double.INFINITY,
594 double minHeight: 0.0,
595 double maxHeight: double.INFINITY,
596 RenderNode relayoutSubtreeRoot
597 }) {
598 if (relayoutSubtreeRoot != null)
599 saveRelayoutSubtreeRoot(relayoutSubtreeRoot);
600 relayoutSubtreeRoot = relayoutSubtreeRoot == null ? this : relayoutSubtreeRo ot;
601 width = clamp(min: minWidth, max: maxWidth);
602 _minHeight = minHeight;
603 _maxHeight = maxHeight;
604 internalLayout(relayoutSubtreeRoot);
605 }
606
607 void relayout() {
608 internalLayout(this);
609 }
610
611 void internalLayout(RenderNode relayoutSubtreeRoot) {
612 assert(_minHeight != null);
613 assert(_maxHeight != null);
614 double y = _padding.top;
615 double innerWidth = width - (_padding.left + _padding.right);
616 RenderBox child = _firstChild;
617 while (child != null) {
618 child.layout(minWidth: innerWidth, maxWidth: innerWidth, relayoutSubtreeRo ot: relayoutSubtreeRoot);
619 assert(child.pos is BlockParentData);
620 child.pos.x = 0.0;
621 child.pos.y = y;
622 y += child.height;
623 child = child.pos.nextSibling;
624 }
625 height = clamp(min: _minHeight, value: y + _padding.bottom, max: _maxHeight) ;
626 layoutDone();
627 }
628
629 void hitTest(double x, double y, List<RenderNode> targets) {
630 RenderBox child = _lastChild;
631 while (child != null) {
632 assert(child.pos is BlockParentData);
633 if ((x >= child.pos.x) && (x < child.pos.x + child.width) &&
634 (y >= child.pos.y) && (y < child.pos.y + child.height)) {
635 child.hitTest(x, y, targets);
636 break;
637 }
638 child = child.pos.previousSibling;
639 }
640 super.hitTest(x, y, targets);
641 }
642
643 void paint(DisplayList canvas) {
644 RenderBox child = _firstChild;
645 while (child != null) {
646 assert(child.pos is BlockParentData);
647 canvas.paintChild(child, child.pos.x, child.pos.y);
648 child = child.pos.nextSibling;
649 }
650 }
651
652 }
653
654
655 // PARAGRAPH LAYOUT MANAGER
656
657 class InlineParentData extends BoxParentData with ContainerParentDataMixin<Inlin eNode> { }
658
659 class ParagraphBox extends RenderBox with ContainerRenderNodeMixin<InlineNode, I nlineParentData> {
660
661 void setupPos(InlineNode child) {
662 if (child.pos is! InlineParentData)
663 child.pos = new InlineParentData();
664 }
665
666 BoxDimensions getIntrinsicDimensions({
667 double minWidth: 0.0,
668 double maxWidth: double.INFINITY,
669 double minHeight: 0.0,
670 double maxHeight: double.INFINITY
671 }) {
672 // ...compute intrinsic dimension given these constraints...
673 }
674
675 double _minWidth; // value cached from parent for relayout call
676 double _maxWidth; // value cached from parent for relayout call
677 double _minHeight; // value cached from parent for relayout call
678 double _maxHeight; // value cached from parent for relayout call
679 void layout({
680 double minWidth: 0.0,
681 double maxWidth: double.INFINITY,
682 double minHeight: 0.0,
683 double maxHeight: double.INFINITY,
684 RenderNode relayoutSubtreeRoot
685 }) {
686 if (relayoutSubtreeRoot != null)
687 saveRelayoutSubtreeRoot(relayoutSubtreeRoot);
688 relayoutSubtreeRoot = relayoutSubtreeRoot == null ? this : relayoutSubtreeRo ot;
689 _minWidth = minWidth;
690 _maxWidth = maxWidth;
691 _minHeight = minHeight;
692 _maxHeight = maxHeight;
693 internalLayout(relayoutSubtreeRoot);
694 layoutDone();
695 }
696
697 void relayout() {
698 internalLayout(this);
699 layoutDone();
700 }
701
702 external void internalLayout(RenderNode relayoutSubtreeRoot);
703 external void hitTest(double x, double y, List<RenderNode> targets);
704 external void paint(DisplayList canvas);
705
706 }
707
708 class InlineNode extends RenderNode with ContainerRenderNodeMixin<InlineNode, In lineParentData> {
709 // ...
710 }
711
712
713 // InlineBox wraps a RenderBox, i.e. it's a box that fits into inline
714 // layout without breaking into multiple lines. This allows you to put
715 // images, form controls, inline blocks, etc, into paragraphs
716 class InlineBox extends InlineNode {
717 InlineBox(this.child);
718 final RenderBox child;
719
720 // ...
721 }
722
723
724 // SCAFFOLD LAYOUT MANAGER
725
726 // a sample special-purpose layout manager
727
728 class ScaffoldBox extends RenderBox {
729
730 ScaffoldBox(this.toolbar, this.body, this.statusbar, this.drawer) {
731 assert(body != null);
732 }
733
734 final RenderBox toolbar;
735 final RenderBox body;
736 final RenderBox statusbar;
737 final RenderBox drawer;
738
739 void layout({
740 double minWidth: 0.0,
741 double maxWidth: double.INFINITY,
742 double minHeight: 0.0,
743 double maxHeight: double.INFINITY,
744 RenderNode relayoutSubtreeRoot
745 }) {
746 width = clamp(min: minWidth, max: maxWidth);
747 height = clamp(min: minHeight, max: maxHeight);
748 relayout();
749 }
750
751 static const kToolbarHeight = 100.0;
752 static const kStatusbarHeight = 50.0;
753
754 void relayout() {
755 double bodyHeight = height;
756 if (toolbar != null) {
757 toolbar.layout(minWidth: width, maxWidth: width, minHeight: kToolbarHeight , maxHeight: kToolbarHeight);
758 assert(toolbar.pos is BoxParentData);
759 toolbar.pos.x = 0.0;
760 toolbar.pos.y = 0.0;
761 bodyHeight -= kToolbarHeight;
762 }
763 if (statusbar != null) {
764 statusbar.layout(minWidth: width, maxWidth: width, minHeight: kStatusbarHe ight, maxHeight: kStatusbarHeight);
765 assert(statusbar.pos is BoxParentData);
766 statusbar.pos.x = 0.0;
767 statusbar.pos.y = height - kStatusbarHeight;
768 bodyHeight -= kStatusbarHeight;
769 }
770 body.layout(minWidth: width, maxWidth: width, minHeight: bodyHeight, maxHeig ht: bodyHeight);
771 if (drawer != null)
772 drawer.layout(minWidth: 0.0, maxWidth: width, minHeight: height, maxHeight : height);
773 layoutDone();
774 }
775
776 void hitTest(double x, double y, List<RenderNode> targets) {
777 if ((drawer != null) && (x < drawer.width)) {
778 drawer.hitTest(x, y, targets);
779 } else if ((toolbar != null) && (y < toolbar.height)) {
780 toolbar.hitTest(x, y, targets);
781 } else if ((statusbar != null) && (y > (statusbar.pos as BoxParentData).y)) {
782 statusbar.hitTest(x, y, targets);
783 } else {
784 body.hitTest(x, y, targets);
785 }
786 super.hitTest(x, y, targets);
787 }
788
789 void paint(DisplayList canvas) {
790 canvas.paintChild(body, (body.pos as BoxParentData).x, (body.pos as BoxParen tData).y);
791 if (statusbar != null)
792 canvas.paintChild(statusbar, (statusbar.pos as BoxParentData).x, (statusba r.pos as BoxParentData).y);
793 if (toolbar != null)
794 canvas.paintChild(toolbar, (toolbar.pos as BoxParentData).x, (toolbar.pos as BoxParentData).y);
795 if (drawer != null)
796 canvas.paintChild(drawer, (drawer.pos as BoxParentData).x, (drawer.pos as BoxParentData).y);
797 }
798
799 }
OLDNEW
« no previous file with comments | « no previous file | sky/framework/node.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698