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-component/scroll-bar-internal-impl.h>
22 #include <dali/public-api/animation/active-constraint.h>
23 #include <dali/public-api/animation/constraint.h>
24 #include <dali/public-api/object/property-input.h>
25 #include <dali/public-api/object/type-registry.h>
26 #include <dali/public-api/object/type-registry-helper.h>
27 #include <dali/public-api/images/resource-image.h>
30 #include <dali-toolkit/public-api/enums.h>
39 * @param[in] x Input value to be squared
40 * @return Result (x*x)
48 const char* BAR_TAB_IMAGE_PATH = DALI_IMAGE_DIR "popup_scroll.png";
49 const Vector4 BAR_TAB_NINE_PATCH_BORDER(0.0f, 12.0f, 14.0f, 14.0f);
50 const Vector3 BAR_TAB_SIZE(18.0f, 72.0f, 0.0f);
51 const Vector3 BAR_TAB_OFFSET_V(-18.0f, 0.0f, 0.1f);
52 const Vector3 BAR_TAB_OFFSET_H(0.0f, -18.0f, 0.1f);
53 const float BAR_CONTRACT_DELAY(0.8f);
54 const float BAR_SHOW_TIME(0.4f);
55 const float BAR_HIDE_TIME(0.5f);
56 const int SECOND_UNIT(1000);
59 * ScrollBarInternal Visibility Constraint
60 * Returns whether scroll bar is visible
62 bool ScrollBarInternalVisibilityConstraint(const bool& current,
63 const PropertyInput& canScrollProperty)
65 bool canScroll = canScrollProperty.GetBoolean();
70 * ScrollBarInternal Size Constraint
71 * Resize ScrollBarInternal Size depends on both ScrollSize and DomainSize
73 struct ScrollBarInternalSizeConstraint
76 * @param[in] vertical Whether this constraint controls a vertical scrollbar (true)
77 * or a horizontal one (false)
79 ScrollBarInternalSizeConstraint(bool vertical)
86 * @param[in] current The current ScrollBarInternal size
87 * @param[in] scrollMinProperty The container's minimum position.
88 * @param[in] scrollMaxProperty The container's maximum position.
89 * @param[in] scrollDirectionProperty The container's scroll direction.
90 * @param[in] scrollSizeProperty The container's size of viewport.
91 * @return The new ScrollBarInternal position is returned.
93 Vector3 operator()(const Vector3& current,
94 const PropertyInput& scrollMinProperty,
95 const PropertyInput& scrollMaxProperty,
96 const PropertyInput& scrollDirectionProperty,
97 const PropertyInput& scrollSizeProperty)
99 const Vector3& min = scrollMinProperty.GetVector3();
100 const Vector3& max = scrollMaxProperty.GetVector3();
101 const Vector3& scrollDirection = scrollDirectionProperty.GetVector3();
102 const Toolkit::ControlOrientation::Type& orientation = static_cast<Toolkit::ControlOrientation::Type>(scrollDirection.z);
103 const Vector3& size = scrollSizeProperty.GetVector3();
104 const Vector3 domainSize = max - min;
106 if (mVertical && Toolkit::IsVertical(orientation))
108 float mod = fabsf(domainSize.height) > size.height ? size.height * ( size.height / fabsf(domainSize.height) ) : size.height * ( (size.height - fabsf(domainSize.height * 0.5f)) / size.height);
109 return Vector3( current.width, mod, current.depth );
113 float mod = fabsf(domainSize.height) > size.width ? size.width * ( size.width / fabsf(domainSize.height) ) : size.width * ( (size.width - fabsf(domainSize.height * 0.5f)) / size.width);
114 return Vector3( current.width, mod, current.depth );
118 bool mVertical; ///< Whether vertical or horizontal
122 * ScrollBarInternal rotation Constraint
123 * Rotate ScrollBarInternal depends on the scroll direction
125 struct ScrollBarInternalRotationConstraint
128 * @param[in] vertical Whether this constraint controls a vertical scrollbar (true)
129 * or a horizontal one (false)
131 ScrollBarInternalRotationConstraint(bool vertical)
132 : mVertical(vertical)
137 * Constraint operator
138 * @param[in] current The current ScrollBarInternal rotation
139 * @param[in] scrollDirectionProperty The container's scroll direction.
140 * @return The new ScrollBarInternal rotation is returned.
142 Quaternion operator()(const Quaternion& current,
143 const PropertyInput& scrollDirectionProperty)
145 const Vector3& scrollDirection = scrollDirectionProperty.GetVector3();
146 const Toolkit::ControlOrientation::Type& orientation = static_cast<Toolkit::ControlOrientation::Type>(scrollDirection.z);
148 if( (mVertical && Toolkit::IsVertical(orientation)) || (!mVertical && Toolkit::IsHorizontal(orientation)) )
150 return Quaternion(0.0f, Vector3::ZAXIS);
154 return Quaternion(0.5f * Math::PI, Vector3::ZAXIS);
158 bool mVertical; ///< Whether vertical or horizontal
162 * ScrollBarInternal Position Constraint
163 * Positions the scroll bar to reflect the current scroll position
166 struct ScrollBarInternalPositionConstraint
169 * @param[in] vertical Whether this constraint controls a vertical scrollbar (true)
170 * or a horizontal one (false)
171 * @param[in] wrap Whether to base scrollbar on original position or wrapped position
173 ScrollBarInternalPositionConstraint(bool vertical, bool wrap = false)
174 : mVertical(vertical),
180 * Constraint operator
181 * @param[in] current The current ScrollBarInternal position
182 * @param[in] scrollBarSizeProperty ScrollBarInternal size
183 * @param[in] scrollRelativePositionProperty The container's relative position (from 0.0 -> 1.0 in each axis)
184 * @param[in] scrollMinProperty The container's minimum position.
185 * @param[in] scrollMaxProperty The container's maximum position.
186 * @param[in] scrollDirectionProperty The container's scroll direction.
187 * @param[in] scrollSizeProperty The container's size of viewport.
188 * @return The new ScrollBarInternal position is returned.
190 Vector3 operator()(const Vector3& current,
191 const PropertyInput& scrollBarSizeProperty,
192 const PropertyInput& scrollRelativePositionProperty,
193 const PropertyInput& scrollMinProperty,
194 const PropertyInput& scrollMaxProperty,
195 const PropertyInput& scrollDirectionProperty,
196 const PropertyInput& scrollSizeProperty)
198 Vector3 barSize = scrollBarSizeProperty.GetVector3();
199 Vector3 relativePosition = scrollRelativePositionProperty.GetVector3();
200 Vector3 size = scrollSizeProperty.GetVector3();
201 const Vector3& min = scrollMinProperty.GetVector3();
202 const Vector3& max = scrollMaxProperty.GetVector3();
203 const Vector3& scrollDirection = scrollDirectionProperty.GetVector3();
204 const Toolkit::ControlOrientation::Type& orientation = static_cast<Toolkit::ControlOrientation::Type>(scrollDirection.z);
206 Vector3 domainSize = max - min;
207 domainSize.x = fabsf(domainSize.x);
208 domainSize.y = fabsf(domainSize.y);
211 Vector3 mask; // Mask movement aspect of scroll bar
212 Vector3 relativeOffset; // base position of scroll bar in relation to the container
213 Vector3 absoluteOffset; // absolute offset position of scroll bar
219 case Toolkit::ControlOrientation::Up:
221 mask = Vector3::YAXIS;
222 relativeOffset = (scrollDirection.y < 0.0f && relativePosition.y <= 0.0f) ? Vector3(1.0f, 1.0f, 0.0f) : Vector3(1.0f, 0.0f, 0.0f); // Right side of stage.
223 absoluteOffset = (scrollDirection.y < 0.0f && relativePosition.y <= 0.0f) ? BAR_TAB_OFFSET_V + Vector3( barSize.width * 0.5f, -barSize.height * 0.5f, 1.0f ) : BAR_TAB_OFFSET_V + Vector3( barSize.width * 0.5f, barSize.height * 0.5f, 1.0f );
226 case Toolkit::ControlOrientation::Left:
228 mask = Vector3::XAXIS;
229 relativeOffset = (scrollDirection.x <= 0.0f && relativePosition.y <= 0.0f) ? Vector3(1.0f, 0.0f, 0.0f) : Vector3(0.0f, 0.0f, 0.0f); // Bottom side of stage.
230 absoluteOffset = (scrollDirection.x <= 0.0f && relativePosition.y <= 0.0f) ? Vector3( -barSize.height * 0.5f, barSize.width * 0.5f, 1.0f ) : Vector3( barSize.height * 0.5f, barSize.width * 0.5f, 1.0f );
233 case Toolkit::ControlOrientation::Down:
235 mask = Vector3::YAXIS;
236 relativeOffset = (scrollDirection.y <= 0.0f && relativePosition.y <= 0.0f) ? Vector3(0.0f, 1.0f, 0.0f) : Vector3(0.0f, 0.0f, 0.0f); // Left side of stage.
237 absoluteOffset = (scrollDirection.y <= 0.0f && relativePosition.y <= 0.0f) ? Vector3( barSize.width * 0.5f, -barSize.height * 0.5f, 1.0f ) : Vector3( barSize.width * 0.5f, barSize.height * 0.5f, 1.0f );
240 case Toolkit::ControlOrientation::Right:
242 mask = Vector3::XAXIS;
243 relativeOffset = (scrollDirection.x <= 0.0f && relativePosition.y <= 0.0f) ? Vector3(1.0f, 1.0f, 0.0f) : Vector3(0.0f, 1.0f, 0.0f); // Up side of stage.
244 absoluteOffset = (scrollDirection.x <= 0.0f && relativePosition.y <= 0.0f) ? Vector3( -barSize.height * 0.5f, -barSize.width * 0.5f, 1.0f ) : Vector3( barSize.height * 0.5f, -barSize.width * 0.5f, 1.0f );
251 mask = Vector3::XAXIS;
252 relativeOffset = Vector3(0.0f, 1.0f, 0.0f); // Bottom side of stage.
253 absoluteOffset = BAR_TAB_OFFSET_H + Vector3( barSize.height * 0.5f, barSize.width * 0.5f, 1.0f );
256 Vector3 maskedRelativePosition = Toolkit::IsVertical(orientation) ? Vector3(relativePosition.x * (size.x-barSize.y), relativePosition.y * (size.y-barSize.y), 0.0f) * mask
257 : Vector3(relativePosition.y * (size.x-barSize.y), relativePosition.x * (size.y-barSize.y), 0.0f) * mask;
259 Vector3 finalPosition = relativeOffset * size + absoluteOffset + maskedRelativePosition;
261 // If Wrapped Slider, then position 1 domain either before or after current slider.
264 if(finalPosition.x < 0.5f)
266 finalPosition.x += size.x;
270 finalPosition.x -= size.x;
273 if(finalPosition.y < 0.5f)
275 finalPosition.y += size.y;
279 finalPosition.y -= size.y;
283 return finalPosition;
286 bool mVertical; ///< Whether vertical or horizontal.
287 bool mWrap; ///< Whether to wrap this position.
291 * ScrollBarInternal HitSize Constraint
292 * Resizes HitArea to size of the container.
294 struct ScrollBarInternalHitSizeConstraint
297 * @param[in] vertical Whether this constraint controls a vertical scrollbar (true)
298 * or a horizontal one (false)
299 * @param[in] thickness The thickness of the scrollbar
301 ScrollBarInternalHitSizeConstraint(bool vertical,
303 : mVertical(vertical),
304 mThickness(thickness)
309 * Constraint operator
310 * @param[in] current The current HitSize
311 * @param[in] scrollDirectionProperty The container's scroll direction.
312 * @param[in] scrollSizeProperty The container's size of viewport.
313 * @return The new ScrollBarInternal Hit Area size is returned.
315 Vector3 operator()(const Vector3& current,
316 const PropertyInput& scrollDirectionProperty,
317 const PropertyInput& scrollSizeProperty)
319 const Vector3& scrollDirection = scrollDirectionProperty.GetVector3();
320 const Toolkit::ControlOrientation::Type& orientation = static_cast<Toolkit::ControlOrientation::Type>(scrollDirection.z);
321 Vector3 size = scrollSizeProperty.GetVector3();
323 Vector3 mask; // Mask size aspect of hit area.
324 Vector3 offset; // Add Offset size.
326 if( (mVertical && Toolkit::IsVertical(orientation)) || (!mVertical && Toolkit::IsHorizontal(orientation)) )
328 mask = Vector3::YAXIS;
329 offset = Vector3::XAXIS * mThickness;
333 mask = Vector3::XAXIS;
334 offset = Vector3::YAXIS * mThickness;
337 return size * mask + offset;
340 bool mVertical; ///< Whether vertical or horizontal.
341 float mThickness; ///< Thickness of the scroll bar
344 } // unnamed namespace
358 using namespace Dali;
365 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ScrollBarInternal, Toolkit::ScrollComponent, Create )
366 DALI_TYPE_REGISTRATION_END()
370 ScrollBarInternal::ScrollBarInternal(Toolkit::Scrollable& container, bool vertical)
371 : mContainer(static_cast<Toolkit::Internal::Scrollable&>(container.GetImplementation())),
373 mAxisMask(vertical ? Vector3::YAXIS : Vector3::XAXIS),
376 Image sliderImage = ResourceImage::New( BAR_TAB_IMAGE_PATH );
378 mSlider = ImageActor::New( sliderImage );
379 mSlider.SetParentOrigin( ParentOrigin::TOP_LEFT );
380 mSlider.SetAnchorPoint( AnchorPoint::CENTER );
381 mSlider.SetSize( BAR_TAB_SIZE );
382 mSlider.SetStyle( ImageActor::STYLE_NINE_PATCH );
383 mSlider.SetNinePatchBorder( BAR_TAB_NINE_PATCH_BORDER );
385 // A duplicate Slider should appear 1 domain away from the original Slider
386 mSliderWrap = ImageActor::New( sliderImage );
387 mSliderWrap.SetParentOrigin( ParentOrigin::TOP_LEFT );
388 mSliderWrap.SetAnchorPoint( AnchorPoint::CENTER );
389 mSliderWrap.SetSize( BAR_TAB_SIZE );
390 mSliderWrap.SetStyle( ImageActor::STYLE_NINE_PATCH );
391 mSliderWrap.SetNinePatchBorder( BAR_TAB_NINE_PATCH_BORDER );
393 // target the container to observe for scrolling
394 Actor target = mContainer.Self();
395 Constraint constraint = Constraint::New<bool>( Actor::Property::VISIBLE,
396 Source( target, vertical ? target.GetPropertyIndex(Scrollable::SCROLLABLE_CAN_SCROLL_VERTICAL) : target.GetPropertyIndex(Scrollable::SCROLLABLE_CAN_SCROLL_HORIZONTAL)),
397 ScrollBarInternalVisibilityConstraint );
398 mSlider.ApplyConstraint( constraint );
399 mSliderWrap.ApplyConstraint( constraint );
401 constraint = Constraint::New<Vector3>( Actor::Property::SIZE,
402 Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_POSITION_MIN_PROPERTY_NAME ) ),
403 Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_POSITION_MAX_PROPERTY_NAME ) ),
404 Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_DIRECTION_PROPERTY_NAME ) ),
405 Source( target, Actor::Property::SIZE ),
406 ScrollBarInternalSizeConstraint( vertical ) );
407 mSlider.ApplyConstraint( constraint );
408 mSliderWrap.ApplyConstraint( constraint );
410 constraint = Constraint::New<Quaternion>( Actor::Property::ROTATION,
411 Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_DIRECTION_PROPERTY_NAME ) ),
412 ScrollBarInternalRotationConstraint( vertical ) );
413 mSlider.ApplyConstraint( constraint );
414 mSliderWrap.ApplyConstraint( constraint );
416 constraint = Constraint::New<Vector3>( Actor::Property::POSITION,
417 Source( mSlider, Actor::Property::SIZE),
418 Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_RELATIVE_POSITION_PROPERTY_NAME ) ),
419 Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_POSITION_MIN_PROPERTY_NAME ) ),
420 Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_POSITION_MAX_PROPERTY_NAME ) ),
421 Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_DIRECTION_PROPERTY_NAME ) ),
422 Source( target, Actor::Property::SIZE ),
423 ScrollBarInternalPositionConstraint(vertical) );
425 mSlider.ApplyConstraint( constraint );
427 constraint = Constraint::New<Vector3>( Actor::Property::POSITION,
428 Source( mSlider, Actor::Property::SIZE),
429 Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_RELATIVE_POSITION_PROPERTY_NAME ) ),
430 Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_POSITION_MIN_PROPERTY_NAME ) ),
431 Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_POSITION_MAX_PROPERTY_NAME ) ),
432 Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_DIRECTION_PROPERTY_NAME ) ),
433 Source( target, Actor::Property::SIZE ),
434 ScrollBarInternalPositionConstraint(vertical, true) );
435 mSliderWrap.ApplyConstraint( constraint );
437 // Add Sliders to internal Actor, to avoid mixing up with regular
438 // Actors added by user.
439 mContainer.AddOverlay( mSlider );
440 mContainer.AddOverlay( mSliderWrap );
441 mContainer.ScrollStartedSignal().Connect( this, &ScrollBarInternal::OnStarted );
442 mContainer.ScrollCompletedSignal().Connect( this, &ScrollBarInternal::OnCompleted );
444 // Hit Area for dragging slider /////////////////////////////////////////////
445 mHitArea = Actor::New();
446 mHitArea.SetPosition(0.0f, 0.0f, 0.2f);
448 mContainer.AddOverlay( mHitArea );
449 constraint = Constraint::New<Vector3>( Actor::Property::SIZE,
450 Source( target, target.GetPropertyIndex( Toolkit::Scrollable::SCROLL_DIRECTION_PROPERTY_NAME ) ),
451 Source( target, Actor::Property::SIZE ),
452 ScrollBarInternalHitSizeConstraint(vertical, BAR_TAB_SIZE.width) );
453 mHitArea.ApplyConstraint( constraint );
457 mHitArea.SetParentOrigin(ParentOrigin::CENTER_RIGHT);
458 mHitArea.SetAnchorPoint(AnchorPoint::CENTER_RIGHT);
462 mHitArea.SetParentOrigin(ParentOrigin::BOTTOM_CENTER);
463 mHitArea.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
466 WaitingContractDelay();
469 ScrollBarInternal::~ScrollBarInternal()
474 void ScrollBarInternal::OnInitialize()
476 EnableGestureDetection(Gesture::Type(Gesture::Pan));
479 void ScrollBarInternal::OnDisconnect()
481 // Disconnect all connected callback functions.
482 mContainer.RemoveOverlay( mSlider );
483 mContainer.RemoveOverlay( mSliderWrap );
486 void ScrollBarInternal::OnPanGesture(Actor actor, PanGesture gesture)
488 switch(gesture.state)
490 case Gesture::Started:
494 mScrollStart = mContainer.GetCurrentScrollPosition();
495 mGestureDisplacement = Vector3::ZERO;
498 case Gesture::Continuing:
500 Vector3 delta(gesture.displacement.x, gesture.displacement.y, 0.0f);
501 mGestureDisplacement+=delta;
503 Vector3 size = mContainer.Self().GetCurrentSize();
504 Vector3 span = size - Vector3(BAR_TAB_SIZE.y, BAR_TAB_SIZE.y, 1.0f);
505 Vector3 domainSize = mContainer.GetDomainSize();
507 Vector3 position = mScrollStart + mGestureDisplacement * mAxisMask * domainSize / span;
508 mContainer.ScrollTo(position, 0.0f);
519 void ScrollBarInternal::OnStarted(const Vector3& position)
521 // TODO: Need to disable this for the scrollbar which isn't being scrolled.
529 void ScrollBarInternal::OnCompleted(const Vector3& position)
535 WaitingContractDelay();
539 bool ScrollBarInternal::OnContractDelayExpired()
551 void ScrollBarInternal::Show()
553 // Cancel any animation
560 mAnimation = Animation::New( BAR_SHOW_TIME );
561 mAnimation.OpacityTo( mSlider, 1.0f, AlphaFunctions::EaseIn );
562 mAnimation.OpacityTo( mSliderWrap, 1.0f, AlphaFunctions::EaseIn );
568 void ScrollBarInternal::Hide()
570 // Cancel any animation
577 mAnimation = Animation::New( BAR_HIDE_TIME );
578 mAnimation.OpacityTo( mSlider, 0.0f, AlphaFunctions::EaseIn );
579 mAnimation.OpacityTo( mSliderWrap, 0.0f, AlphaFunctions::EaseIn );
583 void ScrollBarInternal::CreateTimer()
587 // Create timer for contract delay
588 mTimer = Timer::New( BAR_CONTRACT_DELAY * SECOND_UNIT );
589 mTimer.TickSignal().Connect( this, &ScrollBarInternal::OnContractDelayExpired );
593 void ScrollBarInternal::DestructTimer()
598 mTimer.TickSignal().Disconnect( this, &ScrollBarInternal::OnContractDelayExpired );
603 void ScrollBarInternal::WaitingContractDelay()
609 Toolkit::ScrollBarInternal ScrollBarInternal::New(Toolkit::Scrollable& container, bool vertical)
611 // Create the implementation, temporarily owned by this handle on stack
612 IntrusivePtr< ScrollBarInternal > impl = new ScrollBarInternal( container, vertical );
614 // Pass ownership to CustomActor handle
615 Toolkit::ScrollBarInternal handle( *impl );
617 // Second-phase init of the implementation
618 // This can only be done after the CustomActor connection has been made...
624 } // namespace Internal
626 } // namespace Toolkit