2 * Copyright (c) 2020 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/object/type-registry.h>
26 #include <dali/public-api/object/type-registry-helper.h>
27 #include <dali/integration-api/debug.h>
30 #include <dali-toolkit/public-api/visuals/visual-properties.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/visuals/visual-factory-cache.h>
34 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
35 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
39 namespace //Unnamed namespace
41 // properties set on shader, these properties have the constant value in regardless of the page status
42 const char * const PROPERTY_SPINE_SHADOW ( "uSpineShadowParameter" ); // uniform for both spine and turn effect
44 // properties set on actor, the value of these properties varies depending on the page status
45 // properties used in turn effect
46 const char * const PROPERTY_TURN_DIRECTION( "uIsTurningBack" ); // uniform
47 const char * const PROPERTY_COMMON_PARAMETERS( "uCommonParameters" ); //uniform
49 const char * const PROPERTY_PAN_DISPLACEMENT( "panDisplacement" );// property used to constrain the uniforms
50 const char * const PROPERTY_PAN_CENTER( "panCenter" );// property used to constrain the uniforms
52 // default grid density for page turn effect, 20 pixels by 20 pixels
53 const float DEFAULT_GRID_DENSITY(20.0f);
55 // to bent the page, the minimal horizontal pan start position is viewPageSize.x * MINIMUM_START_POSITION_RATIO
56 const float MINIMUM_START_POSITION_RATIO(0.6f);
58 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: viewPageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
59 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
61 // when the x component of pan position reaches viewPageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
62 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
64 // duration of animation, shorter for faster speed
65 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
66 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
68 // the major&minor radius (in pixels) to form an ellipse shape
69 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
70 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
72 // constants for shadow casting
73 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
74 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
76 // constraints ////////////////////////////////////////////////////////////////
78 * Original Center Constraint
80 * This constraint adjusts the original center property of the page turn shader effect
81 * based on the X-direction displacement of the pan gesture
83 struct OriginalCenterConstraint
85 OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
86 : mOldCenter( originalCenter )
88 mNewCenter = originalCenter + offset;
89 mDistance = offset.Length() * 0.5f;
90 mDirection = offset / mDistance;
93 void operator()( Vector2& current, const PropertyInputContainer& inputs )
95 float displacement = inputs[0]->GetFloat();
97 if( displacement < mDistance )
99 current = mOldCenter + mDirection * displacement;
103 current = mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
114 * Rotation Constraint
116 * This constraint adjusts the rotation property of the page actor
117 * based on the X-direction displacement of the pan gesture
119 struct RotationConstraint
121 RotationConstraint( float distance, float pageWidth, bool isTurnBack )
122 : mDistance( distance*0.5f )
124 mStep = 1.f / pageWidth;
125 mSign = isTurnBack ? -1.0f : 1.0f;
126 mConst = isTurnBack ? -1.0f : 0.f;
127 mRotation = isTurnBack ? Quaternion( Radian( -Math::PI ), Vector3::YAXIS ) : Quaternion( Radian(0.f), Vector3::YAXIS );
130 void operator()( Quaternion& current, const PropertyInputContainer& inputs )
132 float displacement = inputs[0]->GetFloat();
133 if( displacement < mDistance)
139 float coef = std::max(-1.0f, mStep*(mDistance-displacement));
140 float angle = Math::PI * ( mConst + mSign * coef );
141 current = Quaternion( Radian( angle ), Vector3::YAXIS );
149 Quaternion mRotation;
153 * Current Center Constraint
155 * This constraint adjusts the current center property of the page turn shader effect
156 * based on the pan position and the original center position
158 struct CurrentCenterConstraint
160 CurrentCenterConstraint( float pageWidth )
161 : mPageWidth( pageWidth )
163 mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
166 void operator()( Vector2& current, const PropertyInputContainer& inputs )
168 const Vector2& centerPosition = inputs[0]->GetVector2();
169 if( centerPosition.x > 0.f )
171 current.x = mThres+centerPosition.x * 0.5f;
172 current.y = centerPosition.y;
176 const Vector2& centerOrigin = inputs[1]->GetVector2();
177 Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
178 float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
179 // when coef <= 0, the page is flat, slow down the last moment of the page stretch by 10 times to avoid a small bounce
182 coef = (coef+0.225f)/10.0f;
184 current = centerOrigin - direction * coef;
192 struct ShadowBlurStrengthConstraint
194 ShadowBlurStrengthConstraint( float thres )
198 void operator()( float& blurStrength, const PropertyInputContainer& inputs )
200 float displacement = inputs[2]->GetFloat();
201 if( EqualsZero(displacement))
203 const Vector2& cur = inputs[0]->GetVector2();
204 const Vector2& ori = inputs[1]->GetVector2();
205 blurStrength = 5.f*(ori-cur).Length() / mThres;
209 blurStrength = 1.f - (displacement-2.f*mThres)/mThres;
212 blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
218 } //unnamed namespace
234 // empty handle as we cannot create PageTurnView(but type registered for page turn signal)
238 // Setup properties, signals and actions using the type-registry.
239 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::PageTurnView, Toolkit::Control, Create );
241 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "viewPageSize", VECTOR2, VIEW_PAGE_SIZE )
242 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "currentPageId", INTEGER, CURRENT_PAGE_ID )
243 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "spineShadow", VECTOR2, SPINE_SHADOW )
245 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pageTurnStarted", SIGNAL_PAGE_TURN_STARTED )
246 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pageTurnFinished", SIGNAL_PAGE_TURN_FINISHED )
247 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pagePanStarted", SIGNAL_PAGE_PAN_STARTED )
248 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "pagePanFinished", SIGNAL_PAGE_PAN_FINISHED )
250 DALI_TYPE_REGISTRATION_END()
254 // these several constants are also used in the derived classes
255 const char * const PageTurnView::PROPERTY_TEXTURE_WIDTH( "uTextureWidth" ); // uniform name
256 const char * const PageTurnView::PROPERTY_ORIGINAL_CENTER( "originalCenter" ); // property used to constrain the uniform
257 const char * const PageTurnView::PROPERTY_CURRENT_CENTER( "currentCenter" );// property used to constrain the uniform
258 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
259 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
260 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
261 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
263 PageTurnView::Page::Page()
264 : isTurnBack( false )
266 actor = Actor::New();
267 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER_LEFT );
268 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER_LEFT );
269 actor.SetProperty( Actor::Property::VISIBLE, false );
271 propertyPanDisplacement = actor.RegisterProperty( PROPERTY_PAN_DISPLACEMENT, 0.f );
272 propertyPanCenter = actor.RegisterProperty(PROPERTY_PAN_CENTER, Vector2::ZERO);
274 propertyOriginalCenter = actor.RegisterProperty(PROPERTY_ORIGINAL_CENTER, Vector2::ZERO);
275 propertyCurrentCenter = actor.RegisterProperty(PROPERTY_CURRENT_CENTER, Vector2::ZERO);
276 Matrix zeroMatrix(true);
277 actor.RegisterProperty(PROPERTY_COMMON_PARAMETERS, zeroMatrix);
278 propertyTurnDirection = actor.RegisterProperty(PROPERTY_TURN_DIRECTION, -1.f);
281 void PageTurnView::Page::SetTexture( Texture texture )
285 textureSet = TextureSet::New();
287 textureSet.SetTexture( 0u, texture );
290 void PageTurnView::Page::UseEffect(Shader newShader)
295 renderer.SetShader( shader );
299 void PageTurnView::Page::UseEffect(Shader newShader, Geometry geometry)
301 UseEffect( newShader );
305 renderer = Renderer::New( geometry, shader );
309 textureSet = TextureSet::New();
312 renderer.SetTextures( textureSet );
313 renderer.SetProperty( Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON );
314 actor.AddRenderer( renderer );
318 void PageTurnView::Page::ChangeTurnDirection()
320 isTurnBack = !isTurnBack;
321 actor.SetProperty( propertyTurnDirection, isTurnBack ? 1.f : -1.f );
324 void PageTurnView::Page::SetPanDisplacement(float value)
326 actor.SetProperty( propertyPanDisplacement, value );
329 void PageTurnView::Page::SetPanCenter( const Vector2& value )
331 actor.SetProperty( propertyPanCenter, value );
334 void PageTurnView::Page::SetOriginalCenter( const Vector2& value )
336 actor.SetProperty( propertyOriginalCenter, value );
339 void PageTurnView::Page::SetCurrentCenter( const Vector2& value )
341 actor.SetProperty( propertyCurrentCenter, value );
344 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& viewPageSize )
345 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
346 mPageFactory( &pageFactory ),
347 mPageSize( viewPageSize ),
348 mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
349 mDistanceUpCorner( 0.f ),
350 mDistanceBottomCorner( 0.f ),
351 mPanDisplacement( 0.f ),
352 mTotalPageCount( 0 ),
353 mCurrentPageIndex( 0 ),
354 mTurningPageIndex( 0 ),
357 mAnimatingCount( 0 ),
358 mConstraints( false ),
360 mPageUpdated( true ),
361 mPageTurnStartedSignal(),
362 mPageTurnFinishedSignal(),
363 mPagePanStartedSignal(),
364 mPagePanFinishedSignal()
368 PageTurnView::~PageTurnView()
372 void PageTurnView::OnInitialize()
374 // create the book spine effect for static pages
375 Property::Map spineEffectMap = CreatePageTurnBookSpineEffect();
376 mSpineEffectShader = CreateShader( spineEffectMap );
377 mSpineEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter );
378 // create the turn effect for turning pages
379 Property::Map turnEffectMap = CreatePageTurnEffect();
380 mTurnEffectShader = CreateShader( turnEffectMap );
381 mTurnEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter );
383 // create the grid geometry for pages
384 uint16_t width = static_cast<uint16_t>(mPageSize.width / DEFAULT_GRID_DENSITY + 0.5f);
385 uint16_t height = static_cast<uint16_t>(mPageSize.height / DEFAULT_GRID_DENSITY + 0.5f);
386 mGeometry = VisualFactoryCache::CreateGridGeometry( Uint16Pair( width, height ) );
388 mPages.reserve( NUMBER_OF_CACHED_PAGES );
389 for( int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
391 mPages.push_back( Page() );
392 mPages[i].actor.SetProperty( Actor::Property::SIZE, mPageSize );
393 Self().Add( mPages[i].actor );
396 // create the layer for turning pages
397 mTurningPageLayer = Layer::New();
398 mTurningPageLayer.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER_LEFT );
399 mTurningPageLayer.SetProperty( Layer::Property::BEHAVIOR, Layer::LAYER_3D );
400 mTurningPageLayer.Raise();
402 // Set control size and the parent origin of page layers
403 OnPageTurnViewInitialize();
405 Self().Add(mTurningPageLayer);
407 mTotalPageCount = static_cast<int>( mPageFactory->GetNumberOfPages() );
408 // add pages to the scene, and set depth for the stacked pages
409 for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
412 mPages[i].actor.SetProperty( Actor::Property::POSITION_Z, -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
414 mPages[0].actor.SetProperty( Actor::Property::VISIBLE,true);
416 // enable the pan gesture which is attached to the control
417 EnableGestureDetection(GestureType::Value(GestureType::PAN));
419 DevelControl::SetAccessibilityConstructor( Self(), []( Dali::Actor actor ) {
420 return std::unique_ptr< Dali::Accessibility::Accessible >(
421 new Control::Impl::AccessibleImpl( actor, Dali::Accessibility::Role::PAGE_TAB_LIST ) );
425 Shader PageTurnView::CreateShader( const Property::Map& shaderMap )
428 Property::Value* shaderValue = shaderMap.Find( Toolkit::Visual::Property::SHADER, CUSTOM_SHADER );
429 Property::Map shaderSource;
430 if( shaderValue && shaderValue->Get( shaderSource ) )
432 std::string vertexShader;
433 Property::Value* vertexShaderValue = shaderSource.Find( Toolkit::Visual::Shader::Property::VERTEX_SHADER, CUSTOM_VERTEX_SHADER );
434 if( !vertexShaderValue || !vertexShaderValue->Get( vertexShader ) )
436 DALI_LOG_ERROR("PageTurnView::CreateShader failed: vertex shader source is not available.\n");
438 std::string fragmentShader;
439 Property::Value* fragmentShaderValue = shaderSource.Find( Toolkit::Visual::Shader::Property::FRAGMENT_SHADER, CUSTOM_FRAGMENT_SHADER );
440 if( !fragmentShaderValue || !fragmentShaderValue->Get( fragmentShader ) )
442 DALI_LOG_ERROR("PageTurnView::CreateShader failed: fragment shader source is not available.\n");
444 shader = Shader::New( vertexShader, fragmentShader );
448 DALI_LOG_ERROR("PageTurnView::CreateShader failed: shader source is not available.\n");
454 void PageTurnView::SetupShadowView()
456 mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
457 Vector3 origin = mTurningPageLayer.GetCurrentProperty< Vector3 >( Actor::Property::PARENT_ORIGIN );
458 mShadowView.SetProperty( Actor::Property::PARENT_ORIGIN, origin );
459 mShadowView.SetProperty( Actor::Property::ANCHOR_POINT, origin );
460 mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
461 mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
463 mShadowPlaneBackground = Actor::New();
464 mShadowPlaneBackground.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
465 mShadowPlaneBackground.SetProperty( Actor::Property::SIZE, mControlSize );
466 Self().Add( mShadowPlaneBackground );
467 mShadowView.SetShadowPlaneBackground( mShadowPlaneBackground );
469 mPointLight = Actor::New();
470 mPointLight.SetProperty( Actor::Property::ANCHOR_POINT, origin );
471 mPointLight.SetProperty( Actor::Property::PARENT_ORIGIN, origin );
472 mPointLight.SetProperty( Actor::Property::POSITION, Vector3( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO ));
473 Self().Add( mPointLight );
474 mShadowView.SetPointLight( mPointLight );
476 mTurningPageLayer.Add( mShadowView );
477 mShadowView.Activate();
480 void PageTurnView::OnSceneConnection( int depth )
484 Control::OnSceneConnection( depth );
487 void PageTurnView::OnSceneDisconnection()
491 mShadowView.RemoveConstraints();
492 mPointLight.Unparent();
493 mShadowPlaneBackground.Unparent();
494 mShadowView.Unparent();
497 // make sure the status of the control is updated correctly when the pan gesture is interrupted
500 Control::OnSceneDisconnection();
503 void PageTurnView::SetPageSize( const Vector2& viewPageSize )
505 mPageSize = viewPageSize;
509 mPointLight.SetProperty( Actor::Property::POSITION, Vector3( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO ));
512 for( size_t i=0; i<mPages.size(); i++ )
514 mPages[i].actor.SetProperty( Actor::Property::SIZE, mPageSize );
517 OnPageTurnViewInitialize();
519 if( mShadowPlaneBackground )
521 mShadowPlaneBackground.SetProperty( Actor::Property::SIZE, mControlSize );
525 Vector2 PageTurnView::GetPageSize()
530 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
532 mSpineShadowParameter = spineShadowParameter;
534 // set spine shadow parameter to all the shader effects
535 mSpineEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter );
536 mTurnEffectShader.RegisterProperty(PROPERTY_SPINE_SHADOW, mSpineShadowParameter );
539 Vector2 PageTurnView::GetSpineShadowParameter()
541 return mSpineShadowParameter;
544 void PageTurnView::GoToPage( unsigned int pageId )
546 int pageIdx = Clamp( static_cast<int>(pageId), 0, mTotalPageCount-1);
548 if( mCurrentPageIndex == pageIdx )
553 // if any animation ongoing, stop it.
556 // record the new current page index
557 mCurrentPageIndex = pageIdx;
560 // add the current page and the pages right before and after it
561 for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
566 mPages[pageId%NUMBER_OF_CACHED_PAGES].actor.SetProperty( Actor::Property::VISIBLE,true);
569 mPages[(pageId-1)%NUMBER_OF_CACHED_PAGES].actor.SetProperty( Actor::Property::VISIBLE,true);
571 // set ordered depth to the stacked pages
575 unsigned int PageTurnView::GetCurrentPage()
577 DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
578 return static_cast< unsigned int >( mCurrentPageIndex );
581 void PageTurnView::AddPage( int pageIndex )
583 if(pageIndex > -1 && pageIndex < mTotalPageCount) // whether the page is available from the page factory
585 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
588 newPage = mPageFactory->NewPage( pageIndex );
589 DALI_ASSERT_ALWAYS( newPage && "must pass in valid texture" );
591 bool isLeftSide = ( pageIndex < mCurrentPageIndex );
592 if( mPages[index].isTurnBack != isLeftSide )
594 mPages[index].ChangeTurnDirection();
597 float degree = isLeftSide ? 180.f :0.f;
598 mPages[index].actor.SetProperty( Actor::Property::ORIENTATION, Quaternion( Degree( degree ), Vector3::YAXIS ) );
599 mPages[index].actor.SetProperty( Actor::Property::VISIBLE, false );
600 mPages[index].UseEffect( mSpineEffectShader, mGeometry );
601 mPages[index].SetTexture( newPage );
603 // For Portrait, nothing to do
604 // For Landscape, set the parent origin to CENTER
605 OnAddPage( mPages[index].actor, isLeftSide );
609 void PageTurnView::RemovePage( int pageIndex )
611 if( pageIndex > -1 && pageIndex < mTotalPageCount)
613 int index = pageIndex % NUMBER_OF_CACHED_PAGES;
614 mPages[index].actor.SetProperty( Actor::Property::VISIBLE,false);
618 void PageTurnView::OnPan( const PanGesture& gesture )
620 // the pan gesture is attached to control itself instead of each page
621 switch( gesture.GetState() )
623 case GestureState::STARTED:
625 // check whether the undergoing turning page number already reaches the maximum allowed
626 if( mPageUpdated && mAnimatingCount< MAXIMUM_TURNING_NUM && mSlidingCount < 1 )
628 const Vector2& position = gesture.GetPosition();
629 SetPanActor( position ); // determine which page actor is panned
630 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
632 mTurningPageIndex = -1;
634 PanStarted( SetPanPosition( position ) ); // pass in the pan position in the local page coordinate
638 mTurningPageIndex = -1;
642 case GestureState::CONTINUING:
644 PanContinuing( SetPanPosition( gesture.GetPosition() ) ); // pass in the pan position in the local page coordinate
647 case GestureState::FINISHED:
648 case GestureState::CANCELLED:
650 PanFinished( SetPanPosition( gesture.GetPosition() ), gesture.GetSpeed() );
653 case GestureState::CLEAR:
654 case GestureState::POSSIBLE:
662 void PageTurnView::PanStarted( const Vector2& gesturePosition )
664 mPressDownPosition = gesturePosition;
666 if( mTurningPageIndex == -1 )
671 mIndex = mTurningPageIndex % NUMBER_OF_CACHED_PAGES;
673 mOriginalCenter = gesturePosition;
675 mPageUpdated = false;
677 // Guard against destruction during signal emission
678 Toolkit::PageTurnView handle( GetOwner() );
679 mPagePanStartedSignal.Emit( handle );
682 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
684 if( mTurningPageIndex == -1 )
689 // Guard against destruction during signal emission
690 Toolkit::PageTurnView handle( GetOwner() );
694 // when the touch down position is near the spine
695 // or when the panning goes outwards or some other position which would tear the paper in real situation
696 // we change the start position into the current panning position and update the shader parameters
697 if( mOriginalCenter.x < mPageSize.width*MINIMUM_START_POSITION_RATIO
698 || gesturePosition.x > mOriginalCenter.x-1.0f
699 || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
700 ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
702 mOriginalCenter = gesturePosition;
706 mDistanceUpCorner = mOriginalCenter.Length();
707 mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
708 mShadowView.Add( mPages[mIndex].actor );
709 mPages[mIndex].UseEffect( mTurnEffectShader );
710 mPages[mIndex].SetOriginalCenter( mOriginalCenter );
711 mCurrentCenter = mOriginalCenter;
712 mPages[mIndex].SetCurrentCenter( mCurrentCenter );
713 mPanDisplacement = 0.f;
714 mConstraints = false;
718 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), !mPages[mIndex].isTurnBack );
719 int id = mTurningPageIndex + (mPages[mIndex].isTurnBack ? -1 : 1);
720 if( id >=0 && id < mTotalPageCount )
722 mPages[id%NUMBER_OF_CACHED_PAGES].actor.SetProperty( Actor::Property::VISIBLE,true);
725 mShadowView.RemoveConstraints();
727 mPages[mIndex].SetPanDisplacement( 0.f );
729 Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
730 shadowBlurStrengthConstraint.AddSource( Source(mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter) );
731 shadowBlurStrengthConstraint.AddSource( Source(mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter) );
732 shadowBlurStrengthConstraint.AddSource( Source(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement) );
733 shadowBlurStrengthConstraint.Apply();
738 Vector2 currentCenter = gesturePosition;
740 // Test whether the new current center would tear the paper from the top pine in real situation
741 // we do not forbid this totally, which would restrict the panning gesture too much
742 // instead, set it to the nearest allowable position
743 float distanceUpCorner = currentCenter.Length();
744 float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
745 if( distanceUpCorner > mDistanceUpCorner )
747 currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
749 // would tear the paper from the bottom spine in real situation
750 if( distanceBottomCorner > mDistanceBottomCorner )
752 currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
754 // If direction has a very high y component, reduce it.
755 Vector2 curveDirection = currentCenter - mOriginalCenter;
756 if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
758 currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
760 // If the vertical distance is high, reduce it
761 float yShift = currentCenter.y - mOriginalCenter.y;
762 if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
764 currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
767 // use contraints to control the page shape and rotation when the pan position is near the spine
768 if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
770 // set the property values used by the constraints
771 mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
772 mPages[mIndex].SetPanDisplacement( mPanDisplacement );
773 mPages[mIndex].SetPanCenter( currentCenter );
775 // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
776 // also set up the RotationConstraint to the page actor
780 // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
781 if( currentCenter.y >= mOriginalCenter.y )
783 corner = Vector2( 1.1f*mPageSize.width, 0.f );
787 corner = mPageSize*1.1f;
790 Vector2 offset( currentCenter-mOriginalCenter );
791 float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
792 /( offset.x*offset.x + offset.y*offset.y );
796 Constraint originalCenterConstraint = Constraint::New<Vector2>( mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter, OriginalCenterConstraint( mOriginalCenter, offset ));
797 originalCenterConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement ) );
798 originalCenterConstraint.Apply();
800 Constraint currentCenterConstraint = Constraint::New<Vector2>( mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter, CurrentCenterConstraint(mPageSize.width));
801 currentCenterConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyPanCenter ) );
802 currentCenterConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyOriginalCenter ) );
803 currentCenterConstraint.Apply();
805 PageTurnApplyInternalConstraint( mPages[mIndex].actor, mPageSize.height );
807 float distance = offset.Length();
808 Constraint rotationConstraint = Constraint::New<Quaternion>( mPages[mIndex].actor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mPages[mIndex].isTurnBack));
809 rotationConstraint.AddSource( Source( mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement ) );
810 rotationConstraint.Apply();
817 if(mConstraints) // remove the constraint is the pan position move back to far away from the spine
819 mPages[mIndex].actor.RemoveConstraints();
820 mPages[mIndex].SetOriginalCenter(mOriginalCenter );
821 mConstraints = false;
822 mPanDisplacement = 0.f;
825 mPages[mIndex].SetCurrentCenter( currentCenter );
826 mCurrentCenter = currentCenter;
827 PageTurnApplyInternalConstraint(mPages[mIndex].actor, mPageSize.height );
832 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
834 // Guard against destruction during signal emission
835 Toolkit::PageTurnView handle( GetOwner() );
837 if( mTurningPageIndex == -1 )
839 if( mAnimatingCount< MAXIMUM_TURNING_NUM && mSlidingCount < 1)
841 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
847 mPagePanFinishedSignal.Emit( handle );
851 if(mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
853 // update the pages here instead of in the TurnedOver callback function
854 // as new page is allowed to respond to the pan gesture before other pages finishing animation
855 if(mPages[mIndex].isTurnBack)
858 RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
859 AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
864 RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
865 AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
869 // set up an animation to turn the page over
870 float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
871 Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
872 animation.AnimateTo( Property(mPages[mIndex].actor, mPages[mIndex].propertyPanDisplacement),
873 width,AlphaFunction::EASE_OUT_SINE);
874 animation.AnimateTo( Property(mPages[mIndex].actor, mPages[mIndex].propertyPanCenter),
875 Vector2(-mPageSize.width*1.1f, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
876 mAnimationPageIdPair[animation] = mTurningPageIndex;
878 animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
880 else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
882 Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
883 animation.AnimateTo( Property( mPages[mIndex].actor, mPages[mIndex].propertyCurrentCenter ),
884 mOriginalCenter, AlphaFunction::LINEAR );
885 mAnimationPageIdPair[animation] = mTurningPageIndex;
888 animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
890 mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), mPages[mIndex].isTurnBack );
895 // In portrait view, an outwards flick should turn the previous page back
896 // In landscape view, nothing to do
897 OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
902 void PageTurnView::TurnedOver( Animation& animation )
904 int pageId = mAnimationPageIdPair[animation];
905 int index = pageId%NUMBER_OF_CACHED_PAGES;
907 mPages[index].ChangeTurnDirection();
908 mPages[index].actor.RemoveConstraints();
909 Self().Add(mPages[index].actor);
911 mAnimationPageIdPair.erase( animation );
913 float degree = mPages[index].isTurnBack ? 180.f : 0.f;
914 mPages[index].actor.SetProperty( Actor::Property::ORIENTATION, Quaternion( Degree(degree), Vector3::YAXIS ) );
915 mPages[index].UseEffect( mSpineEffectShader );
917 int id = pageId + (mPages[index].isTurnBack ? -1 : 1);
918 if( id >=0 && id < mTotalPageCount )
920 mPages[id%NUMBER_OF_CACHED_PAGES].actor.SetProperty( Actor::Property::VISIBLE,false);
923 OnTurnedOver( mPages[index].actor, mPages[index].isTurnBack );
925 // Guard against destruction during signal emission
926 Toolkit::PageTurnView handle( GetOwner() );
927 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mPages[index].isTurnBack );
930 void PageTurnView::SliddenBack( Animation& animation )
932 int pageId = mAnimationPageIdPair[animation];
933 int index = pageId%NUMBER_OF_CACHED_PAGES;
934 Self().Add(mPages[index].actor);
937 mAnimationPageIdPair.erase( animation );
939 mPages[index].UseEffect( mSpineEffectShader );
941 int id = pageId + (mPages[index].isTurnBack ? -1 : 1);
942 if( id >=0 && id < mTotalPageCount )
944 mPages[id%NUMBER_OF_CACHED_PAGES].actor.SetProperty( Actor::Property::VISIBLE,false);
947 // Guard against destruction during signal emission
948 Toolkit::PageTurnView handle( GetOwner() );
949 mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mPages[index].isTurnBack );
952 void PageTurnView::OrganizePageDepth()
954 for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
956 if(mCurrentPageIndex+i < mTotalPageCount)
958 mPages[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].actor.SetProperty( Actor::Property::POSITION_Z, -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
960 if( mCurrentPageIndex >= i + 1 )
962 mPages[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].actor.SetProperty( Actor::Property::POSITION_Z, -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
967 void PageTurnView::StopTurning()
974 int index = mTurningPageIndex % NUMBER_OF_CACHED_PAGES;
975 Self().Add( mPages[ index ].actor );
976 mPages[ index ].actor.RemoveConstraints();
977 mPages[ index ].UseEffect( mSpineEffectShader );
978 float degree = mTurningPageIndex==mCurrentPageIndex ? 0.f :180.f;
979 mPages[index].actor.SetProperty( Actor::Property::ORIENTATION, Quaternion( Degree(degree), Vector3::YAXIS ) );
983 if( !mAnimationPageIdPair.empty() )
985 for (std::map<Animation,int>::iterator it=mAnimationPageIdPair.begin(); it!=mAnimationPageIdPair.end(); ++it)
987 static_cast<Animation>(it->first).SetCurrentProgress( 1.f );
992 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
994 return mPageTurnStartedSignal;
997 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
999 return mPageTurnFinishedSignal;
1002 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1004 return mPagePanStartedSignal;
1007 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1009 return mPagePanFinishedSignal;
1012 bool PageTurnView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1014 Dali::BaseHandle handle( object );
1016 bool connected( true );
1017 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( handle );
1019 if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_STARTED ) )
1021 pageTurnView.PageTurnStartedSignal().Connect( tracker, functor );
1023 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_FINISHED ) )
1025 pageTurnView.PageTurnFinishedSignal().Connect( tracker, functor );
1027 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_STARTED ) )
1029 pageTurnView.PagePanStartedSignal().Connect( tracker, functor );
1031 else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_FINISHED ) )
1033 pageTurnView.PagePanFinishedSignal().Connect( tracker, functor );
1037 // signalName does not match any signal
1044 void PageTurnView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
1046 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
1050 PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
1054 case Toolkit::PageTurnView::Property::VIEW_PAGE_SIZE:
1056 pageTurnViewImpl.SetPageSize( value.Get<Vector2>() );
1059 case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1061 pageTurnViewImpl.GoToPage( value.Get<int>() );
1064 case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1066 pageTurnViewImpl.SetSpineShadowParameter( value.Get<Vector2>() );
1073 Property::Value PageTurnView::GetProperty( BaseObject* object, Property::Index index )
1075 Property::Value value;
1077 Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
1081 PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
1085 case Toolkit::PageTurnView::Property::VIEW_PAGE_SIZE:
1087 value = pageTurnViewImpl.GetPageSize();
1090 case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1092 value = static_cast<int>( pageTurnViewImpl.GetCurrentPage() );
1095 case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1097 value = pageTurnViewImpl.GetSpineShadowParameter();
1105 } // namespace Internal
1107 } // namespace Toolkit