|  |  | 
|  | /* | 
|  | * Copyright 2011 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  | #include "SkWidget.h" | 
|  | #include "SkCanvas.h" | 
|  | #include "SkKey.h" | 
|  | #include "SkParsePaint.h" | 
|  | #include "SkSystemEventTypes.h" | 
|  | #include "SkTextBox.h" | 
|  |  | 
|  | #if 0 | 
|  |  | 
|  | #ifdef SK_DEBUG | 
|  | static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[]) | 
|  | { | 
|  | const char* value = dom.findAttr(node, attr); | 
|  | if (value) | 
|  | SkDebugf("unknown attribute %s=\"%s\"\n", attr, value); | 
|  | } | 
|  | #else | 
|  | #define assert_no_attr(dom, node, attr) | 
|  | #endif | 
|  |  | 
|  | #include "SkAnimator.h" | 
|  | #include "SkTime.h" | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | enum SkinType { | 
|  | kPushButton_SkinType, | 
|  | kStaticText_SkinType, | 
|  |  | 
|  | kSkinTypeCount | 
|  | }; | 
|  |  | 
|  | struct SkinSuite { | 
|  | SkinSuite(); | 
|  | ~SkinSuite() | 
|  | { | 
|  | for (int i = 0; i < kSkinTypeCount; i++) | 
|  | delete fAnimators[i]; | 
|  | } | 
|  |  | 
|  | SkAnimator*    get(SkinType); | 
|  |  | 
|  | private: | 
|  | SkAnimator*    fAnimators[kSkinTypeCount]; | 
|  | }; | 
|  |  | 
|  | SkinSuite::SkinSuite() | 
|  | { | 
|  | static const char kSkinPath[] = "skins/"; | 
|  |  | 
|  | static const char* gSkinNames[] = { | 
|  | "pushbutton_skin.xml", | 
|  | "statictext_skin.xml" | 
|  | }; | 
|  |  | 
|  | for (unsigned i = 0; i < SK_ARRAY_COUNT(gSkinNames); i++) | 
|  | { | 
|  | size_t        len = strlen(gSkinNames[i]); | 
|  | SkString    path(sizeof(kSkinPath) - 1 + len); | 
|  |  | 
|  | memcpy(path.writable_str(), kSkinPath, sizeof(kSkinPath) - 1); | 
|  | memcpy(path.writable_str() + sizeof(kSkinPath) - 1, gSkinNames[i], len); | 
|  |  | 
|  | fAnimators[i] = new SkAnimator; | 
|  | if (!fAnimators[i]->decodeURI(path.c_str())) | 
|  | { | 
|  | delete fAnimators[i]; | 
|  | fAnimators[i] = NULL; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | SkAnimator* SkinSuite::get(SkinType st) | 
|  | { | 
|  | SkASSERT((unsigned)st < kSkinTypeCount); | 
|  | return fAnimators[st]; | 
|  | } | 
|  |  | 
|  | static SkinSuite* gSkinSuite; | 
|  |  | 
|  | static SkAnimator* get_skin_animator(SkinType st) | 
|  | { | 
|  | #if 0 | 
|  | if (gSkinSuite == NULL) | 
|  | gSkinSuite = new SkinSuite; | 
|  | return gSkinSuite->get(st); | 
|  | #else | 
|  | return NULL; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | void SkWidget::Init() | 
|  | { | 
|  | } | 
|  |  | 
|  | void SkWidget::Term() | 
|  | { | 
|  | delete gSkinSuite; | 
|  | } | 
|  |  | 
|  | void SkWidget::onEnabledChange() | 
|  | { | 
|  | this->inval(NULL); | 
|  | } | 
|  |  | 
|  | void SkWidget::postWidgetEvent() | 
|  | { | 
|  | if (!fEvent.isType("") && this->hasListeners()) | 
|  | { | 
|  | this->prepareWidgetEvent(&fEvent); | 
|  | this->postToListeners(fEvent); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkWidget::prepareWidgetEvent(SkEvent*) | 
|  | { | 
|  | // override in subclass to add any additional fields before posting | 
|  | } | 
|  |  | 
|  | void SkWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node) | 
|  | { | 
|  | this->INHERITED::onInflate(dom, node); | 
|  |  | 
|  | if ((node = dom.getFirstChild(node, "event")) != NULL) | 
|  | fEvent.inflate(dom, node); | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | size_t SkHasLabelWidget::getLabel(SkString* str) const | 
|  | { | 
|  | if (str) | 
|  | *str = fLabel; | 
|  | return fLabel.size(); | 
|  | } | 
|  |  | 
|  | size_t SkHasLabelWidget::getLabel(char buffer[]) const | 
|  | { | 
|  | if (buffer) | 
|  | memcpy(buffer, fLabel.c_str(), fLabel.size()); | 
|  | return fLabel.size(); | 
|  | } | 
|  |  | 
|  | void SkHasLabelWidget::setLabel(const SkString& str) | 
|  | { | 
|  | this->setLabel(str.c_str(), str.size()); | 
|  | } | 
|  |  | 
|  | void SkHasLabelWidget::setLabel(const char label[]) | 
|  | { | 
|  | this->setLabel(label, strlen(label)); | 
|  | } | 
|  |  | 
|  | void SkHasLabelWidget::setLabel(const char label[], size_t len) | 
|  | { | 
|  | if (!fLabel.equals(label, len)) | 
|  | { | 
|  | fLabel.set(label, len); | 
|  | this->onLabelChange(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkHasLabelWidget::onLabelChange() | 
|  | { | 
|  | // override in subclass | 
|  | } | 
|  |  | 
|  | void SkHasLabelWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node) | 
|  | { | 
|  | this->INHERITED::onInflate(dom, node); | 
|  |  | 
|  | const char* text = dom.findAttr(node, "label"); | 
|  | if (text) | 
|  | this->setLabel(text); | 
|  | } | 
|  |  | 
|  | ///////////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | void SkButtonWidget::setButtonState(State state) | 
|  | { | 
|  | if (fState != state) | 
|  | { | 
|  | fState = state; | 
|  | this->onButtonStateChange(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkButtonWidget::onButtonStateChange() | 
|  | { | 
|  | this->inval(NULL); | 
|  | } | 
|  |  | 
|  | void SkButtonWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node) | 
|  | { | 
|  | this->INHERITED::onInflate(dom, node); | 
|  |  | 
|  | int    index; | 
|  | if ((index = dom.findList(node, "buttonState", "off,on,unknown")) >= 0) | 
|  | this->setButtonState((State)index); | 
|  | } | 
|  |  | 
|  | ///////////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | bool SkPushButtonWidget::onEvent(const SkEvent& evt) | 
|  | { | 
|  | if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey) | 
|  | { | 
|  | this->postWidgetEvent(); | 
|  | return true; | 
|  | } | 
|  | return this->INHERITED::onEvent(evt); | 
|  | } | 
|  |  | 
|  | static const char* computeAnimatorState(int enabled, int focused, SkButtonWidget::State state) | 
|  | { | 
|  | if (!enabled) | 
|  | return "disabled"; | 
|  | if (state == SkButtonWidget::kOn_State) | 
|  | { | 
|  | SkASSERT(focused); | 
|  | return "enabled-pressed"; | 
|  | } | 
|  | if (focused) | 
|  | return "enabled-focused"; | 
|  | return "enabled"; | 
|  | } | 
|  |  | 
|  | #include "SkBlurMask.h" | 
|  | #include "SkBlurMaskFilter.h" | 
|  | #include "SkEmbossMaskFilter.h" | 
|  |  | 
|  | static void create_emboss(SkPaint* paint, SkScalar radius, bool focus, bool pressed) | 
|  | { | 
|  | SkEmbossMaskFilter::Light    light; | 
|  |  | 
|  | light.fDirection[0] = SK_Scalar1/2; | 
|  | light.fDirection[1] = SK_Scalar1/2; | 
|  | light.fDirection[2] = SK_Scalar1/3; | 
|  | light.fAmbient        = 0x48; | 
|  | light.fSpecular        = 0x80; | 
|  |  | 
|  | if (pressed) | 
|  | { | 
|  | light.fDirection[0] = -light.fDirection[0]; | 
|  | light.fDirection[1] = -light.fDirection[1]; | 
|  | } | 
|  | if (focus) | 
|  | light.fDirection[2] += SK_Scalar1/4; | 
|  |  | 
|  | SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); | 
|  | paint->setMaskFilter(new SkEmbossMaskFilter(sigma, light))->unref(); | 
|  | } | 
|  |  | 
|  | void SkPushButtonWidget::onDraw(SkCanvas* canvas) | 
|  | { | 
|  | this->INHERITED::onDraw(canvas); | 
|  |  | 
|  | SkString label; | 
|  | this->getLabel(&label); | 
|  |  | 
|  | SkAnimator* anim = get_skin_animator(kPushButton_SkinType); | 
|  |  | 
|  | if (anim) | 
|  | { | 
|  | SkEvent    evt("user"); | 
|  |  | 
|  | evt.setString("id", "prime"); | 
|  | evt.setScalar("prime-width", this->width()); | 
|  | evt.setScalar("prime-height", this->height()); | 
|  | evt.setString("prime-text", label); | 
|  | evt.setString("prime-state", computeAnimatorState(this->isEnabled(), this->hasFocus(), this->getButtonState())); | 
|  |  | 
|  | (void)anim->doUserEvent(evt); | 
|  | SkPaint paint; | 
|  | anim->draw(canvas, &paint, SkTime::GetMSecs()); | 
|  | } | 
|  | else | 
|  | { | 
|  | SkRect    r; | 
|  | SkPaint    p; | 
|  |  | 
|  | r.set(0, 0, this->width(), this->height()); | 
|  | p.setAntiAliasOn(true); | 
|  | p.setColor(SK_ColorBLUE); | 
|  | create_emboss(&p, SkIntToScalar(12)/5, this->hasFocus(), this->getButtonState() == kOn_State); | 
|  | canvas->drawRoundRect(r, SkScalarHalf(this->height()), SkScalarHalf(this->height()), p); | 
|  | p.setMaskFilter(NULL); | 
|  |  | 
|  | p.setTextAlign(SkPaint::kCenter_Align); | 
|  |  | 
|  | SkTextBox    box; | 
|  | box.setMode(SkTextBox::kOneLine_Mode); | 
|  | box.setSpacingAlign(SkTextBox::kCenter_SpacingAlign); | 
|  | box.setBox(0, 0, this->width(), this->height()); | 
|  |  | 
|  | //        if (this->getButtonState() == kOn_State) | 
|  | //            p.setColor(SK_ColorRED); | 
|  | //        else | 
|  | p.setColor(SK_ColorWHITE); | 
|  |  | 
|  | box.draw(canvas, label.c_str(), label.size(), p); | 
|  | } | 
|  | } | 
|  |  | 
|  | SkView::Click* SkPushButtonWidget::onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) | 
|  | { | 
|  | this->acceptFocus(); | 
|  | return new Click(this); | 
|  | } | 
|  |  | 
|  | bool SkPushButtonWidget::onClick(Click* click) | 
|  | { | 
|  | SkRect    r; | 
|  | State    state = kOff_State; | 
|  |  | 
|  | this->getLocalBounds(&r); | 
|  | if (r.contains(click->fCurr)) | 
|  | { | 
|  | if (click->fState == Click::kUp_State) | 
|  | this->postWidgetEvent(); | 
|  | else | 
|  | state = kOn_State; | 
|  | } | 
|  | this->setButtonState(state); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | ////////////////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | SkStaticTextView::SkStaticTextView(U32 flags) : SkView(flags) | 
|  | { | 
|  | fMargin.set(0, 0); | 
|  | fMode = kFixedSize_Mode; | 
|  | fSpacingAlign = SkTextBox::kStart_SpacingAlign; | 
|  | } | 
|  |  | 
|  | SkStaticTextView::~SkStaticTextView() | 
|  | { | 
|  | } | 
|  |  | 
|  | void SkStaticTextView::computeSize() | 
|  | { | 
|  | if (fMode == kAutoWidth_Mode) | 
|  | { | 
|  | SkScalar width = fPaint.measureText(fText.c_str(), fText.size(), NULL, NULL); | 
|  | this->setWidth(width + fMargin.fX * 2); | 
|  | } | 
|  | else if (fMode == kAutoHeight_Mode) | 
|  | { | 
|  | SkScalar width = this->width() - fMargin.fX * 2; | 
|  | int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0; | 
|  |  | 
|  | SkScalar    before, after; | 
|  | (void)fPaint.measureText(0, NULL, &before, &after); | 
|  |  | 
|  | this->setHeight(lines * (after - before) + fMargin.fY * 2); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkStaticTextView::setMode(Mode mode) | 
|  | { | 
|  | SkASSERT((unsigned)mode < kModeCount); | 
|  |  | 
|  | if (fMode != mode) | 
|  | { | 
|  | fMode = SkToU8(mode); | 
|  | this->computeSize(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align) | 
|  | { | 
|  | fSpacingAlign = SkToU8(align); | 
|  | this->inval(NULL); | 
|  | } | 
|  |  | 
|  | void SkStaticTextView::getMargin(SkPoint* margin) const | 
|  | { | 
|  | if (margin) | 
|  | *margin = fMargin; | 
|  | } | 
|  |  | 
|  | void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy) | 
|  | { | 
|  | if (fMargin.fX != dx || fMargin.fY != dy) | 
|  | { | 
|  | fMargin.set(dx, dy); | 
|  | this->computeSize(); | 
|  | this->inval(NULL); | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t SkStaticTextView::getText(SkString* text) const | 
|  | { | 
|  | if (text) | 
|  | *text = fText; | 
|  | return fText.size(); | 
|  | } | 
|  |  | 
|  | size_t SkStaticTextView::getText(char text[]) const | 
|  | { | 
|  | if (text) | 
|  | memcpy(text, fText.c_str(), fText.size()); | 
|  | return fText.size(); | 
|  | } | 
|  |  | 
|  | void SkStaticTextView::setText(const SkString& text) | 
|  | { | 
|  | this->setText(text.c_str(), text.size()); | 
|  | } | 
|  |  | 
|  | void SkStaticTextView::setText(const char text[]) | 
|  | { | 
|  | this->setText(text, strlen(text)); | 
|  | } | 
|  |  | 
|  | void SkStaticTextView::setText(const char text[], size_t len) | 
|  | { | 
|  | if (!fText.equals(text, len)) | 
|  | { | 
|  | fText.set(text, len); | 
|  | this->computeSize(); | 
|  | this->inval(NULL); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkStaticTextView::getPaint(SkPaint* paint) const | 
|  | { | 
|  | if (paint) | 
|  | *paint = fPaint; | 
|  | } | 
|  |  | 
|  | void SkStaticTextView::setPaint(const SkPaint& paint) | 
|  | { | 
|  | if (fPaint != paint) | 
|  | { | 
|  | fPaint = paint; | 
|  | this->computeSize(); | 
|  | this->inval(NULL); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkStaticTextView::onDraw(SkCanvas* canvas) | 
|  | { | 
|  | this->INHERITED::onDraw(canvas); | 
|  |  | 
|  | if (fText.isEmpty()) | 
|  | return; | 
|  |  | 
|  | SkTextBox    box; | 
|  |  | 
|  | box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode); | 
|  | box.setSpacingAlign(this->getSpacingAlign()); | 
|  | box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY); | 
|  | box.draw(canvas, fText.c_str(), fText.size(), fPaint); | 
|  | } | 
|  |  | 
|  | void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node) | 
|  | { | 
|  | this->INHERITED::onInflate(dom, node); | 
|  |  | 
|  | int    index; | 
|  | if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0) | 
|  | this->setMode((Mode)index); | 
|  | else | 
|  | assert_no_attr(dom, node, "mode"); | 
|  |  | 
|  | if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0) | 
|  | this->setSpacingAlign((SkTextBox::SpacingAlign)index); | 
|  | else | 
|  | assert_no_attr(dom, node, "mode"); | 
|  |  | 
|  | SkScalar s[2]; | 
|  | if (dom.findScalars(node, "margin", s, 2)) | 
|  | this->setMargin(s[0], s[1]); | 
|  | else | 
|  | assert_no_attr(dom, node, "margin"); | 
|  |  | 
|  | const char* text = dom.findAttr(node, "text"); | 
|  | if (text) | 
|  | this->setText(text); | 
|  |  | 
|  | if ((node = dom.getFirstChild(node, "paint")) != NULL) | 
|  | SkPaint_Inflate(&fPaint, dom, node); | 
|  | } | 
|  |  | 
|  | ///////////////////////////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | #include "SkImageDecoder.h" | 
|  |  | 
|  | SkBitmapView::SkBitmapView(U32 flags) : SkView(flags) | 
|  | { | 
|  | } | 
|  |  | 
|  | SkBitmapView::~SkBitmapView() | 
|  | { | 
|  | } | 
|  |  | 
|  | bool SkBitmapView::getBitmap(SkBitmap* bitmap) const | 
|  | { | 
|  | if (bitmap) | 
|  | *bitmap = fBitmap; | 
|  | return fBitmap.colorType() != kUnknown_SkColorType; | 
|  | } | 
|  |  | 
|  | void SkBitmapView::setBitmap(const SkBitmap* bitmap, bool viewOwnsPixels) | 
|  | { | 
|  | if (bitmap) | 
|  | { | 
|  | fBitmap = *bitmap; | 
|  | fBitmap.setOwnsPixels(viewOwnsPixels); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool SkBitmapView::loadBitmapFromFile(const char path[]) | 
|  | { | 
|  | SkBitmap    bitmap; | 
|  |  | 
|  | if (SkImageDecoder::DecodeFile(path, &bitmap)) | 
|  | { | 
|  | this->setBitmap(&bitmap, true); | 
|  | bitmap.setOwnsPixels(false); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void SkBitmapView::onDraw(SkCanvas* canvas) | 
|  | { | 
|  | if (fBitmap.colorType() != kUnknown_SkColorType && | 
|  | fBitmap.width() && fBitmap.height()) | 
|  | { | 
|  | SkAutoCanvasRestore    restore(canvas, true); | 
|  | SkPaint                p; | 
|  |  | 
|  | p.setFilterType(SkPaint::kBilinear_FilterType); | 
|  | canvas->scale(    this->width() / fBitmap.width(), | 
|  | this->height() / fBitmap.height(), | 
|  | 0, 0); | 
|  | canvas->drawBitmap(fBitmap, 0, 0, p); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkBitmapView::onInflate(const SkDOM& dom, const SkDOM::Node* node) | 
|  | { | 
|  | this->INHERITED::onInflate(dom, node); | 
|  |  | 
|  | const char* src = dom.findAttr(node, "src"); | 
|  | if (src) | 
|  | (void)this->loadBitmapFromFile(src); | 
|  | } | 
|  |  | 
|  | #endif |