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