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