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 <cstring> // for strcmp
23 #include <dali/public-api/animation/animation.h>
24 #include <dali/public-api/animation/constraint.h>
25 #include <dali/public-api/images/resource-image.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/devel-api/object/type-registry-helper.h>
28 #include <dali/integration-api/debug.h>
31 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-effect.h>
32 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-book-spine-effect.h>
33 #include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
35 // headers needed for backward compatibility of PageFactory::NewPage(pageId) API
36 #include <dali/public-api/actors/image-actor.h>
40 namespace //Unnamed namespace
42 // broken image is loaded if there is no valid image provided for the page
43 const char * const BROKEN_IMAGE_URL( DALI_IMAGE_DIR "broken.png");
45 // names of shader property map
46 const char * const CUSTOM_SHADER( "shader" );
47 const char * const CUSTOM_VERTEX_SHADER( "vertexShader" );
48 const char * const CUSTOM_FRAGMENT_SHADER( "fragmentShader" );
50 // properties set on shader, these properties have the constant value in regardless of the page status
51 const char * const PROPERTY_SPINE_SHADOW ( "uSpineShadowParameter" ); // uniform for both spine and turn effect
53 // properties set on actor, the value of these properties varies depending on the page status
54 // properties used in turn effect
55 const char * const PROPERTY_TURN_DIRECTION( "uIsTurningBack" ); // uniform
56 const char * const PROPERTY_COMMON_PARAMETERS( "uCommonParameters" ); //uniform
58 const char * const PROPERTY_PAN_DISPLACEMENT( "panDisplacement" );// property used to constrain the uniforms
59 const char * const PROPERTY_PAN_CENTER( "panCenter" );// property used to constrain the uniforms
61 // default grid density for page turn effect, 20 pixels by 20 pixels
62 const float DEFAULT_GRID_DENSITY(20.0f);
64 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
65 const float MINIMUM_START_POSITION_RATIO(0.6f);
67 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
68 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
70 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
71 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
73 // duration of animation, shorter for faster speed
74 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
75 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
77 // the major&minor radius (in pixels) to form an ellipse shape
78 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
79 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
81 // constants for shadow casting
82 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
83 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
85 // constraints ////////////////////////////////////////////////////////////////
87 * Original Center Constraint
89 * This constraint adjusts the original center property of the page turn shader effect
90 * based on the X-direction displacement of the pan gesture
92 struct OriginalCenterConstraint
94 OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
95 : mOldCenter( originalCenter )
97 mNewCenter = originalCenter + offset;
98 mDistance = offset.Length() * 0.5f;
99 mDirection = offset / mDistance;
102 void operator()( Vector2& current, const PropertyInputContainer& inputs )
104 float displacement = inputs[0]->GetFloat();
106 if( displacement < mDistance )
108 current = mOldCenter + mDirection * displacement;
112 current = mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
123 * Rotation Constraint
125 * This constraint adjusts the rotation property of the page actor
126 * based on the X-direction displacement of the pan gesture
128 struct RotationConstraint
130 RotationConstraint( float distance, float pageWidth, bool isTurnBack )
131 : mDistance( distance*0.5f )
133 mStep = 1.f / pageWidth;
134 mSign = isTurnBack ? -1.0f : 1.0f;
135 mConst = isTurnBack ? -1.0f : 0.f;
136 mRotation = isTurnBack ? Quaternion( Radian( -Math::PI ), Vector3::YAXIS ) : Quaternion( Radian(0.f), Vector3::YAXIS );
139 void operator()( Quaternion& current, const PropertyInputContainer& inputs )
141 float displacement = inputs[0]->GetFloat();
142 if( displacement < mDistance)
148 float coef = std::max(-1.0f, mStep*(mDistance-displacement));
149 float angle = Math::PI * ( mConst + mSign * coef );
150 current = Quaternion( Radian( angle ), Vector3::YAXIS );
158 Quaternion mRotation;
162 * Current Center Constraint
164 * This constraint adjusts the current center property of the page turn shader effect
165 * based on the pan position and the original center position
167 struct CurrentCenterConstraint
169 CurrentCenterConstraint( float pageWidth )
170 : mPageWidth( pageWidth )
172 mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
175 void operator()( Vector2& current, const PropertyInputContainer& inputs )
177 const Vector2& centerPosition = inputs[0]->GetVector2();
178 if( centerPosition.x > 0.f )
180 current.x = mThres+centerPosition.x * 0.5f;
181 current.y = centerPosition.y;
185 const Vector2& centerOrigin = inputs[1]->GetVector2();
186 Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
187 float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
188 // when coef <= 0, the page is flat, slow down the last moment of the page stretch by 10 times to avoid a small bounce
191 coef = (coef+0.225f)/10.0f;
193 current = centerOrigin - direction * coef;
201 struct ShadowBlurStrengthConstraint
203 ShadowBlurStrengthConstraint( float thres )
207 void operator()( float& blurStrength, const PropertyInputContainer& inputs )
209 float displacement = inputs[2]->GetFloat();
210 if( EqualsZero(displacement))
212 const Vector2& cur = inputs[0]->GetVector2();
213 const Vector2& ori = inputs[1]->GetVector2();
214 blurStrength = 5.f*(ori-cur).Length() / mThres;
218 blurStrength = 1.f - (displacement-2.f*mThres)/mThres;
221 blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
227 } //unnamed namespace
243 // empty handle as we cannot create PageTurnView(but type registered for page turn signal)
247 // Setup properties, signals and actions using the type-registry.
248 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::PageTurnView, Toolkit::Control, Create );
250 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "pageSize", VECTOR2, PAGE_SIZE )
251 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "currentPageId", INTEGER, CURRENT_PAGE_ID )
252 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "spineShadow", VECTOR2, SPINE_SHADOW )
254 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pageTurnStarted", SIGNAL_PAGE_TURN_STARTED )
255 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pageTurnFinished", SIGNAL_PAGE_TURN_FINISHED )
256 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pagePanStarted", SIGNAL_PAGE_PAN_STARTED )
257 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pagePanFinished", SIGNAL_PAGE_PAN_FINISHED )
259 DALI_TYPE_REGISTRATION_END()
263 // these several constants are also used in the derived classes
264 const char * const PageTurnView::PROPERTY_TEXTURE_WIDTH( "uTextureWidth" ); // uniform name
265 const char * const PageTurnView::PROPERTY_ORIGINAL_CENTER( "originalCenter" ); // property used to constrain the uniform
266 const char * const PageTurnView::PROPERTY_CURRENT_CENTER( "currentCenter" );// property used to constrain the uniform
267 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
268 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
269 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
270 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
272 PageTurnView::Page::Page()
273 : isTurnBack( false )
275 actor = Actor::New();
276 actor.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
277 actor.SetParentOrigin( ParentOrigin::CENTER_LEFT );
278 actor.SetVisible( false );
280 propertyPanDisplacement = actor.RegisterProperty( PROPERTY_PAN_DISPLACEMENT, 0.f );
281 propertyPanCenter = actor.RegisterProperty(PROPERTY_PAN_CENTER, Vector2::ZERO);
283 propertyOriginalCenter = actor.RegisterProperty(PROPERTY_ORIGINAL_CENTER, Vector2::ZERO);
284 propertyCurrentCenter = actor.RegisterProperty(PROPERTY_CURRENT_CENTER, Vector2::ZERO);
285 Matrix zeroMatrix(true);
286 actor.RegisterProperty(PROPERTY_COMMON_PARAMETERS, zeroMatrix);
287 propertyTurnDirection = actor.RegisterProperty(PROPERTY_TURN_DIRECTION, -1.f);
290 void PageTurnView::Page::SetImage( Image image )
294 textureSet = TextureSet::New();
297 textureSet.SetImage( 0u, image );
300 void PageTurnView::Page::UseEffect(Shader newShader)
305 renderer.SetShader( shader );
309 void PageTurnView::Page::UseEffect(Shader newShader, Geometry geometry)
311 UseEffect( newShader );
315 renderer = Renderer::New( geometry, shader );
319 textureSet = TextureSet::New();
322 renderer.SetTextures( textureSet );
323 renderer.SetProperty( Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON );
324 actor.AddRenderer( renderer );
328 void PageTurnView::Page::ChangeTurnDirection()
330 isTurnBack = !isTurnBack;
331 actor.SetProperty( propertyTurnDirection, isTurnBack ? 1.f : -1.f );
334 void PageTurnView::Page::SetPanDisplacement(float value)
336 actor.SetProperty( propertyPanDisplacement, value );
339 void PageTurnView::Page::SetPanCenter( const Vector2& value )
341 actor.SetProperty( propertyPanCenter, value );
344 void PageTurnView::Page::SetOriginalCenter( const Vector2& value )
346 actor.SetProperty( propertyOriginalCenter, value );
349 void PageTurnView::Page::SetCurrentCenter( const Vector2& value )
351 actor.SetProperty( propertyCurrentCenter, value );
354 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
355 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS ) ),
356 mPageFactory( &pageFactory ),
357 mPageSize( pageSize ),
358 mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
359 mDistanceUpCorner( 0.f ),
360 mDistanceBottomCorner( 0.f ),
361 mPanDisplacement( 0.f ),
362 mTotalPageCount( 0 ),
363 mCurrentPageIndex( 0 ),
364 mTurningPageIndex( 0 ),
367 mAnimatingCount( 0 ),
368 mConstraints( false ),
370 mPageUpdated( true ),
371 mPageTurnStartedSignal(),
372 mPageTurnFinishedSignal(),
373 mPagePanStartedSignal(),
374 mPagePanFinishedSignal()
378 PageTurnView::~PageTurnView()
382 void PageTurnView::OnInitialize()
384 // create the book spine effect for static pages
385 Property::Map spineEffectMap = CreatePageTurnBookSpineEffect();
386 mSpineEffectShader = CreateShader( spineEffectMap );
387 mSpineEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter );
388 // create the turn effect for turning pages
389 Property::Map turnEffectMap = CreatePageTurnEffect();
390 mTurnEffectShader = CreateShader( turnEffectMap );
391 mTurnEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter );
393 // create the grid geometry for pages
394 uint16_t width = static_cast<uint16_t>(mPageSize.width / DEFAULT_GRID_DENSITY + 0.5f);
395 uint16_t height = static_cast<uint16_t>(mPageSize.height / DEFAULT_GRID_DENSITY + 0.5f);
396 mGeometry = RendererFactoryCache::CreateGridGeometry( Uint16Pair( width, height ) );
398 mPages.reserve( NUMBER_OF_CACHED_PAGES );
399 for( int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
401 mPages.push_back( Page() );
402 mPages[i].actor.SetSize( mPageSize );
403 Self().Add( mPages[i].actor );
406 // create the layer for turning images
407 mTurningPageLayer = Layer::New();
408 mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
409 mTurningPageLayer.SetBehavior(Layer::LAYER_3D);
410 mTurningPageLayer.Raise();
412 // Set control size and the parent origin of page layers
413 OnPageTurnViewInitialize();
415 Self().Add(mTurningPageLayer);
417 mTotalPageCount = static_cast<int>( mPageFactory->GetNumberOfPages() );
418 // add pages to the scene, and set depth for the stacked pages
419 for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
422 mPages[i].actor.SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
424 mPages[0].actor.SetVisible(true);
426 // enable the pan gesture which is attached to the control
427 EnableGestureDetection(Gesture::Type(Gesture::Pan));
430 Shader PageTurnView::CreateShader( const Property::Map& shaderMap )
433 Property::Value* shaderValue = shaderMap.Find( CUSTOM_SHADER );
434 Property::Map shaderSource;
435 if( shaderValue && shaderValue->Get( shaderSource ) )
437 std::string vertexShader;
438 Property::Value* vertexShaderValue = shaderSource.Find( CUSTOM_VERTEX_SHADER );
439 if( !vertexShaderValue || !vertexShaderValue->Get( vertexShader ) )
441 DALI_LOG_ERROR("PageTurnView::CreateShader failed: vertex shader source is not available.\n");
443 std::string fragmentShader;
444 Property::Value* fragmentShaderValue = shaderSource.Find( CUSTOM_FRAGMENT_SHADER );
445 if( !fragmentShaderValue || !fragmentShaderValue->Get( fragmentShader ) )
447 DALI_LOG_ERROR("PageTurnView::CreateShader failed: fragment shader source is not available.\n");
449 shader = Shader::New( vertexShader, fragmentShader );
453 DALI_LOG_ERROR("PageTurnView::CreateShader failed: shader source is not available.\n");
459 void PageTurnView::SetupShadowView()
461 mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
462 Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
463 mShadowView.SetParentOrigin( origin );
464 mShadowView.SetAnchorPoint( origin );
465 mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
466 mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
468 mShadowPlaneBackground = Actor::New();
469 mShadowPlaneBackground.SetParentOrigin( ParentOrigin::CENTER );
470 mShadowPlaneBackground.SetSize( mControlSize );
471 Self().Add( mShadowPlaneBackground );
472 mShadowView.SetShadowPlaneBackground( mShadowPlaneBackground );
474 mPointLight = Actor::New();
475 mPointLight.SetAnchorPoint( origin );
476 mPointLight.SetParentOrigin( origin );
477 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
478 Self().Add( mPointLight );
479 mShadowView.SetPointLight( mPointLight );
481 mTurningPageLayer.Add( mShadowView );
482 mShadowView.Activate();
485 void PageTurnView::OnStageConnection( int depth )
487 Control::OnStageConnection( depth );
492 void PageTurnView::OnStageDisconnection()
496 mShadowView.RemoveConstraints();
497 mPointLight.Unparent();
498 mShadowPlaneBackground.Unparent();
499 mShadowView.Unparent();
502 // make sure the status of the control is updated correctly when the pan gesture is interrupted
505 Control::OnStageDisconnection();
508 void PageTurnView::SetPageSize( const Vector2& pageSize )
510 mPageSize = pageSize;
514 mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
517 for( size_t i=0; i<mPages.size(); i++ )
519 mPages[i].actor.SetSize( mPageSize );
522 OnPageTurnViewInitialize();
524 if( mShadowPlaneBackground )
526 mShadowPlaneBackground.SetSize( mControlSize );
530 Vector2 PageTurnView::GetPageSize()
535 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
537 mSpineShadowParameter = spineShadowParameter;
539 // set spine shadow parameter to all the shader effects
540 mSpineEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter );
541 mTurnEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter );
544 Vector2 PageTurnView::GetSpineShadowParameter()
546 return mSpineShadowParameter;
549 void PageTurnView::GoToPage( unsigned int pageId )
551 int pageIdx = Clamp( static_cast<int>(pageId), 0, mTotalPageCount-1);
553 if( mCurrentPageIndex == pageIdx )
558 // if any animation ongoing, stop it.
561 // record the new current page index
562 mCurrentPageIndex = pageIdx;
565 // add the current page and the pages right before and after it
566 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
571 mPages[pageId%NUMBER_OF_CACHED_PAGES].actor.SetVisible(true);
574 mPages[(pageId-1)%NUMBER_OF_CACHED_PAGES].actor.SetVisible(true);
576 // set ordered depth to the stacked pages
580 unsigned int PageTurnView::GetCurrentPage()
582 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
583 return static_cast< unsigned int >( mCurrentPageIndex );
586 void PageTurnView::AddPage( int pageIndex )
588 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
590 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
593 newPageImage = mPageFactory->NewPage( pageIndex );
595 if( !newPageImage ) // load the broken image
597 newPageImage = ResourceImage::New( BROKEN_IMAGE_URL );
600 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
601 if( mPages[index].isTurnBack != isLeftSide )
603 mPages[index].ChangeTurnDirection();
606 float degree = isLeftSide ? 180.f :0.f;
607 mPages[index].actor.SetOrientation( Degree( degree ), Vector3::YAXIS );
608 mPages[index].actor.SetVisible( false );
609 mPages[index].UseEffect( mSpineEffectShader, mGeometry );
610 mPages[index].SetImage( newPageImage );
612 // For Portrait, nothing to do
613 // For Landscape, set the parent origin to CENTER
614 OnAddPage( mPages[index].actor, isLeftSide );
618 void PageTurnView::RemovePage( int pageIndex )
620 if( pageIndex > -1 && pageIndex < mTotalPageCount)
622 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
623 mPages[index].actor.SetVisible(false);
627 void PageTurnView::OnPan( const PanGesture& gesture )
629 // the pan gesture is attached to control itself instead of each page
630 switch( gesture.state )
632 case Gesture::Started:
634 // check whether the undergoing turning page number already reaches the maximum allowed
635 if( mPageUpdated && mAnimatingCount< MAXIMUM_TURNING_NUM && mSlidingCount < 1 )
637 SetPanActor( gesture.position ); // determine which page actor is panned
638 if( mTurningPageIndex != -1 && mPages[mTurningPageIndex % NUMBER_OF_CACHED_PAGES].actor.GetParent() != Self()) // if the page is added to turning layer,it is undergoing an animation currently
640 mTurningPageIndex = -1;
642 PanStarted( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
646 mTurningPageIndex = -1;
650 case Gesture::Continuing:
652 PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
655 case Gesture::Finished:
656 case Gesture::Cancelled:
658 PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
662 case Gesture::Possible:
670 void PageTurnView::PanStarted( const Vector2& gesturePosition )
672 mPressDownPosition = gesturePosition;
674 if( mTurningPageIndex == -1 )
679 mIndex = mTurningPageIndex % NUMBER_OF_CACHED_PAGES;
681 mOriginalCenter = gesturePosition;
683 mPageUpdated = false;
685 // Guard against destruction during signal emission
686 Toolkit::PageTurnView handle( GetOwner() );
687 mPagePanStartedSignal.Emit( handle );
690 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
692 if( mTurningPageIndex == -1 )
697 // Guard against destruction during signal emission
698 Toolkit::PageTurnView handle( GetOwner() );
702 // when the touch down position is near the spine
703 // or when the panning goes outwards or some other position which would tear the paper in real situation
704 // we change the start position into the current panning position and update the shader parameters
705 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
706 || gesturePosition.x > mOriginalCenter.x-1.0f
707 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
708 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
710 mOriginalCenter = gesturePosition;
714 mDistanceUpCorner = mOriginalCenter.Length();
715 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
716 mShadowView.Add( mPages[mIndex].actor );
717 mPages[mIndex].UseEffect( mTurnEffectShader );
718 mPages[mIndex].SetOriginalCenter( mOriginalCenter );
719 mCurrentCenter = mOriginalCenter;
720 mPages[mIndex].SetCurrentCenter( mCurrentCenter );
721 mPanDisplacement = 0.f;
722 mConstraints = false;
726 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), !mPages[mIndex].isTurnBack );
727 int id = mTurningPageIndex + (mPages[mIndex].isTurnBack ? -1 : 1);
728 if( id >=0 && id < mTotalPageCount )
730 mPages[id%NUMBER_OF_CACHED_PAGES].actor.SetVisible(true);
733 mShadowView.RemoveConstraints();
735 mPages[mIndex].SetPanDisplacement( 0.f );
737 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
738 shadowBlurStrengthConstraint.AddSource( Source(mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter) );
739 shadowBlurStrengthConstraint.AddSource( Source(mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter) );
740 shadowBlurStrengthConstraint.AddSource( Source(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement) );
741 shadowBlurStrengthConstraint.Apply();
746 Vector2 currentCenter = gesturePosition;
748 // Test whether the new current center would tear the paper from the top pine in real situation
749 // we do not forbid this totally, which would restrict the panning gesture too much
750 // instead, set it to the nearest allowable position
751 float distanceUpCorner = currentCenter.Length();
752 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
753 if( distanceUpCorner > mDistanceUpCorner )
755 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
757 // would tear the paper from the bottom spine in real situation
758 if( distanceBottomCorner > mDistanceBottomCorner )
760 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
762 // If direction has a very high y component, reduce it.
763 Vector2 curveDirection = currentCenter - mOriginalCenter;
764 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
766 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
768 // If the vertical distance is high, reduce it
769 float yShift = currentCenter.y - mOriginalCenter.y;
770 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
772 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
775 // use contraints to control the page shape and rotation when the pan position is near the spine
776 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
778 // set the property values used by the constraints
779 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
780 mPages[mIndex].SetPanDisplacement( mPanDisplacement );
781 mPages[mIndex].SetPanCenter( currentCenter );
783 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
784 // also set up the RotationConstraint to the page actor
788 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
789 if( currentCenter.y >= mOriginalCenter.y )
791 corner = Vector2( 1.1f*mPageSize.width, 0.f );
795 corner = mPageSize*1.1f;
798 Vector2 offset( currentCenter-mOriginalCenter );
799 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
800 /( offset.x*offset.x + offset.y*offset.y );
804 Constraint originalCenterConstraint = Constraint::New<Vector2>( mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter, OriginalCenterConstraint( mOriginalCenter, offset ));
805 originalCenterConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement ) );
806 originalCenterConstraint.Apply();
808 Constraint currentCenterConstraint = Constraint::New<Vector2>( mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter, CurrentCenterConstraint(mPageSize.width));
809 currentCenterConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyPanCenter ) );
810 currentCenterConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter ) );
811 currentCenterConstraint.Apply();
813 PageTurnApplyInternalConstraint( mPages[mIndex].actor, mPageSize.height );
815 float distance = offset.Length();
816 Constraint rotationConstraint = Constraint::New<Quaternion>( mPages[mIndex].actor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mPages[mIndex].isTurnBack));
817 rotationConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement ) );
818 rotationConstraint.Apply();
825 if(mConstraints) // remove the constraint is the pan position move back to far away from the spine
827 mPages[mIndex].actor.RemoveConstraints();
828 mPages[mIndex].SetOriginalCenter(mOriginalCenter );
829 mConstraints = false;
830 mPanDisplacement = 0.f;
833 mPages[mIndex].SetCurrentCenter( currentCenter );
834 mCurrentCenter = currentCenter;
835 PageTurnApplyInternalConstraint(mPages[mIndex].actor, mPageSize.height );
840 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
842 // Guard against destruction during signal emission
843 Toolkit::PageTurnView handle( GetOwner() );
845 if( mTurningPageIndex == -1 )
847 if( mAnimatingCount< MAXIMUM_TURNING_NUM && mSlidingCount < 1)
849 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
855 mPagePanFinishedSignal.Emit( handle );
859 if(mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
861 // update the pages here instead of in the TurnedOver callback function
862 // as new page is allowed to respond to the pan gesture before other pages finishing animation
863 if(mPages[mIndex].isTurnBack)
866 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
867 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
872 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
873 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
877 // set up an animation to turn the page over
878 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
879 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
880 animation.AnimateTo( Property(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement),
881 width,AlphaFunction::EASE_OUT_SINE);
882 animation.AnimateTo( Property(mPages[mIndex].actor, mPages[mIndex].propertyPanCenter),
883 Vector2(-mPageSize.width*1.1f, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
884 mAnimationPageIdPair[animation] = mTurningPageIndex;
886 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
888 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
890 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
891 animation.AnimateTo( Property( mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter ),
892 mOriginalCenter, AlphaFunction::LINEAR );
893 mAnimationPageIdPair[animation] = mTurningPageIndex;
896 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
898 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), mPages[mIndex].isTurnBack );
903 // In portrait view, an outwards flick should turn the previous page back
904 // In landscape view, nothing to do
905 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
910 void PageTurnView::TurnedOver( Animation& animation )
912 int pageId = mAnimationPageIdPair[animation];
913 int index = pageId%NUMBER_OF_CACHED_PAGES;
915 mPages[index].ChangeTurnDirection();
916 mPages[index].actor.RemoveConstraints();
917 Self().Add(mPages[index].actor);
919 mAnimationPageIdPair.erase( animation );
921 float degree = mPages[index].isTurnBack ? 180.f : 0.f;
922 mPages[index].actor.SetOrientation( Degree(degree), Vector3::YAXIS );
923 mPages[index].UseEffect( mSpineEffectShader );
925 int id = pageId + (mPages[index].isTurnBack ? -1 : 1);
926 if( id >=0 && id < mTotalPageCount )
928 mPages[id%NUMBER_OF_CACHED_PAGES].actor.SetVisible(false);
931 OnTurnedOver( mPages[index].actor, mPages[index].isTurnBack );
933 // Guard against destruction during signal emission
934 Toolkit::PageTurnView handle( GetOwner() );
935 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mPages[index].isTurnBack );
938 void PageTurnView::SliddenBack( Animation& animation )
940 int pageId = mAnimationPageIdPair[animation];
941 int index = pageId%NUMBER_OF_CACHED_PAGES;
942 Self().Add(mPages[index].actor);
945 mAnimationPageIdPair.erase( animation );
947 mPages[index].UseEffect( mSpineEffectShader );
949 int id = pageId + (mPages[index].isTurnBack ? -1 : 1);
950 if( id >=0 && id < mTotalPageCount )
952 mPages[id%NUMBER_OF_CACHED_PAGES].actor.SetVisible(false);
955 // Guard against destruction during signal emission
956 Toolkit::PageTurnView handle( GetOwner() );
957 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mPages[index].isTurnBack );
960 void PageTurnView::OrganizePageDepth()
962 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
964 if(mCurrentPageIndex+i < mTotalPageCount)
966 mPages[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].actor.SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
968 if( mCurrentPageIndex >= i + 1 )
970 mPages[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].actor.SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
975 void PageTurnView::StopTurning()
982 int index = mTurningPageIndex % NUMBER_OF_CACHED_PAGES;
983 Self().Add( mPages[ index ].actor );
984 mPages[ index ].actor.RemoveConstraints();
985 mPages[ index ].UseEffect( mSpineEffectShader );
986 float degree = mTurningPageIndex==mCurrentPageIndex ? 0.f :180.f;
987 mPages[index].actor.SetOrientation( Degree(degree), Vector3::YAXIS );
991 if( !mAnimationPageIdPair.empty() )
993 for (std::map<Animation,int>::iterator it=mAnimationPageIdPair.begin(); it!=mAnimationPageIdPair.end(); ++it)
995 static_cast<Animation>(it->first).SetCurrentProgress( 1.f );
1000 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1002 return mPageTurnStartedSignal;
1005 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1007 return mPageTurnFinishedSignal;
1010 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1012 return mPagePanStartedSignal;
1015 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1017 return mPagePanFinishedSignal;
1020 bool PageTurnView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1022 Dali::BaseHandle handle( object );
1024 bool connected( true );
1025 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( handle );
1027 if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_STARTED ) )
1029 pageTurnView.PageTurnStartedSignal().Connect( tracker, functor );
1031 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_FINISHED ) )
1033 pageTurnView.PageTurnFinishedSignal().Connect( tracker, functor );
1035 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_STARTED ) )
1037 pageTurnView.PagePanStartedSignal().Connect( tracker, functor );
1039 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_FINISHED ) )
1041 pageTurnView.PagePanFinishedSignal().Connect( tracker, functor );
1045 // signalName does not match any signal
1052 void PageTurnView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
1054 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
1058 PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
1062 case Toolkit::PageTurnView::Property::PAGE_SIZE:
1064 pageTurnViewImpl.SetPageSize( value.Get<Vector2>() );
1067 case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1069 pageTurnViewImpl.GoToPage( value.Get<int>() );
1072 case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1074 pageTurnViewImpl.SetSpineShadowParameter( value.Get<Vector2>() );
1081 Property::Value PageTurnView::GetProperty( BaseObject* object, Property::Index index )
1083 Property::Value value;
1085 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
1089 PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
1093 case Toolkit::PageTurnView::Property::PAGE_SIZE:
1095 value = pageTurnViewImpl.GetPageSize();
1098 case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1100 value = static_cast<int>( pageTurnViewImpl.GetCurrentPage() );
1103 case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1105 value = pageTurnViewImpl.GetSpineShadowParameter();
1113 } // namespace Internal
1115 } // namespace Toolkit