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>
29 #include <dali/devel-api/rendering/cull-face.h>
32 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-effect.h>
33 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-book-spine-effect.h>
37 namespace //Unnamed namespace
39 // default grid density for page turn effect, 10 pixels by 10 pixels
40 const float DEFAULT_GRID_DENSITY(10.0f);
42 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
43 const float MINIMUM_START_POSITION_RATIO(0.6f);
45 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
46 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
48 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
49 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
51 // duration of animation, shorter for faster speed
52 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
53 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
55 // the major&minor radius (in pixels) to form an ellipse shape
56 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
57 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
59 // constants for shadow casting
60 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
61 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
63 // constraints ////////////////////////////////////////////////////////////////
65 * Original Center Constraint
67 * This constraint adjusts the original center property of the page turn shader effect
68 * based on the X-direction displacement of the pan gesture
70 struct OriginalCenterConstraint
72 OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
73 : mOldCenter( originalCenter )
75 mNewCenter = originalCenter + offset;
76 mDistance = offset.Length() * 0.5f;
77 mDirection = offset / mDistance;
80 void operator()( Vector2& current, const PropertyInputContainer& inputs )
82 float displacement = inputs[0]->GetFloat();
84 if( displacement < mDistance )
86 current = mOldCenter + mDirection * displacement;
90 current = mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
101 * Rotation Constraint
103 * This constraint adjusts the rotation property of the page actor
104 * based on the X-direction displacement of the pan gesture
106 struct RotationConstraint
108 RotationConstraint( float distance, float pageWidth, bool isTurnBack )
109 : mDistance( distance*0.5f )
111 mStep = 1.f / pageWidth;
112 mSign = isTurnBack ? -1.0f : 1.0f;
113 mConst = isTurnBack ? -1.0f : 0.f;
114 mRotation = isTurnBack ? Quaternion( Radian( -Math::PI ), Vector3::YAXIS ) : Quaternion( Radian(0.f), Vector3::YAXIS );
117 void operator()( Quaternion& current, const PropertyInputContainer& inputs )
119 float displacement = inputs[0]->GetFloat();
120 if( displacement < mDistance)
126 float coef = std::max(-1.0f, mStep*(mDistance-displacement));
127 float angle = Math::PI * ( mConst + mSign * coef );
128 current = Quaternion( Radian( angle ), Vector3::YAXIS );
136 Quaternion mRotation;
140 * Current Center Constraint
142 * This constraint adjusts the current center property of the page turn shader effect
143 * based on the pan position and the original center position
145 struct CurrentCenterConstraint
147 CurrentCenterConstraint( float pageWidth )
148 : mPageWidth( pageWidth )
150 mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
153 void operator()( Vector2& current, const PropertyInputContainer& inputs )
155 const Vector2& centerPosition = inputs[0]->GetVector2();
156 if( centerPosition.x > 0.f )
158 current.x = mThres+centerPosition.x * 0.5f;
159 current.y = centerPosition.y;
163 const Vector2& centerOrigin = inputs[1]->GetVector2();
164 Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
165 float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
166 // 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
169 coef = (coef+0.225f)/10.0f;
171 current = centerOrigin - direction * coef;
179 struct ShadowBlurStrengthConstraint
181 ShadowBlurStrengthConstraint( float thres )
185 void operator()( float& blurStrength, const PropertyInputContainer& inputs )
187 float displacement = inputs[2]->GetFloat();
188 if( EqualsZero(displacement))
190 const Vector2& cur = inputs[0]->GetVector2();
191 const Vector2& ori = inputs[1]->GetVector2();
192 blurStrength = 5.f*(ori-cur).Length() / mThres;
196 blurStrength = 1.f - (displacement-2.f*mThres)/mThres;
199 blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
205 } //unnamed namespace
221 // empty handle as we cannot create PageTurnView(but type registered for page turn signal)
225 // Setup properties, signals and actions using the type-registry.
226 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::PageTurnView, Toolkit::Control, Create );
228 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "pageSize", VECTOR2, PAGE_SIZE )
229 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "currentPageId", INTEGER, CURRENT_PAGE_ID )
230 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "spineShadow", VECTOR2, SPINE_SHADOW )
232 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pageTurnStarted", SIGNAL_PAGE_TURN_STARTED )
233 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pageTurnFinished", SIGNAL_PAGE_TURN_FINISHED )
234 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pagePanStarted", SIGNAL_PAGE_PAN_STARTED )
235 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pagePanFinished", SIGNAL_PAGE_PAN_FINISHED )
237 DALI_TYPE_REGISTRATION_END()
241 // these several constants are also used in the derived classes
242 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
243 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
244 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
245 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
247 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
248 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS ) ),
249 mPageFactory( pageFactory ),
250 mPageSize( pageSize ),
251 mTotalPageCount( 0 ),
253 mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
254 mCurrentPageIndex( 0 ),
255 mTurningPageIndex( 0 ),
258 mPageUpdated( true ),
259 mDistanceUpCorner( 0.f ),
260 mDistanceBottomCorner( 0.f ),
261 mPanDisplacement( 0.f ),
262 mConstraints( false ),
263 mPageTurnStartedSignal(),
264 mPageTurnFinishedSignal(),
265 mPagePanStartedSignal(),
266 mPagePanFinishedSignal()
268 mPageActors.resize( NUMBER_OF_CACHED_PAGES );
269 mIsAnimating.resize( MAXIMUM_TURNING_NUM );
270 mIsSliding.resize( MAXIMUM_TURNING_NUM );
271 mTurnEffect.resize( MAXIMUM_TURNING_NUM );
272 mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
273 mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
276 PageTurnView::~PageTurnView()
280 void PageTurnView::OnInitialize()
282 // create the two book spine effect for static images, left and right side pages respectively
283 mSpineEffectFront = CreatePageTurnBookSpineEffect();
284 mSpineEffectFront.SetUniform("uIsBackImageVisible", -1.f );
285 mSpineEffectFront.SetUniform("uPageWidth", mPageSize.width );
286 mSpineEffectFront.SetUniform("uShadowWidth", 0.f );
287 mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
289 mSpineEffectBack = CreatePageTurnBookSpineEffect();
290 mSpineEffectBack.SetUniform("uIsBackImageVisible", 1.f );
291 mSpineEffectBack.SetUniform("uPageWidth", mPageSize.width );
292 mSpineEffectBack.SetUniform("uShadowWidth", 0.f );
293 mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
295 // create the page turn effect objects
296 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
298 mTurnEffect[i] = CreatePageTurnEffect();
299 mTurnEffect[i].SetProperty( ShaderEffect::Property::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
300 mTurnEffect[i].SetUniform( "uPageSize", mPageSize );
301 mTurnEffect[i].SetUniform( "uShadowWidth", 0.f);
302 mTurnEffect[i].SetUniform( "uSpineShadowParameter", mSpineShadowParameter );
303 mIsAnimating[i] = false;
304 mIsSliding[i] = false;
305 mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
306 mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
309 mTurningPageLayer = Layer::New();
310 mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
311 mTurningPageLayer.SetBehavior(Layer::LAYER_3D);
313 // Set control size and the parent origin of page layers
314 OnPageTurnViewInitialize();
316 Self().Add(mTurningPageLayer);
318 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
319 // add pages to the scene, and set depth for the stacked pages
320 for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
325 mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
328 mPageActors[0].SetVisible(true);
330 // enable the pan gesture which is attached to the control
331 EnableGestureDetection(Gesture::Type(Gesture::Pan));
334 void PageTurnView::SetupShadowView()
336 mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
337 Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
338 mShadowView.SetParentOrigin( origin );
339 mShadowView.SetAnchorPoint( origin );
340 mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
341 mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
343 mShadowPlaneBackground = Actor::New();
344 mShadowPlaneBackground.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
345 mShadowPlaneBackground.SetSize( mControlSize );
346 Self().Add( mShadowPlaneBackground );
347 mShadowView.SetShadowPlaneBackground( mShadowPlaneBackground );
349 mPointLight = Actor::New();
350 mPointLight.SetAnchorPoint( origin );
351 mPointLight.SetParentOrigin( origin );
352 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
353 Self().Add( mPointLight );
354 mShadowView.SetPointLight( mPointLight );
356 mTurningPageLayer.Add( mShadowView );
357 mShadowView.Activate();
360 void PageTurnView::OnStageConnection( int depth )
362 Control::OnStageConnection( depth );
365 mTurningPageLayer.Raise();
368 void PageTurnView::OnStageDisconnection()
372 mPointLight.Unparent();
373 mShadowPlaneBackground.Unparent();
374 mShadowView.Unparent();
377 // make sure the status of the control is updated correctly when the pan gesture is interrupted
382 Self().Add(mPanActor);
383 mIsAnimating[mIndex] = false;
384 mPanActor.RemoveConstraints();
385 mTurnEffect[mIndex].RemoveConstraints();
388 SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
391 Control::OnStageDisconnection();
394 void PageTurnView::SetPageSize( const Vector2& pageSize )
396 mPageSize = pageSize;
397 mSpineEffectFront.SetUniform("uPageWidth", mPageSize.width );
398 mSpineEffectBack.SetUniform("uPageWidth", mPageSize.width );
399 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
401 mTurnEffect[i].SetUniform( "uPageSize", mPageSize );
406 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
409 for( size_t i=0; i<mPageActors.size(); i++ )
413 mPageActors[i].SetSize( mPageSize );
414 if( mPageActors[i].GetChildCount()>0 )
416 mPageActors[i].GetChildAt(0).SetSize( mPageSize );
421 OnPageTurnViewInitialize();
423 if( mShadowPlaneBackground )
425 mShadowPlaneBackground.SetSize( mControlSize );
429 Vector2 PageTurnView::GetPageSize()
434 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
436 mSpineShadowParameter = spineShadowParameter;
438 // set spine shadow parameter to all the shader effects
439 mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
440 mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
441 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
443 mTurnEffect[i].SetUniform("uSpineShadowParameter", mSpineShadowParameter );
447 Vector2 PageTurnView::GetSpineShadowParameter()
449 return mSpineShadowParameter;
452 void PageTurnView::GoToPage( unsigned int pageId )
454 int pageIdx = Clamp( static_cast<int>(pageId), 0, mTotalPageCount-1);
456 if( mCurrentPageIndex == pageIdx )
461 // record the new current page index
462 mCurrentPageIndex = pageIdx;
464 // clear the old pages
465 for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
469 mPageActors[i].Unparent();
470 mPageActors[i].Reset();
474 // add the current page and the pages right before and after it
475 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
480 mPageActors[pageId%NUMBER_OF_CACHED_PAGES].SetVisible(true);
483 mPageActors[(pageId-1)%NUMBER_OF_CACHED_PAGES].SetVisible(true);
485 // set ordered depth to the stacked pages
489 unsigned int PageTurnView::GetCurrentPage()
491 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
492 return static_cast< unsigned int >( mCurrentPageIndex );
495 void PageTurnView::AddPage( int pageIndex )
497 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
499 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
500 ImageActor newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
501 DALI_ASSERT_ALWAYS( newPage );
503 newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
504 newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
505 newPage.SetSize( mPageSize );
506 Self().Add( newPage );
507 mPageActors[index] = newPage;
509 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
510 mIsTurnBack[ newPage ] = isLeftSide;
513 // new page is added to the left side, so need to rotate it 180 degrees
514 newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
518 newPage.SetShaderEffect(mSpineEffectFront);
521 newPage.SetVisible( false );
523 // For Portrait, nothing to do
524 // 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
525 OnAddPage( newPage, isLeftSide );
529 void PageTurnView::RemovePage( int pageIndex )
531 if( pageIndex > -1 && pageIndex < mTotalPageCount)
533 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
534 mPageActors[index].Unparent();
535 mIsTurnBack.erase( mPageActors[index] );
536 mPageActors[index].Reset();
540 void PageTurnView::OnPan( const PanGesture& gesture )
542 // the pan gesture is attached to control itself instead of each page
543 switch( gesture.state )
545 case Gesture::Started:
548 // to find out whether the undergoing turning page number already reaches the maximum allowed
549 // and get one idle index when it is animatable
550 bool animatable = false;
551 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
553 if( !mIsAnimating[mIndex] )
558 if( mIsSliding[mIndex] )
564 mIndex = mIndex % MAXIMUM_TURNING_NUM;
567 if( mPageUpdated && animatable )
569 SetPanActor( gesture.position ); // determine which page actor is panned
570 if(mPanActor && mPanActor.GetParent() != Self()) // if the page is added to turning layer,it is undergoing an animation currently
574 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
582 case Gesture::Continuing:
584 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
587 case Gesture::Finished:
588 case Gesture::Cancelled:
591 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
595 case Gesture::Possible:
603 void PageTurnView::PanStarted( const Vector2& gesturePosition )
605 mPressDownPosition = gesturePosition;
612 mOriginalCenter = gesturePosition;
613 mTurnEffect[mIndex].SetUniform("uIsTurningBack", mIsTurnBack[ mPanActor] ? 1.f : -1.f);
615 mPageUpdated = false;
617 // Guard against destruction during signal emission
618 Toolkit::PageTurnView handle( GetOwner() );
619 mPagePanStartedSignal.Emit( handle );
622 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
629 // Guard against destruction during signal emission
630 Toolkit::PageTurnView handle( GetOwner() );
634 // when the touch down position is near the spine
635 // or when the panning goes outwards or some other position which would tear the paper in real situation
636 // we change the start position into the current panning position and update the shader parameters
637 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
638 || gesturePosition.x > mOriginalCenter.x-1.0f
639 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
640 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
642 mOriginalCenter = gesturePosition;
646 mDistanceUpCorner = mOriginalCenter.Length();
647 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
648 mShadowView.Add( mPanActor );
649 SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
650 mTurnEffect[mIndex].SetUniform("uOriginalCenter", mOriginalCenter );
651 mCurrentCenter = mOriginalCenter;
652 mTurnEffect[mIndex].SetUniform("uCurrentCenter", mCurrentCenter );
653 mPanDisplacement = 0.f;
656 mIsAnimating[mIndex] = true;
658 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), !mIsTurnBack[mPanActor] );
659 int id = mTurningPageIndex + (mIsTurnBack[mPanActor]? -1 : 1);
660 if( id >=0 && id < mTotalPageCount )
662 mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(true);
665 mShadowView.RemoveConstraints();
667 self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
669 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
670 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter")) );
671 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter")) );
672 shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
673 shadowBlurStrengthConstraint.Apply();
678 Vector2 currentCenter = gesturePosition;
680 // Test whether the new current center would tear the paper from the top pine in real situation
681 // we do not forbid this totally, which would restrict the panning gesture too much
682 // instead, set it to the nearest allowable position
683 float distanceUpCorner = currentCenter.Length();
684 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
685 if( distanceUpCorner > mDistanceUpCorner )
687 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
689 // would tear the paper from the bottom spine in real situation
690 if( distanceBottomCorner > mDistanceBottomCorner )
692 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
694 // If direction has a very high y component, reduce it.
695 Vector2 curveDirection = currentCenter - mOriginalCenter;
696 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
698 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
700 // If the vertical distance is high, reduce it
701 float yShift = currentCenter.y - mOriginalCenter.y;
702 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
704 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
707 // use contraints to control the page shape and rotation when the pan position is near the spine
708 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
710 // set the property values used by the constraints
711 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
712 Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
713 Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
715 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
716 // also set up the RotationConstraint to the page actor
720 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
721 if( currentCenter.y >= mOriginalCenter.y )
723 corner = Vector2( 1.1f*mPageSize.width, 0.f );
727 corner = mPageSize*1.1f;
730 Vector2 offset( currentCenter-mOriginalCenter );
731 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
732 /( offset.x*offset.x + offset.y*offset.y );
736 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter");
737 Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
738 originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
739 originalCenterConstraint.Apply();
741 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter");
742 Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
743 currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
744 currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
745 currentCenterConstraint.Apply();
747 PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
749 float distance = offset.Length();
750 Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
751 rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
752 rotationConstraint.Apply();
754 mConstraints = false;
759 if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
761 mPanActor.RemoveConstraints();
762 mTurnEffect[mIndex].RemoveConstraints();
763 mTurnEffect[mIndex].SetUniform("uOriginalCenter",mOriginalCenter );
765 mPanDisplacement = 0.f;
768 mTurnEffect[mIndex].SetUniform("uCurrentCenter", currentCenter );
769 mCurrentCenter = currentCenter;
770 PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
775 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
777 // Guard against destruction during signal emission
778 Toolkit::PageTurnView handle( GetOwner() );
782 if(!mIsAnimating[mIndex])
784 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
789 mPagePanFinishedSignal.Emit( handle );
791 ImageActor actor = mPanActor;
794 if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
796 // update the pages here instead of in the TurnedOver callback function
797 // as new page is allowed to respond to the pan gesture before other pages finishing animation
798 if(mIsTurnBack[actor])
801 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
802 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
807 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
808 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
812 // set up an animation to turn the page over
814 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
815 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
816 animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
817 width,AlphaFunction::EASE_OUT_SINE);
818 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
819 Vector2(-mPageSize.width*1.1f, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
820 mAnimationPageIdPair[animation] = mTurningPageIndex;
821 mAnimationIndexPair[animation] = mIndex;
823 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
825 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
827 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
828 animation.AnimateTo( Property( mTurnEffect[mIndex], "uCurrentCenter" ),
829 mOriginalCenter, AlphaFunction::LINEAR );
830 mAnimationPageIdPair[animation] = mTurningPageIndex;
831 mAnimationIndexPair[animation] = mIndex;
833 mIsSliding[mIndex] = true;
834 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
836 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), mIsTurnBack[actor] );
841 // In portrait view, an outwards flick should turn the previous page back
842 // In landscape view, nothing to do
843 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
849 void PageTurnView::TurnedOver( Animation& animation )
851 int pageId = mAnimationPageIdPair[animation];
852 ImageActor actor = mPageActors[pageId % NUMBER_OF_CACHED_PAGES];
853 mIsTurnBack[actor] = !mIsTurnBack[actor];
854 actor.RemoveConstraints();
856 int index = mAnimationIndexPair[animation];
857 mIsAnimating[index] = false;
858 mTurnEffect[index].RemoveConstraints();
859 mAnimationIndexPair.erase( animation );
860 mAnimationPageIdPair.erase( animation );
862 SetSpineEffect( actor, mIsTurnBack[actor] );
864 int id = pageId + (mIsTurnBack[actor]? -1 : 1);
865 if( id >=0 && id < mTotalPageCount )
867 mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(false);
870 OnTurnedOver( actor, mIsTurnBack[actor] );
872 // Guard against destruction during signal emission
873 Toolkit::PageTurnView handle( GetOwner() );
874 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mIsTurnBack[actor] );
877 void PageTurnView::SliddenBack( Animation& animation )
879 int pageId = mAnimationPageIdPair[animation];
880 ImageActor actor = mPageActors[pageId % NUMBER_OF_CACHED_PAGES];
882 int index = mAnimationIndexPair[animation];
883 mIsSliding[index] = false;
884 mIsAnimating[index] = false;
885 mAnimationIndexPair.erase( animation );
886 mAnimationPageIdPair.erase( animation );
888 SetSpineEffect( actor, mIsTurnBack[actor] );
890 int id = pageId + (mIsTurnBack[actor]? -1 : 1);
891 if( id >=0 && id < mTotalPageCount )
893 mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(false);
896 // Guard against destruction during signal emission
897 Toolkit::PageTurnView handle( GetOwner() );
898 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mIsTurnBack[actor] );
901 void PageTurnView::OrganizePageDepth()
903 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
905 if(mCurrentPageIndex+i < mTotalPageCount)
907 mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
909 if( mCurrentPageIndex >= i + 1 )
911 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
916 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
918 SetShaderEffectRecursively( actor, shaderEffect );
921 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
923 return mPageTurnStartedSignal;
926 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
928 return mPageTurnFinishedSignal;
931 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
933 return mPagePanStartedSignal;
936 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
938 return mPagePanFinishedSignal;
941 bool PageTurnView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
943 Dali::BaseHandle handle( object );
945 bool connected( true );
946 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( handle );
948 if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_STARTED ) )
950 pageTurnView.PageTurnStartedSignal().Connect( tracker, functor );
952 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_FINISHED ) )
954 pageTurnView.PageTurnFinishedSignal().Connect( tracker, functor );
956 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_STARTED ) )
958 pageTurnView.PagePanStartedSignal().Connect( tracker, functor );
960 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_FINISHED ) )
962 pageTurnView.PagePanFinishedSignal().Connect( tracker, functor );
966 // signalName does not match any signal
973 void PageTurnView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
975 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
979 PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
983 case Toolkit::PageTurnView::Property::PAGE_SIZE:
985 pageTurnViewImpl.SetPageSize( value.Get<Vector2>() );
988 case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
990 pageTurnViewImpl.GoToPage( value.Get<int>() );
993 case Toolkit::PageTurnView::Property::SPINE_SHADOW:
995 pageTurnViewImpl.SetSpineShadowParameter( value.Get<Vector2>() );
1002 Property::Value PageTurnView::GetProperty( BaseObject* object, Property::Index index )
1004 Property::Value value;
1006 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
1010 PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
1014 case Toolkit::PageTurnView::Property::PAGE_SIZE:
1016 value = pageTurnViewImpl.GetPageSize();
1019 case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1021 value = static_cast<int>( pageTurnViewImpl.GetCurrentPage() );
1024 case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1026 value = pageTurnViewImpl.GetSpineShadowParameter();
1034 } // namespace Internal
1036 } // namespace Toolkit