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