LCOV - code coverage report
Current view: top level - xenolith/renderer/material2d/components/scroll - MaterialDataScroll.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 200 393 50.9 %
Date: 2024-05-12 00:16:13 Functions: 33 67 49.3 %

          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             : }

Generated by: LCOV version 1.14