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