diff options
Diffstat (limited to 'src/views/SkListView.cpp')
-rw-r--r-- | src/views/SkListView.cpp | 895 |
1 files changed, 895 insertions, 0 deletions
diff --git a/src/views/SkListView.cpp b/src/views/SkListView.cpp new file mode 100644 index 0000000..27218a9 --- /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<SkBitmapRef> 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 |