• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SkListView()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 
~SkListView()39 SkListView::~SkListView()
40 {
41 	SkSafeUnref(fScrollBar);
42 	SkSafeUnref(fSource);
43 	delete[] fAnims;
44 	delete[] fBindings;
45 }
46 
setHasScrollBar(bool hasSB)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 
setSelection(int index)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 
moveSelectionUp()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 
moveSelectionDown()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 
invalSelection()124 void SkListView::invalSelection()
125 {
126 	SkRect	r;
127 	if (this->getRowRect(fCurrIndex, &r))
128 		this->inval(&r);
129 }
130 
ensureSelectionIsVisible()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 
getContentWidth() const159 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 
getRowRect(int index,SkRect * r) const172 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 
setListSource(SkListSource * src)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 
dirtyCache(unsigned dirtyFlags)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 
ensureCache()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 
ensureVisibleRowCount()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 
onSizeChange()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 
onDraw(SkCanvas * canvas)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 
onEvent(const SkEvent & evt)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 
onPrepareWidgetEvent(SkEvent * evt)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 
GetWidgetEventListIndex(const SkEvent & evt)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 
onInflate(const SkDOM & dom,const SkDOM::Node * node)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);
~SkXMLListSource()497 	virtual ~SkXMLListSource()
498 	{
499 		delete[] fFields;
500 		delete[] fRecords;
501 	}
502 
countFields()503 	virtual int countFields() { return fFieldCount; }
getFieldName(int index,SkString * field)504 	virtual void getFieldName(int index, SkString* field)
505 	{
506 		SkASSERT((unsigned)index < (unsigned)fFieldCount);
507 		if (field)
508 			*field = fFields[index];
509 	}
findFieldIndex(const char field[])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 
countRecords()518 	virtual int	countRecords() { return fRecordCount; }
getRecord(int rowIndex,int fieldIndex,SkString * data)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 
prepareWidgetEvent(SkEvent * evt,int rowIndex)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 
SkXMLListSource(const char doc[],size_t len)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 
Factory(const char name[])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