Fix the warning log in Control causing the wrong position
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / page-turn-view / page-turn-view-impl.cpp
1 /*
2  * Copyright (c) 2016 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/devel-api/visuals/visual-properties-devel.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 pageSize.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: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
58 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
59
60 // when the x component of pan position reaches pageSize.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, "pageSize",        VECTOR2, 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.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
267   actor.SetParentOrigin( ParentOrigin::CENTER_LEFT );
268   actor.SetVisible( 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& pageSize )
344 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
345   mPageFactory( &pageFactory ),
346   mPageSize( pageSize ),
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.SetSize( mPageSize );
392     Self().Add( mPages[i].actor );
393   }
394
395   // create the layer for turning pages
396   mTurningPageLayer = Layer::New();
397   mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
398   mTurningPageLayer.SetBehavior(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.SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
412   }
413   mPages[0].actor.SetVisible(true);
414
415   // enable the pan gesture which is attached to the control
416   EnableGestureDetection(Gesture::Type(Gesture::Pan));
417 }
418
419 Shader PageTurnView::CreateShader( const Property::Map& shaderMap )
420 {
421   Shader shader;
422   Property::Value* shaderValue = shaderMap.Find( Toolkit::DevelVisual::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.GetCurrentParentOrigin();
452   mShadowView.SetParentOrigin( origin );
453   mShadowView.SetAnchorPoint( origin );
454   mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
455   mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
456
457   mShadowPlaneBackground = Actor::New();
458   mShadowPlaneBackground.SetParentOrigin( ParentOrigin::CENTER );
459   mShadowPlaneBackground.SetSize( mControlSize );
460   Self().Add( mShadowPlaneBackground );
461   mShadowView.SetShadowPlaneBackground( mShadowPlaneBackground );
462
463   mPointLight = Actor::New();
464   mPointLight.SetAnchorPoint( origin );
465   mPointLight.SetParentOrigin( origin );
466   mPointLight.SetPosition( 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::OnStageConnection( int depth )
475 {
476   Control::OnStageConnection( depth );
477
478   SetupShadowView();
479 }
480
481 void PageTurnView::OnStageDisconnection()
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::OnStageDisconnection();
495 }
496
497 void PageTurnView::SetPageSize( const Vector2& pageSize )
498 {
499   mPageSize = pageSize;
500
501   if( mPointLight )
502   {
503     mPointLight.SetPosition( 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.SetSize( mPageSize );
509   }
510
511   OnPageTurnViewInitialize();
512
513   if( mShadowPlaneBackground )
514   {
515     mShadowPlaneBackground.SetSize( 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.SetVisible(true);
561   if( pageId > 0 )
562   {
563     mPages[(pageId-1)%NUMBER_OF_CACHED_PAGES].actor.SetVisible(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.SetOrientation( Degree( degree ), Vector3::YAXIS );
593     mPages[index].actor.SetVisible( 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.SetVisible(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.state )
616   {
617     case Gesture::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         SetPanActor( gesture.position ); // determine which page actor is panned
623         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
624         {
625           mTurningPageIndex = -1;
626         }
627         PanStarted( SetPanPosition( gesture.position ) );  // pass in the pan position in the local page coordinate
628       }
629       else
630       {
631         mTurningPageIndex = -1;
632       }
633       break;
634     }
635     case Gesture::Continuing:
636     {
637       PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
638       break;
639     }
640     case Gesture::Finished:
641     case Gesture::Cancelled:
642     {
643       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
644       break;
645     }
646     case Gesture::Clear:
647     case Gesture::Possible:
648     default:
649     {
650       break;
651     }
652   }
653 }
654
655 void PageTurnView::PanStarted( const Vector2& gesturePosition )
656 {
657   mPressDownPosition = gesturePosition;
658
659   if( mTurningPageIndex == -1 )
660   {
661     return;
662   }
663
664   mIndex = mTurningPageIndex % NUMBER_OF_CACHED_PAGES;
665
666   mOriginalCenter = gesturePosition;
667   mPress = false;
668   mPageUpdated = false;
669
670   // Guard against destruction during signal emission
671   Toolkit::PageTurnView handle( GetOwner() );
672   mPagePanStartedSignal.Emit( handle );
673 }
674
675 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
676 {
677   if( mTurningPageIndex == -1  )
678   {
679     return;
680   }
681
682   // Guard against destruction during signal emission
683   Toolkit::PageTurnView handle( GetOwner() );
684
685   if(!mPress)
686   {
687     // when the touch down position is near the spine
688     // or when the panning goes outwards or some other position which would tear the paper in real situation
689     // we change the start position into the current panning position and update the shader parameters
690     if( mOriginalCenter.x <  mPageSize.width*MINIMUM_START_POSITION_RATIO
691         || gesturePosition.x > mOriginalCenter.x-1.0f
692         || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
693              ( 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.SetVisible(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 )
785                    /( offset.x*offset.x + offset.y*offset.y );
786         offset *= k;
787         Actor self = Self();
788
789         Constraint originalCenterConstraint = Constraint::New<Vector2>( mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter, OriginalCenterConstraint( mOriginalCenter, offset ));
790         originalCenterConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement ) );
791         originalCenterConstraint.Apply();
792
793         Constraint currentCenterConstraint = Constraint::New<Vector2>( mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter, CurrentCenterConstraint(mPageSize.width));
794         currentCenterConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyPanCenter ) );
795         currentCenterConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter ) );
796         currentCenterConstraint.Apply();
797
798         PageTurnApplyInternalConstraint( mPages[mIndex].actor, mPageSize.height );
799
800         float distance = offset.Length();
801         Constraint rotationConstraint = Constraint::New<Quaternion>( mPages[mIndex].actor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mPages[mIndex].isTurnBack));
802         rotationConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement ) );
803         rotationConstraint.Apply();
804
805         mConstraints = true;
806       }
807     }
808     else
809     {
810       if(mConstraints) // remove the constraint is the pan position move back to far away from the spine
811       {
812         mPages[mIndex].actor.RemoveConstraints();
813         mPages[mIndex].SetOriginalCenter(mOriginalCenter );
814         mConstraints = false;
815         mPanDisplacement = 0.f;
816       }
817
818       mPages[mIndex].SetCurrentCenter( currentCenter );
819       mCurrentCenter = currentCenter;
820       PageTurnApplyInternalConstraint(mPages[mIndex].actor, mPageSize.height );
821     }
822   }
823 }
824
825 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
826 {
827   // Guard against destruction during signal emission
828   Toolkit::PageTurnView handle( GetOwner() );
829
830   if( mTurningPageIndex == -1  )
831   {
832     if( mAnimatingCount< MAXIMUM_TURNING_NUM && mSlidingCount < 1)
833     {
834       OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
835     }
836
837     return;
838   }
839
840   mPagePanFinishedSignal.Emit( handle );
841
842   if(mPress)
843   {
844     if(mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
845     {
846       // update the pages here instead of in the TurnedOver callback function
847       // as new page is allowed to respond to the pan gesture before other pages finishing animation
848       if(mPages[mIndex].isTurnBack)
849       {
850         mCurrentPageIndex--;
851         RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
852         AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
853       }
854       else
855       {
856         mCurrentPageIndex++;
857         RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
858         AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
859       }
860       OrganizePageDepth();
861
862       // set up an animation to turn the page over
863       float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
864       Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
865       animation.AnimateTo( Property(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement),
866                            width,AlphaFunction::EASE_OUT_SINE);
867       animation.AnimateTo( Property(mPages[mIndex].actor, mPages[mIndex].propertyPanCenter),
868                            Vector2(-mPageSize.width*1.1f, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
869       mAnimationPageIdPair[animation] = mTurningPageIndex;
870       animation.Play();
871       animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
872     }
873     else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
874     {
875       Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
876       animation.AnimateTo( Property( mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter ),
877                            mOriginalCenter, 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.SetOrientation( 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.SetVisible(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.SetVisible(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.SetZ( -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.SetZ( -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.SetOrientation( 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::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::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