83006d41f8eca1fc0aff879fe3677e37b6d984fe
[platform/core/uifw/dali-toolkit.git] / optional / 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 // INTERNAL INCLUDES
22 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
23
24 using namespace Dali;
25
26 namespace //unnamed namespace
27 {
28 // To register type
29 TypeRegistration mType( typeid(Toolkit::PageTurnView), typeid(Toolkit::Control), NULL );
30
31 // default grid density for page turn effect, 10 pixels by 10 pixels
32 const float DEFAULT_GRID_DENSITY(10.0f);
33
34 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
35 const float MINIMUM_START_POSITION_RATIO(0.6f);
36
37 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
38 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
39
40 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
41 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
42
43 // duration of animation, shorter for faster speed
44 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
45 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
46
47 // the major&minor radius (in pixels) to form an ellipse shape
48 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
49 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
50
51 // constants for shadow casting
52 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
53 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
54
55 // constraints ////////////////////////////////////////////////////////////////
56 /**
57  * Original Center Constraint
58  *
59  * This constraint adjusts the original center property of the page turn shader effect
60  * based on the X-direction displacement of the pan gesture
61  */
62 struct OriginalCenterConstraint
63 {
64   OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
65   : mOldCenter( originalCenter )
66   {
67     mNewCenter = originalCenter + offset;
68     mDistance = offset.Length() * 0.5f;
69     mDirection = offset  / mDistance;
70   }
71
72   Vector2 operator()(const Vector2& current, const PropertyInput& panDisplacement)
73   {
74     float displacement = panDisplacement.GetFloat();
75
76     if( displacement < mDistance )
77     {
78       return mOldCenter + mDirection * displacement;
79     }
80     else
81     {
82       return mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
83     }
84   }
85
86   Vector2 mOldCenter;
87   Vector2 mNewCenter;
88   float mDistance;
89   Vector2 mDirection;
90 };
91
92 /**
93  * Rotation Constraint
94  *
95  * This constraint adjusts the rotation property of the page actor
96  * based on the X-direction displacement of the pan gesture
97  */
98 struct RotationConstraint
99 {
100   RotationConstraint( float distance, float pageWidth, bool isTurnBack )
101   : mDistance( distance*0.5f )
102   {
103     mStep = 1.f / pageWidth;
104     mSign = isTurnBack ? -1.0f : 1.0f;
105     mConst = isTurnBack ? -1.0f : 0.f;
106     mRotation = isTurnBack ? Quaternion( -Math::PI, Vector3::YAXIS ) : Quaternion( 0.f, Vector3::YAXIS );
107   }
108
109   Quaternion operator()( const Quaternion& current, const PropertyInput& panDisplacement )
110   {
111     float displacement = panDisplacement.GetFloat();
112     float angle;
113     if( displacement < mDistance)
114     {
115       return mRotation;
116     }
117     else
118     {
119       float coef = std::max(-1.0f, mStep*(mDistance-displacement));
120       angle = Math::PI*( mConst + mSign*coef );
121       return Quaternion( angle, Vector3::YAXIS );
122     }
123   }
124
125   float mDistance;
126   float mStep;
127   float mConst;
128   float mSign;
129   Quaternion mRotation;
130 };
131
132 /**
133  * Current Center Constraint
134  *
135  * This constraint adjusts the current center property of the page turn shader effect
136  * based on the pan position and the original center position
137  */
138 struct CurrentCenterConstraint
139 {
140   CurrentCenterConstraint( float pageWidth)
141   : mPageWidth( pageWidth )
142   {
143     mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
144   }
145
146   Vector2 operator()( const Vector2& current, const PropertyInput& center, const PropertyInput& originalCenter )
147   {
148     Vector2 centerPosition = center.GetVector2();
149     if( centerPosition.x > 0.f )
150     {
151       return Vector2( mThres+centerPosition.x*0.5f , centerPosition.y);
152     }
153     else
154     {
155       Vector2 centerOrigin = originalCenter.GetVector2();
156       Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
157       float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
158       // 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
159       if(coef < 0.025f)
160       {
161         coef = (coef+0.225f)/10.0f;
162       }
163       return centerOrigin - direction * coef;
164     }
165   }
166
167   float mPageWidth;
168   float mThres;
169 };
170
171 struct ShadowBlurStrengthConstraint
172 {
173   ShadowBlurStrengthConstraint( float thres )
174   : mThres( thres )
175   {}
176
177   float operator()( const float current,  const PropertyInput& currentCenter, const PropertyInput& originalCenter, const PropertyInput& panDisplacement)
178   {
179     float displacement = panDisplacement.GetFloat();
180     float blurStrength;
181     if( EqualsZero(displacement))
182     {
183       Vector2 cur = currentCenter.GetVector2();
184       Vector2 ori = originalCenter.GetVector2();
185       blurStrength =  5.f*(ori-cur).Length() / mThres;
186     }
187     else
188     {
189       blurStrength =  1.f - (displacement-2.f*mThres)/mThres;
190     }
191
192     blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
193     return blurStrength;
194   }
195
196   float mThres;
197 };
198
199 bool IsActorHittableFunction( Actor actor, Dali::HitTestAlgorithm::TraverseType type )
200 {
201   bool hittable = false;
202
203   switch (type)
204   {
205     case Dali::HitTestAlgorithm::CHECK_ACTOR:
206     {
207       // Check whether the actor is visible and not fully transparent.
208       Property::Index propertyActorHittable = actor.GetPropertyIndex(Toolkit::PageFactory::ACTOR_HITTABLE);
209       if( actor.IsSensitive()
210        && actor.IsVisible()
211        && actor.GetCurrentWorldColor().a > 0.01f// not FULLY_TRANSPARENT
212        && ( propertyActorHittable != Property::INVALID_INDEX &&
213             actor.GetProperty<bool>( propertyActorHittable ) ) )
214       {
215          hittable = true;
216       }
217       break;
218     }
219     case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
220     {
221       if( actor.IsSensitive() && actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible.
222       {
223         hittable = true;
224       }
225       break;
226     }
227     default:
228     {
229       break;
230     }
231   }
232
233   return hittable;
234 }
235
236 } //unnamed namespace
237
238 namespace Dali
239 {
240
241 namespace Toolkit
242 {
243
244 namespace Internal
245 {
246
247 // these several constants are also used in the derived classes
248 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
249 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
250 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
251 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
252
253 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
254 : Control( REQUIRES_TOUCH_EVENTS ),
255   mPageFactory( pageFactory ),
256   mPageSize( pageSize ),
257   mIsEditMode( false ),
258   mPanning( false ),
259   mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
260   mCurrentPageIndex( 0 ),
261   mIndex( 0 ),
262   mPress( false ),
263   mPageUpdated( true ),
264   mPageTurnStartedSignal(),
265   mPageTurnFinishedSignal(),
266   mPagePanStartedSignal(),
267   mPagePanFinishedSignal()
268 {
269   mPageActors.resize( NUMBER_OF_CACHED_PAGES );
270   mIsAnimating.resize( MAXIMUM_TURNING_NUM );
271   mIsSliding.resize( MAXIMUM_TURNING_NUM );
272   mTurnEffect.resize( MAXIMUM_TURNING_NUM );
273   mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
274   mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
275 }
276
277 PageTurnView::~PageTurnView()
278 {
279 }
280
281 void PageTurnView::OnInitialize()
282 {
283    // create the two book spine effect for static images, left and right side pages respectively
284   mSpineEffectFront = PageTurnBookSpineEffect::New();
285   mSpineEffectFront.SetIsBackImageVisible( false );
286   mSpineEffectFront.SetPageWidth( mPageSize.width );
287   mSpineEffectFront.SetShadowWidth( 0.f );
288   mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
289
290   mSpineEffectBack = PageTurnBookSpineEffect::New();
291   mSpineEffectBack.SetIsBackImageVisible( true );
292   mSpineEffectBack.SetPageWidth( mPageSize.width );
293   mSpineEffectBack.SetShadowWidth( 0.f );
294   mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
295
296   // create the page turn effect objects
297   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
298   {
299     mTurnEffect[i] = Toolkit::PageTurnEffect::New( false );
300     mTurnEffect[i].SetProperty( ShaderEffect::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
301     mTurnEffect[i].SetPageSize( mPageSize );
302     mTurnEffect[i].SetShadowWidth(0.f);
303     mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
304     mIsAnimating[i] = false;
305     mIsSliding[i] = false;
306     mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
307     mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
308   }
309
310   mTurningPageLayer = Layer::New();
311   mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
312   // Set control size and the parent origin of turningPageLayer
313   OnPageTurnViewInitialize();
314
315   mRootOnScreen = Actor::New();
316   mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION );
317   mRootOnScreen.SetSize( mControlSize );
318   Self().Add( mRootOnScreen );
319   mRootOnScreen.Add(mTurningPageLayer);
320
321   mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
322   mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded();
323   if( mNeedOffscreenRendering )
324   {
325     SetupRenderTasks();
326   }
327
328   // add pages to the scene, and set depth for the stacked pages
329   for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
330   {
331     AddPage( i );
332     if(mPageActors[i])
333     {
334       mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
335     }
336   }
337
338   // enable the pan gesture which is attached to the control
339   EnableGestureDetection(Gesture::Type(Gesture::Pan));
340
341   mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage);
342 }
343
344 void PageTurnView::SetupRenderTasks()
345 {
346   mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES );
347   mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES );
348   mRenderedPage.resize( NUMBER_OF_CACHED_PAGES );
349
350   mCameraActor = CameraActor::New(mControlSize);
351   mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
352   mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
353   mCameraActor.SetInheritScale( 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