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 <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", -1.f );
302 mSpineEffectFront.SetUniform("uPageWidth", mPageSize.width );
303 mSpineEffectFront.SetUniform("uShadowWidth", 0.f );
304 mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
306 mSpineEffectBack = CreatePageTurnBookSpineEffect();
307 mSpineEffectBack.SetUniform("uIsBackImageVisible", 1.f );
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].SetSortModifier( -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::OnStageConnection( unsigned int depth )
431 mTurningPageLayer.RaiseToTop();
434 void PageTurnView::OnStageDisconnection()
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::SetSpineShadowParameter( const Vector2& spineShadowParameter )
460 mSpineShadowParameter = spineShadowParameter;
462 // set spine shadow parameter to all the shader effects
463 mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
464 mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
465 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
467 mTurnEffect[i].SetUniform("uSpineShadowParameter", mSpineShadowParameter );
471 Vector2 PageTurnView::GetSpineShadowParameter()
473 return mSpineShadowParameter;
476 void PageTurnView::GoToPage( unsigned int pageId )
478 int pageIdx = static_cast<int>(pageId);
479 // record the new current page index
480 mCurrentPageIndex = pageIdx;
482 // clear the old pages
483 for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
487 mPageActors[i].Unparent();
488 mPageActors[i].Reset();
492 // add the current page and the pages right before and after it
493 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
497 // set ordered depth to the stacked pages
501 unsigned int PageTurnView::GetCurrentPage()
503 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
504 return static_cast< unsigned int >( mCurrentPageIndex );
507 Actor PageTurnView::EnterEditMode()
509 if( mNeedOffscreenRendering )
511 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
515 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
516 mOffscreenTask[index].SetInputEnabled( true );
517 mPageSourceActor[index].SetSensitive( true );
518 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
520 mRootOnScreen.SetSensitive(false);
522 return mPageSourceActor[index].GetChildAt( 0 );
530 void PageTurnView::LeaveEditMode()
532 if( mNeedOffscreenRendering )
534 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
538 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
539 mOffscreenTask[index].SetInputEnabled( false );
540 mPageSourceActor[index].SetSensitive( false );
541 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
543 mRootOnScreen.SetSensitive(true);
547 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
549 if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
551 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
553 Dali::HitTestAlgorithm::Results results;
554 if( !mOffscreenTask[index].GetInputEnabled() )
556 mOffscreenTask[index].SetInputEnabled( true );
557 mPageSourceActor[index].SetSensitive( true );
558 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
559 mOffscreenTask[index].SetInputEnabled( false );
560 mPageSourceActor[index].SetSensitive( false );
564 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
566 actorCoordinates = results.actorCoordinates;
567 return results.actor;
575 void PageTurnView::AddPage( int pageIndex )
577 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
579 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
581 if( mNeedOffscreenRendering )
583 Actor source = mPageFactory.NewPage( pageIndex );
584 if( mPageSourceActor[index].GetChildCount() > 0 )
586 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
588 mPageSourceActor[index].Add( source );
589 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
590 newPage = NewPageFromRenderBuffer( pageIndex );
594 newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
595 DALI_ASSERT_ALWAYS( newPage );
597 newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
598 newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
599 newPage.SetSize( mPageSize );
600 mRootOnScreen.Add( newPage );
601 mPageActors[index] = newPage;
603 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
604 mIsTurnBack[ newPage ] = isLeftSide;
607 // new page is added to the left side, so need to rotate it 180 degrees
608 newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
612 SetShaderEffect( newPage, mSpineEffectFront);
615 // For Portrait, nothing to do
616 // 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
617 OnAddPage( newPage, isLeftSide );
621 void PageTurnView::RemovePage( int pageIndex )
623 if( pageIndex > -1 && pageIndex < mTotalPageCount)
625 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
626 mPageActors[index].Unparent();
627 mIsTurnBack.erase( mPageActors[index] );
628 mPageActors[index].Reset();
629 if( mNeedOffscreenRendering )
631 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
636 void PageTurnView::RenderPage( int pageIndex )
638 if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
639 && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
641 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
642 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
646 void PageTurnView::RefreshAll()
648 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
649 if( mTotalPageCount > 0 )
651 if(mCurrentPageIndex < mTotalPageCount)
653 GoToPage( mCurrentPageIndex );
657 GoToPage( mCurrentPageIndex-- );
662 void PageTurnView::RefreshCurrentPage()
664 RenderPage( mCurrentPageIndex );
667 void PageTurnView::OnPan( const PanGesture& gesture )
671 // when interrupted by the call of DisplayCurrentPageSourceActor(),
672 // make sure the panFinished is always called before stopping to responding the gesture
673 // so the status of the control is updated correctly
677 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
682 // the pan gesture is attached to control itself instead of each page
683 switch( gesture.state )
685 case Gesture::Started:
688 // to find out whether the undergoing turning page number already reaches the maximum allowed
689 // and get one idle index when it is animatable
690 bool animatable = false;
691 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
693 if( !mIsAnimating[mIndex] )
698 if( mIsSliding[mIndex] )
704 mIndex = mIndex % MAXIMUM_TURNING_NUM;
707 if( mPageUpdated && animatable )
709 SetPanActor( gesture.position ); // determine which page actor is panned
710 if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
714 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
722 case Gesture::Continuing:
724 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
727 case Gesture::Finished:
728 case Gesture::Cancelled:
731 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
735 case Gesture::Possible:
743 void PageTurnView::PanStarted( const Vector2& gesturePosition )
745 mPressDownPosition = gesturePosition;
752 mOriginalCenter = gesturePosition;
753 mTurnEffect[mIndex].SetUniform("uIsTurningBack", mIsTurnBack[ mPanActor] ? 1.f : -1.f);
755 mPageUpdated = false;
757 // Guard against destruction during signal emission
758 Toolkit::PageTurnView handle( GetOwner() );
759 mPagePanStartedSignal.Emit( handle );
762 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
769 // Guard against destruction during signal emission
770 Toolkit::PageTurnView handle( GetOwner() );
774 // when the touch down position is near the spine
775 // or when the panning goes outwards or some other position which would tear the paper in real situation
776 // we change the start position into the current panning position and update the shader parameters
777 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
778 || gesturePosition.x > mOriginalCenter.x-1.0f
779 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
780 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
782 mOriginalCenter = gesturePosition;
786 mDistanceUpCorner = mOriginalCenter.Length();
787 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
788 mShadowView.Add( mPanActor );
789 SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
790 mTurnEffect[mIndex].SetUniform("uOriginalCenter", mOriginalCenter );
791 mCurrentCenter = mOriginalCenter;
792 mTurnEffect[mIndex].SetUniform("uCurrentCenter", mCurrentCenter );
793 mPanDisplacement = 0.f;
796 mIsAnimating[mIndex] = true;
798 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
800 mShadowView.RemoveConstraints();
802 self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
804 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
805 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter")) );
806 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter")) );
807 shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
808 shadowBlurStrengthConstraint.Apply();
813 Vector2 currentCenter = gesturePosition;
815 // Test whether the new current center would tear the paper from the top pine in real situation
816 // we do not forbid this totally, which would restrict the panning gesture too much
817 // instead, set it to the nearest allowable position
818 float distanceUpCorner = currentCenter.Length();
819 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
820 if( distanceUpCorner > mDistanceUpCorner )
822 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
824 // would tear the paper from the bottom spine in real situation
825 if( distanceBottomCorner > mDistanceBottomCorner )
827 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
829 // If direction has a very high y component, reduce it.
830 Vector2 curveDirection = currentCenter - mOriginalCenter;
831 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
833 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
835 // If the vertical distance is high, reduce it
836 float yShift = currentCenter.y - mOriginalCenter.y;
837 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
839 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
842 // use contraints to control the page shape and rotation when the pan position is near the spine
843 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
845 // set the property values used by the constraints
846 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
847 Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
848 Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
850 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
851 // also set up the RotationConstraint to the page actor
855 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
856 if( currentCenter.y >= mOriginalCenter.y )
858 corner = Vector2( 1.1f*mPageSize.width, 0.f );
862 corner = mPageSize*1.1f;
865 Vector2 offset( currentCenter-mOriginalCenter );
866 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
867 /( offset.x*offset.x + offset.y*offset.y );
871 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter");
872 Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
873 originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
874 originalCenterConstraint.Apply();
876 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter");
877 Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
878 currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
879 currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
880 currentCenterConstraint.Apply();
882 PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
884 float distance = offset.Length();
885 Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
886 rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
887 rotationConstraint.Apply();
889 mConstraints = false;
894 if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
896 mPanActor.RemoveConstraints();
897 mTurnEffect[mIndex].RemoveConstraints();
898 mTurnEffect[mIndex].SetUniform("uOriginalCenter",mOriginalCenter );
900 mPanDisplacement = 0.f;
903 mTurnEffect[mIndex].SetUniform("uCurrentCenter", currentCenter );
904 mCurrentCenter = currentCenter;
905 PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
910 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
912 // Guard against destruction during signal emission
913 Toolkit::PageTurnView handle( GetOwner() );
917 if(!mIsAnimating[mIndex])
919 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
924 mPagePanFinishedSignal.Emit( handle );
926 ImageActor actor = mPanActor;
929 if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
931 // update the pages here instead of in the TurnedOver callback function
932 // as new page is allowed to respond to the pan gesture before other pages finishing animation
933 if(mIsTurnBack[actor])
936 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
937 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
942 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
943 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
947 // set up an animation to turn the page over
949 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
950 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
951 animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
952 width,AlphaFunction::EASE_OUT_SINE);
953 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
954 Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
955 mAnimationActorPair[animation] = actor;
956 mAnimationIndexPair[animation] = mIndex;
958 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
960 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
962 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
963 animation.AnimateTo( Property( mTurnEffect[mIndex], "uCurrentCenter" ),
964 mOriginalCenter, AlphaFunction::LINEAR );
965 mAnimationActorPair[animation] = actor;
966 mAnimationIndexPair[animation] = mIndex;
968 mIsSliding[mIndex] = true;
969 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
971 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
976 // In portrait view, an outwards flick should turn the previous page back
977 // In landscape view, nothing to do
978 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
984 void PageTurnView::TurnedOver( Animation& animation )
986 ImageActor actor = mAnimationActorPair[animation];
987 mIsTurnBack[actor] = !mIsTurnBack[actor];
988 actor.RemoveConstraints();
989 mRootOnScreen.Add(actor);
990 int index = mAnimationIndexPair[animation];
991 mIsAnimating[index] = false;
992 mTurnEffect[index].RemoveConstraints();
993 mAnimationIndexPair.erase( animation );
994 mAnimationActorPair.erase( animation );
996 SetSpineEffect( actor, mIsTurnBack[actor] );
998 // Guard against destruction during signal emission
999 Toolkit::PageTurnView handle( GetOwner() );
1000 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1003 void PageTurnView::SliddenBack( Animation& animation )
1005 ImageActor actor = mAnimationActorPair[animation];
1006 mRootOnScreen.Add(actor);
1007 int index = mAnimationIndexPair[animation];
1008 mIsSliding[index] = false;
1009 mIsAnimating[index] = false;
1010 mAnimationIndexPair.erase( animation );
1011 mAnimationActorPair.erase( animation );
1013 SetSpineEffect( actor, mIsTurnBack[actor] );
1015 // Guard against destruction during signal emission
1016 Toolkit::PageTurnView handle( GetOwner() );
1017 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1020 void PageTurnView::OrganizePageDepth()
1022 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1024 if(mCurrentPageIndex+i < mTotalPageCount)
1026 mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetSortModifier( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1028 if( mCurrentPageIndex >= i + 1 )
1030 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetSortModifier( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1035 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
1037 SetShaderEffectRecursively( actor, shaderEffect );
1040 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1042 return mPageTurnStartedSignal;
1045 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1047 return mPageTurnFinishedSignal;
1050 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1052 return mPagePanStartedSignal;
1055 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1057 return mPagePanFinishedSignal;
1060 } // namespace Internal
1062 } // namespace Toolkit