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/integration-api/debug.h>
24 #include <dali/public-api/animation/constraint.h>
25 #include <dali/public-api/animation/constraints.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/devel-api/object/type-registry-helper.h>
28 #include <dali/public-api/images/resource-image.h>
31 #include <dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h>
38 const char* DEFAULT_INDICATOR_IMAGE_PATH = DALI_IMAGE_DIR "popup_scroll.png";
39 const Vector4 DEFAULT_INDICATOR_NINE_PATCH_BORDER(4.0f, 9.0f, 7.0f, 11.0f);
40 const float MINIMUM_INDICATOR_HEIGHT(20.0f); // The minimum indicator height for the nine patch border
41 const float DEFAULT_SLIDER_DEPTH(1.0f);
42 const float DEFAULT_INDICATOR_SHOW_DURATION(0.5f);
43 const float DEFAULT_INDICATOR_HIDE_DURATION(0.5f);
44 const float DEFAULT_PAN_GESTURE_PROCESS_TIME(16.7f); // 16.7 milliseconds, i.e. one frame
45 const float DEFAULT_INDICATOR_FIXED_HEIGHT(80.0f);
48 * Indicator size constraint
49 * Indicator size depends on both indicator's parent size and the scroll content size
51 struct IndicatorSizeConstraint
53 IndicatorSizeConstraint()
59 * @param[in] current The current indicator size
60 * @param[in] parentSizeProperty The parent size of scroll indicator.
61 * @return The new scroll indicator size.
63 void operator()(Vector3& current, const PropertyInputContainer& inputs )
65 const Vector3& parentSize = inputs[0]->GetVector3();
66 const float contentSize = inputs[1]->GetFloat();
68 float height = contentSize > parentSize.height ?
69 parentSize.height * ( parentSize.height / contentSize ) :
70 parentSize.height * ( (parentSize.height - contentSize * 0.5f) / parentSize.height);
72 current.y = std::max(MINIMUM_INDICATOR_HEIGHT, height);
77 * Indicator position constraint
78 * Positions the indicator to reflect the current scroll position within the scroll domain.
80 struct IndicatorPositionConstraint
83 * @param[in] minPosition The minimum limit of scroll position
84 * @param[in] maxPosition the maximum limit of scroll position
86 IndicatorPositionConstraint()
92 * @param[in,out] current The current indicator position
93 * @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)
94 * @return The new indicator position is returned.
96 void operator()( Vector3& current, const PropertyInputContainer& inputs )
98 const Vector3& indicatorSize = inputs[0]->GetVector3();
99 const Vector3& parentSize = inputs[1]->GetVector3();
100 const float scrollPosition = -inputs[2]->GetFloat();
101 const float minScrollPosition = inputs[3]->GetFloat();
102 const float maxScrollPosition = inputs[4]->GetFloat();
104 float relativePosition = std::max( 0.0f, std::min( 1.0f, (scrollPosition - minScrollPosition) / (maxScrollPosition - minScrollPosition) ) );
105 current.y = ( parentSize.height - indicatorSize.height ) * relativePosition;
106 current.z = DEFAULT_SLIDER_DEPTH;
110 } // unnamed namespace
124 using namespace Dali;
128 return Toolkit::ScrollBar::New();
131 // Setup properties, signals and actions using the type-registry.
132 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ScrollBar, Toolkit::Control, Create );
134 DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "scroll-direction", STRING, SCROLL_DIRECTION )
135 DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicator-height-policy", STRING, INDICATOR_HEIGHT_POLICY )
136 DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicator-fixed-height", FLOAT, INDICATOR_FIXED_HEIGHT )
137 DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicator-show-duration", FLOAT, INDICATOR_SHOW_DURATION )
138 DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "indicator-hide-duration", FLOAT, INDICATOR_HIDE_DURATION )
139 DALI_PROPERTY_REGISTRATION( Toolkit, ScrollBar, "scroll-position-intervals", ARRAY, SCROLL_POSITION_INTERVALS )
141 DALI_SIGNAL_REGISTRATION( Toolkit, ScrollBar, "pan-finished", PAN_FINISHED_SIGNAL )
142 DALI_SIGNAL_REGISTRATION( Toolkit, ScrollBar, "scroll-position-interval-reached", SCROLL_POSITION_INTERVAL_REACHED_SIGNAL )
144 DALI_TYPE_REGISTRATION_END()
146 const char* SCROLL_DIRECTION_NAME[] = {"Vertical", "Horizontal"};
147 const char* INDICATOR_HEIGHT_POLICY_NAME[] = {"Variable", "Fixed"};
151 ScrollBar::ScrollBar(Toolkit::ScrollBar::Direction direction)
152 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
153 mDirection(direction),
154 mScrollableObject(Handle()),
155 mPropertyScrollPosition(Property::INVALID_INDEX),
156 mPropertyMinScrollPosition(Property::INVALID_INDEX),
157 mPropertyMaxScrollPosition(Property::INVALID_INDEX),
158 mPropertyScrollContentSize(Property::INVALID_INDEX),
159 mIndicatorShowDuration(DEFAULT_INDICATOR_SHOW_DURATION),
160 mIndicatorHideDuration(DEFAULT_INDICATOR_HIDE_DURATION),
163 mCurrentScrollPosition(0.0f),
164 mIndicatorHeightPolicy(Toolkit::ScrollBar::Variable),
165 mIndicatorFixedHeight(DEFAULT_INDICATOR_FIXED_HEIGHT)
169 ScrollBar::~ScrollBar()
173 void ScrollBar::OnInitialize()
175 CreateDefaultIndicatorActor();
178 void ScrollBar::SetScrollPropertySource( Handle handle, Property::Index propertyScrollPosition, Property::Index propertyMinScrollPosition, Property::Index propertyMaxScrollPosition, Property::Index propertyScrollContentSize )
181 && propertyScrollPosition != Property::INVALID_INDEX
182 && propertyMinScrollPosition != Property::INVALID_INDEX
183 && propertyMaxScrollPosition != Property::INVALID_INDEX
184 && propertyScrollContentSize != Property::INVALID_INDEX )
186 mScrollableObject = handle;
187 mPropertyScrollPosition = propertyScrollPosition;
188 mPropertyMinScrollPosition = propertyMinScrollPosition;
189 mPropertyMaxScrollPosition = propertyMaxScrollPosition;
190 mPropertyScrollContentSize = propertyScrollContentSize;
196 DALI_LOG_ERROR("Can not set empty handle of source object or invalid source property index\n");
200 void ScrollBar::CreateDefaultIndicatorActor()
202 Image indicatorImage = ResourceImage::New( DEFAULT_INDICATOR_IMAGE_PATH );
203 ImageActor indicator = ImageActor::New( indicatorImage );
204 indicator.SetNinePatchBorder( DEFAULT_INDICATOR_NINE_PATCH_BORDER );
205 indicator.SetStyle( ImageActor::STYLE_NINE_PATCH );
206 indicator.SetParentOrigin( ParentOrigin::TOP_LEFT );
207 indicator.SetAnchorPoint( AnchorPoint::TOP_LEFT );
209 SetScrollIndicator(indicator);
212 void ScrollBar::SetScrollIndicator( Actor indicator )
214 // Don't allow empty handle
217 mIndicator = indicator;
220 self.Add(mIndicator);
222 if( !mPanGestureDetector )
224 mPanGestureDetector = PanGestureDetector::New();
225 mPanGestureDetector.DetectedSignal().Connect(this, &ScrollBar::OnPan);
228 mPanGestureDetector.DetachAll();
229 mPanGestureDetector.Attach( mIndicator );
231 unsigned int childCount = mIndicator.GetChildCount();
232 for ( unsigned int index = 0; index < childCount; index++ )
234 Actor child = mIndicator.GetChildAt( index );
237 mPanGestureDetector.Attach( child );
243 DALI_LOG_ERROR("Empty handle of scroll indicator\n");
247 Actor ScrollBar::GetScrollIndicator()
252 void ScrollBar::ApplyConstraints()
254 if( mScrollableObject )
256 if(mIndicatorSizeConstraint)
258 mIndicatorSizeConstraint.Remove();
261 // Set indicator height according to the indicator's height policy
262 if(mIndicatorHeightPolicy == Toolkit::ScrollBar::Fixed)
264 mIndicator.SetSize(Self().GetCurrentSize().width, mIndicatorFixedHeight);
268 mIndicatorSizeConstraint = Constraint::New<Vector3>( mIndicator, Actor::Property::SIZE, IndicatorSizeConstraint() );
269 mIndicatorSizeConstraint.AddSource( ParentSource( Actor::Property::SIZE ) );
270 mIndicatorSizeConstraint.AddSource( Source( mScrollableObject, mPropertyScrollContentSize ) );
271 mIndicatorSizeConstraint.Apply();
274 if(mIndicatorPositionConstraint)
276 mIndicatorPositionConstraint.Remove();
279 mIndicatorPositionConstraint = Constraint::New<Vector3>( mIndicator, Actor::Property::POSITION, IndicatorPositionConstraint() );
280 mIndicatorPositionConstraint.AddSource( LocalSource( Actor::Property::SIZE ) );
281 mIndicatorPositionConstraint.AddSource( ParentSource( Actor::Property::SIZE ) );
282 mIndicatorPositionConstraint.AddSource( Source( mScrollableObject, mPropertyScrollPosition ) );
283 mIndicatorPositionConstraint.AddSource( Source( mScrollableObject, mPropertyMinScrollPosition ) );
284 mIndicatorPositionConstraint.AddSource( Source( mScrollableObject, mPropertyMaxScrollPosition ) );
285 mIndicatorPositionConstraint.Apply();
289 void ScrollBar::SetScrollPositionIntervals( const Dali::Vector<float>& positions )
291 mScrollPositionIntervals = positions;
293 if( mScrollableObject )
295 if( mPositionNotification )
297 mScrollableObject.RemovePropertyNotification(mPositionNotification);
300 mPositionNotification = mScrollableObject.AddPropertyNotification( mPropertyScrollPosition, VariableStepCondition(mScrollPositionIntervals) );
301 mPositionNotification.NotifySignal().Connect( this, &ScrollBar::OnScrollPositionIntervalReached );
305 Dali::Vector<float> ScrollBar::GetScrollPositionIntervals() const
307 return mScrollPositionIntervals;
310 void ScrollBar::OnScrollPositionIntervalReached(PropertyNotification& source)
312 // Emit the signal to notify the scroll position crossing
313 if(mScrollableObject)
315 mScrollPositionIntervalReachedSignal.Emit(mScrollableObject.GetProperty<float>(mPropertyScrollPosition));
319 void ScrollBar::ShowIndicator()
321 // Cancel any animation
328 if(mIndicatorShowDuration > 0.0f)
330 mAnimation = Animation::New( mIndicatorShowDuration );
331 mAnimation.AnimateTo( Property( mIndicator, Actor::Property::COLOR_ALPHA ), 1.0f, AlphaFunction::EASE_IN );
336 mIndicator.SetOpacity(1.0f);
340 void ScrollBar::HideIndicator()
342 // Cancel any animation
349 if(mIndicatorHideDuration > 0.0f)
351 mAnimation = Animation::New( mIndicatorHideDuration );
352 mAnimation.AnimateTo( Property( mIndicator, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunction::EASE_IN );
357 mIndicator.SetOpacity(0.0f);
361 bool ScrollBar::OnPanGestureProcessTick()
363 // Update the scroll position property.
364 if( mScrollableObject )
366 mScrollableObject.SetProperty(mPropertyScrollPosition, mCurrentScrollPosition);
372 void ScrollBar::OnPan( Actor source, const PanGesture& gesture )
374 if(mScrollableObject)
376 Dali::Toolkit::ItemView itemView = Dali::Toolkit::ItemView::DownCast(mScrollableObject);
378 switch(gesture.state)
380 case Gesture::Started:
382 if( !mPanProcessTimer )
384 // Make sure the pan gesture is only being processed once per frame.
385 mPanProcessTimer = Timer::New( DEFAULT_PAN_GESTURE_PROCESS_TIME );
386 mPanProcessTimer.TickSignal().Connect( this, &ScrollBar::OnPanGestureProcessTick );
387 mPanProcessTimer.Start();
391 mScrollStart = mScrollableObject.GetProperty<float>(mPropertyScrollPosition);
392 mGestureDisplacement = Vector3::ZERO;
397 case Gesture::Continuing:
399 Vector3 delta(gesture.displacement.x, gesture.displacement.y, 0.0f);
400 mGestureDisplacement+=delta;
402 Vector3 span = Self().GetCurrentSize() - mIndicator.GetCurrentSize();
403 float minScrollPosition = mScrollableObject.GetProperty<float>(mPropertyMinScrollPosition);
404 float maxScrollPosition = mScrollableObject.GetProperty<float>(mPropertyMaxScrollPosition);
405 float domainSize = maxScrollPosition - minScrollPosition;
407 mCurrentScrollPosition = mScrollStart - mGestureDisplacement.y * domainSize / span.y;
408 mCurrentScrollPosition = 0.0f - std::min(maxScrollPosition, std::max(-mCurrentScrollPosition, minScrollPosition));
416 if( mPanProcessTimer )
418 // Destroy the timer when pan gesture is finished.
419 mPanProcessTimer.Stop();
420 mPanProcessTimer.TickSignal().Disconnect( this, &ScrollBar::OnPanGestureProcessTick );
421 mPanProcessTimer.Reset();
426 // Refresh the ItemView cache with extra items
427 GetImpl(itemView).DoRefresh(mCurrentScrollPosition, true);
430 mPanFinishedSignal.Emit();
438 // Disable automatic refresh in ItemView during fast scrolling
439 GetImpl(itemView).SetRefreshEnabled(true);//!mIsPanning);
444 void ScrollBar::OnSizeSet( const Vector3& size )
446 if(mIndicatorHeightPolicy == Toolkit::ScrollBar::Fixed)
448 mIndicator.SetSize(size.width, mIndicatorFixedHeight);
452 void ScrollBar::SetScrollDirection( Toolkit::ScrollBar::Direction direction )
454 mDirection = direction;
457 Toolkit::ScrollBar::Direction ScrollBar::GetScrollDirection() const
462 void ScrollBar::SetIndicatorHeightPolicy( Toolkit::ScrollBar::IndicatorHeightPolicy policy )
464 mIndicatorHeightPolicy = policy;
468 Toolkit::ScrollBar::IndicatorHeightPolicy ScrollBar::GetIndicatorHeightPolicy() const
470 return mIndicatorHeightPolicy;
473 void ScrollBar::SetIndicatorFixedHeight( float height )
475 mIndicatorFixedHeight = height;
477 if(mIndicatorHeightPolicy == Toolkit::ScrollBar::Fixed)
479 mIndicator.SetSize(Self().GetCurrentSize().width, mIndicatorFixedHeight);
483 float ScrollBar::GetIndicatorFixedHeight() const
485 return mIndicatorFixedHeight;
488 void ScrollBar::SetIndicatorShowDuration( float durationSeconds )
490 mIndicatorShowDuration = durationSeconds;
493 float ScrollBar::GetIndicatorShowDuration() const
495 return mIndicatorShowDuration;
498 void ScrollBar::SetIndicatorHideDuration( float durationSeconds )
500 mIndicatorHideDuration = durationSeconds;
503 float ScrollBar::GetIndicatorHideDuration() const
505 return mIndicatorHideDuration;
508 void ScrollBar::OnScrollDirectionPropertySet( Property::Value propertyValue )
510 std::string directionName( propertyValue.Get<std::string>() );
511 if(directionName == "Vertical")
513 SetScrollDirection(Toolkit::ScrollBar::Vertical);
515 else if(directionName == "Horizontal")
517 SetScrollDirection(Toolkit::ScrollBar::Horizontal);
521 DALI_ASSERT_ALWAYS( !"ScrollBar::OnScrollDirectionPropertySet(). Invalid Property value." );
525 void ScrollBar::OnIndicatorHeightPolicyPropertySet( Property::Value propertyValue )
527 std::string policyName( propertyValue.Get<std::string>() );
528 if(policyName == "Variable")
530 SetIndicatorHeightPolicy(Toolkit::ScrollBar::Variable);
532 else if(policyName == "Fixed")
534 SetIndicatorHeightPolicy(Toolkit::ScrollBar::Fixed);
538 DALI_ASSERT_ALWAYS( !"ScrollBar::OnIndicatorHeightPolicyPropertySet(). Invalid Property value." );
542 bool ScrollBar::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
544 Dali::BaseHandle handle( object );
546 bool connected( true );
547 Toolkit::ScrollBar scrollBar = Toolkit::ScrollBar::DownCast( handle );
549 if( 0 == strcmp( signalName.c_str(), PAN_FINISHED_SIGNAL ) )
551 scrollBar.PanFinishedSignal().Connect( tracker, functor );
553 else if( 0 == strcmp( signalName.c_str(), SCROLL_POSITION_INTERVAL_REACHED_SIGNAL ) )
555 scrollBar.ScrollPositionIntervalReachedSignal().Connect( tracker, functor );
559 // signalName does not match any signal
566 void ScrollBar::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
568 Toolkit::ScrollBar scrollBar = Toolkit::ScrollBar::DownCast( Dali::BaseHandle( object ) );
572 ScrollBar& scrollBarImpl( GetImpl( scrollBar ) );
575 case Toolkit::ScrollBar::Property::SCROLL_DIRECTION:
577 scrollBarImpl.OnScrollDirectionPropertySet( value );
580 case Toolkit::ScrollBar::Property::INDICATOR_HEIGHT_POLICY:
582 scrollBarImpl.OnIndicatorHeightPolicyPropertySet( value );
585 case Toolkit::ScrollBar::Property::INDICATOR_FIXED_HEIGHT:
587 scrollBarImpl.SetIndicatorFixedHeight(value.Get<float>());
590 case Toolkit::ScrollBar::Property::INDICATOR_SHOW_DURATION:
592 scrollBarImpl.SetIndicatorShowDuration(value.Get<float>());
595 case Toolkit::ScrollBar::Property::INDICATOR_HIDE_DURATION:
597 scrollBarImpl.SetIndicatorHideDuration(value.Get<float>());
600 case Toolkit::ScrollBar::Property::SCROLL_POSITION_INTERVALS:
602 Dali::Vector<float> positions;
603 size_t positionCount = value.GetSize();
604 positions.Resize( positionCount );
606 for( size_t i = 0; i != positionCount; ++i )
608 value.GetItem(i).Get( positions[i] );
611 scrollBarImpl.SetScrollPositionIntervals(positions);
618 Property::Value ScrollBar::GetProperty( BaseObject* object, Property::Index index )
620 Property::Value value;
622 Toolkit::ScrollBar scrollBar = Toolkit::ScrollBar::DownCast( Dali::BaseHandle( object ) );
626 ScrollBar& scrollBarImpl( GetImpl( scrollBar ) );
629 case Toolkit::ScrollBar::Property::SCROLL_DIRECTION:
631 value = SCROLL_DIRECTION_NAME[ scrollBarImpl.GetScrollDirection() ];
634 case Toolkit::ScrollBar::Property::INDICATOR_HEIGHT_POLICY:
636 value = INDICATOR_HEIGHT_POLICY_NAME[ scrollBarImpl.GetIndicatorHeightPolicy() ];
639 case Toolkit::ScrollBar::Property::INDICATOR_FIXED_HEIGHT:
641 value = scrollBarImpl.GetIndicatorFixedHeight();
644 case Toolkit::ScrollBar::Property::INDICATOR_SHOW_DURATION:
646 value = scrollBarImpl.GetIndicatorShowDuration();
649 case Toolkit::ScrollBar::Property::INDICATOR_HIDE_DURATION:
651 value = scrollBarImpl.GetIndicatorHideDuration();
654 case Toolkit::ScrollBar::Property::SCROLL_POSITION_INTERVALS:
656 Property::Value value;
657 Dali::Vector<float> positions = scrollBarImpl.GetScrollPositionIntervals();
658 size_t positionCount( positions.Size() );
659 for( size_t i( 0 ); i != positionCount; ++i )
661 value.AppendItem( positions[i] );
670 Toolkit::ScrollBar ScrollBar::New(Toolkit::ScrollBar::Direction direction)
672 // Create the implementation, temporarily owned by this handle on stack
673 IntrusivePtr< ScrollBar > impl = new ScrollBar(direction);
675 // Pass ownership to CustomActor handle
676 Toolkit::ScrollBar handle( *impl );
678 // Second-phase init of the implementation
679 // This can only be done after the CustomActor connection has been made...
685 } // namespace Internal
687 } // namespace Toolkit