+++ /dev/null
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "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 = NULL; // our list-source
- fScrollBar = NULL;
- fAnims = NULL; // array of animators[fVisibleRowCount]
- fBindings = NULL; // 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()
-{
- SkSafeUnref(fScrollBar);
- SkSafeUnref(fSource);
- delete[] fAnims;
- delete[] fBindings;
-}
-
-void SkListView::setHasScrollBar(bool hasSB)
-{
- if (hasSB != this->hasScrollBar())
- {
- if (hasSB)
- {
- SkASSERT(fScrollBar == NULL);
- 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 = NULL;
- }
- this->dirtyCache(kAnimContent_DirtyFlag);
- }
-}
-
-void SkListView::setSelection(int index)
-{
- if (fCurrIndex != index)
- {
- fAnimFocusDirty = true;
- this->inval(NULL);
-
- 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(NULL);
-
- 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(NULL);
-
- if (fScrollBar)
- fScrollBar->setTotal(fSource->countRecords());
- }
- return src;
-}
-
-void SkListView::dirtyCache(unsigned dirtyFlags)
-{
- if (dirtyFlags & kAnimCount_DirtyFlag)
- {
- delete fAnims;
- fAnims = NULL;
- fAnimContentDirty = true;
- fAnimFocusDirty = true;
- }
- if (dirtyFlags & kAnimContent_DirtyFlag)
- {
- if (!fAnimContentDirty)
- {
- this->inval(NULL);
- fAnimContentDirty = true;
- }
- fAnimFocusDirty = true;
- }
-}
-
-bool SkListView::ensureCache()
-{
- if (fSkinName.size() == 0)
- return false;
-
- if (fAnims == NULL)
- {
- 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")) != NULL)
- {
- delete[] fBindings;
- fBindings = NULL;
- 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")) != NULL);
-
- fBindingCount = SkToU16(count);
- if (count == 0)
- {
- SkDEBUGF(("SkListView::onInflate: no valid <bind> elements in <listsource>\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 = NULL;
-
- 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[] =
- "<db name='contacts.db'>"
- "<fields>"
- "<field name='name'/>"
- "<field name='work-num'/>"
- "<field name='home-num'/>"
- "<field name='type'/>"
- "</fields>"
- "<records>"
- "<record name='Andy McFadden' work-num='919 357-1234' home-num='919 123-4567' type='0'/>"
- "<record name='Brian Swetland' work-num='919 123-1234' home-num='929 123-4567' type='1' />"
- "<record name='Chris Desalvo' work-num='919 345-1234' home-num='949 123-4567' type='1' />"
- "<record name='Chris White' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
- "<record name='Dan Bornstein' work-num='919 357-1234' home-num='919 123-4567' type='0' />"
- "<record name='Don Cung' work-num='919 123-1234' home-num='929 123-4567' type='2' />"
- "<record name='Eric Fischer' work-num='919 345-1234' home-num='949 123-4567' type='2' />"
- "<record name='Ficus Kirkpatric' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
- "<record name='Jack Veenstra' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
- "<record name='Jeff Yaksick' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
- "<record name='Joe Onorato' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
- "<record name='Mathias Agopian' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
- "<record name='Mike Fleming' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
- "<record name='Nick Sears' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
- "<record name='Rich Miner' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
- "<record name='Tracey Cole' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
- "<record name='Wei Huang' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
- "</records>"
- "</db>";
-
-//SkDebugf("doc size %d\n", sizeof(gDoc)-1);
- return new SkXMLListSource(gDoc, sizeof(gDoc) - 1);
-}
-
-
-