[dali_2.0.16] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / page-turn-view / page-turn-view-impl.cpp
1 /*
2  * Copyright (c) 2021 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 // CLASS HEADER
19 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/animation/animation.h>
24 #include <dali/public-api/animation/constraint.h>
25 #include <dali/public-api/object/type-registry-helper.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <cstring> // for strcmp
28
29 // INTERNAL INCLUDES
30 #include <dali-toolkit/devel-api/controls/control-devel.h>
31 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
32 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-book-spine-effect.h>
33 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-effect.h>
34 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
35 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
36 #include <dali-toolkit/public-api/visuals/visual-properties.h>
37
38 using namespace Dali;
39
40 namespace //Unnamed namespace
41 {
42 // properties set on shader, these properties have the constant value in regardless of the page status
43 const char* const PROPERTY_SPINE_SHADOW("uSpineShadowParameter"); // uniform for both spine and turn effect
44
45 // properties set on actor, the value of these properties varies depending on the page status
46 //    properties used in turn effect
47 const char* const PROPERTY_TURN_DIRECTION("uIsTurningBack");       // uniform
48 const char* const PROPERTY_COMMON_PARAMETERS("uCommonParameters"); //uniform
49
50 const char* const PROPERTY_PAN_DISPLACEMENT("panDisplacement"); // property used to constrain the uniforms
51 const char* const PROPERTY_PAN_CENTER("panCenter");             // property used to constrain the uniforms
52
53 // default grid density for page turn effect, 20 pixels by 20 pixels
54 const float DEFAULT_GRID_DENSITY(20.0f);
55
56 // to bent the page, the minimal horizontal pan start position is viewPageSize.x * MINIMUM_START_POSITION_RATIO
57 const float MINIMUM_START_POSITION_RATIO(0.6f);
58
59 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: viewPageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
60 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
61
62 // when the x component of pan position reaches viewPageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
63 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
64
65 // duration of animation, shorter for faster speed
66 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
67 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
68
69 // the major&minor radius (in pixels) to form an ellipse shape
70 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
71 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
72
73 // constants for shadow casting
74 const float   POINT_LIGHT_HEIGHT_RATIO(2.f);
75 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
76
77 // constraints ////////////////////////////////////////////////////////////////
78 /**
79  * Original Center Constraint
80  *
81  * This constraint adjusts the original center property of the page turn shader effect
82  * based on the X-direction displacement of the pan gesture
83  */
84 struct OriginalCenterConstraint
85 {
86   OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
87   : mOldCenter(originalCenter)
88   {
89     mNewCenter = originalCenter + offset;
90     mDistance  = offset.Length() * 0.5f;
91     mDirection = offset / mDistance;
92   }
93
94   void operator()(Vector2& current, const PropertyInputContainer& inputs)
95   {
96     float displacement = inputs[0]->GetFloat();
97
98     if(displacement < mDistance)
99     {
100       current = mOldCenter + mDirection * displacement;
101     }
102     else
103     {
104       current = mNewCenter + Vector2(0.25f * (displacement - mDistance), 0.f);
105     }
106   }
107
108   Vector2 mOldCenter;
109   Vector2 mNewCenter;
110   float   mDistance;
111   Vector2 mDirection;
112 };
113
114 /**
115  * Rotation Constraint
116  *
117  * This constraint adjusts the rotation property of the page actor
118  * based on the X-direction displacement of the pan gesture
119  */
120 struct RotationConstraint
121 {
122   RotationConstraint(float distance, float pageWidth, bool isTurnBack)
123   : mDistance(distance * 0.5f)
124   {
125     mStep     = 1.f / pageWidth;
126     mSign     = isTurnBack ? -1.0f : 1.0f;
127     mConst    = isTurnBack ? -1.0f : 0.f;
128     mRotation = isTurnBack ? Quaternion(Radian(-Math::PI), Vector3::YAXIS) : Quaternion(Radian(0.f), Vector3::YAXIS);
129   }
130
131   void operator()(Quaternion& current, const PropertyInputContainer& inputs)
132   {
133     float displacement = inputs[0]->GetFloat();
134     if(displacement < mDistance)
135     {
136       current = mRotation;
137     }
138     else
139     {
140       float coef  = std::max(-1.0f, mStep * (mDistance - displacement));
141       float angle = Math::PI * (mConst + mSign * coef);
142       current     = Quaternion(Radian(angle), Vector3::YAXIS);
143     }
144   }
145
146   float      mDistance;
147   float      mStep;
148   float      mConst;
149   float      mSign;
150   Quaternion mRotation;
151 };
152
153 /**
154  * Current Center Constraint
155  *
156  * This constraint adjusts the current center property of the page turn shader effect
157  * based on the pan position and the original center position
158  */
159 struct CurrentCenterConstraint
160 {
161   CurrentCenterConstraint(float pageWidth)
162   : mPageWidth(pageWidth)
163   {
164     mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
165   }
166
167   void operator()(Vector2& current, const PropertyInputContainer& inputs)
168   {
169     const Vector2& centerPosition = inputs[0]->GetVector2();
170     if(centerPosition.x > 0.f)
171     {
172       current.x = mThres + centerPosition.x * 0.5f;
173       current.y = centerPosition.y;
174     }
175     else
176     {
177       const Vector2& centerOrigin = inputs[1]->GetVector2();
178       Vector2        direction    = centerOrigin - Vector2(mThres, centerPosition.y);
179       float          coef         = 1.f + (centerPosition.x * 2.f / mPageWidth);
180       // when coef <= 0, the page is flat, slow down the last moment of the page stretch by 10 times to avoid a small bounce
181       if(coef < 0.025f)
182       {
183         coef = (coef + 0.225f) / 10.0f;
184       }
185       current = centerOrigin - direction * coef;
186     }
187   }
188
189   float mPageWidth;
190   float mThres;
191 };
192
193 struct ShadowBlurStrengthConstraint
194 {
195   ShadowBlurStrengthConstraint(float thres)
196   : mThres(thres)
197   {
198   }
199
200   void operator()(float& blurStrength, const PropertyInputContainer& inputs)
201   {
202     float displacement = inputs[2]->GetFloat();
203     if(EqualsZero(displacement))
204     {
205       const Vector2& cur = inputs[0]->GetVector2();
206       const Vector2& ori = inputs[1]->GetVector2();
207       blurStrength       = 5.f * (ori - cur).Length() / mThres;
208     }
209     else
210     {
211       blurStrength = 1.f - (displacement - 2.f * mThres) / mThres;
212     }
213
214     blurStrength = blurStrength > 1.f ? 1.f : (blurStrength < 0.f ? 0.f : blurStrength);
215   }
216
217   float mThres;
218 };
219
220 } //unnamed namespace
221
222 namespace Dali
223 {
224 namespace Toolkit
225 {
226 namespace Internal
227 {
228 namespace
229 {
230 BaseHandle Create()
231 {
232   // empty handle as we cannot create PageTurnView(but type registered for page turn signal)
233   return BaseHandle();
234 }
235
236 // Setup properties, signals and actions using the type-registry.
237 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::PageTurnView, Toolkit::Control, Create);
238
239 DALI_PROPERTY_REGISTRATION(Toolkit, PageTurnView, "viewPageSize", VECTOR2, VIEW_PAGE_SIZE)
240 DALI_PROPERTY_REGISTRATION(Toolkit, PageTurnView, "currentPageId", INTEGER, CURRENT_PAGE_ID)
241 DALI_PROPERTY_REGISTRATION(Toolkit, PageTurnView, "spineShadow", VECTOR2, SPINE_SHADOW)
242
243 DALI_SIGNAL_REGISTRATION(Toolkit, PageTurnView, "pageTurnStarted", SIGNAL_PAGE_TURN_STARTED)
244 DALI_SIGNAL_REGISTRATION(Toolkit, PageTurnView, "pageTurnFinished", SIGNAL_PAGE_TURN_FINISHED)
245 DALI_SIGNAL_REGISTRATION(Toolkit, PageTurnView, "pagePanStarted", SIGNAL_PAGE_PAN_STARTED)
246 DALI_SIGNAL_REGISTRATION(Toolkit, PageTurnView, "pagePanFinished", SIGNAL_PAGE_PAN_FINISHED)
247
248 DALI_TYPE_REGISTRATION_END()
249
250 } // namespace
251
252 // these several constants are also used in the derived classes
253 const char* const PageTurnView::PROPERTY_TEXTURE_WIDTH("uTextureWidth");    // uniform name
254 const char* const PageTurnView::PROPERTY_ORIGINAL_CENTER("originalCenter"); // property used to constrain the uniform
255 const char* const PageTurnView::PROPERTY_CURRENT_CENTER("currentCenter");   // property used to constrain the uniform
256 const int         PageTurnView::MAXIMUM_TURNING_NUM              = 4;
257 const int         PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
258 const int         PageTurnView::NUMBER_OF_CACHED_PAGES           = NUMBER_OF_CACHED_PAGES_EACH_SIDE * 2;
259 const float       PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE    = 1.0f;
260
261 PageTurnView::Page::Page()
262 : isTurnBack(false)
263 {
264   actor = Actor::New();
265   actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER_LEFT);
266   actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER_LEFT);
267   actor.SetProperty(Actor::Property::VISIBLE, false);
268
269   propertyPanDisplacement = actor.RegisterProperty(PROPERTY_PAN_DISPLACEMENT, 0.f);
270   propertyPanCenter       = actor.RegisterProperty(PROPERTY_PAN_CENTER, Vector2::ZERO);
271
272   propertyOriginalCenter = actor.RegisterProperty(PROPERTY_ORIGINAL_CENTER, Vector2::ZERO);
273   propertyCurrentCenter  = actor.RegisterProperty(PROPERTY_CURRENT_CENTER, Vector2::ZERO);
274   Matrix zeroMatrix(true);
275   actor.RegisterProperty(PROPERTY_COMMON_PARAMETERS, zeroMatrix);
276   propertyTurnDirection = actor.RegisterProperty(PROPERTY_TURN_DIRECTION, -1.f);
277 }
278
279 void PageTurnView::Page::SetTexture(Texture texture)
280 {
281   if(!textureSet)
282   {
283     textureSet = TextureSet::New();
284   }
285   textureSet.SetTexture(0u, texture);
286 }
287
288 void PageTurnView::Page::UseEffect(Shader newShader)
289 {
290   shader = newShader;
291   if(renderer)
292   {
293     renderer.SetShader(shader);
294   }
295 }
296
297 void PageTurnView::Page::UseEffect(Shader newShader, Geometry geometry)
298 {
299   UseEffect(newShader);
300
301   if(!renderer)
302   {
303     renderer = Renderer::New(geometry, shader);
304
305     if(!textureSet)
306     {
307       textureSet = TextureSet::New();
308     }
309
310     renderer.SetTextures(textureSet);
311     renderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON);
312     actor.AddRenderer(renderer);
313   }
314 }
315
316 void PageTurnView::Page::ChangeTurnDirection()
317 {
318   isTurnBack = !isTurnBack;
319   actor.SetProperty(propertyTurnDirection, isTurnBack ? 1.f : -1.f);
320 }
321
322 void PageTurnView::Page::SetPanDisplacement(float value)
323 {
324   actor.SetProperty(propertyPanDisplacement, value);
325 }
326
327 void PageTurnView::Page::SetPanCenter(const Vector2& value)
328 {
329   actor.SetProperty(propertyPanCenter, value);
330 }
331
332 void PageTurnView::Page::SetOriginalCenter(const Vector2& value)
333 {
334   actor.SetProperty(propertyOriginalCenter, value);
335 }
336
337 void PageTurnView::Page::SetCurrentCenter(const Vector2& value)
338 {
339   actor.SetProperty(propertyCurrentCenter, value);
340 }
341
342 PageTurnView::PageTurnView(PageFactory& pageFactory, const Vector2& viewPageSize)
343 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
344   mPageFactory(&pageFactory),
345   mPageSize(viewPageSize),
346   mSpineShadowParameter(DEFAULT_SPINE_SHADOW_PARAMETER),
347   mDistanceUpCorner(0.f),
348   mDistanceBottomCorner(0.f),
349   mPanDisplacement(0.f),
350   mTotalPageCount(0),
351   mCurrentPageIndex(0),
352   mTurningPageIndex(0),
353   mIndex(0),
354   mSlidingCount(0),
355   mAnimatingCount(0),
356   mConstraints(false),
357   mPress(false),
358   mPageUpdated(true),
359   mPageTurnStartedSignal(),
360   mPageTurnFinishedSignal(),
361   mPagePanStartedSignal(),
362   mPagePanFinishedSignal()
363 {
364 }
365
366 PageTurnView::~PageTurnView()
367 {
368 }
369
370 void PageTurnView::OnInitialize()
371 {
372   // create the book spine effect for static pages
373   Property::Map spineEffectMap = CreatePageTurnBookSpineEffect();
374   mSpineEffectShader           = CreateShader(spineEffectMap);
375   mSpineEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter);
376   // create the turn effect for turning pages
377   Property::Map turnEffectMap = CreatePageTurnEffect();
378   mTurnEffectShader           = CreateShader(turnEffectMap);
379   mTurnEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter);
380
381   // create the grid geometry for pages
382   uint16_t width  = static_cast<uint16_t>(mPageSize.width / DEFAULT_GRID_DENSITY + 0.5f);
383   uint16_t height = static_cast<uint16_t>(mPageSize.height / DEFAULT_GRID_DENSITY + 0.5f);
384   mGeometry       = VisualFactoryCache::CreateGridGeometry(Uint16Pair(width, height));
385
386   mPages.reserve(NUMBER_OF_CACHED_PAGES);
387   for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++)
388   {
389     mPages.push_back(Page());
390     mPages[i].actor.SetProperty(Actor::Property::SIZE, mPageSize);
391     Self().Add(mPages[i].actor);
392   }
393
394   // create the layer for turning pages
395   mTurningPageLayer = Layer::New();
396   mTurningPageLayer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER_LEFT);
397   mTurningPageLayer.SetProperty(Layer::Property::BEHAVIOR, Layer::LAYER_3D);
398   mTurningPageLayer.Raise();
399
400   // Set control size and the parent origin of page layers
401   OnPageTurnViewInitialize();
402
403   Self().Add(mTurningPageLayer);
404
405   mTotalPageCount = static_cast<int>(mPageFactory->GetNumberOfPages());
406   // add pages to the scene, and set depth for the stacked pages
407   for(int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++)
408   {
409     AddPage(i);
410     mPages[i].actor.SetProperty(Actor::Property::POSITION_Z, -static_cast<float>(i) * STATIC_PAGE_INTERVAL_DISTANCE);
411   }
412   mPages[0].actor.SetProperty(Actor::Property::VISIBLE, true);
413
414   // enable the pan gesture which is attached to the control
415   EnableGestureDetection(GestureType::Value(GestureType::PAN));
416
417   DevelControl::SetAccessibilityConstructor(Self(), [](Dali::Actor actor) {
418     return std::unique_ptr<Dali::Accessibility::Accessible>(
419       new DevelControl::AccessibleImpl(actor, Dali::Accessibility::Role::PAGE_TAB_LIST));
420   });
421 }
422
423 Shader PageTurnView::CreateShader(const Property::Map& shaderMap)
424 {
425   Shader           shader;
426   Property::Value* shaderValue = shaderMap.Find(Toolkit::Visual::Property::SHADER, CUSTOM_SHADER);
427   Property::Map    shaderSource;
428   if(shaderValue && shaderValue->Get(shaderSource))
429   {
430     std::string      vertexShader;
431     Property::Value* vertexShaderValue = shaderSource.Find(Toolkit::Visual::Shader::Property::VERTEX_SHADER, CUSTOM_VERTEX_SHADER);
432     if(!vertexShaderValue || !vertexShaderValue->Get(vertexShader))
433     {
434       DALI_LOG_ERROR("PageTurnView::CreateShader failed: vertex shader source is not available.\n");
435     }
436     std::string      fragmentShader;
437     Property::Value* fragmentShaderValue = shaderSource.Find(Toolkit::Visual::Shader::Property::FRAGMENT_SHADER, CUSTOM_FRAGMENT_SHADER);
438     if(!fragmentShaderValue || !fragmentShaderValue->Get(fragmentShader))
439     {
440       DALI_LOG_ERROR("PageTurnView::CreateShader failed: fragment shader source is not available.\n");
441     }
442     shader = Shader::New(vertexShader, fragmentShader);
443   }
444   else
445   {
446     DALI_LOG_ERROR("PageTurnView::CreateShader failed: shader source is not available.\n");
447   }
448
449   return shader;
450 }
451
452 void PageTurnView::SetupShadowView()
453 {
454   mShadowView    = Toolkit::ShadowView::New(0.25f, 0.25f);
455   Vector3 origin = mTurningPageLayer.GetCurrentProperty<Vector3>(Actor::Property::PARENT_ORIGIN);
456   mShadowView.SetProperty(Actor::Property::PARENT_ORIGIN, origin);
457   mShadowView.SetProperty(Actor::Property::ANCHOR_POINT, origin);
458   mShadowView.SetPointLightFieldOfView(Math::PI / 2.0f);
459   mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
460
461   mShadowPlaneBackground = Actor::New();
462   mShadowPlaneBackground.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
463   mShadowPlaneBackground.SetProperty(Actor::Property::SIZE, mControlSize);
464   Self().Add(mShadowPlaneBackground);
465   mShadowView.SetShadowPlaneBackground(mShadowPlaneBackground);
466
467   mPointLight = Actor::New();
468   mPointLight.SetProperty(Actor::Property::ANCHOR_POINT, origin);
469   mPointLight.SetProperty(Actor::Property::PARENT_ORIGIN, origin);
470   mPointLight.SetProperty(Actor::Property::POSITION, Vector3(0.f, 0.f, mPageSize.width * POINT_LIGHT_HEIGHT_RATIO));
471   Self().Add(mPointLight);
472   mShadowView.SetPointLight(mPointLight);
473
474   mTurningPageLayer.Add(mShadowView);
475   mShadowView.Activate();
476 }
477
478 void PageTurnView::OnSceneConnection(int depth)
479 {
480   SetupShadowView();
481
482   Control::OnSceneConnection(depth);
483 }
484
485 void PageTurnView::OnSceneDisconnection()
486 {
487   if(mShadowView)
488   {
489     mShadowView.RemoveConstraints();
490     mPointLight.Unparent();
491     mShadowPlaneBackground.Unparent();
492     mShadowView.Unparent();
493   }
494
495   // make sure the status of the control is updated correctly when the pan gesture is interrupted
496   StopTurning();
497
498   Control::OnSceneDisconnection();
499 }
500
501 void PageTurnView::SetPageSize(const Vector2& viewPageSize)
502 {
503   mPageSize = viewPageSize;
504
505   if(mPointLight)
506   {
507     mPointLight.SetProperty(Actor::Property::POSITION, Vector3(0.f, 0.f, mPageSize.width * POINT_LIGHT_HEIGHT_RATIO));
508   }
509
510   for(size_t i = 0; i < mPages.size(); i++)
511   {
512     mPages[i].actor.SetProperty(Actor::Property::SIZE, mPageSize);
513   }
514
515   OnPageTurnViewInitialize();
516
517   if(mShadowPlaneBackground)
518   {
519     mShadowPlaneBackground.SetProperty(Actor::Property::SIZE, mControlSize);
520   }
521 }
522
523 Vector2 PageTurnView::GetPageSize()
524 {
525   return mPageSize;
526 }
527
528 void PageTurnView::SetSpineShadowParameter(const Vector2& spineShadowParameter)
529 {
530   mSpineShadowParameter = spineShadowParameter;
531
532   // set spine shadow parameter to all the shader effects
533   mSpineEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter);
534   mTurnEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter);
535 }
536
537 Vector2 PageTurnView::GetSpineShadowParameter()
538 {
539   return mSpineShadowParameter;
540 }
541
542 void PageTurnView::GoToPage(unsigned int pageId)
543 {
544   int pageIdx = Clamp(static_cast<int>(pageId), 0, mTotalPageCount - 1);
545
546   if(mCurrentPageIndex == pageIdx)
547   {
548     return;
549   }
550
551   // if any animation ongoing, stop it.
552   StopTurning();
553
554   // record the new current page index
555   mCurrentPageIndex = pageIdx;
556
557   // add the current page and the pages right before and after it
558   for(int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++)
559   {
560     AddPage(i);
561   }
562
563   mPages[pageId % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::VISIBLE, true);
564   if(pageId > 0)
565   {
566     mPages[(pageId - 1) % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::VISIBLE, true);
567   }
568   // set ordered depth to the stacked pages
569   OrganizePageDepth();
570 }
571
572 unsigned int PageTurnView::GetCurrentPage()
573 {
574   DALI_ASSERT_ALWAYS(mCurrentPageIndex >= 0);
575   return static_cast<unsigned int>(mCurrentPageIndex);
576 }
577
578 void PageTurnView::AddPage(int pageIndex)
579 {
580   if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
581   {
582     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
583
584     Texture newPage;
585     newPage = mPageFactory->NewPage(pageIndex);
586     DALI_ASSERT_ALWAYS(newPage && "must pass in valid texture");
587
588     bool isLeftSide = (pageIndex < mCurrentPageIndex);
589     if(mPages[index].isTurnBack != isLeftSide)
590     {
591       mPages[index].ChangeTurnDirection();
592     }
593
594     float degree = isLeftSide ? 180.f : 0.f;
595     mPages[index].actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(Degree(degree), Vector3::YAXIS));
596     mPages[index].actor.SetProperty(Actor::Property::VISIBLE, false);
597     mPages[index].UseEffect(mSpineEffectShader, mGeometry);
598     mPages[index].SetTexture(newPage);
599
600     // For Portrait, nothing to do
601     // For Landscape, set the parent origin to CENTER
602     OnAddPage(mPages[index].actor, isLeftSide);
603   }
604 }
605
606 void PageTurnView::RemovePage(int pageIndex)
607 {
608   if(pageIndex > -1 && pageIndex < mTotalPageCount)
609   {
610     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
611     mPages[index].actor.SetProperty(Actor::Property::VISIBLE, false);
612   }
613 }
614
615 void PageTurnView::OnPan(const PanGesture& gesture)
616 {
617   // the pan gesture is attached to control itself instead of each page
618   switch(gesture.GetState())
619   {
620     case GestureState::STARTED:
621     {
622       // check whether the undergoing turning page number already reaches the maximum allowed
623       if(mPageUpdated && mAnimatingCount < MAXIMUM_TURNING_NUM && mSlidingCount < 1)
624       {
625         const Vector2& position = gesture.GetPosition();
626         SetPanActor(position);                                                                                        // determine which page actor is panned
627         if(mTurningPageIndex != -1 && mPages[mTurningPageIndex % NUMBER_OF_CACHED_PAGES].actor.GetParent() != Self()) // if the page is added to turning layer,it is undergoing an animation currently
628         {
629           mTurningPageIndex = -1;
630         }
631         PanStarted(SetPanPosition(position)); // pass in the pan position in the local page coordinate
632       }
633       else
634       {
635         mTurningPageIndex = -1;
636       }
637       break;
638     }
639     case GestureState::CONTINUING:
640     {
641       PanContinuing(SetPanPosition(gesture.GetPosition())); // pass in the pan position in the local page coordinate
642       break;
643     }
644     case GestureState::FINISHED:
645     case GestureState::CANCELLED:
646     {
647       PanFinished(SetPanPosition(gesture.GetPosition()), gesture.GetSpeed());
648       break;
649     }
650     case GestureState::CLEAR:
651     case GestureState::POSSIBLE:
652     default:
653     {
654       break;
655     }
656   }
657 }
658
659 void PageTurnView::PanStarted(const Vector2& gesturePosition)
660 {
661   mPressDownPosition = gesturePosition;
662
663   if(mTurningPageIndex == -1)
664   {
665     return;
666   }
667
668   mIndex = mTurningPageIndex % NUMBER_OF_CACHED_PAGES;
669
670   mOriginalCenter = gesturePosition;
671   mPress          = false;
672   mPageUpdated    = false;
673
674   // Guard against destruction during signal emission
675   Toolkit::PageTurnView handle(GetOwner());
676   mPagePanStartedSignal.Emit(handle);
677 }
678
679 void PageTurnView::PanContinuing(const Vector2& gesturePosition)
680 {
681   if(mTurningPageIndex == -1)
682   {
683     return;
684   }
685
686   // Guard against destruction during signal emission
687   Toolkit::PageTurnView handle(GetOwner());
688
689   if(!mPress)
690   {
691     // when the touch down position is near the spine
692     // or when the panning goes outwards or some other position which would tear the paper in real situation
693     // we change the start position into the current panning position and update the shader parameters
694     if(mOriginalCenter.x < mPageSize.width * MINIMUM_START_POSITION_RATIO || gesturePosition.x > mOriginalCenter.x - 1.0f || ((gesturePosition.x / mOriginalCenter.x > gesturePosition.y / mOriginalCenter.y) && (gesturePosition.x / mOriginalCenter.x > (gesturePosition.y - mPageSize.height) / (mOriginalCenter.y - mPageSize.height))))
695     {
696       mOriginalCenter = gesturePosition;
697     }
698     else
699     {
700       mDistanceUpCorner     = mOriginalCenter.Length();
701       mDistanceBottomCorner = (mOriginalCenter - Vector2(0.0f, mPageSize.height)).Length();
702       mShadowView.Add(mPages[mIndex].actor);
703       mPages[mIndex].UseEffect(mTurnEffectShader);
704       mPages[mIndex].SetOriginalCenter(mOriginalCenter);
705       mCurrentCenter = mOriginalCenter;
706       mPages[mIndex].SetCurrentCenter(mCurrentCenter);
707       mPanDisplacement = 0.f;
708       mConstraints     = false;
709       mPress           = true;
710       mAnimatingCount++;
711
712       mPageTurnStartedSignal.Emit(handle, static_cast<unsigned int>(mTurningPageIndex), !mPages[mIndex].isTurnBack);
713       int id = mTurningPageIndex + (mPages[mIndex].isTurnBack ? -1 : 1);
714       if(id >= 0 && id < mTotalPageCount)
715       {
716         mPages[id % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::VISIBLE, true);
717       }
718
719       mShadowView.RemoveConstraints();
720       Actor self = Self();
721       mPages[mIndex].SetPanDisplacement(0.f);
722
723       Constraint shadowBlurStrengthConstraint = Constraint::New<float>(mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint(mPageSize.width * PAGE_TURN_OVER_THRESHOLD_RATIO));
724       shadowBlurStrengthConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter));
725       shadowBlurStrengthConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter));
726       shadowBlurStrengthConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement));
727       shadowBlurStrengthConstraint.Apply();
728     }
729   }
730   else
731   {
732     Vector2 currentCenter = gesturePosition;
733
734     // Test whether the new current center would tear the paper from the top pine in real situation
735     // we do not forbid this totally, which would restrict the panning gesture too much
736     // instead, set it to the nearest allowable position
737     float distanceUpCorner     = currentCenter.Length();
738     float distanceBottomCorner = (currentCenter - Vector2(0.0f, mPageSize.height)).Length();
739     if(distanceUpCorner > mDistanceUpCorner)
740     {
741       currentCenter = currentCenter * mDistanceUpCorner / distanceUpCorner;
742     }
743     // would tear the paper from the bottom spine in real situation
744     if(distanceBottomCorner > mDistanceBottomCorner)
745     {
746       currentCenter = ((currentCenter - Vector2(0.0f, mPageSize.height)) * mDistanceBottomCorner / distanceBottomCorner + Vector2(0.0f, mPageSize.height));
747     }
748     // If direction has a very high y component, reduce it.
749     Vector2 curveDirection = currentCenter - mOriginalCenter;
750     if(fabs(curveDirection.y) > fabs(curveDirection.x))
751     {
752       currentCenter.y = mOriginalCenter.y + (currentCenter.y - mOriginalCenter.y) * fabs(curveDirection.x / curveDirection.y);
753     }
754     // If the vertical distance is high, reduce it
755     float yShift = currentCenter.y - mOriginalCenter.y;
756     if(fabs(yShift) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO)
757     {
758       currentCenter.y = mOriginalCenter.y + yShift * mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO / fabs(yShift);
759     }
760
761     // use contraints to control the page shape and rotation when the pan position is near the spine
762     if(currentCenter.x <= mPageSize.width * PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width * PAGE_TURN_OVER_THRESHOLD_RATIO)
763     {
764       // set the property values used by the constraints
765       mPanDisplacement = mPageSize.width * PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
766       mPages[mIndex].SetPanDisplacement(mPanDisplacement);
767       mPages[mIndex].SetPanCenter(currentCenter);
768
769       // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
770       // also set up the RotationConstraint to the page actor
771       if(!mConstraints)
772       {
773         Vector2 corner;
774         // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
775         if(currentCenter.y >= mOriginalCenter.y)
776         {
777           corner = Vector2(1.1f * mPageSize.width, 0.f);
778         }
779         else
780         {
781           corner = mPageSize * 1.1f;
782         }
783
784         Vector2 offset(currentCenter - mOriginalCenter);
785         float   k = -((mOriginalCenter.x - corner.x) * offset.x + (mOriginalCenter.y - corner.y) * offset.y) / (offset.x * offset.x + offset.y * offset.y);
786         offset *= k;
787         Actor self = Self();
788
789         Constraint originalCenterConstraint = Constraint::New<Vector2>(mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter, OriginalCenterConstraint(mOriginalCenter, offset));
790         originalCenterConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement));
791         originalCenterConstraint.Apply();
792
793         Constraint currentCenterConstraint = Constraint::New<Vector2>(mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter, CurrentCenterConstraint(mPageSize.width));
794         currentCenterConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyPanCenter));
795         currentCenterConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter));
796         currentCenterConstraint.Apply();
797
798         PageTurnApplyInternalConstraint(mPages[mIndex].actor, mPageSize.height);
799
800         float      distance           = offset.Length();
801         Constraint rotationConstraint = Constraint::New<Quaternion>(mPages[mIndex].actor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mPages[mIndex].isTurnBack));
802         rotationConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement));
803         rotationConstraint.Apply();
804
805         mConstraints = true;
806       }
807     }
808     else
809     {
810       if(mConstraints) // remove the constraint is the pan position move back to far away from the spine
811       {
812         mPages[mIndex].actor.RemoveConstraints();
813         mPages[mIndex].SetOriginalCenter(mOriginalCenter);
814         mConstraints     = false;
815         mPanDisplacement = 0.f;
816       }
817
818       mPages[mIndex].SetCurrentCenter(currentCenter);
819       mCurrentCenter = currentCenter;
820       PageTurnApplyInternalConstraint(mPages[mIndex].actor, mPageSize.height);
821     }
822   }
823 }
824
825 void PageTurnView::PanFinished(const Vector2& gesturePosition, float gestureSpeed)
826 {
827   // Guard against destruction during signal emission
828   Toolkit::PageTurnView handle(GetOwner());
829
830   if(mTurningPageIndex == -1)
831   {
832     if(mAnimatingCount < MAXIMUM_TURNING_NUM && mSlidingCount < 1)
833     {
834       OnPossibleOutwardsFlick(gesturePosition, gestureSpeed);
835     }
836
837     return;
838   }
839
840   mPagePanFinishedSignal.Emit(handle);
841
842   if(mPress)
843   {
844     if(mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
845     {
846       // update the pages here instead of in the TurnedOver callback function
847       // as new page is allowed to respond to the pan gesture before other pages finishing animation
848       if(mPages[mIndex].isTurnBack)
849       {
850         mCurrentPageIndex--;
851         RemovePage(mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE);
852         AddPage(mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE);
853       }
854       else
855       {
856         mCurrentPageIndex++;
857         RemovePage(mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE - 1);
858         AddPage(mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE - 1);
859       }
860       OrganizePageDepth();
861
862       // set up an animation to turn the page over
863       float     width     = mPageSize.width * (1.f + PAGE_TURN_OVER_THRESHOLD_RATIO);
864       Animation animation = Animation::New(std::max(0.1f, PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)));
865       animation.AnimateTo(Property(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement),
866                           width,
867                           AlphaFunction::EASE_OUT_SINE);
868       animation.AnimateTo(Property(mPages[mIndex].actor, mPages[mIndex].propertyPanCenter),
869                           Vector2(-mPageSize.width * 1.1f, 0.5f * mPageSize.height),
870                           AlphaFunction::EASE_OUT_SINE);
871       mAnimationPageIdPair[animation] = mTurningPageIndex;
872       animation.Play();
873       animation.FinishedSignal().Connect(this, &PageTurnView::TurnedOver);
874     }
875     else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
876     {
877       Animation animation = Animation::New(PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO);
878       animation.AnimateTo(Property(mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter),
879                           mOriginalCenter,
880                           AlphaFunction::LINEAR);
881       mAnimationPageIdPair[animation] = mTurningPageIndex;
882       animation.Play();
883       mSlidingCount++;
884       animation.FinishedSignal().Connect(this, &PageTurnView::SliddenBack);
885
886       mPageTurnStartedSignal.Emit(handle, static_cast<unsigned int>(mTurningPageIndex), mPages[mIndex].isTurnBack);
887     }
888   }
889   else
890   {
891     // In portrait view, an outwards flick should turn the previous page back
892     // In landscape view, nothing to do
893     OnPossibleOutwardsFlick(gesturePosition, gestureSpeed);
894   }
895   mPageUpdated = true;
896 }
897
898 void PageTurnView::TurnedOver(Animation& animation)
899 {
900   int pageId = mAnimationPageIdPair[animation];
901   int index  = pageId % NUMBER_OF_CACHED_PAGES;
902
903   mPages[index].ChangeTurnDirection();
904   mPages[index].actor.RemoveConstraints();
905   Self().Add(mPages[index].actor);
906   mAnimatingCount--;
907   mAnimationPageIdPair.erase(animation);
908
909   float degree = mPages[index].isTurnBack ? 180.f : 0.f;
910   mPages[index].actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(Degree(degree), Vector3::YAXIS));
911   mPages[index].UseEffect(mSpineEffectShader);
912
913   int id = pageId + (mPages[index].isTurnBack ? -1 : 1);
914   if(id >= 0 && id < mTotalPageCount)
915   {
916     mPages[id % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::VISIBLE, false);
917   }
918
919   OnTurnedOver(mPages[index].actor, mPages[index].isTurnBack);
920
921   // Guard against destruction during signal emission
922   Toolkit::PageTurnView handle(GetOwner());
923   mPageTurnFinishedSignal.Emit(handle, static_cast<unsigned int>(pageId), mPages[index].isTurnBack);
924 }
925
926 void PageTurnView::SliddenBack(Animation& animation)
927 {
928   int pageId = mAnimationPageIdPair[animation];
929   int index  = pageId % NUMBER_OF_CACHED_PAGES;
930   Self().Add(mPages[index].actor);
931   mSlidingCount--;
932   mAnimatingCount--;
933   mAnimationPageIdPair.erase(animation);
934
935   mPages[index].UseEffect(mSpineEffectShader);
936
937   int id = pageId + (mPages[index].isTurnBack ? -1 : 1);
938   if(id >= 0 && id < mTotalPageCount)
939   {
940     mPages[id % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::VISIBLE, false);
941   }
942
943   // Guard against destruction during signal emission
944   Toolkit::PageTurnView handle(GetOwner());
945   mPageTurnFinishedSignal.Emit(handle, static_cast<unsigned int>(pageId), mPages[index].isTurnBack);
946 }
947
948 void PageTurnView::OrganizePageDepth()
949 {
950   for(int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++)
951   {
952     if(mCurrentPageIndex + i < mTotalPageCount)
953     {
954       mPages[(mCurrentPageIndex + i) % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::POSITION_Z, -static_cast<float>(i) * STATIC_PAGE_INTERVAL_DISTANCE);
955     }
956     if(mCurrentPageIndex >= i + 1)
957     {
958       mPages[(mCurrentPageIndex - i - 1) % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::POSITION_Z, -static_cast<float>(i) * STATIC_PAGE_INTERVAL_DISTANCE);
959     }
960   }
961 }
962
963 void PageTurnView::StopTurning()
964 {
965   mAnimatingCount = 0;
966   mSlidingCount   = 0;
967
968   if(!mPageUpdated)
969   {
970     int index = mTurningPageIndex % NUMBER_OF_CACHED_PAGES;
971     Self().Add(mPages[index].actor);
972     mPages[index].actor.RemoveConstraints();
973     mPages[index].UseEffect(mSpineEffectShader);
974     float degree = mTurningPageIndex == mCurrentPageIndex ? 0.f : 180.f;
975     mPages[index].actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(Degree(degree), Vector3::YAXIS));
976     mPageUpdated = true;
977   }
978
979   if(!mAnimationPageIdPair.empty())
980   {
981     for(std::map<Animation, int>::iterator it = mAnimationPageIdPair.begin(); it != mAnimationPageIdPair.end(); ++it)
982     {
983       static_cast<Animation>(it->first).SetCurrentProgress(1.f);
984     }
985   }
986 }
987
988 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
989 {
990   return mPageTurnStartedSignal;
991 }
992
993 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
994 {
995   return mPageTurnFinishedSignal;
996 }
997
998 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
999 {
1000   return mPagePanStartedSignal;
1001 }
1002
1003 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1004 {
1005   return mPagePanFinishedSignal;
1006 }
1007
1008 bool PageTurnView::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
1009 {
1010   Dali::BaseHandle handle(object);
1011
1012   bool                  connected(true);
1013   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast(handle);
1014
1015   if(0 == strcmp(signalName.c_str(), SIGNAL_PAGE_TURN_STARTED))
1016   {
1017     pageTurnView.PageTurnStartedSignal().Connect(tracker, functor);
1018   }
1019   else if(0 == strcmp(signalName.c_str(), SIGNAL_PAGE_TURN_FINISHED))
1020   {
1021     pageTurnView.PageTurnFinishedSignal().Connect(tracker, functor);
1022   }
1023   else if(0 == strcmp(signalName.c_str(), SIGNAL_PAGE_PAN_STARTED))
1024   {
1025     pageTurnView.PagePanStartedSignal().Connect(tracker, functor);
1026   }
1027   else if(0 == strcmp(signalName.c_str(), SIGNAL_PAGE_PAN_FINISHED))
1028   {
1029     pageTurnView.PagePanFinishedSignal().Connect(tracker, functor);
1030   }
1031   else
1032   {
1033     // signalName does not match any signal
1034     connected = false;
1035   }
1036
1037   return connected;
1038 }
1039
1040 void PageTurnView::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
1041 {
1042   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast(Dali::BaseHandle(object));
1043
1044   if(pageTurnView)
1045   {
1046     PageTurnView& pageTurnViewImpl(GetImplementation(pageTurnView));
1047
1048     switch(index)
1049     {
1050       case Toolkit::PageTurnView::Property::VIEW_PAGE_SIZE:
1051       {
1052         pageTurnViewImpl.SetPageSize(value.Get<Vector2>());
1053         break;
1054       }
1055       case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1056       {
1057         pageTurnViewImpl.GoToPage(value.Get<int>());
1058         break;
1059       }
1060       case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1061       {
1062         pageTurnViewImpl.SetSpineShadowParameter(value.Get<Vector2>());
1063         break;
1064       }
1065     }
1066   }
1067 }
1068
1069 Property::Value PageTurnView::GetProperty(BaseObject* object, Property::Index index)
1070 {
1071   Property::Value value;
1072
1073   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast(Dali::BaseHandle(object));
1074
1075   if(pageTurnView)
1076   {
1077     PageTurnView& pageTurnViewImpl(GetImplementation(pageTurnView));
1078
1079     switch(index)
1080     {
1081       case Toolkit::PageTurnView::Property::VIEW_PAGE_SIZE:
1082       {
1083         value = pageTurnViewImpl.GetPageSize();
1084         break;
1085       }
1086       case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1087       {
1088         value = static_cast<int>(pageTurnViewImpl.GetCurrentPage());
1089         break;
1090       }
1091       case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1092       {
1093         value = pageTurnViewImpl.GetSpineShadowParameter();
1094         break;
1095       }
1096     }
1097   }
1098   return value;
1099 }
1100
1101 } // namespace Internal
1102
1103 } // namespace Toolkit
1104
1105 } // namespace Dali