Add new API to ScrollBar to set background and indicator images.
[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   mBackground.SetNinePatchBorder( border );
202   mBackground.SetStyle( ImageActor::STYLE_NINE_PATCH );
203 }
204
205 void ScrollBar::SetIndicatorImage( Image image )
206 {
207   mIndicator.SetImage(image);
208 }
209
210 void ScrollBar::SetIndicatorImage( Image image, const Vector4& border )
211 {
212   mIndicator.SetImage(image);
213   mIndicator.SetNinePatchBorder( border );
214   mIndicator.SetStyle( ImageActor::STYLE_NINE_PATCH );
215 }
216
217 Actor ScrollBar::GetScrollIndicator()
218 {
219   return mIndicator;
220 }
221
222 void ScrollBar::ApplyConstraints()
223 {
224   if( mScrollConnector )
225   {
226     Constraint constraint;
227
228     if(mIndicatorSizeConstraint)
229     {
230       mIndicator.RemoveConstraint(mIndicatorSizeConstraint);
231     }
232
233     // Set indicator height according to the indicator's height policy
234     if(mIndicatorHeightPolicy == Toolkit::ScrollBar::Fixed)
235     {
236       mIndicator.SetSize(Self().GetCurrentSize().width, mIndicatorFixedHeight);
237     }
238     else
239     {
240       constraint = Constraint::New<Vector3>( Actor::SIZE,
241                                              ParentSource( Actor::SIZE ),
242                                              IndicatorSizeConstraint( mScrollConnector.GetContentLength() ) );
243       mIndicatorSizeConstraint = mIndicator.ApplyConstraint( constraint );
244     }
245
246     if(mIndicatorPositionConstraint)
247     {
248       mIndicator.RemoveConstraint(mIndicatorPositionConstraint);
249     }
250
251     constraint = Constraint::New<Vector3>( Actor::POSITION,
252                                            LocalSource( Actor::SIZE ),
253                                            ParentSource( Actor::SIZE ),
254                                            Source( mScrollPositionObject, Toolkit::ScrollConnector::SCROLL_POSITION ),
255                                            IndicatorPositionConstraint( mScrollConnector.GetMinLimit(), mScrollConnector.GetMaxLimit() ) );
256     mIndicatorPositionConstraint = mIndicator.ApplyConstraint( constraint );
257
258     if( mBackground )
259     {
260       mBackground.RemoveConstraints();
261
262       constraint = Constraint::New<Vector3>(Actor::SIZE,
263                                             ParentSource(Actor::SIZE),
264                                             EqualToConstraint());
265       mBackground.ApplyConstraint(constraint);
266     }
267   }
268 }
269
270 void ScrollBar::SetPositionNotifications( const std::vector<float>& positions )
271 {
272   if(mScrollPositionObject)
273   {
274     if(mPositionNotification)
275     {
276       mScrollPositionObject.RemovePropertyNotification(mPositionNotification);
277     }
278     mPositionNotification = mScrollPositionObject.AddPropertyNotification( Toolkit::ScrollConnector::SCROLL_POSITION, VariableStepCondition(positions) );
279     mPositionNotification.NotifySignal().Connect( this, &ScrollBar::OnScrollPositionNotified );
280   }
281 }
282
283 void ScrollBar::OnScrollPositionNotified(PropertyNotification& source)
284 {
285   // Emit the signal to notify the scroll position crossing
286   mScrollPositionNotifiedSignal.Emit(mScrollPositionObject.GetProperty<float>( Toolkit::ScrollConnector::SCROLL_POSITION ));
287 }
288
289 void ScrollBar::Show()
290 {
291   // Cancel any animation
292   if(mAnimation)
293   {
294     mAnimation.Clear();
295     mAnimation.Reset();
296   }
297
298   mAnimation = Animation::New( INDICATOR_SHOW_TIME );
299   mAnimation.OpacityTo( Self(), 1.0f, AlphaFunctions::EaseIn );
300   mAnimation.Play();
301 }
302
303 void ScrollBar::Hide()
304 {
305   // Cancel any animation
306   if(mAnimation)
307   {
308     mAnimation.Clear();
309     mAnimation.Reset();
310   }
311
312   mAnimation = Animation::New( INDICATOR_HIDE_TIME );
313   mAnimation.OpacityTo( Self(), 0.0f, AlphaFunctions::EaseIn );
314   mAnimation.Play();
315 }
316
317 bool ScrollBar::OnPanGestureProcessTick()
318 {
319   // Update the scroll position property.
320   mScrollPositionObject.SetProperty( Toolkit::ScrollConnector::SCROLL_POSITION, mCurrentScrollPosition );
321
322   Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast(Self().GetParent());
323   if(itemView)
324   {
325     // Refresh ItemView immediately when the scroll position is changed.
326     GetImpl(itemView).DoRefresh(mCurrentScrollPosition, false); // No need to cache extra items.
327   }
328
329   return true;
330 }
331
332 void ScrollBar::OnPan( PanGesture gesture )
333 {
334   if(mScrollPositionObject)
335   {
336     switch(gesture.state)
337     {
338       case Gesture::Started:
339       {
340         if( !mTimer )
341         {
342           // Make sure the pan gesture is only being processed once per frame.
343           mTimer = Timer::New( DEFAULT_PAN_GESTURE_PROCESS_TIME );
344           mTimer.TickSignal().Connect( this, &ScrollBar::OnPanGestureProcessTick );
345           mTimer.Start();
346         }
347
348         Show();
349         mScrollStart = mScrollPositionObject.GetProperty<float>( Toolkit::ScrollConnector::SCROLL_POSITION );
350         mGestureDisplacement = Vector3::ZERO;
351         mIsPanning = true;
352
353         break;
354       }
355       case Gesture::Continuing:
356       {
357         Vector3 delta(gesture.displacement.x, gesture.displacement.y, 0.0f);
358         mGestureDisplacement+=delta;
359
360         Vector3 span = Self().GetCurrentSize() - mIndicator.GetCurrentSize();
361         const float domainSize = fabs(mScrollConnector.GetMaxLimit() - mScrollConnector.GetMinLimit());
362         mCurrentScrollPosition = mScrollStart - mGestureDisplacement.y * domainSize / span.y;
363         mCurrentScrollPosition = std::min(mScrollConnector.GetMaxLimit(), std::max(mCurrentScrollPosition, mScrollConnector.GetMinLimit()));
364
365         break;
366       }
367       default:
368       {
369         mIsPanning = false;
370
371         if( mTimer )
372         {
373           // Destroy the timer when pan gesture is finished.
374           mTimer.Stop();
375           mTimer.TickSignal().Disconnect( this, &ScrollBar::OnPanGestureProcessTick );
376           mTimer.Reset();
377         }
378
379         break;
380       }
381     }
382
383     Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast(Self().GetParent());
384     if(itemView)
385     {
386       // Disable automatic refresh in ItemView during fast scrolling
387       GetImpl(itemView).SetRefreshEnabled(!mIsPanning);
388     }
389   }
390 }
391
392 void ScrollBar::OnScrollDomainChanged(float minPosition, float maxPosition, float contentSize)
393 {
394   // Reapply constraints when the scroll domain is changed
395   ApplyConstraints();
396 }
397
398 void ScrollBar::SetIndicatorHeightPolicy( Toolkit::ScrollBar::IndicatorHeightPolicy policy )
399 {
400   mIndicatorHeightPolicy = policy;
401   ApplyConstraints();
402 }
403
404 Toolkit::ScrollBar::IndicatorHeightPolicy ScrollBar::GetIndicatorHeightPolicy()
405 {
406   return mIndicatorHeightPolicy;
407 }
408
409 void ScrollBar::SetIndicatorFixedHeight( float height )
410 {
411   mIndicatorFixedHeight = height;
412   ApplyConstraints();
413 }
414
415 float ScrollBar::GetIndicatorFixedHeight()
416 {
417   return mIndicatorFixedHeight;
418 }
419
420 void ScrollBar::OnIndicatorHeightPolicyPropertySet( Property::Value propertyValue )
421 {
422   std::string policyName( propertyValue.Get<std::string>() );
423   if(policyName == "Variable")
424   {
425     SetIndicatorHeightPolicy(Toolkit::ScrollBar::Variable);
426   }
427   else if(policyName == "Fixed")
428   {
429     SetIndicatorHeightPolicy(Toolkit::ScrollBar::Fixed);
430   }
431   else
432   {
433     DALI_ASSERT_ALWAYS( !"ScrollBar::OnIndicatorHeightPolicyPropertySet(). Invalid Property value." );
434   }
435 }
436
437 void ScrollBar::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
438 {
439   Toolkit::ScrollBar scrollBar = Toolkit::ScrollBar::DownCast( Dali::BaseHandle( object ) );
440
441   if( scrollBar )
442   {
443     ScrollBar& scrollBarImpl( GetImpl( scrollBar ) );
444     switch( index )
445     {
446       case Toolkit::ScrollBar::PROPERTY_INDICATOR_HEIGHT_POLICY:
447       {
448         scrollBarImpl.OnIndicatorHeightPolicyPropertySet( value );
449         break;
450       }
451       case Toolkit::ScrollBar::PROPERTY_INDICATOR_FIXED_HEIGHT:
452       {
453         scrollBarImpl.SetIndicatorFixedHeight(value.Get<float>());
454         break;
455       }
456     }
457   }
458 }
459
460 Property::Value ScrollBar::GetProperty( BaseObject* object, Property::Index index )
461 {
462   Property::Value value;
463
464   Toolkit::ScrollBar scrollBar = Toolkit::ScrollBar::DownCast( Dali::BaseHandle( object ) );
465
466   if( scrollBar )
467   {
468     ScrollBar& scrollBarImpl( GetImpl( scrollBar ) );
469     switch( index )
470     {
471       case Toolkit::ScrollBar::PROPERTY_INDICATOR_HEIGHT_POLICY:
472       {
473         value = INDICATOR_HEIGHT_POLICY_NAME[ scrollBarImpl.GetIndicatorHeightPolicy() ];
474         break;
475       }
476       case Toolkit::ScrollBar::PROPERTY_INDICATOR_FIXED_HEIGHT:
477       {
478         value = scrollBarImpl.GetIndicatorFixedHeight();
479         break;
480       }
481     }
482   }
483   return value;
484 }
485
486 Toolkit::ScrollBar ScrollBar::New()
487 {
488   // Create the implementation, temporarily owned by this handle on stack
489   IntrusivePtr< ScrollBar > impl = new ScrollBar();
490
491   // Pass ownership to CustomActor handle
492   Toolkit::ScrollBar handle( *impl );
493
494   // Second-phase init of the implementation
495   // This can only be done after the CustomActor connection has been made...
496   impl->Initialize();
497
498   return handle;
499 }
500
501 } // namespace Internal
502
503 } // namespace Toolkit
504
505 } // namespace Dali