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