802d117888297477bfbed65c44af632d3424d16f
[platform/core/uifw/dali-demo.git] / examples / bubble-effect / bubble-effect-example.cpp
1 /*
2  * Copyright (c) 2020 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/dali.h>
19 #include <dali-toolkit/dali-toolkit.h>
20 #include <dali-toolkit/devel-api/controls/bubble-effect/bubble-emitter.h>
21 #include "shared/view.h"
22 #include "shared/utility.h"
23
24 using namespace Dali;
25
26 namespace
27 {
28 const char * const TOOLBAR_IMAGE( DEMO_IMAGE_DIR "top-bar.png" );
29 const char * const APPLICATION_TITLE( "Bubble Effect" );
30 const char * const CHANGE_BACKGROUND_ICON( DEMO_IMAGE_DIR "icon-change.png" );
31 const char * const CHANGE_BACKGROUND_ICON_SELECTED( DEMO_IMAGE_DIR "icon-change-selected.png" );
32 const char * const CHANGE_BUBBLE_SHAPE_ICON( DEMO_IMAGE_DIR "icon-replace.png" );
33 const char * const CHANGE_BUBBLE_SHAPE_ICON_SELECTED( DEMO_IMAGE_DIR "icon-replace-selected.png" );
34
35 const char* BACKGROUND_IMAGES[]=
36 {
37   DEMO_IMAGE_DIR "background-1.jpg",
38   DEMO_IMAGE_DIR "background-2.jpg",
39   DEMO_IMAGE_DIR "background-3.jpg",
40   DEMO_IMAGE_DIR "background-4.jpg",
41   DEMO_IMAGE_DIR "background-5.jpg",
42 };
43 const unsigned int NUM_BACKGROUND_IMAGES( sizeof( BACKGROUND_IMAGES ) / sizeof( BACKGROUND_IMAGES[0] ) );
44
45 const char* BUBBLE_SHAPE_IMAGES[] =
46 {
47   DEMO_IMAGE_DIR "bubble-ball.png",
48   DEMO_IMAGE_DIR "icon-effect-cross.png",
49   DEMO_IMAGE_DIR "icon-item-view-layout-spiral.png",
50   DEMO_IMAGE_DIR "icon-replace.png"
51 };
52 const unsigned int NUM_BUBBLE_SHAPE_IMAGES( sizeof( BUBBLE_SHAPE_IMAGES ) / sizeof( BUBBLE_SHAPE_IMAGES[0] ) );
53
54 const Vector2 DEFAULT_BUBBLE_SIZE( 10.f, 30.f );
55 const unsigned int DEFAULT_NUMBER_OF_BUBBLES( 1000 );
56
57 }// end LOCAL_STUFF
58
59 // This example shows the usage of BubbleEmitter which displays lots of moving bubbles on the window.
60 class BubbleEffectExample : public ConnectionTracker
61 {
62 public:
63   BubbleEffectExample(Application &app)
64   : mApp(app),
65     mBackground(),
66     mBubbleEmitter(),
67     mEmitAnimation(),
68     mChangeBackgroundButton(),
69     mChangeBubbleShapeButton(),
70     mTimerForBubbleEmission(),
71     mHSVDelta( Vector3( 0.f, 0.f, 0.5f ) ),
72     mCurrentTouchPosition(),
73     mEmitPosition(),
74     mAnimateComponentCount( 0 ),
75     mNonMovementCount( 0 ),
76     mTimerInterval( 16 ),
77     mCurrentBackgroundImageId( 0 ),
78     mCurrentBubbleShapeImageId( 0 ),
79     mNeedNewAnimation( true )
80   {
81     // Connect to the Application's Init signal
82     app.InitSignal().Connect(this, &BubbleEffectExample::Create);
83   }
84
85   ~BubbleEffectExample()
86   {
87   }
88
89 private:
90
91   // The Init signal is received once (only) during the Application lifetime
92   void Create(Application& app)
93   {
94     Window window = app.GetWindow();
95     Vector2 windowSize = window.GetSize();
96
97     window.KeyEventSignal().Connect(this, &BubbleEffectExample::OnKeyEvent);
98
99     // Creates a default view with a default tool bar.
100     // The view is added to the window.
101     Toolkit::ToolBar toolBar;
102     Layer content = DemoHelper::CreateView( app,
103                                             mBackground,
104                                             toolBar,
105                                             "",
106                                             TOOLBAR_IMAGE,
107                                             APPLICATION_TITLE );
108
109     // Add a button to change background. (right of toolbar)
110     mChangeBackgroundButton = Toolkit::PushButton::New();
111     mChangeBackgroundButton.SetProperty( Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, CHANGE_BACKGROUND_ICON );
112     mChangeBackgroundButton.SetProperty( Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, CHANGE_BACKGROUND_ICON_SELECTED );
113     mChangeBackgroundButton.ClickedSignal().Connect( this, &BubbleEffectExample::OnChangeIconClicked );
114     toolBar.AddControl( mChangeBackgroundButton,
115                         DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage,
116                         Toolkit::Alignment::HORIZONTAL_RIGHT,
117                         DemoHelper::DEFAULT_MODE_SWITCH_PADDING  );
118     // Add a button to change bubble shape. ( left of bar )
119     mChangeBubbleShapeButton = Toolkit::PushButton::New();
120     mChangeBubbleShapeButton.SetProperty( Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, CHANGE_BUBBLE_SHAPE_ICON );
121     mChangeBubbleShapeButton.SetProperty( Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, CHANGE_BUBBLE_SHAPE_ICON_SELECTED );
122     mChangeBubbleShapeButton.ClickedSignal().Connect( this, &BubbleEffectExample::OnChangeIconClicked );
123     toolBar.AddControl( mChangeBubbleShapeButton,
124                         DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage,
125                         Toolkit::Alignment::HORIZONTAL_LEFT,
126                         DemoHelper::DEFAULT_MODE_SWITCH_PADDING  );
127
128     // Create and initialize the BubbleEmitter object
129     mBubbleEmitter = Toolkit::BubbleEmitter::New( windowSize,
130                                                   DemoHelper::LoadTexture( BUBBLE_SHAPE_IMAGES[mCurrentBubbleShapeImageId] ),
131                                                   DEFAULT_NUMBER_OF_BUBBLES,
132                                                   DEFAULT_BUBBLE_SIZE);
133
134     mBubbleEmitter.SetBackground( DemoHelper::LoadWindowFillingTexture( window.GetSize(), BACKGROUND_IMAGES[mCurrentBackgroundImageId] ), mHSVDelta );
135
136     // Get the root actor of all bubbles, and add it to window.
137     Actor bubbleRoot = mBubbleEmitter.GetRootActor();
138     bubbleRoot.SetProperty( Actor::Property::PARENT_ORIGIN,ParentOrigin::CENTER);
139     bubbleRoot.SetProperty( Actor::Property::POSITION_Z, 0.1f); // Make sure the bubbles displayed on top og the background.
140     content.Add( bubbleRoot );
141
142     // Set the application background
143     mBackground.SetProperty( Toolkit::Control::Property::BACKGROUND, BACKGROUND_IMAGES[ mCurrentBackgroundImageId ] );
144
145     // Set up the timer to emit bubble regularly when the finger is touched down but not moved
146     mTimerForBubbleEmission = Timer::New( mTimerInterval );
147     mTimerForBubbleEmission.TickSignal().Connect(this, &BubbleEffectExample::OnTimerTick);
148
149     // Connect the callback to the touch signal on the background
150     mBackground.TouchSignal().Connect( this, &BubbleEffectExample::OnTouch );
151   }
152
153
154 /***********
155  * Emit bubbles
156  *****************/
157
158   // Set up the animation of emitting bubbles, to be efficient, every animation controls multiple emission ( 4 here )
159   void SetUpAnimation( Vector2 emitPosition, Vector2 direction )
160   {
161     if( mNeedNewAnimation )
162     {
163       float duration = Random::Range(1.f, 1.5f);
164       mEmitAnimation = Animation::New( duration );
165       mNeedNewAnimation = false;
166       mAnimateComponentCount = 0;
167     }
168
169     mBubbleEmitter.EmitBubble( mEmitAnimation, emitPosition, direction + Vector2(0.f, 30.f) /* upwards */, Vector2(300, 600) );
170
171     mAnimateComponentCount++;
172
173     if( mAnimateComponentCount % 4 ==0 )
174     {
175       mEmitAnimation.Play();
176       mNeedNewAnimation = true;
177     }
178   }
179
180   // Emit bubbles when the finger touches down but keep stationary.
181   // And stops emitting new bubble after being stationary for 2 seconds
182   bool OnTimerTick()
183   {
184     if(mEmitPosition == mCurrentTouchPosition) // finger is not moving
185     {
186       mNonMovementCount++;
187       if(mNonMovementCount < (1000 / mTimerInterval)) // 1 seconds
188       {
189         for(int i = 0; i < 4; i++) // emit 4 bubbles every timer tick
190         {
191           SetUpAnimation( mCurrentTouchPosition+Vector2(rand()%5, rand()%5), Vector2(rand()%60-30, rand()%100-50) );
192         }
193       }
194     }
195     else
196     {
197       mNonMovementCount = 0;
198       mEmitPosition = mCurrentTouchPosition;
199     }
200
201     return true;
202   }
203
204   // Callback function of the touch signal on the background
205   bool OnTouch(Dali::Actor actor, const Dali::TouchEvent& event)
206   {
207     switch( event.GetState( 0 ) )
208     {
209       case PointState::DOWN:
210       {
211         mCurrentTouchPosition = mEmitPosition = event.GetScreenPosition( 0 );
212         mTimerForBubbleEmission.Start();
213         mNonMovementCount = 0;
214
215         break;
216       }
217       case PointState::MOTION:
218       {
219         Vector2 displacement = event.GetScreenPosition( 0 ) - mCurrentTouchPosition;
220         mCurrentTouchPosition = event.GetScreenPosition( 0 );
221         //emit multiple bubbles along the moving direction when the finger moves quickly
222         float step = std::min(5.f, displacement.Length());
223         for( float i=0.25f; i<step; i=i+1.f)
224         {
225           SetUpAnimation( mCurrentTouchPosition+displacement*(i/step), displacement );
226         }
227         break;
228       }
229       case PointState::UP:
230       case PointState::LEAVE:
231       case PointState::INTERRUPTED:
232       {
233         mTimerForBubbleEmission.Stop();
234         mEmitAnimation.Play();
235         mNeedNewAnimation = true;
236         mAnimateComponentCount = 0;
237         break;
238       }
239       case PointState::STATIONARY:
240       default:
241       {
242         break;
243       }
244
245     }
246     return true;
247   }
248
249   bool OnChangeIconClicked( Toolkit::Button button )
250   {
251     if(button == mChangeBackgroundButton)
252     {
253       mCurrentBackgroundImageId = (mCurrentBackgroundImageId+1) % NUM_BACKGROUND_IMAGES;
254
255       //Update bubble emitter background
256       mBubbleEmitter.SetBackground( DemoHelper::LoadWindowFillingTexture( mApp.GetWindow().GetSize(), BACKGROUND_IMAGES[ mCurrentBackgroundImageId  ] ), mHSVDelta );
257
258       // Set the application background
259       mBackground.SetProperty( Toolkit::Control::Property::BACKGROUND, BACKGROUND_IMAGES[ mCurrentBackgroundImageId ] );
260     }
261     else if( button == mChangeBubbleShapeButton )
262     {
263       mBubbleEmitter.SetBubbleShape( DemoHelper::LoadTexture( BUBBLE_SHAPE_IMAGES[ ++mCurrentBubbleShapeImageId % NUM_BUBBLE_SHAPE_IMAGES ] ) );
264     }
265     return true;
266   }
267
268   /**
269    * Main key event handler
270    */
271   void OnKeyEvent(const KeyEvent& event)
272   {
273     if(event.GetState() == KeyEvent::DOWN)
274     {
275       if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
276       {
277         mApp.Quit();
278       }
279     }
280   }
281
282 private:
283
284   Application&               mApp;
285   Dali::Toolkit::Control     mBackground;
286
287   Toolkit::BubbleEmitter     mBubbleEmitter;
288   Animation                  mEmitAnimation;
289   Toolkit::PushButton        mChangeBackgroundButton;
290   Toolkit::PushButton        mChangeBubbleShapeButton;
291   Timer                      mTimerForBubbleEmission;
292
293   Vector3                    mHSVDelta;
294   Vector2                    mCurrentTouchPosition;
295   Vector2                    mEmitPosition;
296
297   unsigned int               mAnimateComponentCount;
298   unsigned int               mNonMovementCount;
299   unsigned int               mTimerInterval;
300   unsigned int               mCurrentBackgroundImageId;
301   unsigned int               mCurrentBubbleShapeImageId;
302
303   bool                       mNeedNewAnimation;
304 };
305
306 /*****************************************************************************/
307
308 int DALI_EXPORT_API main(int argc, char **argv)
309 {
310   Application app = Application::New(&argc, &argv, DEMO_THEME_PATH);
311   BubbleEffectExample theApp(app);
312   app.MainLoop();
313   return 0;
314 }