reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame^] | 1 | #include "SkWidget.h" |
| 2 | #include "SkCanvas.h" |
| 3 | #include "SkKey.h" |
| 4 | #include "SkParsePaint.h" |
| 5 | #include "SkSystemEventTypes.h" |
| 6 | #include "SkTextBox.h" |
| 7 | |
| 8 | #if 0 |
| 9 | |
| 10 | #ifdef SK_DEBUG |
| 11 | static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[]) |
| 12 | { |
| 13 | const char* value = dom.findAttr(node, attr); |
| 14 | if (value) |
| 15 | SkDebugf("unknown attribute %s=\"%s\"\n", attr, value); |
| 16 | } |
| 17 | #else |
| 18 | #define assert_no_attr(dom, node, attr) |
| 19 | #endif |
| 20 | |
| 21 | #include "SkAnimator.h" |
| 22 | #include "SkTime.h" |
| 23 | |
| 24 | /////////////////////////////////////////////////////////////////////////////// |
| 25 | |
| 26 | enum SkinType { |
| 27 | kPushButton_SkinType, |
| 28 | kStaticText_SkinType, |
| 29 | |
| 30 | kSkinTypeCount |
| 31 | }; |
| 32 | |
| 33 | struct SkinSuite { |
| 34 | SkinSuite(); |
| 35 | ~SkinSuite() |
| 36 | { |
| 37 | for (int i = 0; i < kSkinTypeCount; i++) |
| 38 | delete fAnimators[i]; |
| 39 | } |
| 40 | |
| 41 | SkAnimator* get(SkinType); |
| 42 | |
| 43 | private: |
| 44 | SkAnimator* fAnimators[kSkinTypeCount]; |
| 45 | }; |
| 46 | |
| 47 | SkinSuite::SkinSuite() |
| 48 | { |
| 49 | static const char kSkinPath[] = "skins/"; |
| 50 | |
| 51 | static const char* gSkinNames[] = { |
| 52 | "pushbutton_skin.xml", |
| 53 | "statictext_skin.xml" |
| 54 | }; |
| 55 | |
| 56 | for (unsigned i = 0; i < SK_ARRAY_COUNT(gSkinNames); i++) |
| 57 | { |
| 58 | size_t len = strlen(gSkinNames[i]); |
| 59 | SkString path(sizeof(kSkinPath) - 1 + len); |
| 60 | |
| 61 | memcpy(path.writable_str(), kSkinPath, sizeof(kSkinPath) - 1); |
| 62 | memcpy(path.writable_str() + sizeof(kSkinPath) - 1, gSkinNames[i], len); |
| 63 | |
| 64 | fAnimators[i] = new SkAnimator; |
| 65 | if (!fAnimators[i]->decodeURI(path.c_str())) |
| 66 | { |
| 67 | delete fAnimators[i]; |
| 68 | fAnimators[i] = nil; |
| 69 | } |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | SkAnimator* SkinSuite::get(SkinType st) |
| 74 | { |
| 75 | SkASSERT((unsigned)st < kSkinTypeCount); |
| 76 | return fAnimators[st]; |
| 77 | } |
| 78 | |
| 79 | static SkinSuite* gSkinSuite; |
| 80 | |
| 81 | static SkAnimator* get_skin_animator(SkinType st) |
| 82 | { |
| 83 | #if 0 |
| 84 | if (gSkinSuite == nil) |
| 85 | gSkinSuite = new SkinSuite; |
| 86 | return gSkinSuite->get(st); |
| 87 | #else |
| 88 | return nil; |
| 89 | #endif |
| 90 | } |
| 91 | |
| 92 | /////////////////////////////////////////////////////////////////////////////// |
| 93 | |
| 94 | void SkWidget::Init() |
| 95 | { |
| 96 | } |
| 97 | |
| 98 | void SkWidget::Term() |
| 99 | { |
| 100 | delete gSkinSuite; |
| 101 | } |
| 102 | |
| 103 | void SkWidget::onEnabledChange() |
| 104 | { |
| 105 | this->inval(nil); |
| 106 | } |
| 107 | |
| 108 | void SkWidget::postWidgetEvent() |
| 109 | { |
| 110 | if (!fEvent.isType("") && this->hasListeners()) |
| 111 | { |
| 112 | this->prepareWidgetEvent(&fEvent); |
| 113 | this->postToListeners(fEvent); |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | void SkWidget::prepareWidgetEvent(SkEvent*) |
| 118 | { |
| 119 | // override in subclass to add any additional fields before posting |
| 120 | } |
| 121 | |
| 122 | void SkWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node) |
| 123 | { |
| 124 | this->INHERITED::onInflate(dom, node); |
| 125 | |
| 126 | if ((node = dom.getFirstChild(node, "event")) != nil) |
| 127 | fEvent.inflate(dom, node); |
| 128 | } |
| 129 | |
| 130 | /////////////////////////////////////////////////////////////////////////////// |
| 131 | |
| 132 | size_t SkHasLabelWidget::getLabel(SkString* str) const |
| 133 | { |
| 134 | if (str) |
| 135 | *str = fLabel; |
| 136 | return fLabel.size(); |
| 137 | } |
| 138 | |
| 139 | size_t SkHasLabelWidget::getLabel(char buffer[]) const |
| 140 | { |
| 141 | if (buffer) |
| 142 | memcpy(buffer, fLabel.c_str(), fLabel.size()); |
| 143 | return fLabel.size(); |
| 144 | } |
| 145 | |
| 146 | void SkHasLabelWidget::setLabel(const SkString& str) |
| 147 | { |
| 148 | this->setLabel(str.c_str(), str.size()); |
| 149 | } |
| 150 | |
| 151 | void SkHasLabelWidget::setLabel(const char label[]) |
| 152 | { |
| 153 | this->setLabel(label, strlen(label)); |
| 154 | } |
| 155 | |
| 156 | void SkHasLabelWidget::setLabel(const char label[], size_t len) |
| 157 | { |
| 158 | if (!fLabel.equals(label, len)) |
| 159 | { |
| 160 | fLabel.set(label, len); |
| 161 | this->onLabelChange(); |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | void SkHasLabelWidget::onLabelChange() |
| 166 | { |
| 167 | // override in subclass |
| 168 | } |
| 169 | |
| 170 | void SkHasLabelWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node) |
| 171 | { |
| 172 | this->INHERITED::onInflate(dom, node); |
| 173 | |
| 174 | const char* text = dom.findAttr(node, "label"); |
| 175 | if (text) |
| 176 | this->setLabel(text); |
| 177 | } |
| 178 | |
| 179 | ///////////////////////////////////////////////////////////////////////////////////// |
| 180 | |
| 181 | void SkButtonWidget::setButtonState(State state) |
| 182 | { |
| 183 | if (fState != state) |
| 184 | { |
| 185 | fState = state; |
| 186 | this->onButtonStateChange(); |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | void SkButtonWidget::onButtonStateChange() |
| 191 | { |
| 192 | this->inval(nil); |
| 193 | } |
| 194 | |
| 195 | void SkButtonWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node) |
| 196 | { |
| 197 | this->INHERITED::onInflate(dom, node); |
| 198 | |
| 199 | int index; |
| 200 | if ((index = dom.findList(node, "buttonState", "off,on,unknown")) >= 0) |
| 201 | this->setButtonState((State)index); |
| 202 | } |
| 203 | |
| 204 | ///////////////////////////////////////////////////////////////////////////////////// |
| 205 | |
| 206 | bool SkPushButtonWidget::onEvent(const SkEvent& evt) |
| 207 | { |
| 208 | if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey) |
| 209 | { |
| 210 | this->postWidgetEvent(); |
| 211 | return true; |
| 212 | } |
| 213 | return this->INHERITED::onEvent(evt); |
| 214 | } |
| 215 | |
| 216 | static const char* computeAnimatorState(int enabled, int focused, SkButtonWidget::State state) |
| 217 | { |
| 218 | if (!enabled) |
| 219 | return "disabled"; |
| 220 | if (state == SkButtonWidget::kOn_State) |
| 221 | { |
| 222 | SkASSERT(focused); |
| 223 | return "enabled-pressed"; |
| 224 | } |
| 225 | if (focused) |
| 226 | return "enabled-focused"; |
| 227 | return "enabled"; |
| 228 | } |
| 229 | |
| 230 | #include "SkBlurMaskFilter.h" |
| 231 | #include "SkEmbossMaskFilter.h" |
| 232 | |
| 233 | static void create_emboss(SkPaint* paint, SkScalar radius, bool focus, bool pressed) |
| 234 | { |
| 235 | SkEmbossMaskFilter::Light light; |
| 236 | |
| 237 | light.fDirection[0] = SK_Scalar1/2; |
| 238 | light.fDirection[1] = SK_Scalar1/2; |
| 239 | light.fDirection[2] = SK_Scalar1/3; |
| 240 | light.fAmbient = 0x48; |
| 241 | light.fSpecular = 0x80; |
| 242 | |
| 243 | if (pressed) |
| 244 | { |
| 245 | light.fDirection[0] = -light.fDirection[0]; |
| 246 | light.fDirection[1] = -light.fDirection[1]; |
| 247 | } |
| 248 | if (focus) |
| 249 | light.fDirection[2] += SK_Scalar1/4; |
| 250 | |
| 251 | paint->setMaskFilter(new SkEmbossMaskFilter(light, radius))->unref(); |
| 252 | } |
| 253 | |
| 254 | void SkPushButtonWidget::onDraw(SkCanvas* canvas) |
| 255 | { |
| 256 | this->INHERITED::onDraw(canvas); |
| 257 | |
| 258 | SkString label; |
| 259 | this->getLabel(&label); |
| 260 | |
| 261 | SkAnimator* anim = get_skin_animator(kPushButton_SkinType); |
| 262 | |
| 263 | if (anim) |
| 264 | { |
| 265 | SkEvent evt("user"); |
| 266 | |
| 267 | evt.setString("id", "prime"); |
| 268 | evt.setScalar("prime-width", this->width()); |
| 269 | evt.setScalar("prime-height", this->height()); |
| 270 | evt.setString("prime-text", label); |
| 271 | evt.setString("prime-state", computeAnimatorState(this->isEnabled(), this->hasFocus(), this->getButtonState())); |
| 272 | |
| 273 | (void)anim->doUserEvent(evt); |
| 274 | SkPaint paint; |
| 275 | anim->draw(canvas, &paint, SkTime::GetMSecs()); |
| 276 | } |
| 277 | else |
| 278 | { |
| 279 | SkRect r; |
| 280 | SkPaint p; |
| 281 | |
| 282 | r.set(0, 0, this->width(), this->height()); |
| 283 | p.setAntiAliasOn(true); |
| 284 | p.setColor(SK_ColorBLUE); |
| 285 | create_emboss(&p, SkIntToScalar(12)/5, this->hasFocus(), this->getButtonState() == kOn_State); |
| 286 | canvas->drawRoundRect(r, SkScalarHalf(this->height()), SkScalarHalf(this->height()), p); |
| 287 | p.setMaskFilter(nil); |
| 288 | |
| 289 | p.setTextAlign(SkPaint::kCenter_Align); |
| 290 | |
| 291 | SkTextBox box; |
| 292 | box.setMode(SkTextBox::kOneLine_Mode); |
| 293 | box.setSpacingAlign(SkTextBox::kCenter_SpacingAlign); |
| 294 | box.setBox(0, 0, this->width(), this->height()); |
| 295 | |
| 296 | // if (this->getButtonState() == kOn_State) |
| 297 | // p.setColor(SK_ColorRED); |
| 298 | // else |
| 299 | p.setColor(SK_ColorWHITE); |
| 300 | |
| 301 | box.draw(canvas, label.c_str(), label.size(), p); |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | SkView::Click* SkPushButtonWidget::onFindClickHandler(SkScalar x, SkScalar y) |
| 306 | { |
| 307 | this->acceptFocus(); |
| 308 | return new Click(this); |
| 309 | } |
| 310 | |
| 311 | bool SkPushButtonWidget::onClick(Click* click) |
| 312 | { |
| 313 | SkRect r; |
| 314 | State state = kOff_State; |
| 315 | |
| 316 | this->getLocalBounds(&r); |
| 317 | if (r.contains(click->fCurr)) |
| 318 | { |
| 319 | if (click->fState == Click::kUp_State) |
| 320 | this->postWidgetEvent(); |
| 321 | else |
| 322 | state = kOn_State; |
| 323 | } |
| 324 | this->setButtonState(state); |
| 325 | return true; |
| 326 | } |
| 327 | |
| 328 | ////////////////////////////////////////////////////////////////////////////////////////// |
| 329 | |
| 330 | SkStaticTextView::SkStaticTextView(U32 flags) : SkView(flags) |
| 331 | { |
| 332 | fMargin.set(0, 0); |
| 333 | fMode = kFixedSize_Mode; |
| 334 | fSpacingAlign = SkTextBox::kStart_SpacingAlign; |
| 335 | } |
| 336 | |
| 337 | SkStaticTextView::~SkStaticTextView() |
| 338 | { |
| 339 | } |
| 340 | |
| 341 | void SkStaticTextView::computeSize() |
| 342 | { |
| 343 | if (fMode == kAutoWidth_Mode) |
| 344 | { |
| 345 | SkScalar width = fPaint.measureText(fText.c_str(), fText.size(), nil, nil); |
| 346 | this->setWidth(width + fMargin.fX * 2); |
| 347 | } |
| 348 | else if (fMode == kAutoHeight_Mode) |
| 349 | { |
| 350 | SkScalar width = this->width() - fMargin.fX * 2; |
| 351 | int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0; |
| 352 | |
| 353 | SkScalar before, after; |
| 354 | (void)fPaint.measureText(0, nil, &before, &after); |
| 355 | |
| 356 | this->setHeight(lines * (after - before) + fMargin.fY * 2); |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | void SkStaticTextView::setMode(Mode mode) |
| 361 | { |
| 362 | SkASSERT((unsigned)mode < kModeCount); |
| 363 | |
| 364 | if (fMode != mode) |
| 365 | { |
| 366 | fMode = SkToU8(mode); |
| 367 | this->computeSize(); |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align) |
| 372 | { |
| 373 | fSpacingAlign = SkToU8(align); |
| 374 | this->inval(nil); |
| 375 | } |
| 376 | |
| 377 | void SkStaticTextView::getMargin(SkPoint* margin) const |
| 378 | { |
| 379 | if (margin) |
| 380 | *margin = fMargin; |
| 381 | } |
| 382 | |
| 383 | void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy) |
| 384 | { |
| 385 | if (fMargin.fX != dx || fMargin.fY != dy) |
| 386 | { |
| 387 | fMargin.set(dx, dy); |
| 388 | this->computeSize(); |
| 389 | this->inval(nil); |
| 390 | } |
| 391 | } |
| 392 | |
| 393 | size_t SkStaticTextView::getText(SkString* text) const |
| 394 | { |
| 395 | if (text) |
| 396 | *text = fText; |
| 397 | return fText.size(); |
| 398 | } |
| 399 | |
| 400 | size_t SkStaticTextView::getText(char text[]) const |
| 401 | { |
| 402 | if (text) |
| 403 | memcpy(text, fText.c_str(), fText.size()); |
| 404 | return fText.size(); |
| 405 | } |
| 406 | |
| 407 | void SkStaticTextView::setText(const SkString& text) |
| 408 | { |
| 409 | this->setText(text.c_str(), text.size()); |
| 410 | } |
| 411 | |
| 412 | void SkStaticTextView::setText(const char text[]) |
| 413 | { |
| 414 | this->setText(text, strlen(text)); |
| 415 | } |
| 416 | |
| 417 | void SkStaticTextView::setText(const char text[], size_t len) |
| 418 | { |
| 419 | if (!fText.equals(text, len)) |
| 420 | { |
| 421 | fText.set(text, len); |
| 422 | this->computeSize(); |
| 423 | this->inval(nil); |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | void SkStaticTextView::getPaint(SkPaint* paint) const |
| 428 | { |
| 429 | if (paint) |
| 430 | *paint = fPaint; |
| 431 | } |
| 432 | |
| 433 | void SkStaticTextView::setPaint(const SkPaint& paint) |
| 434 | { |
| 435 | if (fPaint != paint) |
| 436 | { |
| 437 | fPaint = paint; |
| 438 | this->computeSize(); |
| 439 | this->inval(nil); |
| 440 | } |
| 441 | } |
| 442 | |
| 443 | void SkStaticTextView::onDraw(SkCanvas* canvas) |
| 444 | { |
| 445 | this->INHERITED::onDraw(canvas); |
| 446 | |
| 447 | if (fText.isEmpty()) |
| 448 | return; |
| 449 | |
| 450 | SkTextBox box; |
| 451 | |
| 452 | box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode); |
| 453 | box.setSpacingAlign(this->getSpacingAlign()); |
| 454 | box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY); |
| 455 | box.draw(canvas, fText.c_str(), fText.size(), fPaint); |
| 456 | } |
| 457 | |
| 458 | void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node) |
| 459 | { |
| 460 | this->INHERITED::onInflate(dom, node); |
| 461 | |
| 462 | int index; |
| 463 | if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0) |
| 464 | this->setMode((Mode)index); |
| 465 | else |
| 466 | assert_no_attr(dom, node, "mode"); |
| 467 | |
| 468 | if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0) |
| 469 | this->setSpacingAlign((SkTextBox::SpacingAlign)index); |
| 470 | else |
| 471 | assert_no_attr(dom, node, "mode"); |
| 472 | |
| 473 | SkScalar s[2]; |
| 474 | if (dom.findScalars(node, "margin", s, 2)) |
| 475 | this->setMargin(s[0], s[1]); |
| 476 | else |
| 477 | assert_no_attr(dom, node, "margin"); |
| 478 | |
| 479 | const char* text = dom.findAttr(node, "text"); |
| 480 | if (text) |
| 481 | this->setText(text); |
| 482 | |
| 483 | if ((node = dom.getFirstChild(node, "paint")) != nil) |
| 484 | SkPaint_Inflate(&fPaint, dom, node); |
| 485 | } |
| 486 | |
| 487 | ///////////////////////////////////////////////////////////////////////////////////////////////////// |
| 488 | |
| 489 | #include "SkImageDecoder.h" |
| 490 | |
| 491 | SkBitmapView::SkBitmapView(U32 flags) : SkView(flags) |
| 492 | { |
| 493 | } |
| 494 | |
| 495 | SkBitmapView::~SkBitmapView() |
| 496 | { |
| 497 | } |
| 498 | |
| 499 | bool SkBitmapView::getBitmap(SkBitmap* bitmap) const |
| 500 | { |
| 501 | if (bitmap) |
| 502 | *bitmap = fBitmap; |
| 503 | return fBitmap.getConfig() != SkBitmap::kNo_Config; |
| 504 | } |
| 505 | |
| 506 | void SkBitmapView::setBitmap(const SkBitmap* bitmap, bool viewOwnsPixels) |
| 507 | { |
| 508 | if (bitmap) |
| 509 | { |
| 510 | fBitmap = *bitmap; |
| 511 | fBitmap.setOwnsPixels(viewOwnsPixels); |
| 512 | } |
| 513 | } |
| 514 | |
| 515 | bool SkBitmapView::loadBitmapFromFile(const char path[]) |
| 516 | { |
| 517 | SkBitmap bitmap; |
| 518 | |
| 519 | if (SkImageDecoder::DecodeFile(path, &bitmap)) |
| 520 | { |
| 521 | this->setBitmap(&bitmap, true); |
| 522 | bitmap.setOwnsPixels(false); |
| 523 | return true; |
| 524 | } |
| 525 | return false; |
| 526 | } |
| 527 | |
| 528 | void SkBitmapView::onDraw(SkCanvas* canvas) |
| 529 | { |
| 530 | if (fBitmap.getConfig() != SkBitmap::kNo_Config && |
| 531 | fBitmap.width() && fBitmap.height()) |
| 532 | { |
| 533 | SkAutoCanvasRestore restore(canvas, true); |
| 534 | SkPaint p; |
| 535 | |
| 536 | p.setFilterType(SkPaint::kBilinear_FilterType); |
| 537 | canvas->scale( this->width() / fBitmap.width(), |
| 538 | this->height() / fBitmap.height(), |
| 539 | 0, 0); |
| 540 | canvas->drawBitmap(fBitmap, 0, 0, p); |
| 541 | } |
| 542 | } |
| 543 | |
| 544 | void SkBitmapView::onInflate(const SkDOM& dom, const SkDOM::Node* node) |
| 545 | { |
| 546 | this->INHERITED::onInflate(dom, node); |
| 547 | |
| 548 | const char* src = dom.findAttr(node, "src"); |
| 549 | if (src) |
| 550 | (void)this->loadBitmapFromFile(src); |
| 551 | } |
| 552 | |
| 553 | #endif |
| 554 | |