• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SkListView()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 
~SkListView()32 SkListView::~SkListView()
33 {
34 	SkSafeUnref(fScrollBar);
35 	SkSafeUnref(fSource);
36 	delete[] fAnims;
37 	delete[] fBindings;
38 }
39 
setHasScrollBar(bool hasSB)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 
setSelection(int index)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 
moveSelectionUp()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 
moveSelectionDown()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 
invalSelection()117 void SkListView::invalSelection()
118 {
119 	SkRect	r;
120 	if (this->getRowRect(fCurrIndex, &r))
121 		this->inval(&r);
122 }
123 
ensureSelectionIsVisible()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 
getContentWidth() const152 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 
getRowRect(int index,SkRect * r) const165 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 
setListSource(SkListSource * src)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 
dirtyCache(unsigned dirtyFlags)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 
ensureCache()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 
ensureVisibleRowCount()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 
onSizeChange()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 
onDraw(SkCanvas * canvas)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 
onEvent(const SkEvent & evt)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 
onPrepareWidgetEvent(SkEvent * evt)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 
GetWidgetEventListIndex(const SkEvent & evt)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 
onInflate(const SkDOM & dom,const SkDOM::Node * node)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);
~SkXMLListSource()490 	virtual ~SkXMLListSource()
491 	{
492 		delete[] fFields;
493 		delete[] fRecords;
494 	}
495 
countFields()496 	virtual int countFields() { return fFieldCount; }
getFieldName(int index,SkString * field)497 	virtual void getFieldName(int index, SkString* field)
498 	{
499 		SkASSERT((unsigned)index < (unsigned)fFieldCount);
500 		if (field)
501 			*field = fFields[index];
502 	}
findFieldIndex(const char field[])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 
countRecords()511 	virtual int	countRecords() { return fRecordCount; }
getRecord(int rowIndex,int fieldIndex,SkString * data)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 
prepareWidgetEvent(SkEvent * evt,int rowIndex)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 
SkXMLListSource(const char doc[],size_t len)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 
Factory(const char name[])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