blob: 4b99987db1451c6717fe888a12f120379d1238e9 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001#include "SkView.h"
2#include "SkCanvas.h"
3
4////////////////////////////////////////////////////////////////////////
5
6SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags))
7{
8 fWidth = fHeight = 0;
9 fLoc.set(0, 0);
10 fParent = fFirstChild = fNextSibling = fPrevSibling = NULL;
11
12 fContainsFocus = 0;
13}
14
15SkView::~SkView()
16{
17 this->detachAllChildren();
18}
19
20void SkView::setFlags(uint32_t flags)
21{
22 SkASSERT((flags & ~kAllFlagMasks) == 0);
23
24 uint32_t diff = fFlags ^ flags;
25
26 if (diff & kVisible_Mask)
27 this->inval(NULL);
28
29 fFlags = SkToU8(flags);
30
31 if (diff & kVisible_Mask)
32 {
33 this->inval(NULL);
34 }
35}
36
37void SkView::setVisibleP(bool pred)
38{
39 this->setFlags(SkSetClearShift(fFlags, pred, kVisible_Shift));
40}
41
42void SkView::setEnabledP(bool pred)
43{
44 this->setFlags(SkSetClearShift(fFlags, pred, kEnabled_Shift));
45}
46
47void SkView::setFocusableP(bool pred)
48{
49 this->setFlags(SkSetClearShift(fFlags, pred, kFocusable_Shift));
50}
51
reed@android.comf2b98d62010-12-20 18:26:13 +000052void SkView::setClipToBounds(bool pred) {
53 this->setFlags(SkSetClearShift(fFlags, !pred, kNoClip_Shift));
54}
55
reed@android.com8a1c16f2008-12-17 15:59:43 +000056void SkView::setSize(SkScalar width, SkScalar height)
57{
58 width = SkMaxScalar(0, width);
59 height = SkMaxScalar(0, height);
60
61 if (fWidth != width || fHeight != height)
62 {
63 this->inval(NULL);
64 fWidth = width;
65 fHeight = height;
66 this->inval(NULL);
67 this->onSizeChange();
68 this->invokeLayout();
69 }
70}
71
72void SkView::setLoc(SkScalar x, SkScalar y)
73{
74 if (fLoc.fX != x || fLoc.fY != y)
75 {
76 this->inval(NULL);
77 fLoc.set(x, y);
78 this->inval(NULL);
79 }
80}
81
82void SkView::offset(SkScalar dx, SkScalar dy)
83{
84 if (dx || dy)
85 this->setLoc(fLoc.fX + dx, fLoc.fY + dy);
86}
87
88void SkView::draw(SkCanvas* canvas)
89{
90 if (fWidth && fHeight && this->isVisible())
91 {
92 SkRect r;
93 r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight);
reed@android.comf2b98d62010-12-20 18:26:13 +000094 if (this->isClipToBounds() &&
95 canvas->quickReject(r, SkCanvas::kBW_EdgeType)) {
96 return;
97 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000098
99 SkAutoCanvasRestore as(canvas, true);
100
reed@android.comf2b98d62010-12-20 18:26:13 +0000101 if (this->isClipToBounds()) {
102 canvas->clipRect(r);
103 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 canvas->translate(fLoc.fX, fLoc.fY);
105
reed@android.com6c5f6f22009-08-14 16:08:38 +0000106 if (fParent) {
107 fParent->beforeChild(this, canvas);
108 }
reed@android.com562ea922010-02-08 21:45:03 +0000109
110 int sc = canvas->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 this->onDraw(canvas);
reed@android.com562ea922010-02-08 21:45:03 +0000112 canvas->restoreToCount(sc);
113
reed@android.com6c5f6f22009-08-14 16:08:38 +0000114 if (fParent) {
115 fParent->afterChild(this, canvas);
116 }
117
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 B2FIter iter(this);
119 SkView* child;
120
121 SkCanvas* childCanvas = this->beforeChildren(canvas);
122
123 while ((child = iter.next()) != NULL)
124 child->draw(childCanvas);
125
126 this->afterChildren(canvas);
127 }
128}
129
reed@android.comf2b98d62010-12-20 18:26:13 +0000130void SkView::inval(SkRect* rect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 SkView* view = this;
reed@android.comf2b98d62010-12-20 18:26:13 +0000132 SkRect storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133
reed@android.comf2b98d62010-12-20 18:26:13 +0000134 for (;;) {
135 if (!view->isVisible()) {
136 return;
137 }
138 if (view->isClipToBounds()) {
139 SkRect bounds;
140 view->getLocalBounds(&bounds);
141 if (rect && !bounds.intersect(*rect)) {
142 return;
143 }
144 storage = bounds;
145 rect = &storage;
146 }
147 if (view->handleInval(rect)) {
148 return;
149 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 SkView* parent = view->fParent;
reed@android.comf2b98d62010-12-20 18:26:13 +0000152 if (parent == NULL) {
153 return;
154 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155
reed@android.comf2b98d62010-12-20 18:26:13 +0000156 if (rect) {
157 rect->offset(view->fLoc.fX, view->fLoc.fY);
158 }
159 view = parent;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 }
161}
162
163////////////////////////////////////////////////////////////////////////////
164
165bool SkView::setFocusView(SkView* fv)
166{
167 SkView* view = this;
168
169 do {
170 if (view->onSetFocusView(fv))
171 return true;
172 } while ((view = view->fParent) != NULL);
173 return false;
174}
175
176SkView* SkView::getFocusView() const
177{
178 SkView* focus = NULL;
179 const SkView* view = this;
180 do {
181 if (view->onGetFocusView(&focus))
182 break;
183 } while ((view = view->fParent) != NULL);
184 return focus;
185}
186
187bool SkView::hasFocus() const
188{
189 return this == this->getFocusView();
190}
191
192bool SkView::acceptFocus()
193{
194 return this->isFocusable() && this->setFocusView(this);
195}
196
197/*
198 Try to give focus to this view, or its children
199*/
200SkView* SkView::acceptFocus(FocusDirection dir)
201{
202 if (dir == kNext_FocusDirection)
203 {
204 if (this->acceptFocus())
205 return this;
206
207 B2FIter iter(this);
208 SkView* child, *focus;
209 while ((child = iter.next()) != NULL)
210 if ((focus = child->acceptFocus(dir)) != NULL)
211 return focus;
212 }
213 else // prev
214 {
215 F2BIter iter(this);
216 SkView* child, *focus;
217 while ((child = iter.next()) != NULL)
218 if ((focus = child->acceptFocus(dir)) != NULL)
219 return focus;
220
221 if (this->acceptFocus())
222 return this;
223 }
224
225 return NULL;
226}
227
228SkView* SkView::moveFocus(FocusDirection dir)
229{
230 SkView* focus = this->getFocusView();
231
232 if (focus == NULL)
233 { // start with the root
234 focus = this;
235 while (focus->fParent)
236 focus = focus->fParent;
237 }
238
239 SkView* child, *parent;
240
241 if (dir == kNext_FocusDirection)
242 {
243 parent = focus;
244 child = focus->fFirstChild;
245 if (child)
246 goto FIRST_CHILD;
247 else
248 goto NEXT_SIB;
249
250 do {
251 while (child != parent->fFirstChild)
252 {
253 FIRST_CHILD:
254 if ((focus = child->acceptFocus(dir)) != NULL)
255 return focus;
256 child = child->fNextSibling;
257 }
258 NEXT_SIB:
259 child = parent->fNextSibling;
260 parent = parent->fParent;
261 } while (parent != NULL);
262 }
263 else // prevfocus
264 {
265 parent = focus->fParent;
266 if (parent == NULL) // we're the root
267 return focus->acceptFocus(dir);
268 else
269 {
270 child = focus;
271 while (parent)
272 {
273 while (child != parent->fFirstChild)
274 {
275 child = child->fPrevSibling;
276 if ((focus = child->acceptFocus(dir)) != NULL)
277 return focus;
278 }
279 if (parent->acceptFocus())
280 return parent;
281
282 child = parent;
283 parent = parent->fParent;
284 }
285 }
286 }
287 return NULL;
288}
289
290void SkView::onFocusChange(bool gainFocusP)
291{
292 this->inval(NULL);
293}
294
295////////////////////////////////////////////////////////////////////////////
296
297SkView::Click::Click(SkView* target)
298{
Scroggod3aed392011-06-22 13:26:56 +0000299 SkASSERT(target);
300 fTargetID = target->getSinkID();
301 fType = NULL;
302 fWeOwnTheType = false;
303 fOwner = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304}
305
306SkView::Click::~Click()
307{
308 this->resetType();
309}
310
311void SkView::Click::resetType()
312{
313 if (fWeOwnTheType)
314 {
315 sk_free(fType);
316 fWeOwnTheType = false;
317 }
318 fType = NULL;
319}
320
321bool SkView::Click::isType(const char type[]) const
322{
323 const char* t = fType;
324
325 if (type == t)
326 return true;
327
328 if (type == NULL)
329 type = "";
330 if (t == NULL)
331 t = "";
332 return !strcmp(t, type);
333}
334
335void SkView::Click::setType(const char type[])
336{
337 this->resetType();
338 fType = (char*)type;
339}
340
341void SkView::Click::copyType(const char type[])
342{
343 if (fType != type)
344 {
345 this->resetType();
346 if (type)
347 {
348 size_t len = strlen(type) + 1;
349 fType = (char*)sk_malloc_throw(len);
350 memcpy(fType, type, len);
351 fWeOwnTheType = true;
352 }
353 }
354}
355
356SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y)
357{
reed@android.come72fee52009-11-16 14:52:01 +0000358 if (x < 0 || y < 0 || x >= fWidth || y >= fHeight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359 return false;
reed@android.come72fee52009-11-16 14:52:01 +0000360 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361
reed@android.come72fee52009-11-16 14:52:01 +0000362 if (this->onSendClickToChildren(x, y)) {
363 F2BIter iter(this);
364 SkView* child;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365
reed@android.come72fee52009-11-16 14:52:01 +0000366 while ((child = iter.next()) != NULL)
367 {
368 Click* click = child->findClickHandler(x - child->fLoc.fX,
369 y - child->fLoc.fY);
370 if (click) {
371 return click;
372 }
373 }
374 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375 return this->onFindClickHandler(x, y);
376}
377
378void SkView::DoClickDown(Click* click, int x, int y)
379{
380 SkASSERT(click);
381
382 SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
383 if (target == NULL)
384 return;
385
386 click->fIOrig.set(x, y);
387 click->fICurr = click->fIPrev = click->fIOrig;
388
389 click->fOrig.iset(x, y);
390 target->globalToLocal(&click->fOrig);
391 click->fPrev = click->fCurr = click->fOrig;
392
393 click->fState = Click::kDown_State;
394 target->onClick(click);
395}
396
397void SkView::DoClickMoved(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->fIPrev = click->fICurr;
406 click->fICurr.set(x, y);
407
408 click->fPrev = click->fCurr;
409 click->fCurr.iset(x, y);
410 target->globalToLocal(&click->fCurr);
411
412 click->fState = Click::kMoved_State;
413 target->onClick(click);
414}
415
416void SkView::DoClickUp(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::kUp_State;
432 target->onClick(click);
433}
434
435//////////////////////////////////////////////////////////////////////
436
reed@android.come72fee52009-11-16 14:52:01 +0000437void SkView::invokeLayout() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 SkView::Layout* layout = this->getLayout();
439
reed@android.come72fee52009-11-16 14:52:01 +0000440 if (layout) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441 layout->layoutChildren(this);
reed@android.come72fee52009-11-16 14:52:01 +0000442 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443}
444
reed@android.come72fee52009-11-16 14:52:01 +0000445void SkView::onDraw(SkCanvas* canvas) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 Artist* artist = this->getArtist();
447
reed@android.come72fee52009-11-16 14:52:01 +0000448 if (artist) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449 artist->draw(this, canvas);
reed@android.come72fee52009-11-16 14:52:01 +0000450 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451}
452
reed@android.come72fee52009-11-16 14:52:01 +0000453void SkView::onSizeChange() {}
454
455bool SkView::onSendClickToChildren(SkScalar x, SkScalar y) {
456 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457}
458
reed@android.come72fee52009-11-16 14:52:01 +0000459SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460 return NULL;
461}
462
reed@android.come72fee52009-11-16 14:52:01 +0000463bool SkView::onClick(Click*) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464 return false;
465}
466
reed@android.comf2b98d62010-12-20 18:26:13 +0000467bool SkView::handleInval(const SkRect*) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468 return false;
469}
470
471//////////////////////////////////////////////////////////////////////
472
473void SkView::getLocalBounds(SkRect* bounds) const
474{
475 if (bounds)
476 bounds->set(0, 0, fWidth, fHeight);
477}
478
479//////////////////////////////////////////////////////////////////////
480//////////////////////////////////////////////////////////////////////
481
482void SkView::detachFromParent_NoLayout()
483{
484 if (fParent == NULL)
485 return;
486
487 if (fContainsFocus)
488 (void)this->setFocusView(NULL);
489
490 this->inval(NULL);
491
492 SkView* next = NULL;
493
494 if (fNextSibling != this) // do we have any siblings
495 {
496 fNextSibling->fPrevSibling = fPrevSibling;
497 fPrevSibling->fNextSibling = fNextSibling;
498 next = fNextSibling;
499 }
500
501 if (fParent->fFirstChild == this)
502 fParent->fFirstChild = next;
503
504 fParent = fNextSibling = fPrevSibling = NULL;
505
506 this->unref();
507}
508
509void SkView::detachFromParent()
510{
511 SkView* parent = fParent;
512
513 if (parent)
514 {
515 this->detachFromParent_NoLayout();
516 parent->invokeLayout();
517 }
518}
519
520SkView* SkView::attachChildToBack(SkView* child)
521{
522 SkASSERT(child != this);
523
524 if (child == NULL || fFirstChild == child)
525 goto DONE;
526
527 child->ref();
528 child->detachFromParent_NoLayout();
529
530 if (fFirstChild == NULL)
531 {
532 child->fNextSibling = child;
533 child->fPrevSibling = child;
534 }
535 else
536 {
537 child->fNextSibling = fFirstChild;
538 child->fPrevSibling = fFirstChild->fPrevSibling;
539 fFirstChild->fPrevSibling->fNextSibling = child;
540 fFirstChild->fPrevSibling = child;
541 }
542
543 fFirstChild = child;
544 child->fParent = this;
545 child->inval(NULL);
546
547 this->invokeLayout();
548DONE:
549 return child;
550}
551
552SkView* SkView::attachChildToFront(SkView* child)
553{
554 SkASSERT(child != this);
555
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000556 if (child == NULL || (fFirstChild && fFirstChild->fPrevSibling == child))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 goto DONE;
558
559 child->ref();
560 child->detachFromParent_NoLayout();
561
562 if (fFirstChild == NULL)
563 {
564 fFirstChild = child;
565 child->fNextSibling = child;
566 child->fPrevSibling = child;
567 }
568 else
569 {
570 child->fNextSibling = fFirstChild;
571 child->fPrevSibling = fFirstChild->fPrevSibling;
572 fFirstChild->fPrevSibling->fNextSibling = child;
573 fFirstChild->fPrevSibling = child;
574 }
575
576 child->fParent = this;
577 child->inval(NULL);
578
579 this->invokeLayout();
580DONE:
581 return child;
582}
583
584void SkView::detachAllChildren()
585{
586 while (fFirstChild)
587 fFirstChild->detachFromParent_NoLayout();
588}
589
590void SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const
591{
592 SkASSERT(this);
593
594 if (local)
595 {
596 const SkView* view = this;
597 while (view)
598 {
599 x -= view->fLoc.fX;
600 y -= view->fLoc.fY;
601 view = view->fParent;
602 }
603 local->set(x, y);
604 }
605}
606
607//////////////////////////////////////////////////////////////////
608
609/* Even if the subclass overrides onInflate, they should always be
610 sure to call the inherited method, so that we get called.
611*/
612void SkView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
613{
614 SkScalar x, y;
615
616 x = this->locX();
617 y = this->locY();
618 (void)dom.findScalar(node, "x", &x);
619 (void)dom.findScalar(node, "y", &y);
620 this->setLoc(x, y);
621
622 x = this->width();
623 y = this->height();
624 (void)dom.findScalar(node, "width", &x);
625 (void)dom.findScalar(node, "height", &y);
626 this->setSize(x, y);
627
628 // inflate the flags
629
630 static const char* gFlagNames[] = {
631 "visible", "enabled", "focusable", "flexH", "flexV"
632 };
633 SkASSERT(SK_ARRAY_COUNT(gFlagNames) == kFlagShiftCount);
634
635 bool b;
636 uint32_t flags = this->getFlags();
637 for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++)
638 if (dom.findBool(node, gFlagNames[i], &b))
639 flags = SkSetClearShift(flags, b, i);
640 this->setFlags(flags);
641}
642
643void SkView::inflate(const SkDOM& dom, const SkDOM::Node* node)
644{
645 this->onInflate(dom, node);
646}
647
648void SkView::onPostInflate(const SkTDict<SkView*>&)
649{
650 // override in subclass as needed
651}
652
653void SkView::postInflate(const SkTDict<SkView*>& dict)
654{
655 this->onPostInflate(dict);
656
657 B2FIter iter(this);
658 SkView* child;
659 while ((child = iter.next()) != NULL)
660 child->postInflate(dict);
661}
662
663//////////////////////////////////////////////////////////////////
664
665SkView* SkView::sendEventToParents(const SkEvent& evt)
666{
667 SkView* parent = fParent;
reed@android.com34245c72009-11-03 04:00:48 +0000668
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669 while (parent)
670 {
671 if (parent->doEvent(evt))
672 return parent;
673 parent = parent->fParent;
674 }
675 return NULL;
676}
677
reed@android.com34245c72009-11-03 04:00:48 +0000678SkView* SkView::sendQueryToParents(SkEvent* evt) {
679 SkView* parent = fParent;
680
681 while (parent) {
682 if (parent->doQuery(evt)) {
683 return parent;
684 }
685 parent = parent->fParent;
686 }
687 return NULL;
688}
689
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690//////////////////////////////////////////////////////////////////
691//////////////////////////////////////////////////////////////////
692
693SkView::F2BIter::F2BIter(const SkView* parent)
694{
695 fFirstChild = parent ? parent->fFirstChild : NULL;
696 fChild = fFirstChild ? fFirstChild->fPrevSibling : NULL;
697}
698
699SkView* SkView::F2BIter::next()
700{
701 SkView* curr = fChild;
702
703 if (fChild)
704 {
705 if (fChild == fFirstChild)
706 fChild = NULL;
707 else
708 fChild = fChild->fPrevSibling;
709 }
710 return curr;
711}
712
713SkView::B2FIter::B2FIter(const SkView* parent)
714{
715 fFirstChild = parent ? parent->fFirstChild : NULL;
716 fChild = fFirstChild;
717}
718
719SkView* SkView::B2FIter::next()
720{
721 SkView* curr = fChild;
722
723 if (fChild)
724 {
725 SkView* next = fChild->fNextSibling;
726 if (next == fFirstChild)
727 next = NULL;
728 fChild = next;
729 }
730 return curr;
731}
732
733//////////////////////////////////////////////////////////////////
734//////////////////////////////////////////////////////////////////
735
736#ifdef SK_DEBUG
737
738static inline void show_if_nonzero(const char name[], SkScalar value)
739{
740 if (value)
741 SkDebugf("%s=\"%g\"", name, value/65536.);
742}
743
744static void tab(int level)
745{
746 for (int i = 0; i < level; i++)
747 SkDebugf(" ");
748}
749
750static void dumpview(const SkView* view, int level, bool recurse)
751{
752 tab(level);
753
754 SkDebugf("<view");
755 show_if_nonzero(" x", view->locX());
756 show_if_nonzero(" y", view->locY());
757 show_if_nonzero(" width", view->width());
758 show_if_nonzero(" height", view->height());
759
760 if (recurse)
761 {
762 SkView::B2FIter iter(view);
763 SkView* child;
764 bool noChildren = true;
765
766 while ((child = iter.next()) != NULL)
767 {
768 if (noChildren)
769 SkDebugf(">\n");
770 noChildren = false;
771 dumpview(child, level + 1, true);
772 }
773
774 if (!noChildren)
775 {
776 tab(level);
777 SkDebugf("</view>\n");
778 }
779 else
780 goto ONELINER;
781 }
782 else
783 {
784 ONELINER:
785 SkDebugf(" />\n");
786 }
787}
788
789void SkView::dump(bool recurse) const
790{
791 dumpview(this, 0, recurse);
792}
793
794#endif