2 * Copyright (c) 2014 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 <dali/public-api/animation/animation.h>
23 #include <dali/public-api/animation/constraint.h>
24 #include <dali/devel-api/events/hit-test-algorithm.h>
25 #include <dali/public-api/object/type-registry.h>
26 #include <dali/devel-api/object/type-registry-helper.h>
27 #include <dali/public-api/render-tasks/render-task-list.h>
30 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
31 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-effect.h>
32 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-book-spine-effect.h>
36 namespace //Unnamed namespace
40 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::PageTurnView, Toolkit::Control, NULL )
41 DALI_TYPE_REGISTRATION_END()
43 // default grid density for page turn effect, 10 pixels by 10 pixels
44 const float DEFAULT_GRID_DENSITY(10.0f);
46 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
47 const float MINIMUM_START_POSITION_RATIO(0.6f);
49 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
50 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
52 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
53 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
55 // duration of animation, shorter for faster speed
56 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
57 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
59 // the major&minor radius (in pixels) to form an ellipse shape
60 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
61 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
63 // constants for shadow casting
64 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
65 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
67 // constraints ////////////////////////////////////////////////////////////////
69 * Original Center Constraint
71 * This constraint adjusts the original center property of the page turn shader effect
72 * based on the X-direction displacement of the pan gesture
74 struct OriginalCenterConstraint
76 OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
77 : mOldCenter( originalCenter )
79 mNewCenter = originalCenter + offset;
80 mDistance = offset.Length() * 0.5f;
81 mDirection = offset / mDistance;
84 void operator()( Vector2& current, const PropertyInputContainer& inputs )
86 float displacement = inputs[0]->GetFloat();
88 if( displacement < mDistance )
90 current = mOldCenter + mDirection * displacement;
94 current = mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
105 * Rotation Constraint
107 * This constraint adjusts the rotation property of the page actor
108 * based on the X-direction displacement of the pan gesture
110 struct RotationConstraint
112 RotationConstraint( float distance, float pageWidth, bool isTurnBack )
113 : mDistance( distance*0.5f )
115 mStep = 1.f / pageWidth;
116 mSign = isTurnBack ? -1.0f : 1.0f;
117 mConst = isTurnBack ? -1.0f : 0.f;
118 mRotation = isTurnBack ? Quaternion( Radian( -Math::PI ), Vector3::YAXIS ) : Quaternion( Radian(0.f), Vector3::YAXIS );
121 void operator()( Quaternion& current, const PropertyInputContainer& inputs )
123 float displacement = inputs[0]->GetFloat();
124 if( displacement < mDistance)
130 float coef = std::max(-1.0f, mStep*(mDistance-displacement));
131 float angle = Math::PI * ( mConst + mSign * coef );
132 current = Quaternion( Radian( angle ), Vector3::YAXIS );
140 Quaternion mRotation;
144 * Current Center Constraint
146 * This constraint adjusts the current center property of the page turn shader effect
147 * based on the pan position and the original center position
149 struct CurrentCenterConstraint
151 CurrentCenterConstraint( float pageWidth )
152 : mPageWidth( pageWidth )
154 mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
157 void operator()( Vector2& current, const PropertyInputContainer& inputs )
159 const Vector2& centerPosition = inputs[0]->GetVector2();
160 if( centerPosition.x > 0.f )
162 current.x = mThres+centerPosition.x * 0.5f;
163 current.y = centerPosition.y;
167 const Vector2& centerOrigin = inputs[1]->GetVector2();
168 Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
169 float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
170 // 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
173 coef = (coef+0.225f)/10.0f;
175 current = centerOrigin - direction * coef;
183 struct ShadowBlurStrengthConstraint
185 ShadowBlurStrengthConstraint( float thres )
189 void operator()( float& blurStrength, const PropertyInputContainer& inputs )
191 float displacement = inputs[2]->GetFloat();
192 if( EqualsZero(displacement))
194 const Vector2& cur = inputs[0]->GetVector2();
195 const Vector2& ori = inputs[1]->GetVector2();
196 blurStrength = 5.f*(ori-cur).Length() / mThres;
200 blurStrength = 1.f - (displacement-2.f*mThres)/mThres;
203 blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
209 bool IsActorHittableFunction( Actor actor, Dali::HitTestAlgorithm::TraverseType type )
211 bool hittable = false;
215 case Dali::HitTestAlgorithm::CHECK_ACTOR:
217 // Check whether the actor is visible and not fully transparent.
218 Property::Index propertyActorHittable = actor.GetPropertyIndex(Toolkit::PageFactory::ACTOR_HITTABLE);
219 if( actor.IsSensitive()
221 && actor.GetCurrentWorldColor().a > 0.01f// not FULLY_TRANSPARENT
222 && ( propertyActorHittable != Property::INVALID_INDEX &&
223 actor.GetProperty<bool>( propertyActorHittable ) ) )
229 case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
231 if( actor.IsSensitive() && actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible.
246 } //unnamed namespace
257 // these several constants are also used in the derived classes
258 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
259 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
260 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
261 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
263 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
264 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS ) ),
265 mPageFactory( pageFactory ),
266 mPageSize( pageSize ),
267 mTotalPageCount( 0 ),
268 mIsEditMode( false ),
269 mNeedOffscreenRendering( false ),
271 mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
272 mCurrentPageIndex( 0 ),
275 mPageUpdated( true ),
276 mDistanceUpCorner( 0.f ),
277 mDistanceBottomCorner( 0.f ),
278 mPanDisplacement( 0.f ),
279 mConstraints( false ),
280 mPageTurnStartedSignal(),
281 mPageTurnFinishedSignal(),
282 mPagePanStartedSignal(),
283 mPagePanFinishedSignal()
285 mPageActors.resize( NUMBER_OF_CACHED_PAGES );
286 mIsAnimating.resize( MAXIMUM_TURNING_NUM );
287 mIsSliding.resize( MAXIMUM_TURNING_NUM );
288 mTurnEffect.resize( MAXIMUM_TURNING_NUM );
289 mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
290 mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
293 PageTurnView::~PageTurnView()
297 void PageTurnView::OnInitialize()
299 // create the two book spine effect for static images, left and right side pages respectively
300 mSpineEffectFront = CreatePageTurnBookSpineEffect();
301 mSpineEffectFront.SetUniform("uIsBackImageVisible", false );
302 mSpineEffectFront.SetUniform("uPageWidth", mPageSize.width );
303 mSpineEffectFront.SetUniform("uShadowWidth", 0.f );
304 mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
306 mSpineEffectBack = CreatePageTurnBookSpineEffect();
307 mSpineEffectBack.SetUniform("uIsBackImageVisible", true );
308 mSpineEffectBack.SetUniform("uPageWidth", mPageSize.width );
309 mSpineEffectBack.SetUniform("uShadowWidth", 0.f );
310 mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
312 // create the page turn effect objects
313 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
315 mTurnEffect[i] = CreatePageTurnEffect( false );
316 mTurnEffect[i].SetProperty( ShaderEffect::Property::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
317 mTurnEffect[i].SetUniform( "uPageSize", mPageSize );
318 mTurnEffect[i].SetUniform( "uShadowWidth", 0.f);
319 mTurnEffect[i].SetUniform( "uSpineShadowParameter", mSpineShadowParameter );
320 mIsAnimating[i] = false;
321 mIsSliding[i] = false;
322 mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
323 mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
326 mTurningPageLayer = Layer::New();
327 mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
328 // Set control size and the parent origin of turningPageLayer
329 OnPageTurnViewInitialize();
331 mRootOnScreen = Actor::New();
332 mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION );
333 mRootOnScreen.SetSize( mControlSize );
334 Self().Add( mRootOnScreen );
335 mRootOnScreen.Add(mTurningPageLayer);
337 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
338 mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded();
339 if( mNeedOffscreenRendering )
344 // add pages to the scene, and set depth for the stacked pages
345 for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
350 mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
354 // enable the pan gesture which is attached to the control
355 EnableGestureDetection(Gesture::Type(Gesture::Pan));
357 mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage);
360 void PageTurnView::SetupRenderTasks()
362 mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES );
363 mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES );
364 mRenderedPage.resize( NUMBER_OF_CACHED_PAGES );
366 mCameraActor = CameraActor::New(mControlSize);
367 mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
368 mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
369 mCameraActor.SetInheritScale( false );
370 Self().Add(mCameraActor);
372 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
373 for(int i=0; i<NUMBER_OF_CACHED_PAGES; i++)
375 mPageSourceActor[i] = Actor::New();
376 mPageSourceActor[i].SetParentOrigin(ParentOrigin::CENTER);
377 mPageSourceActor[i].SetColorMode( USE_OWN_COLOR );
378 mPageSourceActor[i].SetPositionInheritanceMode( DONT_INHERIT_POSITION );
379 mPageSourceActor[i].SetInheritScale( false );
380 Self().Add( mPageSourceActor[i] );
381 mPageSourceActor[i].SetSensitive( false );
383 mRenderedPage[i] = FrameBufferImage::New( mControlSize.width, mControlSize.height, Pixel::RGB8888, Image::UNUSED );
384 mOffscreenTask[i] = taskList.CreateTask();
385 mOffscreenTask[i].SetRefreshRate( RenderTask::REFRESH_ONCE );
386 mOffscreenTask[i].SetCameraActor(mCameraActor);
387 mOffscreenTask[i].SetSourceActor( mPageSourceActor[i] );
388 mOffscreenTask[i].SetExclusive(true);
389 mOffscreenTask[i].SetInputEnabled( false );
390 mOffscreenTask[i].SetClearEnabled( true );
391 mOffscreenTask[i].SetClearColor( Vector4(0.f,0.f,0.f,0.f) );
392 mOffscreenTask[i].SetTargetFrameBuffer( mRenderedPage[i] );
393 mOffscreenTask[i].SetScreenToFrameBufferMappingActor( Self() );
397 void PageTurnView::SetupShadowView()
399 mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
400 Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
401 mShadowView.SetParentOrigin( origin );
402 mShadowView.SetAnchorPoint( origin );
403 mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
404 mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
406 mShadowLayer = Layer::New();
407 mShadowLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
408 mRootOnScreen.Add(mShadowLayer);
409 mShadowLayer.Raise();
411 mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
412 mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
413 mShadowPlane.SetSize( mControlSize );
414 mShadowLayer.Add( mShadowPlane );
415 mShadowView.SetShadowPlane( mShadowPlane );
417 mPointLight = Actor::New();
418 mPointLight.SetAnchorPoint( origin );
419 mPointLight.SetParentOrigin( origin );
420 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
421 mRootOnScreen.Add( mPointLight );
422 mShadowView.SetPointLight( mPointLight );
424 mTurningPageLayer.Add( mShadowView );
425 mShadowView.Activate();
428 void PageTurnView::OnControlStageConnection()
431 mTurningPageLayer.RaiseToTop();
434 void PageTurnView::OnControlStageDisconnection()
438 Self().Remove(mPointLight);
439 Self().Remove(mShadowLayer);
440 mTurningPageLayer.Remove( mShadowView );
443 // make sure the status of the control is updated correctly when the pan gesture is interrupted
448 mRootOnScreen.Add(mPanActor);
449 mIsAnimating[mIndex] = false;
450 mPanActor.RemoveConstraints();
451 mTurnEffect[mIndex].RemoveConstraints();
454 SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
458 void PageTurnView::OnControlSizeSet( const Vector3& size )
460 // disable the SetSize of the control from the application
461 Self().SetSize( mControlSize );
464 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
466 mSpineShadowParameter = spineShadowParameter;
468 // set spine shadow parameter to all the shader effects
469 mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
470 mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
471 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
473 mTurnEffect[i].SetUniform("uSpineShadowParameter", mSpineShadowParameter );
477 Vector2 PageTurnView::GetSpineShadowParameter()
479 return mSpineShadowParameter;
482 void PageTurnView::GoToPage( unsigned int pageId )
484 int pageIdx = static_cast<int>(pageId);
485 // record the new current page index
486 mCurrentPageIndex = pageIdx;
488 // clear the old pages
489 for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
493 mPageActors[i].Unparent();
494 mPageActors[i].Reset();
498 // add the current page and the pages right before and after it
499 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
503 // set ordered depth to the stacked pages
507 unsigned int PageTurnView::GetCurrentPage()
509 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
510 return static_cast< unsigned int >( mCurrentPageIndex );
513 Actor PageTurnView::EnterEditMode()
515 if( mNeedOffscreenRendering )
517 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
521 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
522 mOffscreenTask[index].SetInputEnabled( true );
523 mPageSourceActor[index].SetSensitive( true );
524 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
526 mRootOnScreen.SetSensitive(false);
528 return mPageSourceActor[index].GetChildAt( 0 );
536 void PageTurnView::LeaveEditMode()
538 if( mNeedOffscreenRendering )
540 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
544 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
545 mOffscreenTask[index].SetInputEnabled( false );
546 mPageSourceActor[index].SetSensitive( false );
547 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
549 mRootOnScreen.SetSensitive(true);
553 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
555 if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
557 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
559 Dali::HitTestAlgorithm::Results results;
560 if( !mOffscreenTask[index].GetInputEnabled() )
562 mOffscreenTask[index].SetInputEnabled( true );
563 mPageSourceActor[index].SetSensitive( true );
564 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
565 mOffscreenTask[index].SetInputEnabled( false );
566 mPageSourceActor[index].SetSensitive( false );
570 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
572 actorCoordinates = results.actorCoordinates;
573 return results.actor;
581 void PageTurnView::AddPage( int pageIndex )
583 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
585 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
587 if( mNeedOffscreenRendering )
589 Actor source = mPageFactory.NewPage( pageIndex );
590 if( mPageSourceActor[index].GetChildCount() > 0 )
592 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
594 mPageSourceActor[index].Add( source );
595 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
596 newPage = NewPageFromRenderBuffer( pageIndex );
600 newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
601 DALI_ASSERT_ALWAYS( newPage );
603 newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
604 newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
605 newPage.SetSize( mPageSize );
606 mRootOnScreen.Add( newPage );
607 mPageActors[index] = newPage;
609 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
610 mIsTurnBack[ newPage ] = isLeftSide;
613 // new page is added to the left side, so need to rotate it 180 degrees
614 newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
618 SetShaderEffect( newPage, mSpineEffectFront);
621 // For Portrait, nothing to do
622 // 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
623 OnAddPage( newPage, isLeftSide );
627 void PageTurnView::RemovePage( int pageIndex )
629 if( pageIndex > -1 && pageIndex < mTotalPageCount)
631 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
632 mPageActors[index].Unparent();
633 mIsTurnBack.erase( mPageActors[index] );
634 mPageActors[index].Reset();
635 if( mNeedOffscreenRendering )
637 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
642 void PageTurnView::RenderPage( int pageIndex )
644 if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
645 && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
647 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
648 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
652 void PageTurnView::RefreshAll()
654 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
655 if( mTotalPageCount > 0 )
657 if(mCurrentPageIndex < mTotalPageCount)
659 GoToPage( mCurrentPageIndex );
663 GoToPage( mCurrentPageIndex-- );
668 void PageTurnView::RefreshCurrentPage()
670 RenderPage( mCurrentPageIndex );
673 void PageTurnView::OnPan( const PanGesture& gesture )
677 // when interrupted by the call of DisplayCurrentPageSourceActor(),
678 // make sure the panFinished is always called before stopping to responding the gesture
679 // so the status of the control is updated correctly
683 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
688 // the pan gesture is attached to control itself instead of each page
689 switch( gesture.state )
691 case Gesture::Started:
694 // to find out whether the undergoing turning page number already reaches the maximum allowed
695 // and get one idle index when it is animatable
696 bool animatable = false;
697 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
699 if( !mIsAnimating[mIndex] )
704 if( mIsSliding[mIndex] )
710 mIndex = mIndex % MAXIMUM_TURNING_NUM;
713 if( mPageUpdated && animatable )
715 SetPanActor( gesture.position ); // determine which page actor is panned
716 if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
720 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
728 case Gesture::Continuing:
730 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
733 case Gesture::Finished:
734 case Gesture::Cancelled:
737 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
741 case Gesture::Possible:
749 void PageTurnView::PanStarted( const Vector2& gesturePosition )
751 mPressDownPosition = gesturePosition;
758 mOriginalCenter = gesturePosition;
759 mTurnEffect[mIndex].SetUniform("uIsTurningBack", mIsTurnBack[ mPanActor] );
761 mPageUpdated = false;
763 // Guard against destruction during signal emission
764 Toolkit::PageTurnView handle( GetOwner() );
765 mPagePanStartedSignal.Emit( handle );
768 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
775 // Guard against destruction during signal emission
776 Toolkit::PageTurnView handle( GetOwner() );
780 // when the touch down position is near the spine
781 // or when the panning goes outwards or some other position which would tear the paper in real situation
782 // we change the start position into the current panning position and update the shader parameters
783 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
784 || gesturePosition.x > mOriginalCenter.x-1.0f
785 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
786 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
788 mOriginalCenter = gesturePosition;
792 mDistanceUpCorner = mOriginalCenter.Length();
793 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
794 mShadowView.Add( mPanActor );
795 SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
796 mTurnEffect[mIndex].SetUniform("uOriginalCenter", mOriginalCenter );
797 mCurrentCenter = mOriginalCenter;
798 mTurnEffect[mIndex].SetUniform("uCurrentCenter", mCurrentCenter );
799 mPanDisplacement = 0.f;
802 mIsAnimating[mIndex] = true;
804 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
806 mShadowView.RemoveConstraints();
808 self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
810 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
811 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter")) );
812 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter")) );
813 shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
814 shadowBlurStrengthConstraint.Apply();
819 Vector2 currentCenter = gesturePosition;
821 // Test whether the new current center would tear the paper from the top pine in real situation
822 // we do not forbid this totally, which would restrict the panning gesture too much
823 // instead, set it to the nearest allowable position
824 float distanceUpCorner = currentCenter.Length();
825 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
826 if( distanceUpCorner > mDistanceUpCorner )
828 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
830 // would tear the paper from the bottom spine in real situation
831 if( distanceBottomCorner > mDistanceBottomCorner )
833 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
835 // If direction has a very high y component, reduce it.
836 Vector2 curveDirection = currentCenter - mOriginalCenter;
837 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
839 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
841 // If the vertical distance is high, reduce it
842 float yShift = currentCenter.y - mOriginalCenter.y;
843 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
845 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
848 // use contraints to control the page shape and rotation when the pan position is near the spine
849 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
851 // set the property values used by the constraints
852 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
853 Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
854 Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
856 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
857 // also set up the RotationConstraint to the page actor
861 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
862 if( currentCenter.y >= mOriginalCenter.y )
864 corner = Vector2( 1.1f*mPageSize.width, 0.f );
868 corner = mPageSize*1.1f;
871 Vector2 offset( currentCenter-mOriginalCenter );
872 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
873 /( offset.x*offset.x + offset.y*offset.y );
877 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter");
878 Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
879 originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
880 originalCenterConstraint.Apply();
882 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter");
883 Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
884 currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
885 currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
886 currentCenterConstraint.Apply();
888 PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
890 float distance = offset.Length();
891 Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
892 rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
893 rotationConstraint.Apply();
895 mConstraints = false;
900 if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
902 mPanActor.RemoveConstraints();
903 mTurnEffect[mIndex].RemoveConstraints();
904 mTurnEffect[mIndex].SetUniform("uOriginalCenter",mOriginalCenter );
906 mPanDisplacement = 0.f;
909 mTurnEffect[mIndex].SetUniform("uCurrentCenter", currentCenter );
910 mCurrentCenter = currentCenter;
911 PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
916 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
918 // Guard against destruction during signal emission
919 Toolkit::PageTurnView handle( GetOwner() );
923 if(!mIsAnimating[mIndex])
925 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
930 mPagePanFinishedSignal.Emit( handle );
932 ImageActor actor = mPanActor;
935 if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
937 // update the pages here instead of in the TurnedOver callback function
938 // as new page is allowed to respond to the pan gesture before other pages finishing animation
939 if(mIsTurnBack[actor])
942 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
943 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
948 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
949 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
953 // set up an animation to turn the page over
955 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
956 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
957 animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
958 width,AlphaFunction::EASE_OUT_SINE);
959 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
960 Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
961 mAnimationActorPair[animation] = actor;
962 mAnimationIndexPair[animation] = mIndex;
964 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
966 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
968 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
969 animation.AnimateTo( Property( mTurnEffect[mIndex], "uCurrentCenter" ),
970 mOriginalCenter, AlphaFunction::LINEAR );
971 mAnimationActorPair[animation] = actor;
972 mAnimationIndexPair[animation] = mIndex;
974 mIsSliding[mIndex] = true;
975 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
977 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
982 // In portrait view, an outwards flick should turn the previous page back
983 // In landscape view, nothing to do
984 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
990 void PageTurnView::TurnedOver( Animation& animation )
992 ImageActor actor = mAnimationActorPair[animation];
993 mIsTurnBack[actor] = !mIsTurnBack[actor];
994 actor.RemoveConstraints();
995 mRootOnScreen.Add(actor);
996 int index = mAnimationIndexPair[animation];
997 mIsAnimating[index] = false;
998 mTurnEffect[index].RemoveConstraints();
999 mAnimationIndexPair.erase( animation );
1000 mAnimationActorPair.erase( animation );
1002 SetSpineEffect( actor, mIsTurnBack[actor] );
1004 // Guard against destruction during signal emission
1005 Toolkit::PageTurnView handle( GetOwner() );
1006 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1009 void PageTurnView::SliddenBack( Animation& animation )
1011 ImageActor actor = mAnimationActorPair[animation];
1012 mRootOnScreen.Add(actor);
1013 int index = mAnimationIndexPair[animation];
1014 mIsSliding[index] = false;
1015 mIsAnimating[index] = false;
1016 mAnimationIndexPair.erase( animation );
1017 mAnimationActorPair.erase( animation );
1019 SetSpineEffect( actor, mIsTurnBack[actor] );
1021 // Guard against destruction during signal emission
1022 Toolkit::PageTurnView handle( GetOwner() );
1023 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1026 void PageTurnView::OrganizePageDepth()
1028 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1030 if(mCurrentPageIndex+i < mTotalPageCount)
1032 mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1034 if( mCurrentPageIndex >= i + 1 )
1036 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1041 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
1043 SetShaderEffectRecursively( actor, shaderEffect );
1046 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1048 return mPageTurnStartedSignal;
1051 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1053 return mPageTurnFinishedSignal;
1056 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1058 return mPagePanStartedSignal;
1061 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1063 return mPagePanFinishedSignal;
1066 } // namespace Internal
1068 } // namespace Toolkit