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/active-constraint.h>
24 #include <dali/public-api/animation/constraint.h>
25 #include <dali/public-api/events/hit-test-algorithm.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/public-api/object/type-registry-helper.h>
28 #include <dali/public-api/render-tasks/render-task-list.h>
31 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
35 namespace //Unnamed namespace
39 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::PageTurnView, Toolkit::Control, NULL )
40 DALI_TYPE_REGISTRATION_END()
42 // default grid density for page turn effect, 10 pixels by 10 pixels
43 const float DEFAULT_GRID_DENSITY(10.0f);
45 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
46 const float MINIMUM_START_POSITION_RATIO(0.6f);
48 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
49 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
51 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
52 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
54 // duration of animation, shorter for faster speed
55 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
56 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
58 // the major&minor radius (in pixels) to form an ellipse shape
59 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
60 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
62 // constants for shadow casting
63 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
64 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
66 // constraints ////////////////////////////////////////////////////////////////
68 * Original Center Constraint
70 * This constraint adjusts the original center property of the page turn shader effect
71 * based on the X-direction displacement of the pan gesture
73 struct OriginalCenterConstraint
75 OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
76 : mOldCenter( originalCenter )
78 mNewCenter = originalCenter + offset;
79 mDistance = offset.Length() * 0.5f;
80 mDirection = offset / mDistance;
83 Vector2 operator()(const Vector2& current, const PropertyInput& panDisplacement)
85 float displacement = panDisplacement.GetFloat();
87 if( displacement < mDistance )
89 return mOldCenter + mDirection * displacement;
93 return mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
104 * Rotation Constraint
106 * This constraint adjusts the rotation property of the page actor
107 * based on the X-direction displacement of the pan gesture
109 struct RotationConstraint
111 RotationConstraint( float distance, float pageWidth, bool isTurnBack )
112 : mDistance( distance*0.5f )
114 mStep = 1.f / pageWidth;
115 mSign = isTurnBack ? -1.0f : 1.0f;
116 mConst = isTurnBack ? -1.0f : 0.f;
117 mRotation = isTurnBack ? Quaternion( -Math::PI, Vector3::YAXIS ) : Quaternion( 0.f, Vector3::YAXIS );
120 Quaternion operator()( const Quaternion& current, const PropertyInput& panDisplacement )
122 float displacement = panDisplacement.GetFloat();
124 if( displacement < mDistance)
130 float coef = std::max(-1.0f, mStep*(mDistance-displacement));
131 angle = Math::PI*( mConst + mSign*coef );
132 return Quaternion( angle, Vector3::YAXIS );
140 Quaternion mRotation;
144 * Current Center Constraint
146 * This constraint adjusts the current center property of the page turn shader effect
147 * based on the pan position and the original center position
149 struct CurrentCenterConstraint
151 CurrentCenterConstraint( float pageWidth)
152 : mPageWidth( pageWidth )
154 mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
157 Vector2 operator()( const Vector2& current, const PropertyInput& center, const PropertyInput& originalCenter )
159 Vector2 centerPosition = center.GetVector2();
160 if( centerPosition.x > 0.f )
162 return Vector2( mThres+centerPosition.x*0.5f , centerPosition.y);
166 Vector2 centerOrigin = originalCenter.GetVector2();
167 Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
168 float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
169 // 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
172 coef = (coef+0.225f)/10.0f;
174 return centerOrigin - direction * coef;
182 struct ShadowBlurStrengthConstraint
184 ShadowBlurStrengthConstraint( float thres )
188 float operator()( const float current, const PropertyInput& currentCenter, const PropertyInput& originalCenter, const PropertyInput& panDisplacement)
190 float displacement = panDisplacement.GetFloat();
192 if( EqualsZero(displacement))
194 Vector2 cur = currentCenter.GetVector2();
195 Vector2 ori = originalCenter.GetVector2();
196 blurStrength = 5.f*(ori-cur).Length() / mThres;
200 blurStrength = 1.f - (displacement-2.f*mThres)/mThres;
203 blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
210 bool IsActorHittableFunction( Actor actor, Dali::HitTestAlgorithm::TraverseType type )
212 bool hittable = false;
216 case Dali::HitTestAlgorithm::CHECK_ACTOR:
218 // Check whether the actor is visible and not fully transparent.
219 Property::Index propertyActorHittable = actor.GetPropertyIndex(Toolkit::PageFactory::ACTOR_HITTABLE);
220 if( actor.IsSensitive()
222 && actor.GetCurrentWorldColor().a > 0.01f// not FULLY_TRANSPARENT
223 && ( propertyActorHittable != Property::INVALID_INDEX &&
224 actor.GetProperty<bool>( propertyActorHittable ) ) )
230 case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
232 if( actor.IsSensitive() && actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible.
247 } //unnamed namespace
258 // these several constants are also used in the derived classes
259 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
260 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
261 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
262 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
264 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
265 : Control( REQUIRES_TOUCH_EVENTS ),
266 mPageFactory( pageFactory ),
267 mPageSize( pageSize ),
268 mTotalPageCount( 0 ),
269 mIsEditMode( false ),
270 mNeedOffscreenRendering( false ),
272 mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
273 mCurrentPageIndex( 0 ),
276 mPageUpdated( true ),
277 mDistanceUpCorner( 0.f ),
278 mDistanceBottomCorner( 0.f ),
279 mPanDisplacement( 0.f ),
280 mConstraints( false ),
281 mPageTurnStartedSignal(),
282 mPageTurnFinishedSignal(),
283 mPagePanStartedSignal(),
284 mPagePanFinishedSignal()
286 mPageActors.resize( NUMBER_OF_CACHED_PAGES );
287 mIsAnimating.resize( MAXIMUM_TURNING_NUM );
288 mIsSliding.resize( MAXIMUM_TURNING_NUM );
289 mTurnEffect.resize( MAXIMUM_TURNING_NUM );
290 mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
291 mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
294 PageTurnView::~PageTurnView()
298 void PageTurnView::OnInitialize()
300 // create the two book spine effect for static images, left and right side pages respectively
301 mSpineEffectFront = PageTurnBookSpineEffect::New();
302 mSpineEffectFront.SetIsBackImageVisible( false );
303 mSpineEffectFront.SetPageWidth( mPageSize.width );
304 mSpineEffectFront.SetShadowWidth( 0.f );
305 mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
307 mSpineEffectBack = PageTurnBookSpineEffect::New();
308 mSpineEffectBack.SetIsBackImageVisible( true );
309 mSpineEffectBack.SetPageWidth( mPageSize.width );
310 mSpineEffectBack.SetShadowWidth( 0.f );
311 mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
313 // create the page turn effect objects
314 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
316 mTurnEffect[i] = Toolkit::PageTurnEffect::New( false );
317 mTurnEffect[i].SetProperty( ShaderEffect::Property::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
318 mTurnEffect[i].SetPageSize( mPageSize );
319 mTurnEffect[i].SetShadowWidth(0.f);
320 mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
321 mIsAnimating[i] = false;
322 mIsSliding[i] = false;
323 mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
324 mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
327 mTurningPageLayer = Layer::New();
328 mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
329 // Set control size and the parent origin of turningPageLayer
330 OnPageTurnViewInitialize();
332 mRootOnScreen = Actor::New();
333 mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION );
334 mRootOnScreen.SetSize( mControlSize );
335 Self().Add( mRootOnScreen );
336 mRootOnScreen.Add(mTurningPageLayer);
338 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
339 mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded();
340 if( mNeedOffscreenRendering )
345 // add pages to the scene, and set depth for the stacked pages
346 for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
351 mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
355 // enable the pan gesture which is attached to the control
356 EnableGestureDetection(Gesture::Type(Gesture::Pan));
358 mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage);
361 void PageTurnView::SetupRenderTasks()
363 mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES );
364 mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES );
365 mRenderedPage.resize( NUMBER_OF_CACHED_PAGES );
367 mCameraActor = CameraActor::New(mControlSize);
368 mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
369 mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
370 mCameraActor.SetInheritScale( false );
371 Self().Add(mCameraActor);
373 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
374 for(int i=0; i<NUMBER_OF_CACHED_PAGES; i++)
376 mPageSourceActor[i] = Actor::New();
377 mPageSourceActor[i].SetParentOrigin(ParentOrigin::CENTER);
378 mPageSourceActor[i].SetColorMode( USE_OWN_COLOR );
379 mPageSourceActor[i].SetPositionInheritanceMode( DONT_INHERIT_POSITION );
380 mPageSourceActor[i].SetInheritScale( false );
381 Self().Add( mPageSourceActor[i] );
382 mPageSourceActor[i].SetSensitive( false );
384 mRenderedPage[i] = FrameBufferImage::New( mControlSize.width, mControlSize.height, Pixel::RGB8888, Image::UNUSED );
385 mOffscreenTask[i] = taskList.CreateTask();
386 mOffscreenTask[i].SetRefreshRate( RenderTask::REFRESH_ONCE );
387 mOffscreenTask[i].SetCameraActor(mCameraActor);
388 mOffscreenTask[i].SetSourceActor( mPageSourceActor[i] );
389 mOffscreenTask[i].SetExclusive(true);
390 mOffscreenTask[i].SetInputEnabled( false );
391 mOffscreenTask[i].SetClearEnabled( true );
392 mOffscreenTask[i].SetClearColor( Vector4(0.f,0.f,0.f,0.f) );
393 mOffscreenTask[i].SetTargetFrameBuffer( mRenderedPage[i] );
394 mOffscreenTask[i].SetScreenToFrameBufferMappingActor( Self() );
398 void PageTurnView::SetupShadowView()
400 mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
401 Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
402 mShadowView.SetParentOrigin( origin );
403 mShadowView.SetAnchorPoint( origin );
404 mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
405 mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
407 mShadowLayer = Layer::New();
408 mShadowLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
409 mRootOnScreen.Add(mShadowLayer);
410 mShadowLayer.Raise();
412 mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
413 mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
414 mShadowPlane.SetSize( mControlSize );
415 mShadowLayer.Add( mShadowPlane );
416 mShadowView.SetShadowPlane( mShadowPlane );
418 mPointLight = Actor::New();
419 mPointLight.SetAnchorPoint( origin );
420 mPointLight.SetParentOrigin( origin );
421 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
422 mRootOnScreen.Add( mPointLight );
423 mShadowView.SetPointLight( mPointLight );
425 mTurningPageLayer.Add( mShadowView );
426 mShadowView.Activate();
429 void PageTurnView::OnControlStageConnection()
432 mTurningPageLayer.RaiseToTop();
435 void PageTurnView::OnControlStageDisconnection()
439 Self().Remove(mPointLight);
440 Self().Remove(mShadowLayer);
441 mTurningPageLayer.Remove( mShadowView );
444 // make sure the status of the control is updated correctly when the pan gesture is interrupted
449 mRootOnScreen.Add(mPanActor);
450 mIsAnimating[mIndex] = false;
451 mPanActor.RemoveConstraints();
452 mTurnEffect[mIndex].RemoveConstraints();
455 SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
459 void PageTurnView::OnControlSizeSet( const Vector3& size )
461 // disable the SetSize of the control from the application
462 Self().SetSize( mControlSize );
465 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
467 mSpineShadowParameter = spineShadowParameter;
469 // set spine shadow parameter to all the shader effects
470 mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
471 mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
472 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
474 mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
478 Vector2 PageTurnView::GetSpineShadowParameter()
480 return mSpineShadowParameter;
483 void PageTurnView::GoToPage( unsigned int pageId )
485 int pageIdx = static_cast<int>(pageId);
486 // record the new current page index
487 mCurrentPageIndex = pageIdx;
489 // clear the old pages
490 for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
494 mPageActors[i].Unparent();
495 mPageActors[i].Reset();
499 // add the current page and the pages right before and after it
500 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
504 // set ordered depth to the stacked pages
508 unsigned int PageTurnView::GetCurrentPage()
510 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
511 return static_cast< unsigned int >( mCurrentPageIndex );
514 Actor PageTurnView::EnterEditMode()
516 if( mNeedOffscreenRendering )
518 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
522 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
523 mOffscreenTask[index].SetInputEnabled( true );
524 mPageSourceActor[index].SetSensitive( true );
525 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
527 mRootOnScreen.SetSensitive(false);
529 return mPageSourceActor[index].GetChildAt( 0 );
537 void PageTurnView::LeaveEditMode()
539 if( mNeedOffscreenRendering )
541 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
545 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
546 mOffscreenTask[index].SetInputEnabled( false );
547 mPageSourceActor[index].SetSensitive( false );
548 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
550 mRootOnScreen.SetSensitive(true);
554 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
556 if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
558 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
560 Dali::HitTestAlgorithm::Results results;
561 if( !mOffscreenTask[index].GetInputEnabled() )
563 mOffscreenTask[index].SetInputEnabled( true );
564 mPageSourceActor[index].SetSensitive( true );
565 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
566 mOffscreenTask[index].SetInputEnabled( false );
567 mPageSourceActor[index].SetSensitive( false );
571 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
573 actorCoordinates = results.actorCoordinates;
574 return results.actor;
582 void PageTurnView::AddPage( int pageIndex )
584 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
586 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
588 if( mNeedOffscreenRendering )
590 Actor source = mPageFactory.NewPage( pageIndex );
591 if( mPageSourceActor[index].GetChildCount() > 0 )
593 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
595 mPageSourceActor[index].Add( source );
596 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
597 newPage = NewPageFromRenderBuffer( pageIndex );
601 newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
602 DALI_ASSERT_ALWAYS( newPage );
604 newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
605 newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
606 newPage.SetSize( mPageSize );
607 mRootOnScreen.Add( newPage );
608 mPageActors[index] = newPage;
610 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
611 mIsTurnBack[ newPage ] = isLeftSide;
614 // new page is added to the left side, so need to rotate it 180 degrees
615 newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
619 SetShaderEffect( newPage, mSpineEffectFront);
622 // For Portrait, nothing to do
623 // 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
624 OnAddPage( newPage, isLeftSide );
628 void PageTurnView::RemovePage( int pageIndex )
630 if( pageIndex > -1 && pageIndex < mTotalPageCount)
632 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
633 mPageActors[index].Unparent();
634 mIsTurnBack.erase( mPageActors[index] );
635 mPageActors[index].Reset();
636 if( mNeedOffscreenRendering )
638 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
643 void PageTurnView::RenderPage( int pageIndex )
645 if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
646 && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
648 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
649 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
653 void PageTurnView::RefreshAll()
655 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
656 if( mTotalPageCount > 0 )
658 if(mCurrentPageIndex < mTotalPageCount)
660 GoToPage( mCurrentPageIndex );
664 GoToPage( mCurrentPageIndex-- );
669 void PageTurnView::RefreshCurrentPage()
671 RenderPage( mCurrentPageIndex );
674 void PageTurnView::OnPan( const PanGesture& gesture )
678 // when interrupted by the call of DisplayCurrentPageSourceActor(),
679 // make sure the panFinished is always called before stopping to responding the gesture
680 // so the status of the control is updated correctly
684 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
689 // the pan gesture is attached to control itself instead of each page
690 switch( gesture.state )
692 case Gesture::Started:
695 // to find out whether the undergoing turning page number already reaches the maximum allowed
696 // and get one idle index when it is animatable
697 bool animatable = false;
698 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
700 if( !mIsAnimating[mIndex] )
705 if( mIsSliding[mIndex] )
711 mIndex = mIndex % MAXIMUM_TURNING_NUM;
714 if( mPageUpdated && animatable )
716 SetPanActor( gesture.position ); // determine which page actor is panned
717 if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
721 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
729 case Gesture::Continuing:
731 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
734 case Gesture::Finished:
735 case Gesture::Cancelled:
738 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
742 case Gesture::Possible:
750 void PageTurnView::PanStarted( const Vector2& gesturePosition )
752 mPressDownPosition = gesturePosition;
759 mOriginalCenter = gesturePosition;
760 mTurnEffect[mIndex].SetIsTurningBack( mIsTurnBack[ mPanActor] );
762 mPageUpdated = false;
764 // Guard against destruction during signal emission
765 Toolkit::PageTurnView handle( GetOwner() );
766 mPagePanStartedSignal.Emit( handle );
769 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
776 // Guard against destruction during signal emission
777 Toolkit::PageTurnView handle( GetOwner() );
781 // when the touch down position is near the spine
782 // or when the panning goes outwards or some other position which would tear the paper in real situation
783 // we change the start position into the current panning position and update the shader parameters
784 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
785 || gesturePosition.x > mOriginalCenter.x-1.0f
786 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
787 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
789 mOriginalCenter = gesturePosition;
793 mDistanceUpCorner = mOriginalCenter.Length();
794 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
795 mShadowView.Add( mPanActor );
796 SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
797 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
798 mCurrentCenter = mOriginalCenter;
799 mTurnEffect[mIndex].SetCurrentCenter( mCurrentCenter );
800 mPanDisplacement = 0.f;
803 mIsAnimating[mIndex] = true;
805 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
807 mShadowView.RemoveConstraints();
809 self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
810 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView.GetBlurStrengthPropertyIndex(),
811 Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())),
812 Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())),
813 Source( self, mPropertyPanDisplacement[mIndex] ),
814 ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
815 mShadowView.ApplyConstraint( shadowBlurStrengthConstraint );
820 Vector2 currentCenter = gesturePosition;
822 // Test whether the new current center would tear the paper from the top pine in real situation
823 // we do not forbid this totally, which would restrict the panning gesture too much
824 // instead, set it to the nearest allowable position
825 float distanceUpCorner = currentCenter.Length();
826 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
827 if( distanceUpCorner > mDistanceUpCorner )
829 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
831 // would tear the paper from the bottom spine in real situation
832 if( distanceBottomCorner > mDistanceBottomCorner )
834 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
836 // If direction has a very high y component, reduce it.
837 Vector2 curveDirection = currentCenter - mOriginalCenter;
838 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
840 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
842 // If the vertical distance is high, reduce it
843 float yShift = currentCenter.y - mOriginalCenter.y;
844 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
846 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
849 // use contraints to control the page shape and rotation when the pan position is near the spine
850 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
852 // set the property values used by the constraints
853 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
854 Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
855 Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
857 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
858 // also set up the RotationConstraint to the page actor
862 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
863 if( currentCenter.y >= mOriginalCenter.y )
865 corner = Vector2( 1.1f*mPageSize.width, 0.f );
869 corner = mPageSize*1.1f;
872 Vector2 offset( currentCenter-mOriginalCenter );
873 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
874 /( offset.x*offset.x + offset.y*offset.y );
877 Source source(self, mPropertyPanDisplacement[mIndex]);
879 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
880 Constraint originalCenterConstraint = Constraint::New<Vector2>( shaderOriginalCenterPropertyIndex ,
882 OriginalCenterConstraint( mOriginalCenter, offset ));
883 mTurnEffect[mIndex].ApplyConstraint( originalCenterConstraint );
885 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
886 Constraint currentCenterConstraint = Constraint::New<Vector2>( shaderCurrentCenterPropertyIndex,
887 Source(self, mPropertyCurrentCenter[mIndex]),
888 Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex),
889 CurrentCenterConstraint(mPageSize.width));
890 mTurnEffect[mIndex].ApplyConstraint( currentCenterConstraint );
892 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
894 float distance = offset.Length();
895 Constraint rotationConstraint = Constraint::New<Quaternion>( Actor::Property::ORIENTATION,
896 Source( self, mPropertyPanDisplacement[mIndex] ),
897 RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
898 mPanActor.ApplyConstraint( rotationConstraint );
900 mConstraints = false;
905 if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
907 mPanActor.RemoveConstraints();
908 mTurnEffect[mIndex].RemoveConstraints();
909 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
911 mPanDisplacement = 0.f;
914 mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
915 mCurrentCenter = currentCenter;
916 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
921 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
923 // Guard against destruction during signal emission
924 Toolkit::PageTurnView handle( GetOwner() );
928 if(!mIsAnimating[mIndex])
930 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
935 mPagePanFinishedSignal.Emit( handle );
937 ImageActor actor = mPanActor;
940 if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
942 // update the pages here instead of in the TurnedOver callback function
943 // as new page is allowed to respond to the pan gesture before other pages finishing animation
944 if(mIsTurnBack[actor])
947 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
948 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
953 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
954 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
958 // set up an animation to turn the page over
960 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
961 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
962 animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
963 width,AlphaFunctions::EaseOutSine33);
964 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
965 Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunctions::EaseOutSine33);
966 mAnimationActorPair[animation] = actor;
967 mAnimationIndexPair[animation] = mIndex;
969 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
971 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
973 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
974 animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
975 mOriginalCenter, AlphaFunctions::Linear );
976 mAnimationActorPair[animation] = actor;
977 mAnimationIndexPair[animation] = mIndex;
979 mIsSliding[mIndex] = true;
980 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
982 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
987 // In portrait view, an outwards flick should turn the previous page back
988 // In landscape view, nothing to do
989 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
995 void PageTurnView::TurnedOver( Animation& animation )
997 ImageActor actor = mAnimationActorPair[animation];
998 mIsTurnBack[actor] = !mIsTurnBack[actor];
999 actor.RemoveConstraints();
1000 mRootOnScreen.Add(actor);
1001 int index = mAnimationIndexPair[animation];
1002 mIsAnimating[index] = false;
1003 mTurnEffect[index].RemoveConstraints();
1004 mAnimationIndexPair.erase( animation );
1005 mAnimationActorPair.erase( animation );
1007 SetSpineEffect( actor, mIsTurnBack[actor] );
1009 // Guard against destruction during signal emission
1010 Toolkit::PageTurnView handle( GetOwner() );
1011 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1014 void PageTurnView::SliddenBack( Animation& animation )
1016 ImageActor actor = mAnimationActorPair[animation];
1017 mRootOnScreen.Add(actor);
1018 int index = mAnimationIndexPair[animation];
1019 mIsSliding[index] = false;
1020 mIsAnimating[index] = false;
1021 mAnimationIndexPair.erase( animation );
1022 mAnimationActorPair.erase( animation );
1024 SetSpineEffect( actor, mIsTurnBack[actor] );
1026 // Guard against destruction during signal emission
1027 Toolkit::PageTurnView handle( GetOwner() );
1028 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1031 void PageTurnView::OrganizePageDepth()
1033 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1035 if(mCurrentPageIndex+i < mTotalPageCount)
1037 mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1039 if( mCurrentPageIndex >= i + 1 )
1041 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1046 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
1048 SetShaderEffectRecursively( actor, shaderEffect );
1051 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1053 return mPageTurnStartedSignal;
1056 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1058 return mPageTurnFinishedSignal;
1061 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1063 return mPagePanStartedSignal;
1066 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1068 return mPagePanFinishedSignal;
1071 } // namespace Internal
1073 } // namespace Toolkit