2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.h>
22 #include <cstring> // for strcmp
23 #include <dali/public-api/animation/animation.h>
24 #include <dali/public-api/animation/constraint.h>
25 #include <dali/devel-api/events/hit-test-algorithm.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/devel-api/object/type-registry-helper.h>
28 #include <dali/public-api/render-tasks/render-task-list.h>
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>
36 namespace //Unnamed namespace
38 // default grid density for page turn effect, 10 pixels by 10 pixels
39 const float DEFAULT_GRID_DENSITY(10.0f);
41 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
42 const float MINIMUM_START_POSITION_RATIO(0.6f);
44 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
45 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
47 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
48 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
50 // duration of animation, shorter for faster speed
51 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
52 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
54 // the major&minor radius (in pixels) to form an ellipse shape
55 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
56 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
58 // constants for shadow casting
59 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
60 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
62 // constraints ////////////////////////////////////////////////////////////////
64 * Original Center Constraint
66 * This constraint adjusts the original center property of the page turn shader effect
67 * based on the X-direction displacement of the pan gesture
69 struct OriginalCenterConstraint
71 OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
72 : mOldCenter( originalCenter )
74 mNewCenter = originalCenter + offset;
75 mDistance = offset.Length() * 0.5f;
76 mDirection = offset / mDistance;
79 void operator()( Vector2& current, const PropertyInputContainer& inputs )
81 float displacement = inputs[0]->GetFloat();
83 if( displacement < mDistance )
85 current = mOldCenter + mDirection * displacement;
89 current = mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
100 * Rotation Constraint
102 * This constraint adjusts the rotation property of the page actor
103 * based on the X-direction displacement of the pan gesture
105 struct RotationConstraint
107 RotationConstraint( float distance, float pageWidth, bool isTurnBack )
108 : mDistance( distance*0.5f )
110 mStep = 1.f / pageWidth;
111 mSign = isTurnBack ? -1.0f : 1.0f;
112 mConst = isTurnBack ? -1.0f : 0.f;
113 mRotation = isTurnBack ? Quaternion( Radian( -Math::PI ), Vector3::YAXIS ) : Quaternion( Radian(0.f), Vector3::YAXIS );
116 void operator()( Quaternion& current, const PropertyInputContainer& inputs )
118 float displacement = inputs[0]->GetFloat();
119 if( displacement < mDistance)
125 float coef = std::max(-1.0f, mStep*(mDistance-displacement));
126 float angle = Math::PI * ( mConst + mSign * coef );
127 current = Quaternion( Radian( angle ), Vector3::YAXIS );
135 Quaternion mRotation;
139 * Current Center Constraint
141 * This constraint adjusts the current center property of the page turn shader effect
142 * based on the pan position and the original center position
144 struct CurrentCenterConstraint
146 CurrentCenterConstraint( float pageWidth )
147 : mPageWidth( pageWidth )
149 mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
152 void operator()( Vector2& current, const PropertyInputContainer& inputs )
154 const Vector2& centerPosition = inputs[0]->GetVector2();
155 if( centerPosition.x > 0.f )
157 current.x = mThres+centerPosition.x * 0.5f;
158 current.y = centerPosition.y;
162 const Vector2& centerOrigin = inputs[1]->GetVector2();
163 Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
164 float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
165 // Todo: when coef <= 0, the page is flat, slow down the last moment of the page stretch by 10 times to avoid a small bounce
168 coef = (coef+0.225f)/10.0f;
170 current = centerOrigin - direction * coef;
178 struct ShadowBlurStrengthConstraint
180 ShadowBlurStrengthConstraint( float thres )
184 void operator()( float& blurStrength, const PropertyInputContainer& inputs )
186 float displacement = inputs[2]->GetFloat();
187 if( EqualsZero(displacement))
189 const Vector2& cur = inputs[0]->GetVector2();
190 const Vector2& ori = inputs[1]->GetVector2();
191 blurStrength = 5.f*(ori-cur).Length() / mThres;
195 blurStrength = 1.f - (displacement-2.f*mThres)/mThres;
198 blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
204 } //unnamed namespace
220 // empty handle as we cannot create PageTurnView(but type registered for page turn signal)
224 // Setup properties, signals and actions using the type-registry.
225 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::PageTurnView, Toolkit::Control, Create );
227 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "page-size", VECTOR2, PAGE_SIZE )
228 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "current-page-id", INTEGER, CURRENT_PAGE_ID )
229 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "spine-shadow", VECTOR2, SPINE_SHADOW )
231 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "page-turn-started", SIGNAL_PAGE_TURN_STARTED )
232 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "page-turn-finished", SIGNAL_PAGE_TURN_FINISHED )
233 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "page-pan-started", SIGNAL_PAGE_PAN_STARTED )
234 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "page-pan-finished", SIGNAL_PAGE_PAN_FINISHED )
236 DALI_TYPE_REGISTRATION_END()
240 // these several constants are also used in the derived classes
241 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
242 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
243 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
244 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
246 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
247 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS ) ),
248 mPageFactory( pageFactory ),
249 mPageSize( pageSize ),
250 mTotalPageCount( 0 ),
252 mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
253 mCurrentPageIndex( 0 ),
254 mTurningPageIndex( 0 ),
257 mPageUpdated( true ),
258 mDistanceUpCorner( 0.f ),
259 mDistanceBottomCorner( 0.f ),
260 mPanDisplacement( 0.f ),
261 mConstraints( false ),
262 mPageTurnStartedSignal(),
263 mPageTurnFinishedSignal(),
264 mPagePanStartedSignal(),
265 mPagePanFinishedSignal()
267 mPageActors.resize( NUMBER_OF_CACHED_PAGES );
268 mIsAnimating.resize( MAXIMUM_TURNING_NUM );
269 mIsSliding.resize( MAXIMUM_TURNING_NUM );
270 mTurnEffect.resize( MAXIMUM_TURNING_NUM );
271 mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
272 mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
275 PageTurnView::~PageTurnView()
279 void PageTurnView::OnInitialize()
281 // create the two book spine effect for static images, left and right side pages respectively
282 mSpineEffectFront = CreatePageTurnBookSpineEffect();
283 mSpineEffectFront.SetUniform("uIsBackImageVisible", -1.f );
284 mSpineEffectFront.SetUniform("uPageWidth", mPageSize.width );
285 mSpineEffectFront.SetUniform("uShadowWidth", 0.f );
286 mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
288 mSpineEffectBack = CreatePageTurnBookSpineEffect();
289 mSpineEffectBack.SetUniform("uIsBackImageVisible", 1.f );
290 mSpineEffectBack.SetUniform("uPageWidth", mPageSize.width );
291 mSpineEffectBack.SetUniform("uShadowWidth", 0.f );
292 mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
294 // create the page turn effect objects
295 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
297 mTurnEffect[i] = CreatePageTurnEffect();
298 mTurnEffect[i].SetProperty( ShaderEffect::Property::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
299 mTurnEffect[i].SetUniform( "uPageSize", mPageSize );
300 mTurnEffect[i].SetUniform( "uShadowWidth", 0.f);
301 mTurnEffect[i].SetUniform( "uSpineShadowParameter", mSpineShadowParameter );
302 mIsAnimating[i] = false;
303 mIsSliding[i] = false;
304 mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
305 mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
308 mTurningPageLayer = Layer::New();
309 mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
310 mTurningPageLayer.SetBehavior(Layer::LAYER_3D);
312 // Set control size and the parent origin of page layers
313 OnPageTurnViewInitialize();
315 Self().Add(mTurningPageLayer);
317 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
318 // add pages to the scene, and set depth for the stacked pages
319 for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
324 mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
327 mPageActors[0].SetVisible(true);
329 // enable the pan gesture which is attached to the control
330 EnableGestureDetection(Gesture::Type(Gesture::Pan));
333 void PageTurnView::SetupShadowView()
335 mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
336 Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
337 mShadowView.SetParentOrigin( origin );
338 mShadowView.SetAnchorPoint( origin );
339 mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
340 mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
342 mShadowPlaneBackground = Actor::New();
343 mShadowPlaneBackground.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
344 mShadowPlaneBackground.SetSize( mControlSize );
345 Self().Add( mShadowPlaneBackground );
346 mShadowView.SetShadowPlaneBackground( mShadowPlaneBackground );
348 mPointLight = Actor::New();
349 mPointLight.SetAnchorPoint( origin );
350 mPointLight.SetParentOrigin( origin );
351 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
352 Self().Add( mPointLight );
353 mShadowView.SetPointLight( mPointLight );
355 mTurningPageLayer.Add( mShadowView );
356 mShadowView.Activate();
359 void PageTurnView::OnStageConnection( int depth )
362 mTurningPageLayer.Raise();
365 void PageTurnView::OnStageDisconnection()
369 mPointLight.Unparent();
370 mShadowPlaneBackground.Unparent();
371 mShadowView.Unparent();
374 // make sure the status of the control is updated correctly when the pan gesture is interrupted
379 Self().Add(mPanActor);
380 mIsAnimating[mIndex] = false;
381 mPanActor.RemoveConstraints();
382 mTurnEffect[mIndex].RemoveConstraints();
385 SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
389 void PageTurnView::SetPageSize( const Vector2& pageSize )
391 mPageSize = pageSize;
392 mSpineEffectFront.SetUniform("uPageWidth", mPageSize.width );
393 mSpineEffectBack.SetUniform("uPageWidth", mPageSize.width );
394 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
396 mTurnEffect[i].SetUniform( "uPageSize", mPageSize );
401 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
404 for( size_t i=0; i<mPageActors.size(); i++ )
408 mPageActors[i].SetSize( mPageSize );
409 if( mPageActors[i].GetChildCount()>0 )
411 mPageActors[i].GetChildAt(0).SetSize( mPageSize );
416 OnPageTurnViewInitialize();
418 if( mShadowPlaneBackground )
420 mShadowPlaneBackground.SetSize( mControlSize );
424 Vector2 PageTurnView::GetPageSize()
429 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
431 mSpineShadowParameter = spineShadowParameter;
433 // set spine shadow parameter to all the shader effects
434 mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
435 mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
436 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
438 mTurnEffect[i].SetUniform("uSpineShadowParameter", mSpineShadowParameter );
442 Vector2 PageTurnView::GetSpineShadowParameter()
444 return mSpineShadowParameter;
447 void PageTurnView::GoToPage( unsigned int pageId )
449 int pageIdx = Clamp( static_cast<int>(pageId), 0, mTotalPageCount-1);
451 if( mCurrentPageIndex == pageIdx )
456 // record the new current page index
457 mCurrentPageIndex = pageIdx;
459 // clear the old pages
460 for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
464 mPageActors[i].Unparent();
465 mPageActors[i].Reset();
469 // add the current page and the pages right before and after it
470 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
475 mPageActors[pageId%NUMBER_OF_CACHED_PAGES].SetVisible(true);
478 mPageActors[(pageId-1)%NUMBER_OF_CACHED_PAGES].SetVisible(true);
480 // set ordered depth to the stacked pages
484 unsigned int PageTurnView::GetCurrentPage()
486 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
487 return static_cast< unsigned int >( mCurrentPageIndex );
490 void PageTurnView::AddPage( int pageIndex )
492 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
494 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
495 ImageActor newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
496 DALI_ASSERT_ALWAYS( newPage );
498 newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
499 newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
500 newPage.SetSize( mPageSize );
501 Self().Add( newPage );
502 mPageActors[index] = newPage;
504 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
505 mIsTurnBack[ newPage ] = isLeftSide;
508 // new page is added to the left side, so need to rotate it 180 degrees
509 newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
513 newPage.SetShaderEffect(mSpineEffectFront);
516 newPage.SetVisible( false );
518 // For Portrait, nothing to do
519 // For Landscape, set spineEffectBack to the new effect if it is in the left side, and set properties to the back image actor if it exists
520 OnAddPage( newPage, isLeftSide );
524 void PageTurnView::RemovePage( int pageIndex )
526 if( pageIndex > -1 && pageIndex < mTotalPageCount)
528 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
529 mPageActors[index].Unparent();
530 mIsTurnBack.erase( mPageActors[index] );
531 mPageActors[index].Reset();
535 void PageTurnView::OnPan( const PanGesture& gesture )
537 // the pan gesture is attached to control itself instead of each page
538 switch( gesture.state )
540 case Gesture::Started:
543 // to find out whether the undergoing turning page number already reaches the maximum allowed
544 // and get one idle index when it is animatable
545 bool animatable = false;
546 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
548 if( !mIsAnimating[mIndex] )
553 if( mIsSliding[mIndex] )
559 mIndex = mIndex % MAXIMUM_TURNING_NUM;
562 if( mPageUpdated && animatable )
564 SetPanActor( gesture.position ); // determine which page actor is panned
565 if(mPanActor && mPanActor.GetParent() != Self()) // if the page is added to turning layer,it is undergoing an animation currently
569 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
577 case Gesture::Continuing:
579 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
582 case Gesture::Finished:
583 case Gesture::Cancelled:
586 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
590 case Gesture::Possible:
598 void PageTurnView::PanStarted( const Vector2& gesturePosition )
600 mPressDownPosition = gesturePosition;
607 mOriginalCenter = gesturePosition;
608 mTurnEffect[mIndex].SetUniform("uIsTurningBack", mIsTurnBack[ mPanActor] ? 1.f : -1.f);
610 mPageUpdated = false;
612 // Guard against destruction during signal emission
613 Toolkit::PageTurnView handle( GetOwner() );
614 mPagePanStartedSignal.Emit( handle );
617 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
624 // Guard against destruction during signal emission
625 Toolkit::PageTurnView handle( GetOwner() );
629 // when the touch down position is near the spine
630 // or when the panning goes outwards or some other position which would tear the paper in real situation
631 // we change the start position into the current panning position and update the shader parameters
632 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
633 || gesturePosition.x > mOriginalCenter.x-1.0f
634 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
635 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
637 mOriginalCenter = gesturePosition;
641 mDistanceUpCorner = mOriginalCenter.Length();
642 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
643 mShadowView.Add( mPanActor );
644 SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
645 mTurnEffect[mIndex].SetUniform("uOriginalCenter", mOriginalCenter );
646 mCurrentCenter = mOriginalCenter;
647 mTurnEffect[mIndex].SetUniform("uCurrentCenter", mCurrentCenter );
648 mPanDisplacement = 0.f;
651 mIsAnimating[mIndex] = true;
653 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), !mIsTurnBack[mPanActor] );
654 int id = mTurningPageIndex + (mIsTurnBack[mPanActor]? -1 : 1);
655 if( id >=0 && id < mTotalPageCount )
657 mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(true);
660 mShadowView.RemoveConstraints();
662 self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
664 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
665 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter")) );
666 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter")) );
667 shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
668 shadowBlurStrengthConstraint.Apply();
673 Vector2 currentCenter = gesturePosition;
675 // Test whether the new current center would tear the paper from the top pine in real situation
676 // we do not forbid this totally, which would restrict the panning gesture too much
677 // instead, set it to the nearest allowable position
678 float distanceUpCorner = currentCenter.Length();
679 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
680 if( distanceUpCorner > mDistanceUpCorner )
682 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
684 // would tear the paper from the bottom spine in real situation
685 if( distanceBottomCorner > mDistanceBottomCorner )
687 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
689 // If direction has a very high y component, reduce it.
690 Vector2 curveDirection = currentCenter - mOriginalCenter;
691 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
693 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
695 // If the vertical distance is high, reduce it
696 float yShift = currentCenter.y - mOriginalCenter.y;
697 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
699 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
702 // use contraints to control the page shape and rotation when the pan position is near the spine
703 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
705 // set the property values used by the constraints
706 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
707 Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
708 Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
710 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
711 // also set up the RotationConstraint to the page actor
715 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
716 if( currentCenter.y >= mOriginalCenter.y )
718 corner = Vector2( 1.1f*mPageSize.width, 0.f );
722 corner = mPageSize*1.1f;
725 Vector2 offset( currentCenter-mOriginalCenter );
726 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
727 /( offset.x*offset.x + offset.y*offset.y );
731 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter");
732 Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
733 originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
734 originalCenterConstraint.Apply();
736 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter");
737 Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
738 currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
739 currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
740 currentCenterConstraint.Apply();
742 PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
744 float distance = offset.Length();
745 Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
746 rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
747 rotationConstraint.Apply();
749 mConstraints = false;
754 if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
756 mPanActor.RemoveConstraints();
757 mTurnEffect[mIndex].RemoveConstraints();
758 mTurnEffect[mIndex].SetUniform("uOriginalCenter",mOriginalCenter );
760 mPanDisplacement = 0.f;
763 mTurnEffect[mIndex].SetUniform("uCurrentCenter", currentCenter );
764 mCurrentCenter = currentCenter;
765 PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
770 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
772 // Guard against destruction during signal emission
773 Toolkit::PageTurnView handle( GetOwner() );
777 if(!mIsAnimating[mIndex])
779 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
784 mPagePanFinishedSignal.Emit( handle );
786 ImageActor actor = mPanActor;
789 if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
791 // update the pages here instead of in the TurnedOver callback function
792 // as new page is allowed to respond to the pan gesture before other pages finishing animation
793 if(mIsTurnBack[actor])
796 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
797 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
802 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
803 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
807 // set up an animation to turn the page over
809 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
810 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
811 animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
812 width,AlphaFunction::EASE_OUT_SINE);
813 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
814 Vector2(-mPageSize.width*1.1f, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
815 mAnimationPageIdPair[animation] = mTurningPageIndex;
816 mAnimationIndexPair[animation] = mIndex;
818 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
820 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
822 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
823 animation.AnimateTo( Property( mTurnEffect[mIndex], "uCurrentCenter" ),
824 mOriginalCenter, AlphaFunction::LINEAR );
825 mAnimationPageIdPair[animation] = mTurningPageIndex;
826 mAnimationIndexPair[animation] = mIndex;
828 mIsSliding[mIndex] = true;
829 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
831 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), mIsTurnBack[actor] );
836 // In portrait view, an outwards flick should turn the previous page back
837 // In landscape view, nothing to do
838 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
844 void PageTurnView::TurnedOver( Animation& animation )
846 int pageId = mAnimationPageIdPair[animation];
847 ImageActor actor = mPageActors[pageId % NUMBER_OF_CACHED_PAGES];
848 mIsTurnBack[actor] = !mIsTurnBack[actor];
849 actor.RemoveConstraints();
851 int index = mAnimationIndexPair[animation];
852 mIsAnimating[index] = false;
853 mTurnEffect[index].RemoveConstraints();
854 mAnimationIndexPair.erase( animation );
855 mAnimationPageIdPair.erase( animation );
857 SetSpineEffect( actor, mIsTurnBack[actor] );
859 int id = pageId + (mIsTurnBack[actor]? -1 : 1);
860 if( id >=0 && id < mTotalPageCount )
862 mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(false);
865 OnTurnedOver( actor, mIsTurnBack[actor] );
867 // Guard against destruction during signal emission
868 Toolkit::PageTurnView handle( GetOwner() );
869 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mIsTurnBack[actor] );
872 void PageTurnView::SliddenBack( Animation& animation )
874 int pageId = mAnimationPageIdPair[animation];
875 ImageActor actor = mPageActors[pageId % NUMBER_OF_CACHED_PAGES];
877 int index = mAnimationIndexPair[animation];
878 mIsSliding[index] = false;
879 mIsAnimating[index] = false;
880 mAnimationIndexPair.erase( animation );
881 mAnimationPageIdPair.erase( animation );
883 SetSpineEffect( actor, mIsTurnBack[actor] );
885 int id = pageId + (mIsTurnBack[actor]? -1 : 1);
886 if( id >=0 && id < mTotalPageCount )
888 mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(false);
891 // Guard against destruction during signal emission
892 Toolkit::PageTurnView handle( GetOwner() );
893 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mIsTurnBack[actor] );
896 void PageTurnView::OrganizePageDepth()
898 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
900 if(mCurrentPageIndex+i < mTotalPageCount)
902 mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
904 if( mCurrentPageIndex >= i + 1 )
906 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
911 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
913 SetShaderEffectRecursively( actor, shaderEffect );
916 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
918 return mPageTurnStartedSignal;
921 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
923 return mPageTurnFinishedSignal;
926 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
928 return mPagePanStartedSignal;
931 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
933 return mPagePanFinishedSignal;
936 bool PageTurnView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
938 Dali::BaseHandle handle( object );
940 bool connected( true );
941 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( handle );
943 if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_STARTED ) )
945 pageTurnView.PageTurnStartedSignal().Connect( tracker, functor );
947 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_FINISHED ) )
949 pageTurnView.PageTurnFinishedSignal().Connect( tracker, functor );
951 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_STARTED ) )
953 pageTurnView.PagePanStartedSignal().Connect( tracker, functor );
955 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_FINISHED ) )
957 pageTurnView.PagePanFinishedSignal().Connect( tracker, functor );
961 // signalName does not match any signal
968 void PageTurnView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
970 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
974 PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
978 case Toolkit::PageTurnView::Property::PAGE_SIZE:
980 pageTurnViewImpl.SetPageSize( value.Get<Vector2>() );
983 case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
985 pageTurnViewImpl.GoToPage( value.Get<int>() );
988 case Toolkit::PageTurnView::Property::SPINE_SHADOW:
990 pageTurnViewImpl.SetSpineShadowParameter( value.Get<Vector2>() );
997 Property::Value PageTurnView::GetProperty( BaseObject* object, Property::Index index )
999 Property::Value value;
1001 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
1005 PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
1009 case Toolkit::PageTurnView::Property::PAGE_SIZE:
1011 value = pageTurnViewImpl.GetPageSize();
1014 case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1016 value = static_cast<int>( pageTurnViewImpl.GetCurrentPage() );
1019 case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1021 value = pageTurnViewImpl.GetSpineShadowParameter();
1029 } // namespace Internal
1031 } // namespace Toolkit