2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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.
18 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.h>
21 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
25 namespace //unnamed namespace
28 TypeRegistration mType( typeid(Toolkit::PageTurnView), typeid(Toolkit::Control), NULL );
30 // default grid density for page turn effect, 10 pixels by 10 pixels
31 const float DEFAULT_GRID_DENSITY(10.0f);
33 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
34 const float MINIMUM_START_POSITION_RATIO(0.6f);
36 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
37 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
39 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
40 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
42 // duration of animation, shorter for faster speed
43 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
44 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
46 // the major&minor radius (in pixels) to form an ellipse shape
47 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
48 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
50 // constants for shadow casting
51 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
52 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
54 // constraints ////////////////////////////////////////////////////////////////
56 * Original Center Constraint
58 * This constraint adjusts the original center property of the page turn shader effect
59 * based on the X-direction displacement of the pan gesture
61 struct OriginalCenterConstraint
63 OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
64 : mOldCenter( originalCenter )
66 mNewCenter = originalCenter + offset;
67 mDistance = offset.Length() * 0.5f;
68 mDirection = offset / mDistance;
71 Vector2 operator()(const Vector2& current, const PropertyInput& panDisplacement)
73 float displacement = panDisplacement.GetFloat();
75 if( displacement < mDistance )
77 return mOldCenter + mDirection * displacement;
81 return mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
94 * This constraint adjusts the rotation property of the page actor
95 * based on the X-direction displacement of the pan gesture
97 struct RotationConstraint
99 RotationConstraint( float distance, float pageWidth, bool isTurnBack )
100 : mDistance( distance*0.5f )
102 mStep = 1.f / pageWidth;
103 mSign = isTurnBack ? -1.0f : 1.0f;
104 mConst = isTurnBack ? -1.0f : 0.f;
105 mRotation = isTurnBack ? Quaternion( -Math::PI, Vector3::YAXIS ) : Quaternion( 0.f, Vector3::YAXIS );
108 Quaternion operator()( const Quaternion& current, const PropertyInput& panDisplacement )
110 float displacement = panDisplacement.GetFloat();
112 if( displacement < mDistance)
118 float coef = std::max(-1.0f, mStep*(mDistance-displacement));
119 angle = Math::PI*( mConst + mSign*coef );
120 return Quaternion( angle, Vector3::YAXIS );
128 Quaternion mRotation;
132 * Current Center Constraint
134 * This constraint adjusts the current center property of the page turn shader effect
135 * based on the pan position and the original center position
137 struct CurrentCenterConstraint
139 CurrentCenterConstraint( float pageWidth)
140 : mPageWidth( pageWidth )
142 mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
145 Vector2 operator()( const Vector2& current, const PropertyInput& center, const PropertyInput& originalCenter )
147 Vector2 centerPosition = center.GetVector2();
148 if( centerPosition.x > 0.f )
150 return Vector2( mThres+centerPosition.x*0.5f , centerPosition.y);
154 Vector2 centerOrigin = originalCenter.GetVector2();
155 Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
156 float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
157 // 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
160 coef = (coef+0.225f)/10.0f;
162 return centerOrigin - direction * coef;
170 struct ShadowBlurStrengthConstraint
172 ShadowBlurStrengthConstraint( float thres )
176 float operator()( const float current, const PropertyInput& currentCenter, const PropertyInput& originalCenter, const PropertyInput& panDisplacement)
178 float displacement = panDisplacement.GetFloat();
180 if( EqualsZero(displacement))
182 Vector2 cur = currentCenter.GetVector2();
183 Vector2 ori = originalCenter.GetVector2();
184 blurStrength = 5.f*(ori-cur).Length() / mThres;
188 blurStrength = 1.f - (displacement-2.f*mThres)/mThres;
191 blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
198 bool IsActorHittableFunction( Actor actor, Dali::HitTestAlgorithm::TraverseType type )
200 bool hittable = false;
204 case Dali::HitTestAlgorithm::CHECK_ACTOR:
206 // Check whether the actor is visible and not fully transparent.
207 Property::Index propertyActorHittable = actor.GetPropertyIndex(Toolkit::PageFactory::ACTOR_HITTABLE);
208 if( actor.IsSensitive()
210 && actor.GetCurrentWorldColor().a > 0.01f// not FULLY_TRANSPARENT
211 && ( propertyActorHittable != Property::INVALID_INDEX &&
212 actor.GetProperty<bool>( propertyActorHittable ) ) )
218 case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
220 if( actor.IsSensitive() && actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible.
235 } //unnamed namespace
246 // these several constants are also used in the derived classes
247 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
248 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
249 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
250 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
252 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
254 mPageFactory( pageFactory ),
255 mPageSize( pageSize ),
256 mIsEditMode( false ),
258 mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
259 mCurrentPageIndex( 0 ),
262 mPageUpdated( true ),
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 = PageTurnBookSpineEffect::New();
284 mSpineEffectFront.SetIsBackImageVisible( false );
285 mSpineEffectFront.SetPageWidth( mPageSize.width );
286 mSpineEffectFront.SetShadowWidth( 0.f );
287 mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
289 mSpineEffectBack = PageTurnBookSpineEffect::New();
290 mSpineEffectBack.SetIsBackImageVisible( true );
291 mSpineEffectBack.SetPageWidth( mPageSize.width );
292 mSpineEffectBack.SetShadowWidth( 0.f );
293 mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
295 // create the page turn effect objects
296 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
298 mTurnEffect[i] = Toolkit::PageTurnEffect::New( false );
299 mTurnEffect[i].SetProperty( ShaderEffect::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
300 mTurnEffect[i].SetPageSize( mPageSize );
301 mTurnEffect[i].SetShadowWidth(0.f);
302 mTurnEffect[i].SetSpineShadowParameter( 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 // Set control size and the parent origin of turningPageLayer
312 OnPageTurnViewInitialize();
314 mRootOnScreen = Actor::New();
315 mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION );
316 mRootOnScreen.SetSize( mControlSize );
317 Self().Add( mRootOnScreen );
318 mRootOnScreen.Add(mTurningPageLayer);
320 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
321 mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded();
322 if( mNeedOffscreenRendering )
327 // add pages to the scene, and set depth for the stacked pages
328 for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
333 mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
337 // enable the pan gesture which is attached to the control
338 EnableGestureDetection(Gesture::Type(Gesture::Pan));
340 mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage);
343 void PageTurnView::SetupRenderTasks()
345 mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES );
346 mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES );
347 mRenderedPage.resize( NUMBER_OF_CACHED_PAGES );
349 mCameraActor = CameraActor::New(mControlSize);
350 mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
351 mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
352 mCameraActor.SetInheritScale( false );
353 Self().Add(mCameraActor);
355 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
356 for(int i=0; i<NUMBER_OF_CACHED_PAGES; i++)
358 mPageSourceActor[i] = Actor::New();
359 mPageSourceActor[i].SetParentOrigin(ParentOrigin::CENTER);
360 mPageSourceActor[i].SetColorMode( USE_OWN_COLOR );
361 mPageSourceActor[i].SetPositionInheritanceMode( DONT_INHERIT_POSITION );
362 mPageSourceActor[i].SetInheritScale( false );
363 Self().Add( mPageSourceActor[i] );
364 mPageSourceActor[i].SetSensitive( false );
366 mRenderedPage[i] = FrameBufferImage::New( mControlSize.width, mControlSize.height, Pixel::RGB8888, Image::Unused );
367 mOffscreenTask[i] = taskList.CreateTask();
368 mOffscreenTask[i].SetRefreshRate( RenderTask::REFRESH_ONCE );
369 mOffscreenTask[i].SetCameraActor(mCameraActor);
370 mOffscreenTask[i].SetSourceActor( mPageSourceActor[i] );
371 mOffscreenTask[i].SetExclusive(true);
372 mOffscreenTask[i].SetInputEnabled( false );
373 mOffscreenTask[i].SetClearEnabled( true );
374 mOffscreenTask[i].SetClearColor( Vector4(0.f,0.f,0.f,0.f) );
375 mOffscreenTask[i].SetTargetFrameBuffer( mRenderedPage[i] );
376 mOffscreenTask[i].SetScreenToFrameBufferMappingActor( Self() );
380 void PageTurnView::SetupShadowView()
382 mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
383 Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
384 mShadowView.SetParentOrigin( origin );
385 mShadowView.SetAnchorPoint( origin );
386 mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
387 mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
389 mShadowLayer = Layer::New();
390 mShadowLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
391 mRootOnScreen.Add(mShadowLayer);
392 mShadowLayer.Raise();
394 mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
395 mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
396 mShadowPlane.SetSize( mControlSize );
397 mShadowLayer.Add( mShadowPlane );
398 mShadowView.SetShadowPlane( mShadowPlane );
400 mPointLight = Actor::New();
401 mPointLight.SetAnchorPoint( origin );
402 mPointLight.SetParentOrigin( origin );
403 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
404 mRootOnScreen.Add( mPointLight );
405 mShadowView.SetPointLight( mPointLight );
407 mTurningPageLayer.Add( mShadowView );
408 mShadowView.Activate();
411 void PageTurnView::OnControlStageConnection()
414 mTurningPageLayer.RaiseToTop();
417 void PageTurnView::OnControlStageDisconnection()
421 Self().Remove(mPointLight);
422 Self().Remove(mShadowLayer);
423 mTurningPageLayer.Remove( mShadowView );
426 // make sure the status of the control is updated correctly when the pan gesture is interrupted
431 mRootOnScreen.Add(mPanActor);
432 mIsAnimating[mIndex] = false;
433 mPanActor.RemoveConstraints();
434 mTurnEffect[mIndex].RemoveConstraints();
437 SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
441 void PageTurnView::OnControlSizeSet( const Vector3& size )
443 // disable the SetSize of the control from the application
444 Self().SetSize( mControlSize );
447 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
449 mSpineShadowParameter = spineShadowParameter;
451 // set spine shadow parameter to all the shader effects
452 mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
453 mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
454 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
456 mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
460 Vector2 PageTurnView::GetSpineShadowParameter()
462 return mSpineShadowParameter;
465 void PageTurnView::GoToPage( unsigned int pageId )
467 int pageIdx = static_cast<int>(pageId);
468 // record the new current page index
469 mCurrentPageIndex = pageIdx;
471 // clear the old pages
472 for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
476 mPageActors[i].Unparent();
477 mPageActors[i].Reset();
481 // add the current page and the pages right before and after it
482 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
486 // set ordered depth to the stacked pages
490 unsigned int PageTurnView::GetCurrentPage()
492 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
493 return static_cast< unsigned int >( mCurrentPageIndex );
496 Actor PageTurnView::EnterEditMode()
498 if( mNeedOffscreenRendering )
500 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
504 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
505 mOffscreenTask[index].SetInputEnabled( true );
506 mPageSourceActor[index].SetSensitive( true );
507 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
509 mRootOnScreen.SetSensitive(false);
511 return mPageSourceActor[index].GetChildAt( 0 );
519 void PageTurnView::LeaveEditMode()
521 if( mNeedOffscreenRendering )
523 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
527 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
528 mOffscreenTask[index].SetInputEnabled( false );
529 mPageSourceActor[index].SetSensitive( false );
530 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
532 mRootOnScreen.SetSensitive(true);
536 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
538 if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
540 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
542 Dali::HitTestAlgorithm::Results results;
543 if( !mOffscreenTask[index].GetInputEnabled() )
545 mOffscreenTask[index].SetInputEnabled( true );
546 mPageSourceActor[index].SetSensitive( true );
547 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
548 mOffscreenTask[index].SetInputEnabled( false );
549 mPageSourceActor[index].SetSensitive( false );
553 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
555 actorCoordinates = results.actorCoordinates;
556 return results.actor;
564 void PageTurnView::AddPage( int pageIndex )
566 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
568 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
570 if( mNeedOffscreenRendering )
572 Actor source = mPageFactory.NewPage( pageIndex );
573 if( mPageSourceActor[index].GetChildCount() > 0 )
575 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
577 mPageSourceActor[index].Add( source );
578 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
579 newPage = NewPageFromRenderBuffer( pageIndex );
583 newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
584 DALI_ASSERT_ALWAYS( newPage );
586 newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
587 newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
588 newPage.SetSize( mPageSize );
589 mRootOnScreen.Add( newPage );
590 mPageActors[index] = newPage;
592 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
593 mIsTurnBack[ newPage ] = isLeftSide;
596 // new page is added to the left side, so need to rotate it 180 degrees
597 newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
601 SetShaderEffect( newPage, mSpineEffectFront);
604 // For Portrait, nothing to do
605 // 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
606 OnAddPage( newPage, isLeftSide );
610 void PageTurnView::RemovePage( int pageIndex )
612 if( pageIndex > -1 && pageIndex < mTotalPageCount)
614 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
615 mPageActors[index].Unparent();
616 mIsTurnBack.erase( mPageActors[index] );
617 mPageActors[index].Reset();
618 if( mNeedOffscreenRendering )
620 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
625 void PageTurnView::RenderPage( int pageIndex )
627 if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
628 && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
630 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
631 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
635 void PageTurnView::RefreshAll()
637 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
638 if( mTotalPageCount > 0 )
640 if(mCurrentPageIndex < mTotalPageCount)
642 GoToPage( mCurrentPageIndex );
646 GoToPage( mCurrentPageIndex-- );
651 void PageTurnView::RefreshCurrentPage()
653 RenderPage( mCurrentPageIndex );
656 void PageTurnView::OnPan( PanGesture gesture )
660 // when interrupted by the call of DisplayCurrentPageSourceActor(),
661 // make sure the panFinished is always called before stopping to responding the gesture
662 // so the status of the control is updated correctly
666 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
671 // the pan gesture is attached to control itself instead of each page
672 switch( gesture.state )
674 case Gesture::Started:
677 // to find out whether the undergoing turning page number already reaches the maximum allowed
678 // and get one idle index when it is animatable
679 bool animatable = false;
680 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
682 if( !mIsAnimating[mIndex] )
687 if( mIsSliding[mIndex] )
693 mIndex = mIndex % MAXIMUM_TURNING_NUM;
696 if( mPageUpdated && animatable )
698 SetPanActor( gesture.position ); // determine which page actor is panned
699 if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
703 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
711 case Gesture::Continuing:
713 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
716 case Gesture::Finished:
717 case Gesture::Cancelled:
720 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
724 case Gesture::Possible:
732 void PageTurnView::PanStarted( const Vector2& gesturePosition )
734 mPressDownPosition = gesturePosition;
741 mOriginalCenter = gesturePosition;
742 mTurnEffect[mIndex].SetIsTurningBack( mIsTurnBack[ mPanActor] );
744 mPageUpdated = false;
746 // Guard against destruction during signal emission
747 Toolkit::PageTurnView handle( GetOwner() );
748 mPagePanStartedSignal.Emit( handle );
751 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
758 // Guard against destruction during signal emission
759 Toolkit::PageTurnView handle( GetOwner() );
763 // when the touch down position is near the spine
764 // or when the panning goes outwards or some other position which would tear the paper in real situation
765 // we change the start position into the current panning position and update the shader parameters
766 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
767 || gesturePosition.x > mOriginalCenter.x-1.0f
768 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
769 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
771 mOriginalCenter = gesturePosition;
775 mDistanceUpCorner = mOriginalCenter.Length();
776 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
777 mShadowView.Add( mPanActor );
778 SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
779 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
780 mCurrentCenter = mOriginalCenter;
781 mTurnEffect[mIndex].SetCurrentCenter( mCurrentCenter );
782 mPanDisplacement = 0.f;
785 mIsAnimating[mIndex] = true;
787 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
789 mShadowView.RemoveConstraints();
791 self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
792 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView.GetBlurStrengthPropertyIndex(),
793 Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())),
794 Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())),
795 Source( self, mPropertyPanDisplacement[mIndex] ),
796 ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
797 mShadowView.ApplyConstraint( shadowBlurStrengthConstraint );
802 Vector2 currentCenter = gesturePosition;
804 // Test whether the new current center would tear the paper from the top pine in real situation
805 // we do not forbid this totally, which would restrict the panning gesture too much
806 // instead, set it to the nearest allowable position
807 float distanceUpCorner = currentCenter.Length();
808 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
809 if( distanceUpCorner > mDistanceUpCorner )
811 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
813 // would tear the paper from the bottom spine in real situation
814 if( distanceBottomCorner > mDistanceBottomCorner )
816 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
818 // If direction has a very high y component, reduce it.
819 Vector2 curveDirection = currentCenter - mOriginalCenter;
820 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
822 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
824 // If the vertical distance is high, reduce it
825 float yShift = currentCenter.y - mOriginalCenter.y;
826 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
828 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
831 // use contraints to control the page shape and rotation when the pan position is near the spine
832 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
834 // set the property values used by the constraints
835 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
836 Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
837 Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
839 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
840 // also set up the RotationConstraint to the page actor
844 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
845 if( currentCenter.y >= mOriginalCenter.y )
847 corner = Vector2( 1.1f*mPageSize.width, 0.f );
851 corner = mPageSize*1.1f;
854 Vector2 offset( currentCenter-mOriginalCenter );
855 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
856 /( offset.x*offset.x + offset.y*offset.y );
859 Source source(self, mPropertyPanDisplacement[mIndex]);
861 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
862 Constraint originalCenterConstraint = Constraint::New<Vector2>( shaderOriginalCenterPropertyIndex ,
864 OriginalCenterConstraint( mOriginalCenter, offset ));
865 mTurnEffect[mIndex].ApplyConstraint( originalCenterConstraint );
867 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
868 Constraint currentCenterConstraint = Constraint::New<Vector2>( shaderCurrentCenterPropertyIndex,
869 Source(self, mPropertyCurrentCenter[mIndex]),
870 Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex),
871 CurrentCenterConstraint(mPageSize.width));
872 mTurnEffect[mIndex].ApplyConstraint( currentCenterConstraint );
874 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
876 float distance = offset.Length();
877 Constraint rotationConstraint = Constraint::New<Quaternion>( Actor::ROTATION,
878 Source( self, mPropertyPanDisplacement[mIndex] ),
879 RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
880 mPanActor.ApplyConstraint( rotationConstraint );
882 mConstraints = false;
887 if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
889 mPanActor.RemoveConstraints();
890 mTurnEffect[mIndex].RemoveConstraints();
891 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
893 mPanDisplacement = 0.f;
896 mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
897 mCurrentCenter = currentCenter;
898 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
903 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
905 // Guard against destruction during signal emission
906 Toolkit::PageTurnView handle( GetOwner() );
910 if(!mIsAnimating[mIndex])
912 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
917 mPagePanFinishedSignal.Emit( handle );
919 Actor actor = mPanActor;
922 if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
924 // update the pages here instead of in the TurnedOver callback function
925 // as new page is allowed to respond to the pan gesture before other pages finishing animation
926 if(mIsTurnBack[actor])
929 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
930 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
935 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
936 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
940 // set up an animation to turn the page over
942 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
943 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
944 animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
945 width,AlphaFunctions::EaseOutSine33);
946 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
947 Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunctions::EaseOutSine33);
948 mAnimationActorPair[animation] = actor;
949 mAnimationIndexPair[animation] = mIndex;
951 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
953 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
955 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
956 animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
957 mOriginalCenter, AlphaFunctions::Linear );
958 mAnimationActorPair[animation] = actor;
959 mAnimationIndexPair[animation] = mIndex;
961 mIsSliding[mIndex] = true;
962 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
964 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
969 // In portrait view, an outwards flick should turn the previous page back
970 // In landscape view, nothing to do
971 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
977 void PageTurnView::TurnedOver( Animation& animation )
979 Actor actor = mAnimationActorPair[animation];
980 mIsTurnBack[actor] = !mIsTurnBack[actor];
981 actor.RemoveConstraints();
982 mRootOnScreen.Add(actor);
983 int index = mAnimationIndexPair[animation];
984 mIsAnimating[index] = false;
985 mTurnEffect[index].RemoveConstraints();
986 mAnimationIndexPair.erase( animation );
987 mAnimationActorPair.erase( animation );
989 SetSpineEffect( actor, mIsTurnBack[actor] );
991 // Guard against destruction during signal emission
992 Toolkit::PageTurnView handle( GetOwner() );
993 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
996 void PageTurnView::SliddenBack( Animation& animation )
998 Actor actor = mAnimationActorPair[animation];
999 mRootOnScreen.Add(actor);
1000 int index = mAnimationIndexPair[animation];
1001 mIsSliding[index] = false;
1002 mIsAnimating[index] = false;
1003 mAnimationIndexPair.erase( animation );
1004 mAnimationActorPair.erase( animation );
1006 SetSpineEffect( actor, mIsTurnBack[actor] );
1008 // Guard against destruction during signal emission
1009 Toolkit::PageTurnView handle( GetOwner() );
1010 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1013 void PageTurnView::OrganizePageDepth()
1015 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1017 if(mCurrentPageIndex+i < mTotalPageCount)
1019 mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1021 if( mCurrentPageIndex >= i + 1 )
1023 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1028 void PageTurnView::SetShaderEffect( Actor actor, ShaderEffect shaderEffect )
1030 actor.SetShaderEffect( shaderEffect );
1032 if( actor.GetChildCount() > 0 )
1034 actor.GetChildAt( 0 ).SetShaderEffect(shaderEffect);
1038 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1040 return mPageTurnStartedSignal;
1043 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1045 return mPageTurnFinishedSignal;
1048 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1050 return mPagePanStartedSignal;
1053 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1055 return mPagePanFinishedSignal;
1058 } // namespace Internal
1060 } // namespace Toolkit