blob: 69dc6fc7f2e22872a472dfefb710f244da9b5009 [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;
18
19 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);
85 this->inval(NULL);
86 }
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
95void SkView::draw(SkCanvas* canvas)
96{
97 if (fWidth && fHeight && this->isVisible())
98 {
99 SkRect r;
100 r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight);
reed@android.comf2b98d62010-12-20 18:26:13 +0000101 if (this->isClipToBounds() &&
102 canvas->quickReject(r, SkCanvas::kBW_EdgeType)) {
103 return;
104 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105
106 SkAutoCanvasRestore as(canvas, true);
107
reed@android.comf2b98d62010-12-20 18:26:13 +0000108 if (this->isClipToBounds()) {
109 canvas->clipRect(r);
110 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 canvas->translate(fLoc.fX, fLoc.fY);
112
reed@android.com6c5f6f22009-08-14 16:08:38 +0000113 if (fParent) {
114 fParent->beforeChild(this, canvas);
115 }
reed@android.com562ea922010-02-08 21:45:03 +0000116
117 int sc = canvas->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 this->onDraw(canvas);
reed@android.com562ea922010-02-08 21:45:03 +0000119 canvas->restoreToCount(sc);
120
reed@android.com6c5f6f22009-08-14 16:08:38 +0000121 if (fParent) {
122 fParent->afterChild(this, canvas);
123 }
124
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 B2FIter iter(this);
126 SkView* child;
127
128 SkCanvas* childCanvas = this->beforeChildren(canvas);
129
130 while ((child = iter.next()) != NULL)
131 child->draw(childCanvas);
132
133 this->afterChildren(canvas);
134 }
135}
136
reed@android.comf2b98d62010-12-20 18:26:13 +0000137void SkView::inval(SkRect* rect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138 SkView* view = this;
reed@android.comf2b98d62010-12-20 18:26:13 +0000139 SkRect storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140
reed@android.comf2b98d62010-12-20 18:26:13 +0000141 for (;;) {
142 if (!view->isVisible()) {
143 return;
144 }
145 if (view->isClipToBounds()) {
146 SkRect bounds;
147 view->getLocalBounds(&bounds);
148 if (rect && !bounds.intersect(*rect)) {
149 return;
150 }
151 storage = bounds;
152 rect = &storage;
153 }
154 if (view->handleInval(rect)) {
155 return;
156 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158 SkView* parent = view->fParent;
reed@android.comf2b98d62010-12-20 18:26:13 +0000159 if (parent == NULL) {
160 return;
161 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162
reed@android.comf2b98d62010-12-20 18:26:13 +0000163 if (rect) {
164 rect->offset(view->fLoc.fX, view->fLoc.fY);
165 }
166 view = parent;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 }
168}
169
170////////////////////////////////////////////////////////////////////////////
171
172bool SkView::setFocusView(SkView* fv)
173{
174 SkView* view = this;
175
176 do {
177 if (view->onSetFocusView(fv))
178 return true;
179 } while ((view = view->fParent) != NULL);
180 return false;
181}
182
183SkView* SkView::getFocusView() const
184{
185 SkView* focus = NULL;
186 const SkView* view = this;
187 do {
188 if (view->onGetFocusView(&focus))
189 break;
190 } while ((view = view->fParent) != NULL);
191 return focus;
192}
193
194bool SkView::hasFocus() const
195{
196 return this == this->getFocusView();
197}
198
199bool SkView::acceptFocus()
200{
201 return this->isFocusable() && this->setFocusView(this);
202}
203
204/*
205 Try to give focus to this view, or its children
206*/
207SkView* SkView::acceptFocus(FocusDirection dir)
208{
209 if (dir == kNext_FocusDirection)
210 {
211 if (this->acceptFocus())
212 return this;
213
214 B2FIter iter(this);
215 SkView* child, *focus;
216 while ((child = iter.next()) != NULL)
217 if ((focus = child->acceptFocus(dir)) != NULL)
218 return focus;
219 }
220 else // prev
221 {
222 F2BIter iter(this);
223 SkView* child, *focus;
224 while ((child = iter.next()) != NULL)
225 if ((focus = child->acceptFocus(dir)) != NULL)
226 return focus;
227
228 if (this->acceptFocus())
229 return this;
230 }
231
232 return NULL;
233}
234
235SkView* SkView::moveFocus(FocusDirection dir)
236{
237 SkView* focus = this->getFocusView();
238
239 if (focus == NULL)
240 { // start with the root
241 focus = this;
242 while (focus->fParent)
243 focus = focus->fParent;
244 }
245
246 SkView* child, *parent;
247
248 if (dir == kNext_FocusDirection)
249 {
250 parent = focus;
251 child = focus->fFirstChild;
252 if (child)
253 goto FIRST_CHILD;
254 else
255 goto NEXT_SIB;
256
257 do {
258 while (child != parent->fFirstChild)
259 {
260 FIRST_CHILD:
261 if ((focus = child->acceptFocus(dir)) != NULL)
262 return focus;
263 child = child->fNextSibling;
264 }
265 NEXT_SIB:
266 child = parent->fNextSibling;
267 parent = parent->fParent;
268 } while (parent != NULL);
269 }
270 else // prevfocus
271 {
272 parent = focus->fParent;
273 if (parent == NULL) // we're the root
274 return focus->acceptFocus(dir);
275 else
276 {
277 child = focus;
278 while (parent)
279 {
280 while (child != parent->fFirstChild)
281 {
282 child = child->fPrevSibling;
283 if ((focus = child->acceptFocus(dir)) != NULL)
284 return focus;
285 }
286 if (parent->acceptFocus())
287 return parent;
288
289 child = parent;
290 parent = parent->fParent;
291 }
292 }
293 }
294 return NULL;
295}
296
297void SkView::onFocusChange(bool gainFocusP)
298{
299 this->inval(NULL);
300}
301
302////////////////////////////////////////////////////////////////////////////
303
304SkView::Click::Click(SkView* target)
305{
Scroggod3aed392011-06-22 13:26:56 +0000306 SkASSERT(target);
307 fTargetID = target->getSinkID();
308 fType = NULL;
309 fWeOwnTheType = false;
310 fOwner = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311}
312
313SkView::Click::~Click()
314{
315 this->resetType();
316}
317
318void SkView::Click::resetType()
319{
320 if (fWeOwnTheType)
321 {
322 sk_free(fType);
323 fWeOwnTheType = false;
324 }
325 fType = NULL;
326}
327
328bool SkView::Click::isType(const char type[]) const
329{
330 const char* t = fType;
331
332 if (type == t)
333 return true;
334
335 if (type == NULL)
336 type = "";
337 if (t == NULL)
338 t = "";
339 return !strcmp(t, type);
340}
341
342void SkView::Click::setType(const char type[])
343{
344 this->resetType();
345 fType = (char*)type;
346}
347
348void SkView::Click::copyType(const char type[])
349{
350 if (fType != type)
351 {
352 this->resetType();
353 if (type)
354 {
355 size_t len = strlen(type) + 1;
356 fType = (char*)sk_malloc_throw(len);
357 memcpy(fType, type, len);
358 fWeOwnTheType = true;
359 }
360 }
361}
362
363SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y)
364{
reed@android.come72fee52009-11-16 14:52:01 +0000365 if (x < 0 || y < 0 || x >= fWidth || y >= fHeight) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 return false;
reed@android.come72fee52009-11-16 14:52:01 +0000367 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368
reed@android.come72fee52009-11-16 14:52:01 +0000369 if (this->onSendClickToChildren(x, y)) {
370 F2BIter iter(this);
371 SkView* child;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372
reed@android.come72fee52009-11-16 14:52:01 +0000373 while ((child = iter.next()) != NULL)
374 {
375 Click* click = child->findClickHandler(x - child->fLoc.fX,
376 y - child->fLoc.fY);
377 if (click) {
378 return click;
379 }
380 }
381 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382 return this->onFindClickHandler(x, y);
383}
384
385void SkView::DoClickDown(Click* click, int x, int y)
386{
387 SkASSERT(click);
388
389 SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
390 if (target == NULL)
391 return;
392
393 click->fIOrig.set(x, y);
394 click->fICurr = click->fIPrev = click->fIOrig;
395
396 click->fOrig.iset(x, y);
397 target->globalToLocal(&click->fOrig);
398 click->fPrev = click->fCurr = click->fOrig;
399
400 click->fState = Click::kDown_State;
401 target->onClick(click);
402}
403
404void SkView::DoClickMoved(Click* click, int x, int y)
405{
406 SkASSERT(click);
407
408 SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
409 if (target == NULL)
410 return;
411
412 click->fIPrev = click->fICurr;
413 click->fICurr.set(x, y);
414
415 click->fPrev = click->fCurr;
416 click->fCurr.iset(x, y);
417 target->globalToLocal(&click->fCurr);
418
419 click->fState = Click::kMoved_State;
420 target->onClick(click);
421}
422
423void SkView::DoClickUp(Click* click, int x, int y)
424{
425 SkASSERT(click);
426
427 SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
428 if (target == NULL)
429 return;
430
431 click->fIPrev = click->fICurr;
432 click->fICurr.set(x, y);
433
434 click->fPrev = click->fCurr;
435 click->fCurr.iset(x, y);
436 target->globalToLocal(&click->fCurr);
437
438 click->fState = Click::kUp_State;
439 target->onClick(click);
440}
441
442//////////////////////////////////////////////////////////////////////
443
reed@android.come72fee52009-11-16 14:52:01 +0000444void SkView::invokeLayout() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 SkView::Layout* layout = this->getLayout();
446
reed@android.come72fee52009-11-16 14:52:01 +0000447 if (layout) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448 layout->layoutChildren(this);
reed@android.come72fee52009-11-16 14:52:01 +0000449 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450}
451
reed@android.come72fee52009-11-16 14:52:01 +0000452void SkView::onDraw(SkCanvas* canvas) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453 Artist* artist = this->getArtist();
454
reed@android.come72fee52009-11-16 14:52:01 +0000455 if (artist) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 artist->draw(this, canvas);
reed@android.come72fee52009-11-16 14:52:01 +0000457 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458}
459
reed@android.come72fee52009-11-16 14:52:01 +0000460void SkView::onSizeChange() {}
461
462bool SkView::onSendClickToChildren(SkScalar x, SkScalar y) {
463 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464}
465
reed@android.come72fee52009-11-16 14:52:01 +0000466SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 return NULL;
468}
469
reed@android.come72fee52009-11-16 14:52:01 +0000470bool SkView::onClick(Click*) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471 return false;
472}
473
reed@android.comf2b98d62010-12-20 18:26:13 +0000474bool SkView::handleInval(const SkRect*) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475 return false;
476}
477
478//////////////////////////////////////////////////////////////////////
479
480void SkView::getLocalBounds(SkRect* bounds) const
481{
482 if (bounds)
483 bounds->set(0, 0, fWidth, fHeight);
484}
485
486//////////////////////////////////////////////////////////////////////
487//////////////////////////////////////////////////////////////////////
488
489void SkView::detachFromParent_NoLayout()
490{
491 if (fParent == NULL)
492 return;
493
494 if (fContainsFocus)
495 (void)this->setFocusView(NULL);
496
497 this->inval(NULL);
498
499 SkView* next = NULL;
500
501 if (fNextSibling != this) // do we have any siblings
502 {
503 fNextSibling->fPrevSibling = fPrevSibling;
504 fPrevSibling->fNextSibling = fNextSibling;
505 next = fNextSibling;
506 }
507
508 if (fParent->fFirstChild == this)
509 fParent->fFirstChild = next;
510
511 fParent = fNextSibling = fPrevSibling = NULL;
512
513 this->unref();
514}
515
516void SkView::detachFromParent()
517{
518 SkView* parent = fParent;
519
520 if (parent)
521 {
522 this->detachFromParent_NoLayout();
523 parent->invokeLayout();
524 }
525}
526
527SkView* SkView::attachChildToBack(SkView* child)
528{
529 SkASSERT(child != this);
530
531 if (child == NULL || fFirstChild == child)
532 goto DONE;
533
534 child->ref();
535 child->detachFromParent_NoLayout();
536
537 if (fFirstChild == NULL)
538 {
539 child->fNextSibling = child;
540 child->fPrevSibling = child;
541 }
542 else
543 {
544 child->fNextSibling = fFirstChild;
545 child->fPrevSibling = fFirstChild->fPrevSibling;
546 fFirstChild->fPrevSibling->fNextSibling = child;
547 fFirstChild->fPrevSibling = child;
548 }
549
550 fFirstChild = child;
551 child->fParent = this;
552 child->inval(NULL);
553
554 this->invokeLayout();
555DONE:
556 return child;
557}
558
559SkView* SkView::attachChildToFront(SkView* child)
560{
561 SkASSERT(child != this);
562
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000563 if (child == NULL || (fFirstChild && fFirstChild->fPrevSibling == child))
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 goto DONE;
565
566 child->ref();
567 child->detachFromParent_NoLayout();
568
569 if (fFirstChild == NULL)
570 {
571 fFirstChild = child;
572 child->fNextSibling = child;
573 child->fPrevSibling = child;
574 }
575 else
576 {
577 child->fNextSibling = fFirstChild;
578 child->fPrevSibling = fFirstChild->fPrevSibling;
579 fFirstChild->fPrevSibling->fNextSibling = child;
580 fFirstChild->fPrevSibling = child;
581 }
582
583 child->fParent = this;
584 child->inval(NULL);
585
586 this->invokeLayout();
587DONE:
588 return child;
589}
590
591void SkView::detachAllChildren()
592{
593 while (fFirstChild)
594 fFirstChild->detachFromParent_NoLayout();
595}
596
597void SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const
598{
599 SkASSERT(this);
600
601 if (local)
602 {
603 const SkView* view = this;
604 while (view)
605 {
606 x -= view->fLoc.fX;
607 y -= view->fLoc.fY;
608 view = view->fParent;
609 }
610 local->set(x, y);
611 }
612}
613
614//////////////////////////////////////////////////////////////////
615
616/* Even if the subclass overrides onInflate, they should always be
617 sure to call the inherited method, so that we get called.
618*/
619void SkView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
620{
621 SkScalar x, y;
622
623 x = this->locX();
624 y = this->locY();
625 (void)dom.findScalar(node, "x", &x);
626 (void)dom.findScalar(node, "y", &y);
627 this->setLoc(x, y);
628
629 x = this->width();
630 y = this->height();
631 (void)dom.findScalar(node, "width", &x);
632 (void)dom.findScalar(node, "height", &y);
633 this->setSize(x, y);
634
635 // inflate the flags
636
637 static const char* gFlagNames[] = {
638 "visible", "enabled", "focusable", "flexH", "flexV"
639 };
640 SkASSERT(SK_ARRAY_COUNT(gFlagNames) == kFlagShiftCount);
641
642 bool b;
643 uint32_t flags = this->getFlags();
644 for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++)
645 if (dom.findBool(node, gFlagNames[i], &b))
646 flags = SkSetClearShift(flags, b, i);
647 this->setFlags(flags);
648}
649
650void SkView::inflate(const SkDOM& dom, const SkDOM::Node* node)
651{
652 this->onInflate(dom, node);
653}
654
655void SkView::onPostInflate(const SkTDict<SkView*>&)
656{
657 // override in subclass as needed
658}
659
660void SkView::postInflate(const SkTDict<SkView*>& dict)
661{
662 this->onPostInflate(dict);
663
664 B2FIter iter(this);
665 SkView* child;
666 while ((child = iter.next()) != NULL)
667 child->postInflate(dict);
668}
669
670//////////////////////////////////////////////////////////////////
671
672SkView* SkView::sendEventToParents(const SkEvent& evt)
673{
674 SkView* parent = fParent;
reed@android.com34245c72009-11-03 04:00:48 +0000675
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676 while (parent)
677 {
678 if (parent->doEvent(evt))
679 return parent;
680 parent = parent->fParent;
681 }
682 return NULL;
683}
684
reed@android.com34245c72009-11-03 04:00:48 +0000685SkView* SkView::sendQueryToParents(SkEvent* evt) {
686 SkView* parent = fParent;
687
688 while (parent) {
689 if (parent->doQuery(evt)) {
690 return parent;
691 }
692 parent = parent->fParent;
693 }
694 return NULL;
695}
696
reed@android.com8a1c16f2008-12-17 15:59:43 +0000697//////////////////////////////////////////////////////////////////
698//////////////////////////////////////////////////////////////////
699
700SkView::F2BIter::F2BIter(const SkView* parent)
701{
702 fFirstChild = parent ? parent->fFirstChild : NULL;
703 fChild = fFirstChild ? fFirstChild->fPrevSibling : NULL;
704}
705
706SkView* SkView::F2BIter::next()
707{
708 SkView* curr = fChild;
709
710 if (fChild)
711 {
712 if (fChild == fFirstChild)
713 fChild = NULL;
714 else
715 fChild = fChild->fPrevSibling;
716 }
717 return curr;
718}
719
720SkView::B2FIter::B2FIter(const SkView* parent)
721{
722 fFirstChild = parent ? parent->fFirstChild : NULL;
723 fChild = fFirstChild;
724}
725
726SkView* SkView::B2FIter::next()
727{
728 SkView* curr = fChild;
729
730 if (fChild)
731 {
732 SkView* next = fChild->fNextSibling;
733 if (next == fFirstChild)
734 next = NULL;
735 fChild = next;
736 }
737 return curr;
738}
739
740//////////////////////////////////////////////////////////////////
741//////////////////////////////////////////////////////////////////
742
743#ifdef SK_DEBUG
744
745static inline void show_if_nonzero(const char name[], SkScalar value)
746{
747 if (value)
748 SkDebugf("%s=\"%g\"", name, value/65536.);
749}
750
751static void tab(int level)
752{
753 for (int i = 0; i < level; i++)
754 SkDebugf(" ");
755}
756
757static void dumpview(const SkView* view, int level, bool recurse)
758{
759 tab(level);
760
761 SkDebugf("<view");
762 show_if_nonzero(" x", view->locX());
763 show_if_nonzero(" y", view->locY());
764 show_if_nonzero(" width", view->width());
765 show_if_nonzero(" height", view->height());
766
767 if (recurse)
768 {
769 SkView::B2FIter iter(view);
770 SkView* child;
771 bool noChildren = true;
772
773 while ((child = iter.next()) != NULL)
774 {
775 if (noChildren)
776 SkDebugf(">\n");
777 noChildren = false;
778 dumpview(child, level + 1, true);
779 }
780
781 if (!noChildren)
782 {
783 tab(level);
784 SkDebugf("</view>\n");
785 }
786 else
787 goto ONELINER;
788 }
789 else
790 {
791 ONELINER:
792 SkDebugf(" />\n");
793 }
794}
795
796void SkView::dump(bool recurse) const
797{
798 dumpview(this, 0, recurse);
799}
800
801#endif