From 8a1c16ff38322f0210116fa7293eb8817c7e477e Mon Sep 17 00:00:00 2001 From: "reed@android.com" Date: Wed, 17 Dec 2008 15:59:43 +0000 Subject: grab from latest android git-svn-id: http://skia.googlecode.com/svn/trunk@27 2bbb7eff-a529-9590-31e7-b0007b416f81 --- src/views/SkBGViewArtist.cpp | 24 ++ src/views/SkBorderView.cpp | 87 ++++ src/views/SkEvent.cpp | 565 +++++++++++++++++++++++++ src/views/SkEventSink.cpp | 345 ++++++++++++++++ src/views/SkImageView.cpp | 296 +++++++++++++ src/views/SkListView.cpp | 895 ++++++++++++++++++++++++++++++++++++++++ src/views/SkListWidget.cpp | 623 ++++++++++++++++++++++++++++ src/views/SkMetaData.cpp | 405 ++++++++++++++++++ src/views/SkOSFile.cpp | 223 ++++++++++ src/views/SkOSMenu.cpp | 53 +++ src/views/SkOSSound.cpp | 343 +++++++++++++++ src/views/SkParsePaint.cpp | 103 +++++ src/views/SkProgressBarView.cpp | 102 +++++ src/views/SkProgressView.cpp | 125 ++++++ src/views/SkScrollBarView.cpp | 139 +++++++ src/views/SkStackViewLayout.cpp | 262 ++++++++++++ src/views/SkTagList.cpp | 71 ++++ src/views/SkTagList.h | 51 +++ src/views/SkTextBox.cpp | 216 ++++++++++ src/views/SkView.cpp | 760 ++++++++++++++++++++++++++++++++++ src/views/SkViewInflate.cpp | 139 +++++++ src/views/SkViewPriv.cpp | 97 +++++ src/views/SkViewPriv.h | 38 ++ src/views/SkWidget.cpp | 323 +++++++++++++++ src/views/SkWidgetViews.cpp | 570 +++++++++++++++++++++++++ src/views/SkWidgets.cpp | 554 +++++++++++++++++++++++++ src/views/SkWindow.cpp | 367 ++++++++++++++++ 27 files changed, 7776 insertions(+) create mode 100644 src/views/SkBGViewArtist.cpp create mode 100644 src/views/SkBorderView.cpp create mode 100644 src/views/SkEvent.cpp create mode 100644 src/views/SkEventSink.cpp create mode 100644 src/views/SkImageView.cpp create mode 100644 src/views/SkListView.cpp create mode 100644 src/views/SkListWidget.cpp create mode 100644 src/views/SkMetaData.cpp create mode 100644 src/views/SkOSFile.cpp create mode 100644 src/views/SkOSMenu.cpp create mode 100644 src/views/SkOSSound.cpp create mode 100644 src/views/SkParsePaint.cpp create mode 100644 src/views/SkProgressBarView.cpp create mode 100644 src/views/SkProgressView.cpp create mode 100644 src/views/SkScrollBarView.cpp create mode 100644 src/views/SkStackViewLayout.cpp create mode 100644 src/views/SkTagList.cpp create mode 100644 src/views/SkTagList.h create mode 100644 src/views/SkTextBox.cpp create mode 100644 src/views/SkView.cpp create mode 100644 src/views/SkViewInflate.cpp create mode 100644 src/views/SkViewPriv.cpp create mode 100644 src/views/SkViewPriv.h create mode 100644 src/views/SkWidget.cpp create mode 100644 src/views/SkWidgetViews.cpp create mode 100644 src/views/SkWidgets.cpp create mode 100644 src/views/SkWindow.cpp (limited to 'src/views') diff --git a/src/views/SkBGViewArtist.cpp b/src/views/SkBGViewArtist.cpp new file mode 100644 index 0000000000..07da123284 --- /dev/null +++ b/src/views/SkBGViewArtist.cpp @@ -0,0 +1,24 @@ +#include "SkBGViewArtist.h" +#include "SkCanvas.h" +#include "SkParsePaint.h" + +SkBGViewArtist::SkBGViewArtist(SkColor c) +{ + fPaint.setColor(c); +} + +SkBGViewArtist::~SkBGViewArtist() +{ +} + +void SkBGViewArtist::onDraw(SkView*, SkCanvas* canvas) +{ + // only works for views that are clipped their bounds. + canvas->drawPaint(fPaint); +} + +void SkBGViewArtist::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + SkPaint_Inflate(&fPaint, dom, node); +} + diff --git a/src/views/SkBorderView.cpp b/src/views/SkBorderView.cpp new file mode 100644 index 0000000000..3356782326 --- /dev/null +++ b/src/views/SkBorderView.cpp @@ -0,0 +1,87 @@ +#include "SkBorderView.h" +#include "SkAnimator.h" +#include "SkWidgetViews.h" +#include "SkSystemEventTypes.h" +#include "SkTime.h" +#include "SkStackViewLayout.h" + +SkBorderView::SkBorderView() : fTop(SkIntToScalar(0)), fLeft(SkIntToScalar(0)), + fRight(SkIntToScalar(0)), fBottom(SkIntToScalar(0)) +{ + fAnim.setHostEventSink(this); + init_skin_anim(kBorder_SkinEnum, &fAnim); +} + +SkBorderView::~SkBorderView() +{ + +} + +void SkBorderView::setSkin(const char skin[]) +{ + init_skin_anim(skin, &fAnim); +} + +/* virtual */ void SkBorderView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); +} + +/*virtual*/ void SkBorderView::onSizeChange() +{ + this->INHERITED::onSizeChange(); + SkEvent evt("user"); + evt.setString("id", "setDim"); + evt.setScalar("dimX", this->width()); + evt.setScalar("dimY", this->height()); + fAnim.doUserEvent(evt); +} + +/*virtual*/ void SkBorderView::onDraw(SkCanvas* canvas) +{ + SkPaint paint; + SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs()); + + if (diff == SkAnimator::kDifferent) + this->inval(nil); + else if (diff == SkAnimator::kPartiallyDifferent) + { + SkRect bounds; + fAnim.getInvalBounds(&bounds); + this->inval(&bounds); + } +} + +/*virtual*/ bool SkBorderView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Inval)) + { + this->inval(nil); + return true; + } + if (evt.isType("recommendDim")) + { + evt.findScalar("leftMargin", &fLeft); + evt.findScalar("rightMargin", &fRight); + evt.findScalar("topMargin", &fTop); + evt.findScalar("bottomMargin", &fBottom); + + //setup_views.cpp uses SkView::Layout instead of SkStackViewLayout + //but that gives me an error + SkStackViewLayout* layout; + fMargin.set(fLeft, fTop, fRight, fBottom); + if (this->getLayout()) + { + layout = (SkStackViewLayout*)this->getLayout(); + layout->setMargin(fMargin); + } + else + { + layout = new SkStackViewLayout; + layout->setMargin(fMargin); + this->setLayout(layout)->unref(); + } + this->invokeLayout(); + } + return this->INHERITED::onEvent(evt); +} diff --git a/src/views/SkEvent.cpp b/src/views/SkEvent.cpp new file mode 100644 index 0000000000..67549b48b5 --- /dev/null +++ b/src/views/SkEvent.cpp @@ -0,0 +1,565 @@ +/* libs/graphics/views/SkEvent.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkEvent.h" + +void SkEvent::initialize(const char* type, size_t typeLen) { + fType = NULL; + setType(type, typeLen); + f32 = 0; +#ifdef SK_DEBUG + fTargetID = 0; + fTime = 0; + fNextEvent = NULL; +#endif + SkDEBUGCODE(fDebugTrace = false;) +} + +SkEvent::SkEvent() +{ + initialize("", 0); +} + +SkEvent::SkEvent(const SkEvent& src) +{ + *this = src; + if (((size_t) fType & 1) == 0) + setType(src.fType); +} + +SkEvent::SkEvent(const SkString& type) +{ + initialize(type.c_str(), type.size()); +} + +SkEvent::SkEvent(const char type[]) +{ + SkASSERT(type); + initialize(type, strlen(type)); +} + +SkEvent::~SkEvent() +{ + if (((size_t) fType & 1) == 0) + sk_free((void*) fType); +} + +static size_t makeCharArray(char* buffer, size_t compact) +{ + size_t bits = (size_t) compact >> 1; + memcpy(buffer, &bits, sizeof(compact)); + buffer[sizeof(compact)] = 0; + return strlen(buffer); +} + +#if 0 +const char* SkEvent::getType() const +{ + if ((size_t) fType & 1) { // not a pointer + char chars[sizeof(size_t) + 1]; + size_t len = makeCharArray(chars, (size_t) fType); + fType = (char*) sk_malloc_throw(len); + SkASSERT(((size_t) fType & 1) == 0); + memcpy(fType, chars, len); + } + return fType; +} +#endif + +void SkEvent::getType(SkString* str) const +{ + if (str) + { + if ((size_t) fType & 1) // not a pointer + { + char chars[sizeof(size_t) + 1]; + size_t len = makeCharArray(chars, (size_t) fType); + str->set(chars, len); + } + else + str->set(fType); + } +} + +bool SkEvent::isType(const SkString& str) const +{ + return this->isType(str.c_str(), str.size()); +} + +bool SkEvent::isType(const char type[], size_t typeLen) const +{ + if (typeLen == 0) + typeLen = strlen(type); + if ((size_t) fType & 1) { // not a pointer + char chars[sizeof(size_t) + 1]; + size_t len = makeCharArray(chars, (size_t) fType); + return len == typeLen && strncmp(chars, type, typeLen) == 0; + } + return strncmp(fType, type, typeLen) == 0 && fType[typeLen] == 0; +} + +void SkEvent::setType(const char type[], size_t typeLen) +{ + if (typeLen == 0) + typeLen = strlen(type); + if (typeLen <= sizeof(fType)) { + size_t slot = 0; + memcpy(&slot, type, typeLen); + if (slot << 1 >> 1 != slot) + goto useCharStar; + slot <<= 1; + slot |= 1; + fType = (char*) slot; + } else { +useCharStar: + fType = (char*) sk_malloc_throw(typeLen + 1); + SkASSERT(((size_t) fType & 1) == 0); + memcpy(fType, type, typeLen); + fType[typeLen] = 0; + } +} + +void SkEvent::setType(const SkString& type) +{ + setType(type.c_str()); +} + +//////////////////////////////////////////////////////////////////////////// + +#include "SkParse.h" + +void SkEvent::inflate(const SkDOM& dom, const SkDOM::Node* node) +{ + const char* name = dom.findAttr(node, "type"); + if (name) + this->setType(name); + + const char* value; + if ((value = dom.findAttr(node, "fast32")) != NULL) + { + int32_t n; + if (SkParse::FindS32(value, &n)) + this->setFast32(n); + } + + for (node = dom.getFirstChild(node); node; node = dom.getNextSibling(node)) + { + if (strcmp(dom.getName(node), "data")) + { + SkDEBUGCODE(SkDebugf("SkEvent::inflate unrecognized subelement <%s>\n", dom.getName(node));) + continue; + } + + name = dom.findAttr(node, "name"); + if (name == NULL) + { + SkDEBUGCODE(SkDebugf("SkEvent::inflate missing required \"name\" attribute in subelement\n");) + continue; + } + + if ((value = dom.findAttr(node, "s32")) != NULL) + { + int32_t n; + if (SkParse::FindS32(value, &n)) + this->setS32(name, n); + } + else if ((value = dom.findAttr(node, "scalar")) != NULL) + { + SkScalar x; + if (SkParse::FindScalar(value, &x)) + this->setScalar(name, x); + } + else if ((value = dom.findAttr(node, "string")) != NULL) + this->setString(name, value); +#ifdef SK_DEBUG + else + { + SkDebugf("SkEvent::inflate subelement missing required type attribute [S32 | scalar | string]\n", name); + } +#endif + } +} + +#ifdef SK_DEBUG + + #ifndef SkScalarToFloat + #define SkScalarToFloat(x) ((x) / 65536.f) + #endif + + void SkEvent::dump(const char title[]) + { + if (title) + SkDebugf("%s ", title); + + SkString etype; + this->getType(&etype); + SkDebugf("event<%s> fast32=%d", etype.c_str(), this->getFast32()); + + const SkMetaData& md = this->getMetaData(); + SkMetaData::Iter iter(md); + SkMetaData::Type mtype; + int count; + const char* name; + + while ((name = iter.next(&mtype, &count)) != NULL) + { + SkASSERT(count > 0); + + SkDebugf(" <%s>=", name); + switch (mtype) { + case SkMetaData::kS32_Type: // vector version??? + { + int32_t value; + md.findS32(name, &value); + SkDebugf("%d ", value); + } + break; + case SkMetaData::kScalar_Type: + { + const SkScalar* values = md.findScalars(name, &count, NULL); + SkDebugf("%f", SkScalarToFloat(values[0])); + for (int i = 1; i < count; i++) + SkDebugf(", %f", SkScalarToFloat(values[i])); + SkDebugf(" "); + } + break; + case SkMetaData::kString_Type: + { + const char* value = md.findString(name); + SkASSERT(value); + SkDebugf("<%s> ", value); + } + break; + case SkMetaData::kPtr_Type: // vector version??? + { + void* value; + md.findPtr(name, &value); + SkDebugf("%p ", value); + } + break; + case SkMetaData::kBool_Type: // vector version??? + { + bool value; + md.findBool(name, &value); + SkDebugf("%s ", value ? "true" : "false"); + } + break; + default: + SkASSERT(!"unknown metadata type returned from iterator"); + break; + } + } + SkDebugf("\n"); + } +#endif + +/////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG +// #define SK_TRACE_EVENTSx +#endif + +#ifdef SK_TRACE_EVENTS + static void event_log(const char s[]) + { + SkDEBUGF(("%s\n", s)); + } + + #define EVENT_LOG(s) event_log(s) + #define EVENT_LOGN(s, n) do { SkString str(s); str.append(" "); str.appendS32(n); event_log(str.c_str()); } while (0) +#else + #define EVENT_LOG(s) + #define EVENT_LOGN(s, n) +#endif + +#include "SkGlobals.h" +#include "SkThread.h" +#include "SkTime.h" + +#define SK_Event_GlobalsTag SkSetFourByteTag('e', 'v', 'n', 't') + +class SkEvent_Globals : public SkGlobals::Rec { +public: + SkMutex fEventMutex; + SkEvent* fEventQHead, *fEventQTail; + SkEvent* fDelayQHead; + SkDEBUGCODE(int fEventCounter;) +}; + +static SkGlobals::Rec* create_globals() +{ + SkEvent_Globals* rec = new SkEvent_Globals; + rec->fEventQHead = NULL; + rec->fEventQTail = NULL; + rec->fDelayQHead = NULL; + SkDEBUGCODE(rec->fEventCounter = 0;) + return rec; +} + +bool SkEvent::Post(SkEvent* evt, SkEventSinkID sinkID, SkMSec delay) +{ + if (delay) + return SkEvent::PostTime(evt, sinkID, SkTime::GetMSecs() + delay); + + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + evt->fTargetID = sinkID; + +#ifdef SK_TRACE_EVENTS + { + SkString str("SkEvent::Post("); + str.append(evt->getType()); + str.append(", 0x"); + str.appendHex(sinkID); + str.append(", "); + str.appendS32(delay); + str.append(")"); + event_log(str.c_str()); + } +#endif + + globals.fEventMutex.acquire(); + bool wasEmpty = SkEvent::Enqueue(evt); + globals.fEventMutex.release(); + + // call outside of us holding the mutex + if (wasEmpty) + SkEvent::SignalNonEmptyQueue(); + return true; +} + +#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(SK_FIND_MEMORY_LEAKS) +SkMSec gMaxDrawTime; +#endif + +bool SkEvent::PostTime(SkEvent* evt, SkEventSinkID sinkID, SkMSec time) +{ +#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(SK_FIND_MEMORY_LEAKS) + gMaxDrawTime = time; +#endif + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + evt->fTargetID = sinkID; + +#ifdef SK_TRACE_EVENTS + { + SkString str("SkEvent::Post("); + str.append(evt->getType()); + str.append(", 0x"); + str.appendHex(sinkID); + str.append(", "); + str.appendS32(time); + str.append(")"); + event_log(str.c_str()); + } +#endif + + globals.fEventMutex.acquire(); + SkMSec queueDelay = SkEvent::EnqueueTime(evt, time); + globals.fEventMutex.release(); + + // call outside of us holding the mutex + if ((int32_t)queueDelay != ~0) + SkEvent::SignalQueueTimer(queueDelay); + return true; +} + +bool SkEvent::Enqueue(SkEvent* evt) +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + // gEventMutex acquired by caller + + SkASSERT(evt); + + bool wasEmpty = globals.fEventQHead == NULL; + + if (globals.fEventQTail) + globals.fEventQTail->fNextEvent = evt; + globals.fEventQTail = evt; + if (globals.fEventQHead == NULL) + globals.fEventQHead = evt; + evt->fNextEvent = NULL; + + SkDEBUGCODE(++globals.fEventCounter); +// SkDebugf("Enqueue: count=%d\n", gEventCounter); + + return wasEmpty; +} + +SkEvent* SkEvent::Dequeue(SkEventSinkID* sinkID) +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + globals.fEventMutex.acquire(); + + SkEvent* evt = globals.fEventQHead; + if (evt) + { + SkDEBUGCODE(--globals.fEventCounter); + + if (sinkID) + *sinkID = evt->fTargetID; + + globals.fEventQHead = evt->fNextEvent; + if (globals.fEventQHead == NULL) + globals.fEventQTail = NULL; + } + globals.fEventMutex.release(); + +// SkDebugf("Dequeue: count=%d\n", gEventCounter); + + return evt; +} + +bool SkEvent::QHasEvents() +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + // this is not thread accurate, need a semaphore for that + return globals.fEventQHead != NULL; +} + +#ifdef SK_TRACE_EVENTS + static int gDelayDepth; +#endif + +SkMSec SkEvent::EnqueueTime(SkEvent* evt, SkMSec time) +{ +#ifdef SK_TRACE_EVENTS + SkDebugf("enqueue-delay %s %d (%d)", evt->getType(), time, gDelayDepth); + const char* idStr = evt->findString("id"); + if (idStr) + SkDebugf(" (%s)", idStr); + SkDebugf("\n"); + ++gDelayDepth; +#endif + + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + // gEventMutex acquired by caller + + SkEvent* curr = globals.fDelayQHead; + SkEvent* prev = NULL; + + while (curr) + { + if (SkMSec_LT(time, curr->fTime)) + break; + prev = curr; + curr = curr->fNextEvent; + } + + evt->fTime = time; + evt->fNextEvent = curr; + if (prev == NULL) + globals.fDelayQHead = evt; + else + prev->fNextEvent = evt; + + SkMSec delay = globals.fDelayQHead->fTime - SkTime::GetMSecs(); + if ((int32_t)delay <= 0) + delay = 1; + return delay; +} + +////////////////////////////////////////////////////////////////////////////// + +#include "SkEventSink.h" + +bool SkEvent::ProcessEvent() +{ + SkEventSinkID sinkID; + SkEvent* evt = SkEvent::Dequeue(&sinkID); + SkAutoTDelete autoDelete(evt); + bool again = false; + + EVENT_LOGN("ProcessEvent", (int32_t)evt); + + if (evt) + { + (void)SkEventSink::DoEvent(*evt, sinkID); + again = SkEvent::QHasEvents(); + } + return again; +} + +void SkEvent::ServiceQueueTimer() +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + globals.fEventMutex.acquire(); + + bool wasEmpty = false; + SkMSec now = SkTime::GetMSecs(); + SkEvent* evt = globals.fDelayQHead; + + while (evt) + { + if (SkMSec_LT(now, evt->fTime)) + break; + +#ifdef SK_TRACE_EVENTS + --gDelayDepth; + SkDebugf("dequeue-delay %s (%d)", evt->getType(), gDelayDepth); + const char* idStr = evt->findString("id"); + if (idStr) + SkDebugf(" (%s)", idStr); + SkDebugf("\n"); +#endif + + SkEvent* next = evt->fNextEvent; + if (SkEvent::Enqueue(evt)) + wasEmpty = true; + evt = next; + } + globals.fDelayQHead = evt; + + SkMSec time = evt ? evt->fTime - now : 0; + + globals.fEventMutex.release(); + + if (wasEmpty) + SkEvent::SignalNonEmptyQueue(); + + SkEvent::SignalQueueTimer(time); +} + +//////////////////////////////////////////////////////////////// + +void SkEvent::Init() +{ +} + +void SkEvent::Term() +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + SkEvent* evt = globals.fEventQHead; + while (evt) + { + SkEvent* next = evt->fNextEvent; + delete evt; + evt = next; + } + + evt = globals.fDelayQHead; + while (evt) + { + SkEvent* next = evt->fNextEvent; + delete evt; + evt = next; + } +} + diff --git a/src/views/SkEventSink.cpp b/src/views/SkEventSink.cpp new file mode 100644 index 0000000000..c8fe35ca18 --- /dev/null +++ b/src/views/SkEventSink.cpp @@ -0,0 +1,345 @@ +/* libs/graphics/views/SkEventSink.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkEventSink.h" +#include "SkTagList.h" +#include "SkThread.h" + +#include "SkGlobals.h" +#include "SkThread.h" +#include "SkTime.h" + +#define SK_EventSink_GlobalsTag SkSetFourByteTag('e', 'v', 's', 'k') + +class SkEventSink_Globals : public SkGlobals::Rec { +public: + SkMutex fSinkMutex; + SkEventSinkID fNextSinkID; + SkEventSink* fSinkHead; +}; + +static SkGlobals::Rec* create_globals() +{ + SkEventSink_Globals* rec = new SkEventSink_Globals; + rec->fNextSinkID = 0; + rec->fSinkHead = NULL; + return rec; +} + +SkEventSink::SkEventSink() : fTagHead(NULL) +{ + SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals); + + globals.fSinkMutex.acquire(); + + fID = ++globals.fNextSinkID; + fNextSink = globals.fSinkHead; + globals.fSinkHead = this; + + globals.fSinkMutex.release(); +} + +SkEventSink::~SkEventSink() +{ + SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals); + + if (fTagHead) + SkTagList::DeleteAll(fTagHead); + + globals.fSinkMutex.acquire(); + + SkEventSink* sink = globals.fSinkHead; + SkEventSink* prev = NULL; + + for (;;) + { + SkEventSink* next = sink->fNextSink; + if (sink == this) + { + if (prev) + prev->fNextSink = next; + else + globals.fSinkHead = next; + break; + } + prev = sink; + sink = next; + } + globals.fSinkMutex.release(); +} + +bool SkEventSink::doEvent(const SkEvent& evt) +{ + return this->onEvent(evt); +} + +bool SkEventSink::doQuery(SkEvent* evt) +{ + SkASSERT(evt); + return this->onQuery(evt); +} + +bool SkEventSink::onEvent(const SkEvent&) +{ + return false; +} + +bool SkEventSink::onQuery(SkEvent*) +{ + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTagList* SkEventSink::findTagList(U8CPU tag) const +{ + return fTagHead ? SkTagList::Find(fTagHead, tag) : NULL; +} + +void SkEventSink::addTagList(SkTagList* rec) +{ + SkASSERT(rec); + SkASSERT(fTagHead == NULL || SkTagList::Find(fTagHead, rec->fTag) == NULL); + + rec->fNext = fTagHead; + fTagHead = rec; +} + +void SkEventSink::removeTagList(U8CPU tag) +{ + if (fTagHead) + SkTagList::DeleteTag(&fTagHead, tag); +} + +/////////////////////////////////////////////////////////////////////////////// + +struct SkListenersTagList : SkTagList { + SkListenersTagList(U16CPU count) : SkTagList(kListeners_SkTagList) + { + fExtra16 = SkToU16(count); + fIDs = (SkEventSinkID*)sk_malloc_throw(count * sizeof(SkEventSinkID)); + } + virtual ~SkListenersTagList() + { + sk_free(fIDs); + } + + int countListners() const { return fExtra16; } + + int find(SkEventSinkID id) const + { + const SkEventSinkID* idptr = fIDs; + for (int i = fExtra16 - 1; i >= 0; --i) + if (idptr[i] == id) + return i; + return -1; + } + + SkEventSinkID* fIDs; +}; + +void SkEventSink::addListenerID(SkEventSinkID id) +{ + if (id == 0) + return; + + SkListenersTagList* prev = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); + int count = 0; + + if (prev) + { + if (prev->find(id) >= 0) + return; + count = prev->countListners(); + } + + SkListenersTagList* next = SkNEW_ARGS(SkListenersTagList, (count + 1)); + + if (prev) + { + memcpy(next->fIDs, prev->fIDs, count * sizeof(SkEventSinkID)); + this->removeTagList(kListeners_SkTagList); + } + next->fIDs[count] = id; + this->addTagList(next); +} + +void SkEventSink::copyListeners(const SkEventSink& sink) +{ + SkListenersTagList* sinkList = (SkListenersTagList*)sink.findTagList(kListeners_SkTagList); + if (sinkList == NULL) + return; + SkASSERT(sinkList->countListners() > 0); + const SkEventSinkID* iter = sinkList->fIDs; + const SkEventSinkID* stop = iter + sinkList->countListners(); + while (iter < stop) + addListenerID(*iter++); +} + +void SkEventSink::removeListenerID(SkEventSinkID id) +{ + if (id == 0) + return; + + SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); + + if (list == NULL) + return; + + int index = list->find(id); + if (index >= 0) + { + int count = list->countListners(); + SkASSERT(count > 0); + if (count == 1) + this->removeTagList(kListeners_SkTagList); + else + { + // overwrite without resize/reallocating our struct (for speed) + list->fIDs[index] = list->fIDs[count - 1]; + list->fExtra16 = SkToU16(count - 1); + } + } +} + +bool SkEventSink::hasListeners() const +{ + return this->findTagList(kListeners_SkTagList) != NULL; +} + +void SkEventSink::postToListeners(const SkEvent& evt, SkMSec delay) +{ + SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); + if (list) + { + SkASSERT(list->countListners() > 0); + const SkEventSinkID* iter = list->fIDs; + const SkEventSinkID* stop = iter + list->countListners(); + while (iter < stop) + (SkNEW_ARGS(SkEvent, (evt)))->post(*iter++, delay); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +SkEventSink::EventResult SkEventSink::DoEvent(const SkEvent& evt, SkEventSinkID sinkID) +{ + SkEventSink* sink = SkEventSink::FindSink(sinkID); + + if (sink) + { +#ifdef SK_DEBUG + if (evt.isDebugTrace()) + { + SkString etype; + evt.getType(&etype); + SkDebugf("SkEventTrace: dispatching event <%s> to 0x%x", etype.c_str(), sinkID); + const char* idStr = evt.findString("id"); + if (idStr) + SkDebugf(" (%s)", idStr); + SkDebugf("\n"); + } +#endif + return sink->doEvent(evt) ? kHandled_EventResult : kNotHandled_EventResult; + } + else + { +#ifdef SK_DEBUG + if (sinkID) + SkDebugf("DoEvent: Can't find sink for ID(%x)\n", sinkID); + else + SkDebugf("Event sent to 0 sinkID\n"); + + if (evt.isDebugTrace()) + { + SkString etype; + evt.getType(&etype); + SkDebugf("SkEventTrace: eventsink not found <%s> for 0x%x\n", etype.c_str(), sinkID); + } +#endif + return kSinkNotFound_EventResult; + } +} + +SkEventSink* SkEventSink::FindSink(SkEventSinkID sinkID) +{ + if (sinkID == 0) + return 0; + + SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals); + SkAutoMutexAcquire ac(globals.fSinkMutex); + SkEventSink* sink = globals.fSinkHead; + + while (sink) + { + if (sink->getSinkID() == sinkID) + return sink; + sink = sink->fNextSink; + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +#if 0 // experimental, not tested + +#include "SkThread.h" +#include "SkTDict.h" + +#define kMinStringBufferSize 128 +static SkMutex gNamedSinkMutex; +static SkTDict gNamedSinkIDs(kMinStringBufferSize); + +/** Register a name/id pair with the system. If the name already exists, + replace its ID with the new id. This pair will persist until UnregisterNamedSink() + is called. +*/ +void SkEventSink::RegisterNamedSinkID(const char name[], SkEventSinkID id) +{ + if (id && name && *name) + { + SkAutoMutexAcquire ac(gNamedSinkMutex); + gNamedSinkIDs.set(name, id); + } +} + +/** Return the id that matches the specified name (from a previous call to + RegisterNamedSinkID(). If no match is found, return 0 +*/ +SkEventSinkID SkEventSink::FindNamedSinkID(const char name[]) +{ + SkEventSinkID id = 0; + + if (name && *name) + { + SkAutoMutexAcquire ac(gNamedSinkMutex); + (void)gNamedSinkIDs.find(name, &id); + } + return id; +} + +/** Remove all name/id pairs from the system. This is call internally + on shutdown, to ensure no memory leaks. It should not be called + before shutdown. +*/ +void SkEventSink::RemoveAllNamedSinkIDs() +{ + SkAutoMutexAcquire ac(gNamedSinkMutex); + (void)gNamedSinkIDs.reset(); +} +#endif diff --git a/src/views/SkImageView.cpp b/src/views/SkImageView.cpp new file mode 100644 index 0000000000..f19e913789 --- /dev/null +++ b/src/views/SkImageView.cpp @@ -0,0 +1,296 @@ +#include "SkImageView.h" +#include "SkAnimator.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkImageDecoder.h" +#include "SkMatrix.h" +#include "SkSystemEventTypes.h" +#include "SkTime.h" + +SkImageView::SkImageView() +{ + fMatrix = nil; + fScaleType = kMatrix_ScaleType; + + fData.fAnim = nil; // handles initializing the other union values + fDataIsAnim = true; + + fUriIsValid = false; // an empty string is not valid +} + +SkImageView::~SkImageView() +{ + if (fMatrix) + sk_free(fMatrix); + + this->freeData(); +} + +void SkImageView::getUri(SkString* uri) const +{ + if (uri) + *uri = fUri; +} + +void SkImageView::setUri(const char uri[]) +{ + if (!fUri.equals(uri)) + { + fUri.set(uri); + this->onUriChange(); + } +} + +void SkImageView::setUri(const SkString& uri) +{ + if (fUri != uri) + { + fUri = uri; + this->onUriChange(); + } +} + +void SkImageView::setScaleType(ScaleType st) +{ + SkASSERT((unsigned)st <= kFitEnd_ScaleType); + + if ((ScaleType)fScaleType != st) + { + fScaleType = SkToU8(st); + if (fUriIsValid) + this->inval(nil); + } +} + +bool SkImageView::getImageMatrix(SkMatrix* matrix) const +{ + if (fMatrix) + { + SkASSERT(!fMatrix->isIdentity()); + if (matrix) + *matrix = *fMatrix; + return true; + } + else + { + if (matrix) + matrix->reset(); + return false; + } +} + +void SkImageView::setImageMatrix(const SkMatrix* matrix) +{ + bool changed = false; + + if (matrix && !matrix->isIdentity()) + { + if (fMatrix == nil) + fMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix)); + *fMatrix = *matrix; + changed = true; + } + else // set us to identity + { + if (fMatrix) + { + SkASSERT(!fMatrix->isIdentity()); + sk_free(fMatrix); + fMatrix = nil; + changed = true; + } + } + + // only redraw if we changed our matrix and we're not in scaleToFit mode + if (changed && this->getScaleType() == kMatrix_ScaleType && fUriIsValid) + this->inval(nil); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +bool SkImageView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Inval)) + { + if (fUriIsValid) + this->inval(nil); + return true; + } + return this->INHERITED::onEvent(evt); +} + +static inline SkMatrix::ScaleToFit scaleTypeToScaleToFit(SkImageView::ScaleType st) +{ + SkASSERT(st != SkImageView::kMatrix_ScaleType); + SkASSERT((unsigned)st <= SkImageView::kFitEnd_ScaleType); + + SkASSERT(SkImageView::kFitXY_ScaleType - 1 == SkMatrix::kFill_ScaleToFit); + SkASSERT(SkImageView::kFitStart_ScaleType - 1 == SkMatrix::kStart_ScaleToFit); + SkASSERT(SkImageView::kFitCenter_ScaleType - 1 == SkMatrix::kCenter_ScaleToFit); + SkASSERT(SkImageView::kFitEnd_ScaleType - 1 == SkMatrix::kEnd_ScaleToFit); + + return (SkMatrix::ScaleToFit)(st - 1); +} + +void SkImageView::onDraw(SkCanvas* canvas) +{ + SkRect src; + if (!this->getDataBounds(&src)) + { + SkDEBUGCODE(canvas->drawColor(SK_ColorRED);) + return; // nothing to draw + } + + SkAutoCanvasRestore restore(canvas, true); + SkMatrix matrix; + + if (this->getScaleType() == kMatrix_ScaleType) + (void)this->getImageMatrix(&matrix); + else + { + SkRect dst; + dst.set(0, 0, this->width(), this->height()); + matrix.setRectToRect(src, dst, scaleTypeToScaleToFit(this->getScaleType())); + } + canvas->concat(matrix); + + SkPaint paint; + + paint.setAntiAlias(true); + + if (fDataIsAnim) + { + SkMSec now = SkTime::GetMSecs(); + + SkAnimator::DifferenceType diff = fData.fAnim->draw(canvas, &paint, now); + +SkDEBUGF(("SkImageView : now = %X[%12.3f], diff = %d\n", now, now/1000., diff)); + + if (diff == SkAnimator::kDifferent) + this->inval(nil); + else if (diff == SkAnimator::kPartiallyDifferent) + { + SkRect bounds; + fData.fAnim->getInvalBounds(&bounds); + matrix.mapRect(&bounds); // get the bounds into view coordinates + this->inval(&bounds); + } + } + else + canvas->drawBitmap(*fData.fBitmap, 0, 0, &paint); +} + +void SkImageView::onInflate(const SkDOM& dom, const SkDOMNode* node) +{ + this->INHERITED::onInflate(dom, node); + + const char* src = dom.findAttr(node, "src"); + if (src) + this->setUri(src); + + int index = dom.findList(node, "scaleType", "matrix,fitXY,fitStart,fitCenter,fitEnd"); + if (index >= 0) + this->setScaleType((ScaleType)index); + + // need inflate syntax/reader for matrix +} + +///////////////////////////////////////////////////////////////////////////////////// + +void SkImageView::onUriChange() +{ + if (this->freeData()) + this->inval(nil); + fUriIsValid = true; // give ensureUriIsLoaded() a shot at the new uri +} + +bool SkImageView::freeData() +{ + if (fData.fAnim) // test is valid for all union values + { + if (fDataIsAnim) + delete fData.fAnim; + else + delete fData.fBitmap; + + fData.fAnim = nil; // valid for all union values + return true; + } + return false; +} + +bool SkImageView::getDataBounds(SkRect* bounds) +{ + SkASSERT(bounds); + + if (this->ensureUriIsLoaded()) + { + SkScalar width, height; + + if (fDataIsAnim) + { + if (SkScalarIsNaN(width = fData.fAnim->getScalar("dimensions", "x")) || + SkScalarIsNaN(height = fData.fAnim->getScalar("dimensions", "y"))) + { + // cons up fake bounds + width = this->width(); + height = this->height(); + } + } + else + { + width = SkIntToScalar(fData.fBitmap->width()); + height = SkIntToScalar(fData.fBitmap->height()); + } + bounds->set(0, 0, width, height); + return true; + } + return false; +} + +bool SkImageView::ensureUriIsLoaded() +{ + if (fData.fAnim) // test is valid for all union values + { + SkASSERT(fUriIsValid); + return true; + } + if (!fUriIsValid) + return false; + + // try to load the url + if (fUri.endsWith(".xml")) // assume it is screenplay + { + SkAnimator* anim = new SkAnimator; + + if (!anim->decodeURI(fUri.c_str())) + { + delete anim; + fUriIsValid = false; + return false; + } + anim->setHostEventSink(this); + + fData.fAnim = anim; + fDataIsAnim = true; + } + else // assume it is an image format + { + #if 0 + SkBitmap* bitmap = new SkBitmap; + + if (!SkImageDecoder::DecodeURL(fUri.c_str(), bitmap)) + { + delete bitmap; + fUriIsValid = false; + return false; + } + fData.fBitmap = bitmap; + fDataIsAnim = false; + #else + return false; + #endif + } + return true; +} + diff --git a/src/views/SkListView.cpp b/src/views/SkListView.cpp new file mode 100644 index 0000000000..27218a9d67 --- /dev/null +++ b/src/views/SkListView.cpp @@ -0,0 +1,895 @@ +#include "SkWidget.h" +#include "SkCanvas.h" +#include "SkEvent.h" +#include "SkKey.h" +#include "SkParsePaint.h" +#include "SkSystemEventTypes.h" + +#if 0 + +SkEvent* SkListSource::getEvent(int index) +{ + return nil; +} + +#include "SkOSFile.h" + +class SkDirListSource : public SkListSource { +public: + SkDirListSource(const char path[], const char suffix[], const char target[]) + : fPath(path), fSuffix(suffix), fTarget(target) + { + fCount = -1; + } + virtual int countRows() + { + if (fCount < 0) + { + fCount = 0; + fIter.reset(fPath.c_str(), fSuffix.c_str()); + while (fIter.next(nil)) + fCount += 1; + fIter.reset(fPath.c_str(), fSuffix.c_str()); + fIndex = 0; + } + return fCount; + } + virtual void getRow(int index, SkString* left, SkString* right) + { + (void)this->countRows(); + SkASSERT((unsigned)index < (unsigned)fCount); + + if (fIndex > index) + { + fIter.reset(fPath.c_str(), fSuffix.c_str()); + fIndex = 0; + } + + while (fIndex < index) + { + fIter.next(nil); + fIndex += 1; + } + + if (fIter.next(left)) + { + if (left) + left->remove(left->size() - fSuffix.size(), fSuffix.size()); + } + else + { + if (left) + left->reset(); + } + if (right) // only set to ">" if we know we're on a sub-directory + right->reset(); + + fIndex += 1; + } + virtual SkEvent* getEvent(int index) + { + SkASSERT((unsigned)index < (unsigned)fCount); + + SkEvent* evt = new SkEvent(); + SkString label; + + this->getRow(index, &label, nil); + evt->setString("name", label.c_str()); + + int c = fPath.c_str()[fPath.size() - 1]; + if (c != '/' && c != '\\') + label.prepend("/"); + label.prepend(fPath); + label.append(fSuffix); + evt->setString("path", label.c_str()); + evt->setS32("index", index); + evt->setS32("duration", 22); + evt->setType(fTarget); + return evt; + } + +private: + SkString fPath, fSuffix; + SkString fTarget; + SkOSFile::Iter fIter; + int fCount; + int fIndex; +}; + +SkListSource* SkListSource::CreateFromDir(const char path[], const char suffix[], const char target[]) +{ + return new SkDirListSource(path, suffix, target); +} + +////////////////////////////////////////////////////////////////// + +class SkDOMListSource : public SkListSource { +public: + enum Type { + kUnknown_Type, + kDir_Type, + kToggle_Type + }; + struct ItemRec { + SkString fLabel; + SkString fTail, fAltTail; + SkString fTarget; + Type fType; + }; + + SkDOMListSource(const SkDOM& dom, const SkDOM::Node* node) : fDirTail(">") + { + const SkDOM::Node* child = dom.getFirstChild(node, "item"); + int count = 0; + + while (child) + { + count += 1; + child = dom.getNextSibling(child, "item"); + } + + fCount = count; + fList = nil; + if (count) + { + ItemRec* rec = fList = new ItemRec[count]; + + child = dom.getFirstChild(node, "item"); + while (child) + { + rec->fLabel.set(dom.findAttr(child, "label")); + rec->fTail.set(dom.findAttr(child, "tail")); + rec->fAltTail.set(dom.findAttr(child, "alt-tail")); + rec->fTarget.set(dom.findAttr(child, "target")); + rec->fType = kUnknown_Type; + + int index = dom.findList(child, "type", "dir,toggle"); + if (index >= 0) + rec->fType = (Type)(index + 1); + + child = dom.getNextSibling(child, "item"); + rec += 1; + } + } + } + virtual ~SkDOMListSource() + { + delete[] fList; + } + virtual int countRows() + { + return fCount; + } + virtual void getRow(int index, SkString* left, SkString* right) + { + SkASSERT((unsigned)index < (unsigned)fCount); + + if (left) + *left = fList[index].fLabel; + if (right) + *right = fList[index].fType == kDir_Type ? fDirTail : fList[index].fTail; + } + virtual SkEvent* getEvent(int index) + { + SkASSERT((unsigned)index < (unsigned)fCount); + + if (fList[index].fType == kDir_Type) + { + SkEvent* evt = new SkEvent(); + evt->setType(fList[index].fTarget); + evt->setFast32(index); + return evt; + } + if (fList[index].fType == kToggle_Type) + fList[index].fTail.swap(fList[index].fAltTail); + + return nil; + } + +private: + int fCount; + ItemRec* fList; + SkString fDirTail; +}; + +SkListSource* SkListSource::CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node) +{ + return new SkDOMListSource(dom, node); +} + +////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// + +SkListView::SkListView(U32 flags) : SkWidgetView(flags) +{ + fSource = nil; + fScrollIndex = 0; + fCurrIndex = -1; + fRowHeight = SkIntToScalar(16); + fVisibleRowCount = 0; + fStrCache = nil; + + fPaint[kBG_Attr].setColor(0); + fPaint[kNormalText_Attr].setTextSize(SkIntToScalar(14)); + fPaint[kHiliteText_Attr].setTextSize(SkIntToScalar(14)); + fPaint[kHiliteText_Attr].setColor(SK_ColorWHITE); + fPaint[kHiliteCell_Attr].setColor(SK_ColorBLUE); +} + +SkListView::~SkListView() +{ + delete[] fStrCache; + fSource->safeUnref(); +} + +void SkListView::setRowHeight(SkScalar height) +{ + SkASSERT(height >= 0); + + if (fRowHeight != height) + { + fRowHeight = height; + this->inval(nil); + this->onSizeChange(); + } +} + +void SkListView::setSelection(int index) +{ + if (fCurrIndex != index) + { + this->invalSelection(); + fCurrIndex = index; + this->invalSelection(); + this->ensureSelectionIsVisible(); + + { + SkEvent evt; + evt.setType("listview-selection"); + evt.setFast32(index); + this->sendEventToParents(evt); + } + } +} + +void SkListView::moveSelectionUp() +{ + if (fSource) + { + int index = fCurrIndex; + if (index < 0) // no selection + index = fSource->countRows() - 1; + else + index = SkMax32(index - 1, 0); + this->setSelection(index); + } +} + +void SkListView::moveSelectionDown() +{ + if (fSource) + { + int index = fCurrIndex; + if (index < 0) // no selection + index = 0; + else + index = SkMin32(index + 1, fSource->countRows() - 1); + this->setSelection(index); + } +} + +void SkListView::invalSelection() +{ + SkRect r; + if (this->getRowRect(fCurrIndex, &r)) + this->inval(&r); +} + +void SkListView::ensureSelectionIsVisible() +{ + if (fSource == nil) + return; + + if ((unsigned)fCurrIndex < (unsigned)fSource->countRows()) + { + int index = this->logicalToVisualIndex(fCurrIndex); + + if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll + { + if (index < 0) // too high + fScrollIndex = fCurrIndex; + else + fScrollIndex = fCurrIndex - fVisibleRowCount + 1; + SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows()); + + this->dirtyStrCache(); + this->inval(nil); + } + } +} + +bool SkListView::getRowRect(int index, SkRect* r) const +{ + SkASSERT(r); + index = this->logicalToVisualIndex(index); + if (index >= 0) + { + SkScalar top = index * fRowHeight; + + if (top < this->height()) + { + if (r) + r->set(0, top, this->width(), top + fRowHeight); + return true; + } + } + return false; +} + +SkPaint& SkListView::paint(Attr attr) +{ + SkASSERT((unsigned)attr < kAttrCount); + return fPaint[attr]; +} + +SkListSource* SkListView::setListSource(SkListSource* src) +{ + if (fSource != src) + { + SkRefCnt_SafeAssign(fSource, src); + this->dirtyStrCache(); + this->ensureSelectionIsVisible(); + this->inval(nil); + } + return src; +} + +void SkListView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + canvas->drawPaint(fPaint[kBG_Attr]); + + int visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex); + if (visibleCount == 0) + return; + + this->ensureStrCache(visibleCount); + int currIndex = this->logicalToVisualIndex(fCurrIndex); + + if ((unsigned)currIndex < (unsigned)visibleCount) + { + SkAutoCanvasRestore restore(canvas, true); + SkRect r; + + canvas->translate(0, currIndex * fRowHeight); + (void)this->getRowRect(fScrollIndex, &r); + canvas->drawRect(r, fPaint[kHiliteCell_Attr]); + } + + SkPaint* p; + SkScalar y, x = SkIntToScalar(6); + SkScalar rite = this->width() - x; + + { + SkScalar ascent, descent; + fPaint[kNormalText_Attr].measureText(0, nil, &ascent, &descent); + y = SkScalarHalf(fRowHeight - descent + ascent) - ascent; + } + + for (int i = 0; i < visibleCount; i++) + { + if (i == currIndex) + p = &fPaint[kHiliteText_Attr]; + else + p = &fPaint[kNormalText_Attr]; + + p->setTextAlign(SkPaint::kLeft_Align); + canvas->drawText(fStrCache[i].c_str(), fStrCache[i].size(), x, y, *p); + p->setTextAlign(SkPaint::kRight_Align); + canvas->drawText(fStrCache[i + visibleCount].c_str(), fStrCache[i + visibleCount].size(), rite, y, *p); + canvas->translate(0, fRowHeight); + } +} + +void SkListView::onSizeChange() +{ + SkScalar count = SkScalarDiv(this->height(), fRowHeight); + int n = SkScalarFloor(count); + + // only want to show rows that are mostly visible + if (n == 0 || count - SkIntToScalar(n) > SK_Scalar1*75/100) + n += 1; + + if (fVisibleRowCount != n) + { + fVisibleRowCount = n; + this->ensureSelectionIsVisible(); + this->dirtyStrCache(); + } +} + +void SkListView::dirtyStrCache() +{ + if (fStrCache) + { + delete[] fStrCache; + fStrCache = nil; + } +} + +void SkListView::ensureStrCache(int count) +{ + if (fStrCache == nil) + { + fStrCache = new SkString[count << 1]; + + if (fSource) + for (int i = 0; i < count; i++) + fSource->getRow(i + fScrollIndex, &fStrCache[i], &fStrCache[i + count]); + } +} + +bool SkListView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Key)) + { + switch (evt.getFast32()) { + case kUp_SkKey: + this->moveSelectionUp(); + return true; + case kDown_SkKey: + this->moveSelectionDown(); + return true; + case kRight_SkKey: + case kOK_SkKey: + if (fSource && fCurrIndex >= 0) + { + SkEvent* evt = fSource->getEvent(fCurrIndex); + if (evt) + { + SkView* view = this->sendEventToParents(*evt); + delete evt; + return view != nil; + } + else // hack to make toggle work + { + this->dirtyStrCache(); + this->inval(nil); + } + } + break; + } + } + return this->INHERITED::onEvent(evt); +} + +void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + SkScalar x; + const SkDOM::Node* child; + + if (dom.findScalar(node, "row-height", &x)) + this->setRowHeight(x); + + if ((child = dom.getFirstChild(node, "hilite-paint")) != nil) + SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child); + + // look for a listsource + { + SkListSource* src = nil; + + if ((child = dom.getFirstChild(node, "file-listsource")) != nil) + { + const char* path = dom.findAttr(child, "path"); + if (path) + src = SkListSource::CreateFromDir( path, + dom.findAttr(child, "filter"), + dom.findAttr(child, "target")); + } + else if ((child = dom.getFirstChild(node, "xml-listsource")) != nil) + { + src = SkListSource::CreateFromDOM(dom, child); + } + + if (src) + { + this->setListSource(src)->unref(); + this->setSelection(0); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkImageDecoder.h" +#include "SkShader.h" + +class SkScrollBarView : public SkView { +public: + SkScrollBarView(const char bg[], const char fg[]) + { + fBGRef = SkBitmapRef::Decode(bg, true); + fFGRef = SkBitmapRef::Decode(fg, true); + + if (fBGRef) + this->setWidth(SkIntToScalar(fBGRef->bitmap().width())); + } + ~SkScrollBarView() + { + delete fBGRef; + delete fFGRef; + } +protected: + virtual void onDraw(SkCanvas* canvas) + { + if (fBGRef == nil) return; + + SkPaint paint; + + SkShader* shader = SkShader::CreateBitmapShader(fBGRef->bitmap(), false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode); + paint.setShader(shader)->unref(); + + canvas->drawPaint(paint); + } +private: + SkBitmapRef* fBGRef, *fFGRef; +}; + +SkGridView::SkGridView(U32 flags) : SkWidgetView(flags) +{ + fSource = nil; + fCurrIndex = -1; + fVisibleCount.set(0, 0); + + fPaint[kBG_Attr].setColor(SK_ColorWHITE); + fPaint[kHiliteCell_Attr].setColor(SK_ColorYELLOW); + fPaint[kHiliteCell_Attr].setStyle(SkPaint::kStroke_Style); + fPaint[kHiliteCell_Attr].setAntiAliasOn(true); + fPaint[kHiliteCell_Attr].setStrokeWidth(SK_Scalar1*3); + + fScrollBar = new SkScrollBarView("icons/scrollbarGrey.jpg", "icons/scrollbarBlue.jpg"); + this->attachChildToFront(fScrollBar)->unref(); + fScrollBar->setVisibleP(true); +} + +SkGridView::~SkGridView() +{ + fSource->safeUnref(); +} + +void SkGridView::getCellSize(SkPoint* size) const +{ + if (size) + *size = fCellSize; +} + +void SkGridView::setCellSize(SkScalar x, SkScalar y) +{ + SkASSERT(x >= 0 && y >= 0); + + if (!fCellSize.equals(x, y)) + { + fCellSize.set(x, y); + this->inval(nil); + } +} + +void SkGridView::setSelection(int index) +{ + if (fCurrIndex != index) + { + this->invalSelection(); + fCurrIndex = index; + this->invalSelection(); + this->ensureSelectionIsVisible(); + + // this generates the click + { + SkEvent evt; + evt.setType("listview-selection"); + evt.setFast32(index); + this->sendEventToParents(evt); + } + } +} + +void SkGridView::moveSelectionUp() +{ + if (fSource) + { + int index = fCurrIndex; + if (index < 0) // no selection + index = fSource->countRows() - 1; + else + index = SkMax32(index - 1, 0); + this->setSelection(index); + } +} + +void SkGridView::moveSelectionDown() +{ + if (fSource) + { + int index = fCurrIndex; + if (index < 0) // no selection + index = 0; + else + index = SkMin32(index + 1, fSource->countRows() - 1); + this->setSelection(index); + } +} + +void SkGridView::invalSelection() +{ + SkRect r; + if (this->getCellRect(fCurrIndex, &r)) + { + SkScalar inset = 0; + if (fPaint[kHiliteCell_Attr].getStyle() != SkPaint::kFill_Style) + inset += fPaint[kHiliteCell_Attr].getStrokeWidth() / 2; + if (fPaint[kHiliteCell_Attr].isAntiAliasOn()) + inset += SK_Scalar1; + r.inset(-inset, -inset); + this->inval(&r); + } +} + +void SkGridView::ensureSelectionIsVisible() +{ + if (fSource == nil) + return; +#if 0 + if ((unsigned)fCurrIndex < (unsigned)fSource->countRows()) + { + int index = this->logicalToVisualIndex(fCurrIndex); + + if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll + { + if (index < 0) // too high + fScrollIndex = fCurrIndex; + else + fScrollIndex = fCurrIndex - fVisibleRowCount + 1; + SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows()); + + this->dirtyStrCache(); + this->inval(nil); + } + } +#endif +} + +bool SkGridView::getCellRect(int index, SkRect* r) const +{ + if (fVisibleCount.fY == 0) + return false; + + index = this->logicalToVisualIndex(index); + if (index >= 0) + { + SkRect bounds; + int row = index / fVisibleCount.fY; + int col = index % fVisibleCount.fY; + + bounds.set(0, 0, fCellSize.fX, fCellSize.fY); + bounds.offset(col * (fCellSize.fX + SkIntToScalar(col > 0)), + row * (fCellSize.fY + SkIntToScalar(row > 0))); + + if (bounds.fTop < this->height()) + { + if (r) + *r = bounds; + return true; + } + } + return false; +} + +SkPaint& SkGridView::paint(Attr attr) +{ + SkASSERT((unsigned)attr < kAttrCount); + return fPaint[attr]; +} + +SkListSource* SkGridView::setListSource(SkListSource* src) +{ + if (fSource != src) + { + SkRefCnt_SafeAssign(fSource, src); + // this->dirtyStrCache(); + this->ensureSelectionIsVisible(); + this->inval(nil); + } + return src; +} + +#include "SkShader.h" + +static void copybits(SkCanvas* canvas, const SkBitmap& bm, const SkRect& dst, const SkPaint& paint) +{ + SkRect src; + SkMatrix matrix; + + src.set(0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())); + if (matrix.setRectToRect(src, dst)) + { + SkPaint p(paint); + SkShader* shader = SkShader::CreateBitmapShader(bm, false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode); + p.setShader(shader)->unref(); + + shader->setLocalMatrix(matrix); + canvas->drawRect(dst, p); + } +} + +#include "SkImageDecoder.h" + +void SkGridView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + canvas->drawPaint(fPaint[kBG_Attr]); + + if (fSource == nil) + return; + +#if 0 + int visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex); + if (visibleCount == 0) + return; + + this->ensureStrCache(visibleCount); + int currIndex = this->logicalToVisualIndex(fCurrIndex); +#endif + + SkPaint p; + for (int i = 0; i < fSource->countRows(); i++) + { + bool forced = false; + SkEvent* evt = fSource->getEvent(i); + SkASSERT(evt); + SkString path(evt->findString("path")); + delete evt; + + SkBitmapRef* bmr = SkBitmapRef::Decode(path.c_str(), false); + if (bmr == nil) + { + bmr = SkBitmapRef::Decode(path.c_str(), true); + if (bmr) + forced = true; + } + + if (bmr) + { + SkAutoTDelete autoRef(bmr); + SkRect r; + if (!this->getCellRect(i, &r)) + break; + copybits(canvas, bmr->bitmap(), r, p); + } + // only draw one forced bitmap at a time + if (forced) + { + this->inval(nil); // could inval only the remaining visible cells... + break; + } + } + + // draw the hilite + { + SkRect r; + if (fCurrIndex >= 0 && this->getCellRect(fCurrIndex, &r)) + canvas->drawRect(r, fPaint[kHiliteCell_Attr]); + } +} + +static int check_count(int n, SkScalar s) +{ + // only want to show cells that are mostly visible + if (n == 0 || s - SkIntToScalar(n) > SK_Scalar1*75/100) + n += 1; + return n; +} + +void SkGridView::onSizeChange() +{ + fScrollBar->setHeight(this->height()); + fScrollBar->setLoc(this->locX() + this->width() - fScrollBar->width(), 0); + + if (fCellSize.equals(0, 0)) + { + fVisibleCount.set(0, 0); + return; + } + + SkScalar rows = SkScalarDiv(this->height(), fCellSize.fY); + SkScalar cols = SkScalarDiv(this->width(), fCellSize.fX); + int y = SkScalarFloor(rows); + int x = SkScalarFloor(cols); + + y = check_count(y, rows); + x = check_count(x, cols); + + if (!fVisibleCount.equals(x, y)) + { + fVisibleCount.set(x, y); + this->ensureSelectionIsVisible(); + // this->dirtyStrCache(); + } +} + +bool SkGridView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Key)) + { + switch (evt.getFast32()) { + case kUp_SkKey: + this->moveSelectionUp(); + return true; + case kDown_SkKey: + this->moveSelectionDown(); + return true; + case kRight_SkKey: + case kOK_SkKey: + if (fSource && fCurrIndex >= 0) + { + SkEvent* evt = fSource->getEvent(fCurrIndex); + if (evt) + { + // augment the event with our local rect + (void)this->getCellRect(fCurrIndex, (SkRect*)evt->setScalars("local-rect", 4, nil)); + + SkView* view = this->sendEventToParents(*evt); + delete evt; + return view != nil; + } + } + break; + } + } + return this->INHERITED::onEvent(evt); +} + +void SkGridView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + SkScalar x[2]; + const SkDOM::Node* child; + + if (dom.findScalars(node, "cell-size", x, 2)) + this->setCellSize(x[0], x[1]); + + if ((child = dom.getFirstChild(node, "hilite-paint")) != nil) + SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child); + + // look for a listsource + { + SkListSource* src = nil; + + if ((child = dom.getFirstChild(node, "file-listsource")) != nil) + { + const char* path = dom.findAttr(child, "path"); + if (path) + src = SkListSource::CreateFromDir( path, + dom.findAttr(child, "filter"), + dom.findAttr(child, "target")); + } + else if ((child = dom.getFirstChild(node, "xml-listsource")) != nil) + { + src = SkListSource::CreateFromDOM(dom, child); + } + + if (src) + { + this->setListSource(src)->unref(); + this->setSelection(0); + } + } + this->onSizeChange(); +} + +#endif diff --git a/src/views/SkListWidget.cpp b/src/views/SkListWidget.cpp new file mode 100644 index 0000000000..82e5d7813e --- /dev/null +++ b/src/views/SkListWidget.cpp @@ -0,0 +1,623 @@ +#include "SkWidgetViews.h" + +#include "SkAnimator.h" +#include "SkScrollBarView.h" + +extern void init_skin_anim(const char name[], SkAnimator*); + +struct SkListView::BindingRec { + SkString fSlotName; + int fFieldIndex; +}; + +SkListView::SkListView() +{ + fSource = nil; // our list-source + fScrollBar = nil; + fAnims = nil; // array of animators[fVisibleRowCount] + fBindings = nil; // our fields->slot array + fBindingCount = 0; // number of entries in fSlots array + fScrollIndex = 0; // number of cells to skip before first visible cell + fCurrIndex = -1; // index of "selected" cell + fVisibleRowCount = 0; // number of cells that can fit in our bounds + fAnimContentDirty = true; // true if fAnims[] have their correct content + fAnimFocusDirty = true; + + fHeights[kNormal_Height] = SkIntToScalar(16); + fHeights[kSelected_Height] = SkIntToScalar(16); + + this->setFlags(this->getFlags() | kFocusable_Mask); +} + +SkListView::~SkListView() +{ + fScrollBar->safeUnref(); + fSource->safeUnref(); + delete[] fAnims; + delete[] fBindings; +} + +void SkListView::setHasScrollBar(bool hasSB) +{ + if (hasSB != this->hasScrollBar()) + { + if (hasSB) + { + SkASSERT(fScrollBar == nil); + fScrollBar = (SkScrollBarView*)SkWidgetFactory(kScroll_WidgetEnum); + fScrollBar->setVisibleP(true); + this->attachChildToFront(fScrollBar); + fScrollBar->setHeight(this->height()); // assume it auto-sets its width + // fScrollBar->setLoc(this->getContentWidth(), 0); + fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0); + } + else + { + SkASSERT(fScrollBar); + fScrollBar->detachFromParent(); + fScrollBar->unref(); + fScrollBar = nil; + } + this->dirtyCache(kAnimContent_DirtyFlag); + } +} + +void SkListView::setSelection(int index) +{ + if (fCurrIndex != index) + { + fAnimFocusDirty = true; + this->inval(nil); + + this->invalSelection(); + fCurrIndex = index; + this->invalSelection(); + this->ensureSelectionIsVisible(); + } +} + +bool SkListView::moveSelectionUp() +{ + if (fSource) + { + int index = fCurrIndex; + if (index < 0) // no selection + index = fSource->countRecords() - 1; + else + index = SkMax32(index - 1, 0); + + if (fCurrIndex != index) + { + this->setSelection(index); + return true; + } + } + return false; +} + +bool SkListView::moveSelectionDown() +{ + if (fSource) + { + int index = fCurrIndex; + if (index < 0) // no selection + index = 0; + else + index = SkMin32(index + 1, fSource->countRecords() - 1); + + if (fCurrIndex != index) + { + this->setSelection(index); + return true; + } + } + return false; +} + +void SkListView::invalSelection() +{ + SkRect r; + if (this->getRowRect(fCurrIndex, &r)) + this->inval(&r); +} + +void SkListView::ensureSelectionIsVisible() +{ + if (fSource && (unsigned)fCurrIndex < (unsigned)fSource->countRecords()) + { + int index = this->logicalToVisualIndex(fCurrIndex); + + if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll + { + int newIndex; + + if (index < 0) // too high + newIndex = fCurrIndex; + else + newIndex = fCurrIndex - fVisibleRowCount + 1; + SkASSERT((unsigned)newIndex < (unsigned)fSource->countRecords()); + this->inval(nil); + + if (fScrollIndex != newIndex) + { + fScrollIndex = newIndex; + if (fScrollBar) + fScrollBar->setStart(newIndex); + this->dirtyCache(kAnimContent_DirtyFlag); + } + } + } +} + +SkScalar SkListView::getContentWidth() const +{ + SkScalar width = this->width(); + + if (fScrollBar) + { + width -= fScrollBar->width(); + if (width < 0) + width = 0; + } + return width; +} + +bool SkListView::getRowRect(int index, SkRect* r) const +{ + SkASSERT(r); + + index = this->logicalToVisualIndex(index); + if (index >= 0) + { + int selection = this->logicalToVisualIndex(fCurrIndex); + + SkScalar height = fHeights[index == selection ? kSelected_Height : kNormal_Height]; + SkScalar top = index * fHeights[kNormal_Height]; + + if (index > selection && selection >= 0) + top += fHeights[kSelected_Height] - fHeights[kNormal_Height]; + + if (top < this->height()) + { + if (r) + r->set(0, top, this->getContentWidth(), top + height); + return true; + } + } + return false; +} + +SkListSource* SkListView::setListSource(SkListSource* src) +{ + if (fSource != src) + { + SkRefCnt_SafeAssign(fSource, src); + this->ensureSelectionIsVisible(); + this->inval(nil); + + if (fScrollBar) + fScrollBar->setTotal(fSource->countRecords()); + } + return src; +} + +void SkListView::dirtyCache(unsigned dirtyFlags) +{ + if (dirtyFlags & kAnimCount_DirtyFlag) + { + delete fAnims; + fAnims = nil; + fAnimContentDirty = true; + fAnimFocusDirty = true; + } + if (dirtyFlags & kAnimContent_DirtyFlag) + { + if (!fAnimContentDirty) + { + this->inval(nil); + fAnimContentDirty = true; + } + fAnimFocusDirty = true; + } +} + +bool SkListView::ensureCache() +{ + if (fSkinName.size() == 0) + return false; + + if (fAnims == nil) + { + int n = SkMax32(1, fVisibleRowCount); + + SkASSERT(fAnimContentDirty); + fAnims = new SkAnimator[n]; + for (int i = 0; i < n; i++) + { + fAnims[i].setHostEventSink(this); + init_skin_anim(fSkinName.c_str(), &fAnims[i]); + } + + fHeights[kNormal_Height] = fAnims[0].getScalar("idleHeight", "value"); + fHeights[kSelected_Height] = fAnims[0].getScalar("focusedHeight", "value"); + + fAnimFocusDirty = true; + } + + if (fAnimContentDirty && fSource) + { + fAnimContentDirty = false; + + SkString str; + SkEvent evt("user"); + evt.setString("id", "setFields"); + evt.setS32("rowCount", fVisibleRowCount); + + SkEvent dimEvt("user"); + dimEvt.setString("id", "setDim"); + dimEvt.setScalar("dimX", this->getContentWidth()); + dimEvt.setScalar("dimY", this->height()); + + for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++) + { + evt.setS32("relativeIndex", i - fScrollIndex); + for (int j = 0; j < fBindingCount; j++) + { + fSource->getRecord(i, fBindings[j].fFieldIndex, &str); +//SkDEBUGF(("getRecord(%d,%d,%s) slot(%s)\n", i, fBindings[j].fFieldIndex, str.c_str(), fBindings[j].fSlotName.c_str())); + evt.setString(fBindings[j].fSlotName.c_str(), str.c_str()); + } + (void)fAnims[i % fVisibleRowCount].doUserEvent(evt); + (void)fAnims[i % fVisibleRowCount].doUserEvent(dimEvt); + } + fAnimFocusDirty = true; + } + + if (fAnimFocusDirty) + { +//SkDEBUGF(("service fAnimFocusDirty\n")); + fAnimFocusDirty = false; + + SkEvent focusEvt("user"); + focusEvt.setString("id", "setFocus"); + + for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++) + { + focusEvt.setS32("FOCUS", i == fCurrIndex); + (void)fAnims[i % fVisibleRowCount].doUserEvent(focusEvt); + } + } + + return true; +} + +void SkListView::ensureVisibleRowCount() +{ + SkScalar height = this->height(); + int n = 0; + + if (height > 0) + { + n = 1; + height -= fHeights[kSelected_Height]; + if (height > 0) + { + SkScalar count = SkScalarDiv(height, fHeights[kNormal_Height]); + n += SkScalarFloor(count); + if (count - SkIntToScalar(n) > SK_Scalar1*3/4) + n += 1; + + // SkDebugf("count %g, n %d\n", count/65536., n); + } + } + + if (fVisibleRowCount != n) + { + if (fScrollBar) + fScrollBar->setShown(n); + + fVisibleRowCount = n; + this->ensureSelectionIsVisible(); + this->dirtyCache(kAnimCount_DirtyFlag | kAnimContent_DirtyFlag); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkSystemEventTypes.h" +#include "SkTime.h" + +void SkListView::onSizeChange() +{ + this->INHERITED::onSizeChange(); + + if (fScrollBar) + fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0); + + this->ensureVisibleRowCount(); +} + +void SkListView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + this->ensureVisibleRowCount(); + + int visibleCount = SkMin32(fVisibleRowCount, fSource->countRecords() - fScrollIndex); + if (visibleCount == 0 || !this->ensureCache()) + return; + +//SkDebugf("visibleCount %d scrollIndex %d currIndex %d\n", visibleCount, fScrollIndex, fCurrIndex); + + SkAutoCanvasRestore ar(canvas, true); + SkMSec now = SkTime::GetMSecs(); + SkRect bounds; + + bounds.fLeft = 0; + bounds.fRight = this->getContentWidth(); + bounds.fBottom = 0; + // assign bounds.fTop inside the loop + + // hack to reveal our bounds for debugging + if (this->hasFocus()) + canvas->drawARGB(0x11, 0, 0, 0xFF); + else + canvas->drawARGB(0x11, 0x88, 0x88, 0x88); + + for (int i = fScrollIndex; i < fScrollIndex + visibleCount; i++) + { + SkPaint paint; + SkScalar height = fHeights[i == fCurrIndex ? kSelected_Height : kNormal_Height]; + + bounds.fTop = bounds.fBottom; + bounds.fBottom += height; + + canvas->save(); + if (fAnims[i % fVisibleRowCount].draw(canvas, &paint, now) != SkAnimator::kNotDifferent) + this->inval(&bounds); + canvas->restore(); + + canvas->translate(0, height); + } +} + +bool SkListView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Key)) + { + switch (evt.getFast32()) { + case kUp_SkKey: + return this->moveSelectionUp(); + case kDown_SkKey: + return this->moveSelectionDown(); + case kRight_SkKey: + case kOK_SkKey: + this->postWidgetEvent(); + return true; + default: + break; + } + } + return this->INHERITED::onEvent(evt); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static const char gListViewEventSlot[] = "sk-listview-slot-name"; + +/*virtual*/ bool SkListView::onPrepareWidgetEvent(SkEvent* evt) +{ + if (fSource && fCurrIndex >= 0 && this->INHERITED::onPrepareWidgetEvent(evt) && + fSource->prepareWidgetEvent(evt, fCurrIndex)) + { + evt->setS32(gListViewEventSlot, fCurrIndex); + return true; + } + return false; +} + +int SkListView::GetWidgetEventListIndex(const SkEvent& evt) +{ + int32_t index; + + return evt.findS32(gListViewEventSlot, &index) ? index : -1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + { + bool hasScrollBar; + if (dom.findBool(node, "scrollBar", &hasScrollBar)) + this->setHasScrollBar(hasScrollBar); + } + + const SkDOM::Node* child; + + if ((child = dom.getFirstChild(node, "bindings")) != nil) + { + delete[] fBindings; + fBindings = nil; + fBindingCount = 0; + + SkListSource* listSrc = SkListSource::Factory(dom.findAttr(child, "data-fields")); + SkASSERT(listSrc); + fSkinName.set(dom.findAttr(child, "skin-slots")); + SkASSERT(fSkinName.size()); + + this->setListSource(listSrc)->unref(); + + int count = dom.countChildren(child, "bind"); + if (count > 0) + { + fBindings = new BindingRec[count]; + count = 0; // reuse this to count up to the number of valid bindings + + child = dom.getFirstChild(child, "bind"); + SkASSERT(child); + do { + const char* fieldName = dom.findAttr(child, "field"); + const char* slotName = dom.findAttr(child, "slot"); + if (fieldName && slotName) + { + fBindings[count].fFieldIndex = listSrc->findFieldIndex(fieldName); + if (fBindings[count].fFieldIndex >= 0) + fBindings[count++].fSlotName.set(slotName); + } + } while ((child = dom.getNextSibling(child, "bind")) != nil); + + fBindingCount = SkToU16(count); + if (count == 0) + { + SkDEBUGF(("SkListView::onInflate: no valid elements in \n")); + delete[] fBindings; + } + } + this->dirtyCache(kAnimCount_DirtyFlag); + this->setSelection(0); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////// + +class SkXMLListSource : public SkListSource { +public: + SkXMLListSource(const char doc[], size_t len); + virtual ~SkXMLListSource() + { + delete[] fFields; + delete[] fRecords; + } + + virtual int countFields() { return fFieldCount; } + virtual void getFieldName(int index, SkString* field) + { + SkASSERT((unsigned)index < (unsigned)fFieldCount); + if (field) + *field = fFields[index]; + } + virtual int findFieldIndex(const char field[]) + { + for (int i = 0; i < fFieldCount; i++) + if (fFields[i].equals(field)) + return i; + return -1; + } + + virtual int countRecords() { return fRecordCount; } + virtual void getRecord(int rowIndex, int fieldIndex, SkString* data) + { + SkASSERT((unsigned)rowIndex < (unsigned)fRecordCount); + SkASSERT((unsigned)fieldIndex < (unsigned)fFieldCount); + if (data) + *data = fRecords[rowIndex * fFieldCount + fieldIndex]; + } + + virtual bool prepareWidgetEvent(SkEvent* evt, int rowIndex) + { + // hack, for testing right now. Need the xml to tell us what to jam in and where + SkString data; + + this->getRecord(rowIndex, 0, &data); + evt->setString("xml-listsource", data.c_str()); + return true; + } + +private: + SkString* fFields; // [fFieldCount] + SkString* fRecords; // [fRecordCount][fFieldCount] + int fFieldCount, fRecordCount; +}; + +#include "SkDOM.h" + +SkXMLListSource::SkXMLListSource(const char doc[], size_t len) +{ + fFieldCount = fRecordCount = 0; + fFields = fRecords = nil; + + SkDOM dom; + + const SkDOM::Node* node = dom.build(doc, len); + SkASSERT(node); + const SkDOM::Node* child; + + child = dom.getFirstChild(node, "fields"); + if (child) + { + fFieldCount = dom.countChildren(child, "field"); + fFields = new SkString[fFieldCount]; + + int n = 0; + child = dom.getFirstChild(child, "field"); + while (child) + { + fFields[n].set(dom.findAttr(child, "name")); + child = dom.getNextSibling(child, "field"); + n += 1; + } + SkASSERT(n == fFieldCount); + } + + child = dom.getFirstChild(node, "records"); + if (child) + { + fRecordCount = dom.countChildren(child, "record"); + fRecords = new SkString[fRecordCount * fFieldCount]; + + int n = 0; + child = dom.getFirstChild(child, "record"); + while (child) + { + for (int i = 0; i < fFieldCount; i++) + fRecords[n * fFieldCount + i].set(dom.findAttr(child, fFields[i].c_str())); + child = dom.getNextSibling(child, "record"); + n += 1; + } + SkASSERT(n == fRecordCount); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +SkListSource* SkListSource::Factory(const char name[]) +{ + static const char gDoc[] = + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; + +//SkDebugf("doc size %d\n", sizeof(gDoc)-1); + return new SkXMLListSource(gDoc, sizeof(gDoc) - 1); +} + + + diff --git a/src/views/SkMetaData.cpp b/src/views/SkMetaData.cpp new file mode 100644 index 0000000000..c366bd3fee --- /dev/null +++ b/src/views/SkMetaData.cpp @@ -0,0 +1,405 @@ +/* libs/graphics/views/SkMetaData.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkMetaData.h" + +SkMetaData::SkMetaData() : fRec(NULL) +{ +} + +SkMetaData::SkMetaData(const SkMetaData& src) : fRec(NULL) +{ + *this = src; +} + +SkMetaData::~SkMetaData() +{ + this->reset(); +} + +void SkMetaData::reset() +{ + Rec* rec = fRec; + while (rec) + { + Rec* next = rec->fNext; + Rec::Free(rec); + rec = next; + } + fRec = NULL; +} + +SkMetaData& SkMetaData::operator=(const SkMetaData& src) +{ + this->reset(); + + const Rec* rec = src.fRec; + while (rec) + { + this->set(rec->name(), rec->data(), rec->fDataLen, (Type)rec->fType, rec->fDataCount); + rec = rec->fNext; + } + return *this; +} + +void SkMetaData::setS32(const char name[], int32_t value) +{ + (void)this->set(name, &value, sizeof(int32_t), kS32_Type, 1); +} + +void SkMetaData::setScalar(const char name[], SkScalar value) +{ + (void)this->set(name, &value, sizeof(SkScalar), kScalar_Type, 1); +} + +SkScalar* SkMetaData::setScalars(const char name[], int count, const SkScalar values[]) +{ + SkASSERT(count > 0); + if (count > 0) + return (SkScalar*)this->set(name, values, sizeof(SkScalar), kScalar_Type, count); + return NULL; +} + +void SkMetaData::setString(const char name[], const char value[]) +{ + (void)this->set(name, value, sizeof(char), kString_Type, strlen(value) + 1); +} + +void SkMetaData::setPtr(const char name[], void* ptr) +{ + (void)this->set(name, &ptr, sizeof(void*), kPtr_Type, 1); +} + +void SkMetaData::setBool(const char name[], bool value) +{ + (void)this->set(name, &value, sizeof(bool), kBool_Type, 1); +} + +void* SkMetaData::set(const char name[], const void* data, size_t dataSize, Type type, int count) +{ + SkASSERT(name); + SkASSERT(dataSize); + SkASSERT(count > 0); + + (void)this->remove(name, type); + + size_t len = strlen(name); + Rec* rec = Rec::Alloc(sizeof(Rec) + dataSize * count + len + 1); + +#ifndef SK_DEBUG + rec->fType = SkToU8(type); +#else + rec->fType = type; +#endif + rec->fDataLen = SkToU8(dataSize); + rec->fDataCount = SkToU16(count); + if (data) + memcpy(rec->data(), data, dataSize * count); + memcpy(rec->name(), name, len + 1); + +#ifdef SK_DEBUG + rec->fName = rec->name(); + switch (type) { + case kS32_Type: + rec->fData.fS32 = *(const int32_t*)rec->data(); + break; + case kScalar_Type: + rec->fData.fScalar = *(const SkScalar*)rec->data(); + break; + case kString_Type: + rec->fData.fString = (const char*)rec->data(); + break; + case kPtr_Type: + rec->fData.fPtr = *(void**)rec->data(); + break; + case kBool_Type: + rec->fData.fBool = *(const bool*)rec->data(); + break; + default: + SkASSERT(!"bad type"); + break; + } +#endif + + rec->fNext = fRec; + fRec = rec; + return rec->data(); +} + +bool SkMetaData::findS32(const char name[], int32_t* value) const +{ + const Rec* rec = this->find(name, kS32_Type); + if (rec) + { + SkASSERT(rec->fDataCount == 1); + if (value) + *value = *(const int32_t*)rec->data(); + return true; + } + return false; +} + +bool SkMetaData::findScalar(const char name[], SkScalar* value) const +{ + const Rec* rec = this->find(name, kScalar_Type); + if (rec) + { + SkASSERT(rec->fDataCount == 1); + if (value) + *value = *(const SkScalar*)rec->data(); + return true; + } + return false; +} + +const SkScalar* SkMetaData::findScalars(const char name[], int* count, SkScalar values[]) const +{ + const Rec* rec = this->find(name, kScalar_Type); + if (rec) + { + if (count) + *count = rec->fDataCount; + if (values) + memcpy(values, rec->data(), rec->fDataCount * rec->fDataLen); + return (const SkScalar*)rec->data(); + } + return NULL; +} + +bool SkMetaData::findPtr(const char name[], void** value) const +{ + const Rec* rec = this->find(name, kPtr_Type); + if (rec) + { + SkASSERT(rec->fDataCount == 1); + if (value) + *value = *(void**)rec->data(); + return true; + } + return false; +} + +const char* SkMetaData::findString(const char name[]) const +{ + const Rec* rec = this->find(name, kString_Type); + SkASSERT(rec == NULL || rec->fDataLen == sizeof(char)); + return rec ? (const char*)rec->data() : NULL; +} + +bool SkMetaData::findBool(const char name[], bool* value) const +{ + const Rec* rec = this->find(name, kBool_Type); + if (rec) + { + SkASSERT(rec->fDataCount == 1); + if (value) + *value = *(const bool*)rec->data(); + return true; + } + return false; +} + +const SkMetaData::Rec* SkMetaData::find(const char name[], Type type) const +{ + const Rec* rec = fRec; + while (rec) + { + if (rec->fType == type && !strcmp(rec->name(), name)) + return rec; + rec = rec->fNext; + } + return NULL; +} + +bool SkMetaData::remove(const char name[], Type type) +{ + Rec* rec = fRec; + Rec* prev = NULL; + while (rec) + { + Rec* next = rec->fNext; + if (rec->fType == type && !strcmp(rec->name(), name)) + { + if (prev) + prev->fNext = next; + else + fRec = next; + Rec::Free(rec); + return true; + } + prev = rec; + rec = next; + } + return false; +} + +bool SkMetaData::removeS32(const char name[]) +{ + return this->remove(name, kS32_Type); +} + +bool SkMetaData::removeScalar(const char name[]) +{ + return this->remove(name, kScalar_Type); +} + +bool SkMetaData::removeString(const char name[]) +{ + return this->remove(name, kString_Type); +} + +bool SkMetaData::removePtr(const char name[]) +{ + return this->remove(name, kPtr_Type); +} + +bool SkMetaData::removeBool(const char name[]) +{ + return this->remove(name, kBool_Type); +} + +/////////////////////////////////////////////////////////////////////////////////// + +SkMetaData::Iter::Iter(const SkMetaData& metadata) +{ + fRec = metadata.fRec; +} + +void SkMetaData::Iter::reset(const SkMetaData& metadata) +{ + fRec = metadata.fRec; +} + +const char* SkMetaData::Iter::next(SkMetaData::Type* t, int* count) +{ + const char* name = NULL; + + if (fRec) + { + if (t) + *t = (SkMetaData::Type)fRec->fType; + if (count) + *count = fRec->fDataCount; + name = fRec->name(); + + fRec = fRec->fNext; + } + return name; +} + +/////////////////////////////////////////////////////////////////////////////////// + +SkMetaData::Rec* SkMetaData::Rec::Alloc(size_t size) +{ + return (Rec*)sk_malloc_throw(size); +} + +void SkMetaData::Rec::Free(Rec* rec) +{ + sk_free(rec); +} + +/////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkMetaData::UnitTest() +{ +#ifdef SK_SUPPORT_UNITTEST + SkMetaData m1; + + SkASSERT(!m1.findS32("int")); + SkASSERT(!m1.findScalar("scalar")); + SkASSERT(!m1.findString("hello")); + SkASSERT(!m1.removeS32("int")); + SkASSERT(!m1.removeScalar("scalar")); + SkASSERT(!m1.removeString("hello")); + SkASSERT(!m1.removeString("true")); + SkASSERT(!m1.removeString("false")); + + m1.setS32("int", 12345); + m1.setScalar("scalar", SK_Scalar1 * 42); + m1.setString("hello", "world"); + m1.setPtr("ptr", &m1); + m1.setBool("true", true); + m1.setBool("false", false); + + int32_t n; + SkScalar s; + + m1.setScalar("scalar", SK_Scalar1/2); + + SkASSERT(m1.findS32("int", &n) && n == 12345); + SkASSERT(m1.findScalar("scalar", &s) && s == SK_Scalar1/2); + SkASSERT(!strcmp(m1.findString("hello"), "world")); + SkASSERT(m1.hasBool("true", true)); + SkASSERT(m1.hasBool("false", false)); + + Iter iter(m1); + const char* name; + + static const struct { + const char* fName; + SkMetaData::Type fType; + int fCount; + } gElems[] = { + { "int", SkMetaData::kS32_Type, 1 }, + { "scalar", SkMetaData::kScalar_Type, 1 }, + { "ptr", SkMetaData::kPtr_Type, 1 }, + { "hello", SkMetaData::kString_Type, sizeof("world") }, + { "true", SkMetaData::kBool_Type, 1 }, + { "false", SkMetaData::kBool_Type, 1 } + }; + + int loop = 0; + int count; + SkMetaData::Type t; + while ((name = iter.next(&t, &count)) != NULL) + { + int match = 0; + for (unsigned i = 0; i < SK_ARRAY_COUNT(gElems); i++) + { + if (!strcmp(name, gElems[i].fName)) + { + match += 1; + SkASSERT(gElems[i].fType == t); + SkASSERT(gElems[i].fCount == count); + } + } + SkASSERT(match == 1); + loop += 1; + } + SkASSERT(loop == SK_ARRAY_COUNT(gElems)); + + SkASSERT(m1.removeS32("int")); + SkASSERT(m1.removeScalar("scalar")); + SkASSERT(m1.removeString("hello")); + SkASSERT(m1.removeBool("true")); + SkASSERT(m1.removeBool("false")); + + SkASSERT(!m1.findS32("int")); + SkASSERT(!m1.findScalar("scalar")); + SkASSERT(!m1.findString("hello")); + SkASSERT(!m1.findBool("true")); + SkASSERT(!m1.findBool("false")); +#endif +} + +#endif + + diff --git a/src/views/SkOSFile.cpp b/src/views/SkOSFile.cpp new file mode 100644 index 0000000000..c8eeeea405 --- /dev/null +++ b/src/views/SkOSFile.cpp @@ -0,0 +1,223 @@ +#include "SkOSFile.h" + +#ifdef SK_BUILD_FOR_WIN + +static U16* concat_to_16(const char src[], const char suffix[]) +{ + size_t i, len = strlen(src); + size_t len2 = 3 + (suffix ? strlen(suffix) : 0); + U16* dst = (U16*)sk_malloc_throw((len + len2) * sizeof(U16)); + + for (i = 0; i < len; i++) + dst[i] = src[i]; + + if (i > 0 && dst[i-1] != '/') + dst[i++] = '/'; + dst[i++] = '*'; + + if (suffix) + { + while (*suffix) + dst[i++] = *suffix++; + } + dst[i] = 0; + SkASSERT(i + 1 <= len + len2); + + return dst; +} + +SkUTF16_Str::SkUTF16_Str(const char src[]) +{ + size_t len = strlen(src); + + fStr = (U16*)sk_malloc_throw((len + 1) * sizeof(U16)); + for (size_t i = 0; i < len; i++) + fStr[i] = src[i]; + fStr[i] = 0; +} + +//////////////////////////////////////////////////////////////////////////// + +SkOSFile::Iter::Iter() : fHandle(0), fPath16(nil) +{ +} + +SkOSFile::Iter::Iter(const char path[], const char suffix[]) : fHandle(0), fPath16(nil) +{ + this->reset(path, suffix); +} + +SkOSFile::Iter::~Iter() +{ + sk_free(fPath16); + if (fHandle) + ::FindClose(fHandle); +} + +void SkOSFile::Iter::reset(const char path[], const char suffix[]) +{ + if (fHandle) + { + ::FindClose(fHandle); + fHandle = 0; + } + if (path == nil) + path = ""; + + sk_free(fPath16); + fPath16 = concat_to_16(path, suffix); +} + +static bool is_magic_dir(const U16 dir[]) +{ + // return true for "." and ".." + return dir[0] == '.' && (dir[1] == 0 || dir[1] == '.' && dir[2] == 0); +} + +static bool get_the_file(HANDLE handle, SkString* name, WIN32_FIND_DATAW* dataPtr, bool getDir) +{ + WIN32_FIND_DATAW data; + + if (dataPtr == nil) + { + if (::FindNextFileW(handle, &data)) + dataPtr = &data; + else + return false; + } + + for (;;) + { + if (getDir) + { + if ((dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !is_magic_dir(dataPtr->cFileName)) + break; + } + else + { + if (!(dataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + break; + } + if (!::FindNextFileW(handle, dataPtr)) + return false; + } + // if we get here, we've found a file/dir + if (name) + name->setUTF16(dataPtr->cFileName); + return true; +} + +bool SkOSFile::Iter::next(SkString* name, bool getDir) +{ + WIN32_FIND_DATAW data; + WIN32_FIND_DATAW* dataPtr = nil; + + if (fHandle == 0) // our first time + { + if (fPath16 == nil || *fPath16 == 0) // check for no path + return false; + + fHandle = ::FindFirstFileW(fPath16, &data); + if (fHandle != 0 && fHandle != (HANDLE)~0) + dataPtr = &data; + } + return fHandle != (HANDLE)~0 && get_the_file(fHandle, name, dataPtr, getDir); +} + +#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) + +#if 0 +OSStatus FSPathMakeRef ( + const UInt8 * path, + FSRef * ref, + Boolean * isDirectory +); +#endif + +SkOSFile::Iter::Iter() : fDIR(0) +{ +} + +SkOSFile::Iter::Iter(const char path[], const char suffix[]) : fDIR(0) +{ + this->reset(path, suffix); +} + +SkOSFile::Iter::~Iter() +{ + if (fDIR) + ::closedir(fDIR); +} + +void SkOSFile::Iter::reset(const char path[], const char suffix[]) +{ + if (fDIR) + { + ::closedir(fDIR); + fDIR = 0; + } + + fPath.set(path); + if (path) + { + fDIR = ::opendir(path); + fSuffix.set(suffix); + } + else + fSuffix.reset(); +} + +// returns true if suffix is empty, or if str ends with suffix +static bool issuffixfor(const SkString& suffix, const char str[]) +{ + size_t suffixLen = suffix.size(); + size_t strLen = strlen(str); + + return strLen >= suffixLen && + suffixLen == 0 || + memcmp(suffix.c_str(), str + strLen - suffixLen, suffixLen) == 0; +} + +#include + +bool SkOSFile::Iter::next(SkString* name, bool getDir) +{ + if (fDIR) + { + dirent* entry; + + while ((entry = ::readdir(fDIR)) != NULL) + { + struct stat s; + SkString str(fPath); + + if (!str.endsWith("/") && !str.endsWith("\\")) + str.append("/"); + str.append(entry->d_name); + + if (0 == stat(str.c_str(), &s)) + { + if (getDir) + { + if (s.st_mode & S_IFDIR) + break; + } + else + { + if (!(s.st_mode & S_IFDIR) && issuffixfor(fSuffix, entry->d_name)) + break; + } + } + } + if (entry) // we broke out with a file + { + if (name) + name->set(entry->d_name); + return true; + } + } + return false; +} + +#endif + diff --git a/src/views/SkOSMenu.cpp b/src/views/SkOSMenu.cpp new file mode 100644 index 0000000000..3760ddd5cb --- /dev/null +++ b/src/views/SkOSMenu.cpp @@ -0,0 +1,53 @@ +#include "SkOSMenu.h" + +static int gOSMenuCmd = 7000; + +SkOSMenu::SkOSMenu(const char title[]) +{ + fTitle = title; +} + +SkOSMenu::~SkOSMenu() +{ +} + +int SkOSMenu::countItems() const +{ + return fItems.count(); +} + +void SkOSMenu::appendItem(const char title[], const char eventType[], int32_t eventData) +{ + Item* item = fItems.append(); + + item->fTitle = title; + item->fEventType = eventType; + item->fEventData = eventData; + item->fOSCmd = ++gOSMenuCmd; +} + +SkEvent* SkOSMenu::createEvent(uint32_t os_cmd) +{ + const Item* iter = fItems.begin(); + const Item* stop = fItems.end(); + + while (iter < stop) + { + if (iter->fOSCmd == os_cmd) + { + SkEvent* evt = new SkEvent(iter->fEventType); + evt->setFast32(iter->fEventData); + return evt; + } + iter++; + } + return NULL; +} + +const char* SkOSMenu::getItem(int index, uint32_t* cmdID) const +{ + if (cmdID) + *cmdID = fItems[index].fOSCmd; + return fItems[index].fTitle; +} + diff --git a/src/views/SkOSSound.cpp b/src/views/SkOSSound.cpp new file mode 100644 index 0000000000..13cd037756 --- /dev/null +++ b/src/views/SkOSSound.cpp @@ -0,0 +1,343 @@ +#include "SkOSSound.h" + +#ifdef SK_BUILD_FOR_WIN + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +#include +#if defined _WIN32 && _MSC_VER >= 1300 // disable nameless struct/union +#pragma warning ( push ) +#pragma warning ( disable : 4201 ) +#endif +#include +#if defined _WIN32 && _MSC_VER >= 1300 +#pragma warning ( pop ) +#endif +#include + +class CWaveFile { +public: + BOOL Open(const char path[]); + void Close(); + + long Read(char* pData, long nLength); + + long GetLength() const {return m_nLength;} + WAVEFORMATEX* GetWaveFormat() {return (&m_Format);} + +protected: + FILE* m_pFile; + long m_nLength; + WAVEFORMATEX m_Format; + +private: + enum { + WF_OFFSET_FORMATTAG = 20, + WF_OFFSET_CHANNELS = 22, + WF_OFFSET_SAMPLESPERSEC = 24, + WF_OFFSET_AVGBYTESPERSEC = 28, + WF_OFFSET_BLOCKALIGN = 32, + WF_OFFSET_BITSPERSAMPLE = 34, + WF_OFFSET_DATASIZE = 40, + WF_OFFSET_DATA = 44, + WF_HEADER_SIZE = WF_OFFSET_DATA + }; +}; + +BOOL CWaveFile::Open(const char path[]) +{ + BYTE aHeader[WF_HEADER_SIZE]; + +/* hResInfo = FindResource (hInst, lpName, "WAVE"); + + if (hResInfo == NULL) + return FALSE; + + // Load the wave resource. + hRes = LoadResource (hInst, hResInfo); + + if (hRes == NULL) + return FALSE; + + // Lock the wave resource and play it. + lpRes = LockResource (0); +*/ + + + // open file +// m_pFile = _tfopen(szFileName, TEXT("rb")); + m_pFile = fopen(path, "rb"); + if (!m_pFile) { + return FALSE; + } + + // set file length + fseek(m_pFile, 0, SEEK_END); + m_nLength = ftell(m_pFile) - WF_HEADER_SIZE; + + // set the format attribute members + fseek(m_pFile, 0, SEEK_SET); + fread(aHeader, 1, WF_HEADER_SIZE, m_pFile); + m_Format.wFormatTag = *((WORD*) (aHeader + WF_OFFSET_FORMATTAG)); + m_Format.nChannels = *((WORD*) (aHeader + WF_OFFSET_CHANNELS)); + m_Format.nSamplesPerSec = *((DWORD*) (aHeader + WF_OFFSET_SAMPLESPERSEC)); + m_Format.nAvgBytesPerSec = *((DWORD*) (aHeader + WF_OFFSET_AVGBYTESPERSEC)); + m_Format.nBlockAlign = *((WORD*) (aHeader + WF_OFFSET_BLOCKALIGN)); + m_Format.wBitsPerSample = *((WORD*) (aHeader + WF_OFFSET_BITSPERSAMPLE)); + + return TRUE; +} + +void CWaveFile::Close() +{ + fclose(m_pFile); +} + +long CWaveFile::Read(char* pData, long nLength) +{ + return fread(pData, 1, nLength, m_pFile); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +struct SkOSSoundWave { + HWAVEOUT hwo; + WAVEHDR whdr; + DWORD dwOldVolume; + CWaveFile waveFile; + HANDLE hDoneEvent; +}; + +static SkOSSoundWave gWave; +static bool gWavePaused; +static U8 gVolume; +static bool gInited = false; + +static void init_wave() +{ + if (gInited == false) + { + gWave.hwo = nil; + gWavePaused = false; + gVolume = 0x80; + gInited = true; + } +} + +MMRESULT StartWave(const char path[], SkOSSoundWave* wave, U32 vol); +MMRESULT EndWave(SkOSSoundWave* wave); + +#define MAX_ERRMSG 256 + +//#include "SkOSFile.h" // for utf16 + +void SkOSSound::Play(const char path[]) +{ + init_wave(); + + if (gWave.hwo != nil) + SkOSSound::Stop(); + + U32 v32 = (gVolume << 8) | gVolume; // fill it out to 16bits + v32 |= v32 << 16; // set the left and right channels + + StartWave(path, &gWave, v32); + gWavePaused = false; +} + +bool SkOSSound::TogglePause() +{ + init_wave(); + + if (gWavePaused) + SkOSSound::Resume(); + else + SkOSSound::Pause(); + return !gWavePaused; +} + + +void SkOSSound::Pause() +{ + init_wave(); + + if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE)) + return; + waveOutPause(gWave.hwo); + gWavePaused = true; +} + +void SkOSSound::Resume() +{ + init_wave(); + + if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE)) + return; + waveOutRestart(gWave.hwo); + gWavePaused = false; +} + +void SkOSSound::Stop() +{ + init_wave(); + +// if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE)) + if (gWave.hwo == nil) + return; + waveOutReset(gWave.hwo); + EndWave(&gWave); + gWavePaused = false; + gWave.hwo = nil; +} + +U8 SkOSSound::GetVolume() +{ + init_wave(); + return gVolume; +} + +void SkOSSound::SetVolume(U8CPU vol) +{ + if ((int)vol < 0) + vol = 0; + else if (vol > 255) + vol = 255; + + init_wave(); + gVolume = SkToU8(vol); + + if (gWave.hwo) + { + unsigned long v32 = (vol << 8) | vol; // fill it out to 16bits + v32 |= v32 << 16; // set the left and right channels + waveOutSetVolume(gWave.hwo, v32); + } +} + +#if 0 +unsigned long SoundManager::GetPosition() +{ + if (fWave.hwo == nil) + return 0; + MMTIME time; + time.wType = TIME_MS; + if (waveOutGetPosition(fWave.hwo, &time, sizeof(time)) == MMSYSERR_NOERROR && + time.wType == TIME_MS) + { + return time.u.ms; + } + return 0; +} +#endif + +MMRESULT StartWave(const char path[], SkOSSoundWave* wave, U32 vol) +{ + HWAVEOUT hwo = nil; +// WAVEHDR whdr; + MMRESULT mmres = 0; +// CWaveFile waveFile; +// HANDLE hDoneEvent = wave.hDoneEvent = +// CreateEvent(NULL, FALSE, FALSE, TEXT("DONE_EVENT")); + UINT devId; +// DWORD dwOldVolume; + + // Open wave file + if (!wave->waveFile.Open(path)) { +// TCHAR szErrMsg[MAX_ERRMSG]; +// _stprintf(szErrMsg, TEXT("Unable to open file: %s\n"), szWavFile); +// MessageBox(NULL, szErrMsg, TEXT("File I/O Error"), MB_OK); + return MMSYSERR_NOERROR; + } + + // Open audio device + for (devId = 0; devId < waveOutGetNumDevs(); devId++) + { + mmres = waveOutOpen(&hwo, devId, wave->waveFile.GetWaveFormat(), 0, 0, CALLBACK_NULL); + if (mmres == MMSYSERR_NOERROR) + { + wave->hwo = hwo; + break; + } + } + if (mmres != MMSYSERR_NOERROR) + { + SkDEBUGCODE(SkDebugf("waveOutOpen(%s) -> %d\n", path, mmres);) + return mmres; + } + + // Set volume + mmres = waveOutGetVolume(hwo, &wave->dwOldVolume); + if (mmres != MMSYSERR_NOERROR) { + return mmres; + } + + waveOutSetVolume(hwo, vol); + if (mmres != MMSYSERR_NOERROR) { + return mmres; + } + + // Initialize wave header + ZeroMemory(&wave->whdr, sizeof(WAVEHDR)); + wave->whdr.lpData = new char[wave->waveFile.GetLength()]; + wave->whdr.dwBufferLength = wave->waveFile.GetLength(); + wave->whdr.dwUser = 0; + wave->whdr.dwFlags = 0; + wave->whdr.dwLoops = 0; + wave->whdr.dwBytesRecorded = 0; + wave->whdr.lpNext = 0; + wave->whdr.reserved = 0; + + // Play buffer + wave->waveFile.Read(wave->whdr.lpData, wave->whdr.dwBufferLength); + + mmres = waveOutPrepareHeader(hwo, &wave->whdr, sizeof(WAVEHDR)); + if (mmres != MMSYSERR_NOERROR) { + return mmres; + } + + mmres = waveOutWrite(hwo, &wave->whdr, sizeof(WAVEHDR)); +// if (mmres != MMSYSERR_NOERROR) { + return mmres; +// } +} + +#if 0 +void IdleWave(Wave& wave) +{ + // Wait for audio to finish playing + while (!(wave.whdr.dwFlags & WHDR_DONE)) { + WaitForSingleObject(wave.hDoneEvent, INFINITE); + } +} +#endif + +MMRESULT EndWave(SkOSSoundWave* wave) +{ + HWAVEOUT hwo = wave->hwo; + MMRESULT mmres; + // Clean up + mmres = waveOutUnprepareHeader(hwo, &wave->whdr, sizeof(WAVEHDR)); + if (mmres != MMSYSERR_NOERROR) { + return mmres; + } + + waveOutSetVolume(hwo, wave->dwOldVolume); + if (mmres != MMSYSERR_NOERROR) { + return mmres; + } + + mmres = waveOutClose(hwo); + if (mmres != MMSYSERR_NOERROR) { + return mmres; + } + + delete [] wave->whdr.lpData; + wave->waveFile.Close(); + + return MMSYSERR_NOERROR; +} + +#endif /* SK_BUILD_FOR_WIN */ + diff --git a/src/views/SkParsePaint.cpp b/src/views/SkParsePaint.cpp new file mode 100644 index 0000000000..7bf02440f1 --- /dev/null +++ b/src/views/SkParsePaint.cpp @@ -0,0 +1,103 @@ +#include "SkParsePaint.h" +#include "SkTSearch.h" +#include "SkParse.h" +#include "SkImageDecoder.h" +#include "SkGradientShader.h" + +static SkShader* inflate_shader(const SkDOM& dom, const SkDOM::Node* node) +{ + if ((node = dom.getFirstChild(node, "shader")) == nil) + return nil; + + const char* str; + + if (dom.hasAttr(node, "type", "linear-gradient")) + { + SkColor colors[2]; + SkPoint pts[2]; + + colors[0] = colors[1] = SK_ColorBLACK; // need to initialized the alpha to opaque, since FindColor doesn't set it + if ((str = dom.findAttr(node, "c0")) != nil && + SkParse::FindColor(str, &colors[0]) && + (str = dom.findAttr(node, "c1")) != nil && + SkParse::FindColor(str, &colors[1]) && + dom.findScalars(node, "p0", &pts[0].fX, 2) && + dom.findScalars(node, "p1", &pts[1].fX, 2)) + { + SkShader::TileMode mode = SkShader::kClamp_TileMode; + int index; + + if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0) + mode = (SkShader::TileMode)index; + return SkGradientShader::CreateLinear(pts, colors, nil, 2, mode); + } + } + else if (dom.hasAttr(node, "type", "bitmap")) + { + if ((str = dom.findAttr(node, "src")) == nil) + return nil; + + SkBitmap bm; + + if (SkImageDecoder::DecodeFile(str, &bm)) + { + SkShader::TileMode mode = SkShader::kRepeat_TileMode; + int index; + + if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0) + mode = (SkShader::TileMode)index; + + return SkShader::CreateBitmapShader(bm, mode, mode); + } + } + return nil; +} + +void SkPaint_Inflate(SkPaint* paint, const SkDOM& dom, const SkDOM::Node* node) +{ + SkASSERT(paint); + SkASSERT(&dom); + SkASSERT(node); + + SkScalar x; + + if (dom.findScalar(node, "stroke-width", &x)) + paint->setStrokeWidth(x); + if (dom.findScalar(node, "text-size", &x)) + paint->setTextSize(x); + + bool b; + + SkASSERT("legacy: use is-stroke" && !dom.findBool(node, "is-frame", &b)); + + if (dom.findBool(node, "is-stroke", &b)) + paint->setStyle(b ? SkPaint::kStroke_Style : SkPaint::kFill_Style); + if (dom.findBool(node, "is-antialias", &b)) + paint->setAntiAlias(b); + if (dom.findBool(node, "is-lineartext", &b)) + paint->setLinearText(b); + + const char* str = dom.findAttr(node, "color"); + if (str) + { + SkColor c = paint->getColor(); + if (SkParse::FindColor(str, &c)) + paint->setColor(c); + } + + // do this AFTER parsing for the color + if (dom.findScalar(node, "opacity", &x)) + { + x = SkMaxScalar(0, SkMinScalar(x, SK_Scalar1)); + paint->setAlpha(SkScalarRound(x * 255)); + } + + int index = dom.findList(node, "text-anchor", "left,center,right"); + if (index >= 0) + paint->setTextAlign((SkPaint::Align)index); + + SkShader* shader = inflate_shader(dom, node); + if (shader) + paint->setShader(shader)->unref(); +} + diff --git a/src/views/SkProgressBarView.cpp b/src/views/SkProgressBarView.cpp new file mode 100644 index 0000000000..21349a5500 --- /dev/null +++ b/src/views/SkProgressBarView.cpp @@ -0,0 +1,102 @@ +#include "SkProgressBarView.h" +#include "SkAnimator.h" +#include "SkWidgetViews.h" +#include "SkTime.h" +#include "SkSystemEventTypes.h" + +SkProgressBarView::SkProgressBarView() +{ + init_skin_anim(kProgress_SkinEnum, &fAnim); + fAnim.setHostEventSink(this); + fProgress = 0; + fMax = 100; + +} + +void SkProgressBarView::changeProgress(int diff) +{ + int newProg = fProgress + diff; + if (newProg > 0 && newProg < fMax) + this->setProgress(newProg); + //otherwise i'll just leave it as it is + //this implies that if a new max and progress are set, max must be set first +} + +/*virtual*/ void SkProgressBarView::onDraw(SkCanvas* canvas) +{ + SkPaint paint; + SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs()); + + if (diff == SkAnimator::kDifferent) + this->inval(nil); + else if (diff == SkAnimator::kPartiallyDifferent) + { + SkRect bounds; + fAnim.getInvalBounds(&bounds); + this->inval(&bounds); + } +} + +/*virtual*/ bool SkProgressBarView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Inval)) + { + this->inval(nil); + return true; + } + if (evt.isType("recommendDim")) + { + SkScalar height; + + if (evt.findScalar("y", &height)) + this->setHeight(height); + return true; + } + return this->INHERITED::onEvent(evt); +} + +/*virtual*/ void SkProgressBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + int32_t temp; + if (dom.findS32(node, "max", &temp)) + this->setMax(temp); + if (dom.findS32(node, "progress", &temp)) + this->setProgress(temp); +} + +/*virtual*/ void SkProgressBarView::onSizeChange() +{ + this->INHERITED::onSizeChange(); + SkEvent evt("user"); + evt.setString("id", "setDim"); + evt.setScalar("dimX", this->width()); + evt.setScalar("dimY", this->height()); + fAnim.doUserEvent(evt); +} + +void SkProgressBarView::reset() +{ + fProgress = 0; + SkEvent e("user"); + e.setString("id", "reset"); + fAnim.doUserEvent(e); +} + +void SkProgressBarView::setMax(int max) +{ + fMax = max; + SkEvent e("user"); + e.setString("id", "setMax"); + e.setS32("newMax", max); + fAnim.doUserEvent(e); +} + +void SkProgressBarView::setProgress(int progress) +{ + fProgress = progress; + SkEvent e("user"); + e.setString("id", "setProgress"); + e.setS32("newProgress", progress); + fAnim.doUserEvent(e); +} diff --git a/src/views/SkProgressView.cpp b/src/views/SkProgressView.cpp new file mode 100644 index 0000000000..a34c284361 --- /dev/null +++ b/src/views/SkProgressView.cpp @@ -0,0 +1,125 @@ +#include "SkWidget.h" +#include "SkCanvas.h" +#include "SkShader.h" +#include "SkInterpolator.h" +#include "SkTime.h" + +SkProgressView::SkProgressView(uint32_t flags) : SkView(flags), fOnShader(NULL), fOffShader(NULL) +{ + fValue = 0; + fMax = 0; + fInterp = NULL; + fDoInterp = false; +} + +SkProgressView::~SkProgressView() +{ + delete fInterp; + fOnShader->safeUnref(); + fOffShader->safeUnref(); +} + +void SkProgressView::setMax(U16CPU max) +{ + if (fMax != max) + { + fMax = SkToU16(max); + if (fValue > 0) + this->inval(NULL); + } +} + +void SkProgressView::setValue(U16CPU value) +{ + if (fValue != value) + { + if (fDoInterp) + { + if (fInterp) + delete fInterp; + fInterp = new SkInterpolator(1, 2); + SkScalar x = (SkScalar)(fValue << 8); + fInterp->setKeyFrame(0, SkTime::GetMSecs(), &x, 0); + x = (SkScalar)(value << 8); + fInterp->setKeyFrame(1, SkTime::GetMSecs() + 333, &x); + } + fValue = SkToU16(value); + this->inval(NULL); + } +} + +void SkProgressView::onDraw(SkCanvas* canvas) +{ + if (fMax == 0) + return; + + SkFixed percent; + + if (fInterp) + { + SkScalar x; + if (fInterp->timeToValues(SkTime::GetMSecs(), &x) == SkInterpolator::kFreezeEnd_Result) + { + delete fInterp; + fInterp = NULL; + } + percent = (SkFixed)x; // now its 16.8 + percent = SkMax32(0, SkMin32(percent, fMax << 8)); // now its pinned + percent = SkFixedDiv(percent, fMax << 8); // now its 0.16 + this->inval(NULL); + } + else + { + U16CPU value = SkMax32(0, SkMin32(fValue, fMax)); + percent = SkFixedDiv(value, fMax); + } + + + SkRect r; + SkPaint p; + + r.set(0, 0, this->width(), this->height()); + p.setAntiAlias(true); + + r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent)); + p.setStyle(SkPaint::kFill_Style); + + p.setColor(SK_ColorDKGRAY); + p.setShader(fOnShader); + canvas->drawRect(r, p); + + p.setColor(SK_ColorWHITE); + p.setShader(fOffShader); + r.fLeft = r.fRight; + r.fRight = this->width() - SK_Scalar1; + if (r.width() > 0) + canvas->drawRect(r, p); +} + +#include "SkImageDecoder.h" + +static SkShader* inflate_shader(const char file[]) +{ + SkBitmap bm; + + return SkImageDecoder::DecodeFile(file, &bm) ? + SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) : + NULL; +} + +void SkProgressView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + const char* s; + + SkASSERT(fOnShader == NULL); + SkASSERT(fOffShader == NULL); + + if ((s = dom.findAttr(node, "src-on")) != NULL) + fOnShader = inflate_shader(s); + if ((s = dom.findAttr(node, "src-off")) != NULL) + fOffShader = inflate_shader(s); + (void)dom.findBool(node, "do-interp", &fDoInterp); +} + diff --git a/src/views/SkScrollBarView.cpp b/src/views/SkScrollBarView.cpp new file mode 100644 index 0000000000..2e087bd1ae --- /dev/null +++ b/src/views/SkScrollBarView.cpp @@ -0,0 +1,139 @@ +#include "SkScrollBarView.h" +#include "SkAnimator.h" +#include "SkWidgetViews.h" +#include "SkSystemEventTypes.h" +#include "SkTime.h" + +//see SkProgressBarView.cpp +//#include "SkWidgetViews.cpp" + +SkScrollBarView::SkScrollBarView() +{ + fAnim.setHostEventSink(this); + init_skin_anim(kScroll_SkinEnum, &fAnim); + + fTotalLength = 0; + fStartPoint = 0; + fShownLength = 0; + + this->adjust(); +} + +void SkScrollBarView::setStart(unsigned start) +{ + if ((int)start < 0) + start = 0; + + if (fStartPoint != start) + { + fStartPoint = start; + this->adjust(); + } +} + +void SkScrollBarView::setShown(unsigned shown) +{ + if ((int)shown < 0) + shown = 0; + + if (fShownLength != shown) + { + fShownLength = shown; + this->adjust(); + } +} + +void SkScrollBarView::setTotal(unsigned total) +{ + if ((int)total < 0) + total = 0; + + if (fTotalLength != total) + { + fTotalLength = total; + this->adjust(); + } +} + +/* virtual */ void SkScrollBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + int32_t value; + if (dom.findS32(node, "total", &value)) + this->setTotal(value); + if (dom.findS32(node, "shown", &value)) + this->setShown(value); +} + +/*virtual*/ void SkScrollBarView::onSizeChange() +{ + this->INHERITED::onSizeChange(); + SkEvent evt("user"); + evt.setString("id", "setDim"); + evt.setScalar("dimX", this->width()); + evt.setScalar("dimY", this->height()); + fAnim.doUserEvent(evt); +} + +/*virtual*/ void SkScrollBarView::onDraw(SkCanvas* canvas) +{ + SkPaint paint; + SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs()); + + if (diff == SkAnimator::kDifferent) + this->inval(NULL); + else if (diff == SkAnimator::kPartiallyDifferent) + { + SkRect bounds; + fAnim.getInvalBounds(&bounds); + this->inval(&bounds); + } +} + +/*virtual*/ bool SkScrollBarView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Inval)) + { + this->inval(NULL); + return true; + } + if (evt.isType("recommendDim")) + { + SkScalar width; + + if (evt.findScalar("x", &width)) + this->setWidth(width); + return true; + } + + return this->INHERITED::onEvent(evt); +} + +void SkScrollBarView::adjust() +{ + int total = fTotalLength; + int start = fStartPoint; + int shown = fShownLength; + int hideBar = 0; + + if (total <= 0 || shown <= 0 || shown >= total) // no bar to show + { + total = 1; // avoid divide-by-zero. should be done by skin/script + hideBar = 1; // signal we don't want a thumb + } + else + { + if (start + shown > total) + start = total - shown; + } + + SkEvent e("user"); + e.setString("id", "adjustScrollBar"); + e.setScalar("_totalLength", SkIntToScalar(total)); + e.setScalar("_startPoint", SkIntToScalar(start)); + e.setScalar("_shownLength", SkIntToScalar(shown)); +// e.setS32("hideBar", hideBar); + fAnim.doUserEvent(e); +} + diff --git a/src/views/SkStackViewLayout.cpp b/src/views/SkStackViewLayout.cpp new file mode 100644 index 0000000000..ae2e9e81a5 --- /dev/null +++ b/src/views/SkStackViewLayout.cpp @@ -0,0 +1,262 @@ +#include "SkStackViewLayout.h" + +SkStackViewLayout::SkStackViewLayout() +{ + fMargin.set(0, 0, 0, 0); + fSpacer = 0; + fOrient = kHorizontal_Orient; + fPack = kStart_Pack; + fAlign = kStart_Align; + fRound = false; +} + +void SkStackViewLayout::setOrient(Orient ori) +{ + SkASSERT((unsigned)ori < kOrientCount); + fOrient = SkToU8(ori); +} + +void SkStackViewLayout::getMargin(SkRect* margin) const +{ + if (margin) + *margin = fMargin; +} + +void SkStackViewLayout::setMargin(const SkRect& margin) +{ + fMargin = margin; +} + +void SkStackViewLayout::setSpacer(SkScalar spacer) +{ + fSpacer = spacer; +} + +void SkStackViewLayout::setPack(Pack pack) +{ + SkASSERT((unsigned)pack < kPackCount); + fPack = SkToU8(pack); +} + +void SkStackViewLayout::setAlign(Align align) +{ + SkASSERT((unsigned)align < kAlignCount); + fAlign = SkToU8(align); +} + +void SkStackViewLayout::setRound(bool r) +{ + fRound = SkToU8(r); +} + +//////////////////////////////////////////////////////////////////////////////// + +typedef SkScalar (*AlignProc)(SkScalar childLimit, SkScalar parentLimit); +typedef SkScalar (SkView::*GetSizeProc)() const; +typedef void (SkView::*SetLocProc)(SkScalar coord); +typedef void (SkView::*SetSizeProc)(SkScalar coord); + +static SkScalar left_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; } +static SkScalar center_align_proc(SkScalar childLimit, SkScalar parentLimit) { return SkScalarHalf(parentLimit - childLimit); } +static SkScalar right_align_proc(SkScalar childLimit, SkScalar parentLimit) { return parentLimit - childLimit; } +static SkScalar fill_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; } + +/* Measure the main-dimension for all the children. If a child is marked flex in that direction + ignore its current value but increment the counter for flexChildren +*/ +static SkScalar compute_children_limit(SkView* parent, GetSizeProc sizeProc, int* count, + uint32_t flexMask, int* flexCount) +{ + SkView::B2FIter iter(parent); + SkView* child; + SkScalar limit = 0; + int n = 0, flex = 0; + + while ((child = iter.next()) != NULL) + { + n += 1; + if (child->getFlags() & flexMask) + flex += 1; + else + limit += (child->*sizeProc)(); + } + if (count) + *count = n; + if (flexCount) + *flexCount = flex; + return limit; +} + +void SkStackViewLayout::onLayoutChildren(SkView* parent) +{ + static AlignProc gAlignProcs[] = { + left_align_proc, + center_align_proc, + right_align_proc, + fill_align_proc + }; + + SkScalar startM, endM, crossStartM, crossLimit; + GetSizeProc mainGetSizeP, crossGetSizeP; + SetLocProc mainLocP, crossLocP; + SetSizeProc mainSetSizeP, crossSetSizeP; + SkView::Flag_Mask flexMask; + + if (fOrient == kHorizontal_Orient) + { + startM = fMargin.fLeft; + endM = fMargin.fRight; + crossStartM = fMargin.fTop; + crossLimit = -fMargin.fTop - fMargin.fBottom; + + mainGetSizeP = &SkView::width; + crossGetSizeP = &SkView::height; + mainLocP = &SkView::setLocX; + crossLocP = &SkView::setLocY; + + mainSetSizeP = &SkView::setWidth; + crossSetSizeP = &SkView::setHeight; + + flexMask = SkView::kFlexH_Mask; + } + else + { + startM = fMargin.fTop; + endM = fMargin.fBottom; + crossStartM = fMargin.fLeft; + crossLimit = -fMargin.fLeft - fMargin.fRight; + + mainGetSizeP = &SkView::height; + crossGetSizeP = &SkView::width; + mainLocP = &SkView::setLocY; + crossLocP = &SkView::setLocX; + + mainSetSizeP = &SkView::setHeight; + crossSetSizeP = &SkView::setWidth; + + flexMask = SkView::kFlexV_Mask; + } + crossLimit += (parent->*crossGetSizeP)(); + if (fAlign != kStretch_Align) + crossSetSizeP = NULL; + + int childCount, flexCount; + SkScalar childLimit = compute_children_limit(parent, mainGetSizeP, &childCount, flexMask, &flexCount); + + if (childCount == 0) + return; + + childLimit += (childCount - 1) * fSpacer; + + SkScalar parentLimit = (parent->*mainGetSizeP)() - startM - endM; + SkScalar pos = startM + gAlignProcs[fPack](childLimit, parentLimit); + SkScalar flexAmount = 0; + SkView::B2FIter iter(parent); + SkView* child; + + if (flexCount > 0 && parentLimit > childLimit) + flexAmount = (parentLimit - childLimit) / flexCount; + + while ((child = iter.next()) != NULL) + { + if (fRound) + pos = SkIntToScalar(SkScalarRound(pos)); + (child->*mainLocP)(pos); + SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit); + if (fRound) + crossLoc = SkIntToScalar(SkScalarRound(crossLoc)); + (child->*crossLocP)(crossLoc); + + if (crossSetSizeP) + (child->*crossSetSizeP)(crossLimit); + if (child->getFlags() & flexMask) + (child->*mainSetSizeP)(flexAmount); + pos += (child->*mainGetSizeP)() + fSpacer; + } +} + +////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[]) + { + const char* value = dom.findAttr(node, attr); + if (value) + SkDebugf("unknown attribute %s=\"%s\"\n", attr, value); + } +#else + #define assert_no_attr(dom, node, attr) +#endif + +void SkStackViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + int index; + SkScalar value[4]; + + if ((index = dom.findList(node, "orient", "horizontal,vertical")) >= 0) + this->setOrient((Orient)index); + else + assert_no_attr(dom, node, "orient"); + + if (dom.findScalars(node, "margin", value, 4)) + { + SkRect margin; + margin.set(value[0], value[1], value[2], value[3]); + this->setMargin(margin); + } + else + assert_no_attr(dom, node, "margin"); + + if (dom.findScalar(node, "spacer", value)) + this->setSpacer(value[0]); + else + assert_no_attr(dom, node, "spacer"); + + if ((index = dom.findList(node, "pack", "start,center,end")) >= 0) + this->setPack((Pack)index); + else + assert_no_attr(dom, node, "pack"); + + if ((index = dom.findList(node, "align", "start,center,end,stretch")) >= 0) + this->setAlign((Align)index); + else + assert_no_attr(dom, node, "align"); +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +SkFillViewLayout::SkFillViewLayout() +{ + fMargin.setEmpty(); +} + +void SkFillViewLayout::getMargin(SkRect* r) const +{ + if (r) + *r = fMargin; +} + +void SkFillViewLayout::setMargin(const SkRect& margin) +{ + fMargin = margin; +} + +void SkFillViewLayout::onLayoutChildren(SkView* parent) +{ + SkView::B2FIter iter(parent); + SkView* child; + + while ((child = iter.next()) != NULL) + { + child->setLoc(fMargin.fLeft, fMargin.fTop); + child->setSize( parent->width() - fMargin.fRight - fMargin.fLeft, + parent->height() - fMargin.fBottom - fMargin.fTop); + } +} + +void SkFillViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + (void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4); +} + diff --git a/src/views/SkTagList.cpp b/src/views/SkTagList.cpp new file mode 100644 index 0000000000..4576ce67c0 --- /dev/null +++ b/src/views/SkTagList.cpp @@ -0,0 +1,71 @@ +/* libs/graphics/views/SkTagList.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTagList.h" + +SkTagList::~SkTagList() +{ +} + +SkTagList* SkTagList::Find(SkTagList* rec, U8CPU tag) +{ + SkASSERT(tag < kSkTagListCount); + + while (rec != NULL) + { + if (rec->fTag == tag) + break; + rec = rec->fNext; + } + return rec; +} + +void SkTagList::DeleteTag(SkTagList** head, U8CPU tag) +{ + SkASSERT(tag < kSkTagListCount); + + SkTagList* rec = *head; + SkTagList* prev = NULL; + + while (rec != NULL) + { + SkTagList* next = rec->fNext; + + if (rec->fTag == tag) + { + if (prev) + prev->fNext = next; + else + *head = next; + delete rec; + break; + } + prev = rec; + rec = next; + } +} + +void SkTagList::DeleteAll(SkTagList* rec) +{ + while (rec) + { + SkTagList* next = rec->fNext; + delete rec; + rec = next; + } +} + diff --git a/src/views/SkTagList.h b/src/views/SkTagList.h new file mode 100644 index 0000000000..5f428e4022 --- /dev/null +++ b/src/views/SkTagList.h @@ -0,0 +1,51 @@ +/* libs/graphics/views/SkTagList.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTagList_DEFINED +#define SkTagList_DEFINED + +#include "SkTypes.h" + +enum SkTagListEnum { + kListeners_SkTagList, + kViewLayout_SkTagList, + kViewArtist_SkTagList, + + kSkTagListCount +}; + +struct SkTagList { + SkTagList* fNext; + uint16_t fExtra16; + uint8_t fExtra8; + uint8_t fTag; + + SkTagList(U8CPU tag) : fTag(SkToU8(tag)) + { + SkASSERT(tag < kSkTagListCount); + fNext = NULL; + fExtra16 = 0; + fExtra8 = 0; + } + virtual ~SkTagList(); + + static SkTagList* Find(SkTagList* head, U8CPU tag); + static void DeleteTag(SkTagList** headptr, U8CPU tag); + static void DeleteAll(SkTagList* head); +}; + +#endif diff --git a/src/views/SkTextBox.cpp b/src/views/SkTextBox.cpp new file mode 100644 index 0000000000..657f64805c --- /dev/null +++ b/src/views/SkTextBox.cpp @@ -0,0 +1,216 @@ +/* libs/graphics/views/SkTextBox.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTextBox.h" +#include "SkGlyphCache.h" +#include "SkUtils.h" +#include "SkAutoKern.h" + +static inline int is_ws(int c) +{ + return !((c - 1) >> 5); +} + +static size_t linebreak(const char text[], const char stop[], const SkPaint& paint, SkScalar margin) +{ + const char* start = text; + + SkAutoGlyphCache ac(paint, NULL); + SkGlyphCache* cache = ac.getCache(); + SkFixed w = 0; + SkFixed limit = SkScalarToFixed(margin); + SkAutoKern autokern; + + const char* word_start = text; + int prevWS = true; + + while (text < stop) + { + const char* prevText = text; + SkUnichar uni = SkUTF8_NextUnichar(&text); + int currWS = is_ws(uni); + const SkGlyph& glyph = cache->getUnicharMetrics(uni); + + if (!currWS && prevWS) + word_start = prevText; + prevWS = currWS; + + w += autokern.adjust(glyph) + glyph.fAdvanceX; + if (w > limit) + { + if (currWS) // eat the rest of the whitespace + { + while (text < stop && is_ws(SkUTF8_ToUnichar(text))) + text += SkUTF8_CountUTF8Bytes(text); + } + else // backup until a whitespace (or 1 char) + { + if (word_start == start) + { + if (prevText > start) + text = prevText; + } + else + text = word_start; + } + break; + } + } + return text - start; +} + +int SkTextLineBreaker::CountLines(const char text[], size_t len, const SkPaint& paint, SkScalar width) +{ + const char* stop = text + len; + int count = 0; + + if (width > 0) + { + do { + count += 1; + text += linebreak(text, stop, paint, width); + } while (text < stop); + } + return count; +} + +////////////////////////////////////////////////////////////////////////////// + +SkTextBox::SkTextBox() +{ + fBox.setEmpty(); + fSpacingMul = SK_Scalar1; + fSpacingAdd = 0; + fMode = kLineBreak_Mode; + fSpacingAlign = kStart_SpacingAlign; +} + +void SkTextBox::setMode(Mode mode) +{ + SkASSERT((unsigned)mode < kModeCount); + fMode = SkToU8(mode); +} + +void SkTextBox::setSpacingAlign(SpacingAlign align) +{ + SkASSERT((unsigned)align < kSpacingAlignCount); + fSpacingAlign = SkToU8(align); +} + +void SkTextBox::getBox(SkRect* box) const +{ + if (box) + *box = fBox; +} + +void SkTextBox::setBox(const SkRect& box) +{ + fBox = box; +} + +void SkTextBox::setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) +{ + fBox.set(left, top, right, bottom); +} + +void SkTextBox::getSpacing(SkScalar* mul, SkScalar* add) const +{ + if (mul) + *mul = fSpacingMul; + if (add) + *add = fSpacingAdd; +} + +void SkTextBox::setSpacing(SkScalar mul, SkScalar add) +{ + fSpacingMul = mul; + fSpacingAdd = add; +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +void SkTextBox::draw(SkCanvas* canvas, const char text[], size_t len, const SkPaint& paint) +{ + SkASSERT(canvas && &paint && (text || len == 0)); + + SkScalar marginWidth = fBox.width(); + + if (marginWidth <= 0 || len == 0) + return; + + const char* textStop = text + len; + + SkScalar x, y, scaledSpacing, height, fontHeight; + SkPaint::FontMetrics metrics; + + switch (paint.getTextAlign()) { + case SkPaint::kLeft_Align: + x = 0; + break; + case SkPaint::kCenter_Align: + x = SkScalarHalf(marginWidth); + break; + default: + x = marginWidth; + break; + } + x += fBox.fLeft; + + fontHeight = paint.getFontMetrics(&metrics); + scaledSpacing = SkScalarMul(fontHeight, fSpacingMul) + fSpacingAdd; + height = fBox.height(); + + // compute Y position for first line + { + SkScalar textHeight = fontHeight; + + if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign) + { + int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth); + SkASSERT(count > 0); + textHeight += scaledSpacing * (count - 1); + } + + switch (fSpacingAlign) { + case kStart_SpacingAlign: + y = 0; + break; + case kCenter_SpacingAlign: + y = SkScalarHalf(height - textHeight); + break; + default: + SkASSERT(fSpacingAlign == kEnd_SpacingAlign); + y = height - textHeight; + break; + } + y += fBox.fTop - metrics.fAscent; + } + + for (;;) + { + len = linebreak(text, textStop, paint, marginWidth); + if (y + metrics.fDescent + metrics.fLeading > 0) + canvas->drawText(text, len, x, y, paint); + text += len; + if (text >= textStop) + break; + y += scaledSpacing; + if (y + metrics.fAscent >= height) + break; + } +} + diff --git a/src/views/SkView.cpp b/src/views/SkView.cpp new file mode 100644 index 0000000000..a027744c69 --- /dev/null +++ b/src/views/SkView.cpp @@ -0,0 +1,760 @@ +#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; + + 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::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::draw(SkCanvas* canvas) +{ + if (fWidth && fHeight && this->isVisible()) + { + SkRect r; + r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight); + if (canvas->quickReject(r, SkCanvas::kBW_EdgeType)) + return; + + SkAutoCanvasRestore as(canvas, true); + + canvas->clipRect(r); + canvas->translate(fLoc.fX, fLoc.fY); + + this->onDraw(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) +{ + if (!this->isVisible()) + return; + + SkRect bounds; + + this->getLocalBounds(&bounds); + if (rect && !bounds.intersect(*rect)) + return; + + rect = &bounds; + SkView* view = this; + + for (;;) + { + if (view->handleInval(bounds)) + break; + + SkRect parentR; + SkView* parent = view->fParent; + + if (parent == NULL || !parent->isVisible()) + break; + + bounds.offset(view->fLoc.fX, view->fLoc.fY); + parent->getLocalBounds(&parentR); + if (!bounds.intersect(parentR)) + return; + + 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; +} + +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 false; + + F2BIter iter(this); + SkView* child; + + while ((child = iter.next()) != NULL) + { + Click* click = child->findClickHandler(x - child->fLoc.fX, y - child->fLoc.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() +{ +} + +SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y) +{ + return NULL; +} + +bool SkView::onClick(Click*) +{ + return false; +} + +bool SkView::handleInval(const SkRect& r) +{ + 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::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const +{ + SkASSERT(this); + + if (local) + { + const SkView* view = this; + while (view) + { + x -= view->fLoc.fX; + y -= view->fLoc.fY; + view = view->fParent; + } + local->set(x, y); + } +} + +////////////////////////////////////////////////////////////////// + +/* 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&) +{ + // override in subclass as needed +} + +void SkView::postInflate(const SkTDict& 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::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("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("\n"); + } + else + goto ONELINER; + } + else + { + ONELINER: + SkDebugf(" />\n"); + } +} + +void SkView::dump(bool recurse) const +{ + dumpview(this, 0, recurse); +} + +#endif diff --git a/src/views/SkViewInflate.cpp b/src/views/SkViewInflate.cpp new file mode 100644 index 0000000000..8f5489de54 --- /dev/null +++ b/src/views/SkViewInflate.cpp @@ -0,0 +1,139 @@ +#include "SkViewInflate.h" +#include "SkView.h" +#include + +SkViewInflate::SkViewInflate() : fIDs(kMinIDStrAlloc), fStrings(kMinIDStrAlloc) +{ +} + +SkViewInflate::~SkViewInflate() +{ +} + +void SkViewInflate::rInflate(const SkDOM& dom, const SkDOM::Node* node, SkView* parent) +{ + const char* str = dom.findAttr(node, "id"); + if (str) + fIDs.set(str, parent); + + const SkDOM::Node* child = dom.getFirstChild(node); + while (child) + { + SkView* view = this->createView(dom, child); + if (view) + { + this->rInflate(dom, child, view); + parent->attachChildToFront(view)->unref(); + } + else + { + const char* name = dom.getName(child); + const char* target; + + if (!strcmp(name, "listenTo") && (target = dom.findAttr(child, "target")) != NULL) + this->addIDStr(&fListenTo, parent, target); + + if (!strcmp(name, "broadcastTo") && (target = dom.findAttr(child, "target")) != NULL) + this->addIDStr(&fBroadcastTo, parent, target); + } + child = dom.getNextSibling(child); + } + + parent->setVisibleP(true); + this->inflateView(parent, dom, node); +} + +void SkViewInflate::inflateView(SkView* view, const SkDOM& dom, const SkDOM::Node* node) +{ + // called after all of view's children have been instantiated. + // this may be overridden by a subclass, to load in layout or other helpers + // they should call through to us (INHERITED) before or after their patch + view->inflate(dom, node); +} + +SkView* SkViewInflate::inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root) +{ + fIDs.reset(); + + if (root == NULL) + { + root = this->createView(dom, node); + if (root == NULL) + { + printf("createView returned NULL on <%s>\n", dom.getName(node)); + return NULL; + } + } + this->rInflate(dom, node, root); + + // resolve listeners and broadcasters + { + SkView* target; + const IDStr* iter = fListenTo.begin(); + const IDStr* stop = fListenTo.end(); + for (; iter < stop; iter++) + { + if (fIDs.find(iter->fStr, &target)) + target->addListenerID(iter->fView->getSinkID()); + } + + iter = fBroadcastTo.begin(); + stop = fBroadcastTo.end(); + for (; iter < stop; iter++) + { + if (fIDs.find(iter->fStr, &target)) + iter->fView->addListenerID(target->getSinkID()); + } + } + + // now that the tree is built, give everyone a shot at the ID dict + root->postInflate(fIDs); + return root; +} + +SkView* SkViewInflate::inflate(const char xml[], size_t len, SkView* root) +{ + SkDOM dom; + const SkDOM::Node* node = dom.build(xml, len); + + return node ? this->inflate(dom, node, root) : NULL; +} + +SkView* SkViewInflate::findViewByID(const char id[]) const +{ + SkASSERT(id); + SkView* view; + return fIDs.find(id, &view) ? view : NULL; +} + +SkView* SkViewInflate::createView(const SkDOM& dom, const SkDOM::Node* node) +{ + if (!strcmp(dom.getName(node), "view")) + return new SkView; + return NULL; +} + +void SkViewInflate::addIDStr(SkTDArray* list, SkView* view, const char* str) +{ + size_t len = strlen(str) + 1; + IDStr* pair = list->append(); + pair->fView = view; + pair->fStr = (char*)fStrings.alloc(len, SkChunkAlloc::kThrow_AllocFailType); + memcpy(pair->fStr, str, len); +} + +#ifdef SK_DEBUG +void SkViewInflate::dump() const +{ + const IDStr* iter = fListenTo.begin(); + const IDStr* stop = fListenTo.end(); + for (; iter < stop; iter++) + SkDebugf("inflate: listenTo(\"%s\")\n", iter->fStr); + + iter = fBroadcastTo.begin(); + stop = fBroadcastTo.end(); + for (; iter < stop; iter++) + SkDebugf("inflate: broadcastFrom(\"%s\")\n", iter->fStr); +} +#endif + diff --git a/src/views/SkViewPriv.cpp b/src/views/SkViewPriv.cpp new file mode 100644 index 0000000000..b03ca8c101 --- /dev/null +++ b/src/views/SkViewPriv.cpp @@ -0,0 +1,97 @@ +#include "SkViewPriv.h" + +////////////////////////////////////////////////////////////////////// + +void SkView::Artist::draw(SkView* view, SkCanvas* canvas) +{ + SkASSERT(view && canvas); + this->onDraw(view, canvas); +} + +void SkView::Artist::inflate(const SkDOM& dom, const SkDOM::Node* node) +{ + SkASSERT(&dom && node); + this->onInflate(dom, node); +} + +void SkView::Artist::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + // subclass should override this as needed +} + +SkView::Artist* SkView::getArtist() const +{ + Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList); + SkASSERT(rec == NULL || rec->fArtist != NULL); + + return rec ? rec->fArtist : NULL; +} + +SkView::Artist* SkView::setArtist(Artist* obj) +{ + if (obj == NULL) + { + this->removeTagList(kViewArtist_SkTagList); + } + else // add/replace + { + Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList); + + if (rec) + SkRefCnt_SafeAssign(rec->fArtist, obj); + else + this->addTagList(new Artist_SkTagList(obj)); + } + return obj; +} + +//////////////////////////////////////////////////////////////////////////////// + +void SkView::Layout::layoutChildren(SkView* parent) +{ + SkASSERT(parent); + if (parent->width() > 0 && parent->height() > 0) + this->onLayoutChildren(parent); +} + +void SkView::Layout::inflate(const SkDOM& dom, const SkDOM::Node* node) +{ + SkASSERT(&dom && node); + this->onInflate(dom, node); +} + +void SkView::Layout::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + // subclass should override this as needed +} + +SkView::Layout* SkView::getLayout() const +{ + Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList); + SkASSERT(rec == NULL || rec->fLayout != NULL); + + return rec ? rec->fLayout : NULL; +} + +SkView::Layout* SkView::setLayout(Layout* obj, bool invokeLayoutNow) +{ + if (obj == NULL) + { + this->removeTagList(kViewLayout_SkTagList); + } + else // add/replace + { + Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList); + + if (rec) + SkRefCnt_SafeAssign(rec->fLayout, obj); + else + this->addTagList(new Layout_SkTagList(obj)); + } + + if (invokeLayoutNow) + this->invokeLayout(); + + return obj; +} + diff --git a/src/views/SkViewPriv.h b/src/views/SkViewPriv.h new file mode 100644 index 0000000000..06ce59bec6 --- /dev/null +++ b/src/views/SkViewPriv.h @@ -0,0 +1,38 @@ +#ifndef SkViewPriv_DEFINED +#define SkViewPriv_DEFINED + +#include "SkView.h" +#include "SkTagList.h" + +struct Layout_SkTagList : SkTagList { + SkView::Layout* fLayout; + + Layout_SkTagList(SkView::Layout* layout) + : SkTagList(kViewLayout_SkTagList), fLayout(layout) + { + SkASSERT(layout); + layout->ref(); + } + virtual ~Layout_SkTagList() + { + fLayout->unref(); + } +}; + +struct Artist_SkTagList : SkTagList { + SkView::Artist* fArtist; + + Artist_SkTagList(SkView::Artist* artist) + : SkTagList(kViewArtist_SkTagList), fArtist(artist) + { + SkASSERT(artist); + artist->ref(); + } + virtual ~Artist_SkTagList() + { + fArtist->unref(); + } +}; + +#endif + diff --git a/src/views/SkWidget.cpp b/src/views/SkWidget.cpp new file mode 100644 index 0000000000..8d945f171a --- /dev/null +++ b/src/views/SkWidget.cpp @@ -0,0 +1,323 @@ +#include "SkWidget.h" +#include "SkCanvas.h" +#include "SkInterpolator.h" +#include "SkTime.h" +#include "SkParsePaint.h" + +#if 0 +SkWidgetView::SkWidgetView(U32 flags) : SkView(flags) +{ +} + +SkWidgetView::~SkWidgetView() +{ +} + +const char* SkWidgetView::GetEventType() +{ + return "SkWidgetView"; +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +class SkTextView::Interp { +public: + Interp(const SkString& old, SkMSec now, SkMSec dur, AnimaDir dir) : fOldText(old), fInterp(1, 2) + { + SkScalar x = 0; + fInterp.setKeyFrame(0, now, &x, 0); + x = SK_Scalar1; + if (dir == kBackward_AnimDir) + x = -x; + fInterp.setKeyFrame(1, now + dur, &x); + } + bool draw(SkCanvas* canvas, const SkString& newText, SkScalar x, SkScalar y, SkPaint& paint) + { + SkScalar scale; + + if (fInterp.timeToValues(SkTime::GetMSecs(), &scale) == SkInterpolator::kFreezeEnd_Result) + { + canvas->drawText(newText.c_str(), newText.size(), x, y, paint); + return false; + } + else + { + U8 alpha = paint.getAlpha(); + SkScalar above, below; + (void)paint.measureText(nil, 0, &above, &below); + SkScalar height = below - above; + SkScalar dy = SkScalarMul(height, scale); + if (scale < 0) + height = -height; + + // draw the old + paint.setAlpha((U8)SkScalarMul(alpha, SK_Scalar1 - SkScalarAbs(scale))); + canvas->drawText(fOldText.c_str(), fOldText.size(), x, y - dy, paint); + // draw the new + paint.setAlpha((U8)SkScalarMul(alpha, SkScalarAbs(scale))); + canvas->drawText(newText.c_str(), newText.size(), x, y + height - dy, paint); + // restore the paint + paint.setAlpha(alpha); + return true; + } + } + +private: + SkString fOldText; + SkInterpolator fInterp; +}; + +SkTextView::SkTextView(U32 flags) : SkView(flags), fInterp(nil), fDoInterp(false) +{ + fMargin.set(0, 0); +} + +SkTextView::~SkTextView() +{ + delete fInterp; +} + +void SkTextView::getText(SkString* str) const +{ + if (str) + str->set(fText); +} + +void SkTextView::setText(const char text[], AnimaDir dir) +{ + if (!fText.equals(text)) + { + SkString tmp(text); + this->privSetText(tmp, dir); + } +} + +void SkTextView::setText(const char text[], size_t len, AnimaDir dir) +{ + if (!fText.equals(text)) + { + SkString tmp(text, len); + this->privSetText(tmp, dir); + } +} + +void SkTextView::setText(const SkString& src, AnimaDir dir) +{ + if (fText != src) + this->privSetText(src, dir); +} + +void SkTextView::privSetText(const SkString& src, AnimaDir dir) +{ + SkASSERT(fText != src); + + if (fDoInterp) + { + if (fInterp) + delete fInterp; + fInterp = new Interp(fText, SkTime::GetMSecs(), 500, dir); + } + fText = src; + this->inval(nil); +} + +///////////////////////////////////////////////////////////////// + +void SkTextView::getMargin(SkPoint* margin) const +{ + if (margin) + *margin = fMargin; +} + +void SkTextView::setMargin(const SkPoint& margin) +{ + if (fMargin != margin) + { + fMargin = margin; + this->inval(nil); + } +} + +void SkTextView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + if (fText.size() == 0) + return; + + SkPaint::Align align = fPaint.getTextAlign(); + SkScalar x, y; + + switch (align) { + case SkPaint::kLeft_Align: + x = fMargin.fX; + break; + case SkPaint::kCenter_Align: + x = SkScalarHalf(this->width()); + break; + default: + SkASSERT(align == SkPaint::kRight_Align); + x = this->width() - fMargin.fX; + break; + } + + fPaint.measureText(nil, 0, &y, nil); + y = fMargin.fY - y; + + if (fInterp) + { + if (fInterp->draw(canvas, fText, x, y, fPaint)) + this->inval(nil); + else + { + delete fInterp; + fInterp = nil; + } + } + else + canvas->drawText(fText.c_str(), fText.size(), x, y, fPaint); +} + +////////////////////////////////////////////////////////////////////////////////////// + +void SkTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + const char* text = dom.findAttr(node, "text"); + if (text) + this->setText(text); + + SkPoint margin; + if (dom.findScalars(node, "margin", (SkScalar*)&margin, 2)) + this->setMargin(margin); + (void)dom.findBool(node, "do-interp", &fDoInterp); + + SkPaint_Inflate(&fPaint, dom, node); +} + +////////////////////////////////////////////////////////////////////////////////////// + +SkSliderView::SkSliderView(U32 flags) : SkWidgetView(flags) +{ + fValue = 0; + fMax = 0; +} + +static U16 actual_value(U16CPU value, U16CPU max) +{ + return SkToU16(SkMax32(0, SkMin32(value, max))); +} + +void SkSliderView::setMax(U16CPU max) +{ + if (fMax != max) + { + fMax = SkToU16(max); + if (fValue > 0) + this->inval(nil); + } +} + +void SkSliderView::setValue(U16CPU value) +{ + if (fValue != value) + { + U16 prev = actual_value(fValue, fMax); + U16 next = actual_value(value, fMax); + + fValue = SkToU16(value); + if (prev != next) + { + this->inval(nil); + + if (this->hasListeners()) + { + SkEvent evt; + + evt.setType(SkWidgetView::GetEventType()); + evt.setFast32(this->getSinkID()); + evt.setS32("sliderValue", next); + this->postToListeners(evt); + } + } + } +} + +#include "SkGradientShader.h" + +static void setgrad(SkPaint* paint, const SkRect& r) +{ + SkPoint pts[2]; + SkColor colors[2]; + +#if 0 + pts[0].set(r.fLeft, r.fTop); + pts[1].set(r.fLeft + r.height(), r.fBottom); +#else + pts[0].set(r.fRight, r.fBottom); + pts[1].set(r.fRight - r.height(), r.fTop); +#endif + colors[0] = SK_ColorBLUE; + colors[1] = SK_ColorWHITE; + + paint->setShader(SkGradientShader::CreateLinear(pts, colors, nil, 2, SkShader::kMirror_TileMode))->unref(); +} + +void SkSliderView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + U16CPU value = SkMax32(0, SkMin32(fValue, fMax)); + + SkRect r; + SkPaint p; + + r.set(0, 0, this->width(), this->height()); + + p.setAntiAliasOn(true); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1); + r.inset(SK_Scalar1/2, SK_Scalar1/2); + canvas->drawRect(r, p); + + if (fMax) + { + SkFixed percent = SkFixedDiv(value, fMax); + + r.inset(SK_Scalar1/2, SK_Scalar1/2); + r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent)); + p.setStyle(SkPaint::kFill_Style); + setgrad(&p, r); + canvas->drawRect(r, p); + } + +#if 0 + r.set(0, 0, this->width(), this->height()); + r.inset(SK_Scalar1, SK_Scalar1); + r.inset(r.width()/2, 0); + p.setColor(SK_ColorBLACK); + canvas->drawLine(*(SkPoint*)&r.fLeft, *(SkPoint*)&r.fRight, p); +#endif +} + +SkView::Click* SkSliderView::onFindClickHandler(SkScalar x, SkScalar y) +{ + return new Click(this); +} + +bool SkSliderView::onClick(Click* click) +{ + if (fMax) + { + SkScalar percent = SkScalarDiv(click->fCurr.fX + SK_Scalar1, this->width() - SK_Scalar1*2); + percent = SkMaxScalar(0, SkMinScalar(percent, SK_Scalar1)); + this->setValue(SkScalarRound(percent * fMax)); + return true; + } + return false; +} + +#endif + diff --git a/src/views/SkWidgetViews.cpp b/src/views/SkWidgetViews.cpp new file mode 100644 index 0000000000..e7eb772577 --- /dev/null +++ b/src/views/SkWidgetViews.cpp @@ -0,0 +1,570 @@ +#include "SkWidgetViews.h" +#include "SkAnimator.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkStream.h" +#include "SkSystemEventTypes.h" + +#ifdef SK_DEBUG + static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[]) + { + const char* value = dom.findAttr(node, attr); + if (value) + SkDebugf("unknown attribute %s=\"%s\"\n", attr, value); + } +#else + #define assert_no_attr(dom, node, attr) +#endif +/* +I have moved this to SkWidgetViews.h +enum SkinEnum { + kButton_SkinEnum, + kProgress_SkinEnum, + kScroll_SkinEnum, + kStaticText_SkinEnum, + + kSkinEnumCount +}; +*/ + +const char* get_skin_enum_path(SkinEnum se) +{ + SkASSERT((unsigned)se < kSkinEnumCount); + + static const char* gSkinPaths[] = { + "common/default/default/skins/border3.xml", + "common/default/default/skins/button.xml", + "common/default/default/skins/progressBar.xml", + "common/default/default/skins/scrollBar.xml", + "common/default/default/skins/statictextpaint.xml" + }; + + return gSkinPaths[se]; +} + +void init_skin_anim(const char path[], SkAnimator* anim) +{ + SkASSERT(path && anim); + + SkFILEStream stream(path); + + if (!stream.isValid()) + { + SkDEBUGF(("init_skin_anim: loading skin failed <%s>\n", path)); + sk_throw(); + } + + if (!anim->decodeStream(&stream)) + { + SkDEBUGF(("init_skin_anim: decoding skin failed <%s>\n", path)); + sk_throw(); + } +} + +void init_skin_anim(SkinEnum se, SkAnimator* anim) +{ + init_skin_anim(get_skin_enum_path(se), anim); +} + +void init_skin_paint(SkinEnum se, SkPaint* paint) +{ + SkASSERT(paint); + + SkAnimator anim; + SkCanvas canvas; + + init_skin_anim(se, &anim); + anim.draw(&canvas, paint, 0); +} + +void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint) +{ + SkASSERT(paint); + + SkAnimator anim; + SkCanvas canvas; + + if (!anim.decodeDOM(dom, node)) + { + SkDEBUGF(("inflate_paint: decoding dom failed\n")); + SkDEBUGCODE(dom.dump(node);) + sk_throw(); + } + anim.draw(&canvas, paint, 0); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +SkWidgetView::SkWidgetView() : SkView(SkView::kFocusable_Mask | SkView::kEnabled_Mask) +{ +} + +const char* SkWidgetView::getLabel() const +{ + return fLabel.c_str(); +} + +void SkWidgetView::getLabel(SkString* label) const +{ + if (label) + *label = fLabel; +} + +void SkWidgetView::setLabel(const char label[]) +{ + this->setLabel(label, label ? strlen(label) : 0); +} + +void SkWidgetView::setLabel(const char label[], size_t len) +{ + if (label == nil && fLabel.size() != 0 || !fLabel.equals(label, len)) + { + SkString tmp(label, len); + + this->onLabelChange(fLabel.c_str(), tmp.c_str()); + fLabel.swap(tmp); + } +} + +void SkWidgetView::setLabel(const SkString& label) +{ + if (fLabel != label) + { + this->onLabelChange(fLabel.c_str(), label.c_str()); + fLabel = label; + } +} + +bool SkWidgetView::postWidgetEvent() +{ + if (!fEvent.isType("")) + { + SkEvent evt(fEvent); // make a copy since onPrepareWidgetEvent may edit the event + + if (this->onPrepareWidgetEvent(&evt)) + { + SkDEBUGCODE(evt.dump("SkWidgetView::postWidgetEvent");) + + this->postToListeners(evt); // wonder if this should return true if there are > 0 listeners... + return true; + } + } + return false; +} + +/*virtual*/ void SkWidgetView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + const char* label = dom.findAttr(node, "label"); + if (label) + this->setLabel(label); + + if ((node = dom.getFirstChild(node, "event")) != nil) + fEvent.inflate(dom, node); +} + +/*virtual*/ void SkWidgetView::onLabelChange(const char oldLabel[], const char newLabel[]) +{ + this->inval(nil); +} + +static const char gWidgetEventSinkIDSlotName[] = "sk-widget-sinkid-slot"; + +/*virtual*/ bool SkWidgetView::onPrepareWidgetEvent(SkEvent* evt) +{ + evt->setS32(gWidgetEventSinkIDSlotName, this->getSinkID()); + return true; +} + +SkEventSinkID SkWidgetView::GetWidgetEventSinkID(const SkEvent& evt) +{ + int32_t sinkID; + + return evt.findS32(gWidgetEventSinkIDSlotName, &sinkID) ? (SkEventSinkID)sinkID : 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/*virtual*/ bool SkButtonView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey) + { + this->postWidgetEvent(); + return true; + } + return this->INHERITED::onEvent(evt); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +SkCheckButtonView::SkCheckButtonView() : fCheckState(kOff_CheckState) +{ +} + +void SkCheckButtonView::setCheckState(CheckState state) +{ + SkASSERT((unsigned)state <= kUnknown_CheckState); + + if (fCheckState != state) + { + this->onCheckStateChange(this->getCheckState(), state); + fCheckState = SkToU8(state); + } +} + +/*virtual*/ void SkCheckButtonView::onCheckStateChange(CheckState oldState, CheckState newState) +{ + this->inval(nil); +} + +/*virtual*/ void SkCheckButtonView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + int index = dom.findList(node, "check-state", "off,on,unknown"); + if (index >= 0) + this->setCheckState((CheckState)index); +} + +static const char gCheckStateSlotName[] = "sk-checkbutton-check-slot"; + +/*virtual*/ bool SkCheckButtonView::onPrepareWidgetEvent(SkEvent* evt) +{ + // could check if we're "disabled", and return false... + + evt->setS32(gCheckStateSlotName, this->getCheckState()); + return true; +} + +bool SkCheckButtonView::GetWidgetEventCheckState(const SkEvent& evt, CheckState* state) +{ + int32_t state32; + + if (evt.findS32(gCheckStateSlotName, &state32)) + { + if (state) + *state = (CheckState)state32; + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkTime.h" +#include + +class SkAnimButtonView : public SkButtonView { +public: + SkAnimButtonView() + { + fAnim.setHostEventSink(this); + init_skin_anim(kButton_SkinEnum, &fAnim); + } + +protected: + virtual void onLabelChange(const char oldLabel[], const char newLabel[]) + { + this->INHERITED::onLabelChange(oldLabel, newLabel); + + SkEvent evt("user"); + evt.setString("id", "setLabel"); + evt.setString("LABEL", newLabel); + fAnim.doUserEvent(evt); + } + + virtual void onFocusChange(bool gainFocus) + { + this->INHERITED::onFocusChange(gainFocus); + + SkEvent evt("user"); + evt.setString("id", "setFocus"); + evt.setS32("FOCUS", gainFocus); + fAnim.doUserEvent(evt); + } + + virtual void onSizeChange() + { + this->INHERITED::onSizeChange(); + + SkEvent evt("user"); + evt.setString("id", "setDim"); + evt.setScalar("dimX", this->width()); + evt.setScalar("dimY", this->height()); + fAnim.doUserEvent(evt); + } + + virtual void onDraw(SkCanvas* canvas) + { + SkPaint paint; + SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs()); + + if (diff == SkAnimator::kDifferent) + this->inval(nil); + else if (diff == SkAnimator::kPartiallyDifferent) + { + SkRect bounds; + fAnim.getInvalBounds(&bounds); + this->inval(&bounds); + } + } + + virtual bool onEvent(const SkEvent& evt) + { + if (evt.isType(SK_EventType_Inval)) + { + this->inval(nil); + return true; + } + if (evt.isType("recommendDim")) + { + SkScalar height; + + if (evt.findScalar("y", &height)) + this->setHeight(height); + return true; + } + return this->INHERITED::onEvent(evt); + } + + virtual bool onPrepareWidgetEvent(SkEvent* evt) + { + if (this->INHERITED::onPrepareWidgetEvent(evt)) + { + SkEvent e("user"); + e.setString("id", "handlePress"); + (void)fAnim.doUserEvent(e); + return true; + } + return false; + } + +private: + SkAnimator fAnim; + + typedef SkButtonView INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkTextBox.h" + +SkStaticTextView::SkStaticTextView() +{ + fMargin.set(0, 0); + fMode = kFixedSize_Mode; + fSpacingAlign = SkTextBox::kStart_SpacingAlign; + + init_skin_paint(kStaticText_SkinEnum, &fPaint); +} + +SkStaticTextView::~SkStaticTextView() +{ +} + +void SkStaticTextView::computeSize() +{ + if (fMode == kAutoWidth_Mode) + { + SkScalar width = fPaint.measureText(fText.c_str(), fText.size()); + this->setWidth(width + fMargin.fX * 2); + } + else if (fMode == kAutoHeight_Mode) + { + SkScalar width = this->width() - fMargin.fX * 2; + int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0; + + this->setHeight(lines * fPaint.getFontSpacing() + fMargin.fY * 2); + } +} + +void SkStaticTextView::setMode(Mode mode) +{ + SkASSERT((unsigned)mode < kModeCount); + + if (fMode != mode) + { + fMode = SkToU8(mode); + this->computeSize(); + } +} + +void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align) +{ + fSpacingAlign = SkToU8(align); + this->inval(nil); +} + +void SkStaticTextView::getMargin(SkPoint* margin) const +{ + if (margin) + *margin = fMargin; +} + +void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy) +{ + if (fMargin.fX != dx || fMargin.fY != dy) + { + fMargin.set(dx, dy); + this->computeSize(); + this->inval(nil); + } +} + +size_t SkStaticTextView::getText(SkString* text) const +{ + if (text) + *text = fText; + return fText.size(); +} + +size_t SkStaticTextView::getText(char text[]) const +{ + if (text) + memcpy(text, fText.c_str(), fText.size()); + return fText.size(); +} + +void SkStaticTextView::setText(const SkString& text) +{ + this->setText(text.c_str(), text.size()); +} + +void SkStaticTextView::setText(const char text[]) +{ + if (text == nil) + text = ""; + this->setText(text, strlen(text)); +} + +void SkStaticTextView::setText(const char text[], size_t len) +{ + if (!fText.equals(text, len)) + { + fText.set(text, len); + this->computeSize(); + this->inval(nil); + } +} + +void SkStaticTextView::getPaint(SkPaint* paint) const +{ + if (paint) + *paint = fPaint; +} + +void SkStaticTextView::setPaint(const SkPaint& paint) +{ + if (fPaint != paint) + { + fPaint = paint; + this->computeSize(); + this->inval(nil); + } +} + +void SkStaticTextView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + if (fText.isEmpty()) + return; + + SkTextBox box; + + box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode); + box.setSpacingAlign(this->getSpacingAlign()); + box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY); + box.draw(canvas, fText.c_str(), fText.size(), fPaint); +} + +void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + int index; + if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0) + this->setMode((Mode)index); + else + assert_no_attr(dom, node, "mode"); + + if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0) + this->setSpacingAlign((SkTextBox::SpacingAlign)index); + else + assert_no_attr(dom, node, "spacing-align"); + + SkScalar s[2]; + if (dom.findScalars(node, "margin", s, 2)) + this->setMargin(s[0], s[1]); + else + assert_no_attr(dom, node, "margin"); + + const char* text = dom.findAttr(node, "text"); + if (text) + this->setText(text); + + if ((node = dom.getFirstChild(node, "paint")) != nil && + (node = dom.getFirstChild(node, "screenplay")) != nil) + { + inflate_paint(dom, node, &fPaint); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// + +SkView* SkWidgetFactory(const char name[]) +{ + if (name == nil) + return nil; + + // must be in the same order as the SkSkinWidgetEnum is declared + static const char* gNames[] = { + "sk-border", + "sk-button", + "sk-image", + "sk-list", + "sk-progress", + "sk-scroll", + "sk-text" + + }; + + for (int i = 0; i < SK_ARRAY_COUNT(gNames); i++) + if (!strcmp(gNames[i], name)) + return SkWidgetFactory((SkWidgetEnum)i); + + return nil; +} + +#include "SkImageView.h" +#include "SkProgressBarView.h" +#include "SkScrollBarView.h" +#include "SkBorderView.h" + +SkView* SkWidgetFactory(SkWidgetEnum sw) +{ + switch (sw) { + case kBorder_WidgetEnum: + return new SkBorderView; + case kButton_WidgetEnum: + return new SkAnimButtonView; + case kImage_WidgetEnum: + return new SkImageView; + case kList_WidgetEnum: + return new SkListView; + case kProgress_WidgetEnum: + return new SkProgressBarView; + case kScroll_WidgetEnum: + return new SkScrollBarView; + case kText_WidgetEnum: + return new SkStaticTextView; + default: + SkASSERT(!"unknown enum passed to SkWidgetFactory"); + break; + } + return nil; +} diff --git a/src/views/SkWidgets.cpp b/src/views/SkWidgets.cpp new file mode 100644 index 0000000000..fe87cd6e19 --- /dev/null +++ b/src/views/SkWidgets.cpp @@ -0,0 +1,554 @@ +#include "SkWidget.h" +#include "SkCanvas.h" +#include "SkKey.h" +#include "SkParsePaint.h" +#include "SkSystemEventTypes.h" +#include "SkTextBox.h" + +#if 0 + +#ifdef SK_DEBUG + static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[]) + { + const char* value = dom.findAttr(node, attr); + if (value) + SkDebugf("unknown attribute %s=\"%s\"\n", attr, value); + } +#else + #define assert_no_attr(dom, node, attr) +#endif + +#include "SkAnimator.h" +#include "SkTime.h" + +/////////////////////////////////////////////////////////////////////////////// + +enum SkinType { + kPushButton_SkinType, + kStaticText_SkinType, + + kSkinTypeCount +}; + +struct SkinSuite { + SkinSuite(); + ~SkinSuite() + { + for (int i = 0; i < kSkinTypeCount; i++) + delete fAnimators[i]; + } + + SkAnimator* get(SkinType); + +private: + SkAnimator* fAnimators[kSkinTypeCount]; +}; + +SkinSuite::SkinSuite() +{ + static const char kSkinPath[] = "skins/"; + + static const char* gSkinNames[] = { + "pushbutton_skin.xml", + "statictext_skin.xml" + }; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gSkinNames); i++) + { + size_t len = strlen(gSkinNames[i]); + SkString path(sizeof(kSkinPath) - 1 + len); + + memcpy(path.writable_str(), kSkinPath, sizeof(kSkinPath) - 1); + memcpy(path.writable_str() + sizeof(kSkinPath) - 1, gSkinNames[i], len); + + fAnimators[i] = new SkAnimator; + if (!fAnimators[i]->decodeURI(path.c_str())) + { + delete fAnimators[i]; + fAnimators[i] = nil; + } + } +} + +SkAnimator* SkinSuite::get(SkinType st) +{ + SkASSERT((unsigned)st < kSkinTypeCount); + return fAnimators[st]; +} + +static SkinSuite* gSkinSuite; + +static SkAnimator* get_skin_animator(SkinType st) +{ +#if 0 + if (gSkinSuite == nil) + gSkinSuite = new SkinSuite; + return gSkinSuite->get(st); +#else + return nil; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkWidget::Init() +{ +} + +void SkWidget::Term() +{ + delete gSkinSuite; +} + +void SkWidget::onEnabledChange() +{ + this->inval(nil); +} + +void SkWidget::postWidgetEvent() +{ + if (!fEvent.isType("") && this->hasListeners()) + { + this->prepareWidgetEvent(&fEvent); + this->postToListeners(fEvent); + } +} + +void SkWidget::prepareWidgetEvent(SkEvent*) +{ + // override in subclass to add any additional fields before posting +} + +void SkWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + if ((node = dom.getFirstChild(node, "event")) != nil) + fEvent.inflate(dom, node); +} + +/////////////////////////////////////////////////////////////////////////////// + +size_t SkHasLabelWidget::getLabel(SkString* str) const +{ + if (str) + *str = fLabel; + return fLabel.size(); +} + +size_t SkHasLabelWidget::getLabel(char buffer[]) const +{ + if (buffer) + memcpy(buffer, fLabel.c_str(), fLabel.size()); + return fLabel.size(); +} + +void SkHasLabelWidget::setLabel(const SkString& str) +{ + this->setLabel(str.c_str(), str.size()); +} + +void SkHasLabelWidget::setLabel(const char label[]) +{ + this->setLabel(label, strlen(label)); +} + +void SkHasLabelWidget::setLabel(const char label[], size_t len) +{ + if (!fLabel.equals(label, len)) + { + fLabel.set(label, len); + this->onLabelChange(); + } +} + +void SkHasLabelWidget::onLabelChange() +{ + // override in subclass +} + +void SkHasLabelWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + const char* text = dom.findAttr(node, "label"); + if (text) + this->setLabel(text); +} + +///////////////////////////////////////////////////////////////////////////////////// + +void SkButtonWidget::setButtonState(State state) +{ + if (fState != state) + { + fState = state; + this->onButtonStateChange(); + } +} + +void SkButtonWidget::onButtonStateChange() +{ + this->inval(nil); +} + +void SkButtonWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + int index; + if ((index = dom.findList(node, "buttonState", "off,on,unknown")) >= 0) + this->setButtonState((State)index); +} + +///////////////////////////////////////////////////////////////////////////////////// + +bool SkPushButtonWidget::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey) + { + this->postWidgetEvent(); + return true; + } + return this->INHERITED::onEvent(evt); +} + +static const char* computeAnimatorState(int enabled, int focused, SkButtonWidget::State state) +{ + if (!enabled) + return "disabled"; + if (state == SkButtonWidget::kOn_State) + { + SkASSERT(focused); + return "enabled-pressed"; + } + if (focused) + return "enabled-focused"; + return "enabled"; +} + +#include "SkBlurMaskFilter.h" +#include "SkEmbossMaskFilter.h" + +static void create_emboss(SkPaint* paint, SkScalar radius, bool focus, bool pressed) +{ + SkEmbossMaskFilter::Light light; + + light.fDirection[0] = SK_Scalar1/2; + light.fDirection[1] = SK_Scalar1/2; + light.fDirection[2] = SK_Scalar1/3; + light.fAmbient = 0x48; + light.fSpecular = 0x80; + + if (pressed) + { + light.fDirection[0] = -light.fDirection[0]; + light.fDirection[1] = -light.fDirection[1]; + } + if (focus) + light.fDirection[2] += SK_Scalar1/4; + + paint->setMaskFilter(new SkEmbossMaskFilter(light, radius))->unref(); +} + +void SkPushButtonWidget::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + SkString label; + this->getLabel(&label); + + SkAnimator* anim = get_skin_animator(kPushButton_SkinType); + + if (anim) + { + SkEvent evt("user"); + + evt.setString("id", "prime"); + evt.setScalar("prime-width", this->width()); + evt.setScalar("prime-height", this->height()); + evt.setString("prime-text", label); + evt.setString("prime-state", computeAnimatorState(this->isEnabled(), this->hasFocus(), this->getButtonState())); + + (void)anim->doUserEvent(evt); + SkPaint paint; + anim->draw(canvas, &paint, SkTime::GetMSecs()); + } + else + { + SkRect r; + SkPaint p; + + r.set(0, 0, this->width(), this->height()); + p.setAntiAliasOn(true); + p.setColor(SK_ColorBLUE); + create_emboss(&p, SkIntToScalar(12)/5, this->hasFocus(), this->getButtonState() == kOn_State); + canvas->drawRoundRect(r, SkScalarHalf(this->height()), SkScalarHalf(this->height()), p); + p.setMaskFilter(nil); + + p.setTextAlign(SkPaint::kCenter_Align); + + SkTextBox box; + box.setMode(SkTextBox::kOneLine_Mode); + box.setSpacingAlign(SkTextBox::kCenter_SpacingAlign); + box.setBox(0, 0, this->width(), this->height()); + +// if (this->getButtonState() == kOn_State) +// p.setColor(SK_ColorRED); +// else + p.setColor(SK_ColorWHITE); + + box.draw(canvas, label.c_str(), label.size(), p); + } +} + +SkView::Click* SkPushButtonWidget::onFindClickHandler(SkScalar x, SkScalar y) +{ + this->acceptFocus(); + return new Click(this); +} + +bool SkPushButtonWidget::onClick(Click* click) +{ + SkRect r; + State state = kOff_State; + + this->getLocalBounds(&r); + if (r.contains(click->fCurr)) + { + if (click->fState == Click::kUp_State) + this->postWidgetEvent(); + else + state = kOn_State; + } + this->setButtonState(state); + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +SkStaticTextView::SkStaticTextView(U32 flags) : SkView(flags) +{ + fMargin.set(0, 0); + fMode = kFixedSize_Mode; + fSpacingAlign = SkTextBox::kStart_SpacingAlign; +} + +SkStaticTextView::~SkStaticTextView() +{ +} + +void SkStaticTextView::computeSize() +{ + if (fMode == kAutoWidth_Mode) + { + SkScalar width = fPaint.measureText(fText.c_str(), fText.size(), nil, nil); + this->setWidth(width + fMargin.fX * 2); + } + else if (fMode == kAutoHeight_Mode) + { + SkScalar width = this->width() - fMargin.fX * 2; + int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0; + + SkScalar before, after; + (void)fPaint.measureText(0, nil, &before, &after); + + this->setHeight(lines * (after - before) + fMargin.fY * 2); + } +} + +void SkStaticTextView::setMode(Mode mode) +{ + SkASSERT((unsigned)mode < kModeCount); + + if (fMode != mode) + { + fMode = SkToU8(mode); + this->computeSize(); + } +} + +void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align) +{ + fSpacingAlign = SkToU8(align); + this->inval(nil); +} + +void SkStaticTextView::getMargin(SkPoint* margin) const +{ + if (margin) + *margin = fMargin; +} + +void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy) +{ + if (fMargin.fX != dx || fMargin.fY != dy) + { + fMargin.set(dx, dy); + this->computeSize(); + this->inval(nil); + } +} + +size_t SkStaticTextView::getText(SkString* text) const +{ + if (text) + *text = fText; + return fText.size(); +} + +size_t SkStaticTextView::getText(char text[]) const +{ + if (text) + memcpy(text, fText.c_str(), fText.size()); + return fText.size(); +} + +void SkStaticTextView::setText(const SkString& text) +{ + this->setText(text.c_str(), text.size()); +} + +void SkStaticTextView::setText(const char text[]) +{ + this->setText(text, strlen(text)); +} + +void SkStaticTextView::setText(const char text[], size_t len) +{ + if (!fText.equals(text, len)) + { + fText.set(text, len); + this->computeSize(); + this->inval(nil); + } +} + +void SkStaticTextView::getPaint(SkPaint* paint) const +{ + if (paint) + *paint = fPaint; +} + +void SkStaticTextView::setPaint(const SkPaint& paint) +{ + if (fPaint != paint) + { + fPaint = paint; + this->computeSize(); + this->inval(nil); + } +} + +void SkStaticTextView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + if (fText.isEmpty()) + return; + + SkTextBox box; + + box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode); + box.setSpacingAlign(this->getSpacingAlign()); + box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY); + box.draw(canvas, fText.c_str(), fText.size(), fPaint); +} + +void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + int index; + if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0) + this->setMode((Mode)index); + else + assert_no_attr(dom, node, "mode"); + + if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0) + this->setSpacingAlign((SkTextBox::SpacingAlign)index); + else + assert_no_attr(dom, node, "mode"); + + SkScalar s[2]; + if (dom.findScalars(node, "margin", s, 2)) + this->setMargin(s[0], s[1]); + else + assert_no_attr(dom, node, "margin"); + + const char* text = dom.findAttr(node, "text"); + if (text) + this->setText(text); + + if ((node = dom.getFirstChild(node, "paint")) != nil) + SkPaint_Inflate(&fPaint, dom, node); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkImageDecoder.h" + +SkBitmapView::SkBitmapView(U32 flags) : SkView(flags) +{ +} + +SkBitmapView::~SkBitmapView() +{ +} + +bool SkBitmapView::getBitmap(SkBitmap* bitmap) const +{ + if (bitmap) + *bitmap = fBitmap; + return fBitmap.getConfig() != SkBitmap::kNo_Config; +} + +void SkBitmapView::setBitmap(const SkBitmap* bitmap, bool viewOwnsPixels) +{ + if (bitmap) + { + fBitmap = *bitmap; + fBitmap.setOwnsPixels(viewOwnsPixels); + } +} + +bool SkBitmapView::loadBitmapFromFile(const char path[]) +{ + SkBitmap bitmap; + + if (SkImageDecoder::DecodeFile(path, &bitmap)) + { + this->setBitmap(&bitmap, true); + bitmap.setOwnsPixels(false); + return true; + } + return false; +} + +void SkBitmapView::onDraw(SkCanvas* canvas) +{ + if (fBitmap.getConfig() != SkBitmap::kNo_Config && + fBitmap.width() && fBitmap.height()) + { + SkAutoCanvasRestore restore(canvas, true); + SkPaint p; + + p.setFilterType(SkPaint::kBilinear_FilterType); + canvas->scale( this->width() / fBitmap.width(), + this->height() / fBitmap.height(), + 0, 0); + canvas->drawBitmap(fBitmap, 0, 0, p); + } +} + +void SkBitmapView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + const char* src = dom.findAttr(node, "src"); + if (src) + (void)this->loadBitmapFromFile(src); +} + +#endif + diff --git a/src/views/SkWindow.cpp b/src/views/SkWindow.cpp new file mode 100644 index 0000000000..5d00d4fb85 --- /dev/null +++ b/src/views/SkWindow.cpp @@ -0,0 +1,367 @@ +#include "SkWindow.h" +#include "SkCanvas.h" +#include "SkOSMenu.h" +#include "SkSystemEventTypes.h" +#include "SkTime.h" + +#define SK_EventDelayInval "\xd" "n" "\xa" "l" + +#define TEST_BOUNDERx + +#include "SkBounder.h" +class test_bounder : public SkBounder { +public: + test_bounder(const SkBitmap& bm) : fCanvas(bm) {} +protected: + virtual bool onIRect(const SkIRect& r) + { + SkRect rr; + + rr.set(SkIntToScalar(r.fLeft), SkIntToScalar(r.fTop), + SkIntToScalar(r.fRight), SkIntToScalar(r.fBottom)); + + SkPaint p; + + p.setStyle(SkPaint::kStroke_Style); + p.setColor(SK_ColorYELLOW); + +#if 0 + rr.inset(SK_ScalarHalf, SK_ScalarHalf); +#else + rr.inset(-SK_ScalarHalf, -SK_ScalarHalf); +#endif + + fCanvas.drawRect(rr, p); + return true; + } +private: + SkCanvas fCanvas; +}; + +SkWindow::SkWindow() : fFocusView(NULL) +{ + fClick = NULL; + fWaitingOnInval = false; + +#ifdef SK_BUILD_FOR_WINCE + fConfig = SkBitmap::kRGB_565_Config; +#else + fConfig = SkBitmap::kARGB_8888_Config; +#endif +} + +SkWindow::~SkWindow() +{ + delete fClick; + + fMenus.deleteAll(); +} + +void SkWindow::setConfig(SkBitmap::Config config) +{ + this->resize(fBitmap.width(), fBitmap.height(), config); +} + +#include "SkImageDecoder.h" + +void SkWindow::resize(int width, int height, SkBitmap::Config config) +{ + if (config == SkBitmap::kNo_Config) + config = fConfig; + + if (width != fBitmap.width() || height != fBitmap.height() || config != fConfig) + { + fConfig = config; + fBitmap.setConfig(config, width, height); + fBitmap.allocPixels(); + + this->setSize(SkIntToScalar(width), SkIntToScalar(height)); + this->inval(NULL); + } + + SkImageDecoder::SetDeviceConfig(fConfig); +} + +void SkWindow::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +{ + fBitmap.eraseARGB(a, r, g, b); +} + +void SkWindow::eraseRGB(U8CPU r, U8CPU g, U8CPU b) +{ + fBitmap.eraseRGB(r, g, b); +} + +bool SkWindow::handleInval(const SkRect& r) +{ + SkIRect ir; + + r.round(&ir); + fDirtyRgn.op(ir, SkRegion::kUnion_Op); + +#ifdef SK_BUILD_FOR_WIN32 + if (!fWaitingOnInval) + { + fWaitingOnInval = true; + (new SkEvent(SK_EventDelayInval))->post(this->getSinkID(), 10); + } +#else + this->onHandleInval(ir); +#endif + return true; +} + +#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN) + #include + #include + extern GXDisplayProperties gDisplayProps; +#endif + +#ifdef SK_SIMULATE_FAILED_MALLOC +extern bool gEnableControlledThrow; +#endif + +bool SkWindow::update(SkIRect* updateArea) +{ + if (!fDirtyRgn.isEmpty()) + { + SkBitmap bm = this->getBitmap(); + +#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN) + char* buffer = (char*)GXBeginDraw(); + SkASSERT(buffer); + + RECT rect; + GetWindowRect((HWND)((SkOSWindow*)this)->getHWND(), &rect); + buffer += rect.top * gDisplayProps.cbyPitch + rect.left * gDisplayProps.cbxPitch; + + bm.setPixels(buffer); +#endif + + SkCanvas canvas(bm); + + canvas.clipRegion(fDirtyRgn); + if (updateArea) + *updateArea = fDirtyRgn.getBounds(); + + // empty this now, so we can correctly record any inval calls that + // might be made during the draw call. + fDirtyRgn.setEmpty(); + +#ifdef TEST_BOUNDER + test_bounder b(bm); + canvas.setBounder(&b); +#endif +#ifdef SK_SIMULATE_FAILED_MALLOC + gEnableControlledThrow = true; +#endif +#ifdef SK_BUILD_FOR_WIN32 + try { + this->draw(&canvas); + } + catch (...) { + } +#else + this->draw(&canvas); +#endif +#ifdef SK_SIMULATE_FAILED_MALLOC + gEnableControlledThrow = false; +#endif +#ifdef TEST_BOUNDER + canvas.setBounder(NULL); +#endif + +#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN) + GXEndDraw(); +#endif + + return true; + } + return false; +} + +bool SkWindow::handleChar(SkUnichar uni) +{ + if (this->onHandleChar(uni)) + return true; + + SkView* focus = this->getFocusView(); + if (focus == NULL) + focus = this; + + SkEvent evt(SK_EventType_Unichar); + evt.setFast32(uni); + return focus->doEvent(evt); +} + +bool SkWindow::handleKey(SkKey key) +{ + if (key == kNONE_SkKey) + return false; + + if (this->onHandleKey(key)) + return true; + + // send an event to the focus-view + { + SkView* focus = this->getFocusView(); + if (focus == NULL) + focus = this; + + SkEvent evt(SK_EventType_Key); + evt.setFast32(key); + if (focus->doEvent(evt)) + return true; + } + + if (key == kUp_SkKey || key == kDown_SkKey) + { + if (this->moveFocus(key == kUp_SkKey ? kPrev_FocusDirection : kNext_FocusDirection) == NULL) + this->onSetFocusView(NULL); + return true; + } + return false; +} + +bool SkWindow::handleKeyUp(SkKey key) +{ + if (key == kNONE_SkKey) + return false; + + if (this->onHandleKeyUp(key)) + return true; + + //send an event to the focus-view + { + SkView* focus = this->getFocusView(); + if (focus == NULL) + focus = this; + + //should this one be the same? + SkEvent evt(SK_EventType_KeyUp); + evt.setFast32(key); + if (focus->doEvent(evt)) + return true; + } + return false; +} + +void SkWindow::addMenu(SkOSMenu* menu) +{ + *fMenus.append() = menu; + this->onAddMenu(menu); +} + +void SkWindow::setTitle(const char title[]) +{ + if (NULL == title) + title = ""; + this->onSetTitle(title); +} + +bool SkWindow::handleMenu(uint32_t cmd) +{ + for (int i = 0; i < fMenus.count(); i++) + { + SkEvent* evt = fMenus[i]->createEvent(cmd); + if (evt) + { + evt->post(this->getSinkID()); + return true; + } + } + return false; +} + +////////////////////////////////////////////////////////////////////// + +bool SkWindow::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventDelayInval)) + { + SkRegion::Iterator iter(fDirtyRgn); + + for (; !iter.done(); iter.next()) + this->onHandleInval(iter.rect()); + fWaitingOnInval = false; + return true; + } + return this->INHERITED::onEvent(evt); +} + +bool SkWindow::onGetFocusView(SkView** focus) const +{ + if (focus) + *focus = fFocusView; + return true; +} + +bool SkWindow::onSetFocusView(SkView* focus) +{ + if (fFocusView != focus) + { + if (fFocusView) + fFocusView->onFocusChange(false); + fFocusView = focus; + if (focus) + focus->onFocusChange(true); + } + return true; +} + +////////////////////////////////////////////////////////////////////// + +void SkWindow::onHandleInval(const SkIRect&) +{ +} + +bool SkWindow::onHandleChar(SkUnichar) +{ + return false; +} + +bool SkWindow::onHandleKey(SkKey key) +{ + return false; +} + +bool SkWindow::onHandleKeyUp(SkKey key) +{ + return false; +} + +bool SkWindow::handleClick(int x, int y, Click::State state) +{ + bool handled = false; + + switch (state) { + case Click::kDown_State: + if (fClick) + delete fClick; + fClick = this->findClickHandler(SkIntToScalar(x), SkIntToScalar(y)); + if (fClick) + { + SkView::DoClickDown(fClick, x, y); + handled = true; + } + break; + case Click::kMoved_State: + if (fClick) + { + SkView::DoClickMoved(fClick, x, y); + handled = true; + } + break; + case Click::kUp_State: + if (fClick) + { + SkView::DoClickUp(fClick, x, y); + delete fClick; + fClick = NULL; + handled = true; + } + break; + } + return handled; +} + -- cgit v1.2.3