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