c7d99ce283798247b875a4ba6b39774e8edb448d
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / scroll-bar / scroll-bar-impl.cpp
1 /*
2  * Copyright (c) 2014 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/scroll-bar/scroll-bar-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/animation/constraints.h>
23 #include <dali/public-api/object/type-registry.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h>
27
28 using namespace Dali;
29
30 namespace
31 {
32
33 const char* DEFAULT_INDICATOR_IMAGE_PATH = DALI_IMAGE_DIR "popup_scroll.png";
34 const Vector4 DEFAULT_INDICATOR_NINE_PATCH_BORDER(4.0f, 9.0f, 7.0f, 11.0f);
35 const float MINIMUM_INDICATOR_HEIGHT(20.0f); // The minimum indicator height for the nine patch border
36 const float DEFAULT_SLIDER_DEPTH(1.0f);
37 const float DEFAULT_INDICATOR_SHOW_DURATION(0.5f);
38 const float DEFAULT_INDICATOR_HIDE_DURATION(0.5f);
39 const float DEFAULT_PAN_GESTURE_PROCESS_TIME(16.7f); // 16.7 milliseconds, i.e. one frame
40 const float DEFAULT_INDICATOR_FIXED_HEIGHT(80.0f);
41
42 /**
43  * Indicator size constraint
44  * Indicator size depends on both indicator's parent size and the scroll content size
45  */
46 struct IndicatorSizeConstraint
47 {
48   /**
49    * @param[in] contentSize The size of scrollable content
50    */
51   IndicatorSizeConstraint(float contentSize)
52   : mContentSize(contentSize)
53   {
54   }
55
56   /**
57    * Constraint operator
58    * @param[in] current The current indicator size
59    * @param[in] parentSizeProperty The parent size of scroll indicator.
60    * @return The new scroll indicator size.
61    */
62   Vector3 operator()(const Vector3& current,
63                      const PropertyInput& parentSizeProperty)
64   {
65     const Vector3& parentSize = parentSizeProperty.GetVector3();
66     float height = mContentSize > parentSize.height ? parentSize.height * ( parentSize.height / mContentSize ) : parentSize.height * ( (parentSize.height - mContentSize * 0.5f) / parentSize.height);
67     return Vector3( parentSize.width, std::max(MINIMUM_INDICATOR_HEIGHT, height), parentSize.depth );
68   }
69
70   float mContentSize;  ///< The size of scrollable content
71 };
72
73 /**
74  * Indicator position constraint
75  * Positions the indicator to reflect the current scroll position within the scroll domain.
76  */
77 struct IndicatorPositionConstraint
78 {
79   /**
80    * @param[in] minPosition The minimum limit of scroll position
81    * @param[in] maxPosition the maximum limit of scroll position
82    */
83   IndicatorPositionConstraint(float minPosition, float maxPosition)
84   : mMinPosition(minPosition),
85     mMaxPosition(maxPosition)
86   {
87   }
88
89   /**
90    * Constraint operator
91    * @param[in] current The current indicator position
92    * @param[in] indicatorSizeProperty The size of indicator.
93    * @param[in] parentSizeProperty The parent size of indicator.
94    * @param[in] scrollPositionProperty The scroll position of the scrollable container // (from 0.0 -> 1.0 in each axis)
95    * @return The new indicator position is returned.
96    */
97   Vector3 operator()(const Vector3& current,
98                      const PropertyInput& indicatorSizeProperty,
99                      const PropertyInput& parentSizeProperty,
100                      const PropertyInput& scrollPositionProperty)
101   {
102     Vector3 indicatorSize = indicatorSizeProperty.GetVector3();
103     Vector3 parentSize = parentSizeProperty.GetVector3();
104     float scrollPosition = scrollPositionProperty.GetFloat();
105
106     const float domainSize = fabs(mMaxPosition - mMinPosition);
107     float relativePosition = (mMaxPosition - scrollPosition) / domainSize;
108     return Vector3(current.x, relativePosition * (parentSize.height - indicatorSize.height), DEFAULT_SLIDER_DEPTH);
109   }
110
111   float mMinPosition;  ///< The minimum scroll position
112   float mMaxPosition;  ///< The maximum scroll position
113 };
114
115 } // unnamed namespace
116
117 namespace Dali
118 {
119
120 namespace Toolkit
121 {
122
123 const Property::Index ScrollBar::PROPERTY_INDICATOR_HEIGHT_POLICY( Internal::ScrollBar::SCROLLBAR_PROPERTY_START_INDEX );
124 const Property::Index ScrollBar::PROPERTY_INDICATOR_FIXED_HEIGHT( Internal::ScrollBar::SCROLLBAR_PROPERTY_START_INDEX + 1 );
125 const Property::Index ScrollBar::PROPERTY_INDICATOR_SHOW_DURATION( Internal::ScrollBar::SCROLLBAR_PROPERTY_START_INDEX + 2 );
126 const Property::Index ScrollBar::PROPERTY_INDICATOR_HIDE_DURATION( Internal::ScrollBar::SCROLLBAR_PROPERTY_START_INDEX + 3 );
127
128 namespace Internal
129 {
130
131 namespace
132 {
133
134 using namespace Dali;
135
136 const char* INDICATOR_HEIGHT_POLICY_NAME[] = {"Variable", "Fixed"};
137
138 BaseHandle Create()
139 {
140   return Toolkit::ScrollBar::New();
141 }
142
143 TypeRegistration typeRegistration( typeid(Toolkit::ScrollBar), typeid(Toolkit::ScrollComponent), Create );
144
145 PropertyRegistration property1( typeRegistration, "indicator-height-policy", Toolkit::ScrollBar::PROPERTY_INDICATOR_HEIGHT_POLICY, Property::STRING, &ScrollBar::SetProperty, &ScrollBar::GetProperty );
146 PropertyRegistration property2( typeRegistration, "indicator-fixed-height",  Toolkit::ScrollBar::PROPERTY_INDICATOR_FIXED_HEIGHT,  Property::FLOAT,  &ScrollBar::SetProperty, &ScrollBar::GetProperty );
147 PropertyRegistration property3( typeRegistration, "indicator-show-duration",  Toolkit::ScrollBar::PROPERTY_INDICATOR_SHOW_DURATION,  Property::FLOAT,  &ScrollBar::SetProperty, &ScrollBar::GetProperty );
148 PropertyRegistration property4( typeRegistration, "indicator-hide-duration",  Toolkit::ScrollBar::PROPERTY_INDICATOR_HIDE_DURATION,  Property::FLOAT,  &ScrollBar::SetProperty, &ScrollBar::GetProperty );
149 }
150
151 ScrollBar::ScrollBar()
152 : mIndicatorShowDuration(DEFAULT_INDICATOR_SHOW_DURATION),
153   mIndicatorHideDuration(DEFAULT_INDICATOR_HIDE_DURATION),
154   mScrollStart(0.0f),
155   mIsPanning(false),
156   mCurrentScrollPosition(0.0f),
157   mIndicatorHeightPolicy(Toolkit::ScrollBar::Variable),
158   mIndicatorFixedHeight(DEFAULT_INDICATOR_FIXED_HEIGHT)
159 {
160 }
161
162 ScrollBar::~ScrollBar()
163 {
164 }
165
166 void ScrollBar::OnInitialize()
167 {
168   Actor self = Self();
169
170   Image indicatorImage = Image::New( DEFAULT_INDICATOR_IMAGE_PATH );
171   mIndicator = ImageActor::New( indicatorImage );
172   mIndicator.SetNinePatchBorder( DEFAULT_INDICATOR_NINE_PATCH_BORDER );
173   mIndicator.SetStyle( ImageActor::STYLE_NINE_PATCH );
174   mIndicator.SetParentOrigin( ParentOrigin::TOP_LEFT );
175   mIndicator.SetAnchorPoint( AnchorPoint::TOP_LEFT );
176   self.Add(mIndicator);
177
178   self.SetDrawMode(DrawMode::OVERLAY);
179
180   // Enable the pan gesture which is attached to the control
181   EnableGestureDetection(Gesture::Type(Gesture::Pan));
182 }
183
184 void ScrollBar::OnScrollConnectorSet( Toolkit::ScrollConnector oldConnector )
185 {
186   if( oldConnector )
187   {
188     oldConnector.DomainChangedSignal().Disconnect(this, &ScrollBar::OnScrollDomainChanged);
189
190     mScrollPositionObject.Reset();
191   }
192
193   if( mScrollConnector )
194   {
195     mScrollPositionObject = mScrollConnector.GetScrollPositionObject();
196
197     ApplyConstraints();
198     mScrollConnector.DomainChangedSignal().Connect(this, &ScrollBar::OnScrollDomainChanged);
199   }
200 }
201
202 void ScrollBar::SetIndicatorImage( Image image )
203 {
204   mIndicator.SetImage(image);
205 }
206
207 Actor ScrollBar::GetScrollIndicator()
208 {
209   return mIndicator;
210 }
211
212 void ScrollBar::ApplyConstraints()
213 {
214   if( mScrollConnector )
215   {
216     Constraint constraint;
217
218     if(mIndicatorSizeConstraint)
219     {
220       mIndicator.RemoveConstraint(mIndicatorSizeConstraint);
221     }
222
223     // Set indicator height according to the indicator's height policy
224     if(mIndicatorHeightPolicy == Toolkit::ScrollBar::Fixed)
225     {
226       mIndicator.SetSize(Self().GetCurrentSize().width, mIndicatorFixedHeight);
227     }
228     else
229     {
230       constraint = Constraint::New<Vector3>( Actor::SIZE,
231                                              ParentSource( Actor::SIZE ),
232                                              IndicatorSizeConstraint( mScrollConnector.GetContentLength() ) );
233       mIndicatorSizeConstraint = mIndicator.ApplyConstraint( constraint );
234     }
235
236     if(mIndicatorPositionConstraint)
237     {
238       mIndicator.RemoveConstraint(mIndicatorPositionConstraint);
239     }
240
241     constraint = Constraint::New<Vector3>( Actor::POSITION,
242                                            LocalSource( Actor::SIZE ),
243                                            ParentSource( Actor::SIZE ),
244                                            Source( mScrollPositionObject, Toolkit::ScrollConnector::SCROLL_POSITION ),
245                                            IndicatorPositionConstraint( mScrollConnector.GetMinLimit(), mScrollConnector.GetMaxLimit() ) );
246     mIndicatorPositionConstraint = mIndicator.ApplyConstraint( constraint );
247
248     if( mBackground )
249     {
250       mBackground.RemoveConstraints();
251
252       constraint = Constraint::New<Vector3>(Actor::SIZE,
253                                             ParentSource(Actor::SIZE),
254                                             EqualToConstraint());
255       mBackground.ApplyConstraint(constraint);
256     }
257   }
258 }
259
260 void ScrollBar::SetPositionNotifications( const std::vector<float>& positions )
261 {
262   if(mScrollPositionObject)
263   {
264     if(mPositionNotification)
265     {
266       mScrollPositionObject.RemovePropertyNotification(mPositionNotification);
267     }
268     mPositionNotification = mScrollPositionObject.AddPropertyNotification( Toolkit::ScrollConnector::SCROLL_POSITION, VariableStepCondition(positions) );
269     mPositionNotification.NotifySignal().Connect( this, &ScrollBar::OnScrollPositionNotified );
270   }
271 }
272
273 void ScrollBar::OnScrollPositionNotified(PropertyNotification& source)
274 {
275   // Emit the signal to notify the scroll position crossing
276   mScrollPositionNotifiedSignal.Emit(mScrollConnector.GetScrollPosition());
277 }
278
279 void ScrollBar::Show()
280 {
281   // Cancel any animation
282   if(mAnimation)
283   {
284     mAnimation.Clear();
285     mAnimation.Reset();
286   }
287
288   if(mIndicatorShowDuration > 0.0f)
289   {
290     mAnimation = Animation::New( mIndicatorShowDuration );
291     mAnimation.OpacityTo( Self(), 1.0f, AlphaFunctions::EaseIn );
292     mAnimation.Play();
293   }
294   else
295   {
296     Self().SetOpacity(1.0f);
297   }
298 }
299
300 void ScrollBar::Hide()
301 {
302   // Cancel any animation
303   if(mAnimation)
304   {
305     mAnimation.Clear();
306     mAnimation.Reset();
307   }
308
309   if(mIndicatorHideDuration > 0.0f)
310   {
311     mAnimation = Animation::New( mIndicatorHideDuration );
312     mAnimation.OpacityTo( Self(), 0.0f, AlphaFunctions::EaseIn );
313     mAnimation.Play();
314   }
315   else
316   {
317     Self().SetOpacity(0.0f);
318   }
319 }
320
321 bool ScrollBar::OnPanGestureProcessTick()
322 {
323   // Update the scroll position property.
324   if( mScrollConnector )
325   {
326     mScrollConnector.SetScrollPosition(mCurrentScrollPosition);
327   }
328
329   return true;
330 }
331
332 void ScrollBar::OnPan( PanGesture gesture )
333 {
334   if(mScrollConnector)
335   {
336     Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast(Self().GetParent());
337
338     switch(gesture.state)
339     {
340       case Gesture::Started:
341       {
342         if( !mTimer )
343         {
344           // Make sure the pan gesture is only being processed once per frame.
345           mTimer = Timer::New( DEFAULT_PAN_GESTURE_PROCESS_TIME );
346           mTimer.TickSignal().Connect( this, &ScrollBar::OnPanGestureProcessTick );
347           mTimer.Start();
348         }
349
350         Show();
351         mScrollStart = mScrollConnector.GetScrollPosition();
352         mGestureDisplacement = Vector3::ZERO;
353         mIsPanning = true;
354
355         break;
356       }
357       case Gesture::Continuing:
358       {
359         Vector3 delta(gesture.displacement.x, gesture.displacement.y, 0.0f);
360         mGestureDisplacement+=delta;
361
362         Vector3 span = Self().GetCurrentSize() - mIndicator.GetCurrentSize();
363         const float domainSize = fabs(mScrollConnector.GetMaxLimit() - mScrollConnector.GetMinLimit());
364         mCurrentScrollPosition = mScrollStart - mGestureDisplacement.y * domainSize / span.y;
365         mCurrentScrollPosition = std::min(mScrollConnector.GetMaxLimit(), std::max(mCurrentScrollPosition, mScrollConnector.GetMinLimit()));
366
367         break;
368       }
369       default:
370       {
371         mIsPanning = false;
372
373         if( mTimer )
374         {
375           // Destroy the timer when pan gesture is finished.
376           mTimer.Stop();
377           mTimer.TickSignal().Disconnect( this, &ScrollBar::OnPanGestureProcessTick );
378           mTimer.Reset();
379         }
380
381         if(itemView)
382         {
383           // Refresh the ItemView cache with extra items
384           GetImpl(itemView).DoRefresh(mCurrentScrollPosition, true);
385         }
386
387         break;
388       }
389     }
390
391     if(itemView)
392     {
393       // Disable automatic refresh in ItemView during fast scrolling
394       GetImpl(itemView).SetRefreshEnabled(!mIsPanning);
395     }
396   }
397 }
398
399 void ScrollBar::OnScrollDomainChanged(float minPosition, float maxPosition, float contentSize)
400 {
401   // Reapply constraints when the scroll domain is changed
402   ApplyConstraints();
403 }
404
405 void ScrollBar::SetIndicatorHeightPolicy( Toolkit::ScrollBar::IndicatorHeightPolicy policy )
406 {
407   mIndicatorHeightPolicy = policy;
408   ApplyConstraints();
409 }
410
411 Toolkit::ScrollBar::IndicatorHeightPolicy ScrollBar::GetIndicatorHeightPolicy()
412 {
413   return mIndicatorHeightPolicy;
414 }
415
416 void ScrollBar::SetIndicatorFixedHeight( float height )
417 {
418   mIndicatorFixedHeight = height;
419   ApplyConstraints();
420 }
421
422 float ScrollBar::GetIndicatorFixedHeight()
423 {
424   return mIndicatorFixedHeight;
425 }
426
427 void ScrollBar::SetIndicatorShowDuration( float durationSeconds )
428 {
429   mIndicatorShowDuration = durationSeconds;
430 }
431
432 float ScrollBar::GetIndicatorShowDuration()
433 {
434   return mIndicatorShowDuration;
435 }
436
437 void ScrollBar::SetIndicatorHideDuration( float durationSeconds )
438 {
439   mIndicatorHideDuration = durationSeconds;
440 }
441
442 float ScrollBar::GetIndicatorHideDuration()
443 {
444   return mIndicatorHideDuration;
445 }
446
447 void ScrollBar::OnIndicatorHeightPolicyPropertySet( Property::Value propertyValue )
448 {
449   std::string policyName( propertyValue.Get<std::string>() );
450   if(policyName == "Variable")
451   {
452     SetIndicatorHeightPolicy(Toolkit::ScrollBar::Variable);
453   }
454   else if(policyName == "Fixed")
455   {
456     SetIndicatorHeightPolicy(Toolkit::ScrollBar::Fixed);
457   }
458   else
459   {
460     DALI_ASSERT_ALWAYS( !"ScrollBar::OnIndicatorHeightPolicyPropertySet(). Invalid Property value." );
461   }
462 }
463
464 void ScrollBar::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
465 {
466   Toolkit::ScrollBar scrollBar = Toolkit::ScrollBar::DownCast( Dali::BaseHandle( object ) );
467
468   if( scrollBar )
469   {
470     ScrollBar& scrollBarImpl( GetImpl( scrollBar ) );
471     switch( index )
472     {
473       case Toolkit::ScrollBar::PROPERTY_INDICATOR_HEIGHT_POLICY:
474       {
475         scrollBarImpl.OnIndicatorHeightPolicyPropertySet( value );
476         break;
477       }
478       case Toolkit::ScrollBar::PROPERTY_INDICATOR_FIXED_HEIGHT:
479       {
480         scrollBarImpl.SetIndicatorFixedHeight(value.Get<float>());
481         break;
482       }
483       case Toolkit::ScrollBar::PROPERTY_INDICATOR_SHOW_DURATION:
484       {
485         scrollBarImpl.SetIndicatorShowDuration(value.Get<float>());
486         break;
487       }
488       case Toolkit::ScrollBar::PROPERTY_INDICATOR_HIDE_DURATION:
489       {
490         scrollBarImpl.SetIndicatorHideDuration(value.Get<float>());
491         break;
492       }
493     }
494   }
495 }
496
497 Property::Value ScrollBar::GetProperty( BaseObject* object, Property::Index index )
498 {
499   Property::Value value;
500
501   Toolkit::ScrollBar scrollBar = Toolkit::ScrollBar::DownCast( Dali::BaseHandle( object ) );
502
503   if( scrollBar )
504   {
505     ScrollBar& scrollBarImpl( GetImpl( scrollBar ) );
506     switch( index )
507     {
508       case Toolkit::ScrollBar::PROPERTY_INDICATOR_HEIGHT_POLICY:
509       {
510         value = INDICATOR_HEIGHT_POLICY_NAME[ scrollBarImpl.GetIndicatorHeightPolicy() ];
511         break;
512       }
513       case Toolkit::ScrollBar::PROPERTY_INDICATOR_FIXED_HEIGHT:
514       {
515         value = scrollBarImpl.GetIndicatorFixedHeight();
516         break;
517       }
518       case Toolkit::ScrollBar::PROPERTY_INDICATOR_SHOW_DURATION:
519       {
520         value = scrollBarImpl.GetIndicatorShowDuration();
521         break;
522       }
523       case Toolkit::ScrollBar::PROPERTY_INDICATOR_HIDE_DURATION:
524       {
525         value = scrollBarImpl.GetIndicatorHideDuration();
526         break;
527       }
528     }
529   }
530   return value;
531 }
532
533 Toolkit::ScrollBar ScrollBar::New()
534 {
535   // Create the implementation, temporarily owned by this handle on stack
536   IntrusivePtr< ScrollBar > impl = new ScrollBar();
537
538   // Pass ownership to CustomActor handle
539   Toolkit::ScrollBar handle( *impl );
540
541   // Second-phase init of the implementation
542   // This can only be done after the CustomActor connection has been made...
543   impl->Initialize();
544
545   return handle;
546 }
547
548 } // namespace Internal
549
550 } // namespace Toolkit
551
552 } // namespace Dali