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