#include "GList.h" #include "GButton.h" #include "GScrollBar.h" #include "UIConfig.h" #include "UIPackage.h" #include "utils/ByteBuffer.h" NS_FGUI_BEGIN USING_NS_AX; using namespace std; GList::ItemInfo::ItemInfo() { obj = nullptr; updateFlag = 0; selected = false; } GList::GList() : foldInvisibleItems(false), _selectionMode(ListSelectionMode::SINGLE), scrollItemToViewOnClick(true), _layout(ListLayoutType::SINGLE_COLUMN), _lineCount(0), _columnCount(0), _lineGap(0), _columnGap(0), _align(TextHAlignment::LEFT), _verticalAlign(TextVAlignment::TOP), _autoResizeItem(true), _selectionController(nullptr), _pool(nullptr), _selectionHandled(false), _lastSelectedIndex(-1), _virtual(false), _loop(0), _numItems(0), _realNumItems(0), _firstIndex(-1), _virtualListChanged(false), _eventLocked(false), _itemInfoVer(0) { _trackBounds = true; setOpaque(true); _pool = new GObjectPool(); } GList::~GList() { delete _pool; if (_virtualListChanged != 0) CALL_LATER_CANCEL(GList, doRefreshVirtualList); _selectionController = nullptr; scrollItemToViewOnClick = false; } void GList::setDefaultItem(const std::string& value) { _defaultItem = UIPackage::normalizeURL(value); } void GList::setLayout(ListLayoutType value) { if (_layout != value) { _layout = value; setBoundsChangedFlag(); if (_virtual) setVirtualListChangedFlag(true); } } void GList::setLineCount(int value) { if (_lineCount != value) { _lineCount = value; if (_layout == ListLayoutType::FLOW_VERTICAL || _layout == ListLayoutType::PAGINATION) { setBoundsChangedFlag(); if (_virtual) setVirtualListChangedFlag(true); } } } void GList::setColumnCount(int value) { if (_columnCount != value) { _columnCount = value; if (_layout == ListLayoutType::FLOW_HORIZONTAL || _layout == ListLayoutType::PAGINATION) { setBoundsChangedFlag(); if (_virtual) setVirtualListChangedFlag(true); } } } void GList::setLineGap(int value) { if (_lineGap != value) { _lineGap = value; setBoundsChangedFlag(); if (_virtual) setVirtualListChangedFlag(true); } } void GList::setColumnGap(int value) { if (_columnGap != value) { _columnGap = value; setBoundsChangedFlag(); if (_virtual) setVirtualListChangedFlag(true); } } void GList::setAlign(ax::TextHAlignment value) { if (_align != value) { _align = value; setBoundsChangedFlag(); if (_virtual) setVirtualListChangedFlag(true); } } void GList::setVerticalAlign(ax::TextVAlignment value) { if (_verticalAlign != value) { _verticalAlign = value; setBoundsChangedFlag(); if (_virtual) setVirtualListChangedFlag(true); } } void GList::setAutoResizeItem(bool value) { if (_autoResizeItem != value) { _autoResizeItem = value; setBoundsChangedFlag(); if (_virtual) setVirtualListChangedFlag(true); } } GObject* GList::getFromPool(const std::string& url) { GObject* ret; if (url.length() == 0) ret = _pool->getObject(_defaultItem); else ret = _pool->getObject(url); if (ret != nullptr) ret->setVisible(true); return ret; } void GList::returnToPool(GObject* obj) { _pool->returnObject(obj); } GObject* GList::addItemFromPool(const std::string& url) { GObject* obj = getFromPool(url); return addChild(obj); } GObject* GList::addChildAt(GObject* child, int index) { GComponent::addChildAt(child, index); if (dynamic_cast(child)) { GButton* button = (GButton*)child; button->setSelected(false); button->setChangeStateOnClick(false); } child->addEventListener(UIEventType::TouchBegin, AX_CALLBACK_1(GList::onItemTouchBegin, this), EventTag(this)); child->addClickListener(AX_CALLBACK_1(GList::onClickItem, this), EventTag(this)); child->addEventListener(UIEventType::RightClick, AX_CALLBACK_1(GList::onClickItem, this), EventTag(this)); return child; } void GList::removeChildAt(int index) { GObject* child = _children.at(index); child->removeClickListener(EventTag(this)); child->removeEventListener(UIEventType::TouchBegin, EventTag(this)); child->removeEventListener(UIEventType::RightClick, EventTag(this)); GComponent::removeChildAt(index); } void GList::removeChildToPoolAt(int index) { returnToPool(getChildAt(index)); removeChildAt(index); } void GList::removeChildToPool(GObject* child) { returnToPool(child); removeChild(child); } void GList::removeChildrenToPool() { removeChildrenToPool(0, -1); } void GList::removeChildrenToPool(int beginIndex, int endIndex) { if (endIndex < 0 || endIndex >= _children.size()) endIndex = (int)_children.size() - 1; for (int i = beginIndex; i <= endIndex; ++i) removeChildToPoolAt(beginIndex); } int GList::getSelectedIndex() const { if (_virtual) { int cnt = _realNumItems; for (int i = 0; i < cnt; i++) { const ItemInfo& ii = _virtualItems[i]; if ((dynamic_cast(ii.obj) != nullptr && ((GButton*)ii.obj)->isSelected()) || (ii.obj == nullptr && ii.selected)) { if (_loop) return i % _numItems; else return i; } } } else { int cnt = (int)_children.size(); for (int i = 0; i < cnt; i++) { GButton* obj = _children.at(i)->as(); if (obj != nullptr && obj->isSelected()) return i; } } return -1; } void GList::setSelectedIndex(int value) { if (value >= 0 && value < getNumItems()) { if (_selectionMode != ListSelectionMode::SINGLE) clearSelection(); addSelection(value, false); } else clearSelection(); } void GList::setSelectionController(GController* value) { _selectionController = value; } void GList::getSelection(std::vector& result) const { result.clear(); if (_virtual) { int cnt = _realNumItems; for (int i = 0; i < cnt; i++) { const ItemInfo& ii = _virtualItems[i]; if ((dynamic_cast(ii.obj) != nullptr && ((GButton*)ii.obj)->isSelected()) || (ii.obj == nullptr && ii.selected)) { int j = i; if (_loop) { j = i % _numItems; if (std::find(result.cbegin(), result.cend(), j) != result.cend()) continue; } result.push_back(j); } } } else { int cnt = (int)_children.size(); for (int i = 0; i < cnt; i++) { GButton* obj = _children.at(i)->as(); if (obj != nullptr && obj->isSelected()) result.push_back(i); } } } void GList::addSelection(int index, bool scrollItToView) { if (_selectionMode == ListSelectionMode::NONE) return; checkVirtualList(); if (_selectionMode == ListSelectionMode::SINGLE) clearSelection(); if (scrollItToView) scrollToView(index); _lastSelectedIndex = index; GButton* obj = nullptr; if (_virtual) { ItemInfo& ii = _virtualItems[index]; if (ii.obj != nullptr) obj = ii.obj->as(); ii.selected = true; } else obj = getChildAt(index)->as(); if (obj != nullptr && !obj->isSelected()) { obj->setSelected(true); updateSelectionController(index); } } void GList::removeSelection(int index) { if (_selectionMode == ListSelectionMode::NONE) return; GButton* obj = nullptr; if (_virtual) { ItemInfo& ii = _virtualItems[index]; if (ii.obj != nullptr) obj = ii.obj->as(); ii.selected = false; } else obj = getChildAt(index)->as(); if (obj != nullptr) obj->setSelected(false); } void GList::clearSelection() { if (_virtual) { int cnt = _realNumItems; for (int i = 0; i < cnt; i++) { ItemInfo& ii = _virtualItems[i]; if (dynamic_cast(ii.obj)) ((GButton*)ii.obj)->setSelected(false); ii.selected = false; } } else { int cnt = (int)_children.size(); for (int i = 0; i < cnt; i++) { GButton* obj = _children.at(i)->as(); if (obj != nullptr) obj->setSelected(false); } } } void GList::clearSelectionExcept(GObject* g) { if (_virtual) { int cnt = _realNumItems; for (int i = 0; i < cnt; i++) { ItemInfo& ii = _virtualItems[i]; if (ii.obj != g) { if (dynamic_cast(ii.obj)) ((GButton*)ii.obj)->setSelected(false); ii.selected = false; } } } else { int cnt = (int)_children.size(); for (int i = 0; i < cnt; i++) { GButton* obj = _children.at(i)->as(); if (obj != nullptr && obj != g) obj->setSelected(false); } } } void GList::selectAll() { checkVirtualList(); int last = -1; if (_virtual) { int cnt = _realNumItems; for (int i = 0; i < cnt; i++) { ItemInfo& ii = _virtualItems[i]; if (dynamic_cast(ii.obj) && !((GButton*)ii.obj)->isSelected()) { ((GButton*)ii.obj)->setSelected(true); last = i; } ii.selected = true; } } else { int cnt = (int)_children.size(); for (int i = 0; i < cnt; i++) { GButton* obj = _children.at(i)->as(); if (obj != nullptr && !obj->isSelected()) { obj->setSelected(true); last = i; } } } if (last != -1) updateSelectionController(last); } void GList::selectReverse() { checkVirtualList(); int last = -1; if (_virtual) { int cnt = _realNumItems; for (int i = 0; i < cnt; i++) { ItemInfo& ii = _virtualItems[i]; if (dynamic_cast(ii.obj)) { ((GButton*)ii.obj)->setSelected(!((GButton*)ii.obj)->isSelected()); if (((GButton*)ii.obj)->isSelected()) last = i; } ii.selected = !ii.selected; } } else { int cnt = (int)_children.size(); for (int i = 0; i < cnt; i++) { GButton* obj = _children.at(i)->as(); if (obj != nullptr) { obj->setSelected(!obj->isSelected()); if (obj->isSelected()) last = i; } } } if (last != -1) updateSelectionController(last); } void GList::handleArrowKey(int dir) { int index = getSelectedIndex(); if (index == -1) return; switch (dir) { case 1: //up if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_VERTICAL) { index--; if (index >= 0) { clearSelection(); addSelection(index, true); } } else if (_layout == ListLayoutType::FLOW_HORIZONTAL || _layout == ListLayoutType::PAGINATION) { GObject* current = _children.at(index); int k = 0; int i; for (i = index - 1; i >= 0; i--) { GObject* obj = _children.at(i); if (obj->getY() != current->getY()) { current = obj; break; } k++; } for (; i >= 0; i--) { GObject* obj = _children.at(i); if (obj->getY() != current->getY()) { clearSelection(); addSelection(i + k + 1, true); break; } } } break; case 3: //right if (_layout == ListLayoutType::SINGLE_ROW || _layout == ListLayoutType::FLOW_HORIZONTAL || _layout == ListLayoutType::PAGINATION) { index++; if (index < _children.size()) { clearSelection(); addSelection(index, true); } } else if (_layout == ListLayoutType::FLOW_VERTICAL) { GObject* current = _children.at(index); int k = 0; int cnt = (int)_children.size(); int i; for (i = index + 1; i < cnt; i++) { GObject* obj = _children.at(i); if (obj->getX() != current->getX()) { current = obj; break; } k++; } for (; i < cnt; i++) { GObject* obj = _children.at(i); if (obj->getX() != current->getX()) { clearSelection(); addSelection(i - k - 1, true); break; } } } break; case 5: //down if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_VERTICAL) { index++; if (index < _children.size()) { clearSelection(); addSelection(index, true); } } else if (_layout == ListLayoutType::FLOW_HORIZONTAL || _layout == ListLayoutType::PAGINATION) { GObject* current = _children.at(index); int k = 0; int cnt = (int)_children.size(); int i; for (i = index + 1; i < cnt; i++) { GObject* obj = _children.at(i); if (obj->getY() != current->getY()) { current = obj; break; } k++; } for (; i < cnt; i++) { GObject* obj = _children.at(i); if (obj->getY() != current->getY()) { clearSelection(); addSelection(i - k - 1, true); break; } } } break; case 7: //left if (_layout == ListLayoutType::SINGLE_ROW || _layout == ListLayoutType::FLOW_HORIZONTAL || _layout == ListLayoutType::PAGINATION) { index--; if (index >= 0) { clearSelection(); addSelection(index, true); } } else if (_layout == ListLayoutType::FLOW_VERTICAL) { GObject* current = _children.at(index); int k = 0; int i; for (i = index - 1; i >= 0; i--) { GObject* obj = _children.at(i); if (obj->getX() != current->getX()) { current = obj; break; } k++; } for (; i >= 0; i--) { GObject* obj = _children.at(i); if (obj->getX() != current->getX()) { clearSelection(); addSelection(i + k + 1, true); break; } } } break; } } void GList::onItemTouchBegin(EventContext* context) { GButton* item = (GButton*)context->getSender(); if (_selectionMode == ListSelectionMode::NONE) return; _selectionHandled = false; if (UIConfig::defaultScrollTouchEffect && (_scrollPane != nullptr || (_parent != nullptr && _parent->getScrollPane() != nullptr))) return; if (_selectionMode == ListSelectionMode::SINGLE) { setSelectionOnEvent(item, context->getInput()); } else { if (!item->isSelected()) setSelectionOnEvent(item, context->getInput()); } } void GList::onClickItem(EventContext* context) { GButton* item = (GButton*)context->getSender(); if (!_selectionHandled) setSelectionOnEvent(item, context->getInput()); _selectionHandled = false; if (_scrollPane != nullptr && scrollItemToViewOnClick) _scrollPane->scrollToView(item, true); dispatchItemEvent(item, context); } void GList::dispatchItemEvent(GObject* item, EventContext* context) { dispatchEvent(context->getType() == UIEventType::Click ? UIEventType::ClickItem : UIEventType::RightClickItem, item); } void GList::setSelectionOnEvent(GObject* item, InputEvent* evt) { if (!(dynamic_cast(item)) || _selectionMode == ListSelectionMode::NONE) return; _selectionHandled = true; bool dontChangeLastIndex = false; GButton* button = (GButton*)item; int index = childIndexToItemIndex(getChildIndex(item)); if (_selectionMode == ListSelectionMode::SINGLE) { if (!button->isSelected()) { clearSelectionExcept(button); button->setSelected(true); } } else { if (evt->isShiftDown()) { if (!button->isSelected()) { if (_lastSelectedIndex != -1) { int min = MIN(_lastSelectedIndex, index); int max = MAX(_lastSelectedIndex, index); max = MIN(max, getNumItems() - 1); if (_virtual) { for (int i = min; i <= max; i++) { ItemInfo& ii = _virtualItems[i]; if (dynamic_cast(ii.obj)) ((GButton*)ii.obj)->setSelected(true); ii.selected = true; } } else { for (int i = min; i <= max; i++) { GButton* obj = getChildAt(i)->as(); if (obj != nullptr && !obj->isSelected()) obj->setSelected(true); } } dontChangeLastIndex = true; } else { button->setSelected(true); } } } else if (evt->isCtrlDown() || _selectionMode == ListSelectionMode::MULTIPLE_SINGLECLICK) { button->setSelected(!button->isSelected()); } else { if (!button->isSelected()) { clearSelectionExcept(button); button->setSelected(true); } else clearSelectionExcept(button); } } if (!dontChangeLastIndex) _lastSelectedIndex = index; if (button->isSelected()) updateSelectionController(index); } void GList::resizeToFit(int itemCount, int minSize) { ensureBoundsCorrect(); int curCount = getNumItems(); if (itemCount > curCount) itemCount = curCount; if (_virtual) { int lineCount = ceil((float)itemCount / _curLineItemCount); if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) setViewHeight(lineCount * _itemSize.y + MAX(0, lineCount - 1) * _lineGap); else setViewWidth(lineCount * _itemSize.x + MAX(0, lineCount - 1) * _columnGap); } else if (itemCount == 0) { if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) setViewHeight(minSize); else setViewWidth(minSize); } else { int i = itemCount - 1; GObject* obj = nullptr; while (i >= 0) { obj = getChildAt(i); if (!foldInvisibleItems || obj->isVisible()) break; i--; } if (i < 0) { if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) setViewHeight(minSize); else setViewWidth(minSize); } else { float size; if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) { size = obj->getY() + obj->getHeight(); if (size < minSize) size = minSize; setViewHeight(size); } else { size = obj->getX() + obj->getWidth(); if (size < minSize) size = minSize; setViewWidth(size); } } } } int GList::getFirstChildInView() { return childIndexToItemIndex(GComponent::getFirstChildInView()); } void GList::handleSizeChanged() { GComponent::handleSizeChanged(); setBoundsChangedFlag(); if (_virtual) setVirtualListChangedFlag(true); } void GList::handleControllerChanged(GController* c) { GComponent::handleControllerChanged(c); if (_selectionController == c) setSelectedIndex(c->getSelectedIndex()); } void GList::updateSelectionController(int index) { if (_selectionController != nullptr && !_selectionController->changing && index < _selectionController->getPageCount()) { GController* c = _selectionController; _selectionController = nullptr; c->setSelectedIndex(index); _selectionController = c; } } void GList::scrollToView(int index, bool ani, bool setFirst) { if (_virtual) { if (_numItems == 0) return; checkVirtualList(); AXASSERT(index >= 0 && index < (int)_virtualItems.size(), "Invalid child index"); if (_loop) index = floor(_firstIndex / _numItems) * _numItems + index; Rect rect; ItemInfo& ii = _virtualItems[index]; if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) { float pos = 0; for (int i = _curLineItemCount - 1; i < index; i += _curLineItemCount) pos += _virtualItems[i].size.y + _lineGap; rect.setRect(0, pos, _itemSize.x, ii.size.y); } else if (_layout == ListLayoutType::SINGLE_ROW || _layout == ListLayoutType::FLOW_VERTICAL) { float pos = 0; for (int i = _curLineItemCount - 1; i < index; i += _curLineItemCount) pos += _virtualItems[i].size.x + _columnGap; rect.setRect(pos, 0, ii.size.x, _itemSize.y); } else { int page = index / (_curLineItemCount * _curLineItemCount2); rect.setRect(page * getViewWidth() + (index % _curLineItemCount) * (ii.size.x + _columnGap), (index / _curLineItemCount) % _curLineItemCount2 * (ii.size.y + _lineGap), ii.size.x, ii.size.y); } setFirst = true; if (_scrollPane != nullptr) _scrollPane->scrollToView(rect, ani, setFirst); else if (_parent != nullptr && _parent->getScrollPane() != nullptr) _parent->getScrollPane()->scrollToView(transformRect(rect, _parent), ani, setFirst); } else { GObject* obj = getChildAt(index); if (_scrollPane != nullptr) _scrollPane->scrollToView(obj, ani, setFirst); else if (_parent != nullptr && _parent->getScrollPane() != nullptr) _parent->getScrollPane()->scrollToView(obj, ani, setFirst); } } int GList::childIndexToItemIndex(int index) { if (!_virtual) return index; if (_layout == ListLayoutType::PAGINATION) { for (int i = _firstIndex; i < _realNumItems; i++) { if (_virtualItems[i].obj != nullptr) { index--; if (index < 0) return i; } } return index; } else { index += _firstIndex; if (_loop && _numItems > 0) index = index % _numItems; return index; } } int GList::itemIndexToChildIndex(int index) { if (!_virtual) return index; if (_layout == ListLayoutType::PAGINATION) { return getChildIndex(_virtualItems[index].obj); } else { if (_loop && _numItems > 0) { int j = _firstIndex % _numItems; if (index >= j) index = index - j; else index = _numItems - j + index; } else index -= _firstIndex; return index; } } void GList::setVirtual() { setVirtual(false); } void GList::setVirtualAndLoop() { setVirtual(true); } void GList::setVirtual(bool loop) { if (!_virtual) { AXASSERT(_scrollPane != nullptr, "FairyGUI: Virtual list must be scrollable!"); if (loop) { AXASSERT(_layout != ListLayoutType::FLOW_HORIZONTAL && _layout != ListLayoutType::FLOW_VERTICAL, "FairyGUI: Loop list is not supported for FlowHorizontal or FlowVertical layout!"); _scrollPane->setBouncebackEffect(false); } _virtual = true; _loop = loop; removeChildrenToPool(); if (_itemSize.x == 0 || _itemSize.y == 0) { GObject* obj = getFromPool(); AXASSERT(obj != nullptr, "FairyGUI: Virtual List must have a default list item resource."); _itemSize = obj->getSize(); _itemSize.x = ceil(_itemSize.x); _itemSize.y = ceil(_itemSize.y); returnToPool(obj); } if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) { _scrollPane->setScrollStep(_itemSize.y); if (_loop) _scrollPane->_loop = 2; } else { _scrollPane->setScrollStep(_itemSize.x); if (_loop) _scrollPane->_loop = 1; } addEventListener(UIEventType::Scroll, AX_CALLBACK_1(GList::onScroll, this)); setVirtualListChangedFlag(true); } } int GList::getNumItems() { if (_virtual) return _numItems; else return (int)_children.size(); } void GList::setNumItems(int value) { if (_virtual) { AXASSERT(itemRenderer != nullptr, "FairyGUI: Set itemRenderer first!"); _numItems = value; if (_loop) _realNumItems = _numItems * 6; else _realNumItems = _numItems; int oldCount = (int)_virtualItems.size(); if (_realNumItems > oldCount) { for (int i = oldCount; i < _realNumItems; i++) { ItemInfo ii; ii.size = _itemSize; _virtualItems.push_back(ii); } } else { for (int i = _realNumItems; i < oldCount; i++) _virtualItems[i].selected = false; } if (_virtualListChanged != 0) CALL_LATER_CANCEL(GList, doRefreshVirtualList); doRefreshVirtualList(); } else { int cnt = (int)_children.size(); if (value > cnt) { for (int i = cnt; i < value; i++) { if (itemProvider == nullptr) addItemFromPool(); else addItemFromPool(itemProvider(i)); } } else { removeChildrenToPool(value, cnt); } if (itemRenderer != nullptr) { for (int i = 0; i < value; i++) itemRenderer(i, getChildAt(i)); } } } void GList::refreshVirtualList() { AXASSERT(_virtual, "FairyGUI: not virtual list"); setVirtualListChangedFlag(false); } ax::Vec2 GList::getSnappingPosition(const ax::Vec2& pt) { if (_virtual) { Vec2 ret = pt; if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) { int index = getIndexOnPos1(ret.y, false); if (index < (int)_virtualItems.size() && pt.y - ret.y > _virtualItems[index].size.y / 2 && index < _realNumItems) ret.y += _virtualItems[index].size.y + _lineGap; } else if (_layout == ListLayoutType::SINGLE_ROW || _layout == ListLayoutType::FLOW_VERTICAL) { int index = getIndexOnPos2(ret.x, false); if (index < (int)_virtualItems.size() && pt.x - ret.x > _virtualItems[index].size.x / 2 && index < _realNumItems) ret.x += _virtualItems[index].size.x + _columnGap; } else { int index = getIndexOnPos3(ret.x, false); if (index < (int)_virtualItems.size() && pt.x - ret.x > _virtualItems[index].size.x / 2 && index < _realNumItems) ret.x += _virtualItems[index].size.x + _columnGap; } return ret; } else return GComponent::getSnappingPosition(pt); } void GList::checkVirtualList() { if (_virtualListChanged != 0) { doRefreshVirtualList(); CALL_LATER_CANCEL(GList, doRefreshVirtualList); } } void GList::setVirtualListChangedFlag(bool layoutChanged) { if (layoutChanged) _virtualListChanged = 2; else if (_virtualListChanged == 0) _virtualListChanged = 1; CALL_LATER(GList, doRefreshVirtualList); } void GList::doRefreshVirtualList() { bool layoutChanged = _virtualListChanged == 2; _virtualListChanged = 0; _eventLocked = true; if (layoutChanged) { if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::SINGLE_ROW) _curLineItemCount = 1; else if (_layout == ListLayoutType::FLOW_HORIZONTAL) { if (_columnCount > 0) _curLineItemCount = _columnCount; else { _curLineItemCount = floor((_scrollPane->getViewSize().width + _columnGap) / (_itemSize.x + _columnGap)); if (_curLineItemCount <= 0) _curLineItemCount = 1; } } else if (_layout == ListLayoutType::FLOW_VERTICAL) { if (_lineCount > 0) _curLineItemCount = _lineCount; else { _curLineItemCount = floor((_scrollPane->getViewSize().height + _lineGap) / (_itemSize.y + _lineGap)); if (_curLineItemCount <= 0) _curLineItemCount = 1; } } else //pagination { if (_columnCount > 0) _curLineItemCount = _columnCount; else { _curLineItemCount = floor((_scrollPane->getViewSize().width + _columnGap) / (_itemSize.x + _columnGap)); if (_curLineItemCount <= 0) _curLineItemCount = 1; } if (_lineCount > 0) _curLineItemCount2 = _lineCount; else { _curLineItemCount2 = floor((_scrollPane->getViewSize().height + _lineGap) / (_itemSize.y + _lineGap)); if (_curLineItemCount2 <= 0) _curLineItemCount2 = 1; } } } float ch = 0, cw = 0; if (_realNumItems > 0) { int len = ceil((float)_realNumItems / _curLineItemCount) * _curLineItemCount; int len2 = MIN(_curLineItemCount, _realNumItems); if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) { for (int i = 0; i < len; i += _curLineItemCount) ch += _virtualItems[i].size.y + _lineGap; if (ch > 0) ch -= _lineGap; if (_autoResizeItem) cw = _scrollPane->getViewSize().width; else { for (int i = 0; i < len2; i++) cw += _virtualItems[i].size.x + _columnGap; if (cw > 0) cw -= _columnGap; } } else if (_layout == ListLayoutType::SINGLE_ROW || _layout == ListLayoutType::FLOW_VERTICAL) { for (int i = 0; i < len; i += _curLineItemCount) cw += _virtualItems[i].size.x + _columnGap; if (cw > 0) cw -= _columnGap; if (_autoResizeItem) ch = _scrollPane->getViewSize().height; else { for (int i = 0; i < len2; i++) ch += _virtualItems[i].size.y + _lineGap; if (ch > 0) ch -= _lineGap; } } else { int pageCount = ceil((float)len / (_curLineItemCount * _curLineItemCount2)); cw = pageCount * getViewWidth(); ch = getViewHeight(); } } handleAlign(cw, ch); _scrollPane->setContentSize(cw, ch); _eventLocked = false; handleScroll(true); } void GList::onScroll(EventContext* context) { handleScroll(false); } int GList::getIndexOnPos1(float& pos, bool forceUpdate) { if (_realNumItems < _curLineItemCount) { pos = 0; return 0; } if (numChildren() > 0 && !forceUpdate) { float pos2 = getChildAt(0)->getY(); if (pos2 + (_lineGap > 0 ? 0 : -_lineGap) > pos) { for (int i = _firstIndex - _curLineItemCount; i >= 0; i -= _curLineItemCount) { pos2 -= (_virtualItems[i].size.y + _lineGap); if (pos2 <= pos) { pos = pos2; return i; } } pos = 0; return 0; } else { float testGap = _lineGap > 0 ? _lineGap : 0; for (int i = _firstIndex; i < _realNumItems; i += _curLineItemCount) { float pos3 = pos2 + _virtualItems[i].size.y; if (pos3 + testGap > pos) { pos = pos2; return i; } pos2 = pos3 + _lineGap; } pos = pos2; return _realNumItems - _curLineItemCount; } } else { float pos2 = 0; float testGap = _lineGap > 0 ? _lineGap : 0; for (int i = 0; i < _realNumItems; i += _curLineItemCount) { float pos3 = pos2 + _virtualItems[i].size.y; if (pos3 + testGap > pos) { pos = pos2; return i; } pos2 = pos3 + _lineGap; } pos = pos2; return _realNumItems - _curLineItemCount; } } int GList::getIndexOnPos2(float& pos, bool forceUpdate) { if (_realNumItems < _curLineItemCount) { pos = 0; return 0; } if (numChildren() > 0 && !forceUpdate) { float pos2 = getChildAt(0)->getX(); if (pos2 + (_columnGap > 0 ? 0 : -_columnGap) > pos) { for (int i = _firstIndex - _curLineItemCount; i >= 0; i -= _curLineItemCount) { pos2 -= (_virtualItems[i].size.x + _columnGap); if (pos2 <= pos) { pos = pos2; return i; } } pos = 0; return 0; } else { float testGap = _columnGap > 0 ? _columnGap : 0; for (int i = _firstIndex; i < _realNumItems; i += _curLineItemCount) { float pos3 = pos2 + _virtualItems[i].size.x; if (pos3 + testGap > pos) { pos = pos2; return i; } pos2 = pos3 + _columnGap; } pos = pos2; return _realNumItems - _curLineItemCount; } } else { float pos2 = 0; float testGap = _columnGap > 0 ? _columnGap : 0; for (int i = 0; i < _realNumItems; i += _curLineItemCount) { float pos3 = pos2 + _virtualItems[i].size.x; if (pos3 + testGap > pos) { pos = pos2; return i; } pos2 = pos3 + _columnGap; } pos = pos2; return _realNumItems - _curLineItemCount; } } int GList::getIndexOnPos3(float& pos, bool forceUpdate) { if (_realNumItems < _curLineItemCount) { pos = 0; return 0; } float viewWidth = getViewWidth(); int page = floor(pos / viewWidth); int startIndex = page * (_curLineItemCount * _curLineItemCount2); float pos2 = page * viewWidth; float testGap = _columnGap > 0 ? _columnGap : 0; for (int i = 0; i < _curLineItemCount; i++) { float pos3 = pos2 + _virtualItems[startIndex + i].size.x; if (pos3 + testGap > pos) { pos = pos2; return startIndex + i; } pos2 = pos3 + _columnGap; } pos = pos2; return startIndex + _curLineItemCount - 1; } void GList::handleScroll(bool forceUpdate) { if (_eventLocked) return; if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) { int enterCounter = 0; while (handleScroll1(forceUpdate)) { enterCounter++; forceUpdate = false; if (enterCounter > 20) { AXLOG("FairyGUI: list will never be filled as the item renderer function always returns a different size."); break; } } handleArchOrder1(); } else if (_layout == ListLayoutType::SINGLE_ROW || _layout == ListLayoutType::FLOW_VERTICAL) { int enterCounter = 0; while (handleScroll2(forceUpdate)) { enterCounter++; forceUpdate = false; if (enterCounter > 20) { AXLOG("FairyGUI: list will never be filled as the item renderer function always returns a different size."); break; } } handleArchOrder2(); } else { handleScroll3(forceUpdate); } _boundsChanged = false; } bool GList::handleScroll1(bool forceUpdate) { float pos = _scrollPane->getScrollingPosY(); float max = pos + _scrollPane->getViewSize().height; bool end = max == _scrollPane->getContentSize().height; int newFirstIndex = getIndexOnPos1(pos, forceUpdate); if (newFirstIndex == _firstIndex && !forceUpdate) return false; int oldFirstIndex = _firstIndex; _firstIndex = newFirstIndex; int curIndex = newFirstIndex; bool forward = oldFirstIndex > newFirstIndex; int childCount = numChildren(); int lastIndex = oldFirstIndex + childCount - 1; int reuseIndex = forward ? lastIndex : oldFirstIndex; float curX = 0, curY = pos; bool needRender; float deltaSize = 0; float firstItemDeltaSize = 0; std::string url = _defaultItem; int partSize = (int)((_scrollPane->getViewSize().width - _columnGap * (_curLineItemCount - 1)) / _curLineItemCount); _itemInfoVer++; while (curIndex < _realNumItems && (end || curY < max)) { ItemInfo& ii = _virtualItems[curIndex]; if (ii.obj == nullptr || forceUpdate) { if (itemProvider != nullptr) { url = itemProvider(curIndex % _numItems); if (url.size() == 0) url = _defaultItem; url = UIPackage::normalizeURL(url); } if (ii.obj != nullptr && ii.obj->getResourceURL().compare(url) != 0) { if (dynamic_cast(ii.obj)) ii.selected = ((GButton*)ii.obj)->isSelected(); removeChildToPool(ii.obj); ii.obj = nullptr; } } if (ii.obj == nullptr) { if (forward) { for (int j = reuseIndex; j >= oldFirstIndex; j--) { ItemInfo& ii2 = _virtualItems[j]; if (ii2.obj != nullptr && ii2.updateFlag != _itemInfoVer && ii2.obj->getResourceURL().compare(url) == 0) { if (dynamic_cast(ii2.obj)) ii2.selected = ((GButton*)ii2.obj)->isSelected(); ii.obj = ii2.obj; ii2.obj = nullptr; if (j == reuseIndex) reuseIndex--; break; } } } else { for (int j = reuseIndex; j <= lastIndex; j++) { ItemInfo& ii2 = _virtualItems[j]; if (ii2.obj != nullptr && ii2.updateFlag != _itemInfoVer && ii2.obj->getResourceURL().compare(url) == 0) { if (dynamic_cast(ii2.obj)) ii2.selected = ((GButton*)ii2.obj)->isSelected(); ii.obj = ii2.obj; ii2.obj = nullptr; if (j == reuseIndex) reuseIndex++; break; } } } if (ii.obj != nullptr) { setChildIndex(ii.obj, forward ? curIndex - newFirstIndex : numChildren()); } else { ii.obj = _pool->getObject(url); if (forward) addChildAt(ii.obj, curIndex - newFirstIndex); else addChild(ii.obj); } if (dynamic_cast(ii.obj)) ((GButton*)ii.obj)->setSelected(ii.selected); needRender = true; } else needRender = forceUpdate; if (needRender) { if (_autoResizeItem && (_layout == ListLayoutType::SINGLE_COLUMN || _columnCount > 0)) ii.obj->setSize(partSize, ii.obj->getHeight(), true); itemRenderer(curIndex % _numItems, ii.obj); if (curIndex % _curLineItemCount == 0) { deltaSize += ceil(ii.obj->getHeight()) - ii.size.y; if (curIndex == newFirstIndex && oldFirstIndex > newFirstIndex) { firstItemDeltaSize = ceil(ii.obj->getHeight()) - ii.size.y; } } ii.size.x = ceil(ii.obj->getWidth()); ii.size.y = ceil(ii.obj->getHeight()); } ii.updateFlag = _itemInfoVer; ii.obj->setPosition(curX, curY); if (curIndex == newFirstIndex) max += ii.size.y; curX += ii.size.x + _columnGap; if (curIndex % _curLineItemCount == _curLineItemCount - 1) { curX = 0; curY += ii.size.y + _lineGap; } curIndex++; } for (int i = 0; i < childCount; i++) { ItemInfo& ii = _virtualItems[oldFirstIndex + i]; if (ii.updateFlag != _itemInfoVer && ii.obj != nullptr) { if (dynamic_cast(ii.obj)) ii.selected = ((GButton*)ii.obj)->isSelected(); removeChildToPool(ii.obj); ii.obj = nullptr; } } childCount = (int)_children.size(); for (int i = 0; i < childCount; i++) { GObject* obj = _virtualItems[newFirstIndex + i].obj; if (_children.at(i) != obj) setChildIndex(obj, i); } if (deltaSize != 0 || firstItemDeltaSize != 0) _scrollPane->changeContentSizeOnScrolling(0, deltaSize, 0, firstItemDeltaSize); if (curIndex > 0 && numChildren() > 0 && _container->getPositionY2() <= 0 && getChildAt(0)->getY() > -_container->getPositionY2()) return true; else return false; } bool GList::handleScroll2(bool forceUpdate) { float pos = _scrollPane->getScrollingPosX(); float max = pos + _scrollPane->getViewSize().width; bool end = pos == _scrollPane->getContentSize().width; int newFirstIndex = getIndexOnPos2(pos, forceUpdate); if (newFirstIndex == _firstIndex && !forceUpdate) return false; int oldFirstIndex = _firstIndex; _firstIndex = newFirstIndex; int curIndex = newFirstIndex; bool forward = oldFirstIndex > newFirstIndex; int childCount = numChildren(); int lastIndex = oldFirstIndex + childCount - 1; int reuseIndex = forward ? lastIndex : oldFirstIndex; float curX = pos, curY = 0; bool needRender; float deltaSize = 0; float firstItemDeltaSize = 0; string url = _defaultItem; int partSize = (int)((_scrollPane->getViewSize().height - _lineGap * (_curLineItemCount - 1)) / _curLineItemCount); _itemInfoVer++; while (curIndex < _realNumItems && (end || curX < max)) { ItemInfo& ii = _virtualItems[curIndex]; if (ii.obj == nullptr || forceUpdate) { if (itemProvider != nullptr) { url = itemProvider(curIndex % _numItems); if (url.size() == 0) url = _defaultItem; url = UIPackage::normalizeURL(url); } if (ii.obj != nullptr && ii.obj->getResourceURL().compare(url) != 0) { if (dynamic_cast(ii.obj)) ii.selected = ((GButton*)ii.obj)->isSelected(); removeChildToPool(ii.obj); ii.obj = nullptr; } } if (ii.obj == nullptr) { if (forward) { for (int j = reuseIndex; j >= oldFirstIndex; j--) { ItemInfo& ii2 = _virtualItems[j]; if (ii2.obj != nullptr && ii2.updateFlag != _itemInfoVer && ii2.obj->getResourceURL().compare(url) == 0) { if (dynamic_cast(ii2.obj)) ii2.selected = ((GButton*)ii2.obj)->isSelected(); ii.obj = ii2.obj; ii2.obj = nullptr; if (j == reuseIndex) reuseIndex--; break; } } } else { for (int j = reuseIndex; j <= lastIndex; j++) { ItemInfo& ii2 = _virtualItems[j]; if (ii2.obj != nullptr && ii2.updateFlag != _itemInfoVer && ii2.obj->getResourceURL().compare(url) == 0) { if (dynamic_cast(ii2.obj)) ii2.selected = ((GButton*)ii2.obj)->isSelected(); ii.obj = ii2.obj; ii2.obj = nullptr; if (j == reuseIndex) reuseIndex++; break; } } } if (ii.obj != nullptr) { setChildIndex(ii.obj, forward ? curIndex - newFirstIndex : numChildren()); } else { ii.obj = _pool->getObject(url); if (forward) addChildAt(ii.obj, curIndex - newFirstIndex); else addChild(ii.obj); } if (dynamic_cast(ii.obj)) ((GButton*)ii.obj)->setSelected(ii.selected); needRender = true; } else needRender = forceUpdate; if (needRender) { if (_autoResizeItem && (_layout == ListLayoutType::SINGLE_ROW || _lineCount > 0)) ii.obj->setSize(ii.obj->getWidth(), partSize, true); itemRenderer(curIndex % _numItems, ii.obj); if (curIndex % _curLineItemCount == 0) { deltaSize += ceil(ii.obj->getWidth()) - ii.size.x; if (curIndex == newFirstIndex && oldFirstIndex > newFirstIndex) { firstItemDeltaSize = ceil(ii.obj->getWidth()) - ii.size.x; } } ii.size.x = ceil(ii.obj->getWidth()); ii.size.y = ceil(ii.obj->getHeight()); } ii.updateFlag = _itemInfoVer; ii.obj->setPosition(curX, curY); if (curIndex == newFirstIndex) max += ii.size.x; curY += ii.size.y + _lineGap; if (curIndex % _curLineItemCount == _curLineItemCount - 1) { curY = 0; curX += ii.size.x + _columnGap; } curIndex++; } for (int i = 0; i < childCount; i++) { ItemInfo& ii = _virtualItems[oldFirstIndex + i]; if (ii.updateFlag != _itemInfoVer && ii.obj != nullptr) { if (dynamic_cast(ii.obj)) ii.selected = ((GButton*)ii.obj)->isSelected(); removeChildToPool(ii.obj); ii.obj = nullptr; } } childCount = (int)_children.size(); for (int i = 0; i < childCount; i++) { GObject* obj = _virtualItems[newFirstIndex + i].obj; if (_children.at(i) != obj) setChildIndex(obj, i); } if (deltaSize != 0 || firstItemDeltaSize != 0) _scrollPane->changeContentSizeOnScrolling(deltaSize, 0, firstItemDeltaSize, 0); if (curIndex > 0 && numChildren() > 0 && _container->getPositionX() <= 0 && getChildAt(0)->getX() > -_container->getPositionX()) return true; else return false; } void GList::handleScroll3(bool forceUpdate) { float pos = _scrollPane->getScrollingPosX(); int newFirstIndex = getIndexOnPos3(pos, forceUpdate); if (newFirstIndex == _firstIndex && !forceUpdate) return; int oldFirstIndex = _firstIndex; _firstIndex = newFirstIndex; int reuseIndex = oldFirstIndex; int virtualItemCount = (int)_virtualItems.size(); int pageSize = _curLineItemCount * _curLineItemCount2; int startCol = newFirstIndex % _curLineItemCount; float viewWidth = getViewWidth(); int page = (int)(newFirstIndex / pageSize); int startIndex = page * pageSize; int lastIndex = startIndex + pageSize * 2; bool needRender; string url = _defaultItem; int partWidth = (int)((_scrollPane->getViewSize().width - _columnGap * (_curLineItemCount - 1)) / _curLineItemCount); int partHeight = (int)((_scrollPane->getViewSize().height - _lineGap * (_curLineItemCount2 - 1)) / _curLineItemCount2); _itemInfoVer++; for (int i = startIndex; i < lastIndex; i++) { if (i >= _realNumItems) continue; int col = i % _curLineItemCount; if (i - startIndex < pageSize) { if (col < startCol) continue; } else { if (col > startCol) continue; } ItemInfo& ii = _virtualItems[i]; ii.updateFlag = _itemInfoVer; } GObject* lastObj = nullptr; int insertIndex = 0; for (int i = startIndex; i < lastIndex; i++) { if (i >= _realNumItems) continue; ItemInfo& ii = _virtualItems[i]; if (ii.updateFlag != _itemInfoVer) continue; if (ii.obj == nullptr) { reuseIndex = reuseIndex < 0 ? 0 : reuseIndex; while (reuseIndex < virtualItemCount) { ItemInfo& ii2 = _virtualItems[reuseIndex]; if (ii2.obj != nullptr && ii2.updateFlag != _itemInfoVer) { if (dynamic_cast(ii2.obj)) ii2.selected = ((GButton*)ii2.obj)->isSelected(); ii.obj = ii2.obj; ii2.obj = nullptr; break; } reuseIndex++; } if (insertIndex == -1) insertIndex = getChildIndex(lastObj) + 1; if (ii.obj == nullptr) { if (itemProvider != nullptr) { url = itemProvider(i % _numItems); if (url.size() == 0) url = _defaultItem; url = UIPackage::normalizeURL(url); } ii.obj = _pool->getObject(url); addChildAt(ii.obj, insertIndex); } else { insertIndex = setChildIndexBefore(ii.obj, insertIndex); } insertIndex++; if (dynamic_cast(ii.obj)) ((GButton*)ii.obj)->setSelected(ii.selected); needRender = true; } else { needRender = forceUpdate; insertIndex = -1; lastObj = ii.obj; } if (needRender) { if (_autoResizeItem) { if (_curLineItemCount == _columnCount && _curLineItemCount2 == _lineCount) ii.obj->setSize(partWidth, partHeight, true); else if (_curLineItemCount == _columnCount) ii.obj->setSize(partWidth, ii.obj->getHeight(), true); else if (_curLineItemCount2 == _lineCount) ii.obj->setSize(ii.obj->getWidth(), partHeight, true); } itemRenderer(i % _numItems, ii.obj); ii.size.x = ceil(ii.obj->getWidth()); ii.size.y = ceil(ii.obj->getHeight()); } } float borderX = (startIndex / pageSize) * viewWidth; float xx = borderX; float yy = 0; float lineHeight = 0; for (int i = startIndex; i < lastIndex; i++) { if (i >= _realNumItems) continue; ItemInfo& ii = _virtualItems[i]; if (ii.updateFlag == _itemInfoVer) ii.obj->setPosition(xx, yy); if (ii.size.y > lineHeight) lineHeight = ii.size.y; if (i % _curLineItemCount == _curLineItemCount - 1) { xx = borderX; yy += lineHeight + _lineGap; lineHeight = 0; if (i == startIndex + pageSize - 1) { borderX += viewWidth; xx = borderX; yy = 0; } } else xx += ii.size.x + _columnGap; } for (int i = reuseIndex; i < virtualItemCount; i++) { ItemInfo& ii = _virtualItems[i]; if (ii.updateFlag != _itemInfoVer && ii.obj != nullptr) { if (dynamic_cast(ii.obj)) ii.selected = ((GButton*)ii.obj)->isSelected(); removeChildToPool(ii.obj); ii.obj = nullptr; } } } void GList::handleArchOrder1() { if (_childrenRenderOrder == ChildrenRenderOrder::ARCH) { float mid = _scrollPane->getPosY() + getViewHeight() / 2; float minDist = FLT_MAX, dist; int apexIndex = 0; int cnt = numChildren(); for (int i = 0; i < cnt; i++) { GObject* obj = getChildAt(i); if (!foldInvisibleItems || obj->isVisible()) { dist = abs(mid - obj->getY() - obj->getHeight() / 2); if (dist < minDist) { minDist = dist; apexIndex = i; } } } setApexIndex(apexIndex); } } void GList::handleArchOrder2() { if (_childrenRenderOrder == ChildrenRenderOrder::ARCH) { float mid = _scrollPane->getPosX() + getViewWidth() / 2; float minDist = FLT_MAX, dist; int apexIndex = 0; int cnt = numChildren(); for (int i = 0; i < cnt; i++) { GObject* obj = getChildAt(i); if (!foldInvisibleItems || obj->isVisible()) { dist = abs(mid - obj->getX() - obj->getWidth() / 2); if (dist < minDist) { minDist = dist; apexIndex = i; } } } setApexIndex(apexIndex); } } void GList::handleAlign(float contentWidth, float contentHeight) { Vec2 newOffset(0, 0); float viewHeight = getViewHeight(); float viewWidth = getViewWidth(); if (contentHeight < viewHeight) { if (_verticalAlign == TextVAlignment::CENTER) newOffset.y = (int)((viewHeight - contentHeight) / 2); else if (_verticalAlign == TextVAlignment::BOTTOM) newOffset.y = viewHeight - contentHeight; } if (contentWidth < viewWidth) { if (_align == TextHAlignment::CENTER) newOffset.x = (int)((viewWidth - contentWidth) / 2); else if (_align == TextHAlignment::RIGHT) newOffset.x = viewWidth - contentWidth; } if (!newOffset.equals(_alignOffset)) { _alignOffset = newOffset; if (_scrollPane != nullptr) _scrollPane->adjustMaskContainer(); else _container->setPosition2(_margin.left + _alignOffset.x, _margin.top + _alignOffset.y); } } void GList::updateBounds() { if (_virtual) return; int cnt = (int)_children.size(); int i; int j = 0; GObject* child; float curX = 0; float curY = 0; float cw, ch; float maxWidth = 0; float maxHeight = 0; float viewWidth = getViewWidth(); float viewHeight = getViewHeight(); if (_layout == ListLayoutType::SINGLE_COLUMN) { for (i = 0; i < cnt; i++) { child = getChildAt(i); if (foldInvisibleItems && !child->isVisible()) continue; if (curY != 0) curY += _lineGap; child->setY(curY); if (_autoResizeItem) child->setSize(viewWidth, child->getHeight(), true); curY += ceil(child->getHeight()); if (child->getWidth() > maxWidth) maxWidth = child->getWidth(); } ch = curY; if (ch <= viewHeight && _autoResizeItem && _scrollPane != nullptr && _scrollPane->_displayInDemand && _scrollPane->_vtScrollBar != nullptr) { viewWidth += _scrollPane->_vtScrollBar->getWidth(); for (i = 0; i < cnt; i++) { child = getChildAt(i); if (foldInvisibleItems && !child->isVisible()) continue; child->setSize(viewWidth, child->getHeight(), true); if (child->getWidth() > maxWidth) maxWidth = child->getWidth(); } } cw = ceil(maxWidth); } else if (_layout == ListLayoutType::SINGLE_ROW) { for (i = 0; i < cnt; i++) { child = getChildAt(i); if (foldInvisibleItems && !child->isVisible()) continue; if (curX != 0) curX += _columnGap; child->setX(curX); if (_autoResizeItem) child->setSize(child->getWidth(), viewHeight, true); curX += ceil(child->getWidth()); if (child->getHeight() > maxHeight) maxHeight = child->getHeight(); } cw = curX; if (cw <= viewWidth && _autoResizeItem && _scrollPane != nullptr && _scrollPane->_displayInDemand && _scrollPane->_hzScrollBar != nullptr) { viewHeight += _scrollPane->_hzScrollBar->getHeight(); for (i = 0; i < cnt; i++) { child = getChildAt(i); if (foldInvisibleItems && !child->isVisible()) continue; child->setSize(child->getWidth(), viewHeight, true); if (child->getHeight() > maxHeight) maxHeight = child->getHeight(); } } ch = ceil(maxHeight); } else if (_layout == ListLayoutType::FLOW_HORIZONTAL) { if (_autoResizeItem && _columnCount > 0) { float lineSize = 0; int lineStart = 0; float ratio; for (i = 0; i < cnt; i++) { child = getChildAt(i); if (foldInvisibleItems && !child->isVisible()) continue; lineSize += child->sourceSize.width; j++; if (j == _columnCount || i == cnt - 1) { ratio = (viewWidth - lineSize - (j - 1) * _columnGap) / lineSize; curX = 0; for (j = lineStart; j <= i; j++) { child = getChildAt(j); if (foldInvisibleItems && !child->isVisible()) continue; child->setPosition(curX, curY); if (j < i) { child->setSize(child->sourceSize.width + round(child->sourceSize.width * ratio), child->getHeight(), true); curX += ceil(child->getWidth()) + _columnGap; } else { child->setSize(viewWidth - curX, child->getHeight(), true); } if (child->getHeight() > maxHeight) maxHeight = child->getHeight(); } //new line curY += ceil(maxHeight) + _lineGap; maxHeight = 0; j = 0; lineStart = i + 1; lineSize = 0; } } ch = curY + ceil(maxHeight); cw = viewWidth; } else { for (i = 0; i < cnt; i++) { child = getChildAt(i); if (foldInvisibleItems && !child->isVisible()) continue; if (curX != 0) curX += _columnGap; if ((_columnCount != 0 && j >= _columnCount) || (_columnCount == 0 && curX + child->getWidth() > viewWidth && maxHeight != 0)) { //new line curX = 0; curY += ceil(maxHeight) + _lineGap; maxHeight = 0; j = 0; } child->setPosition(curX, curY); curX += ceil(child->getWidth()); if (curX > maxWidth) maxWidth = curX; if (child->getHeight() > maxHeight) maxHeight = child->getHeight(); j++; } ch = curY + ceil(maxHeight); cw = ceil(maxWidth); } } else if (_layout == ListLayoutType::FLOW_VERTICAL) { if (_autoResizeItem && _lineCount > 0) { float lineSize = 0; int lineStart = 0; float ratio; for (i = 0; i < cnt; i++) { child = getChildAt(i); if (foldInvisibleItems && !child->isVisible()) continue; lineSize += child->sourceSize.height; j++; if (j == _lineCount || i == cnt - 1) { ratio = (viewHeight - lineSize - (j - 1) * _lineGap) / lineSize; curY = 0; for (j = lineStart; j <= i; j++) { child = getChildAt(j); if (foldInvisibleItems && !child->isVisible()) continue; child->setPosition(curX, curY); if (j < i) { child->setSize(child->getWidth(), child->sourceSize.height + round(child->sourceSize.height * ratio), true); curY += ceil(child->getHeight()) + _lineGap; } else { child->setSize(child->getWidth(), viewHeight - curY, true); } if (child->getWidth() > maxWidth) maxWidth = child->getWidth(); } //new line curX += ceil(maxWidth) + _columnGap; maxWidth = 0; j = 0; lineStart = i + 1; lineSize = 0; } } cw = curX + ceil(maxWidth); ch = viewHeight; } else { for (i = 0; i < cnt; i++) { child = getChildAt(i); if (foldInvisibleItems && !child->isVisible()) continue; if (curY != 0) curY += _lineGap; if ((_lineCount != 0 && j >= _lineCount) || (_lineCount == 0 && curY + child->getHeight() > viewHeight && maxWidth != 0)) { curY = 0; curX += ceil(maxWidth) + _columnGap; maxWidth = 0; j = 0; } child->setPosition(curX, curY); curY += child->getHeight(); if (curY > maxHeight) maxHeight = curY; if (child->getWidth() > maxWidth) maxWidth = child->getWidth(); j++; } cw = curX + ceil(maxWidth); ch = ceil(maxHeight); } } else //pagination { int page = 0; int k = 0; float eachHeight = 0; if (_autoResizeItem && _lineCount > 0) eachHeight = floor((viewHeight - (_lineCount - 1) * _lineGap) / _lineCount); if (_autoResizeItem && _columnCount > 0) { float lineSize = 0; int lineStart = 0; float ratio; for (i = 0; i < cnt; i++) { child = getChildAt(i); if (foldInvisibleItems && !child->isVisible()) continue; if (j == 0 && ((_lineCount != 0 && k >= _lineCount) || (_lineCount == 0 && curY + (_lineCount > 0 ? eachHeight : child->getHeight()) > viewHeight))) { //new page page++; curY = 0; k = 0; } lineSize += child->sourceSize.width; j++; if (j == _columnCount || i == cnt - 1) { ratio = (viewWidth - lineSize - (j - 1) * _columnGap) / lineSize; curX = 0; for (j = lineStart; j <= i; j++) { child = getChildAt(j); if (foldInvisibleItems && !child->isVisible()) continue; child->setPosition(page * viewWidth + curX, curY); if (j < i) { child->setSize(child->sourceSize.width + round(child->sourceSize.width * ratio), _lineCount > 0 ? eachHeight : child->getHeight(), true); curX += ceil(child->getWidth()) + _columnGap; } else { child->setSize(viewWidth - curX, _lineCount > 0 ? eachHeight : child->getHeight(), true); } if (child->getHeight() > maxHeight) maxHeight = child->getHeight(); } //new line curY += ceil(maxHeight) + _lineGap; maxHeight = 0; j = 0; lineStart = i + 1; lineSize = 0; k++; } } } else { for (i = 0; i < cnt; i++) { child = getChildAt(i); if (foldInvisibleItems && !child->isVisible()) continue; if (curX != 0) curX += _columnGap; if (_autoResizeItem && _lineCount > 0) child->setSize(child->getWidth(), eachHeight, true); if ((_columnCount != 0 && j >= _columnCount) || (_columnCount == 0 && curX + child->getWidth() > viewWidth && maxHeight != 0)) { curX = 0; curY += maxHeight + _lineGap; maxHeight = 0; j = 0; k++; if ((_lineCount != 0 && k >= _lineCount) || (_lineCount == 0 && curY + child->getHeight() > viewHeight && maxWidth != 0)) //new page { page++; curY = 0; k = 0; } } child->setPosition(page * viewWidth + curX, curY); curX += ceil(child->getWidth()); if (curX > maxWidth) maxWidth = curX; if (child->getHeight() > maxHeight) maxHeight = child->getHeight(); j++; } } ch = page > 0 ? viewHeight : (curY + ceil(maxHeight)); cw = (page + 1) * viewWidth; } handleAlign(cw, ch); setBounds(0, 0, cw, ch); } void GList::setup_beforeAdd(ByteBuffer* buffer, int beginPos) { GComponent::setup_beforeAdd(buffer, beginPos); buffer->seek(beginPos, 5); _layout = (ListLayoutType)buffer->readByte(); _selectionMode = (ListSelectionMode)buffer->readByte(); _align = (TextHAlignment)buffer->readByte(); _verticalAlign = (TextVAlignment)buffer->readByte(); _lineGap = buffer->readShort(); _columnGap = buffer->readShort(); _lineCount = buffer->readShort(); _columnCount = buffer->readShort(); _autoResizeItem = buffer->readBool(); _childrenRenderOrder = (ChildrenRenderOrder)buffer->readByte(); _apexIndex = buffer->readShort(); if (buffer->readBool()) { _margin.top = buffer->readInt(); _margin.bottom = buffer->readInt(); _margin.left = buffer->readInt(); _margin.right = buffer->readInt(); } OverflowType overflow = (OverflowType)buffer->readByte(); if (overflow == OverflowType::SCROLL) { int savedPos = buffer->getPos(); buffer->seek(beginPos, 7); setupScroll(buffer); buffer->setPos(savedPos); } else setupOverflow(overflow); if (buffer->readBool()) //clipSoftness buffer->skip(8); if (buffer->version >= 2) { scrollItemToViewOnClick = buffer->readBool(); foldInvisibleItems = buffer->readBool(); } buffer->seek(beginPos, 8); _defaultItem = buffer->readS(); readItems(buffer); } void GList::readItems(ByteBuffer* buffer) { const string* str; int itemCount = buffer->readShort(); for (int i = 0; i < itemCount; i++) { int nextPos = buffer->readUshort(); nextPos += buffer->getPos(); str = buffer->readSP(); if (!str || (*str).empty()) { str = &_defaultItem; if ((*str).empty()) { buffer->setPos(nextPos); continue; } } GObject* obj = getFromPool(*str); if (obj != nullptr) { addChild(obj); setupItem(buffer, obj); } buffer->setPos(nextPos); } } void GList::setupItem(ByteBuffer* buffer, GObject* obj) { const string* str; GButton* btn = dynamic_cast(obj); if ((str = buffer->readSP())) obj->setText(*str); if ((str = buffer->readSP()) && btn) btn->setSelectedTitle(*str); if ((str = buffer->readSP())) obj->setIcon(*str); if ((str = buffer->readSP()) && btn) btn->setSelectedIcon(*str); if ((str = buffer->readSP())) obj->name = *str; GComponent* gcom = dynamic_cast(obj); if (gcom != nullptr) { int cnt = buffer->readShort(); for (int i = 0; i < cnt; i++) { GController* cc = gcom->getController(buffer->readS()); const std::string& pageId = buffer->readS(); cc->setSelectedPageId(pageId); } if (buffer->version >= 2) { cnt = buffer->readShort(); for (int i = 0; i < cnt; i++) { std::string target = buffer->readS(); ObjectPropID propId = (ObjectPropID)buffer->readShort(); std::string value = buffer->readS(); GObject* obj2 = gcom->getChildByPath(target); if (obj2 != nullptr) obj2->setProp(propId, Value(value)); } } } } void GList::setup_afterAdd(ByteBuffer* buffer, int beginPos) { GComponent::setup_afterAdd(buffer, beginPos); buffer->seek(beginPos, 6); int i = buffer->readShort(); if (i != -1) _selectionController = _parent->getControllerAt(i); } NS_FGUI_END