xref: /third_party/skia/tools/viewer/SlideDir.cpp (revision cb93a386)
1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2018 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "tools/viewer/SlideDir.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
11cb93a386Sopenharmony_ci#include "include/core/SkCubicMap.h"
12cb93a386Sopenharmony_ci#include "include/core/SkTypeface.h"
13cb93a386Sopenharmony_ci#include "include/private/SkTPin.h"
14cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGDraw.h"
15cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGGroup.h"
16cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGPaint.h"
17cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGPlane.h"
18cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGRect.h"
19cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGRenderNode.h"
20cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGScene.h"
21cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGText.h"
22cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGTransform.h"
23cb93a386Sopenharmony_ci#include "tools/timer/TimeUtils.h"
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_ci#include <cmath>
26cb93a386Sopenharmony_ci#include <utility>
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_ciclass SlideDir::Animator : public SkRefCnt {
29cb93a386Sopenharmony_cipublic:
30cb93a386Sopenharmony_ci    Animator(const Animator&) = delete;
31cb93a386Sopenharmony_ci    Animator& operator=(const Animator&) = delete;
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ci    void tick(float t) { this->onTick(t); }
34cb93a386Sopenharmony_ci
35cb93a386Sopenharmony_ciprotected:
36cb93a386Sopenharmony_ci    Animator() = default;
37cb93a386Sopenharmony_ci
38cb93a386Sopenharmony_ci    virtual void onTick(float t) = 0;
39cb93a386Sopenharmony_ci};
40cb93a386Sopenharmony_ci
41cb93a386Sopenharmony_cinamespace {
42cb93a386Sopenharmony_ci
43cb93a386Sopenharmony_cistatic constexpr float  kAspectRatio   = 1.5f;
44cb93a386Sopenharmony_cistatic constexpr float  kLabelSize     = 12.0f;
45cb93a386Sopenharmony_cistatic constexpr SkSize kPadding       = { 12.0f , 24.0f };
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_cistatic constexpr float   kFocusDuration = 500;
48cb93a386Sopenharmony_cistatic constexpr SkSize  kFocusInset    = { 100.0f, 100.0f };
49cb93a386Sopenharmony_cistatic constexpr SkPoint kFocusCtrl0    = {   0.3f,   1.0f };
50cb93a386Sopenharmony_cistatic constexpr SkPoint kFocusCtrl1    = {   0.0f,   1.0f };
51cb93a386Sopenharmony_cistatic constexpr SkColor kFocusShade    = 0xa0000000;
52cb93a386Sopenharmony_ci
53cb93a386Sopenharmony_ci// TODO: better unfocus binding?
54cb93a386Sopenharmony_cistatic constexpr SkUnichar kUnfocusKey = ' ';
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ciclass SlideAdapter final : public sksg::RenderNode {
57cb93a386Sopenharmony_cipublic:
58cb93a386Sopenharmony_ci    explicit SlideAdapter(sk_sp<Slide> slide)
59cb93a386Sopenharmony_ci        : fSlide(std::move(slide)) {
60cb93a386Sopenharmony_ci        SkASSERT(fSlide);
61cb93a386Sopenharmony_ci    }
62cb93a386Sopenharmony_ci
63cb93a386Sopenharmony_ci    sk_sp<SlideDir::Animator> makeForwardingAnimator() {
64cb93a386Sopenharmony_ci        // Trivial sksg::Animator -> skottie::Animation tick adapter
65cb93a386Sopenharmony_ci        class ForwardingAnimator final : public SlideDir::Animator {
66cb93a386Sopenharmony_ci        public:
67cb93a386Sopenharmony_ci            explicit ForwardingAnimator(sk_sp<SlideAdapter> adapter)
68cb93a386Sopenharmony_ci                : fAdapter(std::move(adapter)) {}
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_ci        protected:
71cb93a386Sopenharmony_ci            void onTick(float t) override {
72cb93a386Sopenharmony_ci                fAdapter->tick(SkScalarRoundToInt(t));
73cb93a386Sopenharmony_ci            }
74cb93a386Sopenharmony_ci
75cb93a386Sopenharmony_ci        private:
76cb93a386Sopenharmony_ci            sk_sp<SlideAdapter> fAdapter;
77cb93a386Sopenharmony_ci        };
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci        return sk_make_sp<ForwardingAnimator>(sk_ref_sp(this));
80cb93a386Sopenharmony_ci    }
81cb93a386Sopenharmony_ci
82cb93a386Sopenharmony_ciprotected:
83cb93a386Sopenharmony_ci    SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
84cb93a386Sopenharmony_ci        const auto isize = fSlide->getDimensions();
85cb93a386Sopenharmony_ci        return SkRect::MakeIWH(isize.width(), isize.height());
86cb93a386Sopenharmony_ci    }
87cb93a386Sopenharmony_ci
88cb93a386Sopenharmony_ci    void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
89cb93a386Sopenharmony_ci        SkAutoCanvasRestore acr(canvas, true);
90cb93a386Sopenharmony_ci        canvas->clipRect(SkRect::Make(fSlide->getDimensions()), true);
91cb93a386Sopenharmony_ci
92cb93a386Sopenharmony_ci        // TODO: commit the context?
93cb93a386Sopenharmony_ci        fSlide->draw(canvas);
94cb93a386Sopenharmony_ci    }
95cb93a386Sopenharmony_ci
96cb93a386Sopenharmony_ci    const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; }
97cb93a386Sopenharmony_ci
98cb93a386Sopenharmony_ciprivate:
99cb93a386Sopenharmony_ci    void tick(SkMSec t) {
100cb93a386Sopenharmony_ci        fSlide->animate(t * 1e6);
101cb93a386Sopenharmony_ci        this->invalidate();
102cb93a386Sopenharmony_ci    }
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_ci    const sk_sp<Slide> fSlide;
105cb93a386Sopenharmony_ci
106cb93a386Sopenharmony_ci    using INHERITED = sksg::RenderNode;
107cb93a386Sopenharmony_ci};
108cb93a386Sopenharmony_ci
109cb93a386Sopenharmony_ciSkMatrix SlideMatrix(const sk_sp<Slide>& slide, const SkRect& dst) {
110cb93a386Sopenharmony_ci    const auto slideSize = slide->getDimensions();
111cb93a386Sopenharmony_ci    return SkMatrix::RectToRect(SkRect::MakeIWH(slideSize.width(), slideSize.height()), dst,
112cb93a386Sopenharmony_ci                                SkMatrix::kCenter_ScaleToFit);
113cb93a386Sopenharmony_ci}
114cb93a386Sopenharmony_ci
115cb93a386Sopenharmony_ci} // namespace
116cb93a386Sopenharmony_ci
117cb93a386Sopenharmony_cistruct SlideDir::Rec {
118cb93a386Sopenharmony_ci    sk_sp<Slide>                  fSlide;
119cb93a386Sopenharmony_ci    sk_sp<sksg::RenderNode>       fSlideRoot;
120cb93a386Sopenharmony_ci    sk_sp<sksg::Matrix<SkMatrix>> fMatrix;
121cb93a386Sopenharmony_ci    SkRect                        fRect;
122cb93a386Sopenharmony_ci};
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ciclass SlideDir::FocusController final : public Animator {
125cb93a386Sopenharmony_cipublic:
126cb93a386Sopenharmony_ci    FocusController(const SlideDir* dir, const SkRect& focusRect)
127cb93a386Sopenharmony_ci        : fDir(dir)
128cb93a386Sopenharmony_ci        , fRect(focusRect)
129cb93a386Sopenharmony_ci        , fTarget(nullptr)
130cb93a386Sopenharmony_ci        , fMap(kFocusCtrl1, kFocusCtrl0)
131cb93a386Sopenharmony_ci        , fState(State::kIdle) {
132cb93a386Sopenharmony_ci        fShadePaint = sksg::Color::Make(kFocusShade);
133cb93a386Sopenharmony_ci        fShade = sksg::Draw::Make(sksg::Plane::Make(), fShadePaint);
134cb93a386Sopenharmony_ci    }
135cb93a386Sopenharmony_ci
136cb93a386Sopenharmony_ci    bool hasFocus() const { return fState == State::kFocused; }
137cb93a386Sopenharmony_ci
138cb93a386Sopenharmony_ci    void startFocus(const Rec* target) {
139cb93a386Sopenharmony_ci        if (fState != State::kIdle)
140cb93a386Sopenharmony_ci            return;
141cb93a386Sopenharmony_ci
142cb93a386Sopenharmony_ci        fTarget = target;
143cb93a386Sopenharmony_ci
144cb93a386Sopenharmony_ci        // Move the shade & slide to front.
145cb93a386Sopenharmony_ci        fDir->fRoot->removeChild(fTarget->fSlideRoot);
146cb93a386Sopenharmony_ci        fDir->fRoot->addChild(fShade);
147cb93a386Sopenharmony_ci        fDir->fRoot->addChild(fTarget->fSlideRoot);
148cb93a386Sopenharmony_ci
149cb93a386Sopenharmony_ci        fM0 = SlideMatrix(fTarget->fSlide, fTarget->fRect);
150cb93a386Sopenharmony_ci        fM1 = SlideMatrix(fTarget->fSlide, fRect);
151cb93a386Sopenharmony_ci
152cb93a386Sopenharmony_ci        fOpacity0 = 0;
153cb93a386Sopenharmony_ci        fOpacity1 = 1;
154cb93a386Sopenharmony_ci
155cb93a386Sopenharmony_ci        fTimeBase = 0;
156cb93a386Sopenharmony_ci        fState = State::kFocusing;
157cb93a386Sopenharmony_ci
158cb93a386Sopenharmony_ci        // Push initial state to the scene graph.
159cb93a386Sopenharmony_ci        this->onTick(fTimeBase);
160cb93a386Sopenharmony_ci    }
161cb93a386Sopenharmony_ci
162cb93a386Sopenharmony_ci    void startUnfocus() {
163cb93a386Sopenharmony_ci        SkASSERT(fTarget);
164cb93a386Sopenharmony_ci
165cb93a386Sopenharmony_ci        using std::swap;
166cb93a386Sopenharmony_ci        swap(fM0, fM1);
167cb93a386Sopenharmony_ci        swap(fOpacity0, fOpacity1);
168cb93a386Sopenharmony_ci
169cb93a386Sopenharmony_ci        fTimeBase = 0;
170cb93a386Sopenharmony_ci        fState = State::kUnfocusing;
171cb93a386Sopenharmony_ci    }
172cb93a386Sopenharmony_ci
173cb93a386Sopenharmony_ci    bool onMouse(SkScalar x, SkScalar y, skui::InputState state, skui::ModifierKey modifiers) {
174cb93a386Sopenharmony_ci        SkASSERT(fTarget);
175cb93a386Sopenharmony_ci
176cb93a386Sopenharmony_ci        if (!fRect.contains(x, y)) {
177cb93a386Sopenharmony_ci            this->startUnfocus();
178cb93a386Sopenharmony_ci            return true;
179cb93a386Sopenharmony_ci        }
180cb93a386Sopenharmony_ci
181cb93a386Sopenharmony_ci        // Map coords to slide space.
182cb93a386Sopenharmony_ci        const auto xform = SkMatrix::RectToRect(fRect, SkRect::MakeSize(fDir->fWinSize),
183cb93a386Sopenharmony_ci                                                SkMatrix::kCenter_ScaleToFit);
184cb93a386Sopenharmony_ci        const auto pt = xform.mapXY(x, y);
185cb93a386Sopenharmony_ci
186cb93a386Sopenharmony_ci        return fTarget->fSlide->onMouse(pt.x(), pt.y(), state, modifiers);
187cb93a386Sopenharmony_ci    }
188cb93a386Sopenharmony_ci
189cb93a386Sopenharmony_ci    bool onChar(SkUnichar c) {
190cb93a386Sopenharmony_ci        SkASSERT(fTarget);
191cb93a386Sopenharmony_ci
192cb93a386Sopenharmony_ci        return fTarget->fSlide->onChar(c);
193cb93a386Sopenharmony_ci    }
194cb93a386Sopenharmony_ci
195cb93a386Sopenharmony_ciprotected:
196cb93a386Sopenharmony_ci    void onTick(float t) override {
197cb93a386Sopenharmony_ci        if (!this->isAnimating())
198cb93a386Sopenharmony_ci            return;
199cb93a386Sopenharmony_ci
200cb93a386Sopenharmony_ci        if (!fTimeBase) {
201cb93a386Sopenharmony_ci            fTimeBase = t;
202cb93a386Sopenharmony_ci        }
203cb93a386Sopenharmony_ci
204cb93a386Sopenharmony_ci        const auto rel_t = (t - fTimeBase) / kFocusDuration,
205cb93a386Sopenharmony_ci                   map_t = SkTPin(fMap.computeYFromX(rel_t), 0.0f, 1.0f);
206cb93a386Sopenharmony_ci
207cb93a386Sopenharmony_ci        SkMatrix m;
208cb93a386Sopenharmony_ci        for (int i = 0; i < 9; ++i) {
209cb93a386Sopenharmony_ci            m[i] = fM0[i] + map_t * (fM1[i] - fM0[i]);
210cb93a386Sopenharmony_ci        }
211cb93a386Sopenharmony_ci
212cb93a386Sopenharmony_ci        SkASSERT(fTarget);
213cb93a386Sopenharmony_ci        fTarget->fMatrix->setMatrix(m);
214cb93a386Sopenharmony_ci
215cb93a386Sopenharmony_ci        const auto shadeOpacity = fOpacity0 + map_t * (fOpacity1 - fOpacity0);
216cb93a386Sopenharmony_ci        fShadePaint->setOpacity(shadeOpacity);
217cb93a386Sopenharmony_ci
218cb93a386Sopenharmony_ci        if (rel_t < 1)
219cb93a386Sopenharmony_ci            return;
220cb93a386Sopenharmony_ci
221cb93a386Sopenharmony_ci        switch (fState) {
222cb93a386Sopenharmony_ci        case State::kFocusing:
223cb93a386Sopenharmony_ci            fState = State::kFocused;
224cb93a386Sopenharmony_ci            break;
225cb93a386Sopenharmony_ci        case State::kUnfocusing:
226cb93a386Sopenharmony_ci            fState  = State::kIdle;
227cb93a386Sopenharmony_ci            fDir->fRoot->removeChild(fShade);
228cb93a386Sopenharmony_ci            break;
229cb93a386Sopenharmony_ci
230cb93a386Sopenharmony_ci        case State::kIdle:
231cb93a386Sopenharmony_ci        case State::kFocused:
232cb93a386Sopenharmony_ci            SkASSERT(false);
233cb93a386Sopenharmony_ci            break;
234cb93a386Sopenharmony_ci        }
235cb93a386Sopenharmony_ci    }
236cb93a386Sopenharmony_ci
237cb93a386Sopenharmony_ciprivate:
238cb93a386Sopenharmony_ci    enum class State {
239cb93a386Sopenharmony_ci        kIdle,
240cb93a386Sopenharmony_ci        kFocusing,
241cb93a386Sopenharmony_ci        kUnfocusing,
242cb93a386Sopenharmony_ci        kFocused,
243cb93a386Sopenharmony_ci    };
244cb93a386Sopenharmony_ci
245cb93a386Sopenharmony_ci    bool isAnimating() const { return fState == State::kFocusing || fState == State::kUnfocusing; }
246cb93a386Sopenharmony_ci
247cb93a386Sopenharmony_ci    const SlideDir*         fDir;
248cb93a386Sopenharmony_ci    const SkRect            fRect;
249cb93a386Sopenharmony_ci    const Rec*              fTarget;
250cb93a386Sopenharmony_ci
251cb93a386Sopenharmony_ci    SkCubicMap              fMap;
252cb93a386Sopenharmony_ci    sk_sp<sksg::RenderNode> fShade;
253cb93a386Sopenharmony_ci    sk_sp<sksg::PaintNode>  fShadePaint;
254cb93a386Sopenharmony_ci
255cb93a386Sopenharmony_ci    SkMatrix        fM0       = SkMatrix::I(),
256cb93a386Sopenharmony_ci                    fM1       = SkMatrix::I();
257cb93a386Sopenharmony_ci    float           fOpacity0 = 0,
258cb93a386Sopenharmony_ci                    fOpacity1 = 1,
259cb93a386Sopenharmony_ci                    fTimeBase = 0;
260cb93a386Sopenharmony_ci    State           fState    = State::kIdle;
261cb93a386Sopenharmony_ci
262cb93a386Sopenharmony_ci    using INHERITED = Animator;
263cb93a386Sopenharmony_ci};
264cb93a386Sopenharmony_ci
265cb93a386Sopenharmony_ciSlideDir::SlideDir(const SkString& name, SkTArray<sk_sp<Slide>>&& slides, int columns)
266cb93a386Sopenharmony_ci    : fSlides(std::move(slides))
267cb93a386Sopenharmony_ci    , fColumns(columns) {
268cb93a386Sopenharmony_ci    fName = name;
269cb93a386Sopenharmony_ci}
270cb93a386Sopenharmony_ci
271cb93a386Sopenharmony_cistatic sk_sp<sksg::RenderNode> MakeLabel(const SkString& txt,
272cb93a386Sopenharmony_ci                                         const SkPoint& pos,
273cb93a386Sopenharmony_ci                                         const SkMatrix& dstXform) {
274cb93a386Sopenharmony_ci    const auto size = kLabelSize / std::sqrt(dstXform.getScaleX() * dstXform.getScaleY());
275cb93a386Sopenharmony_ci    auto text = sksg::Text::Make(nullptr, txt);
276cb93a386Sopenharmony_ci    text->setEdging(SkFont::Edging::kAntiAlias);
277cb93a386Sopenharmony_ci    text->setSize(size);
278cb93a386Sopenharmony_ci    text->setAlign(SkTextUtils::kCenter_Align);
279cb93a386Sopenharmony_ci    text->setPosition(pos + SkPoint::Make(0, size));
280cb93a386Sopenharmony_ci
281cb93a386Sopenharmony_ci    return sksg::Draw::Make(std::move(text), sksg::Color::Make(SK_ColorBLACK));
282cb93a386Sopenharmony_ci}
283cb93a386Sopenharmony_ci
284cb93a386Sopenharmony_civoid SlideDir::load(SkScalar winWidth, SkScalar winHeight) {
285cb93a386Sopenharmony_ci    // Build a global scene using transformed animation fragments:
286cb93a386Sopenharmony_ci    //
287cb93a386Sopenharmony_ci    // [Group(root)]
288cb93a386Sopenharmony_ci    //     [Transform]
289cb93a386Sopenharmony_ci    //         [Group]
290cb93a386Sopenharmony_ci    //             [AnimationWrapper]
291cb93a386Sopenharmony_ci    //             [Draw]
292cb93a386Sopenharmony_ci    //                 [Text]
293cb93a386Sopenharmony_ci    //                 [Color]
294cb93a386Sopenharmony_ci    //     [Transform]
295cb93a386Sopenharmony_ci    //         [Group]
296cb93a386Sopenharmony_ci    //             [AnimationWrapper]
297cb93a386Sopenharmony_ci    //             [Draw]
298cb93a386Sopenharmony_ci    //                 [Text]
299cb93a386Sopenharmony_ci    //                 [Color]
300cb93a386Sopenharmony_ci    //     ...
301cb93a386Sopenharmony_ci    //
302cb93a386Sopenharmony_ci
303cb93a386Sopenharmony_ci    fWinSize = SkSize::Make(winWidth, winHeight);
304cb93a386Sopenharmony_ci    const auto  cellWidth =  winWidth / fColumns;
305cb93a386Sopenharmony_ci    fCellSize = SkSize::Make(cellWidth, cellWidth / kAspectRatio);
306cb93a386Sopenharmony_ci
307cb93a386Sopenharmony_ci    fRoot = sksg::Group::Make();
308cb93a386Sopenharmony_ci
309cb93a386Sopenharmony_ci    for (int i = 0; i < fSlides.count(); ++i) {
310cb93a386Sopenharmony_ci        const auto& slide     = fSlides[i];
311cb93a386Sopenharmony_ci        slide->load(winWidth, winHeight);
312cb93a386Sopenharmony_ci
313cb93a386Sopenharmony_ci        const auto  slideSize = slide->getDimensions();
314cb93a386Sopenharmony_ci        const auto  cell      = SkRect::MakeXYWH(fCellSize.width()  * (i % fColumns),
315cb93a386Sopenharmony_ci                                                 fCellSize.height() * (i / fColumns),
316cb93a386Sopenharmony_ci                                                 fCellSize.width(),
317cb93a386Sopenharmony_ci                                                 fCellSize.height()),
318cb93a386Sopenharmony_ci                    slideRect = cell.makeInset(kPadding.width(), kPadding.height());
319cb93a386Sopenharmony_ci
320cb93a386Sopenharmony_ci        auto slideMatrix = sksg::Matrix<SkMatrix>::Make(SlideMatrix(slide, slideRect));
321cb93a386Sopenharmony_ci        auto adapter     = sk_make_sp<SlideAdapter>(slide);
322cb93a386Sopenharmony_ci        auto slideGrp    = sksg::Group::Make();
323cb93a386Sopenharmony_ci        slideGrp->addChild(sksg::Draw::Make(sksg::Rect::Make(SkRect::MakeIWH(slideSize.width(),
324cb93a386Sopenharmony_ci                                                                             slideSize.height())),
325cb93a386Sopenharmony_ci                                            sksg::Color::Make(0xfff0f0f0)));
326cb93a386Sopenharmony_ci        slideGrp->addChild(adapter);
327cb93a386Sopenharmony_ci        slideGrp->addChild(MakeLabel(slide->getName(),
328cb93a386Sopenharmony_ci                                     SkPoint::Make(slideSize.width() / 2, slideSize.height()),
329cb93a386Sopenharmony_ci                                     slideMatrix->getMatrix()));
330cb93a386Sopenharmony_ci        auto slideRoot = sksg::TransformEffect::Make(std::move(slideGrp), slideMatrix);
331cb93a386Sopenharmony_ci
332cb93a386Sopenharmony_ci        fSceneAnimators.push_back(adapter->makeForwardingAnimator());
333cb93a386Sopenharmony_ci
334cb93a386Sopenharmony_ci        fRoot->addChild(slideRoot);
335cb93a386Sopenharmony_ci        fRecs.push_back({ slide, slideRoot, slideMatrix, slideRect });
336cb93a386Sopenharmony_ci    }
337cb93a386Sopenharmony_ci
338cb93a386Sopenharmony_ci    fScene = sksg::Scene::Make(fRoot);
339cb93a386Sopenharmony_ci
340cb93a386Sopenharmony_ci    const auto focusRect = SkRect::MakeSize(fWinSize).makeInset(kFocusInset.width(),
341cb93a386Sopenharmony_ci                                                                kFocusInset.height());
342cb93a386Sopenharmony_ci    fFocusController = std::make_unique<FocusController>(this, focusRect);
343cb93a386Sopenharmony_ci}
344cb93a386Sopenharmony_ci
345cb93a386Sopenharmony_civoid SlideDir::unload() {
346cb93a386Sopenharmony_ci    for (const auto& slide : fSlides) {
347cb93a386Sopenharmony_ci        slide->unload();
348cb93a386Sopenharmony_ci    }
349cb93a386Sopenharmony_ci
350cb93a386Sopenharmony_ci    fRecs.reset();
351cb93a386Sopenharmony_ci    fScene.reset();
352cb93a386Sopenharmony_ci    fFocusController.reset();
353cb93a386Sopenharmony_ci    fRoot.reset();
354cb93a386Sopenharmony_ci    fTimeBase = 0;
355cb93a386Sopenharmony_ci}
356cb93a386Sopenharmony_ci
357cb93a386Sopenharmony_ciSkISize SlideDir::getDimensions() const {
358cb93a386Sopenharmony_ci    return SkSize::Make(fWinSize.width(),
359cb93a386Sopenharmony_ci                        fCellSize.height() * (1 + (fSlides.count() - 1) / fColumns)).toCeil();
360cb93a386Sopenharmony_ci}
361cb93a386Sopenharmony_ci
362cb93a386Sopenharmony_civoid SlideDir::draw(SkCanvas* canvas) {
363cb93a386Sopenharmony_ci    fScene->render(canvas);
364cb93a386Sopenharmony_ci}
365cb93a386Sopenharmony_ci
366cb93a386Sopenharmony_cibool SlideDir::animate(double nanos) {
367cb93a386Sopenharmony_ci    SkMSec msec = TimeUtils::NanosToMSec(nanos);
368cb93a386Sopenharmony_ci    if (fTimeBase == 0) {
369cb93a386Sopenharmony_ci        // Reset the animation time.
370cb93a386Sopenharmony_ci        fTimeBase = msec;
371cb93a386Sopenharmony_ci    }
372cb93a386Sopenharmony_ci
373cb93a386Sopenharmony_ci    const auto t = msec - fTimeBase;
374cb93a386Sopenharmony_ci    for (const auto& anim : fSceneAnimators) {
375cb93a386Sopenharmony_ci        anim->tick(t);
376cb93a386Sopenharmony_ci    }
377cb93a386Sopenharmony_ci    fFocusController->tick(t);
378cb93a386Sopenharmony_ci
379cb93a386Sopenharmony_ci    return true;
380cb93a386Sopenharmony_ci}
381cb93a386Sopenharmony_ci
382cb93a386Sopenharmony_cibool SlideDir::onChar(SkUnichar c) {
383cb93a386Sopenharmony_ci    if (fFocusController->hasFocus()) {
384cb93a386Sopenharmony_ci        if (c == kUnfocusKey) {
385cb93a386Sopenharmony_ci            fFocusController->startUnfocus();
386cb93a386Sopenharmony_ci            return true;
387cb93a386Sopenharmony_ci        }
388cb93a386Sopenharmony_ci        return fFocusController->onChar(c);
389cb93a386Sopenharmony_ci    }
390cb93a386Sopenharmony_ci
391cb93a386Sopenharmony_ci    return false;
392cb93a386Sopenharmony_ci}
393cb93a386Sopenharmony_ci
394cb93a386Sopenharmony_cibool SlideDir::onMouse(SkScalar x, SkScalar y, skui::InputState state,
395cb93a386Sopenharmony_ci                       skui::ModifierKey modifiers) {
396cb93a386Sopenharmony_ci    modifiers &= ~skui::ModifierKey::kFirstPress;
397cb93a386Sopenharmony_ci    if (state == skui::InputState::kMove || sknonstd::Any(modifiers))
398cb93a386Sopenharmony_ci        return false;
399cb93a386Sopenharmony_ci
400cb93a386Sopenharmony_ci    if (fFocusController->hasFocus()) {
401cb93a386Sopenharmony_ci        return fFocusController->onMouse(x, y, state, modifiers);
402cb93a386Sopenharmony_ci    }
403cb93a386Sopenharmony_ci
404cb93a386Sopenharmony_ci    const auto* cell = this->findCell(x, y);
405cb93a386Sopenharmony_ci    if (!cell)
406cb93a386Sopenharmony_ci        return false;
407cb93a386Sopenharmony_ci
408cb93a386Sopenharmony_ci    static constexpr SkScalar kClickMoveTolerance = 4;
409cb93a386Sopenharmony_ci
410cb93a386Sopenharmony_ci    switch (state) {
411cb93a386Sopenharmony_ci    case skui::InputState::kDown:
412cb93a386Sopenharmony_ci        fTrackingCell = cell;
413cb93a386Sopenharmony_ci        fTrackingPos = SkPoint::Make(x, y);
414cb93a386Sopenharmony_ci        break;
415cb93a386Sopenharmony_ci    case skui::InputState::kUp:
416cb93a386Sopenharmony_ci        if (cell == fTrackingCell &&
417cb93a386Sopenharmony_ci            SkPoint::Distance(fTrackingPos, SkPoint::Make(x, y)) < kClickMoveTolerance) {
418cb93a386Sopenharmony_ci            fFocusController->startFocus(cell);
419cb93a386Sopenharmony_ci        }
420cb93a386Sopenharmony_ci        break;
421cb93a386Sopenharmony_ci    default:
422cb93a386Sopenharmony_ci        break;
423cb93a386Sopenharmony_ci    }
424cb93a386Sopenharmony_ci
425cb93a386Sopenharmony_ci    return false;
426cb93a386Sopenharmony_ci}
427cb93a386Sopenharmony_ci
428cb93a386Sopenharmony_ciconst SlideDir::Rec* SlideDir::findCell(float x, float y) const {
429cb93a386Sopenharmony_ci    // TODO: use SG hit testing instead of layout info?
430cb93a386Sopenharmony_ci    const auto size = this->getDimensions();
431cb93a386Sopenharmony_ci    if (x < 0 || y < 0 || x >= size.width() || y >= size.height()) {
432cb93a386Sopenharmony_ci        return nullptr;
433cb93a386Sopenharmony_ci    }
434cb93a386Sopenharmony_ci
435cb93a386Sopenharmony_ci    const int col = static_cast<int>(x / fCellSize.width()),
436cb93a386Sopenharmony_ci              row = static_cast<int>(y / fCellSize.height()),
437cb93a386Sopenharmony_ci              idx = row * fColumns + col;
438cb93a386Sopenharmony_ci
439cb93a386Sopenharmony_ci    return idx < fRecs.count() ? &fRecs[idx] : nullptr;
440cb93a386Sopenharmony_ci}
441