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-toolkit/public-api/controls/default-controls/solid-color-actor.h>
26 namespace //unnamed namespace
29 TypeRegistration mType( typeid(Toolkit::PageTurnView), typeid(Toolkit::Control), NULL );
31 // default grid density for page turn effect, 10 pixels by 10 pixels
32 const float DEFAULT_GRID_DENSITY(10.0f);
34 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
35 const float MINIMUM_START_POSITION_RATIO(0.6f);
37 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
38 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
40 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
41 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
43 // duration of animation, shorter for faster speed
44 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
45 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
47 // the major&minor radius (in pixels) to form an ellipse shape
48 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
49 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
51 // constants for shadow casting
52 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
53 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
55 // constraints ////////////////////////////////////////////////////////////////
57 * Original Center Constraint
59 * This constraint adjusts the original center property of the page turn shader effect
60 * based on the X-direction displacement of the pan gesture
62 struct OriginalCenterConstraint
64 OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
65 : mOldCenter( originalCenter )
67 mNewCenter = originalCenter + offset;
68 mDistance = offset.Length() * 0.5f;
69 mDirection = offset / mDistance;
72 Vector2 operator()(const Vector2& current, const PropertyInput& panDisplacement)
74 float displacement = panDisplacement.GetFloat();
76 if( displacement < mDistance )
78 return mOldCenter + mDirection * displacement;
82 return mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
95 * This constraint adjusts the rotation property of the page actor
96 * based on the X-direction displacement of the pan gesture
98 struct RotationConstraint
100 RotationConstraint( float distance, float pageWidth, bool isTurnBack )
101 : mDistance( distance*0.5f )
103 mStep = 1.f / pageWidth;
104 mSign = isTurnBack ? -1.0f : 1.0f;
105 mConst = isTurnBack ? -1.0f : 0.f;
106 mRotation = isTurnBack ? Quaternion( -Math::PI, Vector3::YAXIS ) : Quaternion( 0.f, Vector3::YAXIS );
109 Quaternion operator()( const Quaternion& current, const PropertyInput& panDisplacement )
111 float displacement = panDisplacement.GetFloat();
113 if( displacement < mDistance)
119 float coef = std::max(-1.0f, mStep*(mDistance-displacement));
120 angle = Math::PI*( mConst + mSign*coef );
121 return Quaternion( angle, Vector3::YAXIS );
129 Quaternion mRotation;
133 * Current Center Constraint
135 * This constraint adjusts the current center property of the page turn shader effect
136 * based on the pan position and the original center position
138 struct CurrentCenterConstraint
140 CurrentCenterConstraint( float pageWidth)
141 : mPageWidth( pageWidth )
143 mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
146 Vector2 operator()( const Vector2& current, const PropertyInput& center, const PropertyInput& originalCenter )
148 Vector2 centerPosition = center.GetVector2();
149 if( centerPosition.x > 0.f )
151 return Vector2( mThres+centerPosition.x*0.5f , centerPosition.y);
155 Vector2 centerOrigin = originalCenter.GetVector2();
156 Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
157 float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
158 // 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
161 coef = (coef+0.225f)/10.0f;
163 return centerOrigin - direction * coef;
171 struct ShadowBlurStrengthConstraint
173 ShadowBlurStrengthConstraint( float thres )
177 float operator()( const float current, const PropertyInput& currentCenter, const PropertyInput& originalCenter, const PropertyInput& panDisplacement)
179 float displacement = panDisplacement.GetFloat();
181 if( EqualsZero(displacement))
183 Vector2 cur = currentCenter.GetVector2();
184 Vector2 ori = originalCenter.GetVector2();
185 blurStrength = 5.f*(ori-cur).Length() / mThres;
189 blurStrength = 1.f - (displacement-2.f*mThres)/mThres;
192 blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
199 bool IsActorHittableFunction( Actor actor, Dali::HitTestAlgorithm::TraverseType type )
201 bool hittable = false;
205 case Dali::HitTestAlgorithm::CHECK_ACTOR:
207 // Check whether the actor is visible and not fully transparent.
208 Property::Index propertyActorHittable = actor.GetPropertyIndex(Toolkit::PageFactory::ACTOR_HITTABLE);
209 if( actor.IsSensitive()
211 && actor.GetCurrentWorldColor().a > 0.01f// not FULLY_TRANSPARENT
212 && ( propertyActorHittable != Property::INVALID_INDEX &&
213 actor.GetProperty<bool>( propertyActorHittable ) ) )
219 case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
221 if( actor.IsSensitive() && actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible.
236 } //unnamed namespace
247 // these several constants are also used in the derived classes
248 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
249 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
250 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
251 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
253 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
254 : ControlImpl( true ),
255 mPageFactory( pageFactory ),
256 mPageSize( pageSize ),
257 mIsEditMode( false ),
259 mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
260 mCurrentPageIndex( 0 ),
263 mPageUpdated( true ),
264 mPageTurnStartedSignal(),
265 mPageTurnFinishedSignal(),
266 mPagePanStartedSignal(),
267 mPagePanFinishedSignal()
269 mPageActors.resize( NUMBER_OF_CACHED_PAGES );
270 mIsAnimating.resize( MAXIMUM_TURNING_NUM );
271 mIsSliding.resize( MAXIMUM_TURNING_NUM );
272 mTurnEffect.resize( MAXIMUM_TURNING_NUM );
273 mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
274 mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
277 PageTurnView::~PageTurnView()
281 void PageTurnView::OnInitialize()
283 // create the two book spine effect for static images, left and right side pages respectively
284 mSpineEffectFront = PageTurnBookSpineEffect::New();
285 mSpineEffectFront.SetIsBackImageVisible( false );
286 mSpineEffectFront.SetPageWidth( mPageSize.width );
287 mSpineEffectFront.SetShadowWidth( 0.f );
288 mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
290 mSpineEffectBack = PageTurnBookSpineEffect::New();
291 mSpineEffectBack.SetIsBackImageVisible( true );
292 mSpineEffectBack.SetPageWidth( mPageSize.width );
293 mSpineEffectBack.SetShadowWidth( 0.f );
294 mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
296 // create the page turn effect objects
297 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
299 mTurnEffect[i] = Toolkit::PageTurnEffect::New( false );
300 mTurnEffect[i].SetProperty( ShaderEffect::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
301 mTurnEffect[i].SetPageSize( mPageSize );
302 mTurnEffect[i].SetShadowWidth(0.f);
303 mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
304 mIsAnimating[i] = false;
305 mIsSliding[i] = false;
306 mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
307 mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
310 mTurningPageLayer = Layer::New();
311 mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
312 // Set control size and the parent origin of turningPageLayer
313 OnPageTurnViewInitialize();
315 mRootOnScreen = Actor::New();
316 mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION );
317 mRootOnScreen.SetSize( mControlSize );
318 Self().Add( mRootOnScreen );
319 mRootOnScreen.Add(mTurningPageLayer);
321 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
322 mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded();
323 if( mNeedOffscreenRendering )
328 // add pages to the scene, and set depth for the stacked pages
329 for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
334 mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
338 // enable the pan gesture which is attached to the control
339 EnableGestureDetection(Gesture::Type(Gesture::Pan));
341 mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage);
344 void PageTurnView::SetupRenderTasks()
346 mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES );
347 mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES );
348 mRenderedPage.resize( NUMBER_OF_CACHED_PAGES );
350 mCameraActor = CameraActor::New(mControlSize);
351 mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
352 mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
353 mCameraActor.SetInheritScale( false );
354 Self().Add(mCameraActor);
356 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
357 for(int i=0; i<NUMBER_OF_CACHED_PAGES; i++)
359 mPageSourceActor[i] = Actor::New();
360 mPageSourceActor[i].SetParentOrigin(ParentOrigin::CENTER);
361 mPageSourceActor[i].SetColorMode( USE_OWN_COLOR );
362 mPageSourceActor[i].SetPositionInheritanceMode( DONT_INHERIT_POSITION );
363 mPageSourceActor[i].SetInheritScale( false );
364 Self().Add( mPageSourceActor[i] );
365 mPageSourceActor[i].SetSensitive( false );
367 mRenderedPage[i] = FrameBufferImage::New( mControlSize.width, mControlSize.height, Pixel::RGB8888, Image::Unused );
368 mOffscreenTask[i] = taskList.CreateTask();
369 mOffscreenTask[i].SetRefreshRate( RenderTask::REFRESH_ONCE );
370 mOffscreenTask[i].SetCameraActor(mCameraActor);
371 mOffscreenTask[i].SetSourceActor( mPageSourceActor[i] );
372 mOffscreenTask[i].SetExclusive(true);
373 mOffscreenTask[i].SetInputEnabled( false );
374 mOffscreenTask[i].SetClearEnabled( true );
375 mOffscreenTask[i].SetClearColor( Vector4(0.f,0.f,0.f,0.f) );
376 mOffscreenTask[i].SetTargetFrameBuffer( mRenderedPage[i] );
377 mOffscreenTask[i].SetScreenToFrameBufferMappingActor( Self() );
381 void PageTurnView::SetupShadowView()
383 mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
384 Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
385 mShadowView.SetParentOrigin( origin );
386 mShadowView.SetAnchorPoint( origin );
387 mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
388 mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
390 mShadowLayer = Layer::New();
391 mShadowLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
392 mRootOnScreen.Add(mShadowLayer);
393 mShadowLayer.Raise();
395 mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
396 mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
397 mShadowPlane.SetSize( mControlSize );
398 mShadowLayer.Add( mShadowPlane );
399 mShadowView.SetShadowPlane( mShadowPlane );
401 mPointLight = Actor::New();
402 mPointLight.SetAnchorPoint( origin );
403 mPointLight.SetParentOrigin( origin );
404 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
405 mRootOnScreen.Add( mPointLight );
406 mShadowView.SetPointLight( mPointLight );
408 mTurningPageLayer.Add( mShadowView );
409 mShadowView.Activate();
412 void PageTurnView::OnControlStageConnection()
415 mTurningPageLayer.RaiseToTop();
418 void PageTurnView::OnControlStageDisconnection()
422 Self().Remove(mPointLight);
423 Self().Remove(mShadowLayer);
424 mTurningPageLayer.Remove( mShadowView );
427 // make sure the status of the control is updated correctly when the pan gesture is interrupted
432 mRootOnScreen.Add(mPanActor);
433 mIsAnimating[mIndex] = false;
434 mPanActor.RemoveConstraints();
435 mTurnEffect[mIndex].RemoveConstraints();
438 SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
442 void PageTurnView::OnControlSizeSet( const Vector3& size )
444 // disable the SetSize of the control from the application
445 Self().SetSize( mControlSize );
448 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
450 mSpineShadowParameter = spineShadowParameter;
452 // set spine shadow parameter to all the shader effects
453 mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
454 mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
455 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
457 mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
461 Vector2 PageTurnView::GetSpineShadowParameter()
463 return mSpineShadowParameter;
466 void PageTurnView::GoToPage( unsigned int pageId )
468 int pageIdx = static_cast<int>(pageId);
469 // record the new current page index
470 mCurrentPageIndex = pageIdx;
472 // clear the old pages
473 for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
477 mPageActors[i].Unparent();
478 mPageActors[i].Reset();
482 // add the current page and the pages right before and after it
483 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
487 // set ordered depth to the stacked pages
491 unsigned int PageTurnView::GetCurrentPage()
493 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
494 return static_cast< unsigned int >( mCurrentPageIndex );
497 Actor PageTurnView::EnterEditMode()
499 if( mNeedOffscreenRendering )
501 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
505 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
506 mOffscreenTask[index].SetInputEnabled( true );
507 mPageSourceActor[index].SetSensitive( true );
508 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
510 mRootOnScreen.SetSensitive(false);
512 return mPageSourceActor[index].GetChildAt( 0 );
520 void PageTurnView::LeaveEditMode()
522 if( mNeedOffscreenRendering )
524 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
528 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
529 mOffscreenTask[index].SetInputEnabled( false );
530 mPageSourceActor[index].SetSensitive( false );
531 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
533 mRootOnScreen.SetSensitive(true);
537 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
539 if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
541 int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
543 Dali::HitTestAlgorithm::Results results;
544 if( !mOffscreenTask[index].GetInputEnabled() )
546 mOffscreenTask[index].SetInputEnabled( true );
547 mPageSourceActor[index].SetSensitive( true );
548 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
549 mOffscreenTask[index].SetInputEnabled( false );
550 mPageSourceActor[index].SetSensitive( false );
554 Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
556 actorCoordinates = results.actorCoordinates;
557 return results.actor;
565 void PageTurnView::AddPage( int pageIndex )
567 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
569 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
571 if( mNeedOffscreenRendering )
573 Actor source = mPageFactory.NewPage( pageIndex );
574 if( mPageSourceActor[index].GetChildCount() > 0 )
576 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
578 mPageSourceActor[index].Add( source );
579 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
580 newPage = NewPageFromRenderBuffer( pageIndex );
584 newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
585 DALI_ASSERT_ALWAYS( newPage );
587 newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
588 newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
589 newPage.SetSize( mPageSize );
590 mRootOnScreen.Add( newPage );
591 mPageActors[index] = newPage;
593 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
594 mIsTurnBack[ newPage ] = isLeftSide;
597 // new page is added to the left side, so need to rotate it 180 degrees
598 newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
602 SetShaderEffect( newPage, mSpineEffectFront);
605 // For Portrait, nothing to do
606 // 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
607 OnAddPage( newPage, isLeftSide );
611 void PageTurnView::RemovePage( int pageIndex )
613 if( pageIndex > -1 && pageIndex < mTotalPageCount)
615 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
616 mPageActors[index].Unparent();
617 mIsTurnBack.erase( mPageActors[index] );
618 mPageActors[index].Reset();
619 if( mNeedOffscreenRendering )
621 mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
626 void PageTurnView::RenderPage( int pageIndex )
628 if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
629 && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
631 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
632 mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
636 void PageTurnView::RefreshAll()
638 mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
639 if( mTotalPageCount > 0 )
641 if(mCurrentPageIndex < mTotalPageCount)
643 GoToPage( mCurrentPageIndex );
647 GoToPage( mCurrentPageIndex-- );
652 void PageTurnView::RefreshCurrentPage()
654 RenderPage( mCurrentPageIndex );
657 void PageTurnView::OnPan( PanGesture gesture )
661 // when interrupted by the call of DisplayCurrentPageSourceActor(),
662 // make sure the panFinished is always called before stopping to responding the gesture
663 // so the status of the control is updated correctly
667 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
672 // the pan gesture is attached to control itself instead of each page
673 switch( gesture.state )
675 case Gesture::Started:
678 // to find out whether the undergoing turning page number already reaches the maximum allowed
679 // and get one idle index when it is animatable
680 bool animatable = false;
681 for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
683 if( !mIsAnimating[mIndex] )
688 if( mIsSliding[mIndex] )
694 mIndex = mIndex % MAXIMUM_TURNING_NUM;
697 if( mPageUpdated && animatable )
699 SetPanActor( gesture.position ); // determine which page actor is panned
700 if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
704 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
712 case Gesture::Continuing:
714 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
717 case Gesture::Finished:
718 case Gesture::Cancelled:
721 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
725 case Gesture::Possible:
733 void PageTurnView::PanStarted( const Vector2& gesturePosition )
735 mPressDownPosition = gesturePosition;
742 mOriginalCenter = gesturePosition;
743 mTurnEffect[mIndex].SetIsTurningBack( mIsTurnBack[ mPanActor] );
745 mPageUpdated = false;
747 // Guard against destruction during signal emission
748 Toolkit::PageTurnView handle( GetOwner() );
749 mPagePanStartedSignal.Emit( handle );
752 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
759 // Guard against destruction during signal emission
760 Toolkit::PageTurnView handle( GetOwner() );
764 // when the touch down position is near the spine
765 // or when the panning goes outwards or some other position which would tear the paper in real situation
766 // we change the start position into the current panning position and update the shader parameters
767 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
768 || gesturePosition.x > mOriginalCenter.x-1.0f
769 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
770 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
772 mOriginalCenter = gesturePosition;
776 mDistanceUpCorner = mOriginalCenter.Length();
777 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
778 mShadowView.Add( mPanActor );
779 SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
780 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
781 mCurrentCenter = mOriginalCenter;
782 mTurnEffect[mIndex].SetCurrentCenter( mCurrentCenter );
783 mPanDisplacement = 0.f;
786 mIsAnimating[mIndex] = true;
788 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
790 mShadowView.RemoveConstraints();
792 self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
793 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView.GetBlurStrengthPropertyIndex(),
794 Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())),
795 Source(mTurnEffect[mIndex], mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())),
796 Source( self, mPropertyPanDisplacement[mIndex] ),
797 ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
798 mShadowView.ApplyConstraint( shadowBlurStrengthConstraint );
803 Vector2 currentCenter = gesturePosition;
805 // Test whether the new current center would tear the paper from the top pine in real situation
806 // we do not forbid this totally, which would restrict the panning gesture too much
807 // instead, set it to the nearest allowable position
808 float distanceUpCorner = currentCenter.Length();
809 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
810 if( distanceUpCorner > mDistanceUpCorner )
812 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
814 // would tear the paper from the bottom spine in real situation
815 if( distanceBottomCorner > mDistanceBottomCorner )
817 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
819 // If direction has a very high y component, reduce it.
820 Vector2 curveDirection = currentCenter - mOriginalCenter;
821 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
823 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
825 // If the vertical distance is high, reduce it
826 float yShift = currentCenter.y - mOriginalCenter.y;
827 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
829 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
832 // use contraints to control the page shape and rotation when the pan position is near the spine
833 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
835 // set the property values used by the constraints
836 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
837 Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
838 Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
840 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
841 // also set up the RotationConstraint to the page actor
845 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
846 if( currentCenter.y >= mOriginalCenter.y )
848 corner = Vector2( 1.1f*mPageSize.width, 0.f );
852 corner = mPageSize*1.1f;
855 Vector2 offset( currentCenter-mOriginalCenter );
856 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
857 /( offset.x*offset.x + offset.y*offset.y );
860 Source source(self, mPropertyPanDisplacement[mIndex]);
862 Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
863 Constraint originalCenterConstraint = Constraint::New<Vector2>( shaderOriginalCenterPropertyIndex ,
865 OriginalCenterConstraint( mOriginalCenter, offset ));
866 mTurnEffect[mIndex].ApplyConstraint( originalCenterConstraint );
868 Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
869 Constraint currentCenterConstraint = Constraint::New<Vector2>( shaderCurrentCenterPropertyIndex,
870 Source(self, mPropertyCurrentCenter[mIndex]),
871 Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex),
872 CurrentCenterConstraint(mPageSize.width));
873 mTurnEffect[mIndex].ApplyConstraint( currentCenterConstraint );
875 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
877 float distance = offset.Length();
878 Constraint rotationConstraint = Constraint::New<Quaternion>( Actor::ROTATION,
879 Source( self, mPropertyPanDisplacement[mIndex] ),
880 RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
881 mPanActor.ApplyConstraint( rotationConstraint );
883 mConstraints = false;
888 if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
890 mPanActor.RemoveConstraints();
891 mTurnEffect[mIndex].RemoveConstraints();
892 mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
894 mPanDisplacement = 0.f;
897 mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
898 mCurrentCenter = currentCenter;
899 GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
904 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
906 // Guard against destruction during signal emission
907 Toolkit::PageTurnView handle( GetOwner() );
911 if(!mIsAnimating[mIndex])
913 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
918 mPagePanFinishedSignal.Emit( handle );
920 Actor actor = mPanActor;
923 if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
925 // update the pages here instead of in the TurnedOver callback function
926 // as new page is allowed to respond to the pan gesture before other pages finishing animation
927 if(mIsTurnBack[actor])
930 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
931 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
936 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
937 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
941 // set up an animation to turn the page over
943 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
944 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
945 animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
946 width,AlphaFunctions::EaseOutSine33);
947 animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
948 Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunctions::EaseOutSine33);
949 mAnimationActorPair[animation] = actor;
950 mAnimationIndexPair[animation] = mIndex;
952 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
954 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
956 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
957 animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
958 mOriginalCenter, AlphaFunctions::Linear );
959 mAnimationActorPair[animation] = actor;
960 mAnimationIndexPair[animation] = mIndex;
962 mIsSliding[mIndex] = true;
963 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
965 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
970 // In portrait view, an outwards flick should turn the previous page back
971 // In landscape view, nothing to do
972 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
978 void PageTurnView::TurnedOver( Animation& animation )
980 Actor actor = mAnimationActorPair[animation];
981 mIsTurnBack[actor] = !mIsTurnBack[actor];
982 actor.RemoveConstraints();
983 mRootOnScreen.Add(actor);
984 int index = mAnimationIndexPair[animation];
985 mIsAnimating[index] = false;
986 mTurnEffect[index].RemoveConstraints();
987 mAnimationIndexPair.erase( animation );
988 mAnimationActorPair.erase( animation );
990 SetSpineEffect( actor, mIsTurnBack[actor] );
992 // Guard against destruction during signal emission
993 Toolkit::PageTurnView handle( GetOwner() );
994 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
997 void PageTurnView::SliddenBack( Animation& animation )
999 Actor actor = mAnimationActorPair[animation];
1000 mRootOnScreen.Add(actor);
1001 int index = mAnimationIndexPair[animation];
1002 mIsSliding[index] = false;
1003 mIsAnimating[index] = false;
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[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1014 void PageTurnView::OrganizePageDepth()
1016 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1018 if(mCurrentPageIndex+i < mTotalPageCount)
1020 mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1022 if( mCurrentPageIndex >= i + 1 )
1024 mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1029 void PageTurnView::SetShaderEffect( Actor actor, ShaderEffect shaderEffect )
1031 actor.SetShaderEffect( shaderEffect );
1033 if( actor.GetChildCount() > 0 )
1035 actor.GetChildAt( 0 ).SetShaderEffect(shaderEffect);
1039 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1041 return mPageTurnStartedSignal;
1044 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1046 return mPageTurnFinishedSignal;
1049 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1051 return mPagePanStartedSignal;
1054 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1056 return mPagePanFinishedSignal;
1059 } // namespace Internal
1061 } // namespace Toolkit