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