/*
* 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