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