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