Line data Source code
1 : /**
2 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "XL2dScrollViewBase.h"
24 : #include "XLInputListener.h"
25 : #include "XL2dScrollController.h"
26 :
27 : namespace STAPPLER_VERSIONIZED stappler::xenolith::basic2d {
28 :
29 46 : bool ScrollViewBase::init(Layout layout) {
30 46 : if (!DynamicStateNode::init()) {
31 0 : return false;
32 : }
33 :
34 46 : _layout = layout;
35 :
36 46 : _inputListener = addInputListener(Rc<InputListener>::create());
37 46 : _inputListener->addTapRecognizer([this] (const GestureTap &tap) {
38 0 : if (tap.event == GestureEvent::Activated) {
39 0 : onTap(tap.count, tap.pos);
40 : }
41 0 : return false;
42 46 : }, InputListener::makeButtonMask({InputMouseButton::Touch}));
43 :
44 46 : _inputListener->addPressRecognizer([this] (const GesturePress &s) -> bool {
45 24 : switch (s.event) {
46 24 : case GestureEvent::Began: return onPressBegin(s.pos); break;
47 0 : case GestureEvent::Activated: return onLongPress(s.pos, s.time, s.tickCount); break;
48 0 : case GestureEvent::Ended: return onPressEnd(s.pos, s.time); break;
49 0 : case GestureEvent::Cancelled: return onPressCancel(s.pos, s.time); break;
50 : }
51 0 : return false;
52 46 : }, TimeInterval::milliseconds(425), true);
53 :
54 46 : _inputListener->addSwipeRecognizer([this] (const GestureSwipe &s) -> bool {
55 168 : switch (s.event) {
56 14 : case GestureEvent::Began: return onSwipeEventBegin(s.getId(), s.midpoint, s.delta, s.velocity); break;
57 140 : case GestureEvent::Activated: return onSwipeEvent(s.getId(), s.midpoint, s.delta, s.velocity); break;
58 14 : case GestureEvent::Ended:
59 : case GestureEvent::Cancelled:
60 14 : return onSwipeEventEnd(s.getId(), s.midpoint, s.delta, s.velocity);
61 : break;
62 : }
63 0 : return false;
64 : }, TapDistanceAllowed, true);
65 :
66 46 : _inputListener->addScrollRecognizer([this] (const GestureScroll &w) -> bool {
67 0 : auto pos = getScrollPosition();
68 0 : onSwipeBegin();
69 0 : if (_layout == Vertical) {
70 0 : onDelta(- w.amount.y * 5.0f / _globalScale.y);
71 : } else {
72 0 : onDelta(- w.amount.x * 5.0f / _globalScale.x);
73 : }
74 0 : onScroll(getScrollPosition() - pos, false);
75 0 : return true;
76 : });
77 :
78 46 : setCascadeOpacityEnabled(true);
79 :
80 46 : _root = addChild(Rc<Node>::create());
81 46 : _root->setPosition(Vec2(0, 0));
82 46 : _root->setAnchorPoint((_layout == Vertical)?Vec2(0, 1):Vec2(0, 0));
83 46 : _root->setCascadeOpacityEnabled(true);
84 46 : _root->setOnContentSizeDirtyCallback(std::bind(&ScrollViewBase::onPosition, this));
85 46 : _root->setOnTransformDirtyCallback(std::bind(&ScrollViewBase::onPosition, this));
86 :
87 46 : return true;
88 : }
89 :
90 0 : void ScrollViewBase::setLayout(Layout l) {
91 0 : _layout = l;
92 0 : _contentSizeDirty = true;
93 0 : }
94 :
95 832 : bool ScrollViewBase::visitDraw(FrameInfo &info, NodeFlags parentFlags) {
96 832 : if (_scrollDirty) {
97 12 : updateScrollBounds();
98 : }
99 832 : if (_animationDirty) {
100 0 : fixPosition();
101 : }
102 832 : auto ret = DynamicStateNode::visitDraw(info, parentFlags);
103 832 : if (_animationDirty) {
104 0 : onPosition();
105 0 : onScroll(0, true);
106 0 : _animationDirty = false;
107 : }
108 832 : return ret;
109 : }
110 :
111 46 : void ScrollViewBase::onEnter(Scene *scene) {
112 46 : DynamicStateNode::onEnter(scene);
113 46 : onPosition();
114 46 : }
115 :
116 58 : void ScrollViewBase::onContentSizeDirty() {
117 58 : if (!isnan(_scrollSpaceLimit)) {
118 0 : auto padding = _paddingGlobal;
119 0 : if (isVertical()) {
120 0 : if (_contentSize.width > _scrollSpaceLimit + _scrollSpacePadding * 2.0f) {
121 0 : padding.left = padding.right = (_contentSize.width - _scrollSpaceLimit) / 2.0f;
122 : } else {
123 0 : padding.left = padding.right = _scrollSpacePadding;
124 : }
125 : } else {
126 0 : if (_contentSize.height > _scrollSpaceLimit + _scrollSpacePadding * 2.0f) {
127 0 : padding.top = padding.bottom = (_contentSize.height - _scrollSpaceLimit) / 2.0f;
128 : } else {
129 0 : padding.top = padding.bottom = _scrollSpacePadding;
130 : }
131 : }
132 0 : _paddingGlobal = padding;
133 : }
134 58 : DynamicStateNode::onContentSizeDirty();
135 58 : updateScrollBounds();
136 58 : fixPosition();
137 58 : }
138 :
139 38 : void ScrollViewBase::onTransformDirty(const Mat4 &parentTransform) {
140 38 : DynamicStateNode::onTransformDirty(parentTransform);
141 :
142 38 : Vec3 scale;
143 38 : parentTransform.decompose(&scale, nullptr, nullptr);
144 :
145 38 : if (_scale.x != 1.f) { scale.x *= _scale.x; }
146 38 : if (_scale.y != 1.f) { scale.y *= _scale.y; }
147 38 : if (_scale.z != 1.f) { scale.z *= _scale.z; }
148 :
149 38 : _globalScale = Vec2(scale.x, scale.y);
150 38 : }
151 :
152 20 : void ScrollViewBase::setEnabled(bool value) {
153 20 : _inputListener->setEnabled(value);
154 20 : }
155 0 : bool ScrollViewBase::isEnabled() const {
156 0 : return _inputListener->isEnabled();
157 : }
158 0 : bool ScrollViewBase::isInMotion() const {
159 0 : return _movement == Movement::Manual;
160 : }
161 0 : bool ScrollViewBase::isMoved() const {
162 0 : return _movement != Movement::None;
163 : }
164 :
165 12 : void ScrollViewBase::setScrollCallback(const ScrollCallback & cb) {
166 12 : _scrollCallback = cb;
167 12 : }
168 0 : const ScrollViewBase::ScrollCallback &ScrollViewBase::getScrollCallback() const {
169 0 : return _scrollCallback;
170 : }
171 :
172 0 : void ScrollViewBase::setOverscrollCallback(const OverscrollCallback & cb) {
173 0 : _overscrollCallback = cb;
174 0 : }
175 0 : const ScrollViewBase::OverscrollCallback &ScrollViewBase::getOverscrollCallback() const {
176 0 : return _overscrollCallback;
177 : }
178 :
179 10 : bool ScrollViewBase::addComponentItem(Component *cmp) {
180 10 : if (auto c = dynamic_cast<ScrollController *>(cmp)) {
181 0 : setController(c);
182 0 : return true;
183 : } else {
184 10 : return DynamicStateNode::addComponentItem(cmp);
185 : }
186 : }
187 :
188 46 : void ScrollViewBase::setController(ScrollController *c) {
189 46 : if (c != _controller) {
190 46 : if (_controller) {
191 0 : DynamicStateNode::removeComponent(_controller);
192 0 : _controller = nullptr;
193 : }
194 46 : _controller = c;
195 46 : if (_controller) {
196 46 : DynamicStateNode::addComponentItem(_controller);
197 : }
198 : }
199 46 : }
200 4 : ScrollController *ScrollViewBase::getController() {
201 4 : return _controller;
202 : }
203 :
204 10 : void ScrollViewBase::setPadding(const Padding &p) {
205 10 : if (p != _paddingGlobal) {
206 10 : _paddingGlobal = p;
207 10 : _contentSizeDirty = true;
208 : }
209 10 : }
210 298 : const Padding &ScrollViewBase::getPadding() const {
211 298 : return _paddingGlobal;
212 : }
213 :
214 0 : void ScrollViewBase::setSpaceLimit(float value, float padding) {
215 0 : if (_scrollSpaceLimit != value || _scrollSpacePadding != padding) {
216 0 : _scrollSpaceLimit = value;
217 0 : _scrollSpacePadding = padding;
218 0 : _contentSizeDirty = true;
219 : }
220 0 : }
221 0 : float ScrollViewBase::getSpaceLimit() const {
222 0 : return _scrollSpaceLimit;
223 : }
224 0 : float ScrollViewBase::getSpacePadding() const {
225 0 : return _scrollSpacePadding;
226 : }
227 :
228 3778 : float ScrollViewBase::getScrollableAreaOffset() const {
229 3778 : if (_controller) {
230 3778 : return _controller->getScrollableAreaOffset();
231 : }
232 0 : return std::numeric_limits<float>::quiet_NaN();
233 : }
234 :
235 3409 : float ScrollViewBase::getScrollableAreaSize() const {
236 3409 : if (_controller) {
237 3409 : return _controller->getScrollableAreaSize();
238 : }
239 0 : return std::numeric_limits<float>::quiet_NaN();
240 : }
241 :
242 28710 : Vec2 ScrollViewBase::getPositionForNode(float scrollPos) const {
243 28710 : if (isVertical()) {
244 28704 : return Vec2(0.0f, scrollPos);
245 : } else {
246 6 : return Vec2(scrollPos, 0.0f);
247 : }
248 : }
249 28710 : Size2 ScrollViewBase::getContentSizeForNode(float size) const {
250 28710 : if (isVertical()) {
251 28704 : return Size2(nan(), size);
252 : } else {
253 6 : return Size2(size, nan());
254 : }
255 : }
256 26929 : Vec2 ScrollViewBase::getAnchorPointForNode() const {
257 26929 : if (isVertical()) {
258 26917 : return Vec2(0, 1.0f);
259 : } else {
260 12 : return Vec2(0, 0);
261 : }
262 : }
263 :
264 809172 : float ScrollViewBase::getNodeScrollSize(Size2 size) const {
265 809172 : return (isVertical())?(size.height):(size.width);
266 : }
267 :
268 809474 : float ScrollViewBase::getNodeScrollPosition(Vec2 pos) const {
269 809474 : return (isVertical())?(pos.y):(pos.x);
270 : }
271 :
272 16639 : bool ScrollViewBase::addScrollNode(Node *node, Vec2 pos, Size2 size, ZOrder z, StringView name) {
273 16639 : updateScrollNode(node, pos, size, z, name);
274 16639 : if (z != ZOrder(0)) {
275 145 : _root->addChild(node, z);
276 : } else {
277 16494 : _root->addChild(node);
278 : }
279 16639 : return true;
280 : }
281 :
282 26929 : void ScrollViewBase::updateScrollNode(Node *node, Vec2 pos, Size2 size, ZOrder z, StringView name) {
283 26929 : auto p = node->getParent();
284 26929 : if (p == _root || p == nullptr) {
285 28000 : auto cs = Size2(isnan(size.width)?_root->getContentSize().width:size.width,
286 54929 : isnan(size.height)?_root->getContentSize().height:size.height);
287 :
288 26929 : node->setContentSize(cs);
289 26929 : node->setPosition((isVertical()?Vec2(pos.x,-pos.y):pos));
290 26929 : node->setAnchorPoint(getAnchorPointForNode());
291 26929 : if (z != ZOrder(0)) {
292 887 : node->setLocalZOrder(z);
293 : }
294 : }
295 26929 : }
296 :
297 14709 : bool ScrollViewBase::removeScrollNode(Node *node) {
298 14709 : if (node && node->getParent() == _root) {
299 14709 : node->removeFromParent();
300 14709 : return true;
301 : }
302 0 : return false;
303 : }
304 :
305 220 : float ScrollViewBase::getDistanceFromStart() const {
306 220 : auto min = getScrollMinPosition();
307 220 : if (!isnan(min)) {
308 220 : return fabsf(getScrollPosition() - min);
309 : } else {
310 0 : return std::numeric_limits<float>::quiet_NaN();
311 : }
312 : }
313 :
314 10 : void ScrollViewBase::setScrollMaxVelocity(float value) {
315 10 : _maxVelocity = value;
316 10 : }
317 0 : float ScrollViewBase::getScrollMaxVelocity() const {
318 0 : return _maxVelocity;
319 : }
320 :
321 :
322 0 : Node * ScrollViewBase::getFrontNode() const {
323 0 : if (_controller) {
324 0 : return _controller->getFrontNode();
325 : }
326 0 : return nullptr;
327 : }
328 0 : Node * ScrollViewBase::getBackNode() const {
329 0 : if (_controller) {
330 0 : return _controller->getBackNode();
331 : }
332 0 : return nullptr;
333 : }
334 :
335 2056 : float ScrollViewBase::getScrollMinPosition() const {
336 2056 : auto pos = getScrollableAreaOffset();
337 2056 : if (!isnan(pos)) {
338 2056 : return pos - (isVertical()?_paddingGlobal.top:_paddingGlobal.left);
339 : }
340 0 : if (_controller) {
341 0 : float min = _controller->getScrollMin();
342 0 : if (!isnan(min)) {
343 0 : return min - (isVertical()?_paddingGlobal.top:_paddingGlobal.left);
344 : }
345 : }
346 0 : return std::numeric_limits<float>::quiet_NaN();
347 : }
348 :
349 1512 : float ScrollViewBase::getScrollMaxPosition() const {
350 1512 : auto pos = getScrollableAreaOffset();
351 1512 : auto size = getScrollableAreaSize();
352 1512 : if (!isnan(pos) && !isnan(size)) {
353 1230 : pos -= (isVertical()?_paddingGlobal.top:_paddingGlobal.left);
354 1230 : size += (isVertical()?(_paddingGlobal.top + _paddingGlobal.bottom):(_paddingGlobal.left + _paddingGlobal.right));
355 1230 : if (size > _scrollSize) {
356 1046 : return pos + size - _scrollSize;
357 : } else {
358 184 : return pos;
359 : }
360 : }
361 282 : if (_controller) {
362 282 : float min = _controller->getScrollMin();
363 282 : float max = _controller->getScrollMax();
364 282 : if (!isnan(max) && !isnan(min)) {
365 20 : return std::max(min, max - _scrollSize + (isVertical()?_paddingGlobal.bottom:_paddingGlobal.right));
366 262 : } else if (!isnan(max)) {
367 0 : return max - _scrollSize + (isVertical()?_paddingGlobal.bottom:_paddingGlobal.right);
368 : }
369 : }
370 :
371 262 : return std::numeric_limits<float>::quiet_NaN();
372 : }
373 :
374 1467 : float ScrollViewBase::getScrollLength() const {
375 1467 : float size = getScrollableAreaSize();
376 1467 : if (!isnan(size)) {
377 1311 : return size + (isVertical()?(_paddingGlobal.top + _paddingGlobal.bottom):(_paddingGlobal.left + _paddingGlobal.right));
378 : }
379 :
380 156 : float min = getScrollMinPosition();
381 156 : float max = getScrollMaxPosition();
382 :
383 156 : if (!isnan(min) && !isnan(max)) {
384 0 : float trueMax = max - (isVertical()?_paddingGlobal.bottom:_paddingGlobal.right);
385 0 : float trueMin = min + (isVertical()?_paddingGlobal.top:_paddingGlobal.left);
386 0 : if (trueMax > trueMin) {
387 0 : return max - min + _scrollSize;
388 : } else {
389 0 : return _scrollSize;
390 : }
391 : } else {
392 156 : return std::numeric_limits<float>::quiet_NaN();
393 : }
394 : }
395 :
396 862 : float ScrollViewBase::getScrollSize() const {
397 862 : return _scrollSize;
398 : }
399 :
400 145 : void ScrollViewBase::setScrollRelativePosition(float value) {
401 145 : if (!isnan(value)) {
402 145 : if (value < 0.0f) {
403 0 : value = 0.0f;
404 145 : } else if (value > 1.0f) {
405 0 : value = 1.0f;
406 : }
407 : } else {
408 0 : value = 0.0f;
409 : }
410 :
411 145 : float areaSize = getScrollableAreaSize();
412 145 : float areaOffset = getScrollableAreaOffset();
413 145 : float size = getScrollSize();
414 :
415 145 : if (areaSize < size) {
416 0 : value = 0.0f;
417 : }
418 :
419 145 : auto &padding = getPadding();
420 145 : auto paddingFront = (isVertical())?padding.top:padding.left;
421 145 : auto paddingBack = (isVertical())?padding.bottom:padding.right;
422 :
423 145 : if (!isnan(areaSize) && !isnan(areaOffset) && areaSize > 0) {
424 145 : float liveSize = areaSize + paddingFront + paddingBack - size;
425 145 : float pos = (value * liveSize) - paddingFront + areaOffset;
426 :
427 145 : doSetScrollPosition(pos);
428 : } else {
429 0 : _savedRelativePosition = value;
430 : }
431 145 : }
432 :
433 61 : float ScrollViewBase::getScrollRelativePosition() const {
434 61 : if (!isnan(_savedRelativePosition)) {
435 0 : return _savedRelativePosition;
436 : }
437 :
438 61 : return getScrollRelativePosition(getScrollPosition());
439 : }
440 :
441 61 : float ScrollViewBase::getScrollRelativePosition(float pos) const {
442 61 : float areaSize = getScrollableAreaSize();
443 61 : float areaOffset = getScrollableAreaOffset();
444 61 : float size = getScrollSize();
445 :
446 61 : auto &padding = getPadding();
447 61 : auto paddingFront = (isVertical())?padding.top:padding.left;
448 61 : auto paddingBack = (isVertical())?padding.bottom:padding.right;
449 :
450 61 : if (!isnan(areaSize) && !isnan(areaOffset)) {
451 61 : float liveSize = areaSize + paddingFront + paddingBack - size;
452 61 : return (pos - areaOffset + paddingFront) / liveSize;
453 : }
454 :
455 0 : return 0.0f;
456 : }
457 :
458 156 : void ScrollViewBase::setScrollPosition(float pos) {
459 156 : if (pos != _scrollPosition) {
460 154 : doSetScrollPosition(pos);
461 : }
462 156 : }
463 :
464 299 : void ScrollViewBase::doSetScrollPosition(float pos) {
465 299 : if (isVertical()) {
466 299 : _root->setPositionY(pos + _scrollSize);
467 : } else {
468 0 : _root->setPositionX(-pos);
469 : }
470 299 : }
471 :
472 1343 : float ScrollViewBase::getScrollPosition() const {
473 1343 : if (isVertical()) {
474 1315 : return _root->getPosition().y - _scrollSize;
475 : } else {
476 28 : return -_root->getPosition().x;
477 : }
478 : }
479 :
480 14 : Vec2 ScrollViewBase::getPointForScrollPosition(float pos) {
481 14 : return (isVertical())?Vec2(_root->getPosition().x, pos + _scrollSize):Vec2(-pos, _root->getPosition().y);
482 : }
483 :
484 140 : void ScrollViewBase::onDelta(float delta) {
485 140 : auto pos = getScrollPosition();
486 140 : if (delta < 0) {
487 0 : if (!isnan(_scrollMin) && pos + delta < _scrollMin) {
488 0 : if (_bounce) {
489 0 : float mod = 1.0f / (1.0f + (_scrollMin - (pos + delta)) / 5.0f);
490 0 : setScrollPosition(pos + delta * mod);
491 0 : return;
492 : } else {
493 0 : onOverscroll(delta);
494 0 : setScrollPosition(_scrollMin);
495 0 : return;
496 : }
497 : }
498 140 : } else if (delta > 0) {
499 140 : if (!isnan(_scrollMax) && pos + delta > _scrollMax) {
500 0 : if (_bounce) {
501 0 : float mod = 1.0f / (1.0f + ((pos + delta) - _scrollMax) / 5.0f);
502 0 : setScrollPosition(pos + delta * mod);
503 0 : return;
504 : } else {
505 0 : onOverscroll(delta);
506 0 : setScrollPosition(_scrollMax);
507 0 : return;
508 : }
509 : }
510 : }
511 140 : setScrollPosition(pos + delta);
512 : }
513 :
514 0 : void ScrollViewBase::onOverscrollPerformed(float velocity, float pos, float boundary) {
515 0 : if (_movement == Movement::Auto) {
516 0 : if (_movementAction) {
517 0 : auto n = (pos < boundary)?1.0f:-1.0f;
518 :
519 0 : auto vel = _movementAction->getCurrentVelocity();
520 0 : auto normal = _movementAction->getNormal();
521 0 : if (n * (isVertical()?normal.y:-normal.x) > 0) {
522 0 : velocity = vel;
523 : } else {
524 0 : velocity = -vel;
525 : }
526 : }
527 : }
528 :
529 0 : if (_animationAction) {
530 0 : _root->stopAction(_animationAction);
531 0 : _animationAction = nullptr;
532 0 : _movementAction = nullptr;
533 : }
534 :
535 0 : if (_movement == Movement::Manual || _movement == Movement::None) {
536 0 : if (!_bounce && pos == boundary) {
537 0 : return;
538 : }
539 0 : if ((pos < boundary && velocity < 0) || (pos > boundary && velocity > 0)) {
540 0 : velocity = - fabs(velocity);
541 : } else {
542 0 : velocity = fabs(velocity);
543 : }
544 : }
545 :
546 0 : if (_movement != Movement::Overscroll) {
547 0 : Vec2 boundaryPos = getPointForScrollPosition(boundary);
548 0 : Vec2 currentPos = getPointForScrollPosition(pos);
549 :
550 0 : auto a = ActionAcceleratedMove::createBounce(5000, currentPos, boundaryPos, velocity, std::max(25000.0f, fabsf(velocity) * 50));
551 0 : if (a) {
552 0 : _controller->dropAnimationPadding();
553 0 : _movement = Movement::Overscroll;
554 0 : _animationAction = Rc<Sequence>::create(a, [this] {
555 0 : onAnimationFinished();
556 0 : });
557 0 : _root->runAction(_animationAction);
558 : }
559 0 : }
560 : }
561 :
562 14 : bool ScrollViewBase::onSwipeEventBegin(uint32_t id, const Vec2 &loc, const Vec2 &delta, const Vec2 &velocity) {
563 14 : if (_layout == Layout::Vertical) {
564 14 : if (std::abs(delta.x) < std::abs(delta.y)) {
565 14 : _inputListener->setExclusiveForTouch(id);
566 : } else {
567 0 : return false;
568 : }
569 : } else {
570 0 : if (std::abs(delta.x) > std::abs(delta.y)) {
571 0 : _inputListener->setExclusiveForTouch(id);
572 : } else {
573 0 : return false;
574 : }
575 : }
576 :
577 14 : auto cs = (_layout == Vertical)?(_contentSize.height):(_contentSize.width);
578 14 : auto length = getScrollLength();
579 14 : if (!isnan(length) && cs >= length) {
580 0 : return false;
581 : }
582 :
583 14 : onSwipeBegin();
584 :
585 : /*if (_layout == Vertical) {
586 : return onSwipe(delta.y / _globalScale.y, velocity.y / _globalScale.y, false);
587 : } else {
588 : return onSwipe(- delta.x / _globalScale.x, - velocity.x / _globalScale.x, false);
589 : }*/
590 :
591 14 : return true;
592 : }
593 :
594 140 : bool ScrollViewBase::onSwipeEvent(uint32_t id, const Vec2 &loc, const Vec2 &delta, const Vec2 &velocity) {
595 140 : if (_layout == Vertical) {
596 140 : return onSwipe(delta.y / _globalScale.y, velocity.y / _globalScale.y, false);
597 : } else {
598 0 : return onSwipe(- delta.x / _globalScale.x, - velocity.x / _globalScale.x, false);
599 : }
600 : }
601 :
602 14 : bool ScrollViewBase::onSwipeEventEnd(uint32_t id, const Vec2 &loc, const Vec2 &d, const Vec2 &velocity) {
603 14 : _movement = Movement::None;
604 14 : if (_layout == Vertical) {
605 14 : return onSwipe(0, velocity.y / _globalScale.y, true);
606 : } else {
607 0 : return onSwipe(0, - velocity.x / _globalScale.x, true);
608 : }
609 : }
610 :
611 14 : void ScrollViewBase::onSwipeBegin() {
612 14 : if (_controller) {
613 14 : _controller->dropAnimationPadding();
614 : }
615 14 : _root->stopAllActions();
616 14 : _movementAction = nullptr;
617 14 : _animationAction = nullptr;
618 14 : _movement = Movement::Manual;
619 14 : }
620 :
621 154 : bool ScrollViewBase::onSwipe(float delta, float velocity, bool ended) {
622 154 : if (!ended) {
623 140 : if (_scrollFilter) {
624 0 : delta = _scrollFilter(delta);
625 : }
626 140 : onDelta(delta);
627 : } else {
628 14 : float pos = getScrollPosition();
629 :
630 14 : float acceleration = (velocity > 0)?-5000.0f:5000.0f;
631 14 : if (!isnan(_maxVelocity)) {
632 10 : if (velocity > fabs(_maxVelocity)) {
633 0 : velocity = fabs(_maxVelocity);
634 10 : } else if (velocity < -fabs(_maxVelocity)) {
635 0 : velocity = -fabs(_maxVelocity);
636 : }
637 : }
638 :
639 14 : float duration = fabsf(velocity / acceleration);
640 14 : float path = velocity * duration + acceleration * duration * duration * 0.5f;
641 :
642 14 : if (_controller) {
643 14 : _controller->setAnimationPadding(path);
644 14 : _controller->onScrollPosition();
645 : }
646 :
647 14 : if (!isnan(_scrollMin)) {
648 14 : if (pos < _scrollMin) {
649 0 : float mod = 1.0f / (1.0f + fabsf(_scrollMin - pos) / 5.0f);
650 0 : onOverscrollPerformed(velocity * mod, pos, _scrollMin);
651 0 : return true;
652 : }
653 : }
654 :
655 14 : if (!isnan(_scrollMax)) {
656 14 : if (pos > _scrollMax) {
657 0 : float mod = 1.0f / (1.0f + fabsf(_scrollMax - pos) / 5.0f);
658 0 : onOverscrollPerformed(velocity * mod, pos, _scrollMax);
659 0 : return true;
660 : }
661 : }
662 :
663 14 : if (auto a = onSwipeFinalizeAction(velocity)) {
664 14 : _movement = Movement::Auto;
665 28 : _animationAction = Rc<Sequence>::create(a, [this] {
666 14 : onAnimationFinished();
667 14 : });
668 14 : _root->runAction(_animationAction);
669 : } else {
670 0 : onScroll(0, true);
671 14 : }
672 : }
673 154 : return true;
674 : }
675 :
676 14 : Rc<ActionInterval> ScrollViewBase::onSwipeFinalizeAction(float velocity) {
677 14 : if (velocity == 0) {
678 0 : return nullptr;
679 : }
680 :
681 14 : float acceleration = (velocity > 0)?-5000.0f:5000.0f;
682 14 : float boundary = (velocity > 0)?_scrollMax:_scrollMin;
683 :
684 14 : Vec2 normal = (isVertical())
685 : ?(Vec2(0.0f, (velocity > 0)?1.0f:-1.0f))
686 14 : :(Vec2((velocity > 0)?-1.0f:1.0f, 0.0f));
687 :
688 14 : Rc<ActionInterval> a;
689 :
690 14 : if (!isnan(_maxVelocity)) {
691 10 : if (velocity > fabs(_maxVelocity)) {
692 0 : velocity = fabs(_maxVelocity);
693 10 : } else if (velocity < -fabs(_maxVelocity)) {
694 0 : velocity = -fabs(_maxVelocity);
695 : }
696 : }
697 :
698 14 : if (!isnan(boundary)) {
699 14 : float pos = getScrollPosition();
700 14 : float duration = fabsf(velocity / acceleration);
701 14 : float path = velocity * duration + acceleration * duration * duration * 0.5f;
702 :
703 14 : auto from = _root->getPosition().xy();
704 14 : auto to = getPointForScrollPosition(boundary);
705 :
706 14 : float distance = from.distance(to);
707 14 : if (distance < 2.0f) {
708 0 : setScrollPosition(boundary);
709 0 : return nullptr;
710 : }
711 :
712 14 : if ((velocity > 0 && pos + path > boundary) || (velocity < 0 && pos + path < boundary)) {
713 0 : _movementAction = ActionAcceleratedMove::createAccelerationTo(from, to, fabsf(velocity), -fabsf(acceleration));
714 :
715 0 : auto overscrollPath = path + ((velocity < 0)?(distance):(-distance));
716 0 : if (overscrollPath) {
717 0 : a = Rc<Sequence>::create(_movementAction, [this, overscrollPath] {
718 0 : onOverscroll(overscrollPath);
719 0 : });
720 : }
721 : }
722 : }
723 :
724 14 : if (!_movementAction) {
725 14 : _movementAction = ActionAcceleratedMove::createDecceleration(normal, _root->getPosition().xy(), fabs(velocity), fabsf(acceleration));
726 : }
727 :
728 14 : if (!a) {
729 14 : a = _movementAction;
730 : }
731 :
732 14 : return a;
733 14 : }
734 :
735 38 : void ScrollViewBase::onAnimationFinished() {
736 38 : if (_movement != Movement::None) {
737 14 : _animationDirty = true;
738 : }
739 38 : if (_controller) {
740 38 : _controller->dropAnimationPadding();
741 : }
742 38 : _movement = Movement::None;
743 38 : _movementAction = nullptr;
744 38 : _animationAction = nullptr;
745 38 : onPosition();
746 38 : }
747 :
748 374 : void ScrollViewBase::fixPosition() {
749 374 : if (_movement == Movement::None) {
750 196 : auto pos = getScrollPosition();
751 196 : if (!isnan(_scrollMin)) {
752 194 : if (pos < _scrollMin) {
753 0 : setScrollPosition(_scrollMin);
754 0 : return;
755 : }
756 : }
757 :
758 196 : if (!isnan(_scrollMax)) {
759 184 : if (pos > _scrollMax) {
760 0 : setScrollPosition(_scrollMax);
761 0 : return;
762 : }
763 : }
764 : }
765 : }
766 :
767 676 : void ScrollViewBase::onPosition() {
768 676 : float oldPos = _scrollPosition;
769 : float newPos;
770 676 : if (isVertical()) {
771 666 : newPos = _root->getPosition().y - _scrollSize;
772 : } else {
773 10 : newPos = -_root->getPosition().x;
774 : }
775 :
776 676 : _scrollPosition = newPos;
777 :
778 676 : if (_controller) {
779 676 : if (_movement == Movement::Auto) {
780 204 : _controller->updateAnimationPadding(newPos - oldPos);
781 : }
782 676 : _controller->onScrollPosition();
783 : }
784 :
785 676 : if (_movement == Movement::Auto) {
786 204 : if (!isnan(_scrollMin)) {
787 204 : if (newPos < _scrollMin) {
788 0 : onOverscrollPerformed(0, newPos, _scrollMin);
789 0 : return;
790 : }
791 : }
792 :
793 204 : if (!isnan(_scrollMax)) {
794 130 : if (newPos > _scrollMax) {
795 0 : onOverscrollPerformed(0, newPos, _scrollMax);
796 0 : return;
797 : }
798 : }
799 : }
800 :
801 676 : if (_movement != Movement::None && _movement != Movement::Overscroll && newPos - oldPos != 0) {
802 333 : onScroll(newPos - oldPos, false);
803 343 : } else if (_movement == Movement::Overscroll) {
804 0 : if (!isnan(_scrollMin)) {
805 0 : if (_scrollPosition < _scrollMin) {
806 0 : if (newPos - oldPos < 0) {
807 0 : onOverscroll(newPos - oldPos);
808 : }
809 0 : return;
810 : }
811 : }
812 :
813 0 : if (!isnan(_scrollMax)) {
814 0 : if (newPos > _scrollMax) {
815 0 : if (newPos - oldPos > 0) {
816 0 : onOverscroll(newPos - oldPos);
817 : }
818 0 : return;
819 : }
820 : }
821 : }
822 : }
823 :
824 320 : void ScrollViewBase::updateScrollBounds() {
825 320 : if ((isVertical() && _contentSize.width == 0) || (isHorizontal() && _contentSize.height == 0)) {
826 4 : return;
827 : }
828 :
829 316 : if (_contentSizeDirty) {
830 58 : if (isVertical()) {
831 54 : auto pos = _root->getPosition().y - _scrollSize;
832 54 : _scrollSize = _contentSize.height;
833 54 : _root->setAnchorPoint(Vec2(0, 1));
834 54 : _root->setContentSize(Size2(_contentSize.width - _paddingGlobal.left - _paddingGlobal.right, 0));
835 54 : _root->setPositionY(pos + _scrollSize);
836 54 : _root->setPositionX(_paddingGlobal.left);
837 : } else {
838 4 : _scrollSize = _contentSize.width;
839 4 : _root->setAnchorPoint(Vec2(0, 0));
840 4 : _root->setContentSize(Size2(0, _contentSize.height - _paddingGlobal.top - _paddingGlobal.bottom));
841 4 : _root->setPositionY(_paddingGlobal.bottom);
842 : }
843 : }
844 :
845 316 : _scrollMin = getScrollMinPosition();
846 316 : _scrollMax = getScrollMaxPosition();
847 :
848 316 : _scrollDirty = false;
849 :
850 316 : fixPosition();
851 :
852 316 : _scrollMin = getScrollMinPosition();
853 316 : _scrollMax = getScrollMaxPosition();
854 :
855 316 : if (!isnan(_savedRelativePosition)) {
856 0 : float value = _savedRelativePosition;
857 0 : _savedRelativePosition = nan();
858 0 : setScrollRelativePosition(value);
859 : }
860 :
861 316 : _root->setPositionZ(1.0f);
862 316 : _root->setPositionZ(0.0f);
863 : }
864 :
865 333 : void ScrollViewBase::onScroll(float delta, bool finished) {
866 333 : if (_controller) {
867 333 : _controller->onScroll(delta, finished);
868 : }
869 333 : if (_scrollCallback) {
870 220 : _scrollCallback(delta, finished);
871 : }
872 333 : }
873 :
874 0 : void ScrollViewBase::onOverscroll(float delta) {
875 0 : if (_controller) {
876 0 : _controller->onOverscroll(delta);
877 : }
878 0 : if (_overscrollCallback) {
879 0 : _overscrollCallback(delta);
880 : }
881 0 : }
882 :
883 130 : void ScrollViewBase::setScrollDirty(bool value) {
884 130 : _scrollDirty = value;
885 130 : }
886 :
887 24 : bool ScrollViewBase::onPressBegin(const Vec2 &) {
888 24 : _root->stopAllActions();
889 24 : onAnimationFinished();
890 24 : return false;
891 : }
892 :
893 0 : bool ScrollViewBase::onLongPress(const Vec2 &, const TimeInterval &time, int count) {
894 0 : return true;
895 : }
896 0 : bool ScrollViewBase::onPressEnd(const Vec2 &, const TimeInterval &) {
897 0 : return true;
898 : }
899 0 : bool ScrollViewBase::onPressCancel(const Vec2 &, const TimeInterval &) {
900 0 : return true;
901 : }
902 :
903 0 : void ScrollViewBase::onTap(int count, const Vec2 &loc) {
904 :
905 0 : }
906 :
907 0 : Vec2 ScrollViewBase::convertFromScrollableSpace(const Vec2 &pos) {
908 0 : return _root->getNodeToParentTransform().transformPoint(pos);
909 : }
910 :
911 0 : Vec2 ScrollViewBase::convertToScrollableSpace(const Vec2 &pos) {
912 0 : return _root->getParentToNodeTransform().transformPoint(pos);
913 : }
914 :
915 0 : Vec2 ScrollViewBase::convertFromScrollableSpace(Node *node, Vec2 pos) {
916 0 : auto tmp = node->getNodeToParentTransform() * _root->getNodeToParentTransform();
917 0 : return tmp.transformPoint(pos);
918 : }
919 :
920 0 : Vec2 ScrollViewBase::convertToScrollableSpace(Node *node, Vec2 pos) {
921 0 : auto tmp = _root->getParentToNodeTransform() * node->getParentToNodeTransform();
922 0 : return tmp.transformPoint(pos);
923 : }
924 :
925 : }
|