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 mIsEditMode( false ),
265 mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
266 mCurrentPageIndex( 0 ),
269 mPageUpdated( true ),
270 mPageTurnStartedSignal(),
271 mPageTurnFinishedSignal(),
272 mPagePanStartedSignal(),
273 mPagePanFinishedSignal()
275 mPageActors.resize( NUMBER_OF_CACHED_PAGES );
276 mIsAnimating.resize( MAXIMUM_TURNING_NUM );
277 mIsSliding.resize( MAXIMUM_TURNING_NUM );
278 mTurnEffect.resize( MAXIMUM_TURNING_NUM );
279 mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
280 mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
283 PageTurnView::~PageTurnView()
287 void PageTurnView::OnInitialize()
289 // create the two book spine effect for static images, left and right side pages respectively
290 mSpineEffectFront = PageTurnBookSpineEffect::New();
291 mSpineEffectFront.SetIsBackImageVisible( false );
292 mSpineEffectFront.SetPageWidth( mPageSize.width );
293 mSpineEffectFront.SetShadowWidth( 0.f );
294 mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
296 mSpineEffectBack = PageTurnBookSpineEffect::New();
297 mSpineEffectBack.SetIsBackImageVisible( true );
298 mSpineEffectBack.SetPageWidth( mPageSize.width );
299 mSpineEffectBack.SetShadowWidth( 0.f );
300 mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
302 // create the page turn effect objects
303 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
305 mTurnEffect[i] = Toolkit::PageTurnEffect::New( false );
306 mTurnEffect[i].SetProperty( ShaderEffect::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
307 mTurnEffect[i].SetPageSize( mPageSize );
308 mTurnEffect[i].SetShadowWidth(0.f);
309 mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
310 mIsAnimating[i] = false;
311 mIsSliding[i] = false;
312 mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
313 mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
316 mTurningPageLayer = Layer::New();
317 mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
318 // Set control size and the parent origin of turningPageLayer
319 OnPageTurnViewInitialize();
321 mRootOnScreen = Actor::New();
322 mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION );
323 mRootOnScreen.SetSize( mControlSize );
324 Self().Add( mRootOnScreen );
325 mRootOnScreen.Add(mTurningPageLayer);
327 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
328 mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded();
329 if( mNeedOffscreenRendering )
334 // add pages to the scene, and set depth for the stacked pages
335 for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
340 mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
344 // enable the pan gesture which is attached to the control
345 EnableGestureDetection(Gesture::Type(Gesture::Pan));
347 mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage);
350 void PageTurnView::SetupRenderTasks()
352 mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES );
353 mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES );
354 mRenderedPage.resize( NUMBER_OF_CACHED_PAGES );
356 mCameraActor = CameraActor::New(mControlSize);
357 mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
358 mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
359 mCameraActor.SetInheritScale( false );
360 Self().Add(mCameraActor);
362 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
363 for(int i=0; i<NUMBER_OF_CACHED_PAGES; i++)
365 mPageSourceActor[i] = Actor::New();
366 mPageSourceActor[i].SetParentOrigin(ParentOrigin::CENTER);
367 mPageSourceActor[i].SetColorMode( USE_OWN_COLOR );
368 mPageSourceActor[i].SetPositionInheritanceMode( DONT_INHERIT_POSITION );
369 mPageSourceActor[i].SetInheritScale( false );
370 Self().Add( mPageSourceActor[i] );
371 mPageSourceActor[i].SetSensitive( false );
373 mRenderedPage[i] = FrameBufferImage::New( mControlSize.width, mControlSize.height, Pixel::RGB8888, Image::Unused );
374 mOffscreenTask[i] = taskList.CreateTask();
375 mOffscreenTask[i].SetRefreshRate( RenderTask::REFRESH_ONCE );
376 mOffscreenTask[i].SetCameraActor(mCameraActor);
377 mOffscreenTask[i].SetSourceActor( mPageSourceActor[i] );
378 mOffscreenTask[i].SetExclusive(true);
379 mOffscreenTask[i].SetInputEnabled( false );
380 mOffscreenTask[i].SetClearEnabled( true );
381 mOffscreenTask[i].SetClearColor( Vector4(0.f,0.f,0.f,0.f) );
382 mOffscreenTask[i].SetTargetFrameBuffer( mRenderedPage[i] );
383 mOffscreenTask[i].SetScreenToFrameBufferMappingActor( Self() );
387 void PageTurnView::SetupShadowView()
389 mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
390 Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
391 mShadowView.SetParentOrigin( origin );
392 mShadowView.SetAnchorPoint( origin );
393 mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
394 mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
396 mShadowLayer = Layer::New();
397 mShadowLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
398 mRootOnScreen.Add(mShadowLayer);
399 mShadowLayer.Raise();
401 mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
402 mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
403 mShadowPlane.SetSize( mControlSize );
404 mShadowLayer.Add( mShadowPlane );
405 mShadowView.SetShadowPlane( mShadowPlane );
407 mPointLight = Actor::New();
408 mPointLight.SetAnchorPoint( origin );
409 mPointLight.SetParentOrigin( origin );
410 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
411 mRootOnScreen.Add( mPointLight );
412 mShadowView.SetPointLight( mPointLight );
414 mTurningPageLayer.Add( mShadowView );
415 mShadowView.Activate();
418 void PageTurnView::OnControlStageConnection()
421 mTurningPageLayer.RaiseToTop();
424 void PageTurnView::OnControlStageDisconnection()
428 Self().Remove(mPointLight);
429 Self().Remove(mShadowLayer);
430 mTurningPageLayer.Remove( mShadowView );
433 // make sure the status of the control is updated correctly when the pan gesture is interrupted
438 mRootOnScreen.Add(mPanActor);
439 mIsAnimating[mIndex] = false;
440 mPanActor.RemoveConstraints();
441 mTurnEffect[mIndex].RemoveConstraints();
444 SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
448 void PageTurnView::OnControlSizeSet( const Vector3& size )
450 // disable the SetSize of the control from the application
451 Self().SetSize( mControlSize );
454 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
456 mSpineShadowParameter = spineShadowParameter;
458 // set spine shadow parameter to all the shader effects
459 mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
460 mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
461 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
463 mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
467 Vector2 PageTurnView::GetSpineShadowParameter()
469 return mSpineShadowParameter;
472 void PageTurnView::GoToPage( unsigned int pageId )
474 int pageIdx = static_cast<int>(pageId);
475 // record the new current page index
476 mCurrentPageIndex = pageIdx;
478 // clear the old pages
479 for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
483 mPageActors[i].Unparent();
484 mPageActors[i].Reset();
488 // add the current page and the pages right before and after it
489 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
493 // set ordered depth to the stacked pages
497 unsigned int PageTurnView::GetCurrentPage()
499 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
500 return static_cast< unsigned int >( mCurrentPageIndex );
503 Actor PageTurnView::EnterEditMode()
505 if( mNeedOffscreenRendering )
507 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
511 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
512 mOffscreenTask[index].SetInputEnabled( true );
513 mPageSourceActor[index].SetSensitive( true );
514 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
516 mRootOnScreen.SetSensitive(false);
518 return mPageSourceActor[index].GetChildAt( 0 );
526 void PageTurnView::LeaveEditMode()
528 if( mNeedOffscreenRendering )
530 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
534 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
535 mOffscreenTask[index].SetInputEnabled( false );
536 mPageSourceActor[index].SetSensitive( false );
537 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
539 mRootOnScreen.SetSensitive(true);
543 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
545 if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
547 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
549 Dali::HitTestAlgorithm::Results results;
550 if( !mOffscreenTask[index].GetInputEnabled() )
552 mOffscreenTask[index].SetInputEnabled( true );
553 mPageSourceActor[index].SetSensitive( true );
554 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
555 mOffscreenTask[index].SetInputEnabled( false );
556 mPageSourceActor[index].SetSensitive( false );
560 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
562 actorCoordinates = results.actorCoordinates;
563 return results.actor;
571 void PageTurnView::AddPage( int pageIndex )
573 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
575 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
577 if( mNeedOffscreenRendering )
579 Actor source = mPageFactory.NewPage( pageIndex );
580 if( mPageSourceActor[index].GetChildCount() > 0 )
582 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
584 mPageSourceActor[index].Add( source );
585 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
586 newPage = NewPageFromRenderBuffer( pageIndex );
590 newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
591 DALI_ASSERT_ALWAYS( newPage );
593 newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
594 newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
595 newPage.SetSize( mPageSize );
596 mRootOnScreen.Add( newPage );
597 mPageActors[index] = newPage;
599 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
600 mIsTurnBack[ newPage ] = isLeftSide;
603 // new page is added to the left side, so need to rotate it 180 degrees
604 newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
608 SetShaderEffect( newPage, mSpineEffectFront);
611 // For Portrait, nothing to do
612 // 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
613 OnAddPage( newPage, isLeftSide );
617 void PageTurnView::RemovePage( int pageIndex )
619 if( pageIndex > -1 && pageIndex < mTotalPageCount)
621 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
622 mPageActors[index].Unparent();
623 mIsTurnBack.erase( mPageActors[index] );
624 mPageActors[index].Reset();
625 if( mNeedOffscreenRendering )
627 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
632 void PageTurnView::RenderPage( int pageIndex )
634 if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
635 && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
637 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
638 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
642 void PageTurnView::RefreshAll()
644 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
645 if( mTotalPageCount > 0 )
647 if(mCurrentPageIndex < mTotalPageCount)
649 GoToPage( mCurrentPageIndex );
653 GoToPage( mCurrentPageIndex-- );
658 void PageTurnView::RefreshCurrentPage()
660 RenderPage( mCurrentPageIndex );
663 void PageTurnView::OnPan( PanGesture gesture )
667 // when interrupted by the call of DisplayCurrentPageSourceActor(),
668 // make sure the panFinished is always called before stopping to responding the gesture
669 // so the status of the control is updated correctly
673 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
678 // the pan gesture is attached to control itself instead of each page
679 switch( gesture.state )
681 case Gesture::Started:
684 // to find out whether the undergoing turning page number already reaches the maximum allowed
685 // and get one idle index when it is animatable
686 bool animatable = false;
687 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
689 if( !mIsAnimating[mIndex] )
694 if( mIsSliding[mIndex] )
700 mIndex = mIndex % MAXIMUM_TURNING_NUM;
703 if( mPageUpdated && animatable )
705 SetPanActor( gesture.position ); // determine which page actor is panned
706 if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
710 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
718 case Gesture::Continuing:
720 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
723 case Gesture::Finished:
724 case Gesture::Cancelled:
727 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
731 case Gesture::Possible:
739 void PageTurnView::PanStarted( const Vector2& gesturePosition )
741 mPressDownPosition = gesturePosition;
748 mOriginalCenter = gesturePosition;
749 mTurnEffect[mIndex].SetIsTurningBack( mIsTurnBack[ mPanActor] );
751 mPageUpdated = false;
753 // Guard against destruction during signal emission
754 Toolkit::PageTurnView handle( GetOwner() );
755 mPagePanStartedSignal.Emit( handle );
758 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
765 // Guard against destruction during signal emission
766 Toolkit::PageTurnView handle( GetOwner() );
770 // when the touch down position is near the spine
771 // or when the panning goes outwards or some other position which would tear the paper in real situation
772 // we change the start position into the current panning position and update the shader parameters
773 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
774 || gesturePosition.x > mOriginalCenter.x-1.0f
775 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
776 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
778 mOriginalCenter = gesturePosition;
782 mDistanceUpCorner = mOriginalCenter.Length();
783 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
784 mShadowView.Add( mPanActor );
785 SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
786 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
787 mCurrentCenter = mOriginalCenter;
788 mTurnEffect[mIndex].SetCurrentCenter( mCurrentCenter );
789 mPanDisplacement = 0.f;
792 mIsAnimating[mIndex] = true;
794 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
796 mShadowView.RemoveConstraints();
798 self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
799 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView.GetBlurStrengthPropertyIndex(),
800 Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())),
801 Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())),
802 Source( self, mPropertyPanDisplacement[mIndex] ),
803 ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
804 mShadowView.ApplyConstraint( shadowBlurStrengthConstraint );
809 Vector2 currentCenter = gesturePosition;
811 // Test whether the new current center would tear the paper from the top pine in real situation
812 // we do not forbid this totally, which would restrict the panning gesture too much
813 // instead, set it to the nearest allowable position
814 float distanceUpCorner = currentCenter.Length();
815 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
816 if( distanceUpCorner > mDistanceUpCorner )
818 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
820 // would tear the paper from the bottom spine in real situation
821 if( distanceBottomCorner > mDistanceBottomCorner )
823 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
825 // If direction has a very high y component, reduce it.
826 Vector2 curveDirection = currentCenter - mOriginalCenter;
827 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
829 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
831 // If the vertical distance is high, reduce it
832 float yShift = currentCenter.y - mOriginalCenter.y;
833 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
835 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
838 // use contraints to control the page shape and rotation when the pan position is near the spine
839 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
841 // set the property values used by the constraints
842 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
843 Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
844 Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
846 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
847 // also set up the RotationConstraint to the page actor
851 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
852 if( currentCenter.y >= mOriginalCenter.y )
854 corner = Vector2( 1.1f*mPageSize.width, 0.f );
858 corner = mPageSize*1.1f;
861 Vector2 offset( currentCenter-mOriginalCenter );
862 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
863 /( offset.x*offset.x + offset.y*offset.y );
866 Source source(self, mPropertyPanDisplacement[mIndex]);
868 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
869 Constraint originalCenterConstraint = Constraint::New<Vector2>( shaderOriginalCenterPropertyIndex ,
871 OriginalCenterConstraint( mOriginalCenter, offset ));
872 mTurnEffect[mIndex].ApplyConstraint( originalCenterConstraint );
874 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
875 Constraint currentCenterConstraint = Constraint::New<Vector2>( shaderCurrentCenterPropertyIndex,
876 Source(self, mPropertyCurrentCenter[mIndex]),
877 Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex),
878 CurrentCenterConstraint(mPageSize.width));
879 mTurnEffect[mIndex].ApplyConstraint( currentCenterConstraint );
881 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
883 float distance = offset.Length();
884 Constraint rotationConstraint = Constraint::New<Quaternion>( Actor::ROTATION,
885 Source( self, mPropertyPanDisplacement[mIndex] ),
886 RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
887 mPanActor.ApplyConstraint( rotationConstraint );
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].SetOriginalCenter( mOriginalCenter );
900 mPanDisplacement = 0.f;
903 mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
904 mCurrentCenter = currentCenter;
905 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
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,AlphaFunctions::EaseOutSine33);
953 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
954 Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunctions::EaseOutSine33);
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], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
964 mOriginalCenter, AlphaFunctions::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].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1028 if( mCurrentPageIndex >= i + 1 )
1030 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -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