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