Scrollable public API clean-up phase 1
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / page-turn-view / page-turn-view-impl.cpp
1 /*
2  * Copyright (c) 2014 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 <dali/public-api/animation/animation.h>
23 #include <dali/public-api/events/hit-test-algorithm.h>
24 #include <dali/public-api/object/type-registry.h>
25 #include <dali/public-api/render-tasks/render-task-list.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
29
30 using namespace Dali;
31
32 namespace //unnamed namespace
33 {
34 // To register type
35 TypeRegistration mType( typeid(Toolkit::PageTurnView), typeid(Toolkit::Control), NULL );
36
37 // default grid density for page turn effect, 10 pixels by 10 pixels
38 const float DEFAULT_GRID_DENSITY(10.0f);
39
40 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
41 const float MINIMUM_START_POSITION_RATIO(0.6f);
42
43 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
44 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
45
46 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
47 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
48
49 // duration of animation, shorter for faster speed
50 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
51 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
52
53 // the major&minor radius (in pixels) to form an ellipse shape
54 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
55 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
56
57 // constants for shadow casting
58 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
59 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
60
61 // constraints ////////////////////////////////////////////////////////////////
62 /**
63  * Original Center Constraint
64  *
65  * This constraint adjusts the original center property of the page turn shader effect
66  * based on the X-direction displacement of the pan gesture
67  */
68 struct OriginalCenterConstraint
69 {
70   OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
71   : mOldCenter( originalCenter )
72   {
73     mNewCenter = originalCenter + offset;
74     mDistance = offset.Length() * 0.5f;
75     mDirection = offset  / mDistance;
76   }
77
78   Vector2 operator()(const Vector2& current, const PropertyInput& panDisplacement)
79   {
80     float displacement = panDisplacement.GetFloat();
81
82     if( displacement < mDistance )
83     {
84       return mOldCenter + mDirection * displacement;
85     }
86     else
87     {
88       return mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
89     }
90   }
91
92   Vector2 mOldCenter;
93   Vector2 mNewCenter;
94   float mDistance;
95   Vector2 mDirection;
96 };
97
98 /**
99  * Rotation Constraint
100  *
101  * This constraint adjusts the rotation property of the page actor
102  * based on the X-direction displacement of the pan gesture
103  */
104 struct RotationConstraint
105 {
106   RotationConstraint( float distance, float pageWidth, bool isTurnBack )
107   : mDistance( distance*0.5f )
108   {
109     mStep = 1.f / pageWidth;
110     mSign = isTurnBack ? -1.0f : 1.0f;
111     mConst = isTurnBack ? -1.0f : 0.f;
112     mRotation = isTurnBack ? Quaternion( -Math::PI, Vector3::YAXIS ) : Quaternion( 0.f, Vector3::YAXIS );
113   }
114
115   Quaternion operator()( const Quaternion& current, const PropertyInput& panDisplacement )
116   {
117     float displacement = panDisplacement.GetFloat();
118     float angle;
119     if( displacement < mDistance)
120     {
121       return mRotation;
122     }
123     else
124     {
125       float coef = std::max(-1.0f, mStep*(mDistance-displacement));
126       angle = Math::PI*( mConst + mSign*coef );
127       return Quaternion( angle, Vector3::YAXIS );
128     }
129   }
130
131   float mDistance;
132   float mStep;
133   float mConst;
134   float mSign;
135   Quaternion mRotation;
136 };
137
138 /**
139  * Current Center Constraint
140  *
141  * This constraint adjusts the current center property of the page turn shader effect
142  * based on the pan position and the original center position
143  */
144 struct CurrentCenterConstraint
145 {
146   CurrentCenterConstraint( float pageWidth)
147   : mPageWidth( pageWidth )
148   {
149     mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
150   }
151
152   Vector2 operator()( const Vector2& current, const PropertyInput& center, const PropertyInput& originalCenter )
153   {
154     Vector2 centerPosition = center.GetVector2();
155     if( centerPosition.x > 0.f )
156     {
157       return Vector2( mThres+centerPosition.x*0.5f , centerPosition.y);
158     }
159     else
160     {
161       Vector2 centerOrigin = originalCenter.GetVector2();
162       Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
163       float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
164       // Todo: when coef <= 0, the page is flat, slow down the last moment of the page stretch by 10 times to avoid a small bounce
165       if(coef < 0.025f)
166       {
167         coef = (coef+0.225f)/10.0f;
168       }
169       return centerOrigin - direction * coef;
170     }
171   }
172
173   float mPageWidth;
174   float mThres;
175 };
176
177 struct ShadowBlurStrengthConstraint
178 {
179   ShadowBlurStrengthConstraint( float thres )
180   : mThres( thres )
181   {}
182
183   float operator()( const float current,  const PropertyInput& currentCenter, const PropertyInput& originalCenter, const PropertyInput& panDisplacement)
184   {
185     float displacement = panDisplacement.GetFloat();
186     float blurStrength;
187     if( EqualsZero(displacement))
188     {
189       Vector2 cur = currentCenter.GetVector2();
190       Vector2 ori = originalCenter.GetVector2();
191       blurStrength =  5.f*(ori-cur).Length() / mThres;
192     }
193     else
194     {
195       blurStrength =  1.f - (displacement-2.f*mThres)/mThres;
196     }
197
198     blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
199     return blurStrength;
200   }
201
202   float mThres;
203 };
204
205 bool IsActorHittableFunction( Actor actor, Dali::HitTestAlgorithm::TraverseType type )
206 {
207   bool hittable = false;
208
209   switch (type)
210   {
211     case Dali::HitTestAlgorithm::CHECK_ACTOR:
212     {
213       // Check whether the actor is visible and not fully transparent.
214       Property::Index propertyActorHittable = actor.GetPropertyIndex(Toolkit::PageFactory::ACTOR_HITTABLE);
215       if( actor.IsSensitive()
216        && actor.IsVisible()
217        && actor.GetCurrentWorldColor().a > 0.01f// not FULLY_TRANSPARENT
218        && ( propertyActorHittable != Property::INVALID_INDEX &&
219             actor.GetProperty<bool>( propertyActorHittable ) ) )
220       {
221          hittable = true;
222       }
223       break;
224     }
225     case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
226     {
227       if( actor.IsSensitive() && actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible.
228       {
229         hittable = true;
230       }
231       break;
232     }
233     default:
234     {
235       break;
236     }
237   }
238
239   return hittable;
240 }
241
242 } //unnamed namespace
243
244 namespace Dali
245 {
246
247 namespace Toolkit
248 {
249
250 namespace Internal
251 {
252
253 // these several constants are also used in the derived classes
254 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
255 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
256 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
257 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
258
259 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
260 : Control( REQUIRES_TOUCH_EVENTS ),
261   mPageFactory( pageFactory ),
262   mPageSize( pageSize ),
263   mTotalPageCount( 0 ),
264   mIsEditMode( false ),
265   mNeedOffscreenRendering( false ),
266   mPanning( false ),
267   mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
268   mCurrentPageIndex( 0 ),
269   mIndex( 0 ),
270   mPress( false ),
271   mPageUpdated( true ),
272   mDistanceUpCorner( 0.f ),
273   mDistanceBottomCorner( 0.f ),
274   mPanDisplacement( 0.f ),
275   mConstraints( false ),
276   mPageTurnStartedSignal(),
277   mPageTurnFinishedSignal(),
278   mPagePanStartedSignal(),
279   mPagePanFinishedSignal()
280 {
281   mPageActors.resize( NUMBER_OF_CACHED_PAGES );
282   mIsAnimating.resize( MAXIMUM_TURNING_NUM );
283   mIsSliding.resize( MAXIMUM_TURNING_NUM );
284   mTurnEffect.resize( MAXIMUM_TURNING_NUM );
285   mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
286   mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
287 }
288
289 PageTurnView::~PageTurnView()
290 {
291 }
292
293 void PageTurnView::OnInitialize()
294 {
295    // create the two book spine effect for static images, left and right side pages respectively
296   mSpineEffectFront = PageTurnBookSpineEffect::New();
297   mSpineEffectFront.SetIsBackImageVisible( false );
298   mSpineEffectFront.SetPageWidth( mPageSize.width );
299   mSpineEffectFront.SetShadowWidth( 0.f );
300   mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
301
302   mSpineEffectBack = PageTurnBookSpineEffect::New();
303   mSpineEffectBack.SetIsBackImageVisible( true );
304   mSpineEffectBack.SetPageWidth( mPageSize.width );
305   mSpineEffectBack.SetShadowWidth( 0.f );
306   mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
307
308   // create the page turn effect objects
309   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
310   {
311     mTurnEffect[i] = Toolkit::PageTurnEffect::New( false );
312     mTurnEffect[i].SetProperty( ShaderEffect::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
313     mTurnEffect[i].SetPageSize( mPageSize );
314     mTurnEffect[i].SetShadowWidth(0.f);
315     mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
316     mIsAnimating[i] = false;
317     mIsSliding[i] = false;
318     mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
319     mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
320   }
321
322   mTurningPageLayer = Layer::New();
323   mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
324   // Set control size and the parent origin of turningPageLayer
325   OnPageTurnViewInitialize();
326
327   mRootOnScreen = Actor::New();
328   mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION );
329   mRootOnScreen.SetSize( mControlSize );
330   Self().Add( mRootOnScreen );
331   mRootOnScreen.Add(mTurningPageLayer);
332
333   mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
334   mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded();
335   if( mNeedOffscreenRendering )
336   {
337     SetupRenderTasks();
338   }
339
340   // add pages to the scene, and set depth for the stacked pages
341   for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
342   {
343     AddPage( i );
344     if(mPageActors[i])
345     {
346       mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
347     }
348   }
349
350   // enable the pan gesture which is attached to the control
351   EnableGestureDetection(Gesture::Type(Gesture::Pan));
352
353   mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage);
354 }
355
356 void PageTurnView::SetupRenderTasks()
357 {
358   mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES );
359   mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES );
360   mRenderedPage.resize( NUMBER_OF_CACHED_PAGES );
361
362   mCameraActor = CameraActor::New(mControlSize);
363   mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
364   mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
365   mCameraActor.SetInheritScale( false );
366   Self().Add(mCameraActor);
367
368   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
369   for(int i=0; i<NUMBER_OF_CACHED_PAGES; i++)
370   {
371     mPageSourceActor[i] = Actor::New();
372     mPageSourceActor[i].SetParentOrigin(ParentOrigin::CENTER);
373     mPageSourceActor[i].SetColorMode( USE_OWN_COLOR );
374     mPageSourceActor[i].SetPositionInheritanceMode( DONT_INHERIT_POSITION );
375     mPageSourceActor[i].SetInheritScale( false );
376     Self().Add( mPageSourceActor[i] );
377     mPageSourceActor[i].SetSensitive( false );
378
379     mRenderedPage[i] = FrameBufferImage::New( mControlSize.width, mControlSize.height, Pixel::RGB8888, Image::UNUSED );
380     mOffscreenTask[i] = taskList.CreateTask();
381     mOffscreenTask[i].SetRefreshRate( RenderTask::REFRESH_ONCE );
382     mOffscreenTask[i].SetCameraActor(mCameraActor);
383     mOffscreenTask[i].SetSourceActor( mPageSourceActor[i] );
384     mOffscreenTask[i].SetExclusive(true);
385     mOffscreenTask[i].SetInputEnabled( false );
386     mOffscreenTask[i].SetClearEnabled( true );
387     mOffscreenTask[i].SetClearColor( Vector4(0.f,0.f,0.f,0.f) );
388     mOffscreenTask[i].SetTargetFrameBuffer( mRenderedPage[i] );
389     mOffscreenTask[i].SetScreenToFrameBufferMappingActor( Self() );
390    }
391 }
392
393 void PageTurnView::SetupShadowView()
394 {
395   mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
396   Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
397   mShadowView.SetParentOrigin( origin );
398   mShadowView.SetAnchorPoint( origin );
399   mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
400   mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
401
402   mShadowLayer = Layer::New();
403   mShadowLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
404   mRootOnScreen.Add(mShadowLayer);
405   mShadowLayer.Raise();
406
407   mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
408   mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
409   mShadowPlane.SetSize( mControlSize );
410   mShadowLayer.Add( mShadowPlane );
411   mShadowView.SetShadowPlane( mShadowPlane );
412
413   mPointLight = Actor::New();
414   mPointLight.SetAnchorPoint( origin );
415   mPointLight.SetParentOrigin( origin );
416   mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
417   mRootOnScreen.Add( mPointLight );
418   mShadowView.SetPointLight( mPointLight );
419
420   mTurningPageLayer.Add( mShadowView );
421   mShadowView.Activate();
422 }
423
424 void PageTurnView::OnControlStageConnection()
425 {
426   SetupShadowView();
427   mTurningPageLayer.RaiseToTop();
428 }
429
430 void PageTurnView::OnControlStageDisconnection()
431 {
432   if(mShadowView)
433   {
434     Self().Remove(mPointLight);
435     Self().Remove(mShadowLayer);
436     mTurningPageLayer.Remove( mShadowView );
437   }
438
439   // make sure the status of the control is updated correctly when the pan gesture is interrupted
440   if(mPanning)
441   {
442     mPanning = false;
443
444     mRootOnScreen.Add(mPanActor);
445     mIsAnimating[mIndex] = false;
446     mPanActor.RemoveConstraints();
447     mTurnEffect[mIndex].RemoveConstraints();
448     mPageUpdated = true;
449
450     SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
451   }
452 }
453
454 void PageTurnView::OnControlSizeSet( const Vector3& size )
455 {
456   // disable the SetSize of the control from the application
457   Self().SetSize( mControlSize );
458 }
459
460 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
461 {
462   mSpineShadowParameter = spineShadowParameter;
463
464   // set spine shadow parameter to all the shader effects
465   mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
466   mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
467   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
468   {
469     mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
470   }
471 }
472
473 Vector2 PageTurnView::GetSpineShadowParameter()
474 {
475   return mSpineShadowParameter;
476 }
477
478 void PageTurnView::GoToPage( unsigned int pageId )
479 {
480   int pageIdx = static_cast<int>(pageId);
481   // record the new current page index
482   mCurrentPageIndex = pageIdx;
483
484   // clear the old pages
485   for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
486   {
487     if( mPageActors[i] )
488     {
489       mPageActors[i].Unparent();
490       mPageActors[i].Reset();
491     }
492   }
493
494   // add the current page and the pages right before and after it
495   for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
496   {
497     AddPage( i );
498   }
499   // set ordered depth to the stacked pages
500   OrganizePageDepth();
501 }
502
503 unsigned int PageTurnView::GetCurrentPage()
504 {
505   DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
506   return static_cast< unsigned int >( mCurrentPageIndex );
507 }
508
509 Actor PageTurnView::EnterEditMode()
510 {
511   if( mNeedOffscreenRendering )
512   {
513     DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
514
515     mIsEditMode = true;
516
517     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
518     mOffscreenTask[index].SetInputEnabled( true );
519     mPageSourceActor[index].SetSensitive( true );
520     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
521
522     mRootOnScreen.SetSensitive(false);
523
524     return mPageSourceActor[index].GetChildAt( 0 );
525   }
526   else
527   {
528     return Actor();
529   }
530 }
531
532 void PageTurnView::LeaveEditMode()
533 {
534   if( mNeedOffscreenRendering )
535   {
536     DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
537
538     mIsEditMode = false;
539
540     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
541     mOffscreenTask[index].SetInputEnabled( false );
542     mPageSourceActor[index].SetSensitive( false );
543     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
544
545     mRootOnScreen.SetSensitive(true);
546   }
547 }
548
549 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
550 {
551   if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
552   {
553     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
554
555     Dali::HitTestAlgorithm::Results results;
556     if( !mOffscreenTask[index].GetInputEnabled() )
557     {
558       mOffscreenTask[index].SetInputEnabled( true );
559       mPageSourceActor[index].SetSensitive( true );
560       Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
561       mOffscreenTask[index].SetInputEnabled( false );
562       mPageSourceActor[index].SetSensitive( false );
563     }
564     else
565     {
566       Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
567     }
568     actorCoordinates = results.actorCoordinates;
569     return results.actor;
570   }
571   else
572   {
573     return Actor();
574   }
575 }
576
577 void PageTurnView::AddPage( int pageIndex )
578 {
579   if(pageIndex > -1  && pageIndex < mTotalPageCount) // whether the page is available from the page factory
580   {
581     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
582     ImageActor newPage;
583     if( mNeedOffscreenRendering )
584     {
585       Actor source = mPageFactory.NewPage( pageIndex );
586       if( mPageSourceActor[index].GetChildCount() > 0 )
587       {
588         mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
589       }
590       mPageSourceActor[index].Add( source );
591       mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
592       newPage = NewPageFromRenderBuffer( pageIndex );
593     }
594     else
595     {
596       newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
597       DALI_ASSERT_ALWAYS( newPage );
598     }
599     newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
600     newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
601     newPage.SetSize( mPageSize );
602     mRootOnScreen.Add( newPage );
603     mPageActors[index] = newPage;
604
605     bool isLeftSide = ( pageIndex < mCurrentPageIndex );
606     mIsTurnBack[ newPage ] = isLeftSide;
607     if( isLeftSide )
608     {
609       // new page is added to the left side, so need to rotate it 180 degrees
610       newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
611     }
612     else
613     {
614       SetShaderEffect( newPage, mSpineEffectFront);
615     }
616
617     // For Portrait, nothing to do
618     // For Landscape, set spineEffectBack to the new effect if it is in the left side, and set properties to the back image actor if it exists
619     OnAddPage( newPage, isLeftSide );
620   }
621 }
622
623 void PageTurnView::RemovePage( int pageIndex )
624 {
625   if( pageIndex > -1 && pageIndex < mTotalPageCount)
626   {
627     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
628     mPageActors[index].Unparent();
629     mIsTurnBack.erase( mPageActors[index] );
630     mPageActors[index].Reset();
631     if( mNeedOffscreenRendering )
632     {
633       mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
634     }
635   }
636 }
637
638 void PageTurnView::RenderPage( int pageIndex )
639 {
640   if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
641    && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
642   {
643     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
644     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
645   }
646 }
647
648 void PageTurnView::RefreshAll()
649 {
650   mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
651   if( mTotalPageCount > 0 )
652   {
653     if(mCurrentPageIndex < mTotalPageCount)
654     {
655       GoToPage( mCurrentPageIndex );
656     }
657     else
658     {
659       GoToPage( mCurrentPageIndex-- );
660     }
661   }
662 }
663
664 void PageTurnView::RefreshCurrentPage()
665 {
666   RenderPage( mCurrentPageIndex );
667 }
668
669 void PageTurnView::OnPan( const PanGesture& gesture )
670 {
671   if( mIsEditMode )
672   {
673     // when interrupted by the call of DisplayCurrentPageSourceActor(),
674     // make sure the panFinished is always called before stopping to responding the gesture
675     // so the status of the control is updated correctly
676     if(mPanning)
677     {
678       mPanning = false;
679       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
680     }
681
682     return;
683   }
684   // the pan gesture is attached to control itself instead of each page
685   switch( gesture.state )
686   {
687     case Gesture::Started:
688     {
689       mPanning = true;
690       // to find out whether the undergoing turning page number already reaches the maximum allowed
691       // and get one idle index when it is animatable
692       bool animatable = false;
693       for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
694       {
695         if( !mIsAnimating[mIndex] )
696         {
697           animatable = true;
698           break;
699         }
700         if( mIsSliding[mIndex] )
701         {
702           animatable = false;
703           break;
704         }
705         mIndex++;
706         mIndex = mIndex % MAXIMUM_TURNING_NUM;
707       }
708
709       if( mPageUpdated && animatable )
710       {
711         SetPanActor( gesture.position ); // determine which page actor is panned
712         if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
713         {
714           mPanActor.Reset();
715         }
716         PanStarted( SetPanPosition( gesture.position ) );  // pass in the pan position in the local page coordinate
717       }
718       else
719       {
720         mPanActor.Reset();
721       }
722       break;
723     }
724     case Gesture::Continuing:
725     {
726       PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
727       break;
728     }
729     case Gesture::Finished:
730     case Gesture::Cancelled:
731     {
732       mPanning = false;
733       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
734       break;
735     }
736     case Gesture::Clear:
737     case Gesture::Possible:
738     default:
739     {
740       break;
741     }
742   }
743 }
744
745 void PageTurnView::PanStarted( const Vector2& gesturePosition )
746 {
747   mPressDownPosition = gesturePosition;
748
749   if( !mPanActor )
750   {
751     return;
752   }
753
754   mOriginalCenter = gesturePosition;
755   mTurnEffect[mIndex].SetIsTurningBack( mIsTurnBack[ mPanActor] );
756   mPress = false;
757   mPageUpdated = false;
758
759   // Guard against destruction during signal emission
760   Toolkit::PageTurnView handle( GetOwner() );
761   mPagePanStartedSignal.Emit( handle );
762 }
763
764 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
765 {
766   if( !mPanActor )
767   {
768     return;
769   }
770
771   // Guard against destruction during signal emission
772   Toolkit::PageTurnView handle( GetOwner() );
773
774   if(!mPress)
775   {
776     // when the touch down position is near the spine
777     // or when the panning goes outwards or some other position which would tear the paper in real situation
778     // we change the start position into the current panning position and update the shader parameters
779     if( mOriginalCenter.x <  mPageSize.width*MINIMUM_START_POSITION_RATIO
780         || gesturePosition.x > mOriginalCenter.x-1.0f
781         || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
782              ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
783     {
784       mOriginalCenter = gesturePosition;
785     }
786     else
787     {
788       mDistanceUpCorner = mOriginalCenter.Length();
789       mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
790       mShadowView.Add( mPanActor );
791       SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
792       mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
793       mCurrentCenter = mOriginalCenter;
794       mTurnEffect[mIndex].SetCurrentCenter( mCurrentCenter );
795       mPanDisplacement = 0.f;
796       mConstraints = true;
797       mPress = true;
798       mIsAnimating[mIndex] = true;
799
800       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
801
802       mShadowView.RemoveConstraints();
803       Actor self = Self();
804       self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
805       Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView.GetBlurStrengthPropertyIndex(),
806                                                                         Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())),
807                                                                         Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())),
808                                                                         Source( self, mPropertyPanDisplacement[mIndex] ),
809                                                                         ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
810       mShadowView.ApplyConstraint( shadowBlurStrengthConstraint  );
811     }
812   }
813   else
814   {
815     Vector2 currentCenter = gesturePosition;
816
817     // Test whether the new current center would tear the paper from the top pine in real situation
818     // we do not forbid this totally, which would restrict the panning gesture too much
819     // instead, set it to the nearest allowable position
820     float distanceUpCorner = currentCenter.Length();
821     float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
822     if( distanceUpCorner > mDistanceUpCorner )
823     {
824       currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
825     }
826     // would tear the paper from the bottom spine in real situation
827     if( distanceBottomCorner > mDistanceBottomCorner )
828     {
829       currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
830     }
831     // If direction has a very high y component, reduce it.
832     Vector2 curveDirection = currentCenter - mOriginalCenter;
833     if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
834     {
835       currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
836     }
837     // If the vertical distance is high, reduce it
838     float yShift = currentCenter.y - mOriginalCenter.y;
839     if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
840     {
841       currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
842     }
843
844     // use contraints to control the page shape and rotation when the pan position is near the spine
845     if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
846     {
847       // set the property values used by the constraints
848       mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
849       Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
850       Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
851
852       // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
853       // also set up the RotationConstraint to the page actor
854       if( mConstraints )
855       {
856         Vector2 corner;
857         // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
858         if( currentCenter.y >= mOriginalCenter.y )
859         {
860           corner = Vector2( 1.1f*mPageSize.width, 0.f );
861         }
862         else
863         {
864           corner = mPageSize*1.1f;
865         }
866
867         Vector2 offset( currentCenter-mOriginalCenter );
868         float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
869                    /( offset.x*offset.x + offset.y*offset.y );
870         offset *= k;
871         Actor self = Self();
872         Source source(self, mPropertyPanDisplacement[mIndex]);
873
874         Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
875         Constraint originalCenterConstraint = Constraint::New<Vector2>( shaderOriginalCenterPropertyIndex ,
876                                                                         source,
877                                                                         OriginalCenterConstraint( mOriginalCenter, offset ));
878         mTurnEffect[mIndex].ApplyConstraint( originalCenterConstraint );
879
880         Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
881         Constraint currentCenterConstraint = Constraint::New<Vector2>( shaderCurrentCenterPropertyIndex,
882                                                                        Source(self, mPropertyCurrentCenter[mIndex]),
883                                                                        Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex),
884                                                                        CurrentCenterConstraint(mPageSize.width));
885         mTurnEffect[mIndex].ApplyConstraint( currentCenterConstraint );
886
887         GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
888
889         float distance = offset.Length();
890         Constraint rotationConstraint = Constraint::New<Quaternion>( Actor::ROTATION,
891                                                                      Source( self, mPropertyPanDisplacement[mIndex] ),
892                                                                      RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
893         mPanActor.ApplyConstraint( rotationConstraint );
894
895         mConstraints = false;
896       }
897     }
898     else
899     {
900       if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
901       {
902         mPanActor.RemoveConstraints();
903         mTurnEffect[mIndex].RemoveConstraints();
904         mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
905         mConstraints = true;
906         mPanDisplacement = 0.f;
907       }
908
909       mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
910       mCurrentCenter = currentCenter;
911       GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
912     }
913   }
914 }
915
916 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
917 {
918   // Guard against destruction during signal emission
919   Toolkit::PageTurnView handle( GetOwner() );
920
921   if( !mPanActor )
922   {
923     if(!mIsAnimating[mIndex])
924     {
925       OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
926     }
927     return;
928   }
929
930   mPagePanFinishedSignal.Emit( handle );
931
932   ImageActor actor = mPanActor;
933   if(mPress)
934   {
935     if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
936     {
937       // update the pages here instead of in the TurnedOver callback function
938       // as new page is allowed to respond to the pan gesture before other pages finishing animation
939       if(mIsTurnBack[actor])
940       {
941         mCurrentPageIndex--;
942         RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
943         AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
944       }
945       else
946       {
947         mCurrentPageIndex++;
948         RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
949         AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
950       }
951       OrganizePageDepth();
952
953       // set up an animation to turn the page over
954       Actor self = Self();
955       float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
956       Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
957       animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
958                            width,AlphaFunctions::EaseOutSine33);
959       animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
960                            Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunctions::EaseOutSine33);
961       mAnimationActorPair[animation] = actor;
962       mAnimationIndexPair[animation] = mIndex;
963       animation.Play();
964       animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
965     }
966     else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
967     {
968       Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
969       animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
970                            mOriginalCenter, AlphaFunctions::Linear );
971       mAnimationActorPair[animation] = actor;
972       mAnimationIndexPair[animation] = mIndex;
973       animation.Play();
974       mIsSliding[mIndex] = true;
975       animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
976
977       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
978     }
979   }
980   else
981   {
982     // In portrait view, an outwards flick should turn the previous page back
983     // In landscape view, nothing to do
984     OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
985   }
986
987   mPageUpdated = true;
988 }
989
990 void PageTurnView::TurnedOver( Animation& animation )
991 {
992   ImageActor actor = mAnimationActorPair[animation];
993   mIsTurnBack[actor] = !mIsTurnBack[actor];
994   actor.RemoveConstraints();
995   mRootOnScreen.Add(actor);
996   int index = mAnimationIndexPair[animation];
997   mIsAnimating[index] = false;
998   mTurnEffect[index].RemoveConstraints();
999   mAnimationIndexPair.erase( animation );
1000   mAnimationActorPair.erase( animation );
1001
1002   SetSpineEffect( actor, mIsTurnBack[actor] );
1003
1004   // Guard against destruction during signal emission
1005   Toolkit::PageTurnView handle( GetOwner() );
1006   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1007 }
1008
1009 void PageTurnView::SliddenBack( Animation& animation )
1010 {
1011   ImageActor actor = mAnimationActorPair[animation];
1012   mRootOnScreen.Add(actor);
1013   int index = mAnimationIndexPair[animation];
1014   mIsSliding[index] = false;
1015   mIsAnimating[index] = false;
1016   mAnimationIndexPair.erase( animation );
1017   mAnimationActorPair.erase( animation );
1018
1019   SetSpineEffect( actor, mIsTurnBack[actor] );
1020
1021   // Guard against destruction during signal emission
1022   Toolkit::PageTurnView handle( GetOwner() );
1023   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1024 }
1025
1026 void PageTurnView::OrganizePageDepth()
1027 {
1028   for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1029   {
1030     if(mCurrentPageIndex+i < mTotalPageCount)
1031     {
1032       mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1033     }
1034     if( mCurrentPageIndex >= i + 1 )
1035     {
1036       mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1037     }
1038   }
1039 }
1040
1041 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
1042 {
1043   SetShaderEffectRecursively( actor, shaderEffect );
1044 }
1045
1046 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1047 {
1048   return mPageTurnStartedSignal;
1049 }
1050
1051 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1052 {
1053   return mPageTurnFinishedSignal;
1054 }
1055
1056 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1057 {
1058   return mPagePanStartedSignal;
1059 }
1060
1061 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1062 {
1063   return mPagePanFinishedSignal;
1064 }
1065
1066 } // namespace Internal
1067
1068 } // namespace Toolkit
1069
1070 } // namespace Dali