Update keyboard focus direction enum for Control
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / page-turn-view / page-turn-view-impl.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/animation/animation.h>
23 #include <dali/public-api/animation/constraint.h>
24 #include <dali/devel-api/events/hit-test-algorithm.h>
25 #include <dali/public-api/object/type-registry.h>
26 #include <dali/devel-api/object/type-registry-helper.h>
27 #include <dali/public-api/render-tasks/render-task-list.h>
28
29 // INTERNAL INCLUDES
30 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
31
32 using namespace Dali;
33
34 namespace //Unnamed namespace
35 {
36 // To register type
37
38 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::PageTurnView, Toolkit::Control, NULL )
39 DALI_TYPE_REGISTRATION_END()
40
41 // default grid density for page turn effect, 10 pixels by 10 pixels
42 const float DEFAULT_GRID_DENSITY(10.0f);
43
44 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
45 const float MINIMUM_START_POSITION_RATIO(0.6f);
46
47 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
48 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
49
50 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
51 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
52
53 // duration of animation, shorter for faster speed
54 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
55 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
56
57 // the major&minor radius (in pixels) to form an ellipse shape
58 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
59 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
60
61 // constants for shadow casting
62 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
63 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
64
65 // constraints ////////////////////////////////////////////////////////////////
66 /**
67  * Original Center Constraint
68  *
69  * This constraint adjusts the original center property of the page turn shader effect
70  * based on the X-direction displacement of the pan gesture
71  */
72 struct OriginalCenterConstraint
73 {
74   OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
75   : mOldCenter( originalCenter )
76   {
77     mNewCenter = originalCenter + offset;
78     mDistance = offset.Length() * 0.5f;
79     mDirection = offset  / mDistance;
80   }
81
82   void operator()( Vector2& current, const PropertyInputContainer& inputs )
83   {
84     float displacement = inputs[0]->GetFloat();
85
86     if( displacement < mDistance )
87     {
88       current = mOldCenter + mDirection * displacement;
89     }
90     else
91     {
92       current = mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
93     }
94   }
95
96   Vector2 mOldCenter;
97   Vector2 mNewCenter;
98   float mDistance;
99   Vector2 mDirection;
100 };
101
102 /**
103  * Rotation Constraint
104  *
105  * This constraint adjusts the rotation property of the page actor
106  * based on the X-direction displacement of the pan gesture
107  */
108 struct RotationConstraint
109 {
110   RotationConstraint( float distance, float pageWidth, bool isTurnBack )
111   : mDistance( distance*0.5f )
112   {
113     mStep = 1.f / pageWidth;
114     mSign = isTurnBack ? -1.0f : 1.0f;
115     mConst = isTurnBack ? -1.0f : 0.f;
116     mRotation = isTurnBack ? Quaternion( Radian( -Math::PI ), Vector3::YAXIS ) : Quaternion( Radian(0.f), Vector3::YAXIS );
117   }
118
119   void operator()( Quaternion& current, const PropertyInputContainer& inputs )
120   {
121     float displacement = inputs[0]->GetFloat();
122     if( displacement < mDistance)
123     {
124       current = mRotation;
125     }
126     else
127     {
128       float coef = std::max(-1.0f, mStep*(mDistance-displacement));
129       float angle = Math::PI * ( mConst + mSign * coef );
130       current = Quaternion( Radian( angle ), Vector3::YAXIS );
131     }
132   }
133
134   float mDistance;
135   float mStep;
136   float mConst;
137   float mSign;
138   Quaternion mRotation;
139 };
140
141 /**
142  * Current Center Constraint
143  *
144  * This constraint adjusts the current center property of the page turn shader effect
145  * based on the pan position and the original center position
146  */
147 struct CurrentCenterConstraint
148 {
149   CurrentCenterConstraint( float pageWidth )
150   : mPageWidth( pageWidth )
151   {
152     mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
153   }
154
155   void operator()( Vector2& current, const PropertyInputContainer& inputs )
156   {
157     const Vector2& centerPosition = inputs[0]->GetVector2();
158     if( centerPosition.x > 0.f )
159     {
160       current.x = mThres+centerPosition.x * 0.5f;
161       current.y = centerPosition.y;
162     }
163     else
164     {
165       const Vector2& centerOrigin = inputs[1]->GetVector2();
166       Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
167       float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
168       // Todo: when coef <= 0, the page is flat, slow down the last moment of the page stretch by 10 times to avoid a small bounce
169       if(coef < 0.025f)
170       {
171         coef = (coef+0.225f)/10.0f;
172       }
173       current = centerOrigin - direction * coef;
174     }
175   }
176
177   float mPageWidth;
178   float mThres;
179 };
180
181 struct ShadowBlurStrengthConstraint
182 {
183   ShadowBlurStrengthConstraint( float thres )
184   : mThres( thres )
185   {}
186
187   void operator()( float& blurStrength,  const PropertyInputContainer& inputs )
188   {
189     float displacement = inputs[2]->GetFloat();
190     if( EqualsZero(displacement))
191     {
192       const Vector2& cur = inputs[0]->GetVector2();
193       const Vector2& ori = inputs[1]->GetVector2();
194       blurStrength =  5.f*(ori-cur).Length() / mThres;
195     }
196     else
197     {
198       blurStrength =  1.f - (displacement-2.f*mThres)/mThres;
199     }
200
201     blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
202   }
203
204   float mThres;
205 };
206
207 bool IsActorHittableFunction( Actor actor, Dali::HitTestAlgorithm::TraverseType type )
208 {
209   bool hittable = false;
210
211   switch (type)
212   {
213     case Dali::HitTestAlgorithm::CHECK_ACTOR:
214     {
215       // Check whether the actor is visible and not fully transparent.
216       Property::Index propertyActorHittable = actor.GetPropertyIndex(Toolkit::PageFactory::ACTOR_HITTABLE);
217       if( actor.IsSensitive()
218        && actor.IsVisible()
219        && actor.GetCurrentWorldColor().a > 0.01f// not FULLY_TRANSPARENT
220        && ( propertyActorHittable != Property::INVALID_INDEX &&
221             actor.GetProperty<bool>( propertyActorHittable ) ) )
222       {
223          hittable = true;
224       }
225       break;
226     }
227     case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
228     {
229       if( actor.IsSensitive() && actor.IsVisible() ) // Actor is visible, if not visible then none of its children are visible.
230       {
231         hittable = true;
232       }
233       break;
234     }
235     default:
236     {
237       break;
238     }
239   }
240
241   return hittable;
242 }
243
244 } //unnamed namespace
245
246 namespace Dali
247 {
248
249 namespace Toolkit
250 {
251
252 namespace Internal
253 {
254
255 // these several constants are also used in the derived classes
256 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
257 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
258 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
259 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
260
261 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
262 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS ) ),
263   mPageFactory( pageFactory ),
264   mPageSize( pageSize ),
265   mTotalPageCount( 0 ),
266   mIsEditMode( false ),
267   mNeedOffscreenRendering( false ),
268   mPanning( false ),
269   mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
270   mCurrentPageIndex( 0 ),
271   mIndex( 0 ),
272   mPress( false ),
273   mPageUpdated( true ),
274   mDistanceUpCorner( 0.f ),
275   mDistanceBottomCorner( 0.f ),
276   mPanDisplacement( 0.f ),
277   mConstraints( false ),
278   mPageTurnStartedSignal(),
279   mPageTurnFinishedSignal(),
280   mPagePanStartedSignal(),
281   mPagePanFinishedSignal()
282 {
283   mPageActors.resize( NUMBER_OF_CACHED_PAGES );
284   mIsAnimating.resize( MAXIMUM_TURNING_NUM );
285   mIsSliding.resize( MAXIMUM_TURNING_NUM );
286   mTurnEffect.resize( MAXIMUM_TURNING_NUM );
287   mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
288   mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
289 }
290
291 PageTurnView::~PageTurnView()
292 {
293 }
294
295 void PageTurnView::OnInitialize()
296 {
297    // create the two book spine effect for static images, left and right side pages respectively
298   mSpineEffectFront = PageTurnBookSpineEffect::New();
299   mSpineEffectFront.SetIsBackImageVisible( false );
300   mSpineEffectFront.SetPageWidth( mPageSize.width );
301   mSpineEffectFront.SetShadowWidth( 0.f );
302   mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
303
304   mSpineEffectBack = PageTurnBookSpineEffect::New();
305   mSpineEffectBack.SetIsBackImageVisible( true );
306   mSpineEffectBack.SetPageWidth( mPageSize.width );
307   mSpineEffectBack.SetShadowWidth( 0.f );
308   mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
309
310   // create the page turn effect objects
311   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
312   {
313     mTurnEffect[i] = Toolkit::PageTurnEffect::New( false );
314     mTurnEffect[i].SetProperty( ShaderEffect::Property::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
315     mTurnEffect[i].SetPageSize( mPageSize );
316     mTurnEffect[i].SetShadowWidth(0.f);
317     mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
318     mIsAnimating[i] = false;
319     mIsSliding[i] = false;
320     mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
321     mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
322   }
323
324   mTurningPageLayer = Layer::New();
325   mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
326   // Set control size and the parent origin of turningPageLayer
327   OnPageTurnViewInitialize();
328
329   mRootOnScreen = Actor::New();
330   mRootOnScreen.SetPositionInheritanceMode( USE_PARENT_POSITION );
331   mRootOnScreen.SetSize( mControlSize );
332   Self().Add( mRootOnScreen );
333   mRootOnScreen.Add(mTurningPageLayer);
334
335   mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
336   mNeedOffscreenRendering = mPageFactory.IsOffscreenRenderingNeeded();
337   if( mNeedOffscreenRendering )
338   {
339     SetupRenderTasks();
340   }
341
342   // add pages to the scene, and set depth for the stacked pages
343   for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
344   {
345     AddPage( i );
346     if(mPageActors[i])
347     {
348       mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
349     }
350   }
351
352   // enable the pan gesture which is attached to the control
353   EnableGestureDetection(Gesture::Type(Gesture::Pan));
354
355   mPageFactory.PageRefreshSignal().Connect(this, &PageTurnView::RenderPage);
356 }
357
358 void PageTurnView::SetupRenderTasks()
359 {
360   mPageSourceActor.resize( NUMBER_OF_CACHED_PAGES );
361   mOffscreenTask.resize( NUMBER_OF_CACHED_PAGES );
362   mRenderedPage.resize( NUMBER_OF_CACHED_PAGES );
363
364   mCameraActor = CameraActor::New(mControlSize);
365   mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
366   mCameraActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
367   mCameraActor.SetInheritScale( false );
368   Self().Add(mCameraActor);
369
370   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
371   for(int i=0; i<NUMBER_OF_CACHED_PAGES; i++)
372   {
373     mPageSourceActor[i] = Actor::New();
374     mPageSourceActor[i].SetParentOrigin(ParentOrigin::CENTER);
375     mPageSourceActor[i].SetColorMode( USE_OWN_COLOR );
376     mPageSourceActor[i].SetPositionInheritanceMode( DONT_INHERIT_POSITION );
377     mPageSourceActor[i].SetInheritScale( false );
378     Self().Add( mPageSourceActor[i] );
379     mPageSourceActor[i].SetSensitive( false );
380
381     mRenderedPage[i] = FrameBufferImage::New( mControlSize.width, mControlSize.height, Pixel::RGB8888, Image::UNUSED );
382     mOffscreenTask[i] = taskList.CreateTask();
383     mOffscreenTask[i].SetRefreshRate( RenderTask::REFRESH_ONCE );
384     mOffscreenTask[i].SetCameraActor(mCameraActor);
385     mOffscreenTask[i].SetSourceActor( mPageSourceActor[i] );
386     mOffscreenTask[i].SetExclusive(true);
387     mOffscreenTask[i].SetInputEnabled( false );
388     mOffscreenTask[i].SetClearEnabled( true );
389     mOffscreenTask[i].SetClearColor( Vector4(0.f,0.f,0.f,0.f) );
390     mOffscreenTask[i].SetTargetFrameBuffer( mRenderedPage[i] );
391     mOffscreenTask[i].SetScreenToFrameBufferMappingActor( Self() );
392    }
393 }
394
395 void PageTurnView::SetupShadowView()
396 {
397   mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
398   Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
399   mShadowView.SetParentOrigin( origin );
400   mShadowView.SetAnchorPoint( origin );
401   mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
402   mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
403
404   mShadowLayer = Layer::New();
405   mShadowLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
406   mRootOnScreen.Add(mShadowLayer);
407   mShadowLayer.Raise();
408
409   mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
410   mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
411   mShadowPlane.SetSize( mControlSize );
412   mShadowLayer.Add( mShadowPlane );
413   mShadowView.SetShadowPlane( mShadowPlane );
414
415   mPointLight = Actor::New();
416   mPointLight.SetAnchorPoint( origin );
417   mPointLight.SetParentOrigin( origin );
418   mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
419   mRootOnScreen.Add( mPointLight );
420   mShadowView.SetPointLight( mPointLight );
421
422   mTurningPageLayer.Add( mShadowView );
423   mShadowView.Activate();
424 }
425
426 void PageTurnView::OnStageConnection()
427 {
428   SetupShadowView();
429   mTurningPageLayer.RaiseToTop();
430 }
431
432 void PageTurnView::OnStageDisconnection()
433 {
434   if(mShadowView)
435   {
436     Self().Remove(mPointLight);
437     Self().Remove(mShadowLayer);
438     mTurningPageLayer.Remove( mShadowView );
439   }
440
441   // make sure the status of the control is updated correctly when the pan gesture is interrupted
442   if(mPanning)
443   {
444     mPanning = false;
445
446     mRootOnScreen.Add(mPanActor);
447     mIsAnimating[mIndex] = false;
448     mPanActor.RemoveConstraints();
449     mTurnEffect[mIndex].RemoveConstraints();
450     mPageUpdated = true;
451
452     SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
453   }
454 }
455
456 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
457 {
458   mSpineShadowParameter = spineShadowParameter;
459
460   // set spine shadow parameter to all the shader effects
461   mSpineEffectFront.SetSpineShadowParameter( mSpineShadowParameter );
462   mSpineEffectBack.SetSpineShadowParameter( mSpineShadowParameter );
463   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
464   {
465     mTurnEffect[i].SetSpineShadowParameter( mSpineShadowParameter );
466   }
467 }
468
469 Vector2 PageTurnView::GetSpineShadowParameter()
470 {
471   return mSpineShadowParameter;
472 }
473
474 void PageTurnView::GoToPage( unsigned int pageId )
475 {
476   int pageIdx = static_cast<int>(pageId);
477   // record the new current page index
478   mCurrentPageIndex = pageIdx;
479
480   // clear the old pages
481   for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
482   {
483     if( mPageActors[i] )
484     {
485       mPageActors[i].Unparent();
486       mPageActors[i].Reset();
487     }
488   }
489
490   // add the current page and the pages right before and after it
491   for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
492   {
493     AddPage( i );
494   }
495   // set ordered depth to the stacked pages
496   OrganizePageDepth();
497 }
498
499 unsigned int PageTurnView::GetCurrentPage()
500 {
501   DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
502   return static_cast< unsigned int >( mCurrentPageIndex );
503 }
504
505 Actor PageTurnView::EnterEditMode()
506 {
507   if( mNeedOffscreenRendering )
508   {
509     DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
510
511     mIsEditMode = true;
512
513     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
514     mOffscreenTask[index].SetInputEnabled( true );
515     mPageSourceActor[index].SetSensitive( true );
516     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ALWAYS );
517
518     mRootOnScreen.SetSensitive(false);
519
520     return mPageSourceActor[index].GetChildAt( 0 );
521   }
522   else
523   {
524     return Actor();
525   }
526 }
527
528 void PageTurnView::LeaveEditMode()
529 {
530   if( mNeedOffscreenRendering )
531   {
532     DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
533
534     mIsEditMode = false;
535
536     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
537     mOffscreenTask[index].SetInputEnabled( false );
538     mPageSourceActor[index].SetSensitive( false );
539     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
540
541     mRootOnScreen.SetSensitive(true);
542   }
543 }
544
545 Actor PageTurnView::GetHitActor( Vector2& screenCoordinates, Vector2& actorCoordinates )
546 {
547   if( mNeedOffscreenRendering && mCurrentPageIndex < mTotalPageCount)
548   {
549     int index = mCurrentPageIndex % NUMBER_OF_CACHED_PAGES;
550
551     Dali::HitTestAlgorithm::Results results;
552     if( !mOffscreenTask[index].GetInputEnabled() )
553     {
554       mOffscreenTask[index].SetInputEnabled( true );
555       mPageSourceActor[index].SetSensitive( true );
556       Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
557       mOffscreenTask[index].SetInputEnabled( false );
558       mPageSourceActor[index].SetSensitive( false );
559     }
560     else
561     {
562       Dali::HitTestAlgorithm::HitTest( mOffscreenTask[index], screenCoordinates, results, IsActorHittableFunction );
563     }
564     actorCoordinates = results.actorCoordinates;
565     return results.actor;
566   }
567   else
568   {
569     return Actor();
570   }
571 }
572
573 void PageTurnView::AddPage( int pageIndex )
574 {
575   if(pageIndex > -1  && pageIndex < mTotalPageCount) // whether the page is available from the page factory
576   {
577     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
578     ImageActor newPage;
579     if( mNeedOffscreenRendering )
580     {
581       Actor source = mPageFactory.NewPage( pageIndex );
582       if( mPageSourceActor[index].GetChildCount() > 0 )
583       {
584         mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
585       }
586       mPageSourceActor[index].Add( source );
587       mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
588       newPage = NewPageFromRenderBuffer( pageIndex );
589     }
590     else
591     {
592       newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
593       DALI_ASSERT_ALWAYS( newPage );
594     }
595     newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
596     newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
597     newPage.SetSize( mPageSize );
598     mRootOnScreen.Add( newPage );
599     mPageActors[index] = newPage;
600
601     bool isLeftSide = ( pageIndex < mCurrentPageIndex );
602     mIsTurnBack[ newPage ] = isLeftSide;
603     if( isLeftSide )
604     {
605       // new page is added to the left side, so need to rotate it 180 degrees
606       newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
607     }
608     else
609     {
610       SetShaderEffect( newPage, mSpineEffectFront);
611     }
612
613     // For Portrait, nothing to do
614     // For Landscape, set spineEffectBack to the new effect if it is in the left side, and set properties to the back image actor if it exists
615     OnAddPage( newPage, isLeftSide );
616   }
617 }
618
619 void PageTurnView::RemovePage( int pageIndex )
620 {
621   if( pageIndex > -1 && pageIndex < mTotalPageCount)
622   {
623     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
624     mPageActors[index].Unparent();
625     mIsTurnBack.erase( mPageActors[index] );
626     mPageActors[index].Reset();
627     if( mNeedOffscreenRendering )
628     {
629       mPageSourceActor[index].Remove( mPageSourceActor[index].GetChildAt( 0 ) );
630     }
631   }
632 }
633
634 void PageTurnView::RenderPage( int pageIndex )
635 {
636   if( pageIndex > std::max(-1, mCurrentPageIndex - NUMBER_OF_CACHED_PAGES_EACH_SIDE -1)
637    && pageIndex < std::min(mTotalPageCount, mCurrentPageIndex + NUMBER_OF_CACHED_PAGES_EACH_SIDE))
638   {
639     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
640     mOffscreenTask[index].SetRefreshRate( RenderTask::REFRESH_ONCE );
641   }
642 }
643
644 void PageTurnView::RefreshAll()
645 {
646   mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
647   if( mTotalPageCount > 0 )
648   {
649     if(mCurrentPageIndex < mTotalPageCount)
650     {
651       GoToPage( mCurrentPageIndex );
652     }
653     else
654     {
655       GoToPage( mCurrentPageIndex-- );
656     }
657   }
658 }
659
660 void PageTurnView::RefreshCurrentPage()
661 {
662   RenderPage( mCurrentPageIndex );
663 }
664
665 void PageTurnView::OnPan( const PanGesture& gesture )
666 {
667   if( mIsEditMode )
668   {
669     // when interrupted by the call of DisplayCurrentPageSourceActor(),
670     // make sure the panFinished is always called before stopping to responding the gesture
671     // so the status of the control is updated correctly
672     if(mPanning)
673     {
674       mPanning = false;
675       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
676     }
677
678     return;
679   }
680   // the pan gesture is attached to control itself instead of each page
681   switch( gesture.state )
682   {
683     case Gesture::Started:
684     {
685       mPanning = true;
686       // to find out whether the undergoing turning page number already reaches the maximum allowed
687       // and get one idle index when it is animatable
688       bool animatable = false;
689       for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
690       {
691         if( !mIsAnimating[mIndex] )
692         {
693           animatable = true;
694           break;
695         }
696         if( mIsSliding[mIndex] )
697         {
698           animatable = false;
699           break;
700         }
701         mIndex++;
702         mIndex = mIndex % MAXIMUM_TURNING_NUM;
703       }
704
705       if( mPageUpdated && animatable )
706       {
707         SetPanActor( gesture.position ); // determine which page actor is panned
708         if(mPanActor && mPanActor.GetParent() != mRootOnScreen) // if the page is added to turning layer,it is undergoing an animation currently
709         {
710           mPanActor.Reset();
711         }
712         PanStarted( SetPanPosition( gesture.position ) );  // pass in the pan position in the local page coordinate
713       }
714       else
715       {
716         mPanActor.Reset();
717       }
718       break;
719     }
720     case Gesture::Continuing:
721     {
722       PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
723       break;
724     }
725     case Gesture::Finished:
726     case Gesture::Cancelled:
727     {
728       mPanning = false;
729       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
730       break;
731     }
732     case Gesture::Clear:
733     case Gesture::Possible:
734     default:
735     {
736       break;
737     }
738   }
739 }
740
741 void PageTurnView::PanStarted( const Vector2& gesturePosition )
742 {
743   mPressDownPosition = gesturePosition;
744
745   if( !mPanActor )
746   {
747     return;
748   }
749
750   mOriginalCenter = gesturePosition;
751   mTurnEffect[mIndex].SetIsTurningBack( mIsTurnBack[ mPanActor] );
752   mPress = false;
753   mPageUpdated = false;
754
755   // Guard against destruction during signal emission
756   Toolkit::PageTurnView handle( GetOwner() );
757   mPagePanStartedSignal.Emit( handle );
758 }
759
760 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
761 {
762   if( !mPanActor )
763   {
764     return;
765   }
766
767   // Guard against destruction during signal emission
768   Toolkit::PageTurnView handle( GetOwner() );
769
770   if(!mPress)
771   {
772     // when the touch down position is near the spine
773     // or when the panning goes outwards or some other position which would tear the paper in real situation
774     // we change the start position into the current panning position and update the shader parameters
775     if( mOriginalCenter.x <  mPageSize.width*MINIMUM_START_POSITION_RATIO
776         || gesturePosition.x > mOriginalCenter.x-1.0f
777         || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
778              ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
779     {
780       mOriginalCenter = gesturePosition;
781     }
782     else
783     {
784       mDistanceUpCorner = mOriginalCenter.Length();
785       mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
786       mShadowView.Add( mPanActor );
787       SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
788       mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
789       mCurrentCenter = mOriginalCenter;
790       mTurnEffect[mIndex].SetCurrentCenter( mCurrentCenter );
791       mPanDisplacement = 0.f;
792       mConstraints = true;
793       mPress = true;
794       mIsAnimating[mIndex] = true;
795
796       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), !mIsTurnBack[mPanActor] );
797
798       mShadowView.RemoveConstraints();
799       Actor self = Self();
800       self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
801
802       Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
803       shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName())) );
804       shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName())) );
805       shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
806       shadowBlurStrengthConstraint.Apply();
807     }
808   }
809   else
810   {
811     Vector2 currentCenter = gesturePosition;
812
813     // Test whether the new current center would tear the paper from the top pine in real situation
814     // we do not forbid this totally, which would restrict the panning gesture too much
815     // instead, set it to the nearest allowable position
816     float distanceUpCorner = currentCenter.Length();
817     float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
818     if( distanceUpCorner > mDistanceUpCorner )
819     {
820       currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
821     }
822     // would tear the paper from the bottom spine in real situation
823     if( distanceBottomCorner > mDistanceBottomCorner )
824     {
825       currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
826     }
827     // If direction has a very high y component, reduce it.
828     Vector2 curveDirection = currentCenter - mOriginalCenter;
829     if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
830     {
831       currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
832     }
833     // If the vertical distance is high, reduce it
834     float yShift = currentCenter.y - mOriginalCenter.y;
835     if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
836     {
837       currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
838     }
839
840     // use contraints to control the page shape and rotation when the pan position is near the spine
841     if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
842     {
843       // set the property values used by the constraints
844       mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
845       Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
846       Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
847
848       // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
849       // also set up the RotationConstraint to the page actor
850       if( mConstraints )
851       {
852         Vector2 corner;
853         // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
854         if( currentCenter.y >= mOriginalCenter.y )
855         {
856           corner = Vector2( 1.1f*mPageSize.width, 0.f );
857         }
858         else
859         {
860           corner = mPageSize*1.1f;
861         }
862
863         Vector2 offset( currentCenter-mOriginalCenter );
864         float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
865                    /( offset.x*offset.x + offset.y*offset.y );
866         offset *= k;
867         Actor self = Self();
868
869         Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetOriginalCenterPropertyName());
870         Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
871         originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
872         originalCenterConstraint.Apply();
873
874         Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex(mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName());
875         Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
876         currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
877         currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
878         currentCenterConstraint.Apply();
879
880         GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
881
882         float distance = offset.Length();
883         Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
884         rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
885         rotationConstraint.Apply();
886
887         mConstraints = false;
888       }
889     }
890     else
891     {
892       if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
893       {
894         mPanActor.RemoveConstraints();
895         mTurnEffect[mIndex].RemoveConstraints();
896         mTurnEffect[mIndex].SetOriginalCenter( mOriginalCenter );
897         mConstraints = true;
898         mPanDisplacement = 0.f;
899       }
900
901       mTurnEffect[mIndex].SetCurrentCenter( currentCenter );
902       mCurrentCenter = currentCenter;
903       GetImpl( mTurnEffect[mIndex] ).ApplyInternalConstraint();
904     }
905   }
906 }
907
908 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
909 {
910   // Guard against destruction during signal emission
911   Toolkit::PageTurnView handle( GetOwner() );
912
913   if( !mPanActor )
914   {
915     if(!mIsAnimating[mIndex])
916     {
917       OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
918     }
919     return;
920   }
921
922   mPagePanFinishedSignal.Emit( handle );
923
924   ImageActor actor = mPanActor;
925   if(mPress)
926   {
927     if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
928     {
929       // update the pages here instead of in the TurnedOver callback function
930       // as new page is allowed to respond to the pan gesture before other pages finishing animation
931       if(mIsTurnBack[actor])
932       {
933         mCurrentPageIndex--;
934         RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
935         AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
936       }
937       else
938       {
939         mCurrentPageIndex++;
940         RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
941         AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
942       }
943       OrganizePageDepth();
944
945       // set up an animation to turn the page over
946       Actor self = Self();
947       float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
948       Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
949       animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
950                            width,AlphaFunction::EASE_OUT_SINE);
951       animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
952                            Vector2(-mPageSize.width, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
953       mAnimationActorPair[animation] = actor;
954       mAnimationIndexPair[animation] = mIndex;
955       animation.Play();
956       animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
957     }
958     else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
959     {
960       Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
961       animation.AnimateTo( Property( mTurnEffect[mIndex], mTurnEffect[mIndex].PageTurnEffect::GetCurrentCenterPropertyName() ),
962                            mOriginalCenter, AlphaFunction::LINEAR );
963       mAnimationActorPair[animation] = actor;
964       mAnimationIndexPair[animation] = mIndex;
965       animation.Play();
966       mIsSliding[mIndex] = true;
967       animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
968
969       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[mPanActor] );
970     }
971   }
972   else
973   {
974     // In portrait view, an outwards flick should turn the previous page back
975     // In landscape view, nothing to do
976     OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
977   }
978
979   mPageUpdated = true;
980 }
981
982 void PageTurnView::TurnedOver( Animation& animation )
983 {
984   ImageActor actor = mAnimationActorPair[animation];
985   mIsTurnBack[actor] = !mIsTurnBack[actor];
986   actor.RemoveConstraints();
987   mRootOnScreen.Add(actor);
988   int index = mAnimationIndexPair[animation];
989   mIsAnimating[index] = false;
990   mTurnEffect[index].RemoveConstraints();
991   mAnimationIndexPair.erase( animation );
992   mAnimationActorPair.erase( animation );
993
994   SetSpineEffect( actor, mIsTurnBack[actor] );
995
996   // Guard against destruction during signal emission
997   Toolkit::PageTurnView handle( GetOwner() );
998   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[actor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
999 }
1000
1001 void PageTurnView::SliddenBack( Animation& animation )
1002 {
1003   ImageActor actor = mAnimationActorPair[animation];
1004   mRootOnScreen.Add(actor);
1005   int index = mAnimationIndexPair[animation];
1006   mIsSliding[index] = false;
1007   mIsAnimating[index] = false;
1008   mAnimationIndexPair.erase( animation );
1009   mAnimationActorPair.erase( animation );
1010
1011   SetSpineEffect( actor, mIsTurnBack[actor] );
1012
1013   // Guard against destruction during signal emission
1014   Toolkit::PageTurnView handle( GetOwner() );
1015   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>( mCurrentPageIndex + ( ( mIsTurnBack[mPanActor] ) ? -1 : 0 ) ), mIsTurnBack[actor] );
1016 }
1017
1018 void PageTurnView::OrganizePageDepth()
1019 {
1020   for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
1021   {
1022     if(mCurrentPageIndex+i < mTotalPageCount)
1023     {
1024       mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1025     }
1026     if( mCurrentPageIndex >= i + 1 )
1027     {
1028       mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
1029     }
1030   }
1031 }
1032
1033 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
1034 {
1035   SetShaderEffectRecursively( actor, shaderEffect );
1036 }
1037
1038 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
1039 {
1040   return mPageTurnStartedSignal;
1041 }
1042
1043 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
1044 {
1045   return mPageTurnFinishedSignal;
1046 }
1047
1048 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
1049 {
1050   return mPagePanStartedSignal;
1051 }
1052
1053 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
1054 {
1055   return mPagePanFinishedSignal;
1056 }
1057
1058 } // namespace Internal
1059
1060 } // namespace Toolkit
1061
1062 } // namespace Dali