Add keyboard support and animation control in SceneLoader demo 00/269100/8
authorRichard Huang <r.huang@samsung.com>
Fri, 7 Jan 2022 12:11:56 +0000 (12:11 +0000)
committerRichard Huang <r.huang@samsung.com>
Mon, 17 Jan 2022 18:05:19 +0000 (18:05 +0000)
Change-Id: Ic69ba19f05ab957f591923cd1381046568685a26

examples/scene-loader/scene-loader-example.cpp
examples/scene-loader/scene-loader-example.h
examples/scene-loader/scene-loader-extension.h [new file with mode: 0644]

index 13d7681..417ba6b 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.\r
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.\r
  *\r
  * Licensed under the Apache License, Version 2.0 (the "License");\r
  * you may not use this file except in compliance with the License.\r
@@ -15,6 +15,7 @@
  *\r
  */\r
 #include "scene-loader-example.h"\r
+#include <dali-toolkit/dali-toolkit.h>\r
 #include <dirent.h>\r
 #include <cstring>\r
 #include <string_view>\r
@@ -32,6 +33,7 @@
 #include "dali/public-api/events/key-event.h"\r
 #include "dali/public-api/object/property-array.h"\r
 #include "dali/public-api/render-tasks/render-task-list.h"\r
+#include "scene-loader-extension.h"\r
 \r
 using namespace Dali;\r
 using namespace Dali::Toolkit;\r
@@ -80,10 +82,11 @@ StringVector ListFiles(
 \r
 TextLabel MakeLabel(std::string msg)\r
 {\r
-  TextLabel label = TextLabel::New(msg);\r
+  TextLabel label = TextLabel::New("  " + msg);\r
   label.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);\r
   label.SetProperty(TextLabel::Property::TEXT_COLOR, Color::WHITE);\r
   label.SetProperty(TextLabel::Property::PIXEL_SIZE, ITEM_HEIGHT * 4 / 7);\r
+  label.SetProperty(TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER");\r
   SetActorCentered(label);\r
   return label;\r
 }\r
@@ -108,6 +111,7 @@ struct ItemFactoryImpl : Dali::Toolkit::ItemFactory
   {\r
     auto label = MakeLabel(mSceneNames[itemId]);\r
     mTapDetector.Attach(label);\r
+    label.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);\r
     return label;\r
   }\r
 };\r
@@ -172,7 +176,7 @@ void ConfigureBlendShapeShaders(ResourceBundle& resources, const SceneDefinition
   }\r
 }\r
 \r
-Actor LoadScene(std::string sceneName, CameraActor camera)\r
+Actor LoadScene(std::string sceneName, CameraActor camera, std::vector<AnimationDefinition>* animations, Animation& animation)\r
 {\r
   ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) {\r
     return Application::GetResourcePath() + RESOURCE_TYPE_DIRS[type];\r
@@ -185,12 +189,13 @@ Actor LoadScene(std::string sceneName, CameraActor camera)
   std::vector<AnimationGroupDefinition> animGroups;\r
   std::vector<CameraParameters>         cameraParameters;\r
   std::vector<LightParameters>          lights;\r
-  std::vector<AnimationDefinition>      animations;\r
+\r
+  animations->clear();\r
 \r
   LoadResult output{\r
     resources,\r
     scene,\r
-    animations,\r
+    *animations,\r
     animGroups,\r
     cameraParameters,\r
     lights};\r
@@ -262,13 +267,14 @@ Actor LoadScene(std::string sceneName, CameraActor camera)
     }\r
   }\r
 \r
-  if(!animations.empty())\r
+  if(!animations->empty())\r
   {\r
     auto getActor = [&root](const std::string& name) {\r
       return root.FindChildByName(name);\r
     };\r
 \r
-    animations[0].ReAnimate(getActor).Play();\r
+    animation = (*animations)[0].ReAnimate(getActor);\r
+    animation.Play();\r
   }\r
 \r
   return root;\r
@@ -277,7 +283,8 @@ Actor LoadScene(std::string sceneName, CameraActor camera)
 } // namespace\r
 \r
 SceneLoaderExample::SceneLoaderExample(Dali::Application& app)\r
-: mApp(app)\r
+: mApp(app),\r
+  mSceneLoaderExtension(new SceneLoaderExtension())\r
 {\r
   if(!std::getenv("DALI_APPLICATION_PACKAGE"))\r
   {\r
@@ -295,6 +302,8 @@ SceneLoaderExample::SceneLoaderExample(Dali::Application& app)
   app.TerminateSignal().Connect(this, &SceneLoaderExample::OnTerminate);\r
 }\r
 \r
+SceneLoaderExample::~SceneLoaderExample() = default;\r
+\r
 void SceneLoaderExample::OnInit(Application& app)\r
 {\r
   // get scenes\r
@@ -346,13 +355,20 @@ void SceneLoaderExample::OnInit(Application& app)
 \r
   Vector3 windowSize(window.GetSize());\r
   auto    itemLayout = DefaultItemLayout::New(DefaultItemLayout::LIST);\r
-  itemLayout->SetItemSize(Vector3(windowSize.x, ITEM_HEIGHT, 1.f));\r
+  itemLayout->SetItemSize(Vector3(windowSize.x * 0.9f, ITEM_HEIGHT, 1.f));\r
   items.AddLayout(*itemLayout);\r
   navigationView.Push(items);\r
 \r
   mItemLayout = itemLayout;\r
   mItemView   = items;\r
 \r
+  mItemView.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);\r
+  KeyboardFocusManager::Get().PreFocusChangeSignal().Connect(this, &SceneLoaderExample::OnKeyboardPreFocusChange);\r
+  KeyboardFocusManager::Get().FocusedActorEnterKeySignal().Connect(this, &SceneLoaderExample::OnKeyboardFocusedActorActivated);\r
+  KeyboardFocusManager::Get().FocusChangedSignal().Connect(this, &SceneLoaderExample::OnKeyboardFocusChanged);\r
+\r
+  SetActorCentered(KeyboardFocusManager::Get().GetFocusIndicatorActor());\r
+\r
   // camera\r
   auto camera = CameraActor::New();\r
   camera.SetInvertYAxis(true);\r
@@ -366,7 +382,36 @@ void SceneLoaderExample::OnInit(Application& app)
   mTapDetector = tapDetector;\r
 \r
   // activate layout\r
-  mItemView.ActivateLayout(0, windowSize, 1.f);\r
+  mItemView.ActivateLayout(0, windowSize, 0.f);\r
+\r
+  mSceneLoaderExtension->SetSceneLoader(this);\r
+}\r
+\r
+Actor SceneLoaderExample::OnKeyboardPreFocusChange(Actor current, Actor proposed, Control::KeyboardFocus::Direction direction)\r
+{\r
+  if(!current && !proposed)\r
+  {\r
+    return mItemView;\r
+  }\r
+\r
+  return proposed;\r
+}\r
+\r
+void SceneLoaderExample::OnKeyboardFocusedActorActivated(Actor activatedActor)\r
+{\r
+  if(activatedActor)\r
+  {\r
+    OnTap(activatedActor, Dali::TapGesture());\r
+  }\r
+}\r
+\r
+void SceneLoaderExample::OnKeyboardFocusChanged(Actor originalFocusedActor, Actor currentFocusedActor)\r
+{\r
+  if(currentFocusedActor)\r
+  {\r
+    auto itemId = mItemView.GetItemId(currentFocusedActor);\r
+    mItemView.ScrollToItem(itemId, 0.1f);\r
+  }\r
 }\r
 \r
 void SceneLoaderExample::OnTerminate(Application& app)\r
@@ -397,12 +442,21 @@ void SceneLoaderExample::OnKey(const KeyEvent& e)
 \r
         mNavigationView.Pop();\r
         mScene.Reset();\r
+\r
+        if(mActivatedActor)\r
+        {\r
+          KeyboardFocusManager::Get().SetCurrentFocusActor(mActivatedActor);\r
+        }\r
       }\r
       else\r
       {\r
         mApp.Quit();\r
       }\r
     }\r
+    else\r
+    {\r
+      mSceneLoaderExtension->OnKey(e);\r
+    }\r
   }\r
 }\r
 \r
@@ -424,6 +478,8 @@ void SceneLoaderExample::OnPan(Actor actor, const PanGesture& pan)
 \r
 void SceneLoaderExample::OnTap(Dali::Actor actor, const Dali::TapGesture& tap)\r
 {\r
+  mActivatedActor = actor;\r
+\r
   auto id = mItemView.GetItemId(actor);\r
 \r
   try\r
@@ -432,7 +488,7 @@ void SceneLoaderExample::OnTap(Dali::Actor actor, const Dali::TapGesture& tap)
     auto renderTasks = window.GetRenderTaskList();\r
     renderTasks.RemoveTask(mSceneRender);\r
 \r
-    auto scene = LoadScene(mSceneNames[id], mSceneCamera);\r
+    auto scene = LoadScene(mSceneNames[id], mSceneCamera, &mSceneAnimations, mCurrentAnimation);\r
 \r
     auto sceneRender = renderTasks.CreateTask();\r
     sceneRender.SetCameraActor(mSceneCamera);\r
@@ -452,4 +508,6 @@ void SceneLoaderExample::OnTap(Dali::Actor actor, const Dali::TapGesture& tap)
   }\r
 \r
   mNavigationView.Push(mScene);\r
+\r
+  mSceneLoaderExtension->ConnectTouchSignals();\r
 }\r
index b18c830..a516dbe 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef SCENE_LAUNCHER_H_\r
 #define SCENE_LAUNCHER_H_\r
 /*\r
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.\r
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.\r
  *\r
  * Licensed under the Apache License, Version 2.0 (the "License");\r
  * you may not use this file except in compliance with the License.\r
@@ -16,6 +16,8 @@
  * limitations under the License.\r
  *\r
  */\r
+\r
+#include <memory>\r
 #include "dali-scene-loader/public-api/animation-definition.h"\r
 #include "dali-scene-loader/public-api/camera-parameters.h"\r
 #include "dali-scene-loader/public-api/node-definition.h"\r
 #include "dali/public-api/render-tasks/render-task.h"\r
 #include "dali/public-api/signals/connection-tracker.h"\r
 \r
+class SceneLoaderExtension;\r
+\r
 class SceneLoaderExample : public Dali::ConnectionTracker\r
 {\r
 public:\r
   SceneLoaderExample(Dali::Application& app);\r
-  ~SceneLoaderExample() = default;\r
+  ~SceneLoaderExample();\r
 \r
 private: // data\r
   Dali::Application& mApp;\r
@@ -50,13 +54,22 @@ private: // data
 \r
   Dali::CameraActor mSceneCamera;\r
   Dali::RenderTask  mSceneRender;\r
-  Dali::Actor       mScene;\r
 \r
   Dali::Quaternion mCameraOrientationInv;\r
 \r
   Dali::TapGestureDetector mTapDetector;\r
   Dali::PanGestureDetector mPanDetector;\r
 \r
+  Dali::Actor mActivatedActor;\r
+\r
+public:\r
+  Dali::Actor mScene;\r
+\r
+  std::vector<Dali::SceneLoader::AnimationDefinition> mSceneAnimations;\r
+  Dali::Animation                                     mCurrentAnimation;\r
+\r
+  std::unique_ptr<SceneLoaderExtension> mSceneLoaderExtension;\r
+\r
 private: // methods\r
   void OnInit(Dali::Application& app);\r
   void OnTerminate(Dali::Application& app);\r
@@ -64,6 +77,10 @@ private: // methods
   void OnKey(const Dali::KeyEvent& e);\r
   void OnPan(Dali::Actor actor, const Dali::PanGesture& pan);\r
   void OnTap(Dali::Actor actor, const Dali::TapGesture& tap);\r
+\r
+  Dali::Actor OnKeyboardPreFocusChange(Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction);\r
+  void        OnKeyboardFocusedActorActivated(Dali::Actor activatedActor);\r
+  void        OnKeyboardFocusChanged(Dali::Actor originalFocusedActor, Dali::Actor currentFocusedActor);\r
 };\r
 \r
 #endif //SCENE_LAUNCHER_H_\r
diff --git a/examples/scene-loader/scene-loader-extension.h b/examples/scene-loader/scene-loader-extension.h
new file mode 100644 (file)
index 0000000..e937234
--- /dev/null
@@ -0,0 +1,221 @@
+/*\r
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ */\r
+\r
+#include <dali-toolkit/dali-toolkit.h>\r
+#include <dali/dali.h>\r
+#include <cstring>\r
+\r
+class SceneLoaderExtension : public Dali::ConnectionTracker\r
+{\r
+public:\r
+  SceneLoaderExtension()\r
+  : mSceneLoader(nullptr),\r
+    mCurrentAnimationIndex(ANIMATION_IDLE)\r
+  {\r
+  }\r
+\r
+  ~SceneLoaderExtension() = default; // Nothing to do in destructor\r
+\r
+  void SetSceneLoader(SceneLoaderExample* sceneLoader)\r
+  {\r
+    mSceneLoader = sceneLoader;\r
+  }\r
+\r
+  void ConnectTouchSignals()\r
+  {\r
+    // This is a temporary hack for now to manually connect these signals.\r
+    // We should connect these signals automatically when loading the scene.\r
+\r
+    if(mSceneLoader)\r
+    {\r
+      ConnectTouchSignal(ICON_IDLE);\r
+      ConnectTouchSignal(ICON_SQUAT);\r
+      ConnectTouchSignal(ICON_JUMPING_JACK);\r
+      ConnectTouchSignal(ICON_LUNGE);\r
+    }\r
+  }\r
+\r
+  void OnKey(const Dali::KeyEvent& e)\r
+  {\r
+    // This is a temporary hack for now to manually handle these key events.\r
+    // We should links them to the animations automatically when loading the scene.\r
+\r
+    switch(e.GetKeyCode())\r
+    {\r
+      case KEY_ONE:\r
+      {\r
+        PlaySceneAnimation(ANIMATION_IDLE);\r
+        break;\r
+      }\r
+      case KEY_TWO:\r
+      {\r
+        PlaySceneAnimation(ANIMATION_IDLE_TO_SQUAT);\r
+        break;\r
+      }\r
+      case KEY_THREE:\r
+      {\r
+        PlaySceneAnimation(ANIMATION_IDLE_TO_JUMPING_JACK);\r
+        break;\r
+      }\r
+      case KEY_FOUR:\r
+      {\r
+        PlaySceneAnimation(ANIMATION_IDLE_TO_LUNGE);\r
+        break;\r
+      }\r
+      default:\r
+        break;\r
+    }\r
+  }\r
+\r
+private:\r
+  bool PlaySceneAnimation(unsigned int animationIndex)\r
+  {\r
+    if(mSceneLoader && mSceneLoader->mScene)\r
+    {\r
+      if(mSceneLoader->mCurrentAnimation &&\r
+         mCurrentAnimationIndex != ANIMATION_IDLE &&\r
+         mSceneLoader->mCurrentAnimation.GetState() != Dali::Animation::STOPPED)\r
+      {\r
+        return false;\r
+      }\r
+\r
+      auto root     = mSceneLoader->mScene;\r
+      auto getActor = [&root](const std::string& name) {\r
+        return root.FindChildByName(name);\r
+      };\r
+\r
+      if(mSceneLoader->mSceneAnimations.size() > animationIndex)\r
+      {\r
+        mCurrentAnimationIndex          = animationIndex;\r
+        mSceneLoader->mCurrentAnimation = mSceneLoader->mSceneAnimations[animationIndex].ReAnimate(getActor);\r
+        mSceneLoader->mCurrentAnimation.FinishedSignal().Connect(this, &SceneLoaderExtension::OnAnimationFinished);\r
+        mSceneLoader->mCurrentAnimation.Play();\r
+      }\r
+    }\r
+\r
+    return true;\r
+  }\r
+\r
+  void ConnectTouchSignal(const std::string actorName)\r
+  {\r
+    if(mSceneLoader && mSceneLoader->mScene)\r
+    {\r
+      auto actor = mSceneLoader->mScene.FindChildByName(actorName);\r
+      if(actor)\r
+      {\r
+        actor.TouchedSignal().Connect(this, &SceneLoaderExtension::OnTouch);\r
+      }\r
+    }\r
+  }\r
+\r
+  void OnAnimationFinished(Dali::Animation& source)\r
+  {\r
+    switch(mCurrentAnimationIndex)\r
+    {\r
+      case ANIMATION_IDLE_TO_SQUAT:\r
+      {\r
+        PlaySceneAnimation(ANIMATION_SQUAT_TO_IDLE);\r
+        break;\r
+      }\r
+      case ANIMATION_IDLE_TO_JUMPING_JACK:\r
+      {\r
+        PlaySceneAnimation(ANIMATION_JUMPING_JACK);\r
+        break;\r
+      }\r
+      case ANIMATION_JUMPING_JACK:\r
+      {\r
+        PlaySceneAnimation(ANIMATION_JUMPING_JACK_TO_IDLE);\r
+        break;\r
+      }\r
+      case ANIMATION_IDLE_TO_LUNGE:\r
+      {\r
+        PlaySceneAnimation(ANIMATION_LUNGE);\r
+        break;\r
+      }\r
+      case ANIMATION_LUNGE:\r
+      {\r
+        PlaySceneAnimation(ANIMATION_LUNGE_TO_IDLE);\r
+        break;\r
+      }\r
+      default:\r
+      {\r
+        mCurrentAnimationIndex = ANIMATION_IDLE;\r
+        break;\r
+      }\r
+      break;\r
+    }\r
+  }\r
+\r
+  bool OnTouch(Dali::Actor actor, const Dali::TouchEvent& touch)\r
+  {\r
+    bool processed = false;\r
+\r
+    if(touch.GetPointCount() > 0)\r
+    {\r
+      if(touch.GetState(0) == Dali::PointState::DOWN)\r
+      {\r
+        auto actorName = actor.GetProperty<std::string>(Dali::Actor::Property::NAME);\r
+\r
+        if(ICON_IDLE == actorName)\r
+        {\r
+          processed = PlaySceneAnimation(ANIMATION_IDLE);\r
+        }\r
+        else if(ICON_SQUAT == actorName)\r
+        {\r
+          processed = PlaySceneAnimation(ANIMATION_IDLE_TO_SQUAT);\r
+        }\r
+        else if(ICON_JUMPING_JACK == actorName)\r
+        {\r
+          processed = PlaySceneAnimation(ANIMATION_IDLE_TO_JUMPING_JACK);\r
+        }\r
+        else if(ICON_LUNGE == actorName)\r
+        {\r
+          processed = PlaySceneAnimation(ANIMATION_IDLE_TO_LUNGE);\r
+        }\r
+      }\r
+    }\r
+    return processed;\r
+  }\r
+\r
+private:\r
+  static constexpr unsigned int KEY_ONE   = 10;\r
+  static constexpr unsigned int KEY_TWO   = 11;\r
+  static constexpr unsigned int KEY_THREE = 12;\r
+  static constexpr unsigned int KEY_FOUR  = 13;\r
+\r
+  // Idle animation\r
+  static constexpr unsigned int ANIMATION_IDLE = 0;\r
+  // Squat animation\r
+  static constexpr unsigned int ANIMATION_IDLE_TO_SQUAT = 3;\r
+  static constexpr unsigned int ANIMATION_SQUAT_TO_IDLE = 15;\r
+  // JumpingJack animation\r
+  static constexpr unsigned int ANIMATION_IDLE_TO_JUMPING_JACK = 1;\r
+  static constexpr unsigned int ANIMATION_JUMPING_JACK         = 5;\r
+  static constexpr unsigned int ANIMATION_JUMPING_JACK_TO_IDLE = 6;\r
+  // Lunge animation\r
+  static constexpr unsigned int ANIMATION_IDLE_TO_LUNGE = 2;\r
+  static constexpr unsigned int ANIMATION_LUNGE         = 9;\r
+  static constexpr unsigned int ANIMATION_LUNGE_TO_IDLE = 10;\r
+\r
+  inline static const std::string ICON_IDLE         = "Idle";\r
+  inline static const std::string ICON_SQUAT        = "Squat";\r
+  inline static const std::string ICON_JUMPING_JACK = "JumpingJack";\r
+  inline static const std::string ICON_LUNGE        = "Lunge";\r
+\r
+  SceneLoaderExample* mSceneLoader;\r
+  unsigned int        mCurrentAnimationIndex;\r
+};\r