[AT-SPI] Remove SetAccessibilityConstructor()
[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   Self().SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::PAGE_TAB_LIST);
418 }
419
420 Shader PageTurnView::CreateShader(const Property::Map& shaderMap)
421 {
422   Shader           shader;
423   Property::Value* shaderValue = shaderMap.Find(Toolkit::Visual::Property::SHADER, CUSTOM_SHADER);
424   Property::Map    shaderSource;
425   if(shaderValue && shaderValue->Get(shaderSource))
426   {
427     std::string      vertexShader;
428     Property::Value* vertexShaderValue = shaderSource.Find(Toolkit::Visual::Shader::Property::VERTEX_SHADER, CUSTOM_VERTEX_SHADER);
429     if(!vertexShaderValue || !vertexShaderValue->Get(vertexShader))
430     {
431       DALI_LOG_ERROR("PageTurnView::CreateShader failed: vertex shader source is not available.\n");
432     }
433     std::string      fragmentShader;
434     Property::Value* fragmentShaderValue = shaderSource.Find(Toolkit::Visual::Shader::Property::FRAGMENT_SHADER, CUSTOM_FRAGMENT_SHADER);
435     if(!fragmentShaderValue || !fragmentShaderValue->Get(fragmentShader))
436     {
437       DALI_LOG_ERROR("PageTurnView::CreateShader failed: fragment shader source is not available.\n");
438     }
439     shader = Shader::New(vertexShader, fragmentShader);
440   }
441   else
442   {
443     DALI_LOG_ERROR("PageTurnView::CreateShader failed: shader source is not available.\n");
444   }
445
446   return shader;
447 }
448
449 void PageTurnView::SetupShadowView()
450 {
451   mShadowView    = Toolkit::ShadowView::New(0.25f, 0.25f);
452   Vector3 origin = mTurningPageLayer.GetCurrentProperty<Vector3>(Actor::Property::PARENT_ORIGIN);
453   mShadowView.SetProperty(Actor::Property::PARENT_ORIGIN, origin);
454   mShadowView.SetProperty(Actor::Property::ANCHOR_POINT, origin);
455   mShadowView.SetPointLightFieldOfView(Math::PI / 2.0f);
456   mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
457
458   mShadowPlaneBackground = Actor::New();
459   mShadowPlaneBackground.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
460   mShadowPlaneBackground.SetProperty(Actor::Property::SIZE, mControlSize);
461   Self().Add(mShadowPlaneBackground);
462   mShadowView.SetShadowPlaneBackground(mShadowPlaneBackground);
463
464   mPointLight = Actor::New();
465   mPointLight.SetProperty(Actor::Property::ANCHOR_POINT, origin);
466   mPointLight.SetProperty(Actor::Property::PARENT_ORIGIN, origin);
467   mPointLight.SetProperty(Actor::Property::POSITION, Vector3(0.f, 0.f, mPageSize.width * POINT_LIGHT_HEIGHT_RATIO));
468   Self().Add(mPointLight);
469   mShadowView.SetPointLight(mPointLight);
470
471   mTurningPageLayer.Add(mShadowView);
472   mShadowView.Activate();
473 }
474
475 void PageTurnView::OnSceneConnection(int depth)
476 {
477   SetupShadowView();
478
479   Control::OnSceneConnection(depth);
480 }
481
482 void PageTurnView::OnSceneDisconnection()
483 {
484   if(mShadowView)
485   {
486     mShadowView.RemoveConstraints();
487     mPointLight.Unparent();
488     mShadowPlaneBackground.Unparent();
489     mShadowView.Unparent();
490   }
491
492   // make sure the status of the control is updated correctly when the pan gesture is interrupted
493   StopTurning();
494
495   Control::OnSceneDisconnection();
496 }
497
498 void PageTurnView::SetPageSize(const Vector2& viewPageSize)
499 {
500   mPageSize = viewPageSize;
501
502   if(mPointLight)
503   {
504     mPointLight.SetProperty(Actor::Property::POSITION, Vector3(0.f, 0.f, mPageSize.width * POINT_LIGHT_HEIGHT_RATIO));
505   }
506
507   for(size_t i = 0; i < mPages.size(); i++)
508   {
509     mPages[i].actor.SetProperty(Actor::Property::SIZE, mPageSize);
510   }
511
512   OnPageTurnViewInitialize();
513
514   if(mShadowPlaneBackground)
515   {
516     mShadowPlaneBackground.SetProperty(Actor::Property::SIZE, mControlSize);
517   }
518 }
519
520 Vector2 PageTurnView::GetPageSize()
521 {
522   return mPageSize;
523 }
524
525 void PageTurnView::SetSpineShadowParameter(const Vector2& spineShadowParameter)
526 {
527   mSpineShadowParameter = spineShadowParameter;
528
529   // set spine shadow parameter to all the shader effects
530   mSpineEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter);
531   mTurnEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter);
532 }
533
534 Vector2 PageTurnView::GetSpineShadowParameter()
535 {
536   return mSpineShadowParameter;
537 }
538
539 void PageTurnView::GoToPage(unsigned int pageId)
540 {
541   int pageIdx = Clamp(static_cast<int>(pageId), 0, mTotalPageCount - 1);
542
543   if(mCurrentPageIndex == pageIdx)
544   {
545     return;
546   }
547
548   // if any animation ongoing, stop it.
549   StopTurning();
550
551   // record the new current page index
552   mCurrentPageIndex = pageIdx;
553
554   // add the current page and the pages right before and after it
555   for(int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++)
556   {
557     AddPage(i);
558   }
559
560   mPages[pageId % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::VISIBLE, true);
561   if(pageId > 0)
562   {
563     mPages[(pageId - 1) % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::VISIBLE, true);
564   }
565   // set ordered depth to the stacked pages
566   OrganizePageDepth();
567 }
568
569 unsigned int PageTurnView::GetCurrentPage()
570 {
571   DALI_ASSERT_ALWAYS(mCurrentPageIndex >= 0);
572   return static_cast<unsigned int>(mCurrentPageIndex);
573 }
574
575 void PageTurnView::AddPage(int pageIndex)
576 {
577   if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
578   {
579     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
580
581     Texture newPage;
582     newPage = mPageFactory->NewPage(pageIndex);
583     DALI_ASSERT_ALWAYS(newPage && "must pass in valid texture");
584
585     bool isLeftSide = (pageIndex < mCurrentPageIndex);
586     if(mPages[index].isTurnBack != isLeftSide)
587     {
588       mPages[index].ChangeTurnDirection();
589     }
590
591     float degree = isLeftSide ? 180.f : 0.f;
592     mPages[index].actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(Degree(degree), Vector3::YAXIS));
593     mPages[index].actor.SetProperty(Actor::Property::VISIBLE, false);
594     mPages[index].UseEffect(mSpineEffectShader, mGeometry);
595     mPages[index].SetTexture(newPage);
596
597     // For Portrait, nothing to do
598     // For Landscape, set the parent origin to CENTER
599     OnAddPage(mPages[index].actor, isLeftSide);
600   }
601 }
602
603 void PageTurnView::RemovePage(int pageIndex)
604 {
605   if(pageIndex > -1 && pageIndex < mTotalPageCount)
606   {
607     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
608     mPages[index].actor.SetProperty(Actor::Property::VISIBLE, false);
609   }
610 }
611
612 void PageTurnView::OnPan(const PanGesture& gesture)
613 {
614   // the pan gesture is attached to control itself instead of each page
615   switch(gesture.GetState())
616   {
617     case GestureState::STARTED:
618     {
619       // check whether the undergoing turning page number already reaches the maximum allowed
620       if(mPageUpdated && mAnimatingCount < MAXIMUM_TURNING_NUM && mSlidingCount < 1)
621       {
622         const Vector2& position = gesture.GetPosition();
623         SetPanActor(position);                                                                                        // determine which page actor is panned
624         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
625         {
626           mTurningPageIndex = -1;
627         }
628         PanStarted(SetPanPosition(position)); // pass in the pan position in the local page coordinate
629       }
630       else
631       {
632         mTurningPageIndex = -1;
633       }
634       break;
635     }
636     case GestureState::CONTINUING:
637     {
638       PanContinuing(SetPanPosition(gesture.GetPosition())); // pass in the pan position in the local page coordinate
639       break;
640     }
641     case GestureState::FINISHED:
642     case GestureState::CANCELLED:
643     {
644       PanFinished(SetPanPosition(gesture.GetPosition()), gesture.GetSpeed());
645       break;
646     }
647     case GestureState::CLEAR:
648     case GestureState::POSSIBLE:
649     default:
650     {
651       break;
652     }
653   }
654 }
655
656 void PageTurnView::PanStarted(const Vector2& gesturePosition)
657 {
658   mPressDownPosition = gesturePosition;
659
660   if(mTurningPageIndex == -1)
661   {
662     return;
663   }
664
665   mIndex = mTurningPageIndex % NUMBER_OF_CACHED_PAGES;
666
667   mOriginalCenter = gesturePosition;
668   mPress          = false;
669   mPageUpdated    = false;
670
671   // Guard against destruction during signal emission
672   Toolkit::PageTurnView handle(GetOwner());
673   mPagePanStartedSignal.Emit(handle);
674 }
675
676 void PageTurnView::PanContinuing(const Vector2& gesturePosition)
677 {
678   if(mTurningPageIndex == -1)
679   {
680     return;
681   }
682
683   // Guard against destruction during signal emission
684   Toolkit::PageTurnView handle(GetOwner());
685
686   if(!mPress)
687   {
688     // when the touch down position is near the spine
689     // or when the panning goes outwards or some other position which would tear the paper in real situation
690     // we change the start position into the current panning position and update the shader parameters
691     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))))
692     {
693       mOriginalCenter = gesturePosition;
694     }
695     else
696     {
697       mDistanceUpCorner     = mOriginalCenter.Length();
698       mDistanceBottomCorner = (mOriginalCenter - Vector2(0.0f, mPageSize.height)).Length();
699       mShadowView.Add(mPages[mIndex].actor);
700       mPages[mIndex].UseEffect(mTurnEffectShader);
701       mPages[mIndex].SetOriginalCenter(mOriginalCenter);
702       mCurrentCenter = mOriginalCenter;
703       mPages[mIndex].SetCurrentCenter(mCurrentCenter);
704       mPanDisplacement = 0.f;
705       mConstraints     = false;
706       mPress           = true;
707       mAnimatingCount++;
708
709       mPageTurnStartedSignal.Emit(handle, static_cast<unsigned int>(mTurningPageIndex), !mPages[mIndex].isTurnBack);
710       int id = mTurningPageIndex + (mPages[mIndex].isTurnBack ? -1 : 1);
711       if(id >= 0 && id < mTotalPageCount)
712       {
713         mPages[id % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::VISIBLE, true);
714       }
715
716       mShadowView.RemoveConstraints();
717       Actor self = Self();
718       mPages[mIndex].SetPanDisplacement(0.f);
719
720       Constraint shadowBlurStrengthConstraint = Constraint::New<float>(mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint(mPageSize.width * PAGE_TURN_OVER_THRESHOLD_RATIO));
721       shadowBlurStrengthConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter));
722       shadowBlurStrengthConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter));
723       shadowBlurStrengthConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement));
724       shadowBlurStrengthConstraint.Apply();
725     }
726   }
727   else
728   {
729     Vector2 currentCenter = gesturePosition;
730
731     // Test whether the new current center would tear the paper from the top pine in real situation
732     // we do not forbid this totally, which would restrict the panning gesture too much
733     // instead, set it to the nearest allowable position
734     float distanceUpCorner     = currentCenter.Length();
735     float distanceBottomCorner = (currentCenter - Vector2(0.0f, mPageSize.height)).Length();
736     if(distanceUpCorner > mDistanceUpCorner)
737     {
738       currentCenter = currentCenter * mDistanceUpCorner / distanceUpCorner;
739     }
740     // would tear the paper from the bottom spine in real situation
741     if(distanceBottomCorner > mDistanceBottomCorner)
742     {
743       currentCenter = ((currentCenter - Vector2(0.0f, mPageSize.height)) * mDistanceBottomCorner / distanceBottomCorner + Vector2(0.0f, mPageSize.height));
744     }
745     // If direction has a very high y component, reduce it.
746     Vector2 curveDirection = currentCenter - mOriginalCenter;
747     if(fabs(curveDirection.y) > fabs(curveDirection.x))
748     {
749       currentCenter.y = mOriginalCenter.y + (currentCenter.y - mOriginalCenter.y) * fabs(curveDirection.x / curveDirection.y);
750     }
751     // If the vertical distance is high, reduce it
752     float yShift = currentCenter.y - mOriginalCenter.y;
753     if(fabs(yShift) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO)
754     {
755       currentCenter.y = mOriginalCenter.y + yShift * mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO / fabs(yShift);
756     }
757
758     // use contraints to control the page shape and rotation when the pan position is near the spine
759     if(currentCenter.x <= mPageSize.width * PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width * PAGE_TURN_OVER_THRESHOLD_RATIO)
760     {
761       // set the property values used by the constraints
762       mPanDisplacement = mPageSize.width * PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
763       mPages[mIndex].SetPanDisplacement(mPanDisplacement);
764       mPages[mIndex].SetPanCenter(currentCenter);
765
766       // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
767       // also set up the RotationConstraint to the page actor
768       if(!mConstraints)
769       {
770         Vector2 corner;
771         // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
772         if(currentCenter.y >= mOriginalCenter.y)
773         {
774           corner = Vector2(1.1f * mPageSize.width, 0.f);
775         }
776         else
777         {
778           corner = mPageSize * 1.1f;
779         }
780
781         Vector2 offset(currentCenter - mOriginalCenter);
782         float   k = -((mOriginalCenter.x - corner.x) * offset.x + (mOriginalCenter.y - corner.y) * offset.y) / (offset.x * offset.x + offset.y * offset.y);
783         offset *= k;
784         Actor self = Self();
785
786         Constraint originalCenterConstraint = Constraint::New<Vector2>(mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter, OriginalCenterConstraint(mOriginalCenter, offset));
787         originalCenterConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement));
788         originalCenterConstraint.Apply();
789
790         Constraint currentCenterConstraint = Constraint::New<Vector2>(mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter, CurrentCenterConstraint(mPageSize.width));
791         currentCenterConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyPanCenter));
792         currentCenterConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter));
793         currentCenterConstraint.Apply();
794
795         PageTurnApplyInternalConstraint(mPages[mIndex].actor, mPageSize.height);
796
797         float      distance           = offset.Length();
798         Constraint rotationConstraint = Constraint::New<Quaternion>(mPages[mIndex].actor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mPages[mIndex].isTurnBack));
799         rotationConstraint.AddSource(Source(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement));
800         rotationConstraint.Apply();
801
802         mConstraints = true;
803       }
804     }
805     else
806     {
807       if(mConstraints) // remove the constraint is the pan position move back to far away from the spine
808       {
809         mPages[mIndex].actor.RemoveConstraints();
810         mPages[mIndex].SetOriginalCenter(mOriginalCenter);
811         mConstraints     = false;
812         mPanDisplacement = 0.f;
813       }
814
815       mPages[mIndex].SetCurrentCenter(currentCenter);
816       mCurrentCenter = currentCenter;
817       PageTurnApplyInternalConstraint(mPages[mIndex].actor, mPageSize.height);
818     }
819   }
820 }
821
822 void PageTurnView::PanFinished(const Vector2& gesturePosition, float gestureSpeed)
823 {
824   // Guard against destruction during signal emission
825   Toolkit::PageTurnView handle(GetOwner());
826
827   if(mTurningPageIndex == -1)
828   {
829     if(mAnimatingCount < MAXIMUM_TURNING_NUM && mSlidingCount < 1)
830     {
831       OnPossibleOutwardsFlick(gesturePosition, gestureSpeed);
832     }
833
834     return;
835   }
836
837   mPagePanFinishedSignal.Emit(handle);
838
839   if(mPress)
840   {
841     if(mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
842     {
843       // update the pages here instead of in the TurnedOver callback function
844       // as new page is allowed to respond to the pan gesture before other pages finishing animation
845       if(mPages[mIndex].isTurnBack)
846       {
847         mCurrentPageIndex--;
848         RemovePage(mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE);
849         AddPage(mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE);
850       }
851       else
852       {
853         mCurrentPageIndex++;
854         RemovePage(mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE - 1);
855         AddPage(mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE - 1);
856       }
857       OrganizePageDepth();
858
859       // set up an animation to turn the page over
860       float     width     = mPageSize.width * (1.f + PAGE_TURN_OVER_THRESHOLD_RATIO);
861       Animation animation = Animation::New(std::max(0.1f, PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)));
862       animation.AnimateTo(Property(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement),
863                           width,
864                           AlphaFunction::EASE_OUT_SINE);
865       animation.AnimateTo(Property(mPages[mIndex].actor, mPages[mIndex].propertyPanCenter),
866                           Vector2(-mPageSize.width * 1.1f, 0.5f * mPageSize.height),
867                           AlphaFunction::EASE_OUT_SINE);
868       mAnimationPageIdPair[animation] = mTurningPageIndex;
869       animation.Play();
870       animation.FinishedSignal().Connect(this, &PageTurnView::TurnedOver);
871     }
872     else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
873     {
874       Animation animation = Animation::New(PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO);
875       animation.AnimateTo(Property(mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter),
876                           mOriginalCenter,
877                           AlphaFunction::LINEAR);
878       mAnimationPageIdPair[animation] = mTurningPageIndex;
879       animation.Play();
880       mSlidingCount++;
881       animation.FinishedSignal().Connect(this, &PageTurnView::SliddenBack);
882
883       mPageTurnStartedSignal.Emit(handle, static_cast<unsigned int>(mTurningPageIndex), mPages[mIndex].isTurnBack);
884     }
885   }
886   else
887   {
888     // In portrait view, an outwards flick should turn the previous page back
889     // In landscape view, nothing to do
890     OnPossibleOutwardsFlick(gesturePosition, gestureSpeed);
891   }
892   mPageUpdated = true;
893 }
894
895 void PageTurnView::TurnedOver(Animation& animation)
896 {
897   int pageId = mAnimationPageIdPair[animation];
898   int index  = pageId % NUMBER_OF_CACHED_PAGES;
899
900   mPages[index].ChangeTurnDirection();
901   mPages[index].actor.RemoveConstraints();
902   Self().Add(mPages[index].actor);
903   mAnimatingCount--;
904   mAnimationPageIdPair.erase(animation);
905
906   float degree = mPages[index].isTurnBack ? 180.f : 0.f;
907   mPages[index].actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(Degree(degree), Vector3::YAXIS));
908   mPages[index].UseEffect(mSpineEffectShader);
909
910   int id = pageId + (mPages[index].isTurnBack ? -1 : 1);
911   if(id >= 0 && id < mTotalPageCount)
912   {
913     mPages[id % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::VISIBLE, false);
914   }
915
916   OnTurnedOver(mPages[index].actor, mPages[index].isTurnBack);
917
918   // Guard against destruction during signal emission
919   Toolkit::PageTurnView handle(GetOwner());
920   mPageTurnFinishedSignal.Emit(handle, static_cast<unsigned int>(pageId), mPages[index].isTurnBack);
921 }
922
923 void PageTurnView::SliddenBack(Animation& animation)
924 {
925   int pageId = mAnimationPageIdPair[animation];
926   int index  = pageId % NUMBER_OF_CACHED_PAGES;
927   Self().Add(mPages[index].actor);
928   mSlidingCount--;
929   mAnimatingCount--;
930   mAnimationPageIdPair.erase(animation);
931
932   mPages[index].UseEffect(mSpineEffectShader);
933
934   int id = pageId + (mPages[index].isTurnBack ? -1 : 1);
935   if(id >= 0 && id < mTotalPageCount)
936   {
937     mPages[id % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::VISIBLE, false);
938   }
939
940   // Guard against destruction during signal emission
941   Toolkit::PageTurnView handle(GetOwner());
942   mPageTurnFinishedSignal.Emit(handle, static_cast<unsigned int>(pageId), mPages[index].isTurnBack);
943 }
944
945 void PageTurnView::OrganizePageDepth()
946 {
947   for(int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++)
948   {
949     if(mCurrentPageIndex + i < mTotalPageCount)
950     {
951       mPages[(mCurrentPageIndex + i) % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::POSITION_Z, -static_cast<float>(i) * STATIC_PAGE_INTERVAL_DISTANCE);
952     }
953     if(mCurrentPageIndex >= i + 1)
954     {
955       mPages[(mCurrentPageIndex - i - 1) % NUMBER_OF_CACHED_PAGES].actor.SetProperty(Actor::Property::POSITION_Z, -static_cast<float>(i) * STATIC_PAGE_INTERVAL_DISTANCE);
956     }
957   }
958 }
959
960 void PageTurnView::StopTurning()
961 {
962   mAnimatingCount = 0;
963   mSlidingCount   = 0;
964
965   if(!mPageUpdated)
966   {
967     int index = mTurningPageIndex % NUMBER_OF_CACHED_PAGES;
968     Self().Add(mPages[index].actor);
969     mPages[index].actor.RemoveConstraints();
970     mPages[index].UseEffect(mSpineEffectShader);
971     float degree = mTurningPageIndex == mCurrentPageIndex ? 0.f : 180.f;
972     mPages[index].actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(Degree(degree), Vector3::YAXIS));
973     mPageUpdated = true;
974   }
975
976   if(!mAnimationPageIdPair.empty())
977   {
978     for(std::map<Animation, int>::iterator it = mAnimationPageIdPair.begin(); it != mAnimationPageIdPair.end(); ++it)
979     {
980       static_cast<Animation>(it->first).SetCurrentProgress(1.f);
981     }
982   }
983 }
984
985 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
986 {
987   return mPageTurnStartedSignal;
988 }
989
990 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
991 {
992   return mPageTurnFinishedSignal;
993 }
994
995 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
996 {
997   return mPagePanStartedSignal;
998 }
999
1000 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1001 {
1002   return mPagePanFinishedSignal;
1003 }
1004
1005 bool PageTurnView::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
1006 {
1007   Dali::BaseHandle handle(object);
1008
1009   bool                  connected(true);
1010   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast(handle);
1011
1012   if(0 == strcmp(signalName.c_str(), SIGNAL_PAGE_TURN_STARTED))
1013   {
1014     pageTurnView.PageTurnStartedSignal().Connect(tracker, functor);
1015   }
1016   else if(0 == strcmp(signalName.c_str(), SIGNAL_PAGE_TURN_FINISHED))
1017   {
1018     pageTurnView.PageTurnFinishedSignal().Connect(tracker, functor);
1019   }
1020   else if(0 == strcmp(signalName.c_str(), SIGNAL_PAGE_PAN_STARTED))
1021   {
1022     pageTurnView.PagePanStartedSignal().Connect(tracker, functor);
1023   }
1024   else if(0 == strcmp(signalName.c_str(), SIGNAL_PAGE_PAN_FINISHED))
1025   {
1026     pageTurnView.PagePanFinishedSignal().Connect(tracker, functor);
1027   }
1028   else
1029   {
1030     // signalName does not match any signal
1031     connected = false;
1032   }
1033
1034   return connected;
1035 }
1036
1037 void PageTurnView::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
1038 {
1039   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast(Dali::BaseHandle(object));
1040
1041   if(pageTurnView)
1042   {
1043     PageTurnView& pageTurnViewImpl(GetImplementation(pageTurnView));
1044
1045     switch(index)
1046     {
1047       case Toolkit::PageTurnView::Property::VIEW_PAGE_SIZE:
1048       {
1049         pageTurnViewImpl.SetPageSize(value.Get<Vector2>());
1050         break;
1051       }
1052       case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1053       {
1054         pageTurnViewImpl.GoToPage(value.Get<int>());
1055         break;
1056       }
1057       case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1058       {
1059         pageTurnViewImpl.SetSpineShadowParameter(value.Get<Vector2>());
1060         break;
1061       }
1062     }
1063   }
1064 }
1065
1066 Property::Value PageTurnView::GetProperty(BaseObject* object, Property::Index index)
1067 {
1068   Property::Value value;
1069
1070   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast(Dali::BaseHandle(object));
1071
1072   if(pageTurnView)
1073   {
1074     PageTurnView& pageTurnViewImpl(GetImplementation(pageTurnView));
1075
1076     switch(index)
1077     {
1078       case Toolkit::PageTurnView::Property::VIEW_PAGE_SIZE:
1079       {
1080         value = pageTurnViewImpl.GetPageSize();
1081         break;
1082       }
1083       case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1084       {
1085         value = static_cast<int>(pageTurnViewImpl.GetCurrentPage());
1086         break;
1087       }
1088       case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1089       {
1090         value = pageTurnViewImpl.GetSpineShadowParameter();
1091         break;
1092       }
1093     }
1094   }
1095   return value;
1096 }
1097
1098 } // namespace Internal
1099
1100 } // namespace Toolkit
1101
1102 } // namespace Dali