2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.h>
22 #include <dali/public-api/animation/animation.h>
23 #include <dali/public-api/animation/constraint.h>
24 #include <dali/public-api/events/hit-test-algorithm.h>
25 #include <dali/public-api/object/type-registry.h>
26 #include <dali/public-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 )
458 // disable the SetSize of the control from the application
459 Self().SetSize( mControlSize );
462 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
464 mSpineShadowParameter = spineShadowParameter;
466 // set spine shadow parameter to all the shader effects
467 mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
468 mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
469 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
471 mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
475 Vector2 PageTurnView::GetSpineShadowParameter()
477 return mSpineShadowParameter;
480 void PageTurnView::GoToPage( unsigned int pageId )
482 int pageIdx = static_cast<int>(pageId);
483 // record the new current page index
484 mCurrentPageIndex = pageIdx;
486 // clear the old pages
487 for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
491 mPageActors[i].Unparent();
492 mPageActors[i].Reset();
496 // add the current page and the pages right before and after it
497 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
501 // set ordered depth to the stacked pages
505 unsigned int PageTurnView::GetCurrentPage()
507 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
508 return static_cast< unsigned int >( mCurrentPageIndex );
511 Actor PageTurnView::EnterEditMode()
513 if( mNeedOffscreenRendering )
515 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
519 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
520 mOffscreenTask[index].SetInputEnabled( true );
521 mPageSourceActor[index].SetSensitive( true );
522 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
524 mRootOnScreen.SetSensitive(false);
526 return mPageSourceActor[index].GetChildAt( 0 );
534 void PageTurnView::LeaveEditMode()
536 if( mNeedOffscreenRendering )
538 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
542 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
543 mOffscreenTask[index].SetInputEnabled( false );
544 mPageSourceActor[index].SetSensitive( false );
545 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
547 mRootOnScreen.SetSensitive(true);
551 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
553 if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
555 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
557 Dali::HitTestAlgorithm::Results results;
558 if( !mOffscreenTask[index].GetInputEnabled() )
560 mOffscreenTask[index].SetInputEnabled( true );
561 mPageSourceActor[index].SetSensitive( true );
562 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
563 mOffscreenTask[index].SetInputEnabled( false );
564 mPageSourceActor[index].SetSensitive( false );
568 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
570 actorCoordinates = results.actorCoordinates;
571 return results.actor;
579 void PageTurnView::AddPage( int pageIndex )
581 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
583 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
585 if( mNeedOffscreenRendering )
587 Actor source = mPageFactory.NewPage( pageIndex );
588 if( mPageSourceActor[index].GetChildCount() > 0 )
590 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
592 mPageSourceActor[index].Add( source );
593 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
594 newPage = NewPageFromRenderBuffer( pageIndex );
598 newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
599 DALI_ASSERT_ALWAYS( newPage );
601 newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
602 newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
603 newPage.SetSize( mPageSize );
604 mRootOnScreen.Add( newPage );
605 mPageActors[index] = newPage;
607 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
608 mIsTurnBack[ newPage ] = isLeftSide;
611 // new page is added to the left side, so need to rotate it 180 degrees
612 newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
616 SetShaderEffect( newPage, mSpineEffectFront);
619 // For Portrait, nothing to do
620 // 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
621 OnAddPage( newPage, isLeftSide );
625 void PageTurnView::RemovePage( int pageIndex )
627 if( pageIndex > -1 && pageIndex < mTotalPageCount)
629 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
630 mPageActors[index].Unparent();
631 mIsTurnBack.erase( mPageActors[index] );
632 mPageActors[index].Reset();
633 if( mNeedOffscreenRendering )
635 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
640 void PageTurnView::RenderPage( int pageIndex )
642 if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
643 && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
645 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
646 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
650 void PageTurnView::RefreshAll()
652 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
653 if( mTotalPageCount > 0 )
655 if(mCurrentPageIndex < mTotalPageCount)
657 GoToPage( mCurrentPageIndex );
661 GoToPage( mCurrentPageIndex-- );
666 void PageTurnView::RefreshCurrentPage()
668 RenderPage( mCurrentPageIndex );
671 void PageTurnView::OnPan( const PanGesture& gesture )
675 // when interrupted by the call of DisplayCurrentPageSourceActor(),
676 // make sure the panFinished is always called before stopping to responding the gesture
677 // so the status of the control is updated correctly
681 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
686 // the pan gesture is attached to control itself instead of each page
687 switch( gesture.state )
689 case Gesture::Started:
692 // to find out whether the undergoing turning page number already reaches the maximum allowed
693 // and get one idle index when it is animatable
694 bool animatable = false;
695 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
697 if( !mIsAnimating[mIndex] )
702 if( mIsSliding[mIndex] )
708 mIndex = mIndex % MAXIMUM_TURNING_NUM;
711 if( mPageUpdated && animatable )
713 SetPanActor( gesture.position ); // determine which page actor is panned
714 if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
718 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
726 case Gesture::Continuing:
728 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
731 case Gesture::Finished:
732 case Gesture::Cancelled:
735 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
739 case Gesture::Possible:
747 void PageTurnView::PanStarted( const Vector2& gesturePosition )
749 mPressDownPosition = gesturePosition;
756 mOriginalCenter = gesturePosition;
757 mTurnEffect[mIndex].SetIsTurningBack( mIsTurnBack[ mPanActor] );
759 mPageUpdated = false;
761 // Guard against destruction during signal emission
762 Toolkit::PageTurnView handle( GetOwner() );
763 mPagePanStartedSignal.Emit( handle );
766 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
773 // Guard against destruction during signal emission
774 Toolkit::PageTurnView handle( GetOwner() );
778 // when the touch down position is near the spine
779 // or when the panning goes outwards or some other position which would tear the paper in real situation
780 // we change the start position into the current panning position and update the shader parameters
781 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
782 || gesturePosition.x > mOriginalCenter.x-1.0f
783 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
784 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
786 mOriginalCenter = gesturePosition;
790 mDistanceUpCorner = mOriginalCenter.Length();
791 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
792 mShadowView.Add( mPanActor );
793 SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
794 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
795 mCurrentCenter = mOriginalCenter;
796 mTurnEffect[mIndex].SetCurrentCenter( mCurrentCenter );
797 mPanDisplacement = 0.f;
800 mIsAnimating[mIndex] = true;
802 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
804 mShadowView.RemoveConstraints();
806 self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
808 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
809 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())) );
810 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())) );
811 shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
812 shadowBlurStrengthConstraint.Apply();
817 Vector2 currentCenter = gesturePosition;
819 // Test whether the new current center would tear the paper from the top pine in real situation
820 // we do not forbid this totally, which would restrict the panning gesture too much
821 // instead, set it to the nearest allowable position
822 float distanceUpCorner = currentCenter.Length();
823 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
824 if( distanceUpCorner > mDistanceUpCorner )
826 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
828 // would tear the paper from the bottom spine in real situation
829 if( distanceBottomCorner > mDistanceBottomCorner )
831 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
833 // If direction has a very high y component, reduce it.
834 Vector2 curveDirection = currentCenter - mOriginalCenter;
835 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
837 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
839 // If the vertical distance is high, reduce it
840 float yShift = currentCenter.y - mOriginalCenter.y;
841 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
843 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
846 // use contraints to control the page shape and rotation when the pan position is near the spine
847 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
849 // set the property values used by the constraints
850 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
851 Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
852 Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
854 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
855 // also set up the RotationConstraint to the page actor
859 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
860 if( currentCenter.y >= mOriginalCenter.y )
862 corner = Vector2( 1.1f*mPageSize.width, 0.f );
866 corner = mPageSize*1.1f;
869 Vector2 offset( currentCenter-mOriginalCenter );
870 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
871 /( offset.x*offset.x + offset.y*offset.y );
875 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
876 Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
877 originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
878 originalCenterConstraint.Apply();
880 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
881 Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
882 currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
883 currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
884 currentCenterConstraint.Apply();
886 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
888 float distance = offset.Length();
889 Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
890 rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
891 rotationConstraint.Apply();
893 mConstraints = false;
898 if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
900 mPanActor.RemoveConstraints();
901 mTurnEffect[mIndex].RemoveConstraints();
902 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
904 mPanDisplacement = 0.f;
907 mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
908 mCurrentCenter = currentCenter;
909 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
914 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
916 // Guard against destruction during signal emission
917 Toolkit::PageTurnView handle( GetOwner() );
921 if(!mIsAnimating[mIndex])
923 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
928 mPagePanFinishedSignal.Emit( handle );
930 ImageActor actor = mPanActor;
933 if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
935 // update the pages here instead of in the TurnedOver callback function
936 // as new page is allowed to respond to the pan gesture before other pages finishing animation
937 if(mIsTurnBack[actor])
940 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
941 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
946 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
947 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
951 // set up an animation to turn the page over
953 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
954 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
955 animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
956 width,AlphaFunctions::EaseOutSine33);
957 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
958 Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunctions::EaseOutSine33);
959 mAnimationActorPair[animation] = actor;
960 mAnimationIndexPair[animation] = mIndex;
962 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
964 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
966 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
967 animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
968 mOriginalCenter, AlphaFunctions::Linear );
969 mAnimationActorPair[animation] = actor;
970 mAnimationIndexPair[animation] = mIndex;
972 mIsSliding[mIndex] = true;
973 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
975 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
980 // In portrait view, an outwards flick should turn the previous page back
981 // In landscape view, nothing to do
982 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
988 void PageTurnView::TurnedOver( Animation& animation )
990 ImageActor actor = mAnimationActorPair[animation];
991 mIsTurnBack[actor] = !mIsTurnBack[actor];
992 actor.RemoveConstraints();
993 mRootOnScreen.Add(actor);
994 int index = mAnimationIndexPair[animation];
995 mIsAnimating[index] = false;
996 mTurnEffect[index].RemoveConstraints();
997 mAnimationIndexPair.erase( animation );
998 mAnimationActorPair.erase( animation );
1000 SetSpineEffect( actor, mIsTurnBack[actor] );
1002 // Guard against destruction during signal emission
1003 Toolkit::PageTurnView handle( GetOwner() );
1004 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1007 void PageTurnView::SliddenBack( Animation& animation )
1009 ImageActor actor = mAnimationActorPair[animation];
1010 mRootOnScreen.Add(actor);
1011 int index = mAnimationIndexPair[animation];
1012 mIsSliding[index] = false;
1013 mIsAnimating[index] = false;
1014 mAnimationIndexPair.erase( animation );
1015 mAnimationActorPair.erase( animation );
1017 SetSpineEffect( actor, mIsTurnBack[actor] );
1019 // Guard against destruction during signal emission
1020 Toolkit::PageTurnView handle( GetOwner() );
1021 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1024 void PageTurnView::OrganizePageDepth()
1026 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1028 if(mCurrentPageIndex+i < mTotalPageCount)
1030 mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1032 if( mCurrentPageIndex >= i + 1 )
1034 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1039 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
1041 SetShaderEffectRecursively( actor, shaderEffect );
1044 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1046 return mPageTurnStartedSignal;
1049 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1051 return mPageTurnFinishedSignal;
1054 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1056 return mPagePanStartedSignal;
1059 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1061 return mPagePanFinishedSignal;
1064 } // namespace Internal
1066 } // namespace Toolkit