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, "pageSize", VECTOR2, PAGE_SIZE )
228 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "currentPageId", INTEGER, CURRENT_PAGE_ID )
229 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "spineShadow", VECTOR2, SPINE_SHADOW )
231 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pageTurnStarted", SIGNAL_PAGE_TURN_STARTED )
232 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pageTurnFinished", SIGNAL_PAGE_TURN_FINISHED )
233 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pagePanStarted", SIGNAL_PAGE_PAN_STARTED )
234 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pagePanFinished", 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 )
361 Control::OnStageConnection( depth );
364 mTurningPageLayer.Raise();
367 void PageTurnView::OnStageDisconnection()
371 mPointLight.Unparent();
372 mShadowPlaneBackground.Unparent();
373 mShadowView.Unparent();
376 // make sure the status of the control is updated correctly when the pan gesture is interrupted
381 Self().Add(mPanActor);
382 mIsAnimating[mIndex] = false;
383 mPanActor.RemoveConstraints();
384 mTurnEffect[mIndex].RemoveConstraints();
387 SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
390 Control::OnStageDisconnection();
393 void PageTurnView::SetPageSize( const Vector2& pageSize )
395 mPageSize = pageSize;
396 mSpineEffectFront.SetUniform("uPageWidth", mPageSize.width );
397 mSpineEffectBack.SetUniform("uPageWidth", mPageSize.width );
398 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
400 mTurnEffect[i].SetUniform( "uPageSize", mPageSize );
405 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
408 for( size_t i=0; i<mPageActors.size(); i++ )
412 mPageActors[i].SetSize( mPageSize );
413 if( mPageActors[i].GetChildCount()>0 )
415 mPageActors[i].GetChildAt(0).SetSize( mPageSize );
420 OnPageTurnViewInitialize();
422 if( mShadowPlaneBackground )
424 mShadowPlaneBackground.SetSize( mControlSize );
428 Vector2 PageTurnView::GetPageSize()
433 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
435 mSpineShadowParameter = spineShadowParameter;
437 // set spine shadow parameter to all the shader effects
438 mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
439 mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
440 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
442 mTurnEffect[i].SetUniform("uSpineShadowParameter", mSpineShadowParameter );
446 Vector2 PageTurnView::GetSpineShadowParameter()
448 return mSpineShadowParameter;
451 void PageTurnView::GoToPage( unsigned int pageId )
453 int pageIdx = Clamp( static_cast<int>(pageId), 0, mTotalPageCount-1);
455 if( mCurrentPageIndex == pageIdx )
460 // record the new current page index
461 mCurrentPageIndex = pageIdx;
463 // clear the old pages
464 for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
468 mPageActors[i].Unparent();
469 mPageActors[i].Reset();
473 // add the current page and the pages right before and after it
474 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
479 mPageActors[pageId%NUMBER_OF_CACHED_PAGES].SetVisible(true);
482 mPageActors[(pageId-1)%NUMBER_OF_CACHED_PAGES].SetVisible(true);
484 // set ordered depth to the stacked pages
488 unsigned int PageTurnView::GetCurrentPage()
490 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
491 return static_cast< unsigned int >( mCurrentPageIndex );
494 void PageTurnView::AddPage( int pageIndex )
496 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
498 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
499 ImageActor newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
500 DALI_ASSERT_ALWAYS( newPage );
502 newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
503 newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
504 newPage.SetSize( mPageSize );
505 Self().Add( newPage );
506 mPageActors[index] = newPage;
508 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
509 mIsTurnBack[ newPage ] = isLeftSide;
512 // new page is added to the left side, so need to rotate it 180 degrees
513 newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
517 newPage.SetShaderEffect(mSpineEffectFront);
520 newPage.SetVisible( false );
522 // For Portrait, nothing to do
523 // 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
524 OnAddPage( newPage, isLeftSide );
528 void PageTurnView::RemovePage( int pageIndex )
530 if( pageIndex > -1 && pageIndex < mTotalPageCount)
532 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
533 mPageActors[index].Unparent();
534 mIsTurnBack.erase( mPageActors[index] );
535 mPageActors[index].Reset();
539 void PageTurnView::OnPan( const PanGesture& gesture )
541 // the pan gesture is attached to control itself instead of each page
542 switch( gesture.state )
544 case Gesture::Started:
547 // to find out whether the undergoing turning page number already reaches the maximum allowed
548 // and get one idle index when it is animatable
549 bool animatable = false;
550 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
552 if( !mIsAnimating[mIndex] )
557 if( mIsSliding[mIndex] )
563 mIndex = mIndex % MAXIMUM_TURNING_NUM;
566 if( mPageUpdated && animatable )
568 SetPanActor( gesture.position ); // determine which page actor is panned
569 if(mPanActor && mPanActor.GetParent() != Self()) // if the page is added to turning layer,it is undergoing an animation currently
573 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
581 case Gesture::Continuing:
583 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
586 case Gesture::Finished:
587 case Gesture::Cancelled:
590 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
594 case Gesture::Possible:
602 void PageTurnView::PanStarted( const Vector2& gesturePosition )
604 mPressDownPosition = gesturePosition;
611 mOriginalCenter = gesturePosition;
612 mTurnEffect[mIndex].SetUniform("uIsTurningBack", mIsTurnBack[ mPanActor] ? 1.f : -1.f);
614 mPageUpdated = false;
616 // Guard against destruction during signal emission
617 Toolkit::PageTurnView handle( GetOwner() );
618 mPagePanStartedSignal.Emit( handle );
621 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
628 // Guard against destruction during signal emission
629 Toolkit::PageTurnView handle( GetOwner() );
633 // when the touch down position is near the spine
634 // or when the panning goes outwards or some other position which would tear the paper in real situation
635 // we change the start position into the current panning position and update the shader parameters
636 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
637 || gesturePosition.x > mOriginalCenter.x-1.0f
638 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
639 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
641 mOriginalCenter = gesturePosition;
645 mDistanceUpCorner = mOriginalCenter.Length();
646 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
647 mShadowView.Add( mPanActor );
648 SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
649 mTurnEffect[mIndex].SetUniform("uOriginalCenter", mOriginalCenter );
650 mCurrentCenter = mOriginalCenter;
651 mTurnEffect[mIndex].SetUniform("uCurrentCenter", mCurrentCenter );
652 mPanDisplacement = 0.f;
655 mIsAnimating[mIndex] = true;
657 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), !mIsTurnBack[mPanActor] );
658 int id = mTurningPageIndex + (mIsTurnBack[mPanActor]? -1 : 1);
659 if( id >=0 && id < mTotalPageCount )
661 mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(true);
664 mShadowView.RemoveConstraints();
666 self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
668 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
669 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter")) );
670 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter")) );
671 shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
672 shadowBlurStrengthConstraint.Apply();
677 Vector2 currentCenter = gesturePosition;
679 // Test whether the new current center would tear the paper from the top pine in real situation
680 // we do not forbid this totally, which would restrict the panning gesture too much
681 // instead, set it to the nearest allowable position
682 float distanceUpCorner = currentCenter.Length();
683 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
684 if( distanceUpCorner > mDistanceUpCorner )
686 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
688 // would tear the paper from the bottom spine in real situation
689 if( distanceBottomCorner > mDistanceBottomCorner )
691 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
693 // If direction has a very high y component, reduce it.
694 Vector2 curveDirection = currentCenter - mOriginalCenter;
695 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
697 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
699 // If the vertical distance is high, reduce it
700 float yShift = currentCenter.y - mOriginalCenter.y;
701 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
703 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
706 // use contraints to control the page shape and rotation when the pan position is near the spine
707 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
709 // set the property values used by the constraints
710 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
711 Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
712 Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
714 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
715 // also set up the RotationConstraint to the page actor
719 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
720 if( currentCenter.y >= mOriginalCenter.y )
722 corner = Vector2( 1.1f*mPageSize.width, 0.f );
726 corner = mPageSize*1.1f;
729 Vector2 offset( currentCenter-mOriginalCenter );
730 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
731 /( offset.x*offset.x + offset.y*offset.y );
735 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter");
736 Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
737 originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
738 originalCenterConstraint.Apply();
740 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter");
741 Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
742 currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
743 currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
744 currentCenterConstraint.Apply();
746 PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
748 float distance = offset.Length();
749 Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
750 rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
751 rotationConstraint.Apply();
753 mConstraints = false;
758 if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
760 mPanActor.RemoveConstraints();
761 mTurnEffect[mIndex].RemoveConstraints();
762 mTurnEffect[mIndex].SetUniform("uOriginalCenter",mOriginalCenter );
764 mPanDisplacement = 0.f;
767 mTurnEffect[mIndex].SetUniform("uCurrentCenter", currentCenter );
768 mCurrentCenter = currentCenter;
769 PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
774 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
776 // Guard against destruction during signal emission
777 Toolkit::PageTurnView handle( GetOwner() );
781 if(!mIsAnimating[mIndex])
783 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
788 mPagePanFinishedSignal.Emit( handle );
790 ImageActor actor = mPanActor;
793 if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
795 // update the pages here instead of in the TurnedOver callback function
796 // as new page is allowed to respond to the pan gesture before other pages finishing animation
797 if(mIsTurnBack[actor])
800 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
801 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
806 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
807 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
811 // set up an animation to turn the page over
813 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
814 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
815 animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
816 width,AlphaFunction::EASE_OUT_SINE);
817 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
818 Vector2(-mPageSize.width*1.1f, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
819 mAnimationPageIdPair[animation] = mTurningPageIndex;
820 mAnimationIndexPair[animation] = mIndex;
822 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
824 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
826 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
827 animation.AnimateTo( Property( mTurnEffect[mIndex], "uCurrentCenter" ),
828 mOriginalCenter, AlphaFunction::LINEAR );
829 mAnimationPageIdPair[animation] = mTurningPageIndex;
830 mAnimationIndexPair[animation] = mIndex;
832 mIsSliding[mIndex] = true;
833 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
835 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), mIsTurnBack[actor] );
840 // In portrait view, an outwards flick should turn the previous page back
841 // In landscape view, nothing to do
842 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
848 void PageTurnView::TurnedOver( Animation& animation )
850 int pageId = mAnimationPageIdPair[animation];
851 ImageActor actor = mPageActors[pageId % NUMBER_OF_CACHED_PAGES];
852 mIsTurnBack[actor] = !mIsTurnBack[actor];
853 actor.RemoveConstraints();
855 int index = mAnimationIndexPair[animation];
856 mIsAnimating[index] = false;
857 mTurnEffect[index].RemoveConstraints();
858 mAnimationIndexPair.erase( animation );
859 mAnimationPageIdPair.erase( animation );
861 SetSpineEffect( actor, mIsTurnBack[actor] );
863 int id = pageId + (mIsTurnBack[actor]? -1 : 1);
864 if( id >=0 && id < mTotalPageCount )
866 mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(false);
869 OnTurnedOver( actor, mIsTurnBack[actor] );
871 // Guard against destruction during signal emission
872 Toolkit::PageTurnView handle( GetOwner() );
873 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mIsTurnBack[actor] );
876 void PageTurnView::SliddenBack( Animation& animation )
878 int pageId = mAnimationPageIdPair[animation];
879 ImageActor actor = mPageActors[pageId % NUMBER_OF_CACHED_PAGES];
881 int index = mAnimationIndexPair[animation];
882 mIsSliding[index] = false;
883 mIsAnimating[index] = false;
884 mAnimationIndexPair.erase( animation );
885 mAnimationPageIdPair.erase( animation );
887 SetSpineEffect( actor, mIsTurnBack[actor] );
889 int id = pageId + (mIsTurnBack[actor]? -1 : 1);
890 if( id >=0 && id < mTotalPageCount )
892 mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(false);
895 // Guard against destruction during signal emission
896 Toolkit::PageTurnView handle( GetOwner() );
897 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mIsTurnBack[actor] );
900 void PageTurnView::OrganizePageDepth()
902 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
904 if(mCurrentPageIndex+i < mTotalPageCount)
906 mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
908 if( mCurrentPageIndex >= i + 1 )
910 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
915 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
917 SetShaderEffectRecursively( actor, shaderEffect );
920 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
922 return mPageTurnStartedSignal;
925 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
927 return mPageTurnFinishedSignal;
930 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
932 return mPagePanStartedSignal;
935 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
937 return mPagePanFinishedSignal;
940 bool PageTurnView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
942 Dali::BaseHandle handle( object );
944 bool connected( true );
945 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( handle );
947 if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_STARTED ) )
949 pageTurnView.PageTurnStartedSignal().Connect( tracker, functor );
951 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_FINISHED ) )
953 pageTurnView.PageTurnFinishedSignal().Connect( tracker, functor );
955 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_STARTED ) )
957 pageTurnView.PagePanStartedSignal().Connect( tracker, functor );
959 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_FINISHED ) )
961 pageTurnView.PagePanFinishedSignal().Connect( tracker, functor );
965 // signalName does not match any signal
972 void PageTurnView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
974 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
978 PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
982 case Toolkit::PageTurnView::Property::PAGE_SIZE:
984 pageTurnViewImpl.SetPageSize( value.Get<Vector2>() );
987 case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
989 pageTurnViewImpl.GoToPage( value.Get<int>() );
992 case Toolkit::PageTurnView::Property::SPINE_SHADOW:
994 pageTurnViewImpl.SetSpineShadowParameter( value.Get<Vector2>() );
1001 Property::Value PageTurnView::GetProperty( BaseObject* object, Property::Index index )
1003 Property::Value value;
1005 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
1009 PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
1013 case Toolkit::PageTurnView::Property::PAGE_SIZE:
1015 value = pageTurnViewImpl.GetPageSize();
1018 case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1020 value = static_cast<int>( pageTurnViewImpl.GetCurrentPage() );
1023 case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1025 value = pageTurnViewImpl.GetSpineShadowParameter();
1033 } // namespace Internal
1035 } // namespace Toolkit