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/events/hit-test-algorithm.h>
24 #include <dali/public-api/object/type-registry.h>
25 #include <dali/public-api/render-tasks/render-task-list.h>
28 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
32 namespace //unnamed namespace
35 TypeRegistration mType( typeid(Toolkit::PageTurnView), typeid(Toolkit::Control), NULL );
37 // default grid density for page turn effect, 10 pixels by 10 pixels
38 const float DEFAULT_GRID_DENSITY(10.0f);
40 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
41 const float MINIMUM_START_POSITION_RATIO(0.6f);
43 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
44 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
46 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
47 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
49 // duration of animation, shorter for faster speed
50 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
51 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
53 // the major&minor radius (in pixels) to form an ellipse shape
54 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
55 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
57 // constants for shadow casting
58 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
59 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
61 // constraints ////////////////////////////////////////////////////////////////
63 * Original Center Constraint
65 * This constraint adjusts the original center property of the page turn shader effect
66 * based on the X-direction displacement of the pan gesture
68 struct OriginalCenterConstraint
70 OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
71 : mOldCenter( originalCenter )
73 mNewCenter = originalCenter + offset;
74 mDistance = offset.Length() * 0.5f;
75 mDirection = offset / mDistance;
78 Vector2 operator()(const Vector2& current, const PropertyInput& panDisplacement)
80 float displacement = panDisplacement.GetFloat();
82 if( displacement < mDistance )
84 return mOldCenter + mDirection * displacement;
88 return mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
101 * This constraint adjusts the rotation property of the page actor
102 * based on the X-direction displacement of the pan gesture
104 struct RotationConstraint
106 RotationConstraint( float distance, float pageWidth, bool isTurnBack )
107 : mDistance( distance*0.5f )
109 mStep = 1.f / pageWidth;
110 mSign = isTurnBack ? -1.0f : 1.0f;
111 mConst = isTurnBack ? -1.0f : 0.f;
112 mRotation = isTurnBack ? Quaternion( -Math::PI, Vector3::YAXIS ) : Quaternion( 0.f, Vector3::YAXIS );
115 Quaternion operator()( const Quaternion& current, const PropertyInput& panDisplacement )
117 float displacement = panDisplacement.GetFloat();
119 if( displacement < mDistance)
125 float coef = std::max(-1.0f, mStep*(mDistance-displacement));
126 angle = Math::PI*( mConst + mSign*coef );
127 return Quaternion( angle, Vector3::YAXIS );
135 Quaternion mRotation;
139 * Current Center Constraint
141 * This constraint adjusts the current center property of the page turn shader effect
142 * based on the pan position and the original center position
144 struct CurrentCenterConstraint
146 CurrentCenterConstraint( float pageWidth)
147 : mPageWidth( pageWidth )
149 mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
152 Vector2 operator()( const Vector2& current, const PropertyInput& center, const PropertyInput& originalCenter )
154 Vector2 centerPosition = center.GetVector2();
155 if( centerPosition.x > 0.f )
157 return Vector2( mThres+centerPosition.x*0.5f , centerPosition.y);
161 Vector2 centerOrigin = originalCenter.GetVector2();
162 Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
163 float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
164 // 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
167 coef = (coef+0.225f)/10.0f;
169 return centerOrigin - direction * coef;
177 struct ShadowBlurStrengthConstraint
179 ShadowBlurStrengthConstraint( float thres )
183 float operator()( const float current, const PropertyInput& currentCenter, const PropertyInput& originalCenter, const PropertyInput& panDisplacement)
185 float displacement = panDisplacement.GetFloat();
187 if( EqualsZero(displacement))
189 Vector2 cur = currentCenter.GetVector2();
190 Vector2 ori = originalCenter.GetVector2();
191 blurStrength = 5.f*(ori-cur).Length() / mThres;
195 blurStrength = 1.f - (displacement-2.f*mThres)/mThres;
198 blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
205 bool IsActorHittableFunction( Actor actor, Dali::HitTestAlgorithm::TraverseType type )
207 bool hittable = false;
211 case Dali::HitTestAlgorithm::CHECK_ACTOR:
213 // Check whether the actor is visible and not fully transparent.
214 Property::Index propertyActorHittable = actor.GetPropertyIndex(Toolkit::PageFactory::ACTOR_HITTABLE);
215 if( actor.IsSensitive()
217 && actor.GetCurrentWorldColor().a > 0.01f// not FULLY_TRANSPARENT
218 && ( propertyActorHittable != Property::INVALID_INDEX &&
219 actor.GetProperty<bool>( propertyActorHittable ) ) )
225 case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
227 if( actor.IsSensitive() && actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible.
242 } //unnamed namespace
253 // these several constants are also used in the derived classes
254 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
255 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
256 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
257 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
259 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
260 : Control( REQUIRES_TOUCH_EVENTS ),
261 mPageFactory( pageFactory ),
262 mPageSize( pageSize ),
263 mTotalPageCount( 0 ),
264 mIsEditMode( false ),
265 mNeedOffscreenRendering( false ),
267 mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
268 mCurrentPageIndex( 0 ),
271 mPageUpdated( true ),
272 mDistanceUpCorner( 0.f ),
273 mDistanceBottomCorner( 0.f ),
274 mPanDisplacement( 0.f ),
275 mConstraints( false ),
276 mPageTurnStartedSignal(),
277 mPageTurnFinishedSignal(),
278 mPagePanStartedSignal(),
279 mPagePanFinishedSignal()
281 mPageActors.resize( NUMBER_OF_CACHED_PAGES );
282 mIsAnimating.resize( MAXIMUM_TURNING_NUM );
283 mIsSliding.resize( MAXIMUM_TURNING_NUM );
284 mTurnEffect.resize( MAXIMUM_TURNING_NUM );
285 mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
286 mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
289 PageTurnView::~PageTurnView()
293 void PageTurnView::OnInitialize()
295 // create the two book spine effect for static images, left and right side pages respectively
296 mSpineEffectFront = PageTurnBookSpineEffect::New();
297 mSpineEffectFront.SetIsBackImageVisible( false );
298 mSpineEffectFront.SetPageWidth( mPageSize.width );
299 mSpineEffectFront.SetShadowWidth( 0.f );
300 mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
302 mSpineEffectBack = PageTurnBookSpineEffect::New();
303 mSpineEffectBack.SetIsBackImageVisible( true );
304 mSpineEffectBack.SetPageWidth( mPageSize.width );
305 mSpineEffectBack.SetShadowWidth( 0.f );
306 mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
308 // create the page turn effect objects
309 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
311 mTurnEffect[i] = Toolkit::PageTurnEffect::New( false );
312 mTurnEffect[i].SetProperty( ShaderEffect::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
313 mTurnEffect[i].SetPageSize( mPageSize );
314 mTurnEffect[i].SetShadowWidth(0.f);
315 mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
316 mIsAnimating[i] = false;
317 mIsSliding[i] = false;
318 mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
319 mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
322 mTurningPageLayer = Layer::New();
323 mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
324 // Set control size and the parent origin of turningPageLayer
325 OnPageTurnViewInitialize();
327 mRootOnScreen = Actor::New();
328 mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION );
329 mRootOnScreen.SetSize( mControlSize );
330 Self().Add( mRootOnScreen );
331 mRootOnScreen.Add(mTurningPageLayer);
333 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
334 mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded();
335 if( mNeedOffscreenRendering )
340 // add pages to the scene, and set depth for the stacked pages
341 for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
346 mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
350 // enable the pan gesture which is attached to the control
351 EnableGestureDetection(Gesture::Type(Gesture::Pan));
353 mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage);
356 void PageTurnView::SetupRenderTasks()
358 mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES );
359 mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES );
360 mRenderedPage.resize( NUMBER_OF_CACHED_PAGES );
362 mCameraActor = CameraActor::New(mControlSize);
363 mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
364 mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
365 mCameraActor.SetInheritScale( false );
366 Self().Add(mCameraActor);
368 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
369 for(int i=0; i<NUMBER_OF_CACHED_PAGES; i++)
371 mPageSourceActor[i] = Actor::New();
372 mPageSourceActor[i].SetParentOrigin(ParentOrigin::CENTER);
373 mPageSourceActor[i].SetColorMode( USE_OWN_COLOR );
374 mPageSourceActor[i].SetPositionInheritanceMode( DONT_INHERIT_POSITION );
375 mPageSourceActor[i].SetInheritScale( false );
376 Self().Add( mPageSourceActor[i] );
377 mPageSourceActor[i].SetSensitive( false );
379 mRenderedPage[i] = FrameBufferImage::New( mControlSize.width, mControlSize.height, Pixel::RGB8888, Image::Unused );
380 mOffscreenTask[i] = taskList.CreateTask();
381 mOffscreenTask[i].SetRefreshRate( RenderTask::REFRESH_ONCE );
382 mOffscreenTask[i].SetCameraActor(mCameraActor);
383 mOffscreenTask[i].SetSourceActor( mPageSourceActor[i] );
384 mOffscreenTask[i].SetExclusive(true);
385 mOffscreenTask[i].SetInputEnabled( false );
386 mOffscreenTask[i].SetClearEnabled( true );
387 mOffscreenTask[i].SetClearColor( Vector4(0.f,0.f,0.f,0.f) );
388 mOffscreenTask[i].SetTargetFrameBuffer( mRenderedPage[i] );
389 mOffscreenTask[i].SetScreenToFrameBufferMappingActor( Self() );
393 void PageTurnView::SetupShadowView()
395 mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
396 Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
397 mShadowView.SetParentOrigin( origin );
398 mShadowView.SetAnchorPoint( origin );
399 mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
400 mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
402 mShadowLayer = Layer::New();
403 mShadowLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
404 mRootOnScreen.Add(mShadowLayer);
405 mShadowLayer.Raise();
407 mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
408 mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
409 mShadowPlane.SetSize( mControlSize );
410 mShadowLayer.Add( mShadowPlane );
411 mShadowView.SetShadowPlane( mShadowPlane );
413 mPointLight = Actor::New();
414 mPointLight.SetAnchorPoint( origin );
415 mPointLight.SetParentOrigin( origin );
416 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
417 mRootOnScreen.Add( mPointLight );
418 mShadowView.SetPointLight( mPointLight );
420 mTurningPageLayer.Add( mShadowView );
421 mShadowView.Activate();
424 void PageTurnView::OnControlStageConnection()
427 mTurningPageLayer.RaiseToTop();
430 void PageTurnView::OnControlStageDisconnection()
434 Self().Remove(mPointLight);
435 Self().Remove(mShadowLayer);
436 mTurningPageLayer.Remove( mShadowView );
439 // make sure the status of the control is updated correctly when the pan gesture is interrupted
444 mRootOnScreen.Add(mPanActor);
445 mIsAnimating[mIndex] = false;
446 mPanActor.RemoveConstraints();
447 mTurnEffect[mIndex].RemoveConstraints();
450 SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
454 void PageTurnView::OnControlSizeSet( const Vector3& size )
456 // disable the SetSize of the control from the application
457 Self().SetSize( mControlSize );
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 );
805 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView.GetBlurStrengthPropertyIndex(),
806 Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())),
807 Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())),
808 Source( self, mPropertyPanDisplacement[mIndex] ),
809 ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
810 mShadowView.ApplyConstraint( shadowBlurStrengthConstraint );
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 );
872 Source source(self, mPropertyPanDisplacement[mIndex]);
874 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
875 Constraint originalCenterConstraint = Constraint::New<Vector2>( shaderOriginalCenterPropertyIndex ,
877 OriginalCenterConstraint( mOriginalCenter, offset ));
878 mTurnEffect[mIndex].ApplyConstraint( originalCenterConstraint );
880 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
881 Constraint currentCenterConstraint = Constraint::New<Vector2>( shaderCurrentCenterPropertyIndex,
882 Source(self, mPropertyCurrentCenter[mIndex]),
883 Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex),
884 CurrentCenterConstraint(mPageSize.width));
885 mTurnEffect[mIndex].ApplyConstraint( currentCenterConstraint );
887 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
889 float distance = offset.Length();
890 Constraint rotationConstraint = Constraint::New<Quaternion>( Actor::ROTATION,
891 Source( self, mPropertyPanDisplacement[mIndex] ),
892 RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
893 mPanActor.ApplyConstraint( rotationConstraint );
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].SetOriginalCenter( mOriginalCenter );
906 mPanDisplacement = 0.f;
909 mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
910 mCurrentCenter = currentCenter;
911 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
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,AlphaFunctions::EaseOutSine33);
959 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
960 Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunctions::EaseOutSine33);
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], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
970 mOriginalCenter, AlphaFunctions::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