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>
34 namespace //Unnamed namespace
38 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::PageTurnView, Toolkit::Control, NULL )
39 DALI_TYPE_REGISTRATION_END()
41 // default grid density for page turn effect, 10 pixels by 10 pixels
42 const float DEFAULT_GRID_DENSITY(10.0f);
44 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
45 const float MINIMUM_START_POSITION_RATIO(0.6f);
47 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
48 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
50 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
51 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
53 // duration of animation, shorter for faster speed
54 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
55 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
57 // the major&minor radius (in pixels) to form an ellipse shape
58 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
59 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
61 // constants for shadow casting
62 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
63 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
65 // constraints ////////////////////////////////////////////////////////////////
67 * Original Center Constraint
69 * This constraint adjusts the original center property of the page turn shader effect
70 * based on the X-direction displacement of the pan gesture
72 struct OriginalCenterConstraint
74 OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
75 : mOldCenter( originalCenter )
77 mNewCenter = originalCenter + offset;
78 mDistance = offset.Length() * 0.5f;
79 mDirection = offset / mDistance;
82 void operator()( Vector2& current, const PropertyInputContainer& inputs )
84 float displacement = inputs[0]->GetFloat();
86 if( displacement < mDistance )
88 current = mOldCenter + mDirection * displacement;
92 current = mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
103 * Rotation Constraint
105 * This constraint adjusts the rotation property of the page actor
106 * based on the X-direction displacement of the pan gesture
108 struct RotationConstraint
110 RotationConstraint( float distance, float pageWidth, bool isTurnBack )
111 : mDistance( distance*0.5f )
113 mStep = 1.f / pageWidth;
114 mSign = isTurnBack ? -1.0f : 1.0f;
115 mConst = isTurnBack ? -1.0f : 0.f;
116 mRotation = isTurnBack ? Quaternion( Radian( -Math::PI ), Vector3::YAXIS ) : Quaternion( Radian(0.f), Vector3::YAXIS );
119 void operator()( Quaternion& current, const PropertyInputContainer& inputs )
121 float displacement = inputs[0]->GetFloat();
122 if( displacement < mDistance)
128 float coef = std::max(-1.0f, mStep*(mDistance-displacement));
129 float angle = Math::PI * ( mConst + mSign * coef );
130 current = Quaternion( Radian( angle ), Vector3::YAXIS );
138 Quaternion mRotation;
142 * Current Center Constraint
144 * This constraint adjusts the current center property of the page turn shader effect
145 * based on the pan position and the original center position
147 struct CurrentCenterConstraint
149 CurrentCenterConstraint( float pageWidth )
150 : mPageWidth( pageWidth )
152 mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
155 void operator()( Vector2& current, const PropertyInputContainer& inputs )
157 const Vector2& centerPosition = inputs[0]->GetVector2();
158 if( centerPosition.x > 0.f )
160 current.x = mThres+centerPosition.x * 0.5f;
161 current.y = centerPosition.y;
165 const Vector2& centerOrigin = inputs[1]->GetVector2();
166 Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
167 float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
168 // 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
171 coef = (coef+0.225f)/10.0f;
173 current = centerOrigin - direction * coef;
181 struct ShadowBlurStrengthConstraint
183 ShadowBlurStrengthConstraint( float thres )
187 void operator()( float& blurStrength, const PropertyInputContainer& inputs )
189 float displacement = inputs[2]->GetFloat();
190 if( EqualsZero(displacement))
192 const Vector2& cur = inputs[0]->GetVector2();
193 const Vector2& ori = inputs[1]->GetVector2();
194 blurStrength = 5.f*(ori-cur).Length() / mThres;
198 blurStrength = 1.f - (displacement-2.f*mThres)/mThres;
201 blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
207 bool IsActorHittableFunction( Actor actor, Dali::HitTestAlgorithm::TraverseType type )
209 bool hittable = false;
213 case Dali::HitTestAlgorithm::CHECK_ACTOR:
215 // Check whether the actor is visible and not fully transparent.
216 Property::Index propertyActorHittable = actor.GetPropertyIndex(Toolkit::PageFactory::ACTOR_HITTABLE);
217 if( actor.IsSensitive()
219 && actor.GetCurrentWorldColor().a > 0.01f// not FULLY_TRANSPARENT
220 && ( propertyActorHittable != Property::INVALID_INDEX &&
221 actor.GetProperty<bool>( propertyActorHittable ) ) )
227 case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
229 if( actor.IsSensitive() && actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible.
244 } //unnamed namespace
255 // these several constants are also used in the derived classes
256 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
257 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
258 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
259 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
261 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
262 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS ) ),
263 mPageFactory( pageFactory ),
264 mPageSize( pageSize ),
265 mTotalPageCount( 0 ),
266 mIsEditMode( false ),
267 mNeedOffscreenRendering( false ),
269 mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
270 mCurrentPageIndex( 0 ),
273 mPageUpdated( true ),
274 mDistanceUpCorner( 0.f ),
275 mDistanceBottomCorner( 0.f ),
276 mPanDisplacement( 0.f ),
277 mConstraints( false ),
278 mPageTurnStartedSignal(),
279 mPageTurnFinishedSignal(),
280 mPagePanStartedSignal(),
281 mPagePanFinishedSignal()
283 mPageActors.resize( NUMBER_OF_CACHED_PAGES );
284 mIsAnimating.resize( MAXIMUM_TURNING_NUM );
285 mIsSliding.resize( MAXIMUM_TURNING_NUM );
286 mTurnEffect.resize( MAXIMUM_TURNING_NUM );
287 mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
288 mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
291 PageTurnView::~PageTurnView()
295 void PageTurnView::OnInitialize()
297 // create the two book spine effect for static images, left and right side pages respectively
298 mSpineEffectFront = PageTurnBookSpineEffect::New();
299 mSpineEffectFront.SetIsBackImageVisible( false );
300 mSpineEffectFront.SetPageWidth( mPageSize.width );
301 mSpineEffectFront.SetShadowWidth( 0.f );
302 mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
304 mSpineEffectBack = PageTurnBookSpineEffect::New();
305 mSpineEffectBack.SetIsBackImageVisible( true );
306 mSpineEffectBack.SetPageWidth( mPageSize.width );
307 mSpineEffectBack.SetShadowWidth( 0.f );
308 mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
310 // create the page turn effect objects
311 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
313 mTurnEffect[i] = Toolkit::PageTurnEffect::New( false );
314 mTurnEffect[i].SetProperty( ShaderEffect::Property::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
315 mTurnEffect[i].SetPageSize( mPageSize );
316 mTurnEffect[i].SetShadowWidth(0.f);
317 mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
318 mIsAnimating[i] = false;
319 mIsSliding[i] = false;
320 mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
321 mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
324 mTurningPageLayer = Layer::New();
325 mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
326 // Set control size and the parent origin of turningPageLayer
327 OnPageTurnViewInitialize();
329 mRootOnScreen = Actor::New();
330 mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION );
331 mRootOnScreen.SetSize( mControlSize );
332 Self().Add( mRootOnScreen );
333 mRootOnScreen.Add(mTurningPageLayer);
335 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
336 mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded();
337 if( mNeedOffscreenRendering )
342 // add pages to the scene, and set depth for the stacked pages
343 for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
348 mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
352 // enable the pan gesture which is attached to the control
353 EnableGestureDetection(Gesture::Type(Gesture::Pan));
355 mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage);
358 void PageTurnView::SetupRenderTasks()
360 mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES );
361 mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES );
362 mRenderedPage.resize( NUMBER_OF_CACHED_PAGES );
364 mCameraActor = CameraActor::New(mControlSize);
365 mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
366 mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
367 mCameraActor.SetInheritScale( false );
368 Self().Add(mCameraActor);
370 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
371 for(int i=0; i<NUMBER_OF_CACHED_PAGES; i++)
373 mPageSourceActor[i] = Actor::New();
374 mPageSourceActor[i].SetParentOrigin(ParentOrigin::CENTER);
375 mPageSourceActor[i].SetColorMode( USE_OWN_COLOR );
376 mPageSourceActor[i].SetPositionInheritanceMode( DONT_INHERIT_POSITION );
377 mPageSourceActor[i].SetInheritScale( false );
378 Self().Add( mPageSourceActor[i] );
379 mPageSourceActor[i].SetSensitive( false );
381 mRenderedPage[i] = FrameBufferImage::New( mControlSize.width, mControlSize.height, Pixel::RGB8888, Image::UNUSED );
382 mOffscreenTask[i] = taskList.CreateTask();
383 mOffscreenTask[i].SetRefreshRate( RenderTask::REFRESH_ONCE );
384 mOffscreenTask[i].SetCameraActor(mCameraActor);
385 mOffscreenTask[i].SetSourceActor( mPageSourceActor[i] );
386 mOffscreenTask[i].SetExclusive(true);
387 mOffscreenTask[i].SetInputEnabled( false );
388 mOffscreenTask[i].SetClearEnabled( true );
389 mOffscreenTask[i].SetClearColor( Vector4(0.f,0.f,0.f,0.f) );
390 mOffscreenTask[i].SetTargetFrameBuffer( mRenderedPage[i] );
391 mOffscreenTask[i].SetScreenToFrameBufferMappingActor( Self() );
395 void PageTurnView::SetupShadowView()
397 mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
398 Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
399 mShadowView.SetParentOrigin( origin );
400 mShadowView.SetAnchorPoint( origin );
401 mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
402 mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
404 mShadowLayer = Layer::New();
405 mShadowLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
406 mRootOnScreen.Add(mShadowLayer);
407 mShadowLayer.Raise();
409 mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
410 mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
411 mShadowPlane.SetSize( mControlSize );
412 mShadowLayer.Add( mShadowPlane );
413 mShadowView.SetShadowPlane( mShadowPlane );
415 mPointLight = Actor::New();
416 mPointLight.SetAnchorPoint( origin );
417 mPointLight.SetParentOrigin( origin );
418 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
419 mRootOnScreen.Add( mPointLight );
420 mShadowView.SetPointLight( mPointLight );
422 mTurningPageLayer.Add( mShadowView );
423 mShadowView.Activate();
426 void PageTurnView::OnControlStageConnection()
429 mTurningPageLayer.RaiseToTop();
432 void PageTurnView::OnControlStageDisconnection()
436 Self().Remove(mPointLight);
437 Self().Remove(mShadowLayer);
438 mTurningPageLayer.Remove( mShadowView );
441 // make sure the status of the control is updated correctly when the pan gesture is interrupted
446 mRootOnScreen.Add(mPanActor);
447 mIsAnimating[mIndex] = false;
448 mPanActor.RemoveConstraints();
449 mTurnEffect[mIndex].RemoveConstraints();
452 SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
456 void PageTurnView::OnControlSizeSet( const Vector3& size )
460 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
462 mSpineShadowParameter = spineShadowParameter;
464 // set spine shadow parameter to all the shader effects
465 mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
466 mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
467 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
469 mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
473 Vector2 PageTurnView::GetSpineShadowParameter()
475 return mSpineShadowParameter;
478 void PageTurnView::GoToPage( unsigned int pageId )
480 int pageIdx = static_cast<int>(pageId);
481 // record the new current page index
482 mCurrentPageIndex = pageIdx;
484 // clear the old pages
485 for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
489 mPageActors[i].Unparent();
490 mPageActors[i].Reset();
494 // add the current page and the pages right before and after it
495 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
499 // set ordered depth to the stacked pages
503 unsigned int PageTurnView::GetCurrentPage()
505 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
506 return static_cast< unsigned int >( mCurrentPageIndex );
509 Actor PageTurnView::EnterEditMode()
511 if( mNeedOffscreenRendering )
513 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
517 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
518 mOffscreenTask[index].SetInputEnabled( true );
519 mPageSourceActor[index].SetSensitive( true );
520 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
522 mRootOnScreen.SetSensitive(false);
524 return mPageSourceActor[index].GetChildAt( 0 );
532 void PageTurnView::LeaveEditMode()
534 if( mNeedOffscreenRendering )
536 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
540 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
541 mOffscreenTask[index].SetInputEnabled( false );
542 mPageSourceActor[index].SetSensitive( false );
543 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
545 mRootOnScreen.SetSensitive(true);
549 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
551 if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
553 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
555 Dali::HitTestAlgorithm::Results results;
556 if( !mOffscreenTask[index].GetInputEnabled() )
558 mOffscreenTask[index].SetInputEnabled( true );
559 mPageSourceActor[index].SetSensitive( true );
560 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
561 mOffscreenTask[index].SetInputEnabled( false );
562 mPageSourceActor[index].SetSensitive( false );
566 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
568 actorCoordinates = results.actorCoordinates;
569 return results.actor;
577 void PageTurnView::AddPage( int pageIndex )
579 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
581 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
583 if( mNeedOffscreenRendering )
585 Actor source = mPageFactory.NewPage( pageIndex );
586 if( mPageSourceActor[index].GetChildCount() > 0 )
588 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
590 mPageSourceActor[index].Add( source );
591 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
592 newPage = NewPageFromRenderBuffer( pageIndex );
596 newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
597 DALI_ASSERT_ALWAYS( newPage );
599 newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
600 newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
601 newPage.SetSize( mPageSize );
602 mRootOnScreen.Add( newPage );
603 mPageActors[index] = newPage;
605 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
606 mIsTurnBack[ newPage ] = isLeftSide;
609 // new page is added to the left side, so need to rotate it 180 degrees
610 newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
614 SetShaderEffect( newPage, mSpineEffectFront);
617 // For Portrait, nothing to do
618 // 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
619 OnAddPage( newPage, isLeftSide );
623 void PageTurnView::RemovePage( int pageIndex )
625 if( pageIndex > -1 && pageIndex < mTotalPageCount)
627 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
628 mPageActors[index].Unparent();
629 mIsTurnBack.erase( mPageActors[index] );
630 mPageActors[index].Reset();
631 if( mNeedOffscreenRendering )
633 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
638 void PageTurnView::RenderPage( int pageIndex )
640 if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
641 && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
643 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
644 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
648 void PageTurnView::RefreshAll()
650 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
651 if( mTotalPageCount > 0 )
653 if(mCurrentPageIndex < mTotalPageCount)
655 GoToPage( mCurrentPageIndex );
659 GoToPage( mCurrentPageIndex-- );
664 void PageTurnView::RefreshCurrentPage()
666 RenderPage( mCurrentPageIndex );
669 void PageTurnView::OnPan( const PanGesture& gesture )
673 // when interrupted by the call of DisplayCurrentPageSourceActor(),
674 // make sure the panFinished is always called before stopping to responding the gesture
675 // so the status of the control is updated correctly
679 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
684 // the pan gesture is attached to control itself instead of each page
685 switch( gesture.state )
687 case Gesture::Started:
690 // to find out whether the undergoing turning page number already reaches the maximum allowed
691 // and get one idle index when it is animatable
692 bool animatable = false;
693 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
695 if( !mIsAnimating[mIndex] )
700 if( mIsSliding[mIndex] )
706 mIndex = mIndex % MAXIMUM_TURNING_NUM;
709 if( mPageUpdated && animatable )
711 SetPanActor( gesture.position ); // determine which page actor is panned
712 if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
716 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
724 case Gesture::Continuing:
726 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
729 case Gesture::Finished:
730 case Gesture::Cancelled:
733 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
737 case Gesture::Possible:
745 void PageTurnView::PanStarted( const Vector2& gesturePosition )
747 mPressDownPosition = gesturePosition;
754 mOriginalCenter = gesturePosition;
755 mTurnEffect[mIndex].SetIsTurningBack( mIsTurnBack[ mPanActor] );
757 mPageUpdated = false;
759 // Guard against destruction during signal emission
760 Toolkit::PageTurnView handle( GetOwner() );
761 mPagePanStartedSignal.Emit( handle );
764 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
771 // Guard against destruction during signal emission
772 Toolkit::PageTurnView handle( GetOwner() );
776 // when the touch down position is near the spine
777 // or when the panning goes outwards or some other position which would tear the paper in real situation
778 // we change the start position into the current panning position and update the shader parameters
779 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
780 || gesturePosition.x > mOriginalCenter.x-1.0f
781 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
782 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
784 mOriginalCenter = gesturePosition;
788 mDistanceUpCorner = mOriginalCenter.Length();
789 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
790 mShadowView.Add( mPanActor );
791 SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
792 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
793 mCurrentCenter = mOriginalCenter;
794 mTurnEffect[mIndex].SetCurrentCenter( mCurrentCenter );
795 mPanDisplacement = 0.f;
798 mIsAnimating[mIndex] = true;
800 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
802 mShadowView.RemoveConstraints();
804 self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
806 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
807 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())) );
808 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())) );
809 shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
810 shadowBlurStrengthConstraint.Apply();
815 Vector2 currentCenter = gesturePosition;
817 // Test whether the new current center would tear the paper from the top pine in real situation
818 // we do not forbid this totally, which would restrict the panning gesture too much
819 // instead, set it to the nearest allowable position
820 float distanceUpCorner = currentCenter.Length();
821 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
822 if( distanceUpCorner > mDistanceUpCorner )
824 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
826 // would tear the paper from the bottom spine in real situation
827 if( distanceBottomCorner > mDistanceBottomCorner )
829 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
831 // If direction has a very high y component, reduce it.
832 Vector2 curveDirection = currentCenter - mOriginalCenter;
833 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
835 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
837 // If the vertical distance is high, reduce it
838 float yShift = currentCenter.y - mOriginalCenter.y;
839 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
841 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
844 // use contraints to control the page shape and rotation when the pan position is near the spine
845 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
847 // set the property values used by the constraints
848 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
849 Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
850 Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
852 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
853 // also set up the RotationConstraint to the page actor
857 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
858 if( currentCenter.y >= mOriginalCenter.y )
860 corner = Vector2( 1.1f*mPageSize.width, 0.f );
864 corner = mPageSize*1.1f;
867 Vector2 offset( currentCenter-mOriginalCenter );
868 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
869 /( offset.x*offset.x + offset.y*offset.y );
873 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
874 Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
875 originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
876 originalCenterConstraint.Apply();
878 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
879 Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
880 currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
881 currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
882 currentCenterConstraint.Apply();
884 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
886 float distance = offset.Length();
887 Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
888 rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
889 rotationConstraint.Apply();
891 mConstraints = false;
896 if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
898 mPanActor.RemoveConstraints();
899 mTurnEffect[mIndex].RemoveConstraints();
900 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
902 mPanDisplacement = 0.f;
905 mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
906 mCurrentCenter = currentCenter;
907 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
912 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
914 // Guard against destruction during signal emission
915 Toolkit::PageTurnView handle( GetOwner() );
919 if(!mIsAnimating[mIndex])
921 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
926 mPagePanFinishedSignal.Emit( handle );
928 ImageActor actor = mPanActor;
931 if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
933 // update the pages here instead of in the TurnedOver callback function
934 // as new page is allowed to respond to the pan gesture before other pages finishing animation
935 if(mIsTurnBack[actor])
938 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
939 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
944 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
945 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
949 // set up an animation to turn the page over
951 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
952 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
953 animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
954 width,AlphaFunction::EASE_OUT_SINE);
955 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
956 Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
957 mAnimationActorPair[animation] = actor;
958 mAnimationIndexPair[animation] = mIndex;
960 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
962 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
964 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
965 animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
966 mOriginalCenter, AlphaFunction::LINEAR );
967 mAnimationActorPair[animation] = actor;
968 mAnimationIndexPair[animation] = mIndex;
970 mIsSliding[mIndex] = true;
971 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
973 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
978 // In portrait view, an outwards flick should turn the previous page back
979 // In landscape view, nothing to do
980 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
986 void PageTurnView::TurnedOver( Animation& animation )
988 ImageActor actor = mAnimationActorPair[animation];
989 mIsTurnBack[actor] = !mIsTurnBack[actor];
990 actor.RemoveConstraints();
991 mRootOnScreen.Add(actor);
992 int index = mAnimationIndexPair[animation];
993 mIsAnimating[index] = false;
994 mTurnEffect[index].RemoveConstraints();
995 mAnimationIndexPair.erase( animation );
996 mAnimationActorPair.erase( animation );
998 SetSpineEffect( actor, mIsTurnBack[actor] );
1000 // Guard against destruction during signal emission
1001 Toolkit::PageTurnView handle( GetOwner() );
1002 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1005 void PageTurnView::SliddenBack( Animation& animation )
1007 ImageActor actor = mAnimationActorPair[animation];
1008 mRootOnScreen.Add(actor);
1009 int index = mAnimationIndexPair[animation];
1010 mIsSliding[index] = false;
1011 mIsAnimating[index] = false;
1012 mAnimationIndexPair.erase( animation );
1013 mAnimationActorPair.erase( animation );
1015 SetSpineEffect( actor, mIsTurnBack[actor] );
1017 // Guard against destruction during signal emission
1018 Toolkit::PageTurnView handle( GetOwner() );
1019 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1022 void PageTurnView::OrganizePageDepth()
1024 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1026 if(mCurrentPageIndex+i < mTotalPageCount)
1028 mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1030 if( mCurrentPageIndex >= i + 1 )
1032 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1037 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
1039 SetShaderEffectRecursively( actor, shaderEffect );
1042 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1044 return mPageTurnStartedSignal;
1047 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1049 return mPageTurnFinishedSignal;
1052 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1054 return mPagePanStartedSignal;
1057 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1059 return mPagePanFinishedSignal;
1062 } // namespace Internal
1064 } // namespace Toolkit