(AnimatedImages) Changed the fitting mode as the default has changed
[platform/core/uifw/dali-demo.git] / examples / animated-images / animated-images-example.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <dali-toolkit/dali-toolkit.h>
19 #include <dali-toolkit/devel-api/controls/control-devel.h>
20 #include <dali-toolkit/devel-api/controls/table-view/table-view.h>
21 #include <dali-toolkit/devel-api/visuals/animated-image-visual-actions-devel.h>
22 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
23
24 using namespace Dali;
25 using namespace Dali::Toolkit;
26
27 namespace
28 {
29 const char* const PLAY_ICON_UNSELECTED(DEMO_IMAGE_DIR "icon-play.png");
30 const char* const PLAY_ICON_SELECTED(DEMO_IMAGE_DIR "icon-play-selected.png");
31
32 const unsigned int ANIMATED_IMAGE_COUNT = 2;
33
34 const char* ANIMATED_IMAGE_URLS[ANIMATED_IMAGE_COUNT] =
35   {
36     DEMO_IMAGE_DIR "dog-anim.webp",
37     DEMO_IMAGE_DIR "dali-logo-anim.gif"};
38
39 const char* ANIMATED_ARRAY_URL_FORMATS[ANIMATED_IMAGE_COUNT] =
40   {
41     DEMO_IMAGE_DIR "dog-anim-%03d.png",      // Images are named dog-anim-001.png, dog-anim-002.png, etc.
42     DEMO_IMAGE_DIR "dali-logo-anim-%03d.png" // Images are named dali-logo-anim-001.png, dali-logo-anim-002.png, etc.
43 };
44
45 int ANIMATED_ARRAY_NUMBER_OF_FRAMES[ANIMATED_IMAGE_COUNT] =
46   {
47     8,
48     15};
49
50 const char* ANIMATION_RADIO_BUTTON_NAME("Animation Image");
51 const char* ARRAY_RADIO_BUTTON_NAME("Array");
52
53 /// Structure to specify the layout information for the animated images views.
54 struct ImageLayoutInfo
55 {
56   Vector3 anchorPoint;
57   Vector3 parentOrigin;
58   float   yPosition;
59 };
60
61 ImageLayoutInfo IMAGE_LAYOUT_INFO[ANIMATED_IMAGE_COUNT] =
62   {
63     {AnchorPoint::BOTTOM_CENTER, ParentOrigin::CENTER, -80.0f},
64     {AnchorPoint::TOP_CENTER, ParentOrigin::CENTER, 80.0f}};
65
66 } // unnamed namespace
67
68 /**
69  * @brief This demonstrates how to display and control Animated Images.
70  *
71  * - It displays two animated images, an animated dog and an animated DALi logo.
72  * - The images are loaded paused, a play button is overlayed on top of the images to play the animated image.
73  * - Radio buttons at the bottom allow the user to change between Animated Images and a collection of Image Arrays.
74  */
75 class AnimatedImageController : public ConnectionTracker
76 {
77 public:
78   /**
79    * @brief Constructor.
80    * @param[in]  application  A reference to the Application class
81    */
82   AnimatedImageController(Application& application)
83   : mApplication(application),
84     mImageType(ImageType::ANIMATED_IMAGE)
85   {
86     // Connect to the Application's Init signal
87     mApplication.InitSignal().Connect(this, &AnimatedImageController::Create);
88   }
89
90 private:
91   /**
92    * @brief The image types supported by the application.
93    */
94   enum class ImageType
95   {
96     ANIMATED_IMAGE, ///< Displays Animated Image Files.
97     IMAGE_ARRAY     ///< Displays an array of URLs that are used as an animated image.
98   };
99
100   /**
101    * @brief Called to initialise the application content.
102    * @param[in]  application  A reference to the Application class
103    */
104   void Create(Application& application)
105   {
106     // Set the window background color and connect to the window's key signal to allow Back and Escape to exit.
107     Window window = application.GetWindow();
108     window.SetBackgroundColor(Color::WHITE);
109     window.KeyEventSignal().Connect(this, &AnimatedImageController::OnKeyEvent);
110
111     // Create the animated image-views
112     CreateAnimatedImageViews(window);
113
114     // Create radio buttons to change between Animated images and Image Arrays
115     CreateRadioButtonLayout(window);
116
117     // Create a tap gesture detector to use to pause the animated images
118     mTapDetector = TapGestureDetector::New();
119     mTapDetector.DetectedSignal().Connect(this, &AnimatedImageController::OnTap);
120   }
121
122   /**
123    * @brief Creates and lays out radio buttons to allow changing between the different image types.
124    */
125   void CreateRadioButtonLayout(Window& window)
126   {
127     mAnimatedImageButton = CreateRadioButton(ANIMATION_RADIO_BUTTON_NAME, true);
128     mArrayButton         = CreateRadioButton(ARRAY_RADIO_BUTTON_NAME, false);
129
130     Toolkit::TableView radioButtonLayout = Toolkit::TableView::New(1, 2);
131     radioButtonLayout.SetProperty(Dali::Actor::Property::NAME, "RadioButtonsLayout");
132     radioButtonLayout.SetResizePolicy(ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT);
133     radioButtonLayout.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH);
134     radioButtonLayout.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_CENTER);
135     radioButtonLayout.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_CENTER);
136     radioButtonLayout.SetFitHeight(0);
137     radioButtonLayout.AddChild(mAnimatedImageButton, TableView::CellPosition(0, 0));
138     radioButtonLayout.AddChild(mArrayButton, TableView::CellPosition(0, 1));
139     radioButtonLayout.SetCellAlignment(TableView::CellPosition(0, 0),
140                                        HorizontalAlignment::CENTER,
141                                        VerticalAlignment::CENTER);
142     radioButtonLayout.SetCellAlignment(TableView::CellPosition(0, 1),
143                                        HorizontalAlignment::CENTER,
144                                        VerticalAlignment::CENTER);
145     radioButtonLayout.SetProperty(Actor::Property::POSITION_Y, -10.0f);
146
147     window.Add(radioButtonLayout);
148   }
149
150   /**
151    * @brief Creates a radio button.
152    * @param[in]  name      The name of the button
153    * @param[in]  selected  Whether the button is selected
154    * @return The created radio-button
155    */
156   RadioButton CreateRadioButton(const char* const name, bool selected)
157   {
158     RadioButton radioButton = Toolkit::RadioButton::New(name);
159     radioButton.SetProperty(Button::Property::SELECTED, selected);
160     radioButton.ClickedSignal().Connect(this, &AnimatedImageController::OnRadioButtonClicked);
161     return radioButton;
162   }
163
164   /**
165    * @brief Creates the required animated image views.
166    */
167   void CreateAnimatedImageViews(Window window)
168   {
169     for(unsigned int index = 0; index < ANIMATED_IMAGE_COUNT; ++index)
170     {
171       Control& control = (index == 0) ? mActorDog : mActorLogo;
172       if(control)
173       {
174         // Remove the previous control from the window, it's resources (and children) will be deleted automatically
175         control.Unparent();
176       }
177
178       // Create and lay out the image view according to the index
179       control = Toolkit::ImageView::New();
180       control.SetProperty(Toolkit::ImageView::Property::IMAGE, SetupViewProperties(mImageType, index));
181       control.SetProperty(Actor::Property::ANCHOR_POINT, IMAGE_LAYOUT_INFO[index].anchorPoint);
182       control.SetProperty(Actor::Property::PARENT_ORIGIN, IMAGE_LAYOUT_INFO[index].parentOrigin);
183       control.SetProperty(Actor::Property::POSITION_Y, IMAGE_LAYOUT_INFO[index].yPosition);
184
185       control.SetProperty(Actor::Property::SIZE, Vector2(300, 300));
186
187       // We do not want the animated image playing when it's added to the window.
188       PauseAnimatedImage(control);
189
190       window.Add(control);
191     }
192   }
193
194   /**
195    * @brief Plays the passed in animated image.
196    * @details Also sets up the control so it can be paused when tapped.
197    * @param[in]  control  The animated image to play
198    */
199   void PlayAnimatedImage(Control& control)
200   {
201     DevelControl::DoAction(control,
202                            ImageView::Property::IMAGE,
203                            DevelAnimatedImageVisual::Action::PLAY,
204                            Property::Value());
205
206     if(mTapDetector)
207     {
208       mTapDetector.Attach(control);
209     }
210   }
211
212   /**
213    * @brief Pauses the animated image.
214    * @details Adds a Play button to the control and sets both up so that the animated image can be played again when
215    *          the button is tapped.
216    * @param[in]  control  The animated image to pause
217    */
218   void PauseAnimatedImage(Control& control)
219   {
220     DevelControl::DoAction(control,
221                            ImageView::Property::IMAGE,
222                            DevelAnimatedImageVisual::Action::PAUSE,
223                            Property::Value());
224
225     // Create a push button, and add it as child of the control
226     Toolkit::PushButton animateButton = Toolkit::PushButton::New();
227     animateButton.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, PLAY_ICON_UNSELECTED);
228     animateButton.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, PLAY_ICON_SELECTED);
229     animateButton.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
230     animateButton.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
231     animateButton.ClickedSignal().Connect(this, &AnimatedImageController::OnPlayButtonClicked);
232     control.Add(animateButton);
233
234     if(mTapDetector)
235     {
236       mTapDetector.Detach(control);
237     }
238   }
239
240   /**
241    * @brief Called when the play button is clicked.
242    * @details This method is used to start playing the parent image-view of the clicked button.
243    * @param[in]  button  The button that has been clicked
244    * @return We return true to state that we handled the event
245    */
246   bool OnPlayButtonClicked(Toolkit::Button button)
247   {
248     Control control = (button.GetParent() == mActorDog) ? mActorDog : mActorLogo;
249     PlayAnimatedImage(control);
250
251     button.Unparent();
252
253     return true;
254   }
255
256   /**
257    * @brief Called when the animated image views are tapped.
258    * @details This method is used to pause the tapped animated image view.
259    * @param[in]  actor  The actor that's tapped
260    */
261   void OnTap(Dali::Actor actor, const Dali::TapGesture& /* tap */)
262   {
263     Control control = (actor == mActorDog) ? mActorDog : mActorLogo;
264     PauseAnimatedImage(control);
265   }
266
267   /**
268    * @brief Called when a radio button is clicked.
269    * @details This method is used to change between the different image types.
270    * @param[in]  button  The clicked radio-button
271    * @return We return true to state that we handled the event.
272    *
273    */
274   bool OnRadioButtonClicked(Toolkit::Button button)
275   {
276     mImageType = (button == mAnimatedImageButton) ? ImageType::ANIMATED_IMAGE : ImageType::IMAGE_ARRAY;
277
278     CreateAnimatedImageViews(mApplication.GetWindow());
279     return true;
280   }
281
282   /**
283    * @brief Called when any key event is received.
284    *
285    * Will use this to quit the application if Back or the Escape key is received
286    * @param[in] event The key event information
287    */
288   void OnKeyEvent(const KeyEvent& event)
289   {
290     if(event.GetState() == KeyEvent::DOWN)
291     {
292       if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
293       {
294         mApplication.Quit();
295       }
296     }
297   }
298
299   /**
300    * @brief Sets up the view properties appropriately.
301    * @param[in]  type   The Image type
302    * @param[in]  index  The index
303    * @return The set up property value
304    */
305   Property::Value SetupViewProperties(ImageType type, int index)
306   {
307     Property::Map map;
308
309     AddUrl(map, type, index);
310     AddCache(map, type, index);
311     return Property::Value(map);
312   }
313
314   /**
315    * @brief Adds the URL to the given map appropriately.
316    * @param[in/out]  map    The map to add the URL details to
317    * @param[in]      type   The Image type
318    * @param[in]      index  The index
319    */
320   void AddUrl(Property::Map& map, ImageType type, int index)
321   {
322     if(type == ImageType::ANIMATED_IMAGE)
323     {
324       map.Add(Toolkit::ImageVisual::Property::URL, Property::Value(ANIMATED_IMAGE_URLS[index]));
325     }
326     else
327     {
328       Property::Array frameUrls;
329       for(int i = 1; i <= ANIMATED_ARRAY_NUMBER_OF_FRAMES[index]; ++i)
330       {
331         char* buffer;
332         int   len = asprintf(&buffer, ANIMATED_ARRAY_URL_FORMATS[index], i);
333         if(len > 0)
334         {
335           std::string frameUrl(buffer);
336           free(buffer);
337           frameUrls.Add(Property::Value(frameUrl));
338         }
339       }
340       map.Add(Toolkit::ImageVisual::Property::URL, Property::Value(frameUrls));
341     }
342     map.Add(DevelVisual::Property::VISUAL_FITTING_MODE, DevelVisual::FIT_KEEP_ASPECT_RATIO);
343   }
344
345   /**
346    * @brief Adds the cache properties, if required to the map.
347    * @param[in/out]  map    The map to add the URL details to
348    * @param[in]      type   The Image type
349    * @param[in]      index  The index
350    */
351   void AddCache(Property::Map& map, ImageType type, int index)
352   {
353     if(type == ImageType::IMAGE_ARRAY)
354     {
355       map
356         .Add(Toolkit::ImageVisual::Property::BATCH_SIZE, 4)
357         .Add(Toolkit::ImageVisual::Property::CACHE_SIZE, 10)
358         .Add(Toolkit::ImageVisual::Property::FRAME_DELAY, 150);
359     }
360   }
361
362 private:
363   Application& mApplication; ///< A reference to the application.
364
365   Toolkit::ImageView mActorDog;  ///< The current dog image view.
366   Toolkit::ImageView mActorLogo; ///< The current logo image view.
367
368   Toolkit::RadioButton mAnimatedImageButton; ///< The Animated Image Radio Button.
369   Toolkit::RadioButton mArrayButton;         ///< The Array Radio Button.
370
371   TapGestureDetector mTapDetector; ///< The tap detector.
372
373   ImageType mImageType; ///< The current Image type.
374 };
375
376 int DALI_EXPORT_API main(int argc, char** argv)
377 {
378   Application application = Application::New(&argc, &argv);
379
380   AnimatedImageController test(application);
381
382   application.MainLoop();
383
384   return 0;
385 }