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 "MaterialDataScroll.h"
24 : #include "XLDirector.h"
25 : #include "XLApplication.h"
26 : #include "XL2dLayerRounded.h"
27 : #include "XL2dScrollController.h"
28 : #include "XLIcons.h"
29 :
30 : namespace STAPPLER_VERSIONIZED stappler::xenolith::material2d {
31 :
32 10 : bool DataScroll::Loader::init(const Function<void()> &cb) {
33 10 : if (!Node::init()) {
34 0 : return false;
35 : }
36 :
37 10 : _callback = cb;
38 :
39 10 : setCascadeOpacityEnabled(true);
40 :
41 10 : _icon = addChild(Rc<IconSprite>::create(IconName::Dynamic_Loader));
42 10 : _icon->setContentSize(Size2(36.0f, 36.0f));
43 10 : _icon->setAnchorPoint(Vec2(0.5f, 0.5f));
44 :
45 10 : return true;
46 : }
47 :
48 10 : void DataScroll::Loader::onContentSizeDirty() {
49 10 : Node::onContentSizeDirty();
50 10 : _icon->setPosition(_contentSize / 2.0f);
51 10 : }
52 :
53 10 : void DataScroll::Loader::onEnter(Scene *scene) {
54 10 : Node::onEnter(scene);
55 10 : _icon->animate();
56 10 : if (_callback) {
57 0 : _callback();
58 : }
59 10 : }
60 :
61 10 : void DataScroll::Loader::onExit() {
62 10 : stopAllActions();
63 10 : Node::onExit();
64 10 : _icon->stopAllActions();
65 10 : }
66 :
67 :
68 400 : bool DataScroll::Item::init(Value &&val, Vec2 pos, Size2 size) {
69 400 : _data = std::move(val);
70 400 : _position = pos;
71 400 : _size = size;
72 400 : return true;
73 : }
74 :
75 0 : const Value &DataScroll::Item::getData() const {
76 0 : return _data;
77 : }
78 :
79 960 : Size2 DataScroll::Item::getContentSize() const {
80 960 : return _size;
81 : }
82 :
83 680 : Vec2 DataScroll::Item::getPosition() const {
84 680 : return _position;
85 : }
86 :
87 0 : void DataScroll::Item::setPosition(Vec2 pos) {
88 0 : _position = pos;
89 0 : }
90 :
91 0 : void DataScroll::Item::setContentSize(Size2 size) {
92 0 : _size = size;
93 0 : }
94 :
95 400 : void DataScroll::Item::setId(uint64_t id) {
96 400 : _id = id;
97 400 : }
98 280 : uint64_t DataScroll::Item::getId() const {
99 280 : return _id;
100 : }
101 :
102 400 : void DataScroll::Item::setControllerId(size_t value) {
103 400 : _controllerId = value;
104 400 : }
105 0 : size_t DataScroll::Item::getControllerId() const {
106 0 : return _controllerId;
107 : }
108 :
109 20 : bool DataScroll::Handler::init(DataScroll *s) {
110 20 : _size = s->getRoot()->getContentSize();
111 20 : _layout = s->getLayout();
112 20 : _scroll = s;
113 :
114 20 : return true;
115 : }
116 :
117 0 : void DataScroll::Handler::setCompleteCallback(CompleteCallback &&cb) {
118 0 : _callback = move(cb);
119 0 : }
120 :
121 20 : const DataScroll::Handler::CompleteCallback &DataScroll::Handler::getCompleteCallback() const {
122 20 : return _callback;
123 : }
124 :
125 0 : Size2 DataScroll::Handler::getContentSize() const {
126 0 : return _size;
127 : }
128 :
129 0 : DataScroll * DataScroll::Handler::getScroll() const {
130 0 : return _scroll;
131 : }
132 :
133 10 : bool DataScroll::init(DataSource *source, Layout l) {
134 10 : if (!ScrollView::init(l)) {
135 0 : return false;
136 : }
137 :
138 10 : setScrollMaxVelocity(5000.0f);
139 :
140 10 : _sourceListener = addComponent(Rc<DataListener<DataSource>>::create(std::bind(&DataScroll::onSourceDirty, this), source));
141 10 : _sourceListener->setSubscription(source);
142 :
143 10 : setController(Rc<ScrollController>::create());
144 :
145 10 : return true;
146 : }
147 :
148 30 : void DataScroll::onContentSizeDirty() {
149 30 : ScrollView::onContentSizeDirty();
150 30 : if ((isVertical() && _savedSize != _contentSize.width) || (!isVertical() && _savedSize != _contentSize.height)) {
151 10 : _savedSize = isVertical()?_contentSize.width:_contentSize.height;
152 10 : onSourceDirty();
153 : }
154 30 : }
155 :
156 0 : void DataScroll::reset() {
157 0 : _controller->clear();
158 :
159 0 : auto min = getScrollMinPosition();
160 0 : if (!isnan(min)) {
161 0 : setScrollPosition(min);
162 : } else {
163 0 : setScrollPosition(0.0f - (isVertical()?_paddingGlobal.top:_paddingGlobal.left));
164 : }
165 0 : }
166 :
167 0 : Value DataScroll::save() const {
168 0 : Value ret;
169 0 : ret.setDouble(getScrollRelativePosition(), "value");
170 0 : ret.setInteger(_currentSliceStart.get(), "start");
171 0 : ret.setInteger(_currentSliceLen, "len");
172 0 : return ret;
173 0 : }
174 :
175 0 : void DataScroll::load(const Value &d) {
176 0 : if (d.isDictionary()) {
177 0 : _savedRelativePosition = d.getDouble("value");
178 0 : _currentSliceStart = DataSource::Id(d.getInteger("start"));
179 0 : _currentSliceLen = (size_t)d.getInteger("len");
180 0 : updateSlice();
181 : }
182 0 : }
183 :
184 0 : const DataScroll::ItemMap &DataScroll::getItems() const {
185 0 : return _items;
186 : }
187 :
188 0 : void DataScroll::setSource(DataSource *c) {
189 0 : if (c != _sourceListener->getSubscription()) {
190 0 : _sourceListener->setSubscription(c);
191 0 : _categoryDirty = true;
192 :
193 0 : _invalidateAfter = stappler::Time::now();
194 :
195 0 : if (_contentSize != Size2::ZERO) {
196 0 : _controller->clear();
197 :
198 0 : if (isVertical()) {
199 0 : _controller->addItem(std::bind(&DataScroll::onLoaderRequest, this, Request::Reset), _loaderSize, 0.0f);
200 : } else {
201 0 : auto size = _contentSize.width - _paddingGlobal.left - _loaderSize;
202 0 : _controller->addItem(std::bind(&DataScroll::onLoaderRequest, this, Request::Reset), max(_loaderSize, size), 0.0f);
203 : }
204 :
205 0 : setScrollPosition(0.0f);
206 : }
207 : }
208 0 : }
209 :
210 0 : DataSource *DataScroll::getSource() const {
211 0 : return _sourceListener->getSubscription();
212 : }
213 :
214 0 : void DataScroll::setLookupLevel(uint32_t level) {
215 0 : _categoryLookupLevel = level;
216 0 : _categoryDirty = true;
217 0 : _sourceListener->setDirty();
218 0 : }
219 :
220 0 : uint32_t DataScroll::getLookupLevel() const {
221 0 : return _categoryLookupLevel;
222 : }
223 :
224 0 : void DataScroll::setItemsForSubcats(bool value) {
225 0 : _itemsForSubcats = value;
226 0 : _categoryDirty = true;
227 0 : _sourceListener->setDirty();
228 0 : }
229 :
230 0 : bool DataScroll::isItemsForSubcat() const {
231 0 : return _itemsForSubcats;
232 : }
233 :
234 0 : void DataScroll::setCategoryBounds(bool value) {
235 0 : if (_useCategoryBounds != value) {
236 0 : _useCategoryBounds = value;
237 0 : _categoryDirty = true;
238 : }
239 0 : }
240 :
241 0 : bool DataScroll::hasCategoryBounds() const {
242 0 : return _useCategoryBounds;
243 : }
244 :
245 0 : void DataScroll::setMaxSize(size_t max) {
246 0 : _sliceMax = max;
247 0 : _categoryDirty = true;
248 0 : _sourceListener->setDirty();
249 0 : }
250 :
251 0 : size_t DataScroll::getMaxSize() const {
252 0 : return _sliceMax;
253 : }
254 :
255 0 : void DataScroll::setOriginId(DataSource::Id id) {
256 0 : _sliceOrigin = id;
257 0 : }
258 :
259 0 : DataSource::Id DataScroll::getOriginId() const {
260 0 : return _sliceOrigin;
261 : }
262 :
263 10 : void DataScroll::setLoaderSize(float value) {
264 10 : _loaderSize = value;
265 10 : }
266 :
267 0 : float DataScroll::getLoaderSize() const {
268 0 : return _loaderSize;
269 : }
270 :
271 10 : void DataScroll::setMinLoadTime(TimeInterval time) {
272 10 : _minLoadTime = time;
273 10 : }
274 0 : TimeInterval DataScroll::getMinLoadTime() const {
275 0 : return _minLoadTime;
276 : }
277 :
278 10 : void DataScroll::setHandlerCallback(HandlerCallback &&cb) {
279 10 : _handlerCallback = move(cb);
280 10 : }
281 :
282 10 : void DataScroll::setItemCallback(ItemCallback &&cb) {
283 10 : _itemCallback = move(cb);
284 10 : }
285 :
286 10 : void DataScroll::setLoaderCallback(LoaderCallback &&cb) {
287 10 : _loaderCallback = move(cb);
288 10 : }
289 :
290 20 : void DataScroll::onSourceDirty() {
291 20 : if ((isVertical() && _contentSize.height == 0.0f) || (!isVertical() && _contentSize.width == 0.0f)) {
292 0 : return;
293 : }
294 :
295 20 : if (!_sourceListener->getSubscription() || _items.size() == 0) {
296 10 : _controller->clear();
297 10 : if (isVertical()) {
298 10 : _controller->addItem(std::bind(&DataScroll::onLoaderRequest, this, Request::Reset), _loaderSize, 0.0f);
299 : } else {
300 0 : auto size = _contentSize.width - _paddingGlobal.left - _loaderSize;
301 0 : _controller->addItem(std::bind(&DataScroll::onLoaderRequest, this, Request::Reset), std::max(_loaderSize, size), 0.0f);
302 : }
303 : }
304 :
305 20 : if (!_sourceListener->getSubscription()) {
306 0 : return;
307 : }
308 :
309 20 : auto source = _sourceListener->getSubscription();
310 20 : bool init = (_itemsCount == 0);
311 20 : _itemsCount = source->getCount(_categoryLookupLevel, _itemsForSubcats);
312 :
313 20 : if (_itemsCount == 0) {
314 0 : _categoryDirty = true;
315 0 : _currentSliceStart = DataSource::Id(0);
316 0 : _currentSliceLen = 0;
317 0 : return;
318 20 : } else if (_itemsCount <= _sliceMax) {
319 20 : _slicesCount = 1;
320 20 : _sliceSize = _itemsCount;
321 : } else {
322 0 : _slicesCount = (_itemsCount + _sliceMax - 1) / _sliceMax;
323 0 : _sliceSize = _itemsCount / _slicesCount + 1;
324 : }
325 :
326 20 : if ((!init && _categoryDirty) || _currentSliceLen == 0) {
327 10 : resetSlice();
328 : } else {
329 10 : updateSlice();
330 : }
331 :
332 20 : _scrollDirty = true;
333 20 : _categoryDirty = false;
334 : }
335 :
336 0 : size_t DataScroll::getMaxId() const {
337 0 : if (!_sourceListener->getSubscription()) {
338 0 : return 0;
339 : }
340 :
341 0 : auto ret = _sourceListener->getSubscription()->getCount(_categoryLookupLevel, _itemsForSubcats);
342 0 : if (ret == 0) {
343 0 : return 0;
344 : } else {
345 0 : return ret - 1;
346 : }
347 : }
348 :
349 0 : std::pair<DataSource *, bool> DataScroll::getSourceCategory(int64_t id) {
350 0 : if (!_sourceListener->getSubscription()) {
351 0 : return std::make_pair(nullptr, false);
352 : }
353 :
354 0 : return _sourceListener->getSubscription()->getItemCategory(DataSource::Id(id), _categoryLookupLevel, _itemsForSubcats);
355 : }
356 :
357 20 : bool DataScroll::requestSlice(DataSource::Id first, size_t count, Request type) {
358 20 : if (!_sourceListener->getSubscription()) {
359 0 : return false;
360 : }
361 :
362 20 : if (first.get() >= _itemsCount) {
363 0 : return false;
364 : }
365 :
366 20 : if (first.get() + count > _itemsCount) {
367 0 : count = _itemsCount - size_t(first.get());
368 : }
369 :
370 20 : if (_useCategoryBounds) {
371 0 : _sourceListener->getSubscription()->setCategoryBounds(first, count, _categoryLookupLevel, _itemsForSubcats);
372 : }
373 :
374 20 : _invalidateAfter = stappler::Time::now();
375 40 : _sourceListener->getSubscription()->getSliceData(
376 40 : std::bind(&DataScroll::onSliceData, Rc<DataScroll>(this), std::placeholders::_1, Time::now(), type),
377 20 : first, count, _categoryLookupLevel, _itemsForSubcats);
378 :
379 20 : return true;
380 : }
381 :
382 10 : bool DataScroll::updateSlice() {
383 10 : auto size = std::max(_currentSliceLen, _sliceSize);
384 10 : auto first = _currentSliceStart;
385 10 : if (size > _itemsCount) {
386 0 : size = _itemsCount;
387 : }
388 10 : if (first.get() > _itemsCount - size) {
389 0 : first = DataSource::Id(_itemsCount - size);
390 : }
391 20 : return requestSlice(first, size, Request::Update);
392 : }
393 :
394 10 : bool DataScroll::resetSlice() {
395 10 : if (_sourceListener->getSubscription()) {
396 10 : int64_t start = int64_t(_sliceOrigin.get()) - int64_t(_sliceSize) / 2;
397 10 : if (start + int64_t(_sliceSize) > int64_t(_itemsCount)) {
398 0 : start = int64_t(_itemsCount) - int64_t(_sliceSize);
399 : }
400 10 : if (start < 0) {
401 10 : start = 0;
402 : }
403 10 : return requestSlice(DataSource::Id(start), _sliceSize, Request::Reset);
404 : }
405 0 : return false;
406 : }
407 :
408 0 : bool DataScroll::downloadFrontSlice(size_t size) {
409 0 : if (size == 0) {
410 0 : size = _sliceSize;
411 : }
412 :
413 0 : if (_sourceListener->getSubscription() && !_currentSliceStart.empty()) {
414 0 : DataSource::Id first(0);
415 0 : if (_currentSliceStart.get() > _sliceSize) {
416 0 : first = _currentSliceStart - DataSource::Id(_sliceSize);
417 : } else {
418 0 : size = size_t(_currentSliceStart.get());
419 : }
420 :
421 0 : return requestSlice(first, size, Request::Front);
422 : }
423 0 : return false;
424 : }
425 :
426 0 : bool DataScroll::downloadBackSlice(size_t size) {
427 0 : if (size == 0) {
428 0 : size = _sliceSize;
429 : }
430 :
431 0 : if (_sourceListener->getSubscription() && _currentSliceStart.get() + _currentSliceLen != _itemsCount) {
432 0 : DataSource::Id first(_currentSliceStart.get() + _currentSliceLen);
433 0 : if (first.get() + size > _itemsCount) {
434 0 : size = _itemsCount - size_t(first.get());
435 : }
436 :
437 0 : return requestSlice(first, size, Request::Back);
438 : }
439 0 : return false;
440 : }
441 :
442 20 : void DataScroll::onSliceData(DataMap &val, Time time, Request type) {
443 20 : if (time < _invalidateAfter || !_handlerCallback) {
444 0 : return;
445 : }
446 :
447 20 : if (!_director) {
448 0 : return;
449 : }
450 :
451 20 : if (_items.empty() && type != Request::Update) {
452 10 : type = Request::Reset;
453 : }
454 :
455 20 : auto itemPtr = new ItemMap();
456 20 : auto dataPtr = new DataMap(std::move(val));
457 20 : auto handler = onHandler();
458 :
459 20 : auto deferred = _director->getApplication();
460 :
461 20 : deferred->perform(Rc<thread::Task>::create([handler, itemPtr, dataPtr, time, type] (const thread::Task &) -> bool {
462 20 : (*itemPtr) = handler->run(type, std::move(*dataPtr));
463 420 : for (auto &it : (*itemPtr)) {
464 400 : it.second->setId(it.first.get());
465 : }
466 20 : return true;
467 130 : }, [this, handler, itemPtr, dataPtr, time, type] (const thread::Task &, bool) {
468 20 : onSliceItems(std::move(*itemPtr), time, type);
469 :
470 20 : auto interval = Time::now() - time;
471 20 : if (interval < _minLoadTime && type != Request::Update) {
472 10 : auto a = Rc<Sequence>::create(_minLoadTime - interval, [guard = Rc<DataScroll>(this), handler, itemPtr, dataPtr] {
473 10 : if (guard->isRunning()) {
474 10 : const auto & cb = handler->getCompleteCallback();
475 10 : if (cb) {
476 0 : cb();
477 : }
478 : }
479 :
480 10 : delete itemPtr;
481 10 : delete dataPtr;
482 10 : });
483 10 : runAction(a);
484 10 : } else {
485 10 : const auto & cb = handler->getCompleteCallback();
486 10 : if (cb) {
487 0 : cb();
488 : }
489 :
490 10 : delete itemPtr;
491 10 : delete dataPtr;
492 : }
493 40 : }, this));
494 20 : }
495 :
496 20 : void DataScroll::onSliceItems(ItemMap &&val, Time time, Request type) {
497 20 : if (time < _invalidateAfter) {
498 0 : return;
499 : }
500 :
501 20 : if (_items.size() > _sliceSize) {
502 0 : if (type == Request::Back) {
503 0 : auto newId = _items.begin()->first + DataSource::Id(_items.size() - _sliceSize);
504 0 : _items.erase(_items.begin(), _items.lower_bound(newId));
505 0 : } else if (type == Request::Front) {
506 0 : auto newId = _items.begin()->first + DataSource::Id(_sliceSize);
507 0 : _items.erase(_items.upper_bound(newId), _items.end());
508 : }
509 : }
510 :
511 20 : if (type == Request::Front || type == Request::Back) {
512 0 : val.insert(_items.begin(), _items.end()); // merge item maps
513 : }
514 :
515 20 : _items = std::move(val);
516 :
517 20 : _currentSliceStart = DataSource::Id(_items.begin()->first);
518 20 : _currentSliceLen = size_t(_items.rbegin()->first.get()) + 1 - size_t(_currentSliceStart.get());
519 :
520 20 : float relPos = getScrollRelativePosition();
521 :
522 20 : updateItems();
523 :
524 20 : if (type == Request::Update) {
525 10 : setScrollRelativePosition(relPos);
526 10 : } else if (type == Request::Reset) {
527 10 : if (_sliceOrigin.empty()) {
528 10 : setScrollRelativePosition(0.0f);
529 : } else {
530 0 : auto it = _items.find(DataSource::Id(_sliceOrigin));
531 0 : if (it == _items.end()) {
532 0 : setScrollRelativePosition(0.0f);
533 : } else {
534 0 : auto start = _items.begin()->second->getPosition();
535 0 : auto end = _items.rbegin()->second->getPosition();
536 :
537 0 : if (isVertical()) {
538 0 : setScrollRelativePosition(fabs((it->second->getPosition().y - start.y) / (end.y - start.y)));
539 : } else {
540 0 : setScrollRelativePosition(fabs((it->second->getPosition().x - start.x) / (end.x - start.x)));
541 : }
542 : }
543 : }
544 : }
545 : }
546 :
547 20 : void DataScroll::updateItems() {
548 20 : _controller->clear();
549 :
550 20 : if (!_items.empty()) {
551 20 : if (_items.begin()->first.get() > 0) {
552 0 : Item * first = _items.begin()->second;
553 0 : _controller->addItem(std::bind(&DataScroll::onLoaderRequest, this, Request::Front),
554 : _loaderSize,
555 0 : (_layout == Vertical)?(first->getPosition().y - _loaderSize):(first->getPosition().x - _loaderSize));
556 : }
557 :
558 420 : for (auto &it : _items) {
559 800 : it.second->setControllerId(_controller->addItem(std::bind(&DataScroll::onItemRequest, this, std::placeholders::_1, it.first),
560 400 : it.second->getContentSize(), it.second->getPosition()));
561 : }
562 :
563 20 : if (_items.rbegin()->first.get() < _itemsCount - 1) {
564 0 : Item * last = _items.rbegin()->second;
565 0 : _controller->addItem(std::bind(&DataScroll::onLoaderRequest, this, Request::Back),
566 : _loaderSize,
567 0 : (_layout == Vertical)?(last->getPosition().y + last->getContentSize().height):(last->getPosition().x + last->getContentSize().width));
568 : }
569 : } else {
570 0 : _controller->addItem(std::bind(&DataScroll::onLoaderRequest, this, Request::Reset), _loaderSize, 0.0f);
571 : }
572 :
573 20 : auto b = _movement;
574 20 : _movement = Movement::None;
575 :
576 20 : updateScrollBounds();
577 20 : onPosition();
578 :
579 20 : _movement = b;
580 20 : }
581 :
582 20 : Rc<DataScroll::Handler> DataScroll::onHandler() {
583 20 : return _handlerCallback(this);
584 : }
585 :
586 280 : Rc<Surface> DataScroll::onItemRequest(const ScrollController::Item &item, DataSource::Id id) {
587 280 : if ((isVertical() && item.size.height > 0.0f) || (isHorizontal() && item.size.width > 0)) {
588 280 : auto it = _items.find(id);
589 280 : if (it != _items.end()) {
590 280 : if (_itemCallback) {
591 280 : return _itemCallback(it->second);
592 : }
593 : }
594 : }
595 0 : return nullptr;
596 : }
597 :
598 10 : Rc<DataScroll::Loader> DataScroll::onLoaderRequest(Request type) {
599 10 : if (type == Request::Back) {
600 0 : if (_loaderCallback) {
601 0 : return _loaderCallback(type, [this] {
602 0 : downloadBackSlice(_sliceSize);
603 0 : });
604 : } else {
605 0 : return Rc<Loader>::create([this] {
606 0 : downloadBackSlice(_sliceSize);
607 0 : });
608 : }
609 10 : } else if (type == Request::Front) {
610 0 : if (_loaderCallback) {
611 0 : return _loaderCallback(type, [this] {
612 0 : downloadFrontSlice(_sliceSize);
613 0 : });
614 : } else {
615 0 : return Rc<Loader>::create([this] {
616 0 : downloadFrontSlice(_sliceSize);
617 0 : });
618 : }
619 : } else {
620 10 : if (_loaderCallback) {
621 10 : return _loaderCallback(type, nullptr);
622 : } else {
623 0 : return Rc<Loader>::create(nullptr);
624 : }
625 : }
626 : return nullptr;
627 : }
628 :
629 400 : void DataScroll::updateIndicatorPosition() {
630 400 : if (!_indicatorVisible) {
631 0 : return;
632 : }
633 :
634 400 : const float scrollWidth = _contentSize.width;
635 400 : const float scrollHeight = _contentSize.height;
636 :
637 400 : const float itemSize = getScrollLength() / _currentSliceLen;
638 400 : const float scrollLength = itemSize * _itemsCount;
639 :
640 400 : const float min = getScrollMinPosition() - _currentSliceStart.get() * itemSize;
641 400 : const float max = getScrollMaxPosition() + (_itemsCount - _currentSliceStart.get() - _currentSliceLen) * itemSize;
642 :
643 400 : const float value = (_scrollPosition - min) / (max - min);
644 :
645 400 : ScrollView::updateIndicatorPosition(_indicator, (isVertical()?scrollHeight:scrollWidth) / scrollLength, value, true, 20.0f);
646 : }
647 :
648 0 : void DataScroll::onOverscroll(float delta) {
649 0 : if (delta > 0 && _currentSliceStart.get() + _currentSliceLen == _itemsCount) {
650 0 : ScrollView::onOverscroll(delta);
651 0 : } else if (delta < 0 && _currentSliceStart.empty()) {
652 0 : ScrollView::onOverscroll(delta);
653 : }
654 0 : }
655 :
656 : }
|