1191db6460c5f8956f928e63e1c0210ace350317
[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   mPropertyIndicatorPosition(Property::INVALID_INDEX)
160 {
161 }
162
163 ScrollBar::~ScrollBar()
164 {
165 }
166
167 void ScrollBar::OnInitialize()
168 {
169   Actor self = Self();
170
171   Image indicatorImage = Image::New( DEFAULT_INDICATOR_IMAGE_PATH );
172   mIndicator = ImageActor::New( indicatorImage );
173   mIndicator.SetNinePatchBorder( DEFAULT_INDICATOR_NINE_PATCH_BORDER );
174   mIndicator.SetStyle( ImageActor::STYLE_NINE_PATCH );
175   mIndicator.SetParentOrigin( ParentOrigin::TOP_LEFT );
176   mIndicator.SetAnchorPoint( AnchorPoint::TOP_LEFT );
177   self.Add(mIndicator);
178
179   self.SetDrawMode(DrawMode::OVERLAY);
180
181   // Enable the pan gesture which is attached to the control
182   EnableGestureDetection(Gesture::Type(Gesture::Pan));
183 }
184
185 void ScrollBar::OnScrollConnectorSet( Toolkit::ScrollConnector oldConnector )
186 {
187   if( oldConnector )
188   {
189     oldConnector.DomainChangedSignal().Disconnect(this, &ScrollBar::OnScrollDomainChanged);
190
191     mScrollPositionObject.Reset();
192   }
193
194   if( mScrollConnector )
195   {
196     mScrollPositionObject = mScrollConnector.GetScrollPositionObject();
197
198     ApplyConstraints();
199     mScrollConnector.DomainChangedSignal().Connect(this, &ScrollBar::OnScrollDomainChanged);
200   }
201 }
202
203 void ScrollBar::SetIndicatorImage( Image image )
204 {
205   mIndicator.SetImage(image);
206 }
207
208 Actor ScrollBar::GetScrollIndicator()
209 {
210   return mIndicator;
211 }
212
213 void ScrollBar::ApplyConstraints()
214 {
215   if( mScrollConnector )
216   {
217     Constraint constraint;
218
219     if(mIndicatorSizeConstraint)
220     {
221       mIndicator.RemoveConstraint(mIndicatorSizeConstraint);
222     }
223
224     // Set indicator height according to the indicator's height policy
225     if(mIndicatorHeightPolicy == Toolkit::ScrollBar::Fixed)
226     {
227       mIndicator.SetSize(Self().GetCurrentSize().width, mIndicatorFixedHeight);
228     }
229     else
230     {
231       constraint = Constraint::New<Vector3>( Actor::SIZE,
232                                              ParentSource( Actor::SIZE ),
233                                              IndicatorSizeConstraint( mScrollConnector.GetContentLength() ) );
234       mIndicatorSizeConstraint = mIndicator.ApplyConstraint( constraint );
235     }
236
237     if(mIndicatorPositionConstraint)
238     {
239       mIndicator.RemoveConstraint(mIndicatorPositionConstraint);
240     }
241
242     constraint = Constraint::New<Vector3>( Actor::POSITION,
243                                            LocalSource( Actor::SIZE ),
244                                            ParentSource( Actor::SIZE ),
245                                            Source( mScrollPositionObject, Toolkit::ScrollConnector::SCROLL_POSITION ),
246                                            IndicatorPositionConstraint( mScrollConnector.GetMinLimit(), mScrollConnector.GetMaxLimit() ) );
247     mIndicatorPositionConstraint = mIndicator.ApplyConstraint( constraint );
248
249     if( mBackground )
250     {
251       mBackground.RemoveConstraints();
252
253       constraint = Constraint::New<Vector3>(Actor::SIZE,
254                                             ParentSource(Actor::SIZE),
255                                             EqualToConstraint());
256       mBackground.ApplyConstraint(constraint);
257     }
258   }
259 }
260
261 void ScrollBar::SetPositionNotifications( const std::vector<float>& positions )
262 {
263   if(mScrollPositionObject)
264   {
265     if(mPositionNotification)
266     {
267       mScrollPositionObject.RemovePropertyNotification(mPositionNotification);
268     }
269     mPositionNotification = mScrollPositionObject.AddPropertyNotification( Toolkit::ScrollConnector::SCROLL_POSITION, VariableStepCondition(positions) );
270     mPositionNotification.NotifySignal().Connect( this, &ScrollBar::OnScrollPositionNotified );
271   }
272 }
273
274 void ScrollBar::OnScrollPositionNotified(PropertyNotification& source)
275 {
276   // Emit the signal to notify the scroll position crossing
277   mScrollPositionNotifiedSignal.Emit(mScrollConnector.GetScrollPosition());
278 }
279
280 void ScrollBar::Show()
281 {
282   // Cancel any animation
283   if(mAnimation)
284   {
285     mAnimation.Clear();
286     mAnimation.Reset();
287   }
288
289   if(mIndicatorShowDuration > 0.0f)
290   {
291     mAnimation = Animation::New( mIndicatorShowDuration );
292     mAnimation.OpacityTo( Self(), 1.0f, AlphaFunctions::EaseIn );
293     mAnimation.Play();
294   }
295   else
296   {
297     Self().SetOpacity(1.0f);
298   }
299 }
300
301 void ScrollBar::Hide()
302 {
303   // Cancel any animation
304   if(mAnimation)
305   {
306     mAnimation.Clear();
307     mAnimation.Reset();
308   }
309
310   if(mIndicatorHideDuration > 0.0f)
311   {
312     mAnimation = Animation::New( mIndicatorHideDuration );
313     mAnimation.OpacityTo( Self(), 0.0f, AlphaFunctions::EaseIn );
314     mAnimation.Play();
315   }
316   else
317   {
318     Self().SetOpacity(0.0f);
319   }
320 }
321
322 bool ScrollBar::OnPanGestureProcessTick()
323 {
324   // Update the scroll position property.
325   if( mScrollConnector )
326   {
327     mScrollConnector.SetScrollPosition(mCurrentScrollPosition);
328   }
329
330   return true;
331 }
332
333 void ScrollBar::OnPan( PanGesture gesture )
334 {
335   if(mScrollConnector)
336   {
337     Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast(Self().GetParent());
338
339     switch(gesture.state)
340     {
341       case Gesture::Started:
342       {
343         if( !mTimer )
344         {
345           // Make sure the pan gesture is only being processed once per frame.
346           mTimer = Timer::New( DEFAULT_PAN_GESTURE_PROCESS_TIME );
347           mTimer.TickSignal().Connect( this, &ScrollBar::OnPanGestureProcessTick );
348           mTimer.Start();
349         }
350
351         Show();
352         mScrollStart = mScrollConnector.GetScrollPosition();
353         mGestureDisplacement = Vector3::ZERO;
354         mIsPanning = true;
355
356         break;
357       }
358       case Gesture::Continuing:
359       {
360         Vector3 delta(gesture.displacement.x, gesture.displacement.y, 0.0f);
361         mGestureDisplacement+=delta;
362
363         Vector3 span = Self().GetCurrentSize() - mIndicator.GetCurrentSize();
364         const float domainSize = fabs(mScrollConnector.GetMaxLimit() - mScrollConnector.GetMinLimit());
365         mCurrentScrollPosition = mScrollStart - mGestureDisplacement.y * domainSize / span.y;
366         mCurrentScrollPosition = std::min(mScrollConnector.GetMaxLimit(), std::max(mCurrentScrollPosition, mScrollConnector.GetMinLimit()));
367
368         break;
369       }
370       default:
371       {
372         mIsPanning = false;
373
374         if( mTimer )
375         {
376           // Destroy the timer when pan gesture is finished.
377           mTimer.Stop();
378           mTimer.TickSignal().Disconnect( this, &ScrollBar::OnPanGestureProcessTick );
379           mTimer.Reset();
380         }
381
382         if(itemView)
383         {
384           // Refresh the ItemView cache with extra items
385           GetImpl(itemView).DoRefresh(mCurrentScrollPosition, true);
386         }
387
388         break;
389       }
390     }
391
392     if(itemView)
393     {
394       // Disable automatic refresh in ItemView during fast scrolling
395       GetImpl(itemView).SetRefreshEnabled(!mIsPanning);
396     }
397   }
398 }
399
400 void ScrollBar::OnScrollDomainChanged(float minPosition, float maxPosition, float contentSize)
401 {
402   // Reapply constraints when the scroll domain is changed
403   ApplyConstraints();
404 }
405
406 void ScrollBar::SetIndicatorHeightPolicy( Toolkit::ScrollBar::IndicatorHeightPolicy policy )
407 {
408   mIndicatorHeightPolicy = policy;
409   ApplyConstraints();
410 }
411
412 Toolkit::ScrollBar::IndicatorHeightPolicy ScrollBar::GetIndicatorHeightPolicy()
413 {
414   return mIndicatorHeightPolicy;
415 }
416
417 void ScrollBar::SetIndicatorFixedHeight( float height )
418 {
419   mIndicatorFixedHeight = height;
420   ApplyConstraints();
421 }
422
423 float ScrollBar::GetIndicatorFixedHeight()
424 {
425   return mIndicatorFixedHeight;
426 }
427
428 void ScrollBar::SetIndicatorShowDuration( float durationSeconds )
429 {
430   mIndicatorShowDuration = durationSeconds;
431 }
432
433 float ScrollBar::GetIndicatorShowDuration()
434 {
435   return mIndicatorShowDuration;
436 }
437
438 void ScrollBar::SetIndicatorHideDuration( float durationSeconds )
439 {
440   mIndicatorHideDuration = durationSeconds;
441 }
442
443 float ScrollBar::GetIndicatorHideDuration()
444 {
445   return mIndicatorHideDuration;
446 }
447
448 void ScrollBar::OnIndicatorHeightPolicyPropertySet( Property::Value propertyValue )
449 {
450   std::string policyName( propertyValue.Get<std::string>() );
451   if(policyName == "Variable")
452   {
453     SetIndicatorHeightPolicy(Toolkit::ScrollBar::Variable);
454   }
455   else if(policyName == "Fixed")
456   {
457     SetIndicatorHeightPolicy(Toolkit::ScrollBar::Fixed);
458   }
459   else
460   {
461     DALI_ASSERT_ALWAYS( !"ScrollBar::OnIndicatorHeightPolicyPropertySet(). Invalid Property value." );
462   }
463 }
464
465 void ScrollBar::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
466 {
467   Toolkit::ScrollBar scrollBar = Toolkit::ScrollBar::DownCast( Dali::BaseHandle( object ) );
468
469   if( scrollBar )
470   {
471     ScrollBar& scrollBarImpl( GetImpl( scrollBar ) );
472     switch( index )
473     {
474       case Toolkit::ScrollBar::PROPERTY_INDICATOR_HEIGHT_POLICY:
475       {
476         scrollBarImpl.OnIndicatorHeightPolicyPropertySet( value );
477         break;
478       }
479       case Toolkit::ScrollBar::PROPERTY_INDICATOR_FIXED_HEIGHT:
480       {
481         scrollBarImpl.SetIndicatorFixedHeight(value.Get<float>());
482         break;
483       }
484       case Toolkit::ScrollBar::PROPERTY_INDICATOR_SHOW_DURATION:
485       {
486         scrollBarImpl.SetIndicatorShowDuration(value.Get<float>());
487         break;
488       }
489       case Toolkit::ScrollBar::PROPERTY_INDICATOR_HIDE_DURATION:
490       {
491         scrollBarImpl.SetIndicatorHideDuration(value.Get<float>());
492         break;
493       }
494     }
495   }
496 }
497
498 Property::Value ScrollBar::GetProperty( BaseObject* object, Property::Index index )
499 {
500   Property::Value value;
501
502   Toolkit::ScrollBar scrollBar = Toolkit::ScrollBar::DownCast( Dali::BaseHandle( object ) );
503
504   if( scrollBar )
505   {
506     ScrollBar& scrollBarImpl( GetImpl( scrollBar ) );
507     switch( index )
508     {
509       case Toolkit::ScrollBar::PROPERTY_INDICATOR_HEIGHT_POLICY:
510       {
511         value = INDICATOR_HEIGHT_POLICY_NAME[ scrollBarImpl.GetIndicatorHeightPolicy() ];
512         break;
513       }
514       case Toolkit::ScrollBar::PROPERTY_INDICATOR_FIXED_HEIGHT:
515       {
516         value = scrollBarImpl.GetIndicatorFixedHeight();
517         break;
518       }
519       case Toolkit::ScrollBar::PROPERTY_INDICATOR_SHOW_DURATION:
520       {
521         value = scrollBarImpl.GetIndicatorShowDuration();
522         break;
523       }
524       case Toolkit::ScrollBar::PROPERTY_INDICATOR_HIDE_DURATION:
525       {
526         value = scrollBarImpl.GetIndicatorHideDuration();
527         break;
528       }
529     }
530   }
531   return value;
532 }
533
534 Toolkit::ScrollBar ScrollBar::New()
535 {
536   // Create the implementation, temporarily owned by this handle on stack
537   IntrusivePtr< ScrollBar > impl = new ScrollBar();
538
539   // Pass ownership to CustomActor handle
540   Toolkit::ScrollBar handle( *impl );
541
542   // Second-phase init of the implementation
543   // This can only be done after the CustomActor connection has been made...
544   impl->Initialize();
545
546   return handle;
547 }
548
549 } // namespace Internal
550
551 } // namespace Toolkit
552
553 } // namespace Dali