Home | History | Annotate | Download | only in views
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 #include "SkWidgetViews.h"
      9 
     10 #include "SkAnimator.h"
     11 #include "SkScrollBarView.h"
     12 
     13 extern void init_skin_anim(const char name[], SkAnimator*);
     14 
     15 struct SkListView::BindingRec {
     16 	SkString	fSlotName;
     17 	int			fFieldIndex;
     18 };
     19 
     20 SkListView::SkListView()
     21 {
     22 	fSource = NULL;				// our list-source
     23 	fScrollBar = NULL;
     24 	fAnims = NULL;				// array of animators[fVisibleRowCount]
     25 	fBindings = NULL;			// our fields->slot array
     26 	fBindingCount = 0;			// number of entries in fSlots array
     27 	fScrollIndex = 0;			// number of cells to skip before first visible cell
     28 	fCurrIndex = -1;			// index of "selected" cell
     29 	fVisibleRowCount = 0;		// number of cells that can fit in our bounds
     30 	fAnimContentDirty = true;	// true if fAnims[] have their correct content
     31 	fAnimFocusDirty = true;
     32 
     33 	fHeights[kNormal_Height] = SkIntToScalar(16);
     34 	fHeights[kSelected_Height] = SkIntToScalar(16);
     35 
     36 	this->setFlags(this->getFlags() | kFocusable_Mask);
     37 }
     38 
     39 SkListView::~SkListView()
     40 {
     41 	SkSafeUnref(fScrollBar);
     42 	SkSafeUnref(fSource);
     43 	delete[] fAnims;
     44 	delete[] fBindings;
     45 }
     46 
     47 void SkListView::setHasScrollBar(bool hasSB)
     48 {
     49 	if (hasSB != this->hasScrollBar())
     50 	{
     51 		if (hasSB)
     52 		{
     53 			SkASSERT(fScrollBar == NULL);
     54 			fScrollBar = (SkScrollBarView*)SkWidgetFactory(kScroll_WidgetEnum);
     55 			fScrollBar->setVisibleP(true);
     56 			this->attachChildToFront(fScrollBar);
     57 			fScrollBar->setHeight(this->height());	// assume it auto-sets its width
     58 		//	fScrollBar->setLoc(this->getContentWidth(), 0);
     59 			fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
     60 		}
     61 		else
     62 		{
     63 			SkASSERT(fScrollBar);
     64 			fScrollBar->detachFromParent();
     65 			fScrollBar->unref();
     66 			fScrollBar = NULL;
     67 		}
     68 		this->dirtyCache(kAnimContent_DirtyFlag);
     69 	}
     70 }
     71 
     72 void SkListView::setSelection(int index)
     73 {
     74 	if (fCurrIndex != index)
     75 	{
     76 		fAnimFocusDirty = true;
     77 		this->inval(NULL);
     78 
     79 		this->invalSelection();
     80 		fCurrIndex = index;
     81 		this->invalSelection();
     82 		this->ensureSelectionIsVisible();
     83 	}
     84 }
     85 
     86 bool SkListView::moveSelectionUp()
     87 {
     88 	if (fSource)
     89 	{
     90 		int	index = fCurrIndex;
     91 		if (index < 0)	// no selection
     92 			index = fSource->countRecords() - 1;
     93 		else
     94 			index = SkMax32(index - 1, 0);
     95 
     96 		if (fCurrIndex != index)
     97 		{
     98 			this->setSelection(index);
     99 			return true;
    100 		}
    101 	}
    102 	return false;
    103 }
    104 
    105 bool SkListView::moveSelectionDown()
    106 {
    107 	if (fSource)
    108 	{
    109 		int	index = fCurrIndex;
    110 		if (index < 0)	// no selection
    111 			index = 0;
    112 		else
    113 			index = SkMin32(index + 1, fSource->countRecords() - 1);
    114 
    115 		if (fCurrIndex != index)
    116 		{
    117 			this->setSelection(index);
    118 			return true;
    119 		}
    120 	}
    121 	return false;
    122 }
    123 
    124 void SkListView::invalSelection()
    125 {
    126 	SkRect	r;
    127 	if (this->getRowRect(fCurrIndex, &r))
    128 		this->inval(&r);
    129 }
    130 
    131 void SkListView::ensureSelectionIsVisible()
    132 {
    133 	if (fSource && (unsigned)fCurrIndex < (unsigned)fSource->countRecords())
    134 	{
    135 		int index = this->logicalToVisualIndex(fCurrIndex);
    136 
    137 		if ((unsigned)index >= (unsigned)fVisibleRowCount)	// need to scroll
    138 		{
    139 			int newIndex;
    140 
    141 			if (index < 0)	// too high
    142 				newIndex = fCurrIndex;
    143 			else
    144 				newIndex = fCurrIndex - fVisibleRowCount + 1;
    145 			SkASSERT((unsigned)newIndex < (unsigned)fSource->countRecords());
    146 			this->inval(NULL);
    147 
    148 			if (fScrollIndex != newIndex)
    149 			{
    150 				fScrollIndex = newIndex;
    151 				if (fScrollBar)
    152 					fScrollBar->setStart(newIndex);
    153 				this->dirtyCache(kAnimContent_DirtyFlag);
    154 			}
    155 		}
    156 	}
    157 }
    158 
    159 SkScalar SkListView::getContentWidth() const
    160 {
    161 	SkScalar width = this->width();
    162 
    163 	if (fScrollBar)
    164 	{
    165 		width -= fScrollBar->width();
    166 		if (width < 0)
    167 			width = 0;
    168 	}
    169 	return width;
    170 }
    171 
    172 bool SkListView::getRowRect(int index, SkRect* r) const
    173 {
    174 	SkASSERT(r);
    175 
    176 	index = this->logicalToVisualIndex(index);
    177 	if (index >= 0)
    178 	{
    179 		int	selection = this->logicalToVisualIndex(fCurrIndex);
    180 
    181 		SkScalar height = fHeights[index == selection ? kSelected_Height : kNormal_Height];
    182 		SkScalar top = index * fHeights[kNormal_Height];
    183 
    184 		if (index > selection && selection >= 0)
    185 			top += fHeights[kSelected_Height] - fHeights[kNormal_Height];
    186 
    187 		if (top < this->height())
    188 		{
    189 			if (r)
    190 				r->set(0, top, this->getContentWidth(), top + height);
    191 			return true;
    192 		}
    193 	}
    194 	return false;
    195 }
    196 
    197 SkListSource* SkListView::setListSource(SkListSource* src)
    198 {
    199 	if (fSource != src)
    200 	{
    201 		SkRefCnt_SafeAssign(fSource, src);
    202 		this->ensureSelectionIsVisible();
    203 		this->inval(NULL);
    204 
    205 		if (fScrollBar)
    206 			fScrollBar->setTotal(fSource->countRecords());
    207 	}
    208 	return src;
    209 }
    210 
    211 void SkListView::dirtyCache(unsigned dirtyFlags)
    212 {
    213 	if (dirtyFlags & kAnimCount_DirtyFlag)
    214 	{
    215 		delete fAnims;
    216 		fAnims = NULL;
    217 		fAnimContentDirty = true;
    218 		fAnimFocusDirty = true;
    219 	}
    220 	if (dirtyFlags & kAnimContent_DirtyFlag)
    221 	{
    222 		if (!fAnimContentDirty)
    223 		{
    224 			this->inval(NULL);
    225 			fAnimContentDirty = true;
    226 		}
    227 		fAnimFocusDirty = true;
    228 	}
    229 }
    230 
    231 bool SkListView::ensureCache()
    232 {
    233 	if (fSkinName.size() == 0)
    234 		return false;
    235 
    236 	if (fAnims == NULL)
    237 	{
    238 		int n = SkMax32(1, fVisibleRowCount);
    239 
    240 		SkASSERT(fAnimContentDirty);
    241 		fAnims = new SkAnimator[n];
    242 		for (int i = 0; i < n; i++)
    243 		{
    244 			fAnims[i].setHostEventSink(this);
    245 			init_skin_anim(fSkinName.c_str(), &fAnims[i]);
    246 		}
    247 
    248 		fHeights[kNormal_Height] = fAnims[0].getScalar("idleHeight", "value");
    249 		fHeights[kSelected_Height] = fAnims[0].getScalar("focusedHeight", "value");
    250 
    251 		fAnimFocusDirty = true;
    252 	}
    253 
    254 	if (fAnimContentDirty && fSource)
    255 	{
    256 		fAnimContentDirty = false;
    257 
    258 		SkString	str;
    259 		SkEvent		evt("user");
    260 		evt.setString("id", "setFields");
    261 		evt.setS32("rowCount", fVisibleRowCount);
    262 
    263 		SkEvent	dimEvt("user");
    264 		dimEvt.setString("id", "setDim");
    265 		dimEvt.setScalar("dimX", this->getContentWidth());
    266 		dimEvt.setScalar("dimY", this->height());
    267 
    268 		for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
    269 		{
    270 			evt.setS32("relativeIndex", i - fScrollIndex);
    271 			for (int j = 0; j < fBindingCount; j++)
    272 			{
    273 				fSource->getRecord(i, fBindings[j].fFieldIndex, &str);
    274 //SkDEBUGF(("getRecord(%d,%d,%s) slot(%s)\n", i, fBindings[j].fFieldIndex, str.c_str(), fBindings[j].fSlotName.c_str()));
    275 				evt.setString(fBindings[j].fSlotName.c_str(), str.c_str());
    276 			}
    277 			(void)fAnims[i % fVisibleRowCount].doUserEvent(evt);
    278 			(void)fAnims[i % fVisibleRowCount].doUserEvent(dimEvt);
    279 		}
    280 		fAnimFocusDirty = true;
    281 	}
    282 
    283 	if (fAnimFocusDirty)
    284 	{
    285 //SkDEBUGF(("service fAnimFocusDirty\n"));
    286 		fAnimFocusDirty = false;
    287 
    288 		SkEvent		focusEvt("user");
    289 		focusEvt.setString("id", "setFocus");
    290 
    291 		for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
    292 		{
    293 			focusEvt.setS32("FOCUS", i == fCurrIndex);
    294 			(void)fAnims[i % fVisibleRowCount].doUserEvent(focusEvt);
    295 		}
    296 	}
    297 
    298 	return true;
    299 }
    300 
    301 void SkListView::ensureVisibleRowCount()
    302 {
    303 	SkScalar	height = this->height();
    304 	int			n = 0;
    305 
    306 	if (height > 0)
    307 	{
    308 		n = 1;
    309 		height -= fHeights[kSelected_Height];
    310 		if (height > 0)
    311 		{
    312 			SkScalar count = SkScalarDiv(height, fHeights[kNormal_Height]);
    313 			n += SkScalarFloor(count);
    314 			if (count - SkIntToScalar(n) > SK_Scalar1*3/4)
    315 				n += 1;
    316 
    317 		//	SkDebugf("count %g, n %d\n", count/65536., n);
    318 		}
    319 	}
    320 
    321 	if (fVisibleRowCount != n)
    322 	{
    323 		if (fScrollBar)
    324 			fScrollBar->setShown(n);
    325 
    326 		fVisibleRowCount = n;
    327 		this->ensureSelectionIsVisible();
    328 		this->dirtyCache(kAnimCount_DirtyFlag | kAnimContent_DirtyFlag);
    329 	}
    330 }
    331 
    332 ///////////////////////////////////////////////////////////////////////////////////////////////
    333 
    334 #include "SkSystemEventTypes.h"
    335 #include "SkTime.h"
    336 
    337 void SkListView::onSizeChange()
    338 {
    339 	this->INHERITED::onSizeChange();
    340 
    341 	if (fScrollBar)
    342 		fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
    343 
    344 	this->ensureVisibleRowCount();
    345 }
    346 
    347 void SkListView::onDraw(SkCanvas* canvas)
    348 {
    349 	this->INHERITED::onDraw(canvas);
    350 
    351 	this->ensureVisibleRowCount();
    352 
    353 	int	visibleCount = SkMin32(fVisibleRowCount, fSource->countRecords() - fScrollIndex);
    354 	if (visibleCount == 0 || !this->ensureCache())
    355 		return;
    356 
    357 //SkDebugf("visibleCount %d scrollIndex %d currIndex %d\n", visibleCount, fScrollIndex, fCurrIndex);
    358 
    359 	SkAutoCanvasRestore	ar(canvas, true);
    360 	SkMSec				now = SkTime::GetMSecs();
    361 	SkRect				bounds;
    362 
    363 	bounds.fLeft	= 0;
    364 	bounds.fRight	= this->getContentWidth();
    365 	bounds.fBottom	= 0;
    366 	// assign bounds.fTop inside the loop
    367 
    368 	// hack to reveal our bounds for debugging
    369 	if (this->hasFocus())
    370 		canvas->drawARGB(0x11, 0, 0, 0xFF);
    371 	else
    372 		canvas->drawARGB(0x11, 0x88, 0x88, 0x88);
    373 
    374 	for (int i = fScrollIndex; i < fScrollIndex + visibleCount; i++)
    375 	{
    376 		SkPaint	 paint;
    377 		SkScalar height = fHeights[i == fCurrIndex ? kSelected_Height : kNormal_Height];
    378 
    379 		bounds.fTop = bounds.fBottom;
    380 		bounds.fBottom += height;
    381 
    382 		canvas->save();
    383 		if (fAnims[i % fVisibleRowCount].draw(canvas, &paint, now) != SkAnimator::kNotDifferent)
    384 			this->inval(&bounds);
    385 		canvas->restore();
    386 
    387 		canvas->translate(0, height);
    388 	}
    389 }
    390 
    391 bool SkListView::onEvent(const SkEvent& evt)
    392 {
    393 	if (evt.isType(SK_EventType_Key))
    394 	{
    395 		switch (evt.getFast32()) {
    396 		case kUp_SkKey:
    397 			return this->moveSelectionUp();
    398 		case kDown_SkKey:
    399 			return this->moveSelectionDown();
    400 		case kRight_SkKey:
    401 		case kOK_SkKey:
    402 			this->postWidgetEvent();
    403 			return true;
    404 		default:
    405 			break;
    406 		}
    407 	}
    408 	return this->INHERITED::onEvent(evt);
    409 }
    410 
    411 ///////////////////////////////////////////////////////////////////////////////////////////////
    412 
    413 static const char gListViewEventSlot[] = "sk-listview-slot-name";
    414 
    415 /*virtual*/ bool SkListView::onPrepareWidgetEvent(SkEvent* evt)
    416 {
    417 	if (fSource && fCurrIndex >= 0 && this->INHERITED::onPrepareWidgetEvent(evt) &&
    418 		fSource->prepareWidgetEvent(evt, fCurrIndex))
    419 	{
    420 		evt->setS32(gListViewEventSlot, fCurrIndex);
    421 		return true;
    422 	}
    423 	return false;
    424 }
    425 
    426 int SkListView::GetWidgetEventListIndex(const SkEvent& evt)
    427 {
    428 	int32_t	index;
    429 
    430 	return evt.findS32(gListViewEventSlot, &index) ? index : -1;
    431 }
    432 
    433 ///////////////////////////////////////////////////////////////////////////////////////////////
    434 
    435 void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
    436 {
    437 	this->INHERITED::onInflate(dom, node);
    438 
    439 	{
    440 		bool hasScrollBar;
    441 		if (dom.findBool(node, "scrollBar", &hasScrollBar))
    442 			this->setHasScrollBar(hasScrollBar);
    443 	}
    444 
    445 	const SkDOM::Node*	child;
    446 
    447 	if ((child = dom.getFirstChild(node, "bindings")) != NULL)
    448 	{
    449 		delete[] fBindings;
    450 		fBindings = NULL;
    451 		fBindingCount = 0;
    452 
    453 		SkListSource* listSrc = SkListSource::Factory(dom.findAttr(child, "data-fields"));
    454 		SkASSERT(listSrc);
    455 		fSkinName.set(dom.findAttr(child, "skin-slots"));
    456 		SkASSERT(fSkinName.size());
    457 
    458 		this->setListSource(listSrc)->unref();
    459 
    460 		int count = dom.countChildren(child, "bind");
    461 		if (count > 0)
    462 		{
    463 			fBindings = new BindingRec[count];
    464 			count = 0;	// reuse this to count up to the number of valid bindings
    465 
    466 			child = dom.getFirstChild(child, "bind");
    467 			SkASSERT(child);
    468 			do {
    469 				const char* fieldName = dom.findAttr(child, "field");
    470 				const char* slotName = dom.findAttr(child, "slot");
    471 				if (fieldName && slotName)
    472 				{
    473 					fBindings[count].fFieldIndex = listSrc->findFieldIndex(fieldName);
    474 					if (fBindings[count].fFieldIndex >= 0)
    475 						fBindings[count++].fSlotName.set(slotName);
    476 				}
    477 			} while ((child = dom.getNextSibling(child, "bind")) != NULL);
    478 
    479 			fBindingCount = SkToU16(count);
    480 			if (count == 0)
    481 			{
    482 				SkDEBUGF(("SkListView::onInflate: no valid <bind> elements in <listsource>\n"));
    483 				delete[] fBindings;
    484 			}
    485 		}
    486 		this->dirtyCache(kAnimCount_DirtyFlag);
    487 		this->setSelection(0);
    488 	}
    489 }
    490 
    491 /////////////////////////////////////////////////////////////////////////////////////////////
    492 /////////////////////////////////////////////////////////////////////////////////////////////
    493 
    494 class SkXMLListSource : public SkListSource {
    495 public:
    496 	SkXMLListSource(const char doc[], size_t len);
    497 	virtual ~SkXMLListSource()
    498 	{
    499 		delete[] fFields;
    500 		delete[] fRecords;
    501 	}
    502 
    503 	virtual int countFields() { return fFieldCount; }
    504 	virtual void getFieldName(int index, SkString* field)
    505 	{
    506 		SkASSERT((unsigned)index < (unsigned)fFieldCount);
    507 		if (field)
    508 			*field = fFields[index];
    509 	}
    510 	virtual int findFieldIndex(const char field[])
    511 	{
    512 		for (int i = 0; i < fFieldCount; i++)
    513 			if (fFields[i].equals(field))
    514 				return i;
    515 		return -1;
    516 	}
    517 
    518 	virtual int	countRecords() { return fRecordCount; }
    519 	virtual void getRecord(int rowIndex, int fieldIndex, SkString* data)
    520 	{
    521 		SkASSERT((unsigned)rowIndex < (unsigned)fRecordCount);
    522 		SkASSERT((unsigned)fieldIndex < (unsigned)fFieldCount);
    523 		if (data)
    524 			*data = fRecords[rowIndex * fFieldCount + fieldIndex];
    525 	}
    526 
    527 	virtual bool prepareWidgetEvent(SkEvent* evt, int rowIndex)
    528 	{
    529 		// hack, for testing right now. Need the xml to tell us what to jam in and where
    530 		SkString	data;
    531 
    532 		this->getRecord(rowIndex, 0, &data);
    533 		evt->setString("xml-listsource", data.c_str());
    534 		return true;
    535 	}
    536 
    537 private:
    538 	SkString*	fFields;	// [fFieldCount]
    539 	SkString*	fRecords;	// [fRecordCount][fFieldCount]
    540 	int			fFieldCount, fRecordCount;
    541 };
    542 
    543 #include "SkDOM.h"
    544 
    545 SkXMLListSource::SkXMLListSource(const char doc[], size_t len)
    546 {
    547 	fFieldCount = fRecordCount = 0;
    548 	fFields = fRecords = NULL;
    549 
    550 	SkDOM	dom;
    551 
    552 	const SkDOM::Node* node = dom.build(doc, len);
    553 	SkASSERT(node);
    554 	const SkDOM::Node*	child;
    555 
    556 	child = dom.getFirstChild(node, "fields");
    557 	if (child)
    558 	{
    559 		fFieldCount = dom.countChildren(child, "field");
    560 		fFields = new SkString[fFieldCount];
    561 
    562 		int n = 0;
    563 		child = dom.getFirstChild(child, "field");
    564 		while (child)
    565 		{
    566 			fFields[n].set(dom.findAttr(child, "name"));
    567 			child = dom.getNextSibling(child, "field");
    568 			n += 1;
    569 		}
    570 		SkASSERT(n == fFieldCount);
    571 	}
    572 
    573 	child = dom.getFirstChild(node, "records");
    574 	if (child)
    575 	{
    576 		fRecordCount = dom.countChildren(child, "record");
    577 		fRecords = new SkString[fRecordCount * fFieldCount];
    578 
    579 		int n = 0;
    580 		child = dom.getFirstChild(child, "record");
    581 		while (child)
    582 		{
    583 			for (int i = 0; i < fFieldCount; i++)
    584 				fRecords[n * fFieldCount + i].set(dom.findAttr(child, fFields[i].c_str()));
    585 			child = dom.getNextSibling(child, "record");
    586 			n += 1;
    587 		}
    588 		SkASSERT(n == fRecordCount);
    589 	}
    590 }
    591 
    592 /////////////////////////////////////////////////////////////////////////////////////////////
    593 
    594 SkListSource* SkListSource::Factory(const char name[])
    595 {
    596 	static const char gDoc[] =
    597 		"<db name='contacts.db'>"
    598 			"<fields>"
    599 				"<field name='name'/>"
    600 				"<field name='work-num'/>"
    601 				"<field name='home-num'/>"
    602 				"<field name='type'/>"
    603 			"</fields>"
    604 			"<records>"
    605 				"<record name='Andy McFadden' work-num='919 357-1234' home-num='919 123-4567' type='0'/>"
    606 				"<record name='Brian Swetland' work-num='919 123-1234' home-num='929 123-4567' type='1' />"
    607 				"<record name='Chris Desalvo' work-num='919 345-1234' home-num='949 123-4567' type='1' />"
    608 				"<record name='Chris White' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
    609 				"<record name='Dan Bornstein' work-num='919 357-1234' home-num='919 123-4567' type='0' />"
    610 				"<record name='Don Cung' work-num='919 123-1234' home-num='929 123-4567' type='2' />"
    611 				"<record name='Eric Fischer' work-num='919 345-1234' home-num='949 123-4567' type='2' />"
    612 				"<record name='Ficus Kirkpatric' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
    613 				"<record name='Jack Veenstra' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
    614 				"<record name='Jeff Yaksick' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
    615 				"<record name='Joe Onorato' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
    616 				"<record name='Mathias Agopian' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
    617 				"<record name='Mike Fleming' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
    618 				"<record name='Nick Sears' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
    619 				"<record name='Rich Miner' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
    620 				"<record name='Tracey Cole' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
    621 				"<record name='Wei Huang' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
    622 			"</records>"
    623 		"</db>";
    624 
    625 //SkDebugf("doc size %d\n", sizeof(gDoc)-1);
    626 	return new SkXMLListSource(gDoc, sizeof(gDoc) - 1);
    627 }
    628 
    629 
    630 
    631