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