| |
| /* |
| * 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 "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; |
| |
| paint->setMaskFilter(new SkEmbossMaskFilter(light, radius))->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.getConfig() != SkBitmap::kNo_Config; |
| } |
| |
| 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.getConfig() != SkBitmap::kNo_Config && |
| 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 |