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