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 "XL2dScrollController.h"
24 : #include "XL2dScrollViewBase.h"
25 : #include "XL2dScrollItemHandle.h"
26 :
27 : namespace STAPPLER_VERSIONIZED stappler::xenolith::basic2d {
28 :
29 29110 : ScrollController::Item::Item(NodeFunction &&f, Vec2 pos, Size2 size, ZOrder z, StringView name)
30 29110 : : nodeFunction(move(f)), size(size), pos(pos), zIndex(z), name(name.str<Interface>()) { }
31 :
32 92 : ScrollController::~ScrollController() { }
33 :
34 46 : void ScrollController::onAdded(Node *owner) {
35 46 : Component::onAdded(owner);
36 46 : _scroll = dynamic_cast<ScrollViewBase *>(getOwner());
37 46 : if (_scroll) {
38 46 : _root = _scroll->getRoot();
39 46 : _scroll->setScrollDirty(true);
40 : }
41 46 : }
42 :
43 10 : void ScrollController::onRemoved() {
44 10 : clear();
45 10 : Component::onRemoved();
46 10 : _scroll = nullptr;
47 10 : _root = nullptr;
48 10 : _savedSize = 0.0f;
49 10 : }
50 :
51 58 : void ScrollController::onContentSizeDirty() {
52 58 : Component::onContentSizeDirty();
53 58 : if (!_scroll) {
54 0 : return;
55 : }
56 :
57 58 : auto defSize = _scroll->isVertical()
58 62 : ? _scroll->getContentSize().width - _scroll->getPadding().horizontal()
59 4 : : _scroll->getContentSize().height - _scroll->getPadding().vertical();
60 58 : if (_savedSize != defSize) {
61 36 : float tmpPos = _scroll->getScrollPosition();
62 36 : float relPos = _scroll->getScrollRelativePosition();
63 36 : if (!rebuildObjects()) {
64 28 : for (auto &it : _nodes) {
65 6 : updateScrollNode(it);
66 : }
67 : } else {
68 14 : if (!isnan(relPos) && tmpPos == _scroll->getScrollPosition()) {
69 0 : onScrollPosition();
70 0 : _scroll->setScrollRelativePosition(relPos);
71 : }
72 : }
73 36 : _savedSize = defSize;
74 : } else {
75 422 : for (auto &it : _nodes) {
76 400 : updateScrollNode(it);
77 : }
78 : }
79 : }
80 :
81 694 : void ScrollController::onScrollPosition(bool force) {
82 694 : if (!_scroll || !_root) {
83 50 : return;
84 : }
85 :
86 694 : bool isVertical = _scroll->isVertical();
87 694 : auto csize = _scroll->getContentSize();
88 694 : if ((isVertical && csize.width == 0) || (!isVertical && csize.height == 0)) {
89 50 : return;
90 : }
91 :
92 : do {
93 644 : if (_infoDirty || force) {
94 228 : float start = nan();
95 228 : float end = nan();
96 228 : float size = 0;
97 228 : float pos = 0;
98 :
99 32946 : for (auto &it : _nodes) {
100 32718 : pos = _scroll->getNodeScrollPosition(it.pos);
101 32718 : size = _scroll->getNodeScrollSize(it.size);
102 :
103 32718 : if (isnan(start) || start > pos) {
104 214 : start = pos;
105 : }
106 :
107 32718 : if (isnan(end) || end < pos + size) {
108 3878 : end = pos + size;
109 : }
110 : }
111 :
112 228 : setScrollableArea(isnan(start) ? 0.0f : start, end - start);
113 228 : _scroll->updateScrollBounds();
114 228 : _infoDirty = false;
115 228 : force = false;
116 : }
117 :
118 644 : float pos = _scroll->getScrollPosition();
119 644 : float size = _scroll->getScrollSize();
120 :
121 644 : if (_currentSize == 0) {
122 70 : reset(pos, size);
123 : } else {
124 574 : update(pos, size);
125 : }
126 644 : } while (_infoDirty || force);
127 : }
128 :
129 333 : void ScrollController::onScroll(float delta, bool eneded) {
130 :
131 333 : }
132 :
133 0 : void ScrollController::onOverscroll(float delta) {
134 :
135 0 : }
136 :
137 4 : Node *ScrollController::getRoot() const {
138 4 : return _root;
139 : }
140 :
141 4 : ScrollViewBase *ScrollController::getScroll() const {
142 4 : return _scroll;
143 : }
144 :
145 282 : float ScrollController::getScrollMin() {
146 282 : return _currentMin;
147 : }
148 282 : float ScrollController::getScrollMax() {
149 282 : return _currentMax;
150 : }
151 :
152 68 : void ScrollController::clear() {
153 652 : for (auto &it : _nodes) {
154 584 : if (it.node) {
155 330 : it.node->removeFromParent();
156 330 : it.node = nullptr;
157 : }
158 : }
159 :
160 68 : _nodes.clear();
161 68 : _currentSize = 0.0f;
162 68 : _currentPosition = 0.0f;
163 :
164 68 : _currentMin = 0.0f;
165 68 : _currentMax = 0.0f;
166 68 : }
167 :
168 574 : void ScrollController::update(float position, float size) {
169 574 : reset(position, size);
170 574 : }
171 :
172 644 : void ScrollController::reset(float origPosition, float origSize) {
173 644 : float windowBegin = nan();
174 644 : float windowEnd = nan();
175 :
176 644 : float position = origPosition - 8.0f;
177 644 : float size = origSize + 16.0f;
178 :
179 644 : if (_animationPadding > 0.0f) {
180 148 : size += _animationPadding;
181 496 : } else if (_animationPadding < 0.0f) {
182 0 : position += _animationPadding;
183 0 : size -= _animationPadding;
184 : }
185 :
186 388156 : for (auto &it : _nodes) {
187 387512 : auto nodePos = _scroll->getNodeScrollPosition(it.pos);
188 387512 : auto nodeSize = _scroll->getNodeScrollSize(it.size);
189 387512 : if (nodePos + nodeSize > position && nodePos < position + size) {
190 26681 : if (it.node) {
191 10042 : if (isnan(windowBegin) || windowBegin > nodePos) {
192 423 : windowBegin = nodePos;
193 : }
194 10042 : if (isnan(windowEnd) || windowEnd < nodePos + nodeSize) {
195 2156 : windowEnd = nodePos + nodeSize;
196 : }
197 : }
198 : }
199 : }
200 :
201 644 : _windowBegin = windowBegin;
202 644 : _windowEnd = windowEnd;
203 :
204 388156 : for (auto &it : _nodes) {
205 387512 : auto nodePos = _scroll->getNodeScrollPosition(it.pos);
206 387512 : auto nodeSize = _scroll->getNodeScrollSize(it.size);
207 387512 : if (nodePos + nodeSize <= position || nodePos >= position + size) {
208 360831 : if (it.node && (!_keepNodes || it.node->isVisible())) {
209 14719 : removeScrollNode(it);
210 : }
211 : } else {
212 26681 : onNextObject(it, nodePos, nodeSize);
213 : }
214 : }
215 :
216 644 : _currentPosition = origPosition;
217 644 : _currentSize = origSize;
218 644 : }
219 :
220 26681 : void ScrollController::onNextObject(Item &h, float pos, float size) {
221 26681 : if (!_scroll || !_root) {
222 0 : return;
223 : }
224 :
225 26681 : if (!h.node && h.nodeFunction) {
226 16639 : auto node = h.nodeFunction(h);
227 16639 : if (node) {
228 16639 : bool forward = true;
229 16639 : if (!isnan(_windowBegin) && !isnan(_windowEnd)) {
230 3777 : float windowMid = (_windowBegin + _windowEnd) / 2.0f;
231 3777 : if (pos + size < windowMid) {
232 0 : forward = false;
233 3777 : } else if (pos > windowMid) {
234 3777 : forward = true;
235 : }
236 : }
237 :
238 16639 : h.node = node;
239 16639 : addScrollNode(h);
240 :
241 16639 : if (auto handle = node->getComponentByType<ScrollItemHandle>()) {
242 0 : h.handle = handle;
243 0 : _scroll->updateScrollNode(node, h.pos, h.size, h.zIndex, h.name);
244 0 : handle->onNodeInserted(this, h, size_t(&h - _nodes.data()));
245 :
246 0 : auto nodeSize = _scroll->getNodeScrollSize(node->getContentSize());
247 0 : if (nodeSize > 0.0f && nodeSize != size) {
248 0 : resizeItem(&h, nodeSize, forward);
249 : }
250 : }
251 : }
252 26681 : } else if (h.node) {
253 10042 : h.node->setVisible(true);
254 : // h.node->pushForceRendering();
255 10042 : if (h.handle) {
256 0 : h.handle->onNodeUpdated(this, h, size_t(&h - _nodes.data()));
257 :
258 0 : auto nodeSize = _scroll->getNodeScrollSize(h.node->getContentSize());
259 0 : if (nodeSize > 0.0f && nodeSize != size) {
260 0 : resizeItem(&h, nodeSize, true);
261 : }
262 : }
263 10042 : _scroll->updateScrollNode(h.node, h.pos, h.size, h.zIndex, h.name);
264 : }
265 : }
266 :
267 400 : size_t ScrollController::addItem(NodeFunction &&fn, Size2 size, Vec2 vec, ZOrder z, StringView tag) {
268 400 : _nodes.emplace_back(move(fn), vec, size, z, tag);
269 400 : _infoDirty = true;
270 400 : return _nodes.size() - 1;
271 : }
272 :
273 28710 : size_t ScrollController::addItem(NodeFunction &&fn, float size, float pos, ZOrder z, StringView tag) {
274 28710 : if (!_scroll) {
275 0 : return std::numeric_limits<size_t>::max();
276 : }
277 :
278 28710 : _nodes.emplace_back(move(fn), _scroll->getPositionForNode(pos), _scroll->getContentSizeForNode(size), z, tag);
279 28710 : _infoDirty = true;
280 28710 : return _nodes.size() - 1;
281 : }
282 :
283 380 : size_t ScrollController::addItem(NodeFunction &&fn, float size, ZOrder zIndex, StringView tag) {
284 380 : if (!_scroll) {
285 0 : return std::numeric_limits<size_t>::max();
286 : }
287 :
288 380 : auto pos = 0.0f;
289 380 : if (!_nodes.empty()) {
290 364 : pos = _scroll->getNodeScrollPosition(_nodes.back().pos) + _scroll->getNodeScrollSize(_nodes.back().size);
291 : }
292 :
293 380 : return addItem(move(fn), size, pos, zIndex, tag);
294 : }
295 :
296 0 : size_t ScrollController::addPlaceholder(Size2 size, Vec2 pos) {
297 0 : return addItem([] (const Item &item) -> Rc<Node> {
298 0 : return Rc<Node>::create();
299 0 : }, size, pos);
300 : }
301 0 : size_t ScrollController::addPlaceholder(float size, float pos) {
302 0 : return addItem([] (const Item &item) -> Rc<Node> {
303 0 : return Rc<Node>::create();
304 0 : }, size, pos);
305 : }
306 36 : size_t ScrollController::addPlaceholder(float size) {
307 72 : return addItem([] (const Item &item) -> Rc<Node> {
308 30 : return Rc<Node>::create();
309 72 : }, size);
310 : }
311 :
312 4 : float ScrollController::getNextItemPosition() const {
313 4 : if (!_nodes.empty()) {
314 4 : return _scroll->getNodeScrollPosition(_nodes.back().pos) + _scroll->getNodeScrollSize(_nodes.back().size);
315 : }
316 0 : return 0.0f;
317 : }
318 :
319 4 : void ScrollController::setKeepNodes(bool value) {
320 4 : if (_keepNodes != value) {
321 4 : _keepNodes = value;
322 : }
323 4 : }
324 4 : bool ScrollController::isKeepNodes() const {
325 4 : return _keepNodes;
326 : }
327 :
328 8 : const ScrollController::Item *ScrollController::getItem(size_t n) {
329 8 : if (n < _nodes.size()) {
330 8 : _infoDirty = true;
331 8 : return &_nodes[n];
332 : }
333 0 : return nullptr;
334 : }
335 :
336 8 : const ScrollController::Item *ScrollController::getItem(Node *node) {
337 8 : if (node) {
338 106 : for (auto &it : _nodes) {
339 106 : if (node == it.node) {
340 8 : _infoDirty = true;
341 8 : return ⁢
342 : }
343 : }
344 : }
345 0 : return nullptr;
346 : }
347 :
348 8 : const ScrollController::Item *ScrollController::getItem(StringView str) {
349 8 : if (str.empty()) {
350 0 : return nullptr;
351 : }
352 66 : for (auto &it : _nodes) {
353 66 : if (it.name == str && it.node) {
354 8 : return ⁢
355 : }
356 : }
357 0 : return nullptr;
358 : }
359 :
360 8 : size_t ScrollController::getItemIndex(Node *node) {
361 8 : size_t idx = 0;
362 58 : for (auto &it : _nodes) {
363 58 : if (it.node == node) {
364 8 : return idx;
365 : }
366 50 : idx++;
367 : }
368 0 : return std::numeric_limits<size_t>::max();
369 : }
370 :
371 0 : const Vector<ScrollController::Item> &ScrollController::getItems() const {
372 0 : return _nodes;
373 : }
374 :
375 34 : Vector<ScrollController::Item> &ScrollController::getItems() {
376 34 : _infoDirty = true;
377 34 : return _nodes;
378 : }
379 :
380 4 : size_t ScrollController::size() const {
381 4 : return _nodes.size();
382 : }
383 :
384 228 : bool ScrollController::setScrollableArea(float offset, float size) {
385 228 : if (_scrollAreaOffset != offset || _scrollAreaSize != size) {
386 64 : _scrollAreaOffset = offset;
387 64 : _scrollAreaSize = size;
388 64 : _currentMin = _scrollAreaOffset;
389 64 : _currentMax = _scrollAreaOffset + _scrollAreaSize;
390 64 : if (_scroll) {
391 64 : _scroll->setScrollDirty(true);
392 : }
393 64 : return true;
394 : }
395 164 : return false;
396 : }
397 :
398 3778 : float ScrollController::getScrollableAreaOffset() const {
399 3778 : return _scrollAreaOffset;
400 : }
401 :
402 3409 : float ScrollController::getScrollableAreaSize() const {
403 3409 : return _scrollAreaSize;
404 : }
405 :
406 36 : bool ScrollController::rebuildObjects() {
407 36 : if (_callback) {
408 14 : return _callback(this);
409 : }
410 22 : return false;
411 : }
412 :
413 4 : void ScrollController::setScrollRelativeValue(float value) {
414 4 : if (!_scroll) {
415 0 : return;
416 : }
417 :
418 4 : onScrollPosition();
419 :
420 4 : if (!isnan(value)) {
421 4 : if (value < 0.0f) {
422 0 : value = 0.0f;
423 4 : } else if (value > 1.0f) {
424 0 : value = 1.0f;
425 : }
426 : } else {
427 0 : value = 0.0f;
428 : }
429 :
430 4 : float areaSize = _scroll->getScrollableAreaSize();
431 4 : float areaOffset = _scroll->getScrollableAreaOffset();
432 4 : float size = _scroll->getScrollSize();
433 :
434 4 : auto &padding = _scroll->getPadding();
435 4 : auto paddingFront = (_scroll->isVertical())?padding.top:padding.left;
436 4 : auto paddingBack = (_scroll->isVertical())?padding.bottom:padding.right;
437 :
438 4 : if (!isnan(areaSize) && !isnan(areaOffset)) {
439 4 : float liveSize = areaSize - size + paddingFront + paddingBack;
440 4 : float pos = (value * liveSize) - paddingFront + areaOffset;
441 4 : clear();
442 4 : _scroll->setScrollPosition(pos);
443 : }
444 : }
445 :
446 16639 : void ScrollController::addScrollNode(Item &it) {
447 16639 : if (it.node) {
448 16639 : _scroll->addScrollNode(it.node, it.pos, it.size, it.zIndex, it.name);
449 : // it.node->pushForceRendering();
450 : }
451 16639 : }
452 :
453 406 : void ScrollController::updateScrollNode(Item &it) {
454 406 : if (it.node) {
455 240 : _scroll->updateScrollNode(it.node, it.pos, it.size, it.zIndex, it.name);
456 : }
457 406 : }
458 :
459 14719 : void ScrollController::removeScrollNode(Item &it) {
460 14719 : if (it.node) {
461 14719 : if (!_keepNodes) {
462 14709 : if (it.handle) {
463 0 : if (it.handle->isLocked()) {
464 0 : return; // do not remove locked item
465 : }
466 0 : it.handle->onNodeRemoved(this, it, size_t(&it - _nodes.data()));
467 : }
468 14709 : if (_scroll->removeScrollNode(it.node)) {
469 14709 : it.node = nullptr;
470 14709 : it.handle = nullptr;
471 : }
472 : } else {
473 10 : it.node->setVisible(false);
474 : }
475 : }
476 : }
477 :
478 24 : Vector<Rc<Node>> ScrollController::getNodes() const {
479 24 : Vector<Rc<Node>> ret;
480 328 : for (auto &it : _nodes) {
481 304 : if (it.node) {
482 52 : ret.emplace_back(it.node);
483 : }
484 : }
485 24 : return ret;
486 0 : }
487 :
488 8 : Node * ScrollController::getNodeByName(StringView str) const {
489 8 : if (str.empty()) {
490 0 : return nullptr;
491 : }
492 66 : for (auto &it : _nodes) {
493 66 : if (it.name == str && it.node) {
494 8 : return it.node;
495 : }
496 : }
497 0 : return nullptr;
498 : }
499 :
500 4 : Node * ScrollController::getFrontNode() const {
501 4 : Node *ret = nullptr;
502 4 : float pos = _currentMax;
503 4 : if (!_nodes.empty() && _scroll) {
504 308 : for (auto &it : _nodes) {
505 304 : auto npos = _scroll->getNodeScrollPosition(it.pos);
506 304 : if (it.node && npos < pos) {
507 4 : pos = npos;
508 4 : ret = it.node;
509 : }
510 : }
511 : }
512 4 : return ret;
513 : }
514 :
515 4 : Node * ScrollController::getBackNode() const {
516 4 : Node *ret = nullptr;
517 4 : float pos = _currentMin;
518 4 : if (!_nodes.empty() && _scroll) {
519 308 : for (auto it = _nodes.rbegin(); it != _nodes.rend(); it ++) {
520 304 : auto &item = *it;
521 304 : auto npos = _scroll->getNodeScrollPosition(item.pos);
522 304 : auto size = _scroll->getNodeScrollSize(item.size);
523 304 : if (item.node && npos + size > pos) {
524 4 : pos = npos + size;
525 4 : ret = item.node;
526 : }
527 : }
528 : }
529 4 : return ret;
530 : }
531 :
532 8 : void ScrollController::resizeItem(const Item *item, float newSize, bool forward) {
533 8 : auto &items = getItems();
534 :
535 8 : if (forward) {
536 4 : float offset = 0.0f;
537 308 : for (auto &it : items) {
538 304 : if (&it == item) {
539 4 : offset += (newSize - _scroll->getNodeScrollSize(it.size));
540 4 : it.size = _scroll->isVertical()?Size2(it.size.width, newSize):Size2(newSize, it.size.height);
541 4 : if (it.node) {
542 4 : _scroll->updateScrollNode(it.node, it.pos, it.size, it.zIndex, it.name);
543 : }
544 300 : } else if (offset != 0.0f) {
545 227 : it.pos = _scroll->isVertical()?Vec2(it.pos.x, it.pos.y + offset):Vec2(it.pos.x + offset, it.pos.y);
546 227 : if (it.node) {
547 0 : _scroll->updateScrollNode(it.node, it.pos, it.size, it.zIndex, it.name);
548 : }
549 : }
550 : }
551 : } else {
552 4 : float offset = 0.0f;
553 308 : for (auto it = items.rbegin(); it != items.rend(); ++ it) {
554 304 : if (&(*it) == item) {
555 4 : offset += (newSize - _scroll->getNodeScrollSize(it->size));
556 4 : it->size = _scroll->isVertical()?Size2(it->size.width, newSize):Size2(newSize, it->size.height);
557 4 : it->pos = _scroll->isVertical()?Vec2(it->pos.x, it->pos.y - offset):Vec2(it->pos.x - offset, it->pos.y);
558 4 : if (it->node) {
559 4 : _scroll->updateScrollNode(it->node, it->pos, it->size, it->zIndex, it->name);
560 : }
561 300 : } else if (offset != 0.0f) {
562 25 : it->pos = _scroll->isVertical()?Vec2(it->pos.x, it->pos.y - offset):Vec2(it->pos.x - offset, it->pos.y);
563 25 : if (it->node) {
564 0 : _scroll->updateScrollNode(it->node, it->pos, it->size, it->zIndex, it->name);
565 : }
566 : }
567 : }
568 : }
569 8 : _infoDirty = true;
570 8 : }
571 :
572 14 : void ScrollController::setAnimationPadding(float padding) {
573 14 : if (_animationPadding != padding) {
574 14 : _animationPadding = padding;
575 14 : _infoDirty = true;
576 : }
577 14 : }
578 :
579 52 : void ScrollController::dropAnimationPadding() {
580 52 : if (_animationPadding != 0.0f) {
581 10 : _animationPadding = 0.0f;
582 10 : _infoDirty = true;
583 : }
584 52 : }
585 :
586 204 : void ScrollController::updateAnimationPadding(float value) {
587 204 : if (_animationPadding != 0.0f) {
588 134 : auto val = _animationPadding - value;
589 134 : if (val * _animationPadding <= 0.0f) {
590 4 : _animationPadding = 0.0f;
591 4 : _infoDirty = true;
592 : } else {
593 130 : _animationPadding = val;
594 130 : _infoDirty = true;
595 : }
596 : }
597 204 : }
598 :
599 14 : void ScrollController::setRebuildCallback(const RebuildCallback &cb) {
600 14 : _callback = cb;
601 14 : }
602 :
603 4 : const ScrollController::RebuildCallback &ScrollController::getRebuildCallback() const {
604 4 : return _callback;
605 : }
606 :
607 : }
|