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