Merge "DALi Version 1.0.31" into tizen
[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/animation/active-constraint.h>
24 #include <dali/public-api/animation/constraint.h>
25 #include <dali/public-api/events/hit-test-algorithm.h>
26 #include <dali/public-api/object/type-registry.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 TypeRegistration mType( typeid(Toolkit::PageTurnView), typeid(Toolkit::Control), NULL );
38
39 // default grid density for page turn effect, 10 pixels by 10 pixels
40 const float DEFAULT_GRID_DENSITY(10.0f);
41
42 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
43 const float MINIMUM_START_POSITION_RATIO(0.6f);
44
45 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
46 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
47
48 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
49 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
50
51 // duration of animation, shorter for faster speed
52 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
53 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
54
55 // the major&minor radius (in pixels) to form an ellipse shape
56 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
57 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
58
59 // constants for shadow casting
60 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
61 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
62
63 // constraints ////////////////////////////////////////////////////////////////
64 /**
65  * Original Center Constraint
66  *
67  * This constraint adjusts the original center property of the page turn shader effect
68  * based on the X-direction displacement of the pan gesture
69  */
70 struct OriginalCenterConstraint
71 {
72   OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
73   : mOldCenter( originalCenter )
74   {
75     mNewCenter = originalCenter + offset;
76     mDistance = offset.Length() * 0.5f;
77     mDirection = offset  / mDistance;
78   }
79
80   Vector2 operator()(const Vector2& current, const PropertyInput& panDisplacement)
81   {
82     float displacement = panDisplacement.GetFloat();
83
84     if( displacement < mDistance )
85     {
86       return mOldCenter + mDirection * displacement;
87     }
88     else
89     {
90       return mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
91     }
92   }
93
94   Vector2 mOldCenter;
95   Vector2 mNewCenter;
96   float mDistance;
97   Vector2 mDirection;
98 };
99
100 /**
101  * Rotation Constraint
102  *
103  * This constraint adjusts the rotation property of the page actor
104  * based on the X-direction displacement of the pan gesture
105  */
106 struct RotationConstraint
107 {
108   RotationConstraint( float distance, float pageWidth, bool isTurnBack )
109   : mDistance( distance*0.5f )
110   {
111     mStep = 1.f / pageWidth;
112     mSign = isTurnBack ? -1.0f : 1.0f;
113     mConst = isTurnBack ? -1.0f : 0.f;
114     mRotation = isTurnBack ? Quaternion( -Math::PI, Vector3::YAXIS ) : Quaternion( 0.f, Vector3::YAXIS );
115   }
116
117   Quaternion operator()( const Quaternion& current, const PropertyInput& panDisplacement )
118   {
119     float displacement = panDisplacement.GetFloat();
120     float angle;
121     if( displacement < mDistance)
122     {
123       return mRotation;
124     }
125     else
126     {
127       float coef = std::max(-1.0f, mStep*(mDistance-displacement));
128       angle = Math::PI*( mConst + mSign*coef );
129       return Quaternion( angle, Vector3::YAXIS );
130     }
131   }
132
133   float mDistance;
134   float mStep;
135   float mConst;
136   float mSign;
137   Quaternion mRotation;
138 };
139
140 /**
141  * Current Center Constraint
142  *
143  * This constraint adjusts the current center property of the page turn shader effect
144  * based on the pan position and the original center position
145  */
146 struct CurrentCenterConstraint
147 {
148   CurrentCenterConstraint( float pageWidth)
149   : mPageWidth( pageWidth )
150   {
151     mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
152   }
153
154   Vector2 operator()( const Vector2& current, const PropertyInput& center, const PropertyInput& originalCenter )
155   {
156     Vector2 centerPosition = center.GetVector2();
157     if( centerPosition.x > 0.f )
158     {
159       return Vector2( mThres+centerPosition.x*0.5f , centerPosition.y);
160     }
161     else
162     {
163       Vector2 centerOrigin = originalCenter.GetVector2();
164       Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
165       float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
166       // 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
167       if(coef < 0.025f)
168       {
169         coef = (coef+0.225f)/10.0f;
170       }
171       return centerOrigin - direction * coef;
172     }
173   }
174
175   float mPageWidth;
176   float mThres;
177 };
178
179 struct ShadowBlurStrengthConstraint
180 {
181   ShadowBlurStrengthConstraint( float thres )
182   : mThres( thres )
183   {}
184
185   float operator()( const float current,  const PropertyInput& currentCenter, const PropertyInput& originalCenter, const PropertyInput& panDisplacement)
186   {
187     float displacement = panDisplacement.GetFloat();
188     float blurStrength;
189     if( EqualsZero(displacement))
190     {
191       Vector2 cur = currentCenter.GetVector2();
192       Vector2 ori = originalCenter.GetVector2();
193       blurStrength =  5.f*(ori-cur).Length() / mThres;
194     }
195     else
196     {
197       blurStrength =  1.f - (displacement-2.f*mThres)/mThres;
198     }
199
200     blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
201     return 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( 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::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   // disable the SetSize of the control from the application
459   Self().SetSize( mControlSize );
460 }
461
462 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
463 {
464   mSpineShadowParameter = spineShadowParameter;
465
466   // set spine shadow parameter to all the shader effects
467   mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
468   mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
469   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
470   {
471     mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
472   }
473 }
474
475 Vector2 PageTurnView::GetSpineShadowParameter()
476 {
477   return mSpineShadowParameter;
478 }
479
480 void PageTurnView::GoToPage( unsigned int pageId )
481 {
482   int pageIdx = static_cast<int>(pageId);
483   // record the new current page index
484   mCurrentPageIndex = pageIdx;
485
486   // clear the old pages
487   for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
488   {
489     if( mPageActors[i] )
490     {
491       mPageActors[i].Unparent();
492       mPageActors[i].Reset();
493     }
494   }
495
496   // add the current page and the pages right before and after it
497   for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
498   {
499     AddPage( i );
500   }
501   // set ordered depth to the stacked pages
502   OrganizePageDepth();
503 }
504
505 unsigned int PageTurnView::GetCurrentPage()
506 {
507   DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
508   return static_cast< unsigned int >( mCurrentPageIndex );
509 }
510
511 Actor PageTurnView::EnterEditMode()
512 {
513   if( mNeedOffscreenRendering )
514   {
515     DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
516
517     mIsEditMode = true;
518
519     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
520     mOffscreenTask[index].SetInputEnabled( true );
521     mPageSourceActor[index].SetSensitive( true );
522     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
523
524     mRootOnScreen.SetSensitive(false);
525
526     return mPageSourceActor[index].GetChildAt( 0 );
527   }
528   else
529   {
530     return Actor();
531   }
532 }
533
534 void PageTurnView::LeaveEditMode()
535 {
536   if( mNeedOffscreenRendering )
537   {
538     DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
539
540     mIsEditMode = false;
541
542     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
543     mOffscreenTask[index].SetInputEnabled( false );
544     mPageSourceActor[index].SetSensitive( false );
545     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
546
547     mRootOnScreen.SetSensitive(true);
548   }
549 }
550
551 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
552 {
553   if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
554   {
555     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
556
557     Dali::HitTestAlgorithm::Results results;
558     if( !mOffscreenTask[index].GetInputEnabled() )
559     {
560       mOffscreenTask[index].SetInputEnabled( true );
561       mPageSourceActor[index].SetSensitive( true );
562       Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
563       mOffscreenTask[index].SetInputEnabled( false );
564       mPageSourceActor[index].SetSensitive( false );
565     }
566     else
567     {
568       Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
569     }
570     actorCoordinates = results.actorCoordinates;
571     return results.actor;
572   }
573   else
574   {
575     return Actor();
576   }
577 }
578
579 void PageTurnView::AddPage( int pageIndex )
580 {
581   if(pageIndex > -1  && pageIndex < mTotalPageCount) // whether the page is available from the page factory
582   {
583     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
584     ImageActor newPage;
585     if( mNeedOffscreenRendering )
586     {
587       Actor source = mPageFactory.NewPage( pageIndex );
588       if( mPageSourceActor[index].GetChildCount() > 0 )
589       {
590         mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
591       }
592       mPageSourceActor[index].Add( source );
593       mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
594       newPage = NewPageFromRenderBuffer( pageIndex );
595     }
596     else
597     {
598       newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
599       DALI_ASSERT_ALWAYS( newPage );
600     }
601     newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
602     newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
603     newPage.SetSize( mPageSize );
604     mRootOnScreen.Add( newPage );
605     mPageActors[index] = newPage;
606
607     bool isLeftSide = ( pageIndex < mCurrentPageIndex );
608     mIsTurnBack[ newPage ] = isLeftSide;
609     if( isLeftSide )
610     {
611       // new page is added to the left side, so need to rotate it 180 degrees
612       newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
613     }
614     else
615     {
616       SetShaderEffect( newPage, mSpineEffectFront);
617     }
618
619     // For Portrait, nothing to do
620     // 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
621     OnAddPage( newPage, isLeftSide );
622   }
623 }
624
625 void PageTurnView::RemovePage( int pageIndex )
626 {
627   if( pageIndex > -1 && pageIndex < mTotalPageCount)
628   {
629     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
630     mPageActors[index].Unparent();
631     mIsTurnBack.erase( mPageActors[index] );
632     mPageActors[index].Reset();
633     if( mNeedOffscreenRendering )
634     {
635       mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
636     }
637   }
638 }
639
640 void PageTurnView::RenderPage( int pageIndex )
641 {
642   if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
643    && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
644   {
645     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
646     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
647   }
648 }
649
650 void PageTurnView::RefreshAll()
651 {
652   mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
653   if( mTotalPageCount > 0 )
654   {
655     if(mCurrentPageIndex < mTotalPageCount)
656     {
657       GoToPage( mCurrentPageIndex );
658     }
659     else
660     {
661       GoToPage( mCurrentPageIndex-- );
662     }
663   }
664 }
665
666 void PageTurnView::RefreshCurrentPage()
667 {
668   RenderPage( mCurrentPageIndex );
669 }
670
671 void PageTurnView::OnPan( const PanGesture& gesture )
672 {
673   if( mIsEditMode )
674   {
675     // when interrupted by the call of DisplayCurrentPageSourceActor(),
676     // make sure the panFinished is always called before stopping to responding the gesture
677     // so the status of the control is updated correctly
678     if(mPanning)
679     {
680       mPanning = false;
681       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
682     }
683
684     return;
685   }
686   // the pan gesture is attached to control itself instead of each page
687   switch( gesture.state )
688   {
689     case Gesture::Started:
690     {
691       mPanning = true;
692       // to find out whether the undergoing turning page number already reaches the maximum allowed
693       // and get one idle index when it is animatable
694       bool animatable = false;
695       for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
696       {
697         if( !mIsAnimating[mIndex] )
698         {
699           animatable = true;
700           break;
701         }
702         if( mIsSliding[mIndex] )
703         {
704           animatable = false;
705           break;
706         }
707         mIndex++;
708         mIndex = mIndex % MAXIMUM_TURNING_NUM;
709       }
710
711       if( mPageUpdated && animatable )
712       {
713         SetPanActor( gesture.position ); // determine which page actor is panned
714         if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
715         {
716           mPanActor.Reset();
717         }
718         PanStarted( SetPanPosition( gesture.position ) );  // pass in the pan position in the local page coordinate
719       }
720       else
721       {
722         mPanActor.Reset();
723       }
724       break;
725     }
726     case Gesture::Continuing:
727     {
728       PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
729       break;
730     }
731     case Gesture::Finished:
732     case Gesture::Cancelled:
733     {
734       mPanning = false;
735       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
736       break;
737     }
738     case Gesture::Clear:
739     case Gesture::Possible:
740     default:
741     {
742       break;
743     }
744   }
745 }
746
747 void PageTurnView::PanStarted( const Vector2& gesturePosition )
748 {
749   mPressDownPosition = gesturePosition;
750
751   if( !mPanActor )
752   {
753     return;
754   }
755
756   mOriginalCenter = gesturePosition;
757   mTurnEffect[mIndex].SetIsTurningBack( mIsTurnBack[ mPanActor] );
758   mPress = false;
759   mPageUpdated = false;
760
761   // Guard against destruction during signal emission
762   Toolkit::PageTurnView handle( GetOwner() );
763   mPagePanStartedSignal.Emit( handle );
764 }
765
766 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
767 {
768   if( !mPanActor )
769   {
770     return;
771   }
772
773   // Guard against destruction during signal emission
774   Toolkit::PageTurnView handle( GetOwner() );
775
776   if(!mPress)
777   {
778     // when the touch down position is near the spine
779     // or when the panning goes outwards or some other position which would tear the paper in real situation
780     // we change the start position into the current panning position and update the shader parameters
781     if( mOriginalCenter.x <  mPageSize.width*MINIMUM_START_POSITION_RATIO
782         || gesturePosition.x > mOriginalCenter.x-1.0f
783         || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
784              ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
785     {
786       mOriginalCenter = gesturePosition;
787     }
788     else
789     {
790       mDistanceUpCorner = mOriginalCenter.Length();
791       mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
792       mShadowView.Add( mPanActor );
793       SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
794       mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
795       mCurrentCenter = mOriginalCenter;
796       mTurnEffect[mIndex].SetCurrentCenter( mCurrentCenter );
797       mPanDisplacement = 0.f;
798       mConstraints = true;
799       mPress = true;
800       mIsAnimating[mIndex] = true;
801
802       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
803
804       mShadowView.RemoveConstraints();
805       Actor self = Self();
806       self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
807       Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView.GetBlurStrengthPropertyIndex(),
808                                                                         Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())),
809                                                                         Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())),
810                                                                         Source( self, mPropertyPanDisplacement[mIndex] ),
811                                                                         ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
812       mShadowView.ApplyConstraint( shadowBlurStrengthConstraint  );
813     }
814   }
815   else
816   {
817     Vector2 currentCenter = gesturePosition;
818
819     // Test whether the new current center would tear the paper from the top pine in real situation
820     // we do not forbid this totally, which would restrict the panning gesture too much
821     // instead, set it to the nearest allowable position
822     float distanceUpCorner = currentCenter.Length();
823     float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
824     if( distanceUpCorner > mDistanceUpCorner )
825     {
826       currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
827     }
828     // would tear the paper from the bottom spine in real situation
829     if( distanceBottomCorner > mDistanceBottomCorner )
830     {
831       currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
832     }
833     // If direction has a very high y component, reduce it.
834     Vector2 curveDirection = currentCenter - mOriginalCenter;
835     if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
836     {
837       currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
838     }
839     // If the vertical distance is high, reduce it
840     float yShift = currentCenter.y - mOriginalCenter.y;
841     if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
842     {
843       currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
844     }
845
846     // use contraints to control the page shape and rotation when the pan position is near the spine
847     if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
848     {
849       // set the property values used by the constraints
850       mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
851       Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
852       Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
853
854       // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
855       // also set up the RotationConstraint to the page actor
856       if( mConstraints )
857       {
858         Vector2 corner;
859         // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
860         if( currentCenter.y >= mOriginalCenter.y )
861         {
862           corner = Vector2( 1.1f*mPageSize.width, 0.f );
863         }
864         else
865         {
866           corner = mPageSize*1.1f;
867         }
868
869         Vector2 offset( currentCenter-mOriginalCenter );
870         float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
871                    /( offset.x*offset.x + offset.y*offset.y );
872         offset *= k;
873         Actor self = Self();
874         Source source(self, mPropertyPanDisplacement[mIndex]);
875
876         Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
877         Constraint originalCenterConstraint = Constraint::New<Vector2>( shaderOriginalCenterPropertyIndex ,
878                                                                         source,
879                                                                         OriginalCenterConstraint( mOriginalCenter, offset ));
880         mTurnEffect[mIndex].ApplyConstraint( originalCenterConstraint );
881
882         Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
883         Constraint currentCenterConstraint = Constraint::New<Vector2>( shaderCurrentCenterPropertyIndex,
884                                                                        Source(self, mPropertyCurrentCenter[mIndex]),
885                                                                        Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex),
886                                                                        CurrentCenterConstraint(mPageSize.width));
887         mTurnEffect[mIndex].ApplyConstraint( currentCenterConstraint );
888
889         GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
890
891         float distance = offset.Length();
892         Constraint rotationConstraint = Constraint::New<Quaternion>( Actor::ROTATION,
893                                                                      Source( self, mPropertyPanDisplacement[mIndex] ),
894                                                                      RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
895         mPanActor.ApplyConstraint( rotationConstraint );
896
897         mConstraints = false;
898       }
899     }
900     else
901     {
902       if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
903       {
904         mPanActor.RemoveConstraints();
905         mTurnEffect[mIndex].RemoveConstraints();
906         mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
907         mConstraints = true;
908         mPanDisplacement = 0.f;
909       }
910
911       mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
912       mCurrentCenter = currentCenter;
913       GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
914     }
915   }
916 }
917
918 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
919 {
920   // Guard against destruction during signal emission
921   Toolkit::PageTurnView handle( GetOwner() );
922
923   if( !mPanActor )
924   {
925     if(!mIsAnimating[mIndex])
926     {
927       OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
928     }
929     return;
930   }
931
932   mPagePanFinishedSignal.Emit( handle );
933
934   ImageActor actor = mPanActor;
935   if(mPress)
936   {
937     if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
938     {
939       // update the pages here instead of in the TurnedOver callback function
940       // as new page is allowed to respond to the pan gesture before other pages finishing animation
941       if(mIsTurnBack[actor])
942       {
943         mCurrentPageIndex--;
944         RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
945         AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
946       }
947       else
948       {
949         mCurrentPageIndex++;
950         RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
951         AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
952       }
953       OrganizePageDepth();
954
955       // set up an animation to turn the page over
956       Actor self = Self();
957       float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
958       Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
959       animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
960                            width,AlphaFunctions::EaseOutSine33);
961       animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
962                            Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunctions::EaseOutSine33);
963       mAnimationActorPair[animation] = actor;
964       mAnimationIndexPair[animation] = mIndex;
965       animation.Play();
966       animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
967     }
968     else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
969     {
970       Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
971       animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
972                            mOriginalCenter, AlphaFunctions::Linear );
973       mAnimationActorPair[animation] = actor;
974       mAnimationIndexPair[animation] = mIndex;
975       animation.Play();
976       mIsSliding[mIndex] = true;
977       animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
978
979       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
980     }
981   }
982   else
983   {
984     // In portrait view, an outwards flick should turn the previous page back
985     // In landscape view, nothing to do
986     OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
987   }
988
989   mPageUpdated = true;
990 }
991
992 void PageTurnView::TurnedOver( Animation& animation )
993 {
994   ImageActor actor = mAnimationActorPair[animation];
995   mIsTurnBack[actor] = !mIsTurnBack[actor];
996   actor.RemoveConstraints();
997   mRootOnScreen.Add(actor);
998   int index = mAnimationIndexPair[animation];
999   mIsAnimating[index] = false;
1000   mTurnEffect[index].RemoveConstraints();
1001   mAnimationIndexPair.erase( animation );
1002   mAnimationActorPair.erase( animation );
1003
1004   SetSpineEffect( actor, mIsTurnBack[actor] );
1005
1006   // Guard against destruction during signal emission
1007   Toolkit::PageTurnView handle( GetOwner() );
1008   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1009 }
1010
1011 void PageTurnView::SliddenBack( Animation& animation )
1012 {
1013   ImageActor actor = mAnimationActorPair[animation];
1014   mRootOnScreen.Add(actor);
1015   int index = mAnimationIndexPair[animation];
1016   mIsSliding[index] = false;
1017   mIsAnimating[index] = false;
1018   mAnimationIndexPair.erase( animation );
1019   mAnimationActorPair.erase( animation );
1020
1021   SetSpineEffect( actor, mIsTurnBack[actor] );
1022
1023   // Guard against destruction during signal emission
1024   Toolkit::PageTurnView handle( GetOwner() );
1025   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1026 }
1027
1028 void PageTurnView::OrganizePageDepth()
1029 {
1030   for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1031   {
1032     if(mCurrentPageIndex+i < mTotalPageCount)
1033     {
1034       mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1035     }
1036     if( mCurrentPageIndex >= i + 1 )
1037     {
1038       mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1039     }
1040   }
1041 }
1042
1043 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
1044 {
1045   SetShaderEffectRecursively( actor, shaderEffect );
1046 }
1047
1048 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1049 {
1050   return mPageTurnStartedSignal;
1051 }
1052
1053 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1054 {
1055   return mPageTurnFinishedSignal;
1056 }
1057
1058 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1059 {
1060   return mPagePanStartedSignal;
1061 }
1062
1063 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1064 {
1065   return mPagePanFinishedSignal;
1066 }
1067
1068 } // namespace Internal
1069
1070 } // namespace Toolkit
1071
1072 } // namespace Dali