Updated demos to use DALi clang-format
[platform/core/uifw/dali-demo.git] / examples / magnifier / magnifier-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 // 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(DEMO_IMAGE_DIR "background-magnifier.jpg");
30 const char*   TOOLBAR_IMAGE(DEMO_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 window
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 window 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 window.
45  */
46 struct MagnifierPathConstraint
47 {
48   /**
49    * Constraint constructor
50    * @param[in] windowSize The window size so that the constraint can create a path
51    * within window bounds.
52    */
53   MagnifierPathConstraint(const Vector3& windowSize,
54                           Vector3        offset = Vector3::ZERO)
55   : mWindowSize(windowSize),
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(mWindowSize - 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 mWindowSize; ///< Keep track of the window size for determining path within window 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 } // namespace
161
162 // This example shows how to use the Magnifier component.
163 //
164 class ExampleController : public ConnectionTracker
165 {
166 public:
167   /**
168    * The example controller constructor.
169    * @param[in] application The application instance
170    */
171   ExampleController(Application& application)
172   : mApplication(application),
173     mView(),
174     mAnimationTime(0.0f),
175     mAnimationTimeProperty(Property::INVALID_INDEX),
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     Window window = application.GetWindow();
197     window.KeyEventSignal().Connect(this, &ExampleController::OnKeyEvent);
198     mWindowSize = window.GetSize();
199
200     // The Init signal is received once (only) during the Application lifetime
201
202     // Creates a default view with a default tool bar.
203     // The view is added to the window.
204     Toolkit::ToolBar toolBar;
205     mContent = DemoHelper::CreateView(application,
206                                       mView,
207                                       toolBar,
208                                       BACKGROUND_IMAGE,
209                                       TOOLBAR_IMAGE,
210                                       APPLICATION_TITLE);
211
212     mContent.SetProperty(Actor::Property::LEAVE_REQUIRED, true);
213     mContent.TouchedSignal().Connect(this, &ExampleController::OnTouched);
214
215     // Create magnifier (controlled by human touch)
216     Layer overlay = Layer::New();
217     overlay.SetProperty(Actor::Property::SENSITIVE, false);
218     overlay.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
219     overlay.SetProperty(Actor::Property::SIZE, mWindowSize);
220     window.Add(overlay);
221
222     mMagnifier = Toolkit::Magnifier::New();
223     mMagnifier.SetSourceActor(mView);
224     mMagnifier.SetProperty(Actor::Property::SIZE, MAGNIFIER_SIZE * mWindowSize.width); // Size of magnifier is in relation to window width
225     mMagnifier.SetProperty(Toolkit::Magnifier::Property::MAGNIFICATION_FACTOR, MAGNIFICATION_FACTOR);
226     mMagnifier.SetProperty(Actor::Property::SCALE, Vector3::ZERO);
227     overlay.Add(mMagnifier);
228
229     // Apply constraint to animate the position of the magnifier.
230     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));
231     constraint.AddSource(LocalSource(Actor::Property::SIZE));
232     constraint.AddSource(LocalSource(Actor::Property::PARENT_ORIGIN));
233     constraint.AddSource(LocalSource(Actor::Property::ANCHOR_POINT));
234     constraint.AddSource(ParentSource(Actor::Property::SIZE));
235     constraint.SetRemoveAction(Constraint::DISCARD);
236     constraint.Apply();
237
238     // Create bouncing magnifier automatically bounces around screen.
239     mBouncingMagnifier = Toolkit::Magnifier::New();
240     mBouncingMagnifier.SetSourceActor(mView);
241     mBouncingMagnifier.SetProperty(Actor::Property::SIZE, MAGNIFIER_SIZE * mWindowSize.width); // Size of magnifier is in relation to window width
242     mBouncingMagnifier.SetProperty(Toolkit::Magnifier::Property::MAGNIFICATION_FACTOR, MAGNIFICATION_FACTOR);
243     overlay.Add(mBouncingMagnifier);
244
245     mAnimationTimeProperty = mBouncingMagnifier.RegisterProperty("animationTime", 0.0f);
246     ContinueAnimation();
247
248     // Apply constraint to animate the position of the magnifier.
249     constraint = Constraint::New<Vector3>(mBouncingMagnifier, Actor::Property::POSITION, MagnifierPathConstraint(mWindowSize, mWindowSize * 0.5f));
250     constraint.AddSource(LocalSource(Actor::Property::SIZE));
251     constraint.AddSource(LocalSource(mAnimationTimeProperty));
252     constraint.Apply();
253
254     // Apply constraint to animate the source of the magnifier.
255     constraint = Constraint::New<Vector3>(mBouncingMagnifier, Toolkit::Magnifier::Property::SOURCE_POSITION, MagnifierPathConstraint(mWindowSize));
256     constraint.AddSource(LocalSource(Actor::Property::SIZE));
257     constraint.AddSource(LocalSource(mAnimationTimeProperty));
258     constraint.Apply();
259   }
260
261   /**
262    * Invoked whenever the animation finishes (every 60 seconds)
263    * @param[in] animation The animation
264    */
265   void OnAnimationFinished(Animation& animation)
266   {
267     animation.FinishedSignal().Disconnect(this, &ExampleController::OnAnimationFinished);
268     animation.Clear();
269     ContinueAnimation();
270   }
271
272   /**
273    * Resumes animation for another ANIMATION_DURATION seconds.
274    */
275   void ContinueAnimation()
276   {
277     Animation animation = Animation::New(ANIMATION_DURATION);
278     mAnimationTime += ANIMATION_DURATION;
279     animation.AnimateTo(Property(mBouncingMagnifier, mAnimationTimeProperty), mAnimationTime);
280     animation.Play();
281     animation.FinishedSignal().Connect(this, &ExampleController::OnAnimationFinished);
282   }
283
284   /**
285    * Invoked whenever the quit button is clicked
286    * @param[in] button the quit button
287    */
288   bool OnQuitButtonClicked(Toolkit::Button button)
289   {
290     // quit the application
291     mApplication.Quit();
292     return true;
293   }
294
295   /**
296    * Invoked whenever the content (screen) is touched
297    * @param[in] actor The actor that received the touch
298    * @param[in] event The touch-event information
299    */
300   bool OnTouched(Actor actor, const TouchEvent& event)
301   {
302     if(event.GetPointCount() > 0)
303     {
304       switch(event.GetState(0))
305       {
306         case PointState::DOWN:
307         case PointState::MOTION:
308         {
309           ShowMagnifier();
310           break;
311         }
312         case PointState::UP:
313         case PointState::LEAVE:
314         case PointState::INTERRUPTED:
315         {
316           HideMagnifier();
317           break;
318         }
319         case PointState::STATIONARY:
320         {
321           break;
322         }
323       } // end switch
324
325       Vector3 touchPoint(event.GetScreenPosition(0));
326
327       SetMagnifierPosition(touchPoint - mWindowSize * 0.5f);
328     }
329
330     return false;
331   }
332
333   /**
334    * Shows the magnifier
335    */
336   void ShowMagnifier()
337   {
338     if(!mMagnifierShown)
339     {
340       Animation animation = Animation::New(MAGNIFIER_DISPLAY_DURATION);
341       animation.AnimateTo(Property(mMagnifier, Actor::Property::SCALE), Vector3::ONE, AlphaFunction::EASE_IN);
342       animation.Play();
343       mMagnifierShown = true;
344     }
345   }
346
347   /**
348    * Hides the magnifier
349    */
350   void HideMagnifier()
351   {
352     if(mMagnifierShown)
353     {
354       Animation animation = Animation::New(MAGNIFIER_DISPLAY_DURATION);
355       animation.AnimateTo(Property(mMagnifier, Actor::Property::SCALE), Vector3::ZERO, AlphaFunction::EASE_OUT);
356       animation.Play();
357       mMagnifierShown = false;
358     }
359   }
360
361   /**
362    * Manually sets the magnifier position
363    * @param[in] position The magnifier's position relative to center of window
364    */
365   void SetMagnifierPosition(const Vector3 position)
366   {
367     mMagnifier.SetProperty(Toolkit::Magnifier::Property::SOURCE_POSITION, position);
368
369     // position magnifier glass such that bottom edge is touching/near top of finger.
370     Vector3 glassPosition(position);
371     glassPosition.y -= mWindowSize.width * MAGNIFIER_SIZE.height * 0.5f + mApplication.GetWindow().GetDpi().GetHeight() * FINGER_RADIUS_INCHES;
372
373     mMagnifier.SetProperty(Actor::Property::POSITION, glassPosition);
374   }
375
376   void OnKeyEvent(const KeyEvent& event)
377   {
378     if(event.GetState() == KeyEvent::DOWN)
379     {
380       if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
381       {
382         mApplication.Quit();
383       }
384     }
385   }
386
387 private:
388   Application&       mApplication;           ///< Application instance
389   Toolkit::Control   mView;                  ///< The view
390   Layer              mContent;               ///< The content layer
391   Toolkit::Magnifier mMagnifier;             ///< The manually controlled magnifier
392   Toolkit::Magnifier mBouncingMagnifier;     ///< The animating magnifier (swirly animation)
393   Vector3            mWindowSize;            ///< The size of the window
394   float              mAnimationTime;         ///< Keep track of start animation time.
395   Property::Index    mAnimationTimeProperty; ///< Animation time property (responsible for swirly animation)
396   bool               mMagnifierShown;        ///< Flag indicating whether the magnifier is being shown or not.
397 };
398
399 int DALI_EXPORT_API main(int argc, char** argv)
400 {
401   Application       application = Application::New(&argc, &argv, DEMO_THEME_PATH);
402   ExampleController test(application);
403   application.MainLoop();
404   return 0;
405 }