1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "TransitionView.h"
8
9#include "OverView.h"
10#include "SampleCode.h"
11#include "SkView.h"
12#include "SkCanvas.h"
13#include "SkTime.h"
14#include "SkInterpolator.h"
15
16static const char gIsTransitionQuery[] = "is-transition";
17static const char gReplaceTransitionEvt[] = "replace-transition-view";
18
19bool is_transition(SkView* view) {
20    SkEvent isTransition(gIsTransitionQuery);
21    return view->doQuery(&isTransition);
22}
23
24class TransitionView : public SampleView {
25    enum {
26        // kDurationMS = 500
27        kDurationMS = 1
28    };
29
30public:
31    TransitionView(SkView* prev, SkView* next, int direction) : fInterp(4, 2){
32        fAnimationDirection = (Direction)(1 << (direction % 8));
33
34        fPrev = prev;
35        fPrev->setClipToBounds(false);
36        fPrev->setVisibleP(true);
37        (void)SampleView::SetUsePipe(fPrev, SkOSMenu::kOffState);
38        //Not calling unref because fPrev is assumed to have been created, so
39        //this will result in a transfer of ownership
40        this->attachChildToBack(fPrev);
41
42        fNext = next;
43        fNext->setClipToBounds(true);
44        fNext->setVisibleP(true);
45        (void)SampleView::SetUsePipe(fNext, SkOSMenu::kOffState);
46        //Calling unref because next is a newly created view and TransitionView
47        //is now the sole owner of fNext
48        this->attachChildToFront(fNext)->unref();
49
50        fDone = false;
51        //SkDebugf("--created transition\n");
52    }
53
54    ~TransitionView(){
55        //SkDebugf("--deleted transition\n");
56    }
57
58    virtual void requestMenu(SkOSMenu* menu) {
59        if (SampleView::IsSampleView(fNext))
60            ((SampleView*)fNext)->requestMenu(menu);
61    }
62
63protected:
64    virtual bool onQuery(SkEvent* evt) {
65        if (SampleCode::TitleQ(*evt)) {
66            SkString title;
67            if (SampleCode::RequestTitle(fNext, &title)) {
68                SampleCode::TitleR(evt, title.c_str());
69                return true;
70            }
71            return false;
72        }
73        if (evt->isType(gIsTransitionQuery)) {
74            return true;
75        }
76        return this->INHERITED::onQuery(evt);
77    }
78    virtual bool onEvent(const SkEvent& evt) {
79        if (evt.isType(gReplaceTransitionEvt)) {
80            SkView* prev = fPrev;
81            prev->ref();
82
83            fPrev->detachFromParent();
84            fPrev = (SkView*)SkEventSink::FindSink(evt.getFast32());
85            (void)SampleView::SetUsePipe(fPrev, SkOSMenu::kOffState);
86            //attach the new fPrev and call unref to balance the ref in onDraw
87            this->attachChildToBack(fPrev)->unref();
88            this->inval(NULL);
89
90            SkASSERT(1 == prev->getRefCnt());
91            prev->unref();
92            return true;
93        }
94        if (evt.isType("transition-done")) {
95            fNext->setLoc(0, 0);
96            fNext->setClipToBounds(false);
97            SkEvent* evt = new SkEvent(gReplaceTransitionEvt,
98                                       this->getParent()->getSinkID());
99            evt->setFast32(fNext->getSinkID());
100            //increate ref count of fNext so it survives detachAllChildren
101            fNext->ref();
102            this->detachAllChildren();
103            evt->post();
104            return true;
105        }
106        return this->INHERITED::onEvent(evt);
107    }
108    virtual void onDrawBackground(SkCanvas* canvas) {}
109    virtual void onDrawContent(SkCanvas* canvas) {
110        if (fDone)
111            return;
112
113        if (is_overview(fNext) || is_overview(fPrev)) {
114            fPipeState = SkOSMenu::kOffState;
115        }
116
117        SkScalar values[4];
118        SkInterpolator::Result result = fInterp.timeToValues(SkTime::GetMSecs(), values);
119        //SkDebugf("transition %x %d pipe:%d\n", this, result, fUsePipe);
120        //SkDebugf("%f %f %f %f %d\n", values[0], values[1], values[2], values[3], result);
121        if (SkInterpolator::kNormal_Result == result) {
122            fPrev->setLocX(values[kPrevX]);
123            fPrev->setLocY(values[kPrevY]);
124            fNext->setLocX(values[kNextX]);
125            fNext->setLocY(values[kNextY]);
126            this->inval(NULL);
127        }
128        else {
129            (new SkEvent("transition-done", this->getSinkID()))->post();
130            fDone = true;
131        }
132    }
133
134    virtual void onSizeChange() {
135        this->INHERITED::onSizeChange();
136
137        fNext->setSize(this->width(), this->height());
138        fPrev->setSize(this->width(), this->height());
139
140        SkScalar lr = 0, ud = 0;
141        if (fAnimationDirection & (kLeftDirection|kULDirection|kDLDirection))
142            lr = this->width();
143        if (fAnimationDirection & (kRightDirection|kURDirection|kDRDirection))
144            lr = -this->width();
145        if (fAnimationDirection & (kUpDirection|kULDirection|kURDirection))
146            ud = this->height();
147        if (fAnimationDirection & (kDownDirection|kDLDirection|kDRDirection))
148            ud = -this->height();
149
150        fBegin[kPrevX] = fBegin[kPrevY] = 0;
151        fBegin[kNextX] = lr;
152        fBegin[kNextY] = ud;
153        fNext->setLocX(lr);
154        fNext->setLocY(ud);
155
156        if (is_transition(fPrev))
157            lr = ud = 0;
158        fEnd[kPrevX] = -lr;
159        fEnd[kPrevY] = -ud;
160        fEnd[kNextX] = fEnd[kNextY] = 0;
161        SkScalar blend[] = { 0.8f, 0.0f,
162                             0.0f, SK_Scalar1 };
163        fInterp.setKeyFrame(0, SkTime::GetMSecs(), fBegin, blend);
164        fInterp.setKeyFrame(1, SkTime::GetMSecs()+kDurationMS, fEnd, blend);
165    }
166
167private:
168    enum {
169        kPrevX = 0,
170        kPrevY = 1,
171        kNextX = 2,
172        kNextY = 3
173    };
174    SkView* fPrev;
175    SkView* fNext;
176    bool    fDone;
177    SkInterpolator fInterp;
178
179    enum Direction{
180        kUpDirection    = 1,
181        kURDirection    = 1 << 1,
182        kRightDirection = 1 << 2,
183        kDRDirection    = 1 << 3,
184        kDownDirection  = 1 << 4,
185        kDLDirection    = 1 << 5,
186        kLeftDirection  = 1 << 6,
187        kULDirection    = 1 << 7
188    };
189
190    Direction fAnimationDirection;
191    SkScalar fBegin[4];
192    SkScalar fEnd[4];
193
194    typedef SampleView INHERITED;
195};
196
197SkView* create_transition(SkView* prev, SkView* next, int direction) {
198#ifdef SK_BUILD_FOR_ANDROID
199    // Disable transitions for Android
200    return next;
201#else
202    return SkNEW_ARGS(TransitionView, (prev, next, direction));
203#endif
204}
205