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