2019-11-23 20:27:39 +08:00
|
|
|
/****************************************************************************
|
|
|
|
Copyright (c) 2015 Neo Kim (neo.kim@neofect.com)
|
|
|
|
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
2024-07-11 23:28:31 +08:00
|
|
|
Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md).
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2024-06-10 02:25:43 +08:00
|
|
|
https://axmol.dev/
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
|
|
all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
THE SOFTWARE.
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "ui/UIScrollViewBar.h"
|
2023-06-11 13:08:08 +08:00
|
|
|
#include "platform/Image.h"
|
|
|
|
#include "2d/Sprite.h"
|
|
|
|
#include "base/Utils.h"
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2022-07-11 17:50:21 +08:00
|
|
|
NS_AX_BEGIN
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
namespace ui
|
|
|
|
{
|
|
|
|
|
|
|
|
static const char* HALF_CIRCLE_IMAGE =
|
|
|
|
"iVBORw0KGgoAAAANSUhEUgAAAAwAAAAGCAMAAADAMI+zAAAAJ1BMVEX///////////////////////////////////////////////////"
|
|
|
|
"9Ruv0SAAAADHRSTlMABgcbbW7Hz9Dz+PmlcJP5AAAAMElEQVR4AUXHwQ2AQAhFwYcLH1H6r1djzDK3ASxUpTBeK/"
|
|
|
|
"uTCyz7dx54b44m4p5cD1MwAooEJyk3AAAAAElFTkSuQmCC";
|
|
|
|
static const char* BODY_IMAGE_1_PIXEL_HEIGHT =
|
|
|
|
"iVBORw0KGgoAAAANSUhEUgAAAAwAAAABCAMAAADdNb8LAAAAA1BMVEX///+nxBvIAAAACklEQVR4AWNABgAADQABYc2cpAAAAABJRU5ErkJggg==";
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
static const char* HALF_CIRCLE_IMAGE_KEY = "/__halfCircleImage";
|
2019-11-23 20:27:39 +08:00
|
|
|
static const char* BODY_IMAGE_1_PIXEL_HEIGHT_KEY = "/__bodyImage";
|
|
|
|
|
|
|
|
static const Color3B DEFAULT_COLOR(52, 65, 87);
|
2021-12-25 10:04:45 +08:00
|
|
|
static const float DEFAULT_MARGIN = 20;
|
|
|
|
static const float DEFAULT_AUTO_HIDE_TIME = 0.2f;
|
2019-11-23 20:27:39 +08:00
|
|
|
static const float DEFAULT_SCROLLBAR_OPACITY = 0.4f;
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
ScrollViewBar::ScrollViewBar(ScrollView* parent, ScrollView::Direction direction)
|
|
|
|
: _parent(parent)
|
|
|
|
, _direction(direction)
|
|
|
|
, _upperHalfCircle(nullptr)
|
|
|
|
, _lowerHalfCircle(nullptr)
|
|
|
|
, _body(nullptr)
|
|
|
|
, _opacity(255 * DEFAULT_SCROLLBAR_OPACITY)
|
|
|
|
, _marginFromBoundary(DEFAULT_MARGIN)
|
|
|
|
, _marginForLength(DEFAULT_MARGIN)
|
|
|
|
, _touching(false)
|
|
|
|
, _autoHideEnabled(true)
|
|
|
|
, _autoHideTime(DEFAULT_AUTO_HIDE_TIME)
|
|
|
|
, _autoHideRemainingTime(0)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2022-07-16 10:43:05 +08:00
|
|
|
AXASSERT(parent != nullptr, "Parent scroll view must not be null!");
|
|
|
|
AXASSERT(direction != ScrollView::Direction::BOTH, "Illegal scroll direction for scroll bar!");
|
2019-11-23 20:27:39 +08:00
|
|
|
setCascadeColorEnabled(true);
|
|
|
|
setCascadeOpacityEnabled(true);
|
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
ScrollViewBar::~ScrollViewBar() {}
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
ScrollViewBar* ScrollViewBar::create(ScrollView* parent, ScrollView::Direction direction)
|
|
|
|
{
|
2021-12-08 00:11:53 +08:00
|
|
|
ScrollViewBar* node = new ScrollViewBar(parent, direction);
|
|
|
|
if (node->init())
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
node->autorelease();
|
|
|
|
return node;
|
|
|
|
}
|
2022-07-16 10:43:05 +08:00
|
|
|
AX_SAFE_DELETE(node);
|
2019-11-23 20:27:39 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScrollViewBar::init()
|
|
|
|
{
|
|
|
|
if (!ProtectedNode::init())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
_upperHalfCircle = utils::createSpriteFromBase64Cached(HALF_CIRCLE_IMAGE, HALF_CIRCLE_IMAGE_KEY);
|
|
|
|
_upperHalfCircle->setAnchorPoint(Vec2::ANCHOR_MIDDLE_BOTTOM);
|
|
|
|
addProtectedChild(_upperHalfCircle);
|
2021-12-25 10:04:45 +08:00
|
|
|
|
|
|
|
_lowerHalfCircle = Sprite::createWithTexture(_upperHalfCircle->getTexture(), _upperHalfCircle->getTextureRect(),
|
|
|
|
_upperHalfCircle->isTextureRectRotated());
|
2019-11-23 20:27:39 +08:00
|
|
|
_lowerHalfCircle->setScaleY(-1);
|
|
|
|
_lowerHalfCircle->setAnchorPoint(Vec2::ANCHOR_MIDDLE_BOTTOM);
|
|
|
|
addProtectedChild(_lowerHalfCircle);
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
_body = utils::createSpriteFromBase64Cached(BODY_IMAGE_1_PIXEL_HEIGHT, BODY_IMAGE_1_PIXEL_HEIGHT_KEY);
|
|
|
|
_body->setAnchorPoint(Vec2::ANCHOR_MIDDLE_BOTTOM);
|
|
|
|
addProtectedChild(_body);
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
setColor(DEFAULT_COLOR);
|
|
|
|
onScrolled(Vec2::ZERO);
|
|
|
|
ProtectedNode::setOpacity(0);
|
|
|
|
_autoHideRemainingTime = 0.0f;
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
if (_direction == ScrollView::Direction::HORIZONTAL)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
setRotation(90);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
void ScrollViewBar::setPositionFromCorner(const Vec2& positionFromCorner)
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
if (_direction == ScrollView::Direction::VERTICAL)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
_marginForLength = positionFromCorner.y;
|
2019-11-23 20:27:39 +08:00
|
|
|
_marginFromBoundary = positionFromCorner.x;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
_marginForLength = positionFromCorner.x;
|
2019-11-23 20:27:39 +08:00
|
|
|
_marginFromBoundary = positionFromCorner.y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Vec2 ScrollViewBar::getPositionFromCorner() const
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
if (_direction == ScrollView::Direction::VERTICAL)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
return Vec2(_marginFromBoundary, _marginForLength);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return Vec2(_marginForLength, _marginFromBoundary);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScrollViewBar::setWidth(float width)
|
|
|
|
{
|
|
|
|
float scale = width / _body->getContentSize().width;
|
|
|
|
_body->setScaleX(scale);
|
|
|
|
_upperHalfCircle->setScale(scale);
|
|
|
|
_lowerHalfCircle->setScale(-scale);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScrollViewBar::setAutoHideEnabled(bool autoHideEnabled)
|
|
|
|
{
|
2023-02-22 11:13:56 +08:00
|
|
|
if (_autoHideEnabled == autoHideEnabled)
|
|
|
|
return;
|
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
_autoHideEnabled = autoHideEnabled;
|
2023-02-22 11:13:56 +08:00
|
|
|
if (!_autoHideEnabled)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2023-02-22 11:13:56 +08:00
|
|
|
ProtectedNode::setOpacity(255);
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
2023-02-22 11:13:56 +08:00
|
|
|
else if (!_touching && _autoHideRemainingTime <= 0)
|
|
|
|
{
|
2019-11-23 20:27:39 +08:00
|
|
|
ProtectedNode::setOpacity(0);
|
2023-02-22 11:13:56 +08:00
|
|
|
}
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
float ScrollViewBar::getWidth() const
|
|
|
|
{
|
|
|
|
return _body->getBoundingBox().size.width;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScrollViewBar::updateLength(float length)
|
|
|
|
{
|
|
|
|
float ratio = length / _body->getTextureRect().size.height;
|
|
|
|
_body->setScaleY(ratio);
|
|
|
|
_upperHalfCircle->setPositionY(_body->getPositionY() + length);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScrollViewBar::onEnter()
|
|
|
|
{
|
|
|
|
ProtectedNode::onEnter();
|
|
|
|
scheduleUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScrollViewBar::update(float deltaTime)
|
|
|
|
{
|
|
|
|
processAutoHide(deltaTime);
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
void ScrollViewBar::processAutoHide(float deltaTime)
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
if (!_autoHideEnabled || _autoHideRemainingTime <= 0)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
else if (_touching)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
// If it is touching, don't auto hide.
|
|
|
|
return;
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
_autoHideRemainingTime -= deltaTime;
|
2021-12-25 10:04:45 +08:00
|
|
|
if (_autoHideRemainingTime <= _autoHideTime)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
_autoHideRemainingTime = MAX(0, _autoHideRemainingTime);
|
|
|
|
ProtectedNode::setOpacity(_opacity * (_autoHideRemainingTime / _autoHideTime));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScrollViewBar::onTouchBegan()
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
if (!_autoHideEnabled)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_touching = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScrollViewBar::onTouchEnded()
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
if (!_autoHideEnabled)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_touching = false;
|
2021-12-25 10:04:45 +08:00
|
|
|
|
|
|
|
if (_autoHideRemainingTime <= 0)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
// If the remaining time is 0, it means that it didn't moved after touch started so scroll bar is not showing.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_autoHideRemainingTime = _autoHideTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScrollViewBar::onScrolled(const Vec2& outOfBoundary)
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
if (_autoHideEnabled)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
_autoHideRemainingTime = _autoHideTime;
|
|
|
|
ProtectedNode::setOpacity(_opacity);
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
Layout* innerContainer = _parent->getInnerContainer();
|
2021-12-25 10:04:45 +08:00
|
|
|
|
|
|
|
float innerContainerMeasure = 0;
|
|
|
|
float scrollViewMeasure = 0;
|
|
|
|
float outOfBoundaryValue = 0;
|
2019-11-23 20:27:39 +08:00
|
|
|
float innerContainerPosition = 0;
|
2021-12-25 10:04:45 +08:00
|
|
|
if (_direction == ScrollView::Direction::VERTICAL)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
innerContainerMeasure = innerContainer->getContentSize().height;
|
|
|
|
scrollViewMeasure = _parent->getContentSize().height;
|
|
|
|
outOfBoundaryValue = outOfBoundary.y;
|
2019-11-23 20:27:39 +08:00
|
|
|
innerContainerPosition = -innerContainer->getPositionY();
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
else if (_direction == ScrollView::Direction::HORIZONTAL)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
innerContainerMeasure = innerContainer->getContentSize().width;
|
|
|
|
scrollViewMeasure = _parent->getContentSize().width;
|
|
|
|
outOfBoundaryValue = outOfBoundary.x;
|
2019-11-23 20:27:39 +08:00
|
|
|
innerContainerPosition = -innerContainer->getPositionX();
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
float length = calculateLength(innerContainerMeasure, scrollViewMeasure, outOfBoundaryValue);
|
2021-12-25 10:04:45 +08:00
|
|
|
Vec2 position =
|
|
|
|
calculatePosition(innerContainerMeasure, scrollViewMeasure, innerContainerPosition, outOfBoundaryValue, length);
|
2019-11-23 20:27:39 +08:00
|
|
|
updateLength(length);
|
|
|
|
setPosition(position);
|
|
|
|
}
|
|
|
|
|
|
|
|
float ScrollViewBar::calculateLength(float innerContainerMeasure, float scrollViewMeasure, float outOfBoundaryValue)
|
|
|
|
{
|
|
|
|
float denominatorValue = innerContainerMeasure;
|
2021-12-25 10:04:45 +08:00
|
|
|
if (outOfBoundaryValue != 0)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
// If it is out of boundary, the length of scroll bar gets shorter quickly.
|
|
|
|
static const float GETTING_SHORTER_FACTOR = 20;
|
2021-12-25 10:04:45 +08:00
|
|
|
denominatorValue +=
|
|
|
|
(outOfBoundaryValue > 0 ? outOfBoundaryValue : -outOfBoundaryValue) * GETTING_SHORTER_FACTOR;
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
float lengthRatio = scrollViewMeasure / denominatorValue;
|
|
|
|
return fabsf(scrollViewMeasure - 2 * _marginForLength) * lengthRatio;
|
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
Vec2 ScrollViewBar::calculatePosition(float innerContainerMeasure,
|
|
|
|
float scrollViewMeasure,
|
|
|
|
float innerContainerPosition,
|
|
|
|
float outOfBoundaryValue,
|
|
|
|
float length)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
float denominatorValue = innerContainerMeasure - scrollViewMeasure;
|
2021-12-25 10:04:45 +08:00
|
|
|
if (outOfBoundaryValue != 0)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
denominatorValue += std::abs(outOfBoundaryValue);
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
float positionRatio = 0;
|
2021-12-25 10:04:45 +08:00
|
|
|
if (denominatorValue != 0)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
positionRatio = innerContainerPosition / denominatorValue;
|
|
|
|
positionRatio = MAX(positionRatio, 0);
|
|
|
|
positionRatio = MIN(positionRatio, 1);
|
|
|
|
}
|
|
|
|
float position = (scrollViewMeasure - length - 2 * _marginForLength) * positionRatio + _marginForLength;
|
2021-12-25 10:04:45 +08:00
|
|
|
if (_direction == ScrollView::Direction::VERTICAL)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
return Vec2(_parent->getContentSize().width - _marginFromBoundary, position);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return Vec2(position, _marginFromBoundary);
|
|
|
|
}
|
|
|
|
}
|
2021-12-25 10:04:45 +08:00
|
|
|
} // namespace ui
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2022-07-11 17:50:21 +08:00
|
|
|
NS_AX_END
|