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/public-api/controls/default-controls/solid-color-actor.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, "page-size", VECTOR2, PAGE_SIZE )
229 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "current-page-id", INTEGER, CURRENT_PAGE_ID )
230 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "spine-shadow", VECTOR2, SPINE_SHADOW )
232 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "page-turn-started", SIGNAL_PAGE_TURN_STARTED )
233 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "page-turn-finished", SIGNAL_PAGE_TURN_FINISHED )
234 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "page-pan-started", SIGNAL_PAGE_PAN_STARTED )
235 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "page-pan-finished", 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 mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
344 mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
345 mShadowPlane.SetSize( mControlSize );
346 Self().Add( mShadowPlane );
347 mShadowView.SetShadowPlane( mShadowPlane );
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 )
363 mTurningPageLayer.Raise();
366 void PageTurnView::OnStageDisconnection()
370 mPointLight.Unparent();
371 mShadowPlane.Unparent();
372 mShadowView.Unparent();
375 // make sure the status of the control is updated correctly when the pan gesture is interrupted
380 Self().Add(mPanActor);
381 mIsAnimating[mIndex] = false;
382 mPanActor.RemoveConstraints();
383 mTurnEffect[mIndex].RemoveConstraints();
386 SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
390 void PageTurnView::SetPageSize( const Vector2& pageSize )
392 mPageSize = pageSize;
393 mSpineEffectFront.SetUniform("uPageWidth", mPageSize.width );
394 mSpineEffectBack.SetUniform("uPageWidth", mPageSize.width );
395 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
397 mTurnEffect[i].SetUniform( "uPageSize", mPageSize );
402 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
405 for( size_t i=0; i<mPageActors.size(); i++ )
409 mPageActors[i].SetSize( mPageSize );
410 if( mPageActors[i].GetChildCount()>0 )
412 mPageActors[i].GetChildAt(0).SetSize( mPageSize );
417 OnPageTurnViewInitialize();
421 mShadowPlane.SetSize( mControlSize );
425 Vector2 PageTurnView::GetPageSize()
430 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
432 mSpineShadowParameter = spineShadowParameter;
434 // set spine shadow parameter to all the shader effects
435 mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
436 mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
437 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
439 mTurnEffect[i].SetUniform("uSpineShadowParameter", mSpineShadowParameter );
443 Vector2 PageTurnView::GetSpineShadowParameter()
445 return mSpineShadowParameter;
448 void PageTurnView::GoToPage( unsigned int pageId )
450 int pageIdx = Clamp( static_cast<int>(pageId), 0, mTotalPageCount-1);
452 if( mCurrentPageIndex == pageIdx )
457 // record the new current page index
458 mCurrentPageIndex = pageIdx;
460 // clear the old pages
461 for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
465 mPageActors[i].Unparent();
466 mPageActors[i].Reset();
470 // add the current page and the pages right before and after it
471 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
476 mPageActors[pageId%NUMBER_OF_CACHED_PAGES].SetVisible(true);
479 mPageActors[(pageId-1)%NUMBER_OF_CACHED_PAGES].SetVisible(true);
481 // set ordered depth to the stacked pages
485 unsigned int PageTurnView::GetCurrentPage()
487 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
488 return static_cast< unsigned int >( mCurrentPageIndex );
491 void PageTurnView::AddPage( int pageIndex )
493 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
495 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
496 ImageActor newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
497 DALI_ASSERT_ALWAYS( newPage );
499 newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
500 newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
501 newPage.SetSize( mPageSize );
502 Self().Add( newPage );
503 mPageActors[index] = newPage;
505 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
506 mIsTurnBack[ newPage ] = isLeftSide;
509 // new page is added to the left side, so need to rotate it 180 degrees
510 newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
514 newPage.SetShaderEffect(mSpineEffectFront);
517 newPage.SetVisible( false );
519 // For Portrait, nothing to do
520 // 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
521 OnAddPage( newPage, isLeftSide );
525 void PageTurnView::RemovePage( int pageIndex )
527 if( pageIndex > -1 && pageIndex < mTotalPageCount)
529 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
530 mPageActors[index].Unparent();
531 mIsTurnBack.erase( mPageActors[index] );
532 mPageActors[index].Reset();
536 void PageTurnView::OnPan( const PanGesture& gesture )
538 // the pan gesture is attached to control itself instead of each page
539 switch( gesture.state )
541 case Gesture::Started:
544 // to find out whether the undergoing turning page number already reaches the maximum allowed
545 // and get one idle index when it is animatable
546 bool animatable = false;
547 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
549 if( !mIsAnimating[mIndex] )
554 if( mIsSliding[mIndex] )
560 mIndex = mIndex % MAXIMUM_TURNING_NUM;
563 if( mPageUpdated && animatable )
565 SetPanActor( gesture.position ); // determine which page actor is panned
566 if(mPanActor && mPanActor.GetParent() != Self()) // if the page is added to turning layer,it is undergoing an animation currently
570 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
578 case Gesture::Continuing:
580 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
583 case Gesture::Finished:
584 case Gesture::Cancelled:
587 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
591 case Gesture::Possible:
599 void PageTurnView::PanStarted( const Vector2& gesturePosition )
601 mPressDownPosition = gesturePosition;
608 mOriginalCenter = gesturePosition;
609 mTurnEffect[mIndex].SetUniform("uIsTurningBack", mIsTurnBack[ mPanActor] ? 1.f : -1.f);
611 mPageUpdated = false;
613 // Guard against destruction during signal emission
614 Toolkit::PageTurnView handle( GetOwner() );
615 mPagePanStartedSignal.Emit( handle );
618 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
625 // Guard against destruction during signal emission
626 Toolkit::PageTurnView handle( GetOwner() );
630 // when the touch down position is near the spine
631 // or when the panning goes outwards or some other position which would tear the paper in real situation
632 // we change the start position into the current panning position and update the shader parameters
633 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
634 || gesturePosition.x > mOriginalCenter.x-1.0f
635 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
636 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
638 mOriginalCenter = gesturePosition;
642 mDistanceUpCorner = mOriginalCenter.Length();
643 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
644 mShadowView.Add( mPanActor );
645 SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
646 mTurnEffect[mIndex].SetUniform("uOriginalCenter", mOriginalCenter );
647 mCurrentCenter = mOriginalCenter;
648 mTurnEffect[mIndex].SetUniform("uCurrentCenter", mCurrentCenter );
649 mPanDisplacement = 0.f;
652 mIsAnimating[mIndex] = true;
654 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), !mIsTurnBack[mPanActor] );
655 int id = mTurningPageIndex + (mIsTurnBack[mPanActor]? -1 : 1);
656 if( id >=0 && id < mTotalPageCount )
658 mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(true);
661 mShadowView.RemoveConstraints();
663 self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
665 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
666 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter")) );
667 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter")) );
668 shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
669 shadowBlurStrengthConstraint.Apply();
674 Vector2 currentCenter = gesturePosition;
676 // Test whether the new current center would tear the paper from the top pine in real situation
677 // we do not forbid this totally, which would restrict the panning gesture too much
678 // instead, set it to the nearest allowable position
679 float distanceUpCorner = currentCenter.Length();
680 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
681 if( distanceUpCorner > mDistanceUpCorner )
683 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
685 // would tear the paper from the bottom spine in real situation
686 if( distanceBottomCorner > mDistanceBottomCorner )
688 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
690 // If direction has a very high y component, reduce it.
691 Vector2 curveDirection = currentCenter - mOriginalCenter;
692 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
694 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
696 // If the vertical distance is high, reduce it
697 float yShift = currentCenter.y - mOriginalCenter.y;
698 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
700 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
703 // use contraints to control the page shape and rotation when the pan position is near the spine
704 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
706 // set the property values used by the constraints
707 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
708 Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
709 Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
711 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
712 // also set up the RotationConstraint to the page actor
716 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
717 if( currentCenter.y >= mOriginalCenter.y )
719 corner = Vector2( 1.1f*mPageSize.width, 0.f );
723 corner = mPageSize*1.1f;
726 Vector2 offset( currentCenter-mOriginalCenter );
727 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
728 /( offset.x*offset.x + offset.y*offset.y );
732 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter");
733 Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
734 originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
735 originalCenterConstraint.Apply();
737 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter");
738 Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
739 currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
740 currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
741 currentCenterConstraint.Apply();
743 PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
745 float distance = offset.Length();
746 Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
747 rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
748 rotationConstraint.Apply();
750 mConstraints = false;
755 if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
757 mPanActor.RemoveConstraints();
758 mTurnEffect[mIndex].RemoveConstraints();
759 mTurnEffect[mIndex].SetUniform("uOriginalCenter",mOriginalCenter );
761 mPanDisplacement = 0.f;
764 mTurnEffect[mIndex].SetUniform("uCurrentCenter", currentCenter );
765 mCurrentCenter = currentCenter;
766 PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
771 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
773 // Guard against destruction during signal emission
774 Toolkit::PageTurnView handle( GetOwner() );
778 if(!mIsAnimating[mIndex])
780 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
785 mPagePanFinishedSignal.Emit( handle );
787 ImageActor actor = mPanActor;
790 if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
792 // update the pages here instead of in the TurnedOver callback function
793 // as new page is allowed to respond to the pan gesture before other pages finishing animation
794 if(mIsTurnBack[actor])
797 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
798 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
803 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
804 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
808 // set up an animation to turn the page over
810 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
811 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
812 animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
813 width,AlphaFunction::EASE_OUT_SINE);
814 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
815 Vector2(-mPageSize.width*1.1f, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
816 mAnimationPageIdPair[animation] = mTurningPageIndex;
817 mAnimationIndexPair[animation] = mIndex;
819 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
821 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
823 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
824 animation.AnimateTo( Property( mTurnEffect[mIndex], "uCurrentCenter" ),
825 mOriginalCenter, AlphaFunction::LINEAR );
826 mAnimationPageIdPair[animation] = mTurningPageIndex;
827 mAnimationIndexPair[animation] = mIndex;
829 mIsSliding[mIndex] = true;
830 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
832 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), mIsTurnBack[actor] );
837 // In portrait view, an outwards flick should turn the previous page back
838 // In landscape view, nothing to do
839 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
845 void PageTurnView::TurnedOver( Animation& animation )
847 int pageId = mAnimationPageIdPair[animation];
848 ImageActor actor = mPageActors[pageId % NUMBER_OF_CACHED_PAGES];
849 mIsTurnBack[actor] = !mIsTurnBack[actor];
850 actor.RemoveConstraints();
852 int index = mAnimationIndexPair[animation];
853 mIsAnimating[index] = false;
854 mTurnEffect[index].RemoveConstraints();
855 mAnimationIndexPair.erase( animation );
856 mAnimationPageIdPair.erase( animation );
858 SetSpineEffect( actor, mIsTurnBack[actor] );
860 int id = pageId + (mIsTurnBack[actor]? -1 : 1);
861 if( id >=0 && id < mTotalPageCount )
863 mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(false);
866 OnTurnedOver( actor, mIsTurnBack[actor] );
868 // Guard against destruction during signal emission
869 Toolkit::PageTurnView handle( GetOwner() );
870 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mIsTurnBack[actor] );
873 void PageTurnView::SliddenBack( Animation& animation )
875 int pageId = mAnimationPageIdPair[animation];
876 ImageActor actor = mPageActors[pageId % NUMBER_OF_CACHED_PAGES];
878 int index = mAnimationIndexPair[animation];
879 mIsSliding[index] = false;
880 mIsAnimating[index] = false;
881 mAnimationIndexPair.erase( animation );
882 mAnimationPageIdPair.erase( animation );
884 SetSpineEffect( actor, mIsTurnBack[actor] );
886 int id = pageId + (mIsTurnBack[actor]? -1 : 1);
887 if( id >=0 && id < mTotalPageCount )
889 mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(false);
892 // Guard against destruction during signal emission
893 Toolkit::PageTurnView handle( GetOwner() );
894 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mIsTurnBack[actor] );
897 void PageTurnView::OrganizePageDepth()
899 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
901 if(mCurrentPageIndex+i < mTotalPageCount)
903 mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
905 if( mCurrentPageIndex >= i + 1 )
907 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
912 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
914 SetShaderEffectRecursively( actor, shaderEffect );
917 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
919 return mPageTurnStartedSignal;
922 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
924 return mPageTurnFinishedSignal;
927 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
929 return mPagePanStartedSignal;
932 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
934 return mPagePanFinishedSignal;
937 bool PageTurnView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
939 Dali::BaseHandle handle( object );
941 bool connected( true );
942 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( handle );
944 if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_STARTED ) )
946 pageTurnView.PageTurnStartedSignal().Connect( tracker, functor );
948 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_FINISHED ) )
950 pageTurnView.PageTurnFinishedSignal().Connect( tracker, functor );
952 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_STARTED ) )
954 pageTurnView.PagePanStartedSignal().Connect( tracker, functor );
956 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_FINISHED ) )
958 pageTurnView.PagePanFinishedSignal().Connect( tracker, functor );
962 // signalName does not match any signal
969 void PageTurnView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
971 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
975 PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
979 case Toolkit::PageTurnView::Property::PAGE_SIZE:
981 pageTurnViewImpl.SetPageSize( value.Get<Vector2>() );
984 case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
986 pageTurnViewImpl.GoToPage( value.Get<int>() );
989 case Toolkit::PageTurnView::Property::SPINE_SHADOW:
991 pageTurnViewImpl.SetSpineShadowParameter( value.Get<Vector2>() );
998 Property::Value PageTurnView::GetProperty( BaseObject* object, Property::Index index )
1000 Property::Value value;
1002 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
1006 PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
1010 case Toolkit::PageTurnView::Property::PAGE_SIZE:
1012 value = pageTurnViewImpl.GetPageSize();
1015 case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1017 value = static_cast<int>( pageTurnViewImpl.GetCurrentPage() );
1020 case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1022 value = pageTurnViewImpl.GetSpineShadowParameter();
1030 } // namespace Internal
1032 } // namespace Toolkit