2 * Copyright (c) 2015 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/devel-api/events/hit-test-algorithm.h>
25 #include <dali/public-api/object/type-registry.h>
26 #include <dali/devel-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::OnStageConnection()
429 mTurningPageLayer.RaiseToTop();
432 void PageTurnView::OnStageDisconnection()
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::SetSpineShadowParameter( const Vector2& spineShadowParameter )
458 mSpineShadowParameter = spineShadowParameter;
460 // set spine shadow parameter to all the shader effects
461 mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
462 mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
463 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
465 mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
469 Vector2 PageTurnView::GetSpineShadowParameter()
471 return mSpineShadowParameter;
474 void PageTurnView::GoToPage( unsigned int pageId )
476 int pageIdx = static_cast<int>(pageId);
477 // record the new current page index
478 mCurrentPageIndex = pageIdx;
480 // clear the old pages
481 for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
485 mPageActors[i].Unparent();
486 mPageActors[i].Reset();
490 // add the current page and the pages right before and after it
491 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
495 // set ordered depth to the stacked pages
499 unsigned int PageTurnView::GetCurrentPage()
501 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
502 return static_cast< unsigned int >( mCurrentPageIndex );
505 Actor PageTurnView::EnterEditMode()
507 if( mNeedOffscreenRendering )
509 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
513 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
514 mOffscreenTask[index].SetInputEnabled( true );
515 mPageSourceActor[index].SetSensitive( true );
516 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
518 mRootOnScreen.SetSensitive(false);
520 return mPageSourceActor[index].GetChildAt( 0 );
528 void PageTurnView::LeaveEditMode()
530 if( mNeedOffscreenRendering )
532 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
536 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
537 mOffscreenTask[index].SetInputEnabled( false );
538 mPageSourceActor[index].SetSensitive( false );
539 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
541 mRootOnScreen.SetSensitive(true);
545 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
547 if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
549 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
551 Dali::HitTestAlgorithm::Results results;
552 if( !mOffscreenTask[index].GetInputEnabled() )
554 mOffscreenTask[index].SetInputEnabled( true );
555 mPageSourceActor[index].SetSensitive( true );
556 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
557 mOffscreenTask[index].SetInputEnabled( false );
558 mPageSourceActor[index].SetSensitive( false );
562 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
564 actorCoordinates = results.actorCoordinates;
565 return results.actor;
573 void PageTurnView::AddPage( int pageIndex )
575 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
577 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
579 if( mNeedOffscreenRendering )
581 Actor source = mPageFactory.NewPage( pageIndex );
582 if( mPageSourceActor[index].GetChildCount() > 0 )
584 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
586 mPageSourceActor[index].Add( source );
587 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
588 newPage = NewPageFromRenderBuffer( pageIndex );
592 newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
593 DALI_ASSERT_ALWAYS( newPage );
595 newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
596 newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
597 newPage.SetSize( mPageSize );
598 mRootOnScreen.Add( newPage );
599 mPageActors[index] = newPage;
601 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
602 mIsTurnBack[ newPage ] = isLeftSide;
605 // new page is added to the left side, so need to rotate it 180 degrees
606 newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
610 SetShaderEffect( newPage, mSpineEffectFront);
613 // For Portrait, nothing to do
614 // 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
615 OnAddPage( newPage, isLeftSide );
619 void PageTurnView::RemovePage( int pageIndex )
621 if( pageIndex > -1 && pageIndex < mTotalPageCount)
623 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
624 mPageActors[index].Unparent();
625 mIsTurnBack.erase( mPageActors[index] );
626 mPageActors[index].Reset();
627 if( mNeedOffscreenRendering )
629 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
634 void PageTurnView::RenderPage( int pageIndex )
636 if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
637 && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
639 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
640 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
644 void PageTurnView::RefreshAll()
646 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
647 if( mTotalPageCount > 0 )
649 if(mCurrentPageIndex < mTotalPageCount)
651 GoToPage( mCurrentPageIndex );
655 GoToPage( mCurrentPageIndex-- );
660 void PageTurnView::RefreshCurrentPage()
662 RenderPage( mCurrentPageIndex );
665 void PageTurnView::OnPan( const PanGesture& gesture )
669 // when interrupted by the call of DisplayCurrentPageSourceActor(),
670 // make sure the panFinished is always called before stopping to responding the gesture
671 // so the status of the control is updated correctly
675 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
680 // the pan gesture is attached to control itself instead of each page
681 switch( gesture.state )
683 case Gesture::Started:
686 // to find out whether the undergoing turning page number already reaches the maximum allowed
687 // and get one idle index when it is animatable
688 bool animatable = false;
689 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
691 if( !mIsAnimating[mIndex] )
696 if( mIsSliding[mIndex] )
702 mIndex = mIndex % MAXIMUM_TURNING_NUM;
705 if( mPageUpdated && animatable )
707 SetPanActor( gesture.position ); // determine which page actor is panned
708 if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
712 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
720 case Gesture::Continuing:
722 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
725 case Gesture::Finished:
726 case Gesture::Cancelled:
729 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
733 case Gesture::Possible:
741 void PageTurnView::PanStarted( const Vector2& gesturePosition )
743 mPressDownPosition = gesturePosition;
750 mOriginalCenter = gesturePosition;
751 mTurnEffect[mIndex].SetIsTurningBack( mIsTurnBack[ mPanActor] );
753 mPageUpdated = false;
755 // Guard against destruction during signal emission
756 Toolkit::PageTurnView handle( GetOwner() );
757 mPagePanStartedSignal.Emit( handle );
760 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
767 // Guard against destruction during signal emission
768 Toolkit::PageTurnView handle( GetOwner() );
772 // when the touch down position is near the spine
773 // or when the panning goes outwards or some other position which would tear the paper in real situation
774 // we change the start position into the current panning position and update the shader parameters
775 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
776 || gesturePosition.x > mOriginalCenter.x-1.0f
777 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
778 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
780 mOriginalCenter = gesturePosition;
784 mDistanceUpCorner = mOriginalCenter.Length();
785 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
786 mShadowView.Add( mPanActor );
787 SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
788 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
789 mCurrentCenter = mOriginalCenter;
790 mTurnEffect[mIndex].SetCurrentCenter( mCurrentCenter );
791 mPanDisplacement = 0.f;
794 mIsAnimating[mIndex] = true;
796 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
798 mShadowView.RemoveConstraints();
800 self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
802 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
803 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())) );
804 shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())) );
805 shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
806 shadowBlurStrengthConstraint.Apply();
811 Vector2 currentCenter = gesturePosition;
813 // Test whether the new current center would tear the paper from the top pine in real situation
814 // we do not forbid this totally, which would restrict the panning gesture too much
815 // instead, set it to the nearest allowable position
816 float distanceUpCorner = currentCenter.Length();
817 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
818 if( distanceUpCorner > mDistanceUpCorner )
820 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
822 // would tear the paper from the bottom spine in real situation
823 if( distanceBottomCorner > mDistanceBottomCorner )
825 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
827 // If direction has a very high y component, reduce it.
828 Vector2 curveDirection = currentCenter - mOriginalCenter;
829 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
831 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
833 // If the vertical distance is high, reduce it
834 float yShift = currentCenter.y - mOriginalCenter.y;
835 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
837 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
840 // use contraints to control the page shape and rotation when the pan position is near the spine
841 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
843 // set the property values used by the constraints
844 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
845 Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
846 Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
848 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
849 // also set up the RotationConstraint to the page actor
853 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
854 if( currentCenter.y >= mOriginalCenter.y )
856 corner = Vector2( 1.1f*mPageSize.width, 0.f );
860 corner = mPageSize*1.1f;
863 Vector2 offset( currentCenter-mOriginalCenter );
864 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
865 /( offset.x*offset.x + offset.y*offset.y );
869 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
870 Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
871 originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
872 originalCenterConstraint.Apply();
874 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
875 Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
876 currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
877 currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
878 currentCenterConstraint.Apply();
880 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
882 float distance = offset.Length();
883 Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
884 rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
885 rotationConstraint.Apply();
887 mConstraints = false;
892 if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
894 mPanActor.RemoveConstraints();
895 mTurnEffect[mIndex].RemoveConstraints();
896 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
898 mPanDisplacement = 0.f;
901 mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
902 mCurrentCenter = currentCenter;
903 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
908 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
910 // Guard against destruction during signal emission
911 Toolkit::PageTurnView handle( GetOwner() );
915 if(!mIsAnimating[mIndex])
917 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
922 mPagePanFinishedSignal.Emit( handle );
924 ImageActor actor = mPanActor;
927 if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
929 // update the pages here instead of in the TurnedOver callback function
930 // as new page is allowed to respond to the pan gesture before other pages finishing animation
931 if(mIsTurnBack[actor])
934 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
935 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
940 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
941 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
945 // set up an animation to turn the page over
947 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
948 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
949 animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
950 width,AlphaFunction::EASE_OUT_SINE);
951 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
952 Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
953 mAnimationActorPair[animation] = actor;
954 mAnimationIndexPair[animation] = mIndex;
956 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
958 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
960 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
961 animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
962 mOriginalCenter, AlphaFunction::LINEAR );
963 mAnimationActorPair[animation] = actor;
964 mAnimationIndexPair[animation] = mIndex;
966 mIsSliding[mIndex] = true;
967 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
969 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
974 // In portrait view, an outwards flick should turn the previous page back
975 // In landscape view, nothing to do
976 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
982 void PageTurnView::TurnedOver( Animation& animation )
984 ImageActor actor = mAnimationActorPair[animation];
985 mIsTurnBack[actor] = !mIsTurnBack[actor];
986 actor.RemoveConstraints();
987 mRootOnScreen.Add(actor);
988 int index = mAnimationIndexPair[animation];
989 mIsAnimating[index] = false;
990 mTurnEffect[index].RemoveConstraints();
991 mAnimationIndexPair.erase( animation );
992 mAnimationActorPair.erase( animation );
994 SetSpineEffect( actor, mIsTurnBack[actor] );
996 // Guard against destruction during signal emission
997 Toolkit::PageTurnView handle( GetOwner() );
998 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1001 void PageTurnView::SliddenBack( Animation& animation )
1003 ImageActor actor = mAnimationActorPair[animation];
1004 mRootOnScreen.Add(actor);
1005 int index = mAnimationIndexPair[animation];
1006 mIsSliding[index] = false;
1007 mIsAnimating[index] = false;
1008 mAnimationIndexPair.erase( animation );
1009 mAnimationActorPair.erase( animation );
1011 SetSpineEffect( actor, mIsTurnBack[actor] );
1013 // Guard against destruction during signal emission
1014 Toolkit::PageTurnView handle( GetOwner() );
1015 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1018 void PageTurnView::OrganizePageDepth()
1020 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1022 if(mCurrentPageIndex+i < mTotalPageCount)
1024 mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1026 if( mCurrentPageIndex >= i + 1 )
1028 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1033 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
1035 SetShaderEffectRecursively( actor, shaderEffect );
1038 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1040 return mPageTurnStartedSignal;
1043 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1045 return mPageTurnFinishedSignal;
1048 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1050 return mPagePanStartedSignal;
1053 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1055 return mPagePanFinishedSignal;
1058 } // namespace Internal
1060 } // namespace Toolkit