/*
* 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] = nullptr;
}
}
}
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 == nullptr)
gSkinSuite = new SkinSuite;
return gSkinSuite->get(st);
#else
return nullptr;
#endif
}
///////////////////////////////////////////////////////////////////////////////
void SkWidget::Init()
{
}
void SkWidget::Term()
{
delete gSkinSuite;
}
void SkWidget::onEnabledChange()
{
this->inval(nullptr);
}
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")) != nullptr)
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(nullptr);
}
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(nullptr);
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(), nullptr, nullptr);
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, nullptr, &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(nullptr);
}
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(nullptr);
}
}
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(nullptr);
}
}
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(nullptr);
}
}
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")) != nullptr)
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