/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkView.h" #include "SkCanvas.h" //////////////////////////////////////////////////////////////////////// SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags)) { fWidth = fHeight = 0; fLoc.set(0, 0); fParent = fFirstChild = fNextSibling = fPrevSibling = NULL; fMatrix.setIdentity(); fContainsFocus = 0; } SkView::~SkView() { this->detachAllChildren(); } void SkView::setFlags(uint32_t flags) { SkASSERT((flags & ~kAllFlagMasks) == 0); uint32_t diff = fFlags ^ flags; if (diff & kVisible_Mask) this->inval(NULL); fFlags = SkToU8(flags); if (diff & kVisible_Mask) { this->inval(NULL); } } void SkView::setVisibleP(bool pred) { this->setFlags(SkSetClearShift(fFlags, pred, kVisible_Shift)); } void SkView::setEnabledP(bool pred) { this->setFlags(SkSetClearShift(fFlags, pred, kEnabled_Shift)); } void SkView::setFocusableP(bool pred) { this->setFlags(SkSetClearShift(fFlags, pred, kFocusable_Shift)); } void SkView::setClipToBounds(bool pred) { this->setFlags(SkSetClearShift(fFlags, !pred, kNoClip_Shift)); } void SkView::setSize(SkScalar width, SkScalar height) { width = SkMaxScalar(0, width); height = SkMaxScalar(0, height); if (fWidth != width || fHeight != height) { this->inval(NULL); fWidth = width; fHeight = height; this->inval(NULL); this->onSizeChange(); this->invokeLayout(); } } void SkView::setLoc(SkScalar x, SkScalar y) { if (fLoc.fX != x || fLoc.fY != y) { this->inval(NULL); fLoc.set(x, y); this->inval(NULL); } } void SkView::offset(SkScalar dx, SkScalar dy) { if (dx || dy) this->setLoc(fLoc.fX + dx, fLoc.fY + dy); } void SkView::setLocalMatrix(const SkMatrix& matrix) { this->inval(NULL); fMatrix = matrix; this->inval(NULL); } void SkView::draw(SkCanvas* canvas) { if (fWidth && fHeight && this->isVisible()) { SkRect r; r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight); if (this->isClipToBounds() && canvas->quickReject(r, SkCanvas::kBW_EdgeType)) { return; } SkAutoCanvasRestore as(canvas, true); if (this->isClipToBounds()) { canvas->clipRect(r); } canvas->translate(fLoc.fX, fLoc.fY); canvas->concat(fMatrix); if (fParent) { fParent->beforeChild(this, canvas); } int sc = canvas->save(); this->onDraw(canvas); canvas->restoreToCount(sc); if (fParent) { fParent->afterChild(this, canvas); } B2FIter iter(this); SkView* child; SkCanvas* childCanvas = this->beforeChildren(canvas); while ((child = iter.next()) != NULL) child->draw(childCanvas); this->afterChildren(canvas); } } void SkView::inval(SkRect* rect) { SkView* view = this; SkRect storage; for (;;) { if (!view->isVisible()) { return; } if (view->isClipToBounds()) { SkRect bounds; view->getLocalBounds(&bounds); if (rect && !bounds.intersect(*rect)) { return; } storage = bounds; rect = &storage; } if (view->handleInval(rect)) { return; } SkView* parent = view->fParent; if (parent == NULL) { return; } if (rect) { rect->offset(view->fLoc.fX, view->fLoc.fY); } view = parent; } } //////////////////////////////////////////////////////////////////////////// bool SkView::setFocusView(SkView* fv) { SkView* view = this; do { if (view->onSetFocusView(fv)) return true; } while ((view = view->fParent) != NULL); return false; } SkView* SkView::getFocusView() const { SkView* focus = NULL; const SkView* view = this; do { if (view->onGetFocusView(&focus)) break; } while ((view = view->fParent) != NULL); return focus; } bool SkView::hasFocus() const { return this == this->getFocusView(); } bool SkView::acceptFocus() { return this->isFocusable() && this->setFocusView(this); } /* Try to give focus to this view, or its children */ SkView* SkView::acceptFocus(FocusDirection dir) { if (dir == kNext_FocusDirection) { if (this->acceptFocus()) return this; B2FIter iter(this); SkView* child, *focus; while ((child = iter.next()) != NULL) if ((focus = child->acceptFocus(dir)) != NULL) return focus; } else // prev { F2BIter iter(this); SkView* child, *focus; while ((child = iter.next()) != NULL) if ((focus = child->acceptFocus(dir)) != NULL) return focus; if (this->acceptFocus()) return this; } return NULL; } SkView* SkView::moveFocus(FocusDirection dir) { SkView* focus = this->getFocusView(); if (focus == NULL) { // start with the root focus = this; while (focus->fParent) focus = focus->fParent; } SkView* child, *parent; if (dir == kNext_FocusDirection) { parent = focus; child = focus->fFirstChild; if (child) goto FIRST_CHILD; else goto NEXT_SIB; do { while (child != parent->fFirstChild) { FIRST_CHILD: if ((focus = child->acceptFocus(dir)) != NULL) return focus; child = child->fNextSibling; } NEXT_SIB: child = parent->fNextSibling; parent = parent->fParent; } while (parent != NULL); } else // prevfocus { parent = focus->fParent; if (parent == NULL) // we're the root return focus->acceptFocus(dir); else { child = focus; while (parent) { while (child != parent->fFirstChild) { child = child->fPrevSibling; if ((focus = child->acceptFocus(dir)) != NULL) return focus; } if (parent->acceptFocus()) return parent; child = parent; parent = parent->fParent; } } } return NULL; } void SkView::onFocusChange(bool gainFocusP) { this->inval(NULL); } //////////////////////////////////////////////////////////////////////////// SkView::Click::Click(SkView* target) { SkASSERT(target); fTargetID = target->getSinkID(); fType = NULL; fWeOwnTheType = false; fOwner = NULL; } SkView::Click::~Click() { this->resetType(); } void SkView::Click::resetType() { if (fWeOwnTheType) { sk_free(fType); fWeOwnTheType = false; } fType = NULL; } bool SkView::Click::isType(const char type[]) const { const char* t = fType; if (type == t) return true; if (type == NULL) type = ""; if (t == NULL) t = ""; return !strcmp(t, type); } void SkView::Click::setType(const char type[]) { this->resetType(); fType = (char*)type; } void SkView::Click::copyType(const char type[]) { if (fType != type) { this->resetType(); if (type) { size_t len = strlen(type) + 1; fType = (char*)sk_malloc_throw(len); memcpy(fType, type, len); fWeOwnTheType = true; } } } SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y) { if (x < 0 || y < 0 || x >= fWidth || y >= fHeight) { return NULL; } if (this->onSendClickToChildren(x, y)) { F2BIter iter(this); SkView* child; while ((child = iter.next()) != NULL) { SkPoint p; child->globalToLocal(x, y, &p); Click* click = child->findClickHandler(p.fX, p.fY); if (click) { return click; } } } return this->onFindClickHandler(x, y); } void SkView::DoClickDown(Click* click, int x, int y) { SkASSERT(click); SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); if (target == NULL) return; click->fIOrig.set(x, y); click->fICurr = click->fIPrev = click->fIOrig; click->fOrig.iset(x, y); target->globalToLocal(&click->fOrig); click->fPrev = click->fCurr = click->fOrig; click->fState = Click::kDown_State; target->onClick(click); } void SkView::DoClickMoved(Click* click, int x, int y) { SkASSERT(click); SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); if (target == NULL) return; click->fIPrev = click->fICurr; click->fICurr.set(x, y); click->fPrev = click->fCurr; click->fCurr.iset(x, y); target->globalToLocal(&click->fCurr); click->fState = Click::kMoved_State; target->onClick(click); } void SkView::DoClickUp(Click* click, int x, int y) { SkASSERT(click); SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); if (target == NULL) return; click->fIPrev = click->fICurr; click->fICurr.set(x, y); click->fPrev = click->fCurr; click->fCurr.iset(x, y); target->globalToLocal(&click->fCurr); click->fState = Click::kUp_State; target->onClick(click); } ////////////////////////////////////////////////////////////////////// void SkView::invokeLayout() { SkView::Layout* layout = this->getLayout(); if (layout) { layout->layoutChildren(this); } } void SkView::onDraw(SkCanvas* canvas) { Artist* artist = this->getArtist(); if (artist) { artist->draw(this, canvas); } } void SkView::onSizeChange() {} bool SkView::onSendClickToChildren(SkScalar x, SkScalar y) { return true; } SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y) { return NULL; } bool SkView::onClick(Click*) { return false; } bool SkView::handleInval(const SkRect*) { return false; } ////////////////////////////////////////////////////////////////////// void SkView::getLocalBounds(SkRect* bounds) const { if (bounds) bounds->set(0, 0, fWidth, fHeight); } ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// void SkView::detachFromParent_NoLayout() { if (fParent == NULL) return; if (fContainsFocus) (void)this->setFocusView(NULL); this->inval(NULL); SkView* next = NULL; if (fNextSibling != this) // do we have any siblings { fNextSibling->fPrevSibling = fPrevSibling; fPrevSibling->fNextSibling = fNextSibling; next = fNextSibling; } if (fParent->fFirstChild == this) fParent->fFirstChild = next; fParent = fNextSibling = fPrevSibling = NULL; this->unref(); } void SkView::detachFromParent() { SkView* parent = fParent; if (parent) { this->detachFromParent_NoLayout(); parent->invokeLayout(); } } SkView* SkView::attachChildToBack(SkView* child) { SkASSERT(child != this); if (child == NULL || fFirstChild == child) goto DONE; child->ref(); child->detachFromParent_NoLayout(); if (fFirstChild == NULL) { child->fNextSibling = child; child->fPrevSibling = child; } else { child->fNextSibling = fFirstChild; child->fPrevSibling = fFirstChild->fPrevSibling; fFirstChild->fPrevSibling->fNextSibling = child; fFirstChild->fPrevSibling = child; } fFirstChild = child; child->fParent = this; child->inval(NULL); this->invokeLayout(); DONE: return child; } SkView* SkView::attachChildToFront(SkView* child) { SkASSERT(child != this); if (child == NULL || (fFirstChild && fFirstChild->fPrevSibling == child)) goto DONE; child->ref(); child->detachFromParent_NoLayout(); if (fFirstChild == NULL) { fFirstChild = child; child->fNextSibling = child; child->fPrevSibling = child; } else { child->fNextSibling = fFirstChild; child->fPrevSibling = fFirstChild->fPrevSibling; fFirstChild->fPrevSibling->fNextSibling = child; fFirstChild->fPrevSibling = child; } child->fParent = this; child->inval(NULL); this->invokeLayout(); DONE: return child; } void SkView::detachAllChildren() { while (fFirstChild) fFirstChild->detachFromParent_NoLayout(); } void SkView::localToGlobal(SkMatrix* matrix) const { if (matrix) { matrix->reset(); const SkView* view = this; while (view) { matrix->preConcat(view->getLocalMatrix()); matrix->preTranslate(-view->fLoc.fX, -view->fLoc.fY); view = view->fParent; } } } void SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const { SkASSERT(this); if (local) { SkMatrix m; this->localToGlobal(&m); SkPoint p; m.invert(&m); m.mapXY(x, y, &p); local->set(p.fX, p.fY); } } ////////////////////////////////////////////////////////////////// /* Even if the subclass overrides onInflate, they should always be sure to call the inherited method, so that we get called. */ void SkView::onInflate(const SkDOM& dom, const SkDOM::Node* node) { SkScalar x, y; x = this->locX(); y = this->locY(); (void)dom.findScalar(node, "x", &x); (void)dom.findScalar(node, "y", &y); this->setLoc(x, y); x = this->width(); y = this->height(); (void)dom.findScalar(node, "width", &x); (void)dom.findScalar(node, "height", &y); this->setSize(x, y); // inflate the flags static const char* gFlagNames[] = { "visible", "enabled", "focusable", "flexH", "flexV" }; SkASSERT(SK_ARRAY_COUNT(gFlagNames) == kFlagShiftCount); bool b; uint32_t flags = this->getFlags(); for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++) if (dom.findBool(node, gFlagNames[i], &b)) flags = SkSetClearShift(flags, b, i); this->setFlags(flags); } void SkView::inflate(const SkDOM& dom, const SkDOM::Node* node) { this->onInflate(dom, node); } void SkView::onPostInflate(const SkTDict<SkView*>&) { // override in subclass as needed } void SkView::postInflate(const SkTDict<SkView*>& dict) { this->onPostInflate(dict); B2FIter iter(this); SkView* child; while ((child = iter.next()) != NULL) child->postInflate(dict); } ////////////////////////////////////////////////////////////////// SkView* SkView::sendEventToParents(const SkEvent& evt) { SkView* parent = fParent; while (parent) { if (parent->doEvent(evt)) return parent; parent = parent->fParent; } return NULL; } SkView* SkView::sendQueryToParents(SkEvent* evt) { SkView* parent = fParent; while (parent) { if (parent->doQuery(evt)) { return parent; } parent = parent->fParent; } return NULL; } ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// SkView::F2BIter::F2BIter(const SkView* parent) { fFirstChild = parent ? parent->fFirstChild : NULL; fChild = fFirstChild ? fFirstChild->fPrevSibling : NULL; } SkView* SkView::F2BIter::next() { SkView* curr = fChild; if (fChild) { if (fChild == fFirstChild) fChild = NULL; else fChild = fChild->fPrevSibling; } return curr; } SkView::B2FIter::B2FIter(const SkView* parent) { fFirstChild = parent ? parent->fFirstChild : NULL; fChild = fFirstChild; } SkView* SkView::B2FIter::next() { SkView* curr = fChild; if (fChild) { SkView* next = fChild->fNextSibling; if (next == fFirstChild) next = NULL; fChild = next; } return curr; } ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// #ifdef SK_DEBUG static inline void show_if_nonzero(const char name[], SkScalar value) { if (value) SkDebugf("%s=\"%g\"", name, value/65536.); } static void tab(int level) { for (int i = 0; i < level; i++) SkDebugf(" "); } static void dumpview(const SkView* view, int level, bool recurse) { tab(level); SkDebugf("<view"); show_if_nonzero(" x", view->locX()); show_if_nonzero(" y", view->locY()); show_if_nonzero(" width", view->width()); show_if_nonzero(" height", view->height()); if (recurse) { SkView::B2FIter iter(view); SkView* child; bool noChildren = true; while ((child = iter.next()) != NULL) { if (noChildren) SkDebugf(">\n"); noChildren = false; dumpview(child, level + 1, true); } if (!noChildren) { tab(level); SkDebugf("</view>\n"); } else goto ONELINER; } else { ONELINER: SkDebugf(" />\n"); } } void SkView::dump(bool recurse) const { dumpview(this, 0, recurse); } #endif