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/render-tasks/render-task-list.h>
30 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
34 namespace //unnamed namespace
37 TypeRegistration mType( typeid(Toolkit::PageTurnView), typeid(Toolkit::Control), NULL );
39 // default grid density for page turn effect, 10 pixels by 10 pixels
40 const float DEFAULT_GRID_DENSITY(10.0f);
42 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
43 const float MINIMUM_START_POSITION_RATIO(0.6f);
45 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
46 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
48 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
49 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
51 // duration of animation, shorter for faster speed
52 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
53 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
55 // the major&minor radius (in pixels) to form an ellipse shape
56 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
57 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
59 // constants for shadow casting
60 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
61 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
63 // constraints ////////////////////////////////////////////////////////////////
65 * Original Center Constraint
67 * This constraint adjusts the original center property of the page turn shader effect
68 * based on the X-direction displacement of the pan gesture
70 struct OriginalCenterConstraint
72 OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
73 : mOldCenter( originalCenter )
75 mNewCenter = originalCenter + offset;
76 mDistance = offset.Length() * 0.5f;
77 mDirection = offset / mDistance;
80 Vector2 operator()(const Vector2& current, const PropertyInput& panDisplacement)
82 float displacement = panDisplacement.GetFloat();
84 if( displacement < mDistance )
86 return mOldCenter + mDirection * displacement;
90 return mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
101 * Rotation Constraint
103 * This constraint adjusts the rotation property of the page actor
104 * based on the X-direction displacement of the pan gesture
106 struct RotationConstraint
108 RotationConstraint( float distance, float pageWidth, bool isTurnBack )
109 : mDistance( distance*0.5f )
111 mStep = 1.f / pageWidth;
112 mSign = isTurnBack ? -1.0f : 1.0f;
113 mConst = isTurnBack ? -1.0f : 0.f;
114 mRotation = isTurnBack ? Quaternion( -Math::PI, Vector3::YAXIS ) : Quaternion( 0.f, Vector3::YAXIS );
117 Quaternion operator()( const Quaternion& current, const PropertyInput& panDisplacement )
119 float displacement = panDisplacement.GetFloat();
121 if( displacement < mDistance)
127 float coef = std::max(-1.0f, mStep*(mDistance-displacement));
128 angle = Math::PI*( mConst + mSign*coef );
129 return Quaternion( angle, Vector3::YAXIS );
137 Quaternion mRotation;
141 * Current Center Constraint
143 * This constraint adjusts the current center property of the page turn shader effect
144 * based on the pan position and the original center position
146 struct CurrentCenterConstraint
148 CurrentCenterConstraint( float pageWidth)
149 : mPageWidth( pageWidth )
151 mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
154 Vector2 operator()( const Vector2& current, const PropertyInput& center, const PropertyInput& originalCenter )
156 Vector2 centerPosition = center.GetVector2();
157 if( centerPosition.x > 0.f )
159 return Vector2( mThres+centerPosition.x*0.5f , centerPosition.y);
163 Vector2 centerOrigin = originalCenter.GetVector2();
164 Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
165 float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
166 // 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
169 coef = (coef+0.225f)/10.0f;
171 return centerOrigin - direction * coef;
179 struct ShadowBlurStrengthConstraint
181 ShadowBlurStrengthConstraint( float thres )
185 float operator()( const float current, const PropertyInput& currentCenter, const PropertyInput& originalCenter, const PropertyInput& panDisplacement)
187 float displacement = panDisplacement.GetFloat();
189 if( EqualsZero(displacement))
191 Vector2 cur = currentCenter.GetVector2();
192 Vector2 ori = originalCenter.GetVector2();
193 blurStrength = 5.f*(ori-cur).Length() / mThres;
197 blurStrength = 1.f - (displacement-2.f*mThres)/mThres;
200 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( 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::GridDensity, 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 );
807 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView.GetBlurStrengthPropertyIndex(),
808 Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())),
809 Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())),
810 Source( self, mPropertyPanDisplacement[mIndex] ),
811 ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
812 mShadowView.ApplyConstraint( shadowBlurStrengthConstraint );
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 );
874 Source source(self, mPropertyPanDisplacement[mIndex]);
876 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
877 Constraint originalCenterConstraint = Constraint::New<Vector2>( shaderOriginalCenterPropertyIndex ,
879 OriginalCenterConstraint( mOriginalCenter, offset ));
880 mTurnEffect[mIndex].ApplyConstraint( originalCenterConstraint );
882 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
883 Constraint currentCenterConstraint = Constraint::New<Vector2>( shaderCurrentCenterPropertyIndex,
884 Source(self, mPropertyCurrentCenter[mIndex]),
885 Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex),
886 CurrentCenterConstraint(mPageSize.width));
887 mTurnEffect[mIndex].ApplyConstraint( currentCenterConstraint );
889 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
891 float distance = offset.Length();
892 Constraint rotationConstraint = Constraint::New<Quaternion>( Actor::Property::Rotation,
893 Source( self, mPropertyPanDisplacement[mIndex] ),
894 RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
895 mPanActor.ApplyConstraint( rotationConstraint );
897 mConstraints = false;
902 if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
904 mPanActor.RemoveConstraints();
905 mTurnEffect[mIndex].RemoveConstraints();
906 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
908 mPanDisplacement = 0.f;
911 mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
912 mCurrentCenter = currentCenter;
913 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
918 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
920 // Guard against destruction during signal emission
921 Toolkit::PageTurnView handle( GetOwner() );
925 if(!mIsAnimating[mIndex])
927 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
932 mPagePanFinishedSignal.Emit( handle );
934 ImageActor actor = mPanActor;
937 if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
939 // update the pages here instead of in the TurnedOver callback function
940 // as new page is allowed to respond to the pan gesture before other pages finishing animation
941 if(mIsTurnBack[actor])
944 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
945 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
950 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
951 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
955 // set up an animation to turn the page over
957 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
958 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
959 animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
960 width,AlphaFunctions::EaseOutSine33);
961 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
962 Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunctions::EaseOutSine33);
963 mAnimationActorPair[animation] = actor;
964 mAnimationIndexPair[animation] = mIndex;
966 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
968 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
970 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
971 animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
972 mOriginalCenter, AlphaFunctions::Linear );
973 mAnimationActorPair[animation] = actor;
974 mAnimationIndexPair[animation] = mIndex;
976 mIsSliding[mIndex] = true;
977 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
979 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
984 // In portrait view, an outwards flick should turn the previous page back
985 // In landscape view, nothing to do
986 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
992 void PageTurnView::TurnedOver( Animation& animation )
994 ImageActor actor = mAnimationActorPair[animation];
995 mIsTurnBack[actor] = !mIsTurnBack[actor];
996 actor.RemoveConstraints();
997 mRootOnScreen.Add(actor);
998 int index = mAnimationIndexPair[animation];
999 mIsAnimating[index] = false;
1000 mTurnEffect[index].RemoveConstraints();
1001 mAnimationIndexPair.erase( animation );
1002 mAnimationActorPair.erase( animation );
1004 SetSpineEffect( actor, mIsTurnBack[actor] );
1006 // Guard against destruction during signal emission
1007 Toolkit::PageTurnView handle( GetOwner() );
1008 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1011 void PageTurnView::SliddenBack( Animation& animation )
1013 ImageActor actor = mAnimationActorPair[animation];
1014 mRootOnScreen.Add(actor);
1015 int index = mAnimationIndexPair[animation];
1016 mIsSliding[index] = false;
1017 mIsAnimating[index] = false;
1018 mAnimationIndexPair.erase( animation );
1019 mAnimationActorPair.erase( animation );
1021 SetSpineEffect( actor, mIsTurnBack[actor] );
1023 // Guard against destruction during signal emission
1024 Toolkit::PageTurnView handle( GetOwner() );
1025 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1028 void PageTurnView::OrganizePageDepth()
1030 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1032 if(mCurrentPageIndex+i < mTotalPageCount)
1034 mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1036 if( mCurrentPageIndex >= i + 1 )
1038 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1043 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
1045 SetShaderEffectRecursively( actor, shaderEffect );
1048 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1050 return mPageTurnStartedSignal;
1053 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1055 return mPageTurnFinishedSignal;
1058 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1060 return mPagePanStartedSignal;
1063 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1065 return mPagePanFinishedSignal;
1068 } // namespace Internal
1070 } // namespace Toolkit