2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/controls/scroll-bar/scroll-bar-impl.h>
22 #include <cstring> // for strcmp
23 #include <dali/public-api/animation/constraint.h>
24 #include <dali/public-api/animation/constraints.h>
25 #include <dali/public-api/images/resource-image.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/public-api/object/property-array.h>
28 #include <dali/devel-api/object/type-registry-helper.h>
29 #include <dali/integration-api/debug.h>
33 #include <dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h>
40 const char* DEFAULT_INDICATOR_IMAGE_PATH = DALI_IMAGE_DIR "popup_scroll.png";
41 const Vector4 DEFAULT_INDICATOR_NINE_PATCH_BORDER(4.0f, 9.0f, 7.0f, 11.0f);
42 const float MINIMUM_INDICATOR_HEIGHT(20.0f); // The minimum indicator height for the nine patch border
43 const float DEFAULT_SLIDER_DEPTH(1.0f);
44 const float DEFAULT_INDICATOR_SHOW_DURATION(0.5f);
45 const float DEFAULT_INDICATOR_HIDE_DURATION(0.5f);
46 const float DEFAULT_PAN_GESTURE_PROCESS_TIME(16.7f); // 16.7 milliseconds, i.e. one frame
47 const float DEFAULT_INDICATOR_FIXED_HEIGHT(80.0f);
50 * Indicator size constraint
51 * Indicator size depends on both indicator's parent size and the scroll content size
53 struct IndicatorSizeConstraint
55 IndicatorSizeConstraint()
61 * @param[in] current The current indicator size
62 * @param[in] parentSizeProperty The parent size of scroll indicator.
63 * @return The new scroll indicator size.
65 void operator()(Vector3& current, const PropertyInputContainer& inputs )
67 const Vector3& parentSize = inputs[0]->GetVector3();
68 const float contentSize = inputs[1]->GetFloat();
70 float height = contentSize > parentSize.height ?
71 parentSize.height * ( parentSize.height / contentSize ) :
72 parentSize.height * ( (parentSize.height - contentSize * 0.5f) / parentSize.height);
74 current.y = std::max(MINIMUM_INDICATOR_HEIGHT, height);
79 * Indicator position constraint
80 * Positions the indicator to reflect the current scroll position within the scroll domain.
82 struct IndicatorPositionConstraint
85 * @param[in] minPosition The minimum limit of scroll position
86 * @param[in] maxPosition the maximum limit of scroll position
88 IndicatorPositionConstraint()
94 * @param[in,out] current The current indicator position
95 * @param[in] inputs Contains the size of indicator, the size of indicator's parent, and the scroll position of the scrollable container (from 0.0 -> 1.0 in each axis)
96 * @return The new indicator position is returned.
98 void operator()( Vector3& current, const PropertyInputContainer& inputs )
100 const Vector3& indicatorSize = inputs[0]->GetVector3();
101 const Vector3& parentSize = inputs[1]->GetVector3();
102 const float scrollPosition = -inputs[2]->GetFloat();
103 const float minScrollPosition = inputs[3]->GetFloat();
104 const float maxScrollPosition = inputs[4]->GetFloat();
106 float relativePosition = std::max( 0.0f, std::min( 1.0f, (scrollPosition - minScrollPosition) / (maxScrollPosition - minScrollPosition) ) );
107 current.y = ( parentSize.height - indicatorSize.height ) * relativePosition;
108 current.z = DEFAULT_SLIDER_DEPTH;
112 } // unnamed namespace
126 using namespace Dali;
130 return Toolkit::ScrollBar::New();
133 // Setup properties, signals and actions using the type-registry.
134 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ScrollBar, Toolkit::Control, Create );
136 DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "scroll-direction", STRING, SCROLL_DIRECTION )
137 DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicator-height-policy", STRING, INDICATOR_HEIGHT_POLICY )
138 DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicator-fixed-height", FLOAT, INDICATOR_FIXED_HEIGHT )
139 DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicator-show-duration", FLOAT, INDICATOR_SHOW_DURATION )
140 DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicator-hide-duration", FLOAT, INDICATOR_HIDE_DURATION )
141 DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "scroll-position-intervals", ARRAY, SCROLL_POSITION_INTERVALS )
143 DALI_SIGNAL_REGISTRATION( Toolkit, ScrollBar, "pan-finished", PAN_FINISHED_SIGNAL )
144 DALI_SIGNAL_REGISTRATION( Toolkit, ScrollBar, "scroll-position-interval-reached", SCROLL_POSITION_INTERVAL_REACHED_SIGNAL )
146 DALI_TYPE_REGISTRATION_END()
148 const char* SCROLL_DIRECTION_NAME[] = {"Vertical", "Horizontal"};
149 const char* INDICATOR_HEIGHT_POLICY_NAME[] = {"Variable", "Fixed"};
153 ScrollBar::ScrollBar(Toolkit::ScrollBar::Direction direction)
154 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
155 mIndicatorShowAlpha(1.0f),
156 mDirection(direction),
157 mScrollableObject(WeakHandleBase()),
158 mPropertyScrollPosition(Property::INVALID_INDEX),
159 mPropertyMinScrollPosition(Property::INVALID_INDEX),
160 mPropertyMaxScrollPosition(Property::INVALID_INDEX),
161 mPropertyScrollContentSize(Property::INVALID_INDEX),
162 mIndicatorShowDuration(DEFAULT_INDICATOR_SHOW_DURATION),
163 mIndicatorHideDuration(DEFAULT_INDICATOR_HIDE_DURATION),
165 mCurrentScrollPosition(0.0f),
166 mIndicatorHeightPolicy(Toolkit::ScrollBar::Variable),
167 mIndicatorFixedHeight(DEFAULT_INDICATOR_FIXED_HEIGHT),
169 mIndicatorFirstShow(true)
173 ScrollBar::~ScrollBar()
177 void ScrollBar::OnInitialize()
179 CreateDefaultIndicatorActor();
180 Self().SetDrawMode(DrawMode::OVERLAY_2D);
183 void ScrollBar::SetScrollPropertySource( Handle handle, Property::Index propertyScrollPosition, Property::Index propertyMinScrollPosition, Property::Index propertyMaxScrollPosition, Property::Index propertyScrollContentSize )
186 && propertyScrollPosition != Property::INVALID_INDEX
187 && propertyMinScrollPosition != Property::INVALID_INDEX
188 && propertyMaxScrollPosition != Property::INVALID_INDEX
189 && propertyScrollContentSize != Property::INVALID_INDEX )
191 mScrollableObject = WeakHandleBase(handle);
192 mPropertyScrollPosition = propertyScrollPosition;
193 mPropertyMinScrollPosition = propertyMinScrollPosition;
194 mPropertyMaxScrollPosition = propertyMaxScrollPosition;
195 mPropertyScrollContentSize = propertyScrollContentSize;
201 DALI_LOG_ERROR("Can not set empty handle of source object or invalid source property index\n");
205 void ScrollBar::CreateDefaultIndicatorActor()
207 Image indicatorImage = ResourceImage::New( DEFAULT_INDICATOR_IMAGE_PATH );
208 ImageActor indicator = ImageActor::New( indicatorImage );
209 indicator.SetNinePatchBorder( DEFAULT_INDICATOR_NINE_PATCH_BORDER );
210 indicator.SetStyle( ImageActor::STYLE_NINE_PATCH );
211 indicator.SetParentOrigin( ParentOrigin::TOP_LEFT );
212 indicator.SetAnchorPoint( AnchorPoint::TOP_LEFT );
214 SetScrollIndicator(indicator);
217 void ScrollBar::SetScrollIndicator( Actor indicator )
219 // Don't allow empty handle
222 mIndicator = indicator;
223 mIndicatorFirstShow = true;
224 Self().Add(mIndicator);
226 EnableGestureDetection(Gesture::Type(Gesture::Pan));
228 PanGestureDetector detector( GetPanGestureDetector() );
229 detector.DetachAll();
230 detector.Attach( mIndicator );
232 unsigned int childCount = mIndicator.GetChildCount();
233 for ( unsigned int index = 0; index < childCount; index++ )
235 Actor child = mIndicator.GetChildAt( index );
238 detector.Attach( child );
244 DALI_LOG_ERROR("Empty handle of scroll indicator\n");
248 Actor ScrollBar::GetScrollIndicator()
253 void ScrollBar::ApplyConstraints()
255 Handle scrollableHandle = mScrollableObject.GetBaseHandle();
257 if( scrollableHandle )
259 if(mIndicatorSizeConstraint)
261 mIndicatorSizeConstraint.Remove();
264 // Set indicator height according to the indicator's height policy
265 if(mIndicatorHeightPolicy == Toolkit::ScrollBar::Fixed)
267 mIndicator.SetSize(Self().GetCurrentSize().width, mIndicatorFixedHeight);
271 mIndicatorSizeConstraint = Constraint::New<Vector3>( mIndicator, Actor::Property::SIZE, IndicatorSizeConstraint() );
272 mIndicatorSizeConstraint.AddSource( ParentSource( Actor::Property::SIZE ) );
273 mIndicatorSizeConstraint.AddSource( Source( scrollableHandle, mPropertyScrollContentSize ) );
274 mIndicatorSizeConstraint.Apply();
277 if(mIndicatorPositionConstraint)
279 mIndicatorPositionConstraint.Remove();
282 mIndicatorPositionConstraint = Constraint::New<Vector3>( mIndicator, Actor::Property::POSITION, IndicatorPositionConstraint() );
283 mIndicatorPositionConstraint.AddSource( LocalSource( Actor::Property::SIZE ) );
284 mIndicatorPositionConstraint.AddSource( ParentSource( Actor::Property::SIZE ) );
285 mIndicatorPositionConstraint.AddSource( Source( scrollableHandle, mPropertyScrollPosition ) );
286 mIndicatorPositionConstraint.AddSource( Source( scrollableHandle, mPropertyMinScrollPosition ) );
287 mIndicatorPositionConstraint.AddSource( Source( scrollableHandle, mPropertyMaxScrollPosition ) );
288 mIndicatorPositionConstraint.Apply();
292 void ScrollBar::SetScrollPositionIntervals( const Dali::Vector<float>& positions )
294 mScrollPositionIntervals = positions;
296 Handle scrollableHandle = mScrollableObject.GetBaseHandle();
298 if( scrollableHandle )
300 if( mPositionNotification )
302 scrollableHandle.RemovePropertyNotification(mPositionNotification);
305 mPositionNotification = scrollableHandle.AddPropertyNotification( mPropertyScrollPosition, VariableStepCondition(mScrollPositionIntervals) );
306 mPositionNotification.NotifySignal().Connect( this, &ScrollBar::OnScrollPositionIntervalReached );
310 Dali::Vector<float> ScrollBar::GetScrollPositionIntervals() const
312 return mScrollPositionIntervals;
315 void ScrollBar::OnScrollPositionIntervalReached(PropertyNotification& source)
317 // Emit the signal to notify the scroll position crossing
318 Handle scrollableHandle = mScrollableObject.GetBaseHandle();
321 mScrollPositionIntervalReachedSignal.Emit(scrollableHandle.GetProperty<float>(mPropertyScrollPosition));
325 void ScrollBar::ShowIndicator()
327 // Cancel any animation
334 if( mIndicatorFirstShow )
336 // Preserve the alpha value from the stylesheet
337 mIndicatorShowAlpha = Self().GetCurrentColor().a;
338 mIndicatorFirstShow = false;
341 if(mIndicatorShowDuration > 0.0f)
343 mAnimation = Animation::New( mIndicatorShowDuration );
344 mAnimation.AnimateTo( Property( mIndicator, Actor::Property::COLOR_ALPHA ), mIndicatorShowAlpha, AlphaFunction::EASE_IN );
349 mIndicator.SetOpacity(mIndicatorShowAlpha);
353 void ScrollBar::HideIndicator()
355 // Cancel any animation
362 if(mIndicatorHideDuration > 0.0f)
364 mAnimation = Animation::New( mIndicatorHideDuration );
365 mAnimation.AnimateTo( Property( mIndicator, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunction::EASE_IN );
370 mIndicator.SetOpacity(0.0f);
374 bool ScrollBar::OnPanGestureProcessTick()
376 // Update the scroll position property.
377 Handle scrollableHandle = mScrollableObject.GetBaseHandle();
378 if( scrollableHandle )
380 scrollableHandle.SetProperty(mPropertyScrollPosition, mCurrentScrollPosition);
386 void ScrollBar::OnPan( const PanGesture& gesture )
388 Handle scrollableHandle = mScrollableObject.GetBaseHandle();
392 Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast(scrollableHandle);
394 switch(gesture.state)
396 case Gesture::Started:
398 if( !mPanProcessTimer )
400 // Make sure the pan gesture is only being processed once per frame.
401 mPanProcessTimer = Timer::New( DEFAULT_PAN_GESTURE_PROCESS_TIME );
402 mPanProcessTimer.TickSignal().Connect( this, &ScrollBar::OnPanGestureProcessTick );
403 mPanProcessTimer.Start();
407 mScrollStart = scrollableHandle.GetProperty<float>(mPropertyScrollPosition);
408 mGestureDisplacement = Vector3::ZERO;
413 case Gesture::Continuing:
415 Vector3 delta(gesture.displacement.x, gesture.displacement.y, 0.0f);
416 mGestureDisplacement+=delta;
418 Vector3 span = Self().GetCurrentSize() - mIndicator.GetCurrentSize();
419 float minScrollPosition = scrollableHandle.GetProperty<float>(mPropertyMinScrollPosition);
420 float maxScrollPosition = scrollableHandle.GetProperty<float>(mPropertyMaxScrollPosition);
421 float domainSize = maxScrollPosition - minScrollPosition;
423 mCurrentScrollPosition = mScrollStart - mGestureDisplacement.y * domainSize / span.y;
424 mCurrentScrollPosition = 0.0f - std::min(maxScrollPosition, std::max(-mCurrentScrollPosition, minScrollPosition));
432 if( mPanProcessTimer )
434 // Destroy the timer when pan gesture is finished.
435 mPanProcessTimer.Stop();
436 mPanProcessTimer.TickSignal().Disconnect( this, &ScrollBar::OnPanGestureProcessTick );
437 mPanProcessTimer.Reset();
442 // Refresh the ItemView cache with extra items
443 GetImpl(itemView).DoRefresh(mCurrentScrollPosition, true);
446 mPanFinishedSignal.Emit();
454 // Disable automatic refresh in ItemView during fast scrolling
455 GetImpl(itemView).SetRefreshEnabled(!mIsPanning);
460 void ScrollBar::OnSizeSet( const Vector3& size )
462 if(mIndicatorHeightPolicy == Toolkit::ScrollBar::Fixed)
464 mIndicator.SetSize(size.width, mIndicatorFixedHeight);
468 void ScrollBar::SetScrollDirection( Toolkit::ScrollBar::Direction direction )
470 mDirection = direction;
473 Toolkit::ScrollBar::Direction ScrollBar::GetScrollDirection() const
478 void ScrollBar::SetIndicatorHeightPolicy( Toolkit::ScrollBar::IndicatorHeightPolicy policy )
480 mIndicatorHeightPolicy = policy;
484 Toolkit::ScrollBar::IndicatorHeightPolicy ScrollBar::GetIndicatorHeightPolicy() const
486 return mIndicatorHeightPolicy;
489 void ScrollBar::SetIndicatorFixedHeight( float height )
491 mIndicatorFixedHeight = height;
493 if(mIndicatorHeightPolicy == Toolkit::ScrollBar::Fixed)
495 mIndicator.SetSize(Self().GetCurrentSize().width, mIndicatorFixedHeight);
499 float ScrollBar::GetIndicatorFixedHeight() const
501 return mIndicatorFixedHeight;
504 void ScrollBar::SetIndicatorShowDuration( float durationSeconds )
506 mIndicatorShowDuration = durationSeconds;
509 float ScrollBar::GetIndicatorShowDuration() const
511 return mIndicatorShowDuration;
514 void ScrollBar::SetIndicatorHideDuration( float durationSeconds )
516 mIndicatorHideDuration = durationSeconds;
519 float ScrollBar::GetIndicatorHideDuration() const
521 return mIndicatorHideDuration;
524 void ScrollBar::OnScrollDirectionPropertySet( Property::Value propertyValue )
526 std::string directionName( propertyValue.Get<std::string>() );
527 if(directionName == "Vertical")
529 SetScrollDirection(Toolkit::ScrollBar::Vertical);
531 else if(directionName == "Horizontal")
533 SetScrollDirection(Toolkit::ScrollBar::Horizontal);
537 DALI_ASSERT_ALWAYS( !"ScrollBar::OnScrollDirectionPropertySet(). Invalid Property value." );
541 void ScrollBar::OnIndicatorHeightPolicyPropertySet( Property::Value propertyValue )
543 std::string policyName( propertyValue.Get<std::string>() );
544 if(policyName == "Variable")
546 SetIndicatorHeightPolicy(Toolkit::ScrollBar::Variable);
548 else if(policyName == "Fixed")
550 SetIndicatorHeightPolicy(Toolkit::ScrollBar::Fixed);
554 DALI_ASSERT_ALWAYS( !"ScrollBar::OnIndicatorHeightPolicyPropertySet(). Invalid Property value." );
558 bool ScrollBar::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
560 Dali::BaseHandle handle( object );
562 bool connected( true );
563 Toolkit::ScrollBar scrollBar = Toolkit::ScrollBar::DownCast( handle );
565 if( 0 == strcmp( signalName.c_str(), PAN_FINISHED_SIGNAL ) )
567 scrollBar.PanFinishedSignal().Connect( tracker, functor );
569 else if( 0 == strcmp( signalName.c_str(), SCROLL_POSITION_INTERVAL_REACHED_SIGNAL ) )
571 scrollBar.ScrollPositionIntervalReachedSignal().Connect( tracker, functor );
575 // signalName does not match any signal
582 void ScrollBar::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
584 Toolkit::ScrollBar scrollBar = Toolkit::ScrollBar::DownCast( Dali::BaseHandle( object ) );
588 ScrollBar& scrollBarImpl( GetImpl( scrollBar ) );
591 case Toolkit::ScrollBar::Property::SCROLL_DIRECTION:
593 scrollBarImpl.OnScrollDirectionPropertySet( value );
596 case Toolkit::ScrollBar::Property::INDICATOR_HEIGHT_POLICY:
598 scrollBarImpl.OnIndicatorHeightPolicyPropertySet( value );
601 case Toolkit::ScrollBar::Property::INDICATOR_FIXED_HEIGHT:
603 scrollBarImpl.SetIndicatorFixedHeight(value.Get<float>());
606 case Toolkit::ScrollBar::Property::INDICATOR_SHOW_DURATION:
608 scrollBarImpl.SetIndicatorShowDuration(value.Get<float>());
611 case Toolkit::ScrollBar::Property::INDICATOR_HIDE_DURATION:
613 scrollBarImpl.SetIndicatorHideDuration(value.Get<float>());
616 case Toolkit::ScrollBar::Property::SCROLL_POSITION_INTERVALS:
618 Property::Array* array = value.GetArray();
621 Dali::Vector<float> positions;
622 size_t positionCount = array->Count();
623 positions.Resize( positionCount );
624 for( size_t i = 0; i != positionCount; ++i )
626 array->GetElementAt( i ).Get( positions[i] );
629 scrollBarImpl.SetScrollPositionIntervals(positions);
637 Property::Value ScrollBar::GetProperty( BaseObject* object, Property::Index index )
639 Property::Value value;
641 Toolkit::ScrollBar scrollBar = Toolkit::ScrollBar::DownCast( Dali::BaseHandle( object ) );
645 ScrollBar& scrollBarImpl( GetImpl( scrollBar ) );
648 case Toolkit::ScrollBar::Property::SCROLL_DIRECTION:
650 value = SCROLL_DIRECTION_NAME[ scrollBarImpl.GetScrollDirection() ];
653 case Toolkit::ScrollBar::Property::INDICATOR_HEIGHT_POLICY:
655 value = INDICATOR_HEIGHT_POLICY_NAME[ scrollBarImpl.GetIndicatorHeightPolicy() ];
658 case Toolkit::ScrollBar::Property::INDICATOR_FIXED_HEIGHT:
660 value = scrollBarImpl.GetIndicatorFixedHeight();
663 case Toolkit::ScrollBar::Property::INDICATOR_SHOW_DURATION:
665 value = scrollBarImpl.GetIndicatorShowDuration();
668 case Toolkit::ScrollBar::Property::INDICATOR_HIDE_DURATION:
670 value = scrollBarImpl.GetIndicatorHideDuration();
673 case Toolkit::ScrollBar::Property::SCROLL_POSITION_INTERVALS:
675 Property::Value value( Property::ARRAY );
676 Property::Array* array = value.GetArray();
680 Dali::Vector<float> positions = scrollBarImpl.GetScrollPositionIntervals();
681 size_t positionCount( array->Count() );
682 for( size_t i( 0 ); i != positionCount; ++i )
684 array->PushBack( positions[i] );
694 Toolkit::ScrollBar ScrollBar::New(Toolkit::ScrollBar::Direction direction)
696 // Create the implementation, temporarily owned by this handle on stack
697 IntrusivePtr< ScrollBar > impl = new ScrollBar(direction);
699 // Pass ownership to CustomActor handle
700 Toolkit::ScrollBar handle( *impl );
702 // Second-phase init of the implementation
703 // This can only be done after the CustomActor connection has been made...
709 } // namespace Internal
711 } // namespace Toolkit