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