b5632d56d225c2f7a0d629515b585a14d271f0d1
[platform/core/uifw/dali-demo.git] / examples / magnifier / magnifier-example.cpp
1 /*
2  * Copyright (c) 2014 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 // EXTERNAL INCLUDES
19
20 // INTERNAL INCLUDES
21 #include "shared/view.h"
22
23 #include <dali-toolkit/dali-toolkit.h>
24 #include <dali-toolkit/devel-api/controls/magnifier/magnifier.h>
25 using namespace Dali;
26
27 namespace
28 {
29 const char* BACKGROUND_IMAGE( DALI_IMAGE_DIR "background-magnifier.jpg" );
30 const char* TOOLBAR_IMAGE( DALI_IMAGE_DIR "top-bar.png" );
31 const char* APPLICATION_TITLE( "Magnifier Example" );
32 const Vector3 MAGNIFIER_SIZE(0.25f, 0.25f, 0.0f);       ///< Magnifier sides should be 25% of the width of the stage
33 const float ANIMATION_DURATION(60.0f);                  ///< Run animation for a minute before repeating.
34 const float MAGNIFIER_DISPLAY_DURATION(0.125f);         ///< Duration in seconds for show/hide manual magnifier animation
35
36 const float MAGNIFICATION_FACTOR(2.0f);                 ///< Amount to magnify by.
37 const float MAGNIFIER_INDENT(10.0f);                    ///< Indentation around edge of stage to define where magnifiers may move.
38 const float FINGER_RADIUS_INCHES(0.25f);                ///< Average finger radius in inches from the center of index finger to edge.
39
40 /**
41  * MagnifierPathConstraint
42  * This constraint governs the position of the
43  * animating magnifier in a swirly pattern around
44  * the stage.
45  */
46 struct MagnifierPathConstraint
47 {
48   /**
49    * Constraint constructor
50    * @param[in] stageSize The stage size so that the constraint can create a path
51    * within stage bounds.
52    */
53   MagnifierPathConstraint(const Vector3& stageSize,
54                           Vector3 offset = Vector3::ZERO)
55   : mStageSize(stageSize),
56     mOffset(offset)
57   {
58   }
59
60   void operator()( Vector3& current, const PropertyInputContainer& inputs )
61   {
62     float time = inputs[1]->GetFloat();
63     const Vector3& size = inputs[0]->GetVector3();
64
65     current = mOffset;
66
67     Vector3 range( mStageSize - size - Vector3::ONE * MAGNIFIER_INDENT * 2.0f );
68     current.x += 0.5f * sinf(time * 0.471f) * range.width;
69     current.y += 0.5f * sinf(time * 0.8739f) * range.height;
70   }
71
72   Vector3 mStageSize;     ///< Keep track of the stage size for determining path within stage bounds
73   Vector3 mOffset;        ///< Amount to offset magnifier path
74 };
75
76 /**
77  * Confine Actor to boundaries of reference actor (e.g. Parent)
78  * Actor bounds (top-left position + size) are confined to reference Actor's
79  * bounds.
80  */
81 struct ConfinementConstraint
82 {
83   /**
84    * Confinement constraint constructor.
85    * @param[in] offsetOrigin (optional) Whether to offset the parent origin or not.
86    * @param[in] topLeftMargin (optional) Top-Left margins (defaults to 0.0f, 0.0f)
87    * @param[in] bottomRightMargin (optional) Bottom-Right margins (defaults to 0.0f, 0.0f)
88    * @param[in] flipHorizontal (optional) whether to flip Actor to the other side X if near edge, and by
89    * how much (defaults to 0.0f i.e. no flip)
90    * @param[in] flipVertical (optional) whether to flip Actor to the other side Y if near edge, and by
91    * how much (defaults to 0.0f i.e. no flip)
92    */
93   ConfinementConstraint(Vector3 offsetOrigin = Vector3::ZERO, Vector2 topLeftMargin = Vector2::ZERO, Vector2 bottomRightMargin = Vector2::ZERO, bool flipHorizontal = false, bool flipVertical = false)
94   : mOffsetOrigin(offsetOrigin),
95     mMinIndent(topLeftMargin),
96     mMaxIndent(bottomRightMargin),
97     mFlipHorizontal(flipHorizontal),
98     mFlipVertical(flipVertical)
99   {
100   }
101
102   void operator()( Vector3& current, const PropertyInputContainer& inputs )
103   {
104     const Vector3& size = inputs[0]->GetVector3();
105     const Vector3 origin = inputs[1]->GetVector3();
106     const Vector3& anchor = inputs[2]->GetVector3();
107     const Vector3& referenceSize = inputs[3]->GetVector3();
108
109     Vector3 offset(mOffsetOrigin * referenceSize);
110
111     // Get actual position of Actor relative to parent's Top-Left.
112     Vector3 position(current + offset + origin * referenceSize);
113
114     current += offset;
115
116     // if top-left corner is outside of Top-Left bounds, then push back in screen.
117     Vector3 corner(position - size * anchor - mMinIndent);
118
119     if(mFlipHorizontal && corner.x < 0.0f)
120     {
121       corner.x = 0.0f;
122       current.x += size.width;
123     }
124
125     if(mFlipVertical && corner.y < 0.0f)
126     {
127       corner.y = 0.0f;
128       current.y += size.height;
129     }
130
131     current.x -= std::min(corner.x, 0.0f);
132     current.y -= std::min(corner.y, 0.0f);
133
134     // if bottom-right corner is outside of Bottom-Right bounds, then push back in screen.
135     corner += size - referenceSize + mMinIndent + mMaxIndent;
136
137     if(mFlipHorizontal && corner.x > 0.0f)
138     {
139       corner.x = 0.0f;
140       current.x -= size.width;
141     }
142
143     if(mFlipVertical && corner.y > 0.0f)
144     {
145       corner.y = 0.0f;
146       current.y -= size.height;
147     }
148
149     current.x -= std::max(corner.x, 0.0f);
150     current.y -= std::max(corner.y, 0.0f);
151   }
152
153   Vector3 mOffsetOrigin;                                ///< Manual Parent Offset Origin.
154   Vector3 mMinIndent;                                   ///< Top-Left Margin
155   Vector3 mMaxIndent;                                   ///< Bottom-Right Margin.
156   bool mFlipHorizontal;                                 ///< Whether to flip actor's position if exceeds horizontal screen bounds
157   bool mFlipVertical;                                   ///< Whether to flip actor's position if exceeds vertical screen bounds
158 };
159
160 }
161
162 // This example shows how to use the Magnifier component.
163 //
164 class ExampleController : public ConnectionTracker
165 {
166 public:
167
168   /**
169    * The example controller constructor.
170    * @param[in] application The application instance
171    */
172   ExampleController( Application& application )
173   : mApplication( application ),
174     mView(),
175     mAnimationTime(0.0f),
176     mMagnifierShown(false)
177   {
178     // Connect to the Application's Init signal
179     mApplication.InitSignal().Connect( this, &ExampleController::Create );
180   }
181
182   /**
183    * The example controller destructor
184    */
185   ~ExampleController()
186   {
187     // Nothing to do here;
188   }
189
190   /**
191    * Invoked upon creation of application
192    * @param[in] application The application instance
193    */
194   void Create( Application& application )
195   {
196     Stage::GetCurrent().KeyEventSignal().Connect(this, &ExampleController::OnKeyEvent);
197
198     mStageSize = Stage::GetCurrent().GetSize();
199
200     // The Init signal is received once (only) during the Application lifetime
201
202     // Hide the indicator bar
203     application.GetWindow().ShowIndicator( Dali::Window::INVISIBLE );
204
205     // Creates a default view with a default tool bar.
206     // The view is added to the stage.
207     Toolkit::ToolBar toolBar;
208     mContent = DemoHelper::CreateView( application,
209                                        mView,
210                                        toolBar,
211                                        BACKGROUND_IMAGE,
212                                        TOOLBAR_IMAGE,
213                                        APPLICATION_TITLE );
214
215     mContent.SetLeaveRequired(true);
216     mContent.TouchedSignal().Connect( this, &ExampleController::OnTouched );
217
218     // Create magnifier (controlled by human touch)
219     Layer overlay = Layer::New();
220     overlay.SetSensitive(false);
221     overlay.SetParentOrigin( ParentOrigin::CENTER );
222     overlay.SetSize(mStageSize);
223     Stage::GetCurrent().Add(overlay);
224
225     mMagnifier = Toolkit::Magnifier::New();
226     mMagnifier.SetSourceActor( mView.GetChildAt( 0 ) );
227     mMagnifier.SetSize( MAGNIFIER_SIZE * mStageSize.width );  // Size of magnifier is in relation to stage width
228     mMagnifier.SetProperty( Toolkit::Magnifier::Property::MAGNIFICATION_FACTOR, MAGNIFICATION_FACTOR );
229     mMagnifier.SetScale(Vector3::ZERO);
230     overlay.Add( mMagnifier );
231
232     // Apply constraint to animate the position of the magnifier.
233     Constraint constraint = Constraint::New<Vector3>( mMagnifier, Actor::Property::POSITION, ConfinementConstraint(Vector3( 0.5f, 0.5f, 0.0f ), Vector2::ONE * MAGNIFIER_INDENT, Vector2::ONE * MAGNIFIER_INDENT) );
234     constraint.AddSource( LocalSource(Actor::Property::SIZE) );
235     constraint.AddSource( LocalSource(Actor::Property::PARENT_ORIGIN) );
236     constraint.AddSource( LocalSource(Actor::Property::ANCHOR_POINT) );
237     constraint.AddSource( ParentSource(Actor::Property::SIZE) );
238     constraint.SetRemoveAction(Constraint::Discard);
239     constraint.Apply();
240
241     // Create bouncing magnifier automatically bounces around screen.
242     mBouncingMagnifier = Toolkit::Magnifier::New();
243     mBouncingMagnifier.SetSourceActor( mView.GetChildAt( 0 ) );
244     mBouncingMagnifier.SetSize( MAGNIFIER_SIZE * mStageSize.width ); // Size of magnifier is in relation to stage width
245     mBouncingMagnifier.SetProperty( Toolkit::Magnifier::Property::MAGNIFICATION_FACTOR, MAGNIFICATION_FACTOR );
246     overlay.Add( mBouncingMagnifier );
247
248     mAnimationTimeProperty = mBouncingMagnifier.RegisterProperty("animation-time", 0.0f);
249     ContinueAnimation();
250
251     // Apply constraint to animate the position of the magnifier.
252     constraint = Constraint::New<Vector3>( mBouncingMagnifier, Actor::Property::POSITION, MagnifierPathConstraint(mStageSize, mStageSize * 0.5f) );
253     constraint.AddSource( LocalSource(Actor::Property::SIZE) );
254     constraint.AddSource( LocalSource(mAnimationTimeProperty) );
255     constraint.Apply();
256
257     // Apply constraint to animate the source of the magnifier.
258     constraint = Constraint::New<Vector3>( mBouncingMagnifier, Toolkit::Magnifier::Property::SOURCE_POSITION, MagnifierPathConstraint(mStageSize) );
259     constraint.AddSource( LocalSource(Actor::Property::SIZE) );
260     constraint.AddSource( LocalSource(mAnimationTimeProperty) );
261     constraint.Apply();
262   }
263
264   /**
265    * Invoked whenever the animation finishes (every 60 seconds)
266    * @param[in] animation The animation
267    */
268   void OnAnimationFinished( Animation& animation )
269   {
270     animation.FinishedSignal().Disconnect(this, &ExampleController::OnAnimationFinished);
271     animation.Clear();
272     ContinueAnimation();
273   }
274
275   /**
276    * Resumes animation for another ANIMATION_DURATION seconds.
277    */
278   void ContinueAnimation()
279   {
280     Animation animation = Animation::New(ANIMATION_DURATION);
281     mAnimationTime += ANIMATION_DURATION;
282     animation.AnimateTo( Property(mBouncingMagnifier, mAnimationTimeProperty), mAnimationTime );
283     animation.Play();
284     animation.FinishedSignal().Connect(this, &ExampleController::OnAnimationFinished);
285   }
286
287   /**
288    * Invoked whenever the quit button is clicked
289    * @param[in] button the quit button
290    */
291   bool OnQuitButtonClicked( Toolkit::Button button )
292   {
293     // quit the application
294     mApplication.Quit();
295     return true;
296   }
297
298   /**
299    * Invoked whenever the content (screen) is touched
300    * @param[in] actor The actor that received the touch
301    * @param[in] event The touch-event information
302    */
303   bool OnTouched( Actor actor, const TouchEvent& event )
304   {
305     if(event.GetPointCount() > 0)
306     {
307       const TouchPoint& point = event.GetPoint(0);
308       switch(point.state)
309       {
310         case TouchPoint::Down:
311         case TouchPoint::Motion:
312         {
313           ShowMagnifier();
314           break;
315         }
316         case TouchPoint::Up:
317         case TouchPoint::Leave:
318         case TouchPoint::Interrupted:
319         {
320           HideMagnifier();
321           break;
322         }
323         default:
324         {
325           break;
326         }
327       } // end switch
328
329       Vector3 touchPoint(point.screen);
330
331       SetMagnifierPosition(touchPoint - mStageSize * 0.5f);
332     }
333
334     return false;
335   }
336
337   /**
338    * Shows the magnifier
339    */
340   void ShowMagnifier()
341   {
342     if(!mMagnifierShown)
343     {
344       Animation animation = Animation::New(MAGNIFIER_DISPLAY_DURATION);
345       animation.AnimateTo(Property(mMagnifier, Actor::Property::SCALE), Vector3::ONE, AlphaFunction::EASE_IN);
346       animation.Play();
347       mMagnifierShown = true;
348     }
349   }
350
351   /**
352    * Hides the magnifier
353    */
354   void HideMagnifier()
355   {
356     if(mMagnifierShown)
357     {
358       Animation animation = Animation::New(MAGNIFIER_DISPLAY_DURATION);
359       animation.AnimateTo(Property(mMagnifier, Actor::Property::SCALE), Vector3::ZERO, AlphaFunction::EASE_OUT);
360       animation.Play();
361       mMagnifierShown = false;
362     }
363   }
364
365   /**
366    * Manually sets the magnifier position
367    * @param[in] position The magnifier's position relative to center of stage
368    */
369   void SetMagnifierPosition(const Vector3 position)
370   {
371     mMagnifier.SetProperty( Toolkit::Magnifier::Property::SOURCE_POSITION, position );
372
373     // position magnifier glass such that bottom edge is touching/near top of finger.
374     Vector3 glassPosition(position);
375     glassPosition.y -= mStageSize.width * MAGNIFIER_SIZE.height * 0.5f + Stage::GetCurrent().GetDpi().height * FINGER_RADIUS_INCHES;
376
377     mMagnifier.SetPosition( glassPosition );
378   }
379
380   void OnKeyEvent(const KeyEvent& event)
381   {
382     if(event.state == KeyEvent::Down)
383     {
384       if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
385       {
386         mApplication.Quit();
387       }
388     }
389   }
390
391 private:
392
393   Application&  mApplication;                             ///< Application instance
394   Toolkit::Control mView;                                 ///< The view
395   Layer mContent;                                         ///< The content layer
396   Toolkit::Magnifier mMagnifier;                          ///< The manually controlled magnifier
397   Toolkit::Magnifier mBouncingMagnifier;                  ///< The animating magnifier (swirly animation)
398   Vector3 mStageSize;                                     ///< The size of the stage
399   float mAnimationTime;                                   ///< Keep track of start animation time.
400   Property::Index mAnimationTimeProperty;                 ///< Animation time property (responsible for swirly animation)
401   bool mMagnifierShown;                                   ///< Flag indicating whether the magnifier is being shown or not.
402
403 };
404
405 void RunTest( Application& application )
406 {
407   ExampleController test( application );
408
409   application.MainLoop();
410 }
411
412 // Entry point for Linux & Tizen applications
413 //
414 int main( int argc, char **argv )
415 {
416   Application application = Application::New( &argc, &argv, DALI_DEMO_THEME_PATH );
417
418   RunTest( application );
419
420   return 0;
421 }