df54bd6aac158c16b91e7f7ddb1a6195cf083251
[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::make_unique<DevelControl::ControlAccessible>(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