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