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