Merge "Moved TextureSet::SetImage() to separate devel-api module" 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) 2016 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/public-api/images/resource-image.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/public-api/object/type-registry-helper.h>
28 #include <dali/devel-api/images/texture-set-image.h>
29 #include <dali/integration-api/debug.h>
30
31 // INTERNAL INCLUDES
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 #include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
35
36 using namespace Dali;
37
38 namespace //Unnamed namespace
39 {
40 // broken image is loaded if there is no valid image provided for the page
41 const char * const BROKEN_IMAGE_URL( DALI_IMAGE_DIR "broken.png");
42
43 // names of shader property map
44 const char * const CUSTOM_SHADER( "shader" );
45 const char * const CUSTOM_VERTEX_SHADER( "vertexShader" );
46 const char * const CUSTOM_FRAGMENT_SHADER( "fragmentShader" );
47
48 // properties set on shader, these properties have the constant value in regardless of the page status
49 const char * const PROPERTY_SPINE_SHADOW ( "uSpineShadowParameter" ); // uniform for both spine and turn effect
50
51 // properties set on actor, the value of these properties varies depending on the page status
52 //    properties used in turn effect
53 const char * const PROPERTY_TURN_DIRECTION( "uIsTurningBack" ); // uniform
54 const char * const PROPERTY_COMMON_PARAMETERS( "uCommonParameters" ); //uniform
55
56 const char * const PROPERTY_PAN_DISPLACEMENT( "panDisplacement" );// property used to constrain the uniforms
57 const char * const PROPERTY_PAN_CENTER( "panCenter" );// property used to constrain the uniforms
58
59 // default grid density for page turn effect, 20 pixels by 20 pixels
60 const float DEFAULT_GRID_DENSITY(20.0f);
61
62 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
63 const float MINIMUM_START_POSITION_RATIO(0.6f);
64
65 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
66 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
67
68 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
69 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
70
71 // duration of animation, shorter for faster speed
72 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
73 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
74
75 // the major&minor radius (in pixels) to form an ellipse shape
76 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
77 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
78
79 // constants for shadow casting
80 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
81 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
82
83 // constraints ////////////////////////////////////////////////////////////////
84 /**
85  * Original Center Constraint
86  *
87  * This constraint adjusts the original center property of the page turn shader effect
88  * based on the X-direction displacement of the pan gesture
89  */
90 struct OriginalCenterConstraint
91 {
92   OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
93   : mOldCenter( originalCenter )
94   {
95     mNewCenter = originalCenter + offset;
96     mDistance = offset.Length() * 0.5f;
97     mDirection = offset  / mDistance;
98   }
99
100   void operator()( Vector2& current, const PropertyInputContainer& inputs )
101   {
102     float displacement = inputs[0]->GetFloat();
103
104     if( displacement < mDistance )
105     {
106       current = mOldCenter + mDirection * displacement;
107     }
108     else
109     {
110       current = mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
111     }
112   }
113
114   Vector2 mOldCenter;
115   Vector2 mNewCenter;
116   float mDistance;
117   Vector2 mDirection;
118 };
119
120 /**
121  * Rotation Constraint
122  *
123  * This constraint adjusts the rotation property of the page actor
124  * based on the X-direction displacement of the pan gesture
125  */
126 struct RotationConstraint
127 {
128   RotationConstraint( float distance, float pageWidth, bool isTurnBack )
129   : mDistance( distance*0.5f )
130   {
131     mStep = 1.f / pageWidth;
132     mSign = isTurnBack ? -1.0f : 1.0f;
133     mConst = isTurnBack ? -1.0f : 0.f;
134     mRotation = isTurnBack ? Quaternion( Radian( -Math::PI ), Vector3::YAXIS ) : Quaternion( Radian(0.f), Vector3::YAXIS );
135   }
136
137   void operator()( Quaternion& current, const PropertyInputContainer& inputs )
138   {
139     float displacement = inputs[0]->GetFloat();
140     if( displacement < mDistance)
141     {
142       current = mRotation;
143     }
144     else
145     {
146       float coef = std::max(-1.0f, mStep*(mDistance-displacement));
147       float angle = Math::PI * ( mConst + mSign * coef );
148       current = Quaternion( Radian( angle ), Vector3::YAXIS );
149     }
150   }
151
152   float mDistance;
153   float mStep;
154   float mConst;
155   float mSign;
156   Quaternion mRotation;
157 };
158
159 /**
160  * Current Center Constraint
161  *
162  * This constraint adjusts the current center property of the page turn shader effect
163  * based on the pan position and the original center position
164  */
165 struct CurrentCenterConstraint
166 {
167   CurrentCenterConstraint( float pageWidth )
168   : mPageWidth( pageWidth )
169   {
170     mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
171   }
172
173   void operator()( Vector2& current, const PropertyInputContainer& inputs )
174   {
175     const Vector2& centerPosition = inputs[0]->GetVector2();
176     if( centerPosition.x > 0.f )
177     {
178       current.x = mThres+centerPosition.x * 0.5f;
179       current.y = centerPosition.y;
180     }
181     else
182     {
183       const Vector2& centerOrigin = inputs[1]->GetVector2();
184       Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
185       float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
186       // when coef <= 0, the page is flat, slow down the last moment of the page stretch by 10 times to avoid a small bounce
187       if(coef < 0.025f)
188       {
189         coef = (coef+0.225f)/10.0f;
190       }
191       current = centerOrigin - direction * coef;
192     }
193   }
194
195   float mPageWidth;
196   float mThres;
197 };
198
199 struct ShadowBlurStrengthConstraint
200 {
201   ShadowBlurStrengthConstraint( float thres )
202   : mThres( thres )
203   {}
204
205   void operator()( float& blurStrength,  const PropertyInputContainer& inputs )
206   {
207     float displacement = inputs[2]->GetFloat();
208     if( EqualsZero(displacement))
209     {
210       const Vector2& cur = inputs[0]->GetVector2();
211       const Vector2& ori = inputs[1]->GetVector2();
212       blurStrength =  5.f*(ori-cur).Length() / mThres;
213     }
214     else
215     {
216       blurStrength =  1.f - (displacement-2.f*mThres)/mThres;
217     }
218
219     blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
220   }
221
222   float mThres;
223 };
224
225 } //unnamed namespace
226
227 namespace Dali
228 {
229
230 namespace Toolkit
231 {
232
233 namespace Internal
234 {
235
236 namespace
237 {
238
239 BaseHandle Create()
240 {
241   // empty handle as we cannot create PageTurnView(but type registered for page turn signal)
242   return BaseHandle();
243 }
244
245 // Setup properties, signals and actions using the type-registry.
246 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::PageTurnView, Toolkit::Control, Create );
247
248 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "pageSize",        VECTOR2, PAGE_SIZE )
249 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "currentPageId",   INTEGER, CURRENT_PAGE_ID )
250 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "spineShadow",     VECTOR2, SPINE_SHADOW )
251
252 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pageTurnStarted",    SIGNAL_PAGE_TURN_STARTED )
253 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pageTurnFinished",   SIGNAL_PAGE_TURN_FINISHED )
254 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pagePanStarted",     SIGNAL_PAGE_PAN_STARTED )
255 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pagePanFinished",    SIGNAL_PAGE_PAN_FINISHED )
256
257 DALI_TYPE_REGISTRATION_END()
258
259 }
260
261 // these several constants are also used in the derived classes
262 const char * const PageTurnView::PROPERTY_TEXTURE_WIDTH( "uTextureWidth" ); // uniform name
263 const char * const PageTurnView::PROPERTY_ORIGINAL_CENTER( "originalCenter" ); // property used to constrain the uniform
264 const char * const PageTurnView::PROPERTY_CURRENT_CENTER( "currentCenter" );// property used to constrain the uniform
265 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
266 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
267 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
268 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
269
270 PageTurnView::Page::Page()
271 : isTurnBack( false )
272 {
273   actor = Actor::New();
274   actor.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
275   actor.SetParentOrigin( ParentOrigin::CENTER_LEFT );
276   actor.SetVisible( false );
277
278   propertyPanDisplacement = actor.RegisterProperty( PROPERTY_PAN_DISPLACEMENT, 0.f );
279   propertyPanCenter = actor.RegisterProperty(PROPERTY_PAN_CENTER, Vector2::ZERO);
280
281   propertyOriginalCenter = actor.RegisterProperty(PROPERTY_ORIGINAL_CENTER, Vector2::ZERO);
282   propertyCurrentCenter = actor.RegisterProperty(PROPERTY_CURRENT_CENTER, Vector2::ZERO);
283   Matrix zeroMatrix(true);
284   actor.RegisterProperty(PROPERTY_COMMON_PARAMETERS, zeroMatrix);
285   propertyTurnDirection = actor.RegisterProperty(PROPERTY_TURN_DIRECTION, -1.f);
286 }
287
288 void PageTurnView::Page::SetImage( Image image  )
289 {
290   if( !textureSet )
291   {
292     textureSet = TextureSet::New();
293   }
294
295   TextureSetImage( textureSet, 0u, image );
296 }
297
298 void PageTurnView::Page::UseEffect(Shader newShader)
299 {
300   shader = newShader;
301   if( renderer )
302   {
303     renderer.SetShader( shader );
304   }
305 }
306
307 void PageTurnView::Page::UseEffect(Shader newShader, Geometry geometry)
308 {
309   UseEffect( newShader );
310
311   if( !renderer )
312   {
313     renderer = Renderer::New( geometry, shader );
314
315     if( !textureSet )
316     {
317       textureSet = TextureSet::New();
318     }
319
320     renderer.SetTextures( textureSet );
321     renderer.SetProperty( Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON );
322     actor.AddRenderer( renderer );
323   }
324 }
325
326 void PageTurnView::Page::ChangeTurnDirection()
327 {
328   isTurnBack = !isTurnBack;
329   actor.SetProperty( propertyTurnDirection, isTurnBack ? 1.f : -1.f );
330 }
331
332 void PageTurnView::Page::SetPanDisplacement(float value)
333 {
334  actor.SetProperty( propertyPanDisplacement, value );
335 }
336
337 void PageTurnView::Page::SetPanCenter( const Vector2& value )
338 {
339   actor.SetProperty( propertyPanCenter, value );
340 }
341
342 void PageTurnView::Page::SetOriginalCenter( const Vector2& value )
343 {
344   actor.SetProperty( propertyOriginalCenter, value );
345 }
346
347 void PageTurnView::Page::SetCurrentCenter( const Vector2& value )
348 {
349   actor.SetProperty( propertyCurrentCenter, value );
350 }
351
352 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
353 : Control( ControlBehaviour( ACTOR_BEHAVIOUR_NONE ) ),
354   mPageFactory( &pageFactory ),
355   mPageSize( pageSize ),
356   mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
357   mDistanceUpCorner( 0.f ),
358   mDistanceBottomCorner( 0.f ),
359   mPanDisplacement( 0.f ),
360   mTotalPageCount( 0 ),
361   mCurrentPageIndex( 0 ),
362   mTurningPageIndex( 0 ),
363   mIndex( 0 ),
364   mSlidingCount( 0 ),
365   mAnimatingCount( 0 ),
366   mConstraints( false ),
367   mPress( false ),
368   mPageUpdated( true ),
369   mPageTurnStartedSignal(),
370   mPageTurnFinishedSignal(),
371   mPagePanStartedSignal(),
372   mPagePanFinishedSignal()
373 {
374 }
375
376 PageTurnView::~PageTurnView()
377 {
378 }
379
380 void PageTurnView::OnInitialize()
381 {
382    // create the book spine effect for static pages
383   Property::Map spineEffectMap = CreatePageTurnBookSpineEffect();
384   mSpineEffectShader = CreateShader( spineEffectMap );
385   mSpineEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter );
386   // create the turn effect for turning pages
387   Property::Map turnEffectMap = CreatePageTurnEffect();
388   mTurnEffectShader = CreateShader( turnEffectMap );
389   mTurnEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter );
390
391   // create the grid geometry for pages
392   uint16_t width = static_cast<uint16_t>(mPageSize.width / DEFAULT_GRID_DENSITY + 0.5f);
393   uint16_t height = static_cast<uint16_t>(mPageSize.height / DEFAULT_GRID_DENSITY + 0.5f);
394   mGeometry = RendererFactoryCache::CreateGridGeometry( Uint16Pair( width, height ) );
395
396   mPages.reserve( NUMBER_OF_CACHED_PAGES );
397   for( int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
398   {
399     mPages.push_back( Page() );
400     mPages[i].actor.SetSize( mPageSize );
401     Self().Add( mPages[i].actor );
402   }
403
404   // create the layer for turning images
405   mTurningPageLayer = Layer::New();
406   mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
407   mTurningPageLayer.SetBehavior(Layer::LAYER_3D);
408   mTurningPageLayer.Raise();
409
410   // Set control size and the parent origin of page layers
411   OnPageTurnViewInitialize();
412
413   Self().Add(mTurningPageLayer);
414
415   mTotalPageCount = static_cast<int>( mPageFactory->GetNumberOfPages() );
416   // add pages to the scene, and set depth for the stacked pages
417   for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
418   {
419     AddPage( i );
420     mPages[i].actor.SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
421   }
422   mPages[0].actor.SetVisible(true);
423
424   // enable the pan gesture which is attached to the control
425   EnableGestureDetection(Gesture::Type(Gesture::Pan));
426 }
427
428 Shader PageTurnView::CreateShader( const Property::Map& shaderMap )
429 {
430   Shader shader;
431   Property::Value* shaderValue = shaderMap.Find( CUSTOM_SHADER );
432   Property::Map shaderSource;
433   if( shaderValue && shaderValue->Get( shaderSource ) )
434   {
435     std::string vertexShader;
436     Property::Value* vertexShaderValue = shaderSource.Find( CUSTOM_VERTEX_SHADER );
437     if( !vertexShaderValue || !vertexShaderValue->Get( vertexShader ) )
438     {
439       DALI_LOG_ERROR("PageTurnView::CreateShader failed: vertex shader source is not available.\n");
440     }
441     std::string fragmentShader;
442     Property::Value* fragmentShaderValue = shaderSource.Find( CUSTOM_FRAGMENT_SHADER );
443     if( !fragmentShaderValue || !fragmentShaderValue->Get( fragmentShader ) )
444     {
445       DALI_LOG_ERROR("PageTurnView::CreateShader failed: fragment shader source is not available.\n");
446     }
447     shader = Shader::New( vertexShader, fragmentShader );
448   }
449   else
450   {
451     DALI_LOG_ERROR("PageTurnView::CreateShader failed: shader source is not available.\n");
452   }
453
454   return shader;
455 }
456
457 void PageTurnView::SetupShadowView()
458 {
459   mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
460   Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
461   mShadowView.SetParentOrigin( origin );
462   mShadowView.SetAnchorPoint( origin );
463   mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
464   mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
465
466   mShadowPlaneBackground = Actor::New();
467   mShadowPlaneBackground.SetParentOrigin( ParentOrigin::CENTER );
468   mShadowPlaneBackground.SetSize( mControlSize );
469   Self().Add( mShadowPlaneBackground );
470   mShadowView.SetShadowPlaneBackground( mShadowPlaneBackground );
471
472   mPointLight = Actor::New();
473   mPointLight.SetAnchorPoint( origin );
474   mPointLight.SetParentOrigin( origin );
475   mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
476   Self().Add( mPointLight );
477   mShadowView.SetPointLight( mPointLight );
478
479   mTurningPageLayer.Add( mShadowView );
480   mShadowView.Activate();
481 }
482
483 void PageTurnView::OnStageConnection( int depth )
484 {
485   Control::OnStageConnection( depth );
486
487   SetupShadowView();
488 }
489
490 void PageTurnView::OnStageDisconnection()
491 {
492   if(mShadowView)
493   {
494     mShadowView.RemoveConstraints();
495     mPointLight.Unparent();
496     mShadowPlaneBackground.Unparent();
497     mShadowView.Unparent();
498   }
499
500   // make sure the status of the control is updated correctly when the pan gesture is interrupted
501   StopTurning();
502
503   Control::OnStageDisconnection();
504 }
505
506 void PageTurnView::SetPageSize( const Vector2& pageSize )
507 {
508   mPageSize = pageSize;
509
510   if( mPointLight )
511   {
512     mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
513   }
514
515   for( size_t i=0; i<mPages.size(); i++ )
516   {
517     mPages[i].actor.SetSize( mPageSize );
518   }
519
520   OnPageTurnViewInitialize();
521
522   if( mShadowPlaneBackground )
523   {
524     mShadowPlaneBackground.SetSize( mControlSize );
525   }
526 }
527
528 Vector2 PageTurnView::GetPageSize()
529 {
530   return mPageSize;
531 }
532
533 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
534 {
535   mSpineShadowParameter = spineShadowParameter;
536
537   // set spine shadow parameter to all the shader effects
538   mSpineEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter );
539   mTurnEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter );
540 }
541
542 Vector2 PageTurnView::GetSpineShadowParameter()
543 {
544   return mSpineShadowParameter;
545 }
546
547 void PageTurnView::GoToPage( unsigned int pageId )
548 {
549   int pageIdx = Clamp( static_cast<int>(pageId), 0, mTotalPageCount-1);
550
551   if( mCurrentPageIndex == pageIdx  )
552   {
553     return;
554   }
555
556   // if any animation ongoing, stop it.
557   StopTurning();
558
559   // record the new current page index
560   mCurrentPageIndex = pageIdx;
561
562
563   // add the current page and the pages right before and after it
564   for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
565   {
566     AddPage( i );
567   }
568
569   mPages[pageId%NUMBER_OF_CACHED_PAGES].actor.SetVisible(true);
570   if( pageId > 0 )
571   {
572     mPages[(pageId-1)%NUMBER_OF_CACHED_PAGES].actor.SetVisible(true);
573   }
574   // set ordered depth to the stacked pages
575   OrganizePageDepth();
576 }
577
578 unsigned int PageTurnView::GetCurrentPage()
579 {
580   DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
581   return static_cast< unsigned int >( mCurrentPageIndex );
582 }
583
584 void PageTurnView::AddPage( int pageIndex )
585 {
586   if(pageIndex > -1  && pageIndex < mTotalPageCount) // whether the page is available from the page factory
587   {
588     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
589
590     Image newPageImage;
591     newPageImage = mPageFactory->NewPage( pageIndex );
592
593     if( !newPageImage ) // load the broken image
594     {
595       newPageImage = ResourceImage::New( BROKEN_IMAGE_URL );
596     }
597
598     bool isLeftSide = ( pageIndex < mCurrentPageIndex );
599     if( mPages[index].isTurnBack != isLeftSide )
600     {
601       mPages[index].ChangeTurnDirection();
602     }
603
604     float degree = isLeftSide ? 180.f :0.f;
605     mPages[index].actor.SetOrientation( Degree( degree ), Vector3::YAXIS );
606     mPages[index].actor.SetVisible( false );
607     mPages[index].UseEffect( mSpineEffectShader, mGeometry );
608     mPages[index].SetImage( newPageImage );
609
610     // For Portrait, nothing to do
611     // For Landscape, set the parent origin to CENTER
612      OnAddPage( mPages[index].actor, isLeftSide );
613   }
614 }
615
616 void PageTurnView::RemovePage( int pageIndex )
617 {
618   if( pageIndex > -1 && pageIndex < mTotalPageCount)
619   {
620     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
621     mPages[index].actor.SetVisible(false);
622   }
623 }
624
625 void PageTurnView::OnPan( const PanGesture& gesture )
626 {
627   // the pan gesture is attached to control itself instead of each page
628   switch( gesture.state )
629   {
630     case Gesture::Started:
631     {
632       // check whether the undergoing turning page number already reaches the maximum allowed
633       if( mPageUpdated && mAnimatingCount< MAXIMUM_TURNING_NUM && mSlidingCount < 1 )
634       {
635         SetPanActor( gesture.position ); // determine which page actor is panned
636         if( mTurningPageIndex != -1 && mPages[mTurningPageIndex % NUMBER_OF_CACHED_PAGES].actor.GetParent() != Self()) // if the page is added to turning layer,it is undergoing an animation currently
637         {
638           mTurningPageIndex = -1;
639         }
640         PanStarted( SetPanPosition( gesture.position ) );  // pass in the pan position in the local page coordinate
641       }
642       else
643       {
644         mTurningPageIndex = -1;
645       }
646       break;
647     }
648     case Gesture::Continuing:
649     {
650       PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
651       break;
652     }
653     case Gesture::Finished:
654     case Gesture::Cancelled:
655     {
656       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
657       break;
658     }
659     case Gesture::Clear:
660     case Gesture::Possible:
661     default:
662     {
663       break;
664     }
665   }
666 }
667
668 void PageTurnView::PanStarted( const Vector2& gesturePosition )
669 {
670   mPressDownPosition = gesturePosition;
671
672   if( mTurningPageIndex == -1 )
673   {
674     return;
675   }
676
677   mIndex = mTurningPageIndex % NUMBER_OF_CACHED_PAGES;
678
679   mOriginalCenter = gesturePosition;
680   mPress = false;
681   mPageUpdated = false;
682
683   // Guard against destruction during signal emission
684   Toolkit::PageTurnView handle( GetOwner() );
685   mPagePanStartedSignal.Emit( handle );
686 }
687
688 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
689 {
690   if( mTurningPageIndex == -1  )
691   {
692     return;
693   }
694
695   // Guard against destruction during signal emission
696   Toolkit::PageTurnView handle( GetOwner() );
697
698   if(!mPress)
699   {
700     // when the touch down position is near the spine
701     // or when the panning goes outwards or some other position which would tear the paper in real situation
702     // we change the start position into the current panning position and update the shader parameters
703     if( mOriginalCenter.x <  mPageSize.width*MINIMUM_START_POSITION_RATIO
704         || gesturePosition.x > mOriginalCenter.x-1.0f
705         || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
706              ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
707     {
708       mOriginalCenter = gesturePosition;
709     }
710     else
711     {
712       mDistanceUpCorner = mOriginalCenter.Length();
713       mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
714       mShadowView.Add( mPages[mIndex].actor );
715       mPages[mIndex].UseEffect( mTurnEffectShader );
716       mPages[mIndex].SetOriginalCenter( mOriginalCenter );
717       mCurrentCenter = mOriginalCenter;
718       mPages[mIndex].SetCurrentCenter( mCurrentCenter );
719       mPanDisplacement = 0.f;
720       mConstraints = false;
721       mPress = true;
722       mAnimatingCount++;
723
724       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), !mPages[mIndex].isTurnBack );
725       int id = mTurningPageIndex + (mPages[mIndex].isTurnBack ? -1 : 1);
726       if( id >=0 && id < mTotalPageCount )
727       {
728         mPages[id%NUMBER_OF_CACHED_PAGES].actor.SetVisible(true);
729       }
730
731       mShadowView.RemoveConstraints();
732       Actor self = Self();
733       mPages[mIndex].SetPanDisplacement( 0.f );
734
735       Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
736       shadowBlurStrengthConstraint.AddSource( Source(mPages[mIndex].actor,  mPages[mIndex].propertyCurrentCenter) );
737       shadowBlurStrengthConstraint.AddSource( Source(mPages[mIndex].actor,  mPages[mIndex].propertyOriginalCenter) );
738       shadowBlurStrengthConstraint.AddSource( Source(mPages[mIndex].actor,  mPages[mIndex].propertyPanDisplacement) );
739       shadowBlurStrengthConstraint.Apply();
740     }
741   }
742   else
743   {
744     Vector2 currentCenter = gesturePosition;
745
746     // Test whether the new current center would tear the paper from the top pine in real situation
747     // we do not forbid this totally, which would restrict the panning gesture too much
748     // instead, set it to the nearest allowable position
749     float distanceUpCorner = currentCenter.Length();
750     float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
751     if( distanceUpCorner > mDistanceUpCorner )
752     {
753       currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
754     }
755     // would tear the paper from the bottom spine in real situation
756     if( distanceBottomCorner > mDistanceBottomCorner )
757     {
758       currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
759     }
760     // If direction has a very high y component, reduce it.
761     Vector2 curveDirection = currentCenter - mOriginalCenter;
762     if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
763     {
764       currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
765     }
766     // If the vertical distance is high, reduce it
767     float yShift = currentCenter.y - mOriginalCenter.y;
768     if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
769     {
770       currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
771     }
772
773     // use contraints to control the page shape and rotation when the pan position is near the spine
774     if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
775     {
776       // set the property values used by the constraints
777       mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
778       mPages[mIndex].SetPanDisplacement( mPanDisplacement );
779       mPages[mIndex].SetPanCenter( currentCenter );
780
781       // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
782       // also set up the RotationConstraint to the page actor
783       if( !mConstraints )
784       {
785         Vector2 corner;
786         // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
787         if( currentCenter.y >= mOriginalCenter.y )
788         {
789           corner = Vector2( 1.1f*mPageSize.width, 0.f );
790         }
791         else
792         {
793           corner = mPageSize*1.1f;
794         }
795
796         Vector2 offset( currentCenter-mOriginalCenter );
797         float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
798                    /( offset.x*offset.x + offset.y*offset.y );
799         offset *= k;
800         Actor self = Self();
801
802         Constraint originalCenterConstraint = Constraint::New<Vector2>( mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter, OriginalCenterConstraint( mOriginalCenter, offset ));
803         originalCenterConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement ) );
804         originalCenterConstraint.Apply();
805
806         Constraint currentCenterConstraint = Constraint::New<Vector2>( mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter, CurrentCenterConstraint(mPageSize.width));
807         currentCenterConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyPanCenter ) );
808         currentCenterConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter ) );
809         currentCenterConstraint.Apply();
810
811         PageTurnApplyInternalConstraint( mPages[mIndex].actor, mPageSize.height );
812
813         float distance = offset.Length();
814         Constraint rotationConstraint = Constraint::New<Quaternion>( mPages[mIndex].actor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mPages[mIndex].isTurnBack));
815         rotationConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement ) );
816         rotationConstraint.Apply();
817
818         mConstraints = true;
819       }
820     }
821     else
822     {
823       if(mConstraints) // remove the constraint is the pan position move back to far away from the spine
824       {
825         mPages[mIndex].actor.RemoveConstraints();
826         mPages[mIndex].SetOriginalCenter(mOriginalCenter );
827         mConstraints = false;
828         mPanDisplacement = 0.f;
829       }
830
831       mPages[mIndex].SetCurrentCenter( currentCenter );
832       mCurrentCenter = currentCenter;
833       PageTurnApplyInternalConstraint(mPages[mIndex].actor, mPageSize.height );
834     }
835   }
836 }
837
838 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
839 {
840   // Guard against destruction during signal emission
841   Toolkit::PageTurnView handle( GetOwner() );
842
843   if( mTurningPageIndex == -1  )
844   {
845     if( mAnimatingCount< MAXIMUM_TURNING_NUM && mSlidingCount < 1)
846     {
847       OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
848     }
849
850     return;
851   }
852
853   mPagePanFinishedSignal.Emit( handle );
854
855   if(mPress)
856   {
857     if(mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
858     {
859       // update the pages here instead of in the TurnedOver callback function
860       // as new page is allowed to respond to the pan gesture before other pages finishing animation
861       if(mPages[mIndex].isTurnBack)
862       {
863         mCurrentPageIndex--;
864         RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
865         AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
866       }
867       else
868       {
869         mCurrentPageIndex++;
870         RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
871         AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
872       }
873       OrganizePageDepth();
874
875       // set up an animation to turn the page over
876       float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
877       Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
878       animation.AnimateTo( Property(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement),
879                            width,AlphaFunction::EASE_OUT_SINE);
880       animation.AnimateTo( Property(mPages[mIndex].actor, mPages[mIndex].propertyPanCenter),
881                            Vector2(-mPageSize.width*1.1f, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
882       mAnimationPageIdPair[animation] = mTurningPageIndex;
883       animation.Play();
884       animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
885     }
886     else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
887     {
888       Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
889       animation.AnimateTo( Property( mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter ),
890                            mOriginalCenter, AlphaFunction::LINEAR );
891       mAnimationPageIdPair[animation] = mTurningPageIndex;
892       animation.Play();
893       mSlidingCount++;
894       animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
895
896       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), mPages[mIndex].isTurnBack );
897     }
898   }
899   else
900   {
901     // In portrait view, an outwards flick should turn the previous page back
902     // In landscape view, nothing to do
903     OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
904   }
905   mPageUpdated = true;
906 }
907
908 void PageTurnView::TurnedOver( Animation& animation )
909 {
910   int pageId = mAnimationPageIdPair[animation];
911   int index = pageId%NUMBER_OF_CACHED_PAGES;
912
913   mPages[index].ChangeTurnDirection();
914   mPages[index].actor.RemoveConstraints();
915   Self().Add(mPages[index].actor);
916   mAnimatingCount--;
917   mAnimationPageIdPair.erase( animation );
918
919   float degree = mPages[index].isTurnBack ? 180.f : 0.f;
920   mPages[index].actor.SetOrientation( Degree(degree), Vector3::YAXIS );
921   mPages[index].UseEffect( mSpineEffectShader );
922
923   int id = pageId + (mPages[index].isTurnBack ? -1 : 1);
924   if( id >=0 && id < mTotalPageCount )
925   {
926     mPages[id%NUMBER_OF_CACHED_PAGES].actor.SetVisible(false);
927   }
928
929   OnTurnedOver( mPages[index].actor, mPages[index].isTurnBack );
930
931   // Guard against destruction during signal emission
932   Toolkit::PageTurnView handle( GetOwner() );
933   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mPages[index].isTurnBack );
934 }
935
936 void PageTurnView::SliddenBack( Animation& animation )
937 {
938   int pageId = mAnimationPageIdPair[animation];
939   int index = pageId%NUMBER_OF_CACHED_PAGES;
940   Self().Add(mPages[index].actor);
941   mSlidingCount--;
942   mAnimatingCount--;
943   mAnimationPageIdPair.erase( animation );
944
945   mPages[index].UseEffect( mSpineEffectShader );
946
947   int id = pageId + (mPages[index].isTurnBack ? -1 : 1);
948   if( id >=0 && id < mTotalPageCount )
949   {
950     mPages[id%NUMBER_OF_CACHED_PAGES].actor.SetVisible(false);
951   }
952
953   // Guard against destruction during signal emission
954   Toolkit::PageTurnView handle( GetOwner() );
955   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mPages[index].isTurnBack );
956 }
957
958 void PageTurnView::OrganizePageDepth()
959 {
960   for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
961   {
962     if(mCurrentPageIndex+i < mTotalPageCount)
963     {
964       mPages[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].actor.SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
965     }
966     if( mCurrentPageIndex >= i + 1 )
967     {
968       mPages[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].actor.SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
969     }
970   }
971 }
972
973 void PageTurnView::StopTurning()
974 {
975   mAnimatingCount = 0;
976   mSlidingCount = 0;
977
978   if( !mPageUpdated )
979   {
980     int index = mTurningPageIndex % NUMBER_OF_CACHED_PAGES;
981     Self().Add( mPages[ index ].actor );
982     mPages[ index ].actor.RemoveConstraints();
983     mPages[ index ].UseEffect( mSpineEffectShader );
984     float degree = mTurningPageIndex==mCurrentPageIndex ? 0.f :180.f;
985     mPages[index].actor.SetOrientation( Degree(degree), Vector3::YAXIS );
986     mPageUpdated = true;
987   }
988
989   if( !mAnimationPageIdPair.empty() )
990   {
991     for (std::map<Animation,int>::iterator it=mAnimationPageIdPair.begin(); it!=mAnimationPageIdPair.end(); ++it)
992     {
993       static_cast<Animation>(it->first).SetCurrentProgress( 1.f );
994     }
995   }
996 }
997
998 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
999 {
1000   return mPageTurnStartedSignal;
1001 }
1002
1003 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1004 {
1005   return mPageTurnFinishedSignal;
1006 }
1007
1008 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1009 {
1010   return mPagePanStartedSignal;
1011 }
1012
1013 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1014 {
1015   return mPagePanFinishedSignal;
1016 }
1017
1018 bool PageTurnView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1019 {
1020   Dali::BaseHandle handle( object );
1021
1022   bool connected( true );
1023   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( handle );
1024
1025   if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_STARTED ) )
1026   {
1027     pageTurnView.PageTurnStartedSignal().Connect( tracker, functor );
1028   }
1029   else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_FINISHED ) )
1030   {
1031     pageTurnView.PageTurnFinishedSignal().Connect( tracker, functor );
1032   }
1033   else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_STARTED ) )
1034   {
1035     pageTurnView.PagePanStartedSignal().Connect( tracker, functor );
1036   }
1037   else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_FINISHED ) )
1038   {
1039     pageTurnView.PagePanFinishedSignal().Connect( tracker, functor );
1040   }
1041   else
1042   {
1043     // signalName does not match any signal
1044     connected = false;
1045   }
1046
1047   return connected;
1048 }
1049
1050 void PageTurnView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
1051 {
1052   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
1053
1054   if( pageTurnView )
1055   {
1056     PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
1057
1058     switch( index )
1059     {
1060       case Toolkit::PageTurnView::Property::PAGE_SIZE:
1061       {
1062         pageTurnViewImpl.SetPageSize( value.Get<Vector2>() );
1063         break;
1064       }
1065       case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1066       {
1067         pageTurnViewImpl.GoToPage( value.Get<int>() );
1068         break;
1069       }
1070       case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1071       {
1072         pageTurnViewImpl.SetSpineShadowParameter( value.Get<Vector2>() );
1073         break;
1074       }
1075     }
1076   }
1077 }
1078
1079 Property::Value PageTurnView::GetProperty( BaseObject* object, Property::Index index )
1080 {
1081   Property::Value value;
1082
1083   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
1084
1085   if( pageTurnView )
1086   {
1087     PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
1088
1089     switch( index )
1090     {
1091       case Toolkit::PageTurnView::Property::PAGE_SIZE:
1092       {
1093         value = pageTurnViewImpl.GetPageSize();
1094         break;
1095       }
1096       case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1097       {
1098         value = static_cast<int>( pageTurnViewImpl.GetCurrentPage() );
1099         break;
1100       }
1101       case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1102       {
1103         value = pageTurnViewImpl.GetSpineShadowParameter();
1104         break;
1105       }
1106     }
1107   }
1108   return value;
1109 }
1110
1111 } // namespace Internal
1112
1113 } // namespace Toolkit
1114
1115 } // namespace Dali