/* * 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" #include "SkDOM.h" static inline uint32_t SkSetClearShift(uint32_t bits, bool cond, unsigned shift) { SkASSERT((int)cond == 0 || (int)cond == 1); return (bits & ~(1 << shift)) | ((int)cond << shift); } //////////////////////////////////////////////////////////////////////// SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags)) { fWidth = fHeight = 0; fLoc.set(0, 0); fParent = fFirstChild = fNextSibling = fPrevSibling = nullptr; 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(nullptr); fFlags = SkToU8(flags); if (diff & kVisible_Mask) { this->inval(nullptr); } } 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(nullptr); fWidth = width; fHeight = height; this->inval(nullptr); this->onSizeChange(); this->invokeLayout(); } } void SkView::setLoc(SkScalar x, SkScalar y) { if (fLoc.fX != x || fLoc.fY != y) { this->inval(nullptr); fLoc.set(x, y); this->inval(nullptr); } } 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(nullptr); fMatrix = matrix; this->inval(nullptr); } 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)) { 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()) != nullptr) 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 == nullptr) { 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) != nullptr); return false; } SkView* SkView::getFocusView() const { SkView* focus = nullptr; const SkView* view = this; do { if (view->onGetFocusView(&focus)) { break; } } while ((view = view->fParent) != nullptr); 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()) != nullptr) { if ((focus = child->acceptFocus(dir)) != nullptr) { return focus; } } } else { // prev F2BIter iter(this); SkView* child, *focus; while ((child = iter.next()) != nullptr) { if ((focus = child->acceptFocus(dir)) != nullptr) { return focus; } } if (this->acceptFocus()) { return this; } } return nullptr; } SkView* SkView::moveFocus(FocusDirection dir) { SkView* focus = this->getFocusView(); if (focus == nullptr) { // 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)) != nullptr) return focus; child = child->fNextSibling; } NEXT_SIB: child = parent->fNextSibling; parent = parent->fParent; } while (parent != nullptr); } else { // prevfocus parent = focus->fParent; if (parent == nullptr) { // 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)) != nullptr) { return focus; } } if (parent->acceptFocus()) { return parent; } child = parent; parent = parent->fParent; } } } return nullptr; } void SkView::onFocusChange(bool gainFocusP) { this->inval(nullptr); } //////////////////////////////////////////////////////////////////////////// SkView::Click::Click(SkView* target) { SkASSERT(target); fTargetID = target->getSinkID(); fType = nullptr; fWeOwnTheType = false; fOwner = nullptr; } SkView::Click::~Click() { this->resetType(); } void SkView::Click::resetType() { if (fWeOwnTheType) { sk_free(fType); fWeOwnTheType = false; } fType = nullptr; } bool SkView::Click::isType(const char type[]) const { const char* t = fType; if (type == t) { return true; } if (type == nullptr) { type = ""; } if (t == nullptr) { 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, unsigned modi) { if (x < 0 || y < 0 || x >= fWidth || y >= fHeight) { return nullptr; } if (this->onSendClickToChildren(x, y, modi)) { F2BIter iter(this); SkView* child; while ((child = iter.next()) != nullptr) { SkPoint p; #if 0 if (!child->globalToLocal(x, y, &p)) { continue; } #else // the above seems broken, so just respecting fLoc for now <reed> p.set(x - child->fLoc.x(), y - child->fLoc.y()); #endif Click* click = child->findClickHandler(p.fX, p.fY, modi); if (click) { return click; } } } return this->onFindClickHandler(x, y, modi); } void SkView::DoClickDown(Click* click, int x, int y, unsigned modi) { SkASSERT(click); SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); if (nullptr == target) { return; } click->fIOrig.set(x, y); click->fICurr = click->fIPrev = click->fIOrig; click->fOrig.iset(x, y); if (!target->globalToLocal(&click->fOrig)) { // no history to let us recover from this failure return; } click->fPrev = click->fCurr = click->fOrig; click->fState = Click::kDown_State; click->fModifierKeys = modi; target->onClick(click); } void SkView::DoClickMoved(Click* click, int x, int y, unsigned modi) { SkASSERT(click); SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); if (nullptr == target) { return; } click->fIPrev = click->fICurr; click->fICurr.set(x, y); click->fPrev = click->fCurr; click->fCurr.iset(x, y); if (!target->globalToLocal(&click->fCurr)) { // on failure pretend the mouse didn't move click->fCurr = click->fPrev; } click->fState = Click::kMoved_State; click->fModifierKeys = modi; target->onClick(click); } void SkView::DoClickUp(Click* click, int x, int y, unsigned modi) { SkASSERT(click); SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); if (nullptr == target) { return; } click->fIPrev = click->fICurr; click->fICurr.set(x, y); click->fPrev = click->fCurr; click->fCurr.iset(x, y); if (!target->globalToLocal(&click->fCurr)) { // on failure pretend the mouse didn't move click->fCurr = click->fPrev; } click->fState = Click::kUp_State; click->fModifierKeys = modi; 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, unsigned modi) { return true; } SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) { return nullptr; } 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() { this->validate(); if (fParent == nullptr) { return; } if (fContainsFocus) { (void)this->setFocusView(nullptr); } this->inval(nullptr); SkView* next = nullptr; 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 = nullptr; this->validate(); this->unref(); } void SkView::detachFromParent() { this->validate(); SkView* parent = fParent; if (parent) { this->detachFromParent_NoLayout(); parent->invokeLayout(); } } SkView* SkView::attachChildToBack(SkView* child) { this->validate(); SkASSERT(child != this); if (child == nullptr || fFirstChild == child) goto DONE; child->ref(); child->detachFromParent_NoLayout(); if (fFirstChild == nullptr) { 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(nullptr); this->validate(); this->invokeLayout(); DONE: return child; } SkView* SkView::attachChildToFront(SkView* child) { this->validate(); SkASSERT(child != this); if (child == nullptr || (fFirstChild && fFirstChild->fPrevSibling == child)) goto DONE; child->ref(); child->detachFromParent_NoLayout(); if (fFirstChild == nullptr) { 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(nullptr); this->validate(); this->invokeLayout(); DONE: return child; } void SkView::detachAllChildren() { this->validate(); 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; } } } bool SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const { if (local) { SkMatrix m; this->localToGlobal(&m); if (!m.invert(&m)) { return false; } SkPoint p; m.mapXY(x, y, &p); local->set(p.fX, p.fY); } return true; } ////////////////////////////////////////////////////////////////// /* 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); } ////////////////////////////////////////////////////////////////// SkView* SkView::sendEventToParents(const SkEvent& evt) { SkView* parent = fParent; while (parent) { if (parent->doEvent(evt)) { return parent; } parent = parent->fParent; } return nullptr; } SkView* SkView::sendQueryToParents(SkEvent* evt) { SkView* parent = fParent; while (parent) { if (parent->doQuery(evt)) { return parent; } parent = parent->fParent; } return nullptr; } ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// SkView::F2BIter::F2BIter(const SkView* parent) { fFirstChild = parent ? parent->fFirstChild : nullptr; fChild = fFirstChild ? fFirstChild->fPrevSibling : nullptr; } SkView* SkView::F2BIter::next() { SkView* curr = fChild; if (fChild) { if (fChild == fFirstChild) { fChild = nullptr; } else { fChild = fChild->fPrevSibling; } } return curr; } SkView::B2FIter::B2FIter(const SkView* parent) { fFirstChild = parent ? parent->fFirstChild : nullptr; fChild = fFirstChild; } SkView* SkView::B2FIter::next() { SkView* curr = fChild; if (fChild) { SkView* next = fChild->fNextSibling; if (next == fFirstChild) next = nullptr; fChild = next; } return curr; } ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// #ifdef SK_DEBUG void SkView::validate() const { // SkASSERT(this->getRefCnt() > 0 && this->getRefCnt() < 100); if (fParent) { SkASSERT(fNextSibling); SkASSERT(fPrevSibling); } else { bool nextNull = nullptr == fNextSibling; bool prevNull = nullptr == fNextSibling; SkASSERT(nextNull == prevNull); } } 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()) != nullptr) { 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