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/constraint.h>
23 #include <dali/public-api/object/property-input.h>
24 #include <dali/public-api/object/type-registry.h>
25 #include <dali/public-api/object/type-registry-helper.h>
26 #include <dali/public-api/images/resource-image.h>
29 #include <dali-toolkit/public-api/enums.h>
38 * @param[in] x Input value to be squared
39 * @return Result (x*x)
47 const char* BAR_TAB_IMAGE_PATH = DALI_IMAGE_DIR "popup_scroll.png";
48 const Vector4 BAR_TAB_NINE_PATCH_BORDER(0.0f, 12.0f, 14.0f, 14.0f);
49 const Vector3 BAR_TAB_SIZE(18.0f, 72.0f, 0.0f);
50 const Vector3 BAR_TAB_OFFSET_V(-18.0f, 0.0f, 0.1f);
51 const Vector3 BAR_TAB_OFFSET_H(0.0f, -18.0f, 0.1f);
52 const float BAR_CONTRACT_DELAY(0.8f);
53 const float BAR_SHOW_TIME(0.4f);
54 const float BAR_HIDE_TIME(0.5f);
55 const int SECOND_UNIT(1000);
58 * ScrollBarInternal Visibility Constraint
59 * Returns whether scroll bar is visible
61 void ScrollBarInternalVisibilityConstraint( bool& current, const PropertyInputContainer& inputs )
63 current = inputs[0]->GetBoolean();
67 * ScrollBarInternal Size Constraint
68 * Resize ScrollBarInternal Size depends on both ScrollSize and DomainSize
70 struct ScrollBarInternalSizeConstraint
73 * @param[in] vertical Whether this constraint controls a vertical scrollbar (true)
74 * or a horizontal one (false)
76 ScrollBarInternalSizeConstraint(bool vertical)
83 * @param[in,out] current The current ScrollBarInternal size
84 * @param[in] inputs Contains the container's minimum position, its maximum position, its scroll direction & its size of viewport.
85 * @return The new ScrollBarInternal position is returned.
87 void operator()( Vector3& current, const PropertyInputContainer& inputs )
89 const Vector3& min = inputs[0]->GetVector3();
90 const Vector3& max = inputs[1]->GetVector3();
91 const Vector3& scrollDirection = inputs[2]->GetVector3();
92 const Toolkit::ControlOrientation::Type& orientation = static_cast<Toolkit::ControlOrientation::Type>(scrollDirection.z);
93 const Vector3& size = inputs[3]->GetVector3();
94 const Vector3 domainSize = max - min;
96 if (mVertical && Toolkit::IsVertical(orientation))
98 current.height = fabsf(domainSize.height) > size.height ? size.height * ( size.height / fabsf(domainSize.height) ) : size.height * ( (size.height - fabsf(domainSize.height * 0.5f)) / size.height);
102 current.height = fabsf(domainSize.height) > size.width ? size.width * ( size.width / fabsf(domainSize.height) ) : size.width * ( (size.width - fabsf(domainSize.height * 0.5f)) / size.width);
106 bool mVertical; ///< Whether vertical or horizontal
110 * ScrollBarInternal rotation Constraint
111 * Rotate ScrollBarInternal depends on the scroll direction
113 struct ScrollBarInternalRotationConstraint
116 * @param[in] vertical Whether this constraint controls a vertical scrollbar (true)
117 * or a horizontal one (false)
119 ScrollBarInternalRotationConstraint(bool vertical)
120 : mVertical(vertical)
125 * Constraint operator
126 * @param[in,out] current The current ScrollBarInternal rotation
127 * @param[in] scrollDirectionProperty The container's scroll direction.
128 * @return The new ScrollBarInternal rotation is returned.
130 void operator()( Quaternion& current, const PropertyInputContainer& inputs )
132 const Vector3& scrollDirection = inputs[0]->GetVector3();
133 const Toolkit::ControlOrientation::Type& orientation = static_cast<Toolkit::ControlOrientation::Type>(scrollDirection.z);
135 if( (mVertical && Toolkit::IsVertical(orientation)) || (!mVertical && Toolkit::IsHorizontal(orientation)) )
137 current = Quaternion(0.0f, Vector3::ZAXIS);
141 current = Quaternion(0.5f * Math::PI, Vector3::ZAXIS);
145 bool mVertical; ///< Whether vertical or horizontal
149 * ScrollBarInternal Position Constraint
150 * Positions the scroll bar to reflect the current scroll position
153 struct ScrollBarInternalPositionConstraint
156 * @param[in] vertical Whether this constraint controls a vertical scrollbar (true)
157 * or a horizontal one (false)
158 * @param[in] wrap Whether to base scrollbar on original position or wrapped position
160 ScrollBarInternalPositionConstraint(bool vertical, bool wrap = false)
161 : mVertical(vertical),
167 * Constraint operator
168 * @param[in] finalPosition The current ScrollBarInternal position
169 * @param[in] inputs Contains:
170 * The ScrollBarInternal size,
171 * The container's relative position (from 0.0 -> 1.0 in each axis),
172 * The container's minimum position,
173 * The container's maximum position,
174 * The container's scroll direction,
175 * The container's size of viewport.
176 * @return The new ScrollBarInternal position is returned.
178 void operator()( Vector3& finalPosition, const PropertyInputContainer& inputs )
180 const Vector3& barSize = inputs[0]->GetVector3();
181 const Vector3& relativePosition = inputs[1]->GetVector3();
182 const Vector3& min = inputs[2]->GetVector3();
183 const Vector3& max = inputs[3]->GetVector3();
184 const Vector3& scrollDirection = inputs[4]->GetVector3();
185 const Vector3& size = inputs[5]->GetVector3();
186 const Toolkit::ControlOrientation::Type& orientation = static_cast<Toolkit::ControlOrientation::Type>(scrollDirection.z);
188 Vector3 domainSize = max - min;
189 domainSize.x = fabsf(domainSize.x);
190 domainSize.y = fabsf(domainSize.y);
193 Vector3 mask; // Mask movement aspect of scroll bar
194 Vector3 relativeOffset; // base position of scroll bar in relation to the container
195 Vector3 absoluteOffset; // absolute offset position of scroll bar
201 case Toolkit::ControlOrientation::Up:
203 mask = Vector3::YAXIS;
204 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.
205 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 );
208 case Toolkit::ControlOrientation::Left:
210 mask = Vector3::XAXIS;
211 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.
212 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 );
215 case Toolkit::ControlOrientation::Down:
217 mask = Vector3::YAXIS;
218 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.
219 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 );
222 case Toolkit::ControlOrientation::Right:
224 mask = Vector3::XAXIS;
225 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.
226 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 mask = Vector3::XAXIS;
234 relativeOffset = Vector3(0.0f, 1.0f, 0.0f); // Bottom side of stage.
235 absoluteOffset = BAR_TAB_OFFSET_H + Vector3( barSize.height * 0.5f, barSize.width * 0.5f, 1.0f );
238 Vector3 maskedRelativePosition = Toolkit::IsVertical(orientation) ? Vector3(relativePosition.x * (size.x-barSize.y), relativePosition.y * (size.y-barSize.y), 0.0f) * mask
239 : Vector3(relativePosition.y * (size.x-barSize.y), relativePosition.x * (size.y-barSize.y), 0.0f) * mask;
241 finalPosition = relativeOffset * size + absoluteOffset + maskedRelativePosition;
243 // If Wrapped Slider, then position 1 domain either before or after current slider.
246 if(finalPosition.x < 0.5f)
248 finalPosition.x += size.x;
252 finalPosition.x -= size.x;
255 if(finalPosition.y < 0.5f)
257 finalPosition.y += size.y;
261 finalPosition.y -= size.y;
266 bool mVertical; ///< Whether vertical or horizontal.
267 bool mWrap; ///< Whether to wrap this position.
271 * ScrollBarInternal HitSize Constraint
272 * Resizes HitArea to size of the container.
274 struct ScrollBarInternalHitSizeConstraint
277 * @param[in] vertical Whether this constraint controls a vertical scrollbar (true)
278 * or a horizontal one (false)
279 * @param[in] thickness The thickness of the scrollbar
281 ScrollBarInternalHitSizeConstraint(bool vertical,
283 : mVertical(vertical),
284 mThickness(thickness)
289 * Constraint operator
290 * @param[in] current The current HitSize
291 * @param[in] inputs Contains the container's scroll direction and size of its viewport.
292 * @return The new ScrollBarInternal Hit Area size is returned.
294 void operator()( Vector3& current, const PropertyInputContainer& inputs )
296 const Vector3& scrollDirection = inputs[0]->GetVector3();
297 const Toolkit::ControlOrientation::Type& orientation = static_cast<Toolkit::ControlOrientation::Type>(scrollDirection.z);
298 const Vector3& size = inputs[1]->GetVector3();
300 Vector3 mask; // Mask size aspect of hit area.
301 Vector3 offset; // Add Offset size.
303 if( (mVertical && Toolkit::IsVertical(orientation)) || (!mVertical && Toolkit::IsHorizontal(orientation)) )
305 mask = Vector3::YAXIS;
306 offset = Vector3::XAXIS * mThickness;
310 mask = Vector3::XAXIS;
311 offset = Vector3::YAXIS * mThickness;
314 current = size * mask + offset;
317 bool mVertical; ///< Whether vertical or horizontal.
318 float mThickness; ///< Thickness of the scroll bar
321 } // unnamed namespace
335 using namespace Dali;
342 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ScrollBarInternal, Toolkit::ScrollComponent, Create )
343 DALI_TYPE_REGISTRATION_END()
347 ScrollBarInternal::ScrollBarInternal(Toolkit::Scrollable& container, bool vertical)
348 : mContainer(static_cast<Toolkit::Internal::Scrollable&>(container.GetImplementation())),
350 mAxisMask(vertical ? Vector3::YAXIS : Vector3::XAXIS),
353 Image sliderImage = ResourceImage::New( BAR_TAB_IMAGE_PATH );
355 mSlider = ImageActor::New( sliderImage );
356 mSlider.SetParentOrigin( ParentOrigin::TOP_LEFT );
357 mSlider.SetAnchorPoint( AnchorPoint::CENTER );
358 mSlider.SetSize( BAR_TAB_SIZE );
359 mSlider.SetStyle( ImageActor::STYLE_NINE_PATCH );
360 mSlider.SetNinePatchBorder( BAR_TAB_NINE_PATCH_BORDER );
362 // A duplicate Slider should appear 1 domain away from the original Slider
363 mSliderWrap = ImageActor::New( sliderImage );
364 mSliderWrap.SetParentOrigin( ParentOrigin::TOP_LEFT );
365 mSliderWrap.SetAnchorPoint( AnchorPoint::CENTER );
366 mSliderWrap.SetSize( BAR_TAB_SIZE );
367 mSliderWrap.SetStyle( ImageActor::STYLE_NINE_PATCH );
368 mSliderWrap.SetNinePatchBorder( BAR_TAB_NINE_PATCH_BORDER );
370 // target the container to observe for scrolling
371 Actor target = mContainer.Self();
372 Constraint constraint = Constraint::New<bool>( mSlider, Actor::Property::VISIBLE, ScrollBarInternalVisibilityConstraint );
373 constraint.AddSource( Source( target, vertical ? Toolkit::Scrollable::Property::CAN_SCROLL_VERTICAL : Toolkit::Scrollable::Property::CAN_SCROLL_HORIZONTAL ) );
376 constraint = constraint.Clone( mSliderWrap );
379 constraint = Constraint::New<Vector3>( mSlider, Actor::Property::SIZE, ScrollBarInternalSizeConstraint( vertical ) );
380 constraint.AddSource( Source( target, Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
381 constraint.AddSource( Source( target, Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
382 constraint.AddSource( Source( target, Toolkit::Scrollable::Property::SCROLL_DIRECTION ) );
383 constraint.AddSource( Source( target, Actor::Property::SIZE ) );
386 constraint = constraint.Clone( mSliderWrap );
389 constraint = Constraint::New<Quaternion>( mSlider, Actor::Property::ORIENTATION, ScrollBarInternalRotationConstraint( vertical ) );
390 constraint.AddSource( Source( target, Toolkit::Scrollable::Property::SCROLL_DIRECTION ) );
393 constraint = constraint.Clone( mSliderWrap );
396 constraint = Constraint::New<Vector3>( mSlider, Actor::Property::POSITION, ScrollBarInternalPositionConstraint(vertical) );
397 constraint.AddSource( Source( mSlider, Actor::Property::SIZE) );
398 constraint.AddSource( Source( target, Toolkit::Scrollable::Property::SCROLL_RELATIVE_POSITION ) );
399 constraint.AddSource( Source( target, Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
400 constraint.AddSource( Source( target, Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
401 constraint.AddSource( Source( target, Toolkit::Scrollable::Property::SCROLL_DIRECTION ) );
402 constraint.AddSource( Source( target, Actor::Property::SIZE ) );
405 constraint = Constraint::New<Vector3>( mSliderWrap, Actor::Property::POSITION, ScrollBarInternalPositionConstraint(vertical, true) );
406 constraint.AddSource( Source( mSlider, Actor::Property::SIZE) );
407 constraint.AddSource( Source( target, Toolkit::Scrollable::Property::SCROLL_RELATIVE_POSITION ) );
408 constraint.AddSource( Source( target, Toolkit::Scrollable::Property::SCROLL_POSITION_MIN ) );
409 constraint.AddSource( Source( target, Toolkit::Scrollable::Property::SCROLL_POSITION_MAX ) );
410 constraint.AddSource( Source( target, Toolkit::Scrollable::Property::SCROLL_DIRECTION ) );
411 constraint.AddSource( Source( target, Actor::Property::SIZE ) );
414 // Add Sliders to internal Actor, to avoid mixing up with regular
415 // Actors added by user.
416 mContainer.AddOverlay( mSlider );
417 mContainer.AddOverlay( mSliderWrap );
418 mContainer.ScrollStartedSignal().Connect( this, &ScrollBarInternal::OnStarted );
419 mContainer.ScrollCompletedSignal().Connect( this, &ScrollBarInternal::OnCompleted );
421 // Hit Area for dragging slider /////////////////////////////////////////////
422 mHitArea = Actor::New();
423 mHitArea.SetPosition(0.0f, 0.0f, 0.2f);
425 mContainer.AddOverlay( mHitArea );
426 constraint = Constraint::New<Vector3>( mHitArea, Actor::Property::SIZE, ScrollBarInternalHitSizeConstraint(vertical, BAR_TAB_SIZE.width) );
427 constraint.AddSource( Source( target, Toolkit::Scrollable::Property::SCROLL_DIRECTION ) );
428 constraint.AddSource( Source( target, Actor::Property::SIZE ) );
433 mHitArea.SetParentOrigin(ParentOrigin::CENTER_RIGHT);
434 mHitArea.SetAnchorPoint(AnchorPoint::CENTER_RIGHT);
438 mHitArea.SetParentOrigin(ParentOrigin::BOTTOM_CENTER);
439 mHitArea.SetAnchorPoint(AnchorPoint::BOTTOM_CENTER);
442 WaitingContractDelay();
445 ScrollBarInternal::~ScrollBarInternal()
450 void ScrollBarInternal::OnInitialize()
452 EnableGestureDetection(Gesture::Type(Gesture::Pan));
455 void ScrollBarInternal::OnDisconnect()
457 // Disconnect all connected callback functions.
458 mContainer.RemoveOverlay( mSlider );
459 mContainer.RemoveOverlay( mSliderWrap );
462 void ScrollBarInternal::OnPanGesture(Actor actor, PanGesture gesture)
464 switch(gesture.state)
466 case Gesture::Started:
470 mScrollStart = mContainer.GetCurrentScrollPosition();
471 mGestureDisplacement = Vector3::ZERO;
474 case Gesture::Continuing:
476 Vector3 delta(gesture.displacement.x, gesture.displacement.y, 0.0f);
477 mGestureDisplacement+=delta;
479 Vector3 size = mContainer.Self().GetCurrentSize();
480 Vector3 span = size - Vector3(BAR_TAB_SIZE.y, BAR_TAB_SIZE.y, 1.0f);
481 Vector3 domainSize = mContainer.GetDomainSize();
483 Vector3 position = mScrollStart + mGestureDisplacement * mAxisMask * domainSize / span;
484 mContainer.ScrollTo(position, 0.0f);
495 void ScrollBarInternal::OnStarted(const Vector3& position)
497 // TODO: Need to disable this for the scrollbar which isn't being scrolled.
505 void ScrollBarInternal::OnCompleted(const Vector3& position)
511 WaitingContractDelay();
515 bool ScrollBarInternal::OnContractDelayExpired()
527 void ScrollBarInternal::Show()
529 // Cancel any animation
536 mAnimation = Animation::New( BAR_SHOW_TIME );
537 mAnimation.AnimateTo( Property( mSlider, Actor::Property::COLOR_ALPHA ), 1.0f, AlphaFunctions::EaseIn );
538 mAnimation.AnimateTo( Property( mSliderWrap, Actor::Property::COLOR_ALPHA ), 1.0f, AlphaFunctions::EaseIn );
544 void ScrollBarInternal::Hide()
546 // Cancel any animation
553 mAnimation = Animation::New( BAR_HIDE_TIME );
554 mAnimation.AnimateTo( Property( mSlider, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunctions::EaseIn );
555 mAnimation.AnimateTo( Property( mSliderWrap, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunctions::EaseIn );
559 void ScrollBarInternal::CreateTimer()
563 // Create timer for contract delay
564 mTimer = Timer::New( BAR_CONTRACT_DELAY * SECOND_UNIT );
565 mTimer.TickSignal().Connect( this, &ScrollBarInternal::OnContractDelayExpired );
569 void ScrollBarInternal::DestructTimer()
574 mTimer.TickSignal().Disconnect( this, &ScrollBarInternal::OnContractDelayExpired );
579 void ScrollBarInternal::WaitingContractDelay()
585 Toolkit::ScrollBarInternal ScrollBarInternal::New(Toolkit::Scrollable& container, bool vertical)
587 // Create the implementation, temporarily owned by this handle on stack
588 IntrusivePtr< ScrollBarInternal > impl = new ScrollBarInternal( container, vertical );
590 // Pass ownership to CustomActor handle
591 Toolkit::ScrollBarInternal handle( *impl );
593 // Second-phase init of the implementation
594 // This can only be done after the CustomActor connection has been made...
600 } // namespace Internal
602 } // namespace Toolkit