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