blob: fc1ddfbfc5828fd686313155059f74148d899dd6 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkView.h"
9#include "SkCanvas.h"
10
11////////////////////////////////////////////////////////////////////////
12
13SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags))
14{
15 fWidth = fHeight = 0;
16 fLoc.set(0, 0);
17 fParent = fFirstChild = fNextSibling = fPrevSibling = NULL;
reed@google.comf03bb562011-11-11 21:42:12 +000018 fMatrix.setIdentity();
reed@android.com8a1c16f2008-12-17 15:59:43 +000019 fContainsFocus = 0;
20}
21
22SkView::~SkView()
23{
24 this->detachAllChildren();
25}
26
27void SkView::setFlags(uint32_t flags)
28{
29 SkASSERT((flags & ~kAllFlagMasks) == 0);
30
31 uint32_t diff = fFlags ^ flags;
32
33 if (diff & kVisible_Mask)
34 this->inval(NULL);
35
36 fFlags = SkToU8(flags);
37
38 if (diff & kVisible_Mask)
39 {
40 this->inval(NULL);
41 }
42}
43
44void SkView::setVisibleP(bool pred)
45{
46 this->setFlags(SkSetClearShift(fFlags, pred, kVisible_Shift));
47}
48
49void SkView::setEnabledP(bool pred)
50{
51 this->setFlags(SkSetClearShift(fFlags, pred, kEnabled_Shift));
52}
53
54void SkView::setFocusableP(bool pred)
55{
56 this->setFlags(SkSetClearShift(fFlags, pred, kFocusable_Shift));
57}
58
reed@android.comf2b98d62010-12-20 18:26:13 +000059void SkView::setClipToBounds(bool pred) {
60 this->setFlags(SkSetClearShift(fFlags, !pred, kNoClip_Shift));
61}
62
reed@android.com8a1c16f2008-12-17 15:59:43 +000063void SkView::setSize(SkScalar width, SkScalar height)
64{
65 width = SkMaxScalar(0, width);
66 height = SkMaxScalar(0, height);
67
68 if (fWidth != width || fHeight != height)
69 {
70 this->inval(NULL);
71 fWidth = width;
72 fHeight = height;
73 this->inval(NULL);
74 this->onSizeChange();
75 this->invokeLayout();
76 }
77}
78
79void SkView::setLoc(SkScalar x, SkScalar y)
80{
81 if (fLoc.fX != x || fLoc.fY != y)
82 {
83 this->inval(NULL);
84 fLoc.set(x, y);
reed@google.comf03bb562011-11-11 21:42:12 +000085 this->inval(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 }
87}
88
89void SkView::offset(SkScalar dx, SkScalar dy)
90{
91 if (dx || dy)
92 this->setLoc(fLoc.fX + dx, fLoc.fY + dy);
93}
94
reed@google.comf03bb562011-11-11 21:42:12 +000095void SkView::setLocalMatrix(const SkMatrix& matrix)
96{
97 this->inval(NULL);
98 fMatrix = matrix;
99 this->inval(NULL);
100}
101
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102void SkView::draw(SkCanvas* canvas)
103{
104 if (fWidth && fHeight && this->isVisible())
105 {
106 SkRect r;
107 r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight);
reed@android.comf2b98d62010-12-20 18:26:13 +0000108 if (this->isClipToBounds() &&
109 canvas->quickReject(r, SkCanvas::kBW_EdgeType)) {
110 return;
111 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112
113 SkAutoCanvasRestore as(canvas, true);
114
reed@android.comf2b98d62010-12-20 18:26:13 +0000115 if (this->isClipToBounds()) {
116 canvas->clipRect(r);
117 }
reed@google.comf03bb562011-11-11 21:42:12 +0000118
119 canvas->translate(fLoc.fX, fLoc.fY);
120 canvas->concat(fMatrix);
121
reed@android.com6c5f6f22009-08-14 16:08:38 +0000122 if (fParent) {
123 fParent->beforeChild(this, canvas);
124 }
reed@android.com562ea922010-02-08 21:45:03 +0000125
126 int sc = canvas->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 this->onDraw(canvas);
reed@android.com562ea922010-02-08 21:45:03 +0000128 canvas->restoreToCount(sc);
129
reed@android.com6c5f6f22009-08-14 16:08:38 +0000130 if (fParent) {
131 fParent->afterChild(this, canvas);
132 }
133
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 B2FIter iter(this);
135 SkView* child;
136
137 SkCanvas* childCanvas = this->beforeChildren(canvas);
138
139 while ((child = iter.next()) != NULL)
140 child->draw(childCanvas);
141
142 this->afterChildren(canvas);
143 }
144}
145
reed@android.comf2b98d62010-12-20 18:26:13 +0000146void SkView::inval(SkRect* rect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 SkView* view = this;
reed@android.comf2b98d62010-12-20 18:26:13 +0000148 SkRect storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149
reed@android.comf2b98d62010-12-20 18:26:13 +0000150 for (;;) {
151 if (!view->isVisible()) {
152 return;
153 }
154 if (view->isClipToBounds()) {
155 SkRect bounds;
156 view->getLocalBounds(&bounds);
157 if (rect && !bounds.intersect(*rect)) {
158 return;
159 }
160 storage = bounds;
161 rect = &storage;
162 }
163 if (view->handleInval(rect)) {
164 return;
165 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 SkView* parent = view->fParent;
reed@android.comf2b98d62010-12-20 18:26:13 +0000168 if (parent == NULL) {
169 return;
170 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171
reed@android.comf2b98d62010-12-20 18:26:13 +0000172 if (rect) {
173 rect->offset(view->fLoc.fX, view->fLoc.fY);
174 }
175 view = parent;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 }
177}
178
179////////////////////////////////////////////////////////////////////////////
180
181bool SkView::setFocusView(SkView* fv)
182{
183 SkView* view = this;
184
185 do {
186 if (view->onSetFocusView(fv))
187 return true;
188 } while ((view = view->fParent) != NULL);
189 return false;
190}
191
192SkView* SkView::getFocusView() const
193{
194 SkView* focus = NULL;
195 const SkView* view = this;
196 do {
197 if (view->onGetFocusView(&focus))
198 break;
199 } while ((view = view->fParent) != NULL);
200 return focus;
201}
202
203bool SkView::hasFocus() const
204{
205 return this == this->getFocusView();
206}
207
208bool SkView::acceptFocus()
209{
210 return this->isFocusable() && this->setFocusView(this);
211}
212
213/*
214 Try to give focus to this view, or its children
215*/
216SkView* SkView::acceptFocus(FocusDirection dir)
217{
218 if (dir == kNext_FocusDirection)
219 {
220 if (this->acceptFocus())
221 return this;
222
223 B2FIter iter(this);
224 SkView* child, *focus;
225 while ((child = iter.next()) != NULL)
226 if ((focus = child->acceptFocus(dir)) != NULL)
227 return focus;
228 }
229 else // prev
230 {
231 F2BIter iter(this);
232 SkView* child, *focus;
233 while ((child = iter.next()) != NULL)
234 if ((focus = child->acceptFocus(dir)) != NULL)
235 return focus;
236
237 if (this->acceptFocus())
238 return this;
239 }
240
241 return NULL;
242}
243
244SkView* SkView::moveFocus(FocusDirection dir)
245{
246 SkView* focus = this->getFocusView();
247
248 if (focus == NULL)
249 { // start with the root
250 focus = this;
251 while (focus->fParent)
252 focus = focus->fParent;
253 }
254
255 SkView* child, *parent;
256
257 if (dir == kNext_FocusDirection)
258 {
259 parent = focus;
260 child = focus->fFirstChild;
261 if (child)
262 goto FIRST_CHILD;
263 else
264 goto NEXT_SIB;
265
266 do {
267 while (child != parent->fFirstChild)
268 {
269 FIRST_CHILD:
270 if ((focus = child->acceptFocus(dir)) != NULL)
271 return focus;
272 child = child->fNextSibling;
273 }
274 NEXT_SIB:
275 child = parent->fNextSibling;
276 parent = parent->fParent;
277 } while (parent != NULL);
278 }
279 else // prevfocus
280 {
281 parent = focus->fParent;
282 if (parent == NULL) // we're the root
283 return focus->acceptFocus(dir);
284 else
285 {
286 child = focus;
287 while (parent)
288 {
289 while (child != parent->fFirstChild)
290 {
291 child = child->fPrevSibling;
292 if ((focus = child->acceptFocus(dir)) != NULL)
293 return focus;
294 }
295 if (parent->acceptFocus())
296 return parent;
297
298 child = parent;
299 parent = parent->fParent;
300 }
301 }
302 }
303 return NULL;
304}
305
306void SkView::onFocusChange(bool gainFocusP)
307{
308 this->inval(NULL);
309}
310
311////////////////////////////////////////////////////////////////////////////
312
313SkView::Click::Click(SkView* target)
314{
Scroggod3aed392011-06-22 13:26:56 +0000315 SkASSERT(target);
316 fTargetID = target->getSinkID();
317 fType = NULL;
318 fWeOwnTheType = false;
319 fOwner = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320}
321
322SkView::Click::~Click()
323{
324 this->resetType();
325}
326
327void SkView::Click::resetType()
328{
329 if (fWeOwnTheType)
330 {
331 sk_free(fType);
332 fWeOwnTheType = false;
333 }
334 fType = NULL;
335}
336
337bool SkView::Click::isType(const char type[]) const
338{
339 const char* t = fType;
340
341 if (type == t)
342 return true;
343
344 if (type == NULL)
345 type = "";
346 if (t == NULL)
347 t = "";
348 return !strcmp(t, type);
349}
350
351void SkView::Click::setType(const char type[])
352{
353 this->resetType();
354 fType = (char*)type;
355}
356
357void SkView::Click::copyType(const char type[])
358{
359 if (fType != type)
360 {
361 this->resetType();
362 if (type)
363 {
364 size_t len = strlen(type) + 1;
365 fType = (char*)sk_malloc_throw(len);
366 memcpy(fType, type, len);
367 fWeOwnTheType = true;
368 }
369 }
370}
371
372SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y)
373{
reed@android.come72fee52009-11-16 14:52:01 +0000374 if (x < 0 || y < 0 || x >= fWidth || y >= fHeight) {
epoger@google.com17b78942011-08-26 14:40:38 +0000375 return NULL;
reed@android.come72fee52009-11-16 14:52:01 +0000376 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377
reed@android.come72fee52009-11-16 14:52:01 +0000378 if (this->onSendClickToChildren(x, y)) {
379 F2BIter iter(this);
380 SkView* child;
reed@google.comf03bb562011-11-11 21:42:12 +0000381
reed@android.come72fee52009-11-16 14:52:01 +0000382 while ((child = iter.next()) != NULL)
383 {
reed@google.comf03bb562011-11-11 21:42:12 +0000384 SkPoint p;
385 child->globalToLocal(x, y, &p);
386
387 Click* click = child->findClickHandler(p.fX, p.fY);
388
reed@android.come72fee52009-11-16 14:52:01 +0000389 if (click) {
390 return click;
391 }
392 }
393 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394 return this->onFindClickHandler(x, y);
395}
396
397void SkView::DoClickDown(Click* click, int x, int y)
398{
399 SkASSERT(click);
400
401 SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
402 if (target == NULL)
403 return;
404
405 click->fIOrig.set(x, y);
406 click->fICurr = click->fIPrev = click->fIOrig;
407
408 click->fOrig.iset(x, y);
409 target->globalToLocal(&click->fOrig);
410 click->fPrev = click->fCurr = click->fOrig;
411
412 click->fState = Click::kDown_State;
413 target->onClick(click);
414}
415
416void SkView::DoClickMoved(Click* click, int x, int y)
417{
418 SkASSERT(click);
419
420 SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
421 if (target == NULL)
422 return;
423
424 click->fIPrev = click->fICurr;
425 click->fICurr.set(x, y);
426
427 click->fPrev = click->fCurr;
428 click->fCurr.iset(x, y);
429 target->globalToLocal(&click->fCurr);
430
431 click->fState = Click::kMoved_State;
432 target->onClick(click);
433}
434
435void SkView::DoClickUp(Click* click, int x, int y)
436{
437 SkASSERT(click);
438
439 SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
440 if (target == NULL)
441 return;
442
443 click->fIPrev = click->fICurr;
444 click->fICurr.set(x, y);
445
446 click->fPrev = click->fCurr;
447 click->fCurr.iset(x, y);
448 target->globalToLocal(&click->fCurr);
449
450 click->fState = Click::kUp_State;
451 target->onClick(click);
452}
453
454//////////////////////////////////////////////////////////////////////
455
reed@android.come72fee52009-11-16 14:52:01 +0000456void SkView::invokeLayout() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457 SkView::Layout* layout = this->getLayout();
458
reed@android.come72fee52009-11-16 14:52:01 +0000459 if (layout) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460 layout->layoutChildren(this);
reed@android.come72fee52009-11-16 14:52:01 +0000461 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462}
463
reed@android.come72fee52009-11-16 14:52:01 +0000464void SkView::onDraw(SkCanvas* canvas) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465 Artist* artist = this->getArtist();
466
reed@android.come72fee52009-11-16 14:52:01 +0000467 if (artist) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468 artist->draw(this, canvas);
reed@android.come72fee52009-11-16 14:52:01 +0000469 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470}
471
reed@android.come72fee52009-11-16 14:52:01 +0000472void SkView::onSizeChange() {}
473
474bool SkView::onSendClickToChildren(SkScalar x, SkScalar y) {
475 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476}
477
reed@android.come72fee52009-11-16 14:52:01 +0000478SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479 return NULL;
480}
481
reed@android.come72fee52009-11-16 14:52:01 +0000482bool SkView::onClick(Click*) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 return false;
484}
485
reed@android.comf2b98d62010-12-20 18:26:13 +0000486bool SkView::handleInval(const SkRect*) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487 return false;
488}
489
490//////////////////////////////////////////////////////////////////////
491
492void SkView::getLocalBounds(SkRect* bounds) const
493{
494 if (bounds)
495 bounds->set(0, 0, fWidth, fHeight);
496}
497
498//////////////////////////////////////////////////////////////////////
499//////////////////////////////////////////////////////////////////////
500
501void SkView::detachFromParent_NoLayout()
502{
503 if (fParent == NULL)
504 return;
505
506 if (fContainsFocus)
507 (void)this->setFocusView(NULL);
508
509 this->inval(NULL);
510
511 SkView* next = NULL;
512
513 if (fNextSibling != this) // do we have any siblings
514 {
515 fNextSibling->fPrevSibling = fPrevSibling;
516 fPrevSibling->fNextSibling = fNextSibling;
517 next = fNextSibling;
518 }
519
520 if (fParent->fFirstChild == this)
521 fParent->fFirstChild = next;
522
523 fParent = fNextSibling = fPrevSibling = NULL;
524
525 this->unref();
526}
527
528void SkView::detachFromParent()
529{
530 SkView* parent = fParent;
531
532 if (parent)
533 {
534 this->detachFromParent_NoLayout();
535 parent->invokeLayout();
536 }
537}
538
539SkView* SkView::attachChildToBack(SkView* child)
540{
541 SkASSERT(child != this);
542
543 if (child == NULL || fFirstChild == child)
544 goto DONE;
545
546 child->ref();
547 child->detachFromParent_NoLayout();
548
549 if (fFirstChild == NULL)
550 {
551 child->fNextSibling = child;
552 child->fPrevSibling = child;
553 }
554 else
555 {
556 child->fNextSibling = fFirstChild;
557 child->fPrevSibling = fFirstChild->fPrevSibling;
558 fFirstChild->fPrevSibling->fNextSibling = child;
559 fFirstChild->fPrevSibling = child;
560 }
561
562 fFirstChild = child;
563 child->fParent = this;
564 child->inval(NULL);
565
566 this->invokeLayout();
567DONE:
568 return child;
569}
570
571SkView* SkView::attachChildToFront(SkView* child)
572{
573 SkASSERT(child != this);
574
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000575 if (child == NULL || (fFirstChild && fFirstChild->fPrevSibling == child))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 goto DONE;
577
578 child->ref();
579 child->detachFromParent_NoLayout();
580
581 if (fFirstChild == NULL)
582 {
583 fFirstChild = child;
584 child->fNextSibling = child;
585 child->fPrevSibling = child;
586 }
587 else
588 {
589 child->fNextSibling = fFirstChild;
590 child->fPrevSibling = fFirstChild->fPrevSibling;
591 fFirstChild->fPrevSibling->fNextSibling = child;
592 fFirstChild->fPrevSibling = child;
593 }
594
595 child->fParent = this;
596 child->inval(NULL);
597
598 this->invokeLayout();
599DONE:
600 return child;
601}
602
603void SkView::detachAllChildren()
604{
605 while (fFirstChild)
606 fFirstChild->detachFromParent_NoLayout();
607}
608
reed@google.comf03bb562011-11-11 21:42:12 +0000609void SkView::localToGlobal(SkMatrix* matrix) const
610{
611 if (matrix) {
612 matrix->reset();
613 const SkView* view = this;
614 while (view)
615 {
616 matrix->preConcat(view->getLocalMatrix());
617 matrix->preTranslate(-view->fLoc.fX, -view->fLoc.fY);
618 view = view->fParent;
619 }
620 }
621}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622void SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const
623{
624 SkASSERT(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 if (local)
626 {
reed@google.comf03bb562011-11-11 21:42:12 +0000627 SkMatrix m;
628 this->localToGlobal(&m);
629 SkPoint p;
630 m.invert(&m);
631 m.mapXY(x, y, &p);
632 local->set(p.fX, p.fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 }
634}
635
636//////////////////////////////////////////////////////////////////
637
638/* Even if the subclass overrides onInflate, they should always be
639 sure to call the inherited method, so that we get called.
640*/
641void SkView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
642{
643 SkScalar x, y;
644
645 x = this->locX();
646 y = this->locY();
647 (void)dom.findScalar(node, "x", &x);
648 (void)dom.findScalar(node, "y", &y);
649 this->setLoc(x, y);
650
651 x = this->width();
652 y = this->height();
653 (void)dom.findScalar(node, "width", &x);
654 (void)dom.findScalar(node, "height", &y);
655 this->setSize(x, y);
656
657 // inflate the flags
658
659 static const char* gFlagNames[] = {
660 "visible", "enabled", "focusable", "flexH", "flexV"
661 };
662 SkASSERT(SK_ARRAY_COUNT(gFlagNames) == kFlagShiftCount);
663
664 bool b;
665 uint32_t flags = this->getFlags();
666 for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++)
667 if (dom.findBool(node, gFlagNames[i], &b))
668 flags = SkSetClearShift(flags, b, i);
669 this->setFlags(flags);
670}
671
672void SkView::inflate(const SkDOM& dom, const SkDOM::Node* node)
673{
674 this->onInflate(dom, node);
675}
676
677void SkView::onPostInflate(const SkTDict<SkView*>&)
678{
679 // override in subclass as needed
680}
681
682void SkView::postInflate(const SkTDict<SkView*>& dict)
683{
684 this->onPostInflate(dict);
685
686 B2FIter iter(this);
687 SkView* child;
688 while ((child = iter.next()) != NULL)
689 child->postInflate(dict);
690}
691
692//////////////////////////////////////////////////////////////////
693
694SkView* SkView::sendEventToParents(const SkEvent& evt)
695{
696 SkView* parent = fParent;
reed@android.com34245c72009-11-03 04:00:48 +0000697
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698 while (parent)
699 {
700 if (parent->doEvent(evt))
701 return parent;
702 parent = parent->fParent;
703 }
704 return NULL;
705}
706
reed@android.com34245c72009-11-03 04:00:48 +0000707SkView* SkView::sendQueryToParents(SkEvent* evt) {
708 SkView* parent = fParent;
709
710 while (parent) {
711 if (parent->doQuery(evt)) {
712 return parent;
713 }
714 parent = parent->fParent;
715 }
716 return NULL;
717}
718
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719//////////////////////////////////////////////////////////////////
720//////////////////////////////////////////////////////////////////
721
722SkView::F2BIter::F2BIter(const SkView* parent)
723{
724 fFirstChild = parent ? parent->fFirstChild : NULL;
725 fChild = fFirstChild ? fFirstChild->fPrevSibling : NULL;
726}
727
728SkView* SkView::F2BIter::next()
729{
730 SkView* curr = fChild;
731
732 if (fChild)
733 {
734 if (fChild == fFirstChild)
735 fChild = NULL;
736 else
737 fChild = fChild->fPrevSibling;
738 }
739 return curr;
740}
741
742SkView::B2FIter::B2FIter(const SkView* parent)
743{
744 fFirstChild = parent ? parent->fFirstChild : NULL;
745 fChild = fFirstChild;
746}
747
748SkView* SkView::B2FIter::next()
749{
750 SkView* curr = fChild;
751
752 if (fChild)
753 {
754 SkView* next = fChild->fNextSibling;
755 if (next == fFirstChild)
756 next = NULL;
757 fChild = next;
758 }
759 return curr;
760}
761
762//////////////////////////////////////////////////////////////////
763//////////////////////////////////////////////////////////////////
764
765#ifdef SK_DEBUG
766
767static inline void show_if_nonzero(const char name[], SkScalar value)
768{
769 if (value)
770 SkDebugf("%s=\"%g\"", name, value/65536.);
771}
772
773static void tab(int level)
774{
775 for (int i = 0; i < level; i++)
776 SkDebugf(" ");
777}
778
779static void dumpview(const SkView* view, int level, bool recurse)
780{
781 tab(level);
782
783 SkDebugf("<view");
784 show_if_nonzero(" x", view->locX());
785 show_if_nonzero(" y", view->locY());
786 show_if_nonzero(" width", view->width());
787 show_if_nonzero(" height", view->height());
788
789 if (recurse)
790 {
791 SkView::B2FIter iter(view);
792 SkView* child;
793 bool noChildren = true;
794
795 while ((child = iter.next()) != NULL)
796 {
797 if (noChildren)
798 SkDebugf(">\n");
799 noChildren = false;
800 dumpview(child, level + 1, true);
801 }
802
803 if (!noChildren)
804 {
805 tab(level);
806 SkDebugf("</view>\n");
807 }
808 else
809 goto ONELINER;
810 }
811 else
812 {
813 ONELINER:
814 SkDebugf(" />\n");
815 }
816}
817
818void SkView::dump(bool recurse) const
819{
820 dumpview(this, 0, recurse);
821}
822
823#endif