b55b7579e992a14d3137783f710574969608f826
[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 <dali/public-api/animation/animation.h>
23 #include <dali/public-api/animation/constraint.h>
24 #include <dali/devel-api/events/hit-test-algorithm.h>
25 #include <dali/public-api/object/type-registry.h>
26 #include <dali/devel-api/object/type-registry-helper.h>
27 #include <dali/public-api/render-tasks/render-task-list.h>
28
29 // INTERNAL INCLUDES
30 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
31
32 using namespace Dali;
33
34 namespace //Unnamed namespace
35 {
36 // To register type
37
38 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::PageTurnView, Toolkit::Control, NULL )
39 DALI_TYPE_REGISTRATION_END()
40
41 // default grid density for page turn effect, 10 pixels by 10 pixels
42 const float DEFAULT_GRID_DENSITY(10.0f);
43
44 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
45 const float MINIMUM_START_POSITION_RATIO(0.6f);
46
47 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
48 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
49
50 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
51 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
52
53 // duration of animation, shorter for faster speed
54 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
55 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
56
57 // the major&minor radius (in pixels) to form an ellipse shape
58 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
59 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
60
61 // constants for shadow casting
62 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
63 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
64
65 // constraints ////////////////////////////////////////////////////////////////
66 /**
67  * Original Center Constraint
68  *
69  * This constraint adjusts the original center property of the page turn shader effect
70  * based on the X-direction displacement of the pan gesture
71  */
72 struct OriginalCenterConstraint
73 {
74   OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
75   : mOldCenter( originalCenter )
76   {
77     mNewCenter = originalCenter + offset;
78     mDistance = offset.Length() * 0.5f;
79     mDirection = offset  / mDistance;
80   }
81
82   void operator()( Vector2& current, const PropertyInputContainer& inputs )
83   {
84     float displacement = inputs[0]->GetFloat();
85
86     if( displacement < mDistance )
87     {
88       current = mOldCenter + mDirection * displacement;
89     }
90     else
91     {
92       current = mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
93     }
94   }
95
96   Vector2 mOldCenter;
97   Vector2 mNewCenter;
98   float mDistance;
99   Vector2 mDirection;
100 };
101
102 /**
103  * Rotation Constraint
104  *
105  * This constraint adjusts the rotation property of the page actor
106  * based on the X-direction displacement of the pan gesture
107  */
108 struct RotationConstraint
109 {
110   RotationConstraint( float distance, float pageWidth, bool isTurnBack )
111   : mDistance( distance*0.5f )
112   {
113     mStep = 1.f / pageWidth;
114     mSign = isTurnBack ? -1.0f : 1.0f;
115     mConst = isTurnBack ? -1.0f : 0.f;
116     mRotation = isTurnBack ? Quaternion( Radian( -Math::PI ), Vector3::YAXIS ) : Quaternion( Radian(0.f), Vector3::YAXIS );
117   }
118
119   void operator()( Quaternion& current, const PropertyInputContainer& inputs )
120   {
121     float displacement = inputs[0]->GetFloat();
122     if( displacement < mDistance)
123     {
124       current = mRotation;
125     }
126     else
127     {
128       float coef = std::max(-1.0f, mStep*(mDistance-displacement));
129       float angle = Math::PI * ( mConst + mSign * coef );
130       current = Quaternion( Radian( angle ), Vector3::YAXIS );
131     }
132   }
133
134   float mDistance;
135   float mStep;
136   float mConst;
137   float mSign;
138   Quaternion mRotation;
139 };
140
141 /**
142  * Current Center Constraint
143  *
144  * This constraint adjusts the current center property of the page turn shader effect
145  * based on the pan position and the original center position
146  */
147 struct CurrentCenterConstraint
148 {
149   CurrentCenterConstraint( float pageWidth )
150   : mPageWidth( pageWidth )
151   {
152     mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
153   }
154
155   void operator()( Vector2& current, const PropertyInputContainer& inputs )
156   {
157     const Vector2& centerPosition = inputs[0]->GetVector2();
158     if( centerPosition.x > 0.f )
159     {
160       current.x = mThres+centerPosition.x * 0.5f;
161       current.y = centerPosition.y;
162     }
163     else
164     {
165       const Vector2& centerOrigin = inputs[1]->GetVector2();
166       Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
167       float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
168       // 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
169       if(coef < 0.025f)
170       {
171         coef = (coef+0.225f)/10.0f;
172       }
173       current = centerOrigin - direction * coef;
174     }
175   }
176
177   float mPageWidth;
178   float mThres;
179 };
180
181 struct ShadowBlurStrengthConstraint
182 {
183   ShadowBlurStrengthConstraint( float thres )
184   : mThres( thres )
185   {}
186
187   void operator()( float& blurStrength,  const PropertyInputContainer& inputs )
188   {
189     float displacement = inputs[2]->GetFloat();
190     if( EqualsZero(displacement))
191     {
192       const Vector2& cur = inputs[0]->GetVector2();
193       const Vector2& ori = inputs[1]->GetVector2();
194       blurStrength =  5.f*(ori-cur).Length() / mThres;
195     }
196     else
197     {
198       blurStrength =  1.f - (displacement-2.f*mThres)/mThres;
199     }
200
201     blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
202   }
203
204   float mThres;
205 };
206
207 bool IsActorHittableFunction( Actor actor, Dali::HitTestAlgorithm::TraverseType type )
208 {
209   bool hittable = false;
210
211   switch (type)
212   {
213     case Dali::HitTestAlgorithm::CHECK_ACTOR:
214     {
215       // Check whether the actor is visible and not fully transparent.
216       Property::Index propertyActorHittable = actor.GetPropertyIndex(Toolkit::PageFactory::ACTOR_HITTABLE);
217       if( actor.IsSensitive()
218        && actor.IsVisible()
219        && actor.GetCurrentWorldColor().a > 0.01f// not FULLY_TRANSPARENT
220        && ( propertyActorHittable != Property::INVALID_INDEX &&
221             actor.GetProperty<bool>( propertyActorHittable ) ) )
222       {
223          hittable = true;
224       }
225       break;
226     }
227     case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
228     {
229       if( actor.IsSensitive() && actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible.
230       {
231         hittable = true;
232       }
233       break;
234     }
235     default:
236     {
237       break;
238     }
239   }
240
241   return hittable;
242 }
243
244 } //unnamed namespace
245
246 namespace Dali
247 {
248
249 namespace Toolkit
250 {
251
252 namespace Internal
253 {
254
255 // these several constants are also used in the derived classes
256 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
257 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
258 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
259 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
260
261 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
262 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS ) ),
263   mPageFactory( pageFactory ),
264   mPageSize( pageSize ),
265   mTotalPageCount( 0 ),
266   mIsEditMode( false ),
267   mNeedOffscreenRendering( false ),
268   mPanning( false ),
269   mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
270   mCurrentPageIndex( 0 ),
271   mIndex( 0 ),
272   mPress( false ),
273   mPageUpdated( true ),
274   mDistanceUpCorner( 0.f ),
275   mDistanceBottomCorner( 0.f ),
276   mPanDisplacement( 0.f ),
277   mConstraints( false ),
278   mPageTurnStartedSignal(),
279   mPageTurnFinishedSignal(),
280   mPagePanStartedSignal(),
281   mPagePanFinishedSignal()
282 {
283   mPageActors.resize( NUMBER_OF_CACHED_PAGES );
284   mIsAnimating.resize( MAXIMUM_TURNING_NUM );
285   mIsSliding.resize( MAXIMUM_TURNING_NUM );
286   mTurnEffect.resize( MAXIMUM_TURNING_NUM );
287   mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
288   mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
289 }
290
291 PageTurnView::~PageTurnView()
292 {
293 }
294
295 void PageTurnView::OnInitialize()
296 {
297    // create the two book spine effect for static images, left and right side pages respectively
298   mSpineEffectFront = PageTurnBookSpineEffect::New();
299   mSpineEffectFront.SetIsBackImageVisible( false );
300   mSpineEffectFront.SetPageWidth( mPageSize.width );
301   mSpineEffectFront.SetShadowWidth( 0.f );
302   mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
303
304   mSpineEffectBack = PageTurnBookSpineEffect::New();
305   mSpineEffectBack.SetIsBackImageVisible( true );
306   mSpineEffectBack.SetPageWidth( mPageSize.width );
307   mSpineEffectBack.SetShadowWidth( 0.f );
308   mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
309
310   // create the page turn effect objects
311   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
312   {
313     mTurnEffect[i] = Toolkit::PageTurnEffect::New( false );
314     mTurnEffect[i].SetProperty( ShaderEffect::Property::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
315     mTurnEffect[i].SetPageSize( mPageSize );
316     mTurnEffect[i].SetShadowWidth(0.f);
317     mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
318     mIsAnimating[i] = false;
319     mIsSliding[i] = false;
320     mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
321     mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
322   }
323
324   mTurningPageLayer = Layer::New();
325   mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
326   // Set control size and the parent origin of turningPageLayer
327   OnPageTurnViewInitialize();
328
329   mRootOnScreen = Actor::New();
330   mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION );
331   mRootOnScreen.SetSize( mControlSize );
332   Self().Add( mRootOnScreen );
333   mRootOnScreen.Add(mTurningPageLayer);
334
335   mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
336   mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded();
337   if( mNeedOffscreenRendering )
338   {
339     SetupRenderTasks();
340   }
341
342   // add pages to the scene, and set depth for the stacked pages
343   for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
344   {
345     AddPage( i );
346     if(mPageActors[i])
347     {
348       mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
349     }
350   }
351
352   // enable the pan gesture which is attached to the control
353   EnableGestureDetection(Gesture::Type(Gesture::Pan));
354
355   mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage);
356 }
357
358 void PageTurnView::SetupRenderTasks()
359 {
360   mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES );
361   mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES );
362   mRenderedPage.resize( NUMBER_OF_CACHED_PAGES );
363
364   mCameraActor = CameraActor::New(mControlSize);
365   mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
366   mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
367   mCameraActor.SetInheritScale( false );
368   Self().Add(mCameraActor);
369
370   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
371   for(int i=0; i<NUMBER_OF_CACHED_PAGES; i++)
372   {
373     mPageSourceActor[i] = Actor::New();
374     mPageSourceActor[i].SetParentOrigin(ParentOrigin::CENTER);
375     mPageSourceActor[i].SetColorMode( USE_OWN_COLOR );
376     mPageSourceActor[i].SetPositionInheritanceMode( DONT_INHERIT_POSITION );
377     mPageSourceActor[i].SetInheritScale( false );
378     Self().Add( mPageSourceActor[i] );
379     mPageSourceActor[i].SetSensitive( false );
380
381     mRenderedPage[i] = FrameBufferImage::New( mControlSize.width, mControlSize.height, Pixel::RGB8888, Image::UNUSED );
382     mOffscreenTask[i] = taskList.CreateTask();
383     mOffscreenTask[i].SetRefreshRate( RenderTask::REFRESH_ONCE );
384     mOffscreenTask[i].SetCameraActor(mCameraActor);
385     mOffscreenTask[i].SetSourceActor( mPageSourceActor[i] );
386     mOffscreenTask[i].SetExclusive(true);
387     mOffscreenTask[i].SetInputEnabled( false );
388     mOffscreenTask[i].SetClearEnabled( true );
389     mOffscreenTask[i].SetClearColor( Vector4(0.f,0.f,0.f,0.f) );
390     mOffscreenTask[i].SetTargetFrameBuffer( mRenderedPage[i] );
391     mOffscreenTask[i].SetScreenToFrameBufferMappingActor( Self() );
392    }
393 }
394
395 void PageTurnView::SetupShadowView()
396 {
397   mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
398   Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
399   mShadowView.SetParentOrigin( origin );
400   mShadowView.SetAnchorPoint( origin );
401   mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
402   mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
403
404   mShadowLayer = Layer::New();
405   mShadowLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
406   mRootOnScreen.Add(mShadowLayer);
407   mShadowLayer.Raise();
408
409   mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
410   mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
411   mShadowPlane.SetSize( mControlSize );
412   mShadowLayer.Add( mShadowPlane );
413   mShadowView.SetShadowPlane( mShadowPlane );
414
415   mPointLight = Actor::New();
416   mPointLight.SetAnchorPoint( origin );
417   mPointLight.SetParentOrigin( origin );
418   mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
419   mRootOnScreen.Add( mPointLight );
420   mShadowView.SetPointLight( mPointLight );
421
422   mTurningPageLayer.Add( mShadowView );
423   mShadowView.Activate();
424 }
425
426 void PageTurnView::OnControlStageConnection()
427 {
428   SetupShadowView();
429   mTurningPageLayer.RaiseToTop();
430 }
431
432 void PageTurnView::OnControlStageDisconnection()
433 {
434   if(mShadowView)
435   {
436     Self().Remove(mPointLight);
437     Self().Remove(mShadowLayer);
438     mTurningPageLayer.Remove( mShadowView );
439   }
440
441   // make sure the status of the control is updated correctly when the pan gesture is interrupted
442   if(mPanning)
443   {
444     mPanning = false;
445
446     mRootOnScreen.Add(mPanActor);
447     mIsAnimating[mIndex] = false;
448     mPanActor.RemoveConstraints();
449     mTurnEffect[mIndex].RemoveConstraints();
450     mPageUpdated = true;
451
452     SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
453   }
454 }
455
456 void PageTurnView::OnControlSizeSet( const Vector3& size )
457 {
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
806       Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
807       shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())) );
808       shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())) );
809       shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
810       shadowBlurStrengthConstraint.Apply();
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
873         Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
874         Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
875         originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
876         originalCenterConstraint.Apply();
877
878         Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
879         Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
880         currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
881         currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
882         currentCenterConstraint.Apply();
883
884         GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
885
886         float distance = offset.Length();
887         Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
888         rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
889         rotationConstraint.Apply();
890
891         mConstraints = false;
892       }
893     }
894     else
895     {
896       if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
897       {
898         mPanActor.RemoveConstraints();
899         mTurnEffect[mIndex].RemoveConstraints();
900         mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
901         mConstraints = true;
902         mPanDisplacement = 0.f;
903       }
904
905       mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
906       mCurrentCenter = currentCenter;
907       GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
908     }
909   }
910 }
911
912 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
913 {
914   // Guard against destruction during signal emission
915   Toolkit::PageTurnView handle( GetOwner() );
916
917   if( !mPanActor )
918   {
919     if(!mIsAnimating[mIndex])
920     {
921       OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
922     }
923     return;
924   }
925
926   mPagePanFinishedSignal.Emit( handle );
927
928   ImageActor actor = mPanActor;
929   if(mPress)
930   {
931     if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
932     {
933       // update the pages here instead of in the TurnedOver callback function
934       // as new page is allowed to respond to the pan gesture before other pages finishing animation
935       if(mIsTurnBack[actor])
936       {
937         mCurrentPageIndex--;
938         RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
939         AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
940       }
941       else
942       {
943         mCurrentPageIndex++;
944         RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
945         AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
946       }
947       OrganizePageDepth();
948
949       // set up an animation to turn the page over
950       Actor self = Self();
951       float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
952       Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
953       animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
954                            width,AlphaFunction::EASE_OUT_SINE);
955       animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
956                            Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
957       mAnimationActorPair[animation] = actor;
958       mAnimationIndexPair[animation] = mIndex;
959       animation.Play();
960       animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
961     }
962     else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
963     {
964       Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
965       animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
966                            mOriginalCenter, AlphaFunction::LINEAR );
967       mAnimationActorPair[animation] = actor;
968       mAnimationIndexPair[animation] = mIndex;
969       animation.Play();
970       mIsSliding[mIndex] = true;
971       animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
972
973       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
974     }
975   }
976   else
977   {
978     // In portrait view, an outwards flick should turn the previous page back
979     // In landscape view, nothing to do
980     OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
981   }
982
983   mPageUpdated = true;
984 }
985
986 void PageTurnView::TurnedOver( Animation& animation )
987 {
988   ImageActor actor = mAnimationActorPair[animation];
989   mIsTurnBack[actor] = !mIsTurnBack[actor];
990   actor.RemoveConstraints();
991   mRootOnScreen.Add(actor);
992   int index = mAnimationIndexPair[animation];
993   mIsAnimating[index] = false;
994   mTurnEffect[index].RemoveConstraints();
995   mAnimationIndexPair.erase( animation );
996   mAnimationActorPair.erase( animation );
997
998   SetSpineEffect( actor, mIsTurnBack[actor] );
999
1000   // Guard against destruction during signal emission
1001   Toolkit::PageTurnView handle( GetOwner() );
1002   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1003 }
1004
1005 void PageTurnView::SliddenBack( Animation& animation )
1006 {
1007   ImageActor actor = mAnimationActorPair[animation];
1008   mRootOnScreen.Add(actor);
1009   int index = mAnimationIndexPair[animation];
1010   mIsSliding[index] = false;
1011   mIsAnimating[index] = false;
1012   mAnimationIndexPair.erase( animation );
1013   mAnimationActorPair.erase( animation );
1014
1015   SetSpineEffect( actor, mIsTurnBack[actor] );
1016
1017   // Guard against destruction during signal emission
1018   Toolkit::PageTurnView handle( GetOwner() );
1019   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1020 }
1021
1022 void PageTurnView::OrganizePageDepth()
1023 {
1024   for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1025   {
1026     if(mCurrentPageIndex+i < mTotalPageCount)
1027     {
1028       mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1029     }
1030     if( mCurrentPageIndex >= i + 1 )
1031     {
1032       mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1033     }
1034   }
1035 }
1036
1037 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
1038 {
1039   SetShaderEffectRecursively( actor, shaderEffect );
1040 }
1041
1042 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1043 {
1044   return mPageTurnStartedSignal;
1045 }
1046
1047 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1048 {
1049   return mPageTurnFinishedSignal;
1050 }
1051
1052 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1053 {
1054   return mPagePanStartedSignal;
1055 }
1056
1057 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1058 {
1059   return mPagePanFinishedSignal;
1060 }
1061
1062 } // namespace Internal
1063
1064 } // namespace Toolkit
1065
1066 } // namespace Dali