LCOV - code coverage report
Current view: top level - xenolith/renderer/material2d/components/menu - MaterialTabBar.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 195 395 49.4 %
Date: 2024-05-12 00:16:13 Functions: 14 33 42.4 %

          Line data    Source code
       1             : /**
       2             :  Copyright (c) 2024 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 "MaterialTabBar.h"
      24             : #include "MaterialStyleContainer.h"
      25             : #include "XLFontLocale.h"
      26             : #include "XLInputListener.h"
      27             : #include "XLFontLocale.h"
      28             : #include "XLAction.h"
      29             : #include "XLActionEase.h"
      30             : #include "XLDirector.h"
      31             : #include "XLApplication.h"
      32             : 
      33             : namespace STAPPLER_VERSIONIZED stappler::xenolith::material2d {
      34             : 
      35             : constexpr static float TAB_MIN_WIDTH = 72.0f;
      36             : constexpr static float TAB_MAX_WIDTH = 264.0f;
      37             : 
      38           6 : void TabBarButton::initialize(const TabButtonCallback &cb, TabBar::ButtonStyle style, bool swallow, bool wrapped) {
      39           6 :         _tabButtonCallback = cb;
      40           6 :         setSwallowEvents(swallow);
      41             : 
      42           6 :         _tabStyle = style;
      43           6 :         _wrapped = wrapped;
      44             : 
      45           6 :         _labelText->setLocaleEnabled(true);
      46           6 :         _labelText->setTextTransform(font::TextTransform::Uppercase);
      47           6 : }
      48             : 
      49           6 : bool TabBarButton::init(MenuSourceButton *btn, const TabButtonCallback &cb, TabBar::ButtonStyle style, bool swallow, bool wrapped) {
      50           6 :         if (!Button::init(NodeStyle::Text, ColorRole::Secondary)) {
      51           0 :                 return false;
      52             :         }
      53             : 
      54           6 :         setShapeStyle(ShapeStyle::None);
      55           6 :         setTapCallback(std::bind(&TabBarButton::onTabButton, this));
      56             : 
      57           6 :         initialize(cb, style, swallow, wrapped);
      58           6 :         setMenuSourceButton(btn);
      59           6 :         _followContentSize = false;
      60           6 :         return true;
      61             : }
      62             : 
      63           0 : bool TabBarButton::init(MenuSource *source, const TabButtonCallback &cb, TabBar::ButtonStyle style, bool swallow, bool wrapped) {
      64           0 :         if (!Button::init(NodeStyle::Text, ColorRole::Secondary)) {
      65           0 :                 return false;
      66             :         }
      67             : 
      68           0 :         initialize(cb, style, swallow, wrapped);
      69             : 
      70             :         //_labelText->setString("SystemMore"_locale);
      71           0 :         _leadingIcon->setIconName(IconName::Navigation_more_vert_solid);
      72           0 :         _floatingMenuSource = source;
      73           0 :         _followContentSize = false;
      74           0 :         return true;
      75             : }
      76             : 
      77           6 : void TabBarButton::onTabButton() {
      78           6 :         if (_tabButtonCallback) {
      79           6 :                 _tabButtonCallback(this, _menuButtonListener->getSubscription());
      80             :         }
      81           6 : }
      82             : 
      83          30 : void TabBarButton::layoutContent() {
      84          30 :         _labelValue->setVisible(false);
      85          30 :         _trailingIcon->setVisible(false);
      86             : 
      87          30 :         switch (_tabStyle) {
      88           0 :         case TabBar::ButtonStyle::Icon:
      89           0 :                 _labelText->setVisible(false);
      90           0 :                 _leadingIcon->setVisible(true);
      91           0 :                 _leadingIcon->setAnchorPoint(Vec2(0.5f, 0.5f));
      92           0 :                 _leadingIcon->setPosition(Vec2(_contentSize.width / 2.0f, _contentSize.height / 2.0f));
      93           0 :                 break;
      94           0 :         case TabBar::ButtonStyle::Title:
      95           0 :                 _leadingIcon->setVisible(false);
      96           0 :                 _labelText->setVisible(true);
      97           0 :                 _labelText->setAlignment(font::TextAlign::Center);
      98           0 :                 _labelText->setFontWeight(_selected?font::FontWeight::SemiBold:font::FontWeight::Regular);
      99           0 :                 _labelText->setFontSize(14);
     100           0 :                 _labelText->setWidth(_contentSize.width - (_wrapped?32.0f:16.0f) - (_floatingMenuSource?16.0f:0.0f));
     101           0 :                 _labelText->tryUpdateLabel();
     102           0 :                 if (_labelText->getLinesCount() > 1) {
     103           0 :                         _labelText->setFontSize(12);
     104             :                 }
     105           0 :                 if (_floatingMenuSource) {
     106           0 :                         _leadingIcon->setVisible(true);
     107           0 :                         _leadingIcon->setIconName(IconName::Navigation_arrow_drop_down_solid);
     108           0 :                         _leadingIcon->setAnchorPoint(Vec2(1.0f, 0.5f));
     109           0 :                         _labelText->setAnchorPoint(Vec2(0.5f, 0.5f));
     110           0 :                         _labelText->setPosition(Vec2((_contentSize.width) / 2.0f - 8.0f, _contentSize.height / 2.0f));
     111           0 :                         _leadingIcon->setPosition(Vec2(_labelText->getPosition().x + _labelText->getContentSize().width / 2.0f, _contentSize.height / 2.0f));
     112           0 :                         _leadingIcon->setAnchorPoint(Vec2(0.0f, 0.5f));
     113             :                 } else {
     114           0 :                         _labelText->setAnchorPoint(Vec2(0.5f, 0.5f));
     115           0 :                         _labelText->setPosition(Vec2(_contentSize.width / 2.0f, _contentSize.height / 2.0f));
     116             :                 }
     117           0 :                 break;
     118          30 :         case TabBar::ButtonStyle::TitleIcon:
     119          30 :                 _leadingIcon->setVisible(true);
     120          30 :                 _labelText->setVisible(true);
     121          30 :                 _labelText->setAlignment(font::TextAlign::Center);
     122             : 
     123          30 :                 _leadingIcon->setAnchorPoint(Vec2(0.5f, 0.5f));
     124          30 :                 _leadingIcon->setPosition(Vec2(_contentSize.width / 2.0f, (_contentSize.height / 2.0f - 3.0f) + 13.0f));
     125          30 :                 _labelText->setFontWeight(_selected?font::FontWeight::SemiBold:font::FontWeight::Regular);
     126          30 :                 _labelText->setFontSize(12);
     127          30 :                 _labelText->setWidth(_contentSize.width - (_wrapped?30.0f:14.0f));
     128          30 :                 _labelText->setAnchorPoint(Vec2(0.5f, 0.5f));
     129          30 :                 _labelText->setPosition(Vec2(_contentSize.width / 2.0f, (_contentSize.height / 2.0f - 3.0f) - 13.0f));
     130          30 :                 _labelText->setMaxLines(1);
     131          30 :                 _labelText->setAdjustValue(4);
     132          30 :                 break;
     133             :         }
     134          30 : }
     135             : 
     136           2 : bool TabBar::init(MenuSource *source, ButtonStyle button, BarStyle bar, Alignment align) {
     137           2 :         if (!Surface::init(SurfaceStyle(NodeStyle::Filled))) {
     138           0 :                 return false;
     139             :         }
     140             : 
     141           2 :         _alignment = align;
     142           2 :         _buttonStyle = button;
     143           2 :         _barStyle = bar;
     144             : 
     145           2 :         _source = addComponent(Rc<DataListener<MenuSource>>::create([this] (SubscriptionFlags flags) {
     146           2 :                 onMenuSource();
     147           2 :         }));
     148           2 :         _source->setSubscription(source);
     149             : 
     150           2 :         if (_source && _source->getSubscription()) {
     151           2 :                 size_t idx = 0;
     152           2 :                 for (auto &it : _source->getSubscription()->getItems()) {
     153           2 :                         if (auto btn = dynamic_cast<MenuSourceButton *>(it.get())) {
     154           2 :                                 if (btn->isSelected()) {
     155           2 :                                         _selectedIndex = idx;
     156           2 :                                         break;
     157             :                                 }
     158           0 :                                 ++idx;
     159             :                         }
     160             :                 }
     161             :         }
     162             : 
     163           2 :         _scroll = addChild(Rc<ScrollView>::create(ScrollView::Horizontal));
     164           2 :         _scroll->setController(Rc<ScrollController>::create());
     165           2 :         _scroll->setOverscrollVisible(false);
     166           2 :         _scroll->setIndicatorVisible(false);
     167           2 :         _scroll->setScrollCallback(std::bind(&TabBar::onScrollPosition, this));
     168             : 
     169           2 :         _layer = _scroll->getRoot()->addChild(Rc<Layer>::create(), ZOrder(1));
     170           2 :         _layer->setAnchorPoint(Vec2(0.0f, 0.0f));
     171           2 :         _layer->setVisible(false);
     172             : 
     173           2 :         _left = addChild(Rc<IconSprite>::create(IconName::Navigation_chevron_left_solid));
     174           2 :         auto leftListener = _left->addInputListener(Rc<InputListener>::create());
     175           2 :         leftListener->addTouchRecognizer([] (const GestureData &) -> bool { return true; }, InputListener::makeButtonMask({InputMouseButton::Touch}));
     176           2 :         leftListener->setSwallowEvents(InputListener::EventMaskTouch);
     177             : 
     178           2 :         _right = addChild(Rc<IconSprite>::create(IconName::Navigation_chevron_right_solid));
     179           2 :         auto rightListener = _right->addInputListener(Rc<InputListener>::create());
     180           2 :         rightListener->addTouchRecognizer([] (const GestureData &) -> bool { return true; }, InputListener::makeButtonMask({InputMouseButton::Touch}));
     181           2 :         rightListener->setSwallowEvents(InputListener::EventMaskTouch);
     182             : 
     183           2 :         return true;
     184             : }
     185             : 
     186           4 : void TabBar::onContentSizeDirty() {
     187             :         struct ItemData {
     188             :                 float width = 0.0f;
     189             :                 MenuSourceButton *button = nullptr;
     190             :                 bool primary = true;
     191             :                 bool wrapped = false;
     192             :         };
     193             : 
     194           4 :         Surface::onContentSizeDirty();
     195             : 
     196           4 :         if (_buttonCount == 0) {
     197           2 :                 auto c = _scroll->getController();
     198           2 :                 c->clear();
     199           2 :                 return;
     200             :         }
     201             : 
     202           2 :         float width = 0.0f;
     203           2 :         Vector<ItemData> itemWidth; itemWidth.reserve(_buttonCount);
     204             : 
     205           2 :         if (_source && _source->getSubscription()) {
     206           2 :                 auto &items = _source->getSubscription()->getItems();
     207           8 :                 for (auto &item : items) {
     208           6 :                         if (auto btn = dynamic_cast<MenuSourceButton *>(item.get())) {
     209           6 :                                 bool wrapped = false;
     210           6 :                                 auto w = getItemSize(btn->getName(), false, btn->isSelected());
     211           6 :                                 if (w > TAB_MAX_WIDTH) {
     212           0 :                                         wrapped = true;
     213           0 :                                         w = TAB_MAX_WIDTH - 60.0f;
     214             :                                 }
     215           6 :                                 width += w;
     216           6 :                                 itemWidth.push_back(ItemData{w, btn, true, wrapped});
     217             :                         }
     218             :                 }
     219             :         }
     220             : 
     221           2 :         if (_contentSize.width == 0.0f) {
     222           0 :                 return;
     223             :         }
     224             : 
     225           2 :         float extraWidth = TAB_MIN_WIDTH;
     226           2 :         Rc<MenuSource> extraSource;
     227           2 :         if ((_barStyle == BarStyle::Layout) && width > _contentSize.width) {
     228           0 :                 width = 0.0f;
     229           0 :                 ItemData *prev = nullptr;
     230           0 :                 for (auto &it : itemWidth) {
     231           0 :                         if (extraSource != nullptr) {
     232           0 :                                 extraSource->addItem(it.button);
     233           0 :                                 it.primary = false;
     234             :                         } else {
     235           0 :                                 if (width + it.width > _contentSize.width) {
     236           0 :                                         extraSource = Rc<MenuSource>::create();
     237           0 :                                         extraSource->addItem(prev->button);
     238           0 :                                         extraSource->addItem(it.button);
     239           0 :                                         prev->primary = false;
     240           0 :                                         it.primary = false;
     241           0 :                                         extraWidth = getItemSize("SystemMore"_locale, true);
     242           0 :                                         width += extraWidth;
     243           0 :                                         width -= prev->width;
     244             :                                 } else {
     245           0 :                                         width += it.width;
     246           0 :                                         prev = &it;
     247             :                                 }
     248             :                         }
     249             :                 }
     250             :         }
     251             : 
     252           2 :         float scale = 1.0f;
     253           2 :         if (_alignment == Alignment::Justify) {
     254           2 :                 if (width < _contentSize.width) {
     255           2 :                         scale = _contentSize.width / width;
     256             :                 }
     257           2 :                 width = _contentSize.width;
     258             :         }
     259             : 
     260           2 :         auto pos = _scroll->getScrollRelativePosition();
     261           2 :         auto c = _scroll->getController();
     262           2 :         c->clear();
     263             : 
     264           2 :         _positions.clear();
     265             : 
     266           2 :         float currentWidth = 0.0f;
     267           8 :         for (auto &it : itemWidth) {
     268           6 :                 if (it.primary) {
     269           6 :                         c->addItem(std::bind(&TabBar::onItem, this, it.button, it.wrapped), it.width * scale);
     270           6 :                         _positions.emplace_back(currentWidth, it.width * scale);
     271           6 :                         currentWidth += it.width * scale;
     272             :                 } else {
     273           0 :                         _positions.emplace_back(nan(), nan());
     274             :                 }
     275             :         }
     276             : 
     277           2 :         if (extraSource) {
     278           0 :                 _extra = extraSource;
     279           0 :                 c->addItem(std::bind(&TabBar::onItem, this, nullptr, false), extraWidth * scale);
     280             :         }
     281             : 
     282           2 :         _scrollWidth = width;
     283           2 :         if (_contentSize.width >= _scrollWidth) {
     284           2 :                 _left->setVisible(false);
     285           2 :                 _right->setVisible(false);
     286             : 
     287           2 :                 _scroll->setContentSize(Size2(_scrollWidth, _contentSize.height));
     288           2 :                 switch (_alignment) {
     289           0 :                 case Alignment::Right:
     290           0 :                         _scroll->setAnchorPoint(Vec2(1.0f, 0.5f));
     291           0 :                         _scroll->setPosition(Vec2(_contentSize.width, _contentSize.height / 2.0f));
     292           0 :                         break;
     293           2 :                 case Alignment::Center:
     294             :                 case Alignment::Justify:
     295           2 :                         _scroll->setAnchorPoint(Vec2(0.5f, 0.5f));
     296           2 :                         _scroll->setPosition(Vec2(_contentSize.width / 2.0f, _contentSize.height / 2.0f));
     297           2 :                         break;
     298           0 :                 case Alignment::Left:
     299           0 :                         _scroll->setAnchorPoint(Vec2(0.0f, 0.5f));
     300           0 :                         _scroll->setPosition(Vec2(0.0f, _contentSize.height / 2.0f));
     301           0 :                         break;
     302           0 :                 default: break;
     303             :                 }
     304             : 
     305           2 :                 _scroll->updateScrollBounds();
     306           2 :                 _scroll->setScrollPosition(0.0f);
     307             :         } else {
     308           0 :                 _left->setVisible(true);
     309           0 :                 _right->setVisible(true);
     310             : 
     311           0 :                 _left->setAnchorPoint(Vec2(0.5f, 0.5f));
     312           0 :                 _left->setPosition(Vec2(16.0f, _contentSize.height / 2.0f));
     313             : 
     314           0 :                 _right->setAnchorPoint(Vec2(0.5f, 0.5f));
     315           0 :                 _right->setPosition(Vec2(_contentSize.width - 16.0f, _contentSize.height / 2.0f));
     316             : 
     317           0 :                 _scroll->setAnchorPoint(Vec2(0.5f, 0.5f));
     318           0 :                 _scroll->setPosition(Vec2(_contentSize.width / 2.0f, _contentSize.height / 2.0f));
     319           0 :                 _scroll->setContentSize(Size2(_contentSize.width - 64.0f, _contentSize.height));
     320           0 :                 _scroll->setScrollRelativePosition(pos);
     321           0 :                 _scroll->updateScrollBounds();
     322           0 :                 _scroll->getController()->onScrollPosition(true);
     323             : 
     324           0 :                 onScrollPositionProgress(0.5f);
     325             :         }
     326             : 
     327           2 :         setSelectedTabIndex(_selectedIndex);
     328           2 : }
     329             : 
     330           0 : void TabBar::setMenuSource(MenuSource *source) {
     331           0 :         if (_source->getSubscription() != source) {
     332           0 :                 _source->setSubscription(source);
     333           0 :                 _selectedIndex = maxOf<size_t>();
     334             : 
     335           0 :                 if (_source && _source->getSubscription()) {
     336           0 :                         auto &items = _source->getSubscription()->getItems();
     337           0 :                         size_t idx = 0;
     338           0 :                         for (auto &it : items) {
     339           0 :                                 if (auto btn = dynamic_cast<MenuSourceButton *>(it.get())) {
     340           0 :                                         if (btn->isSelected()) {
     341           0 :                                                 _selectedIndex = idx;
     342           0 :                                                 break;
     343             :                                         }
     344           0 :                                         ++idx;
     345             :                                 }
     346             :                         }
     347             :                 }
     348             :         }
     349           0 : }
     350             : 
     351           0 : MenuSource *TabBar::getMenuSource() const {
     352           0 :         return _source->getSubscription();
     353             : }
     354             : 
     355           0 : void TabBar::setAccentColor(const ColorRole &color) {
     356           0 :         if (_accentColor != color) {
     357           0 :                 _accentColor = color;
     358             :         }
     359           0 : }
     360           0 : ColorRole TabBar::getAccentColor() const {
     361           0 :         return _accentColor;
     362             : }
     363             : 
     364           0 : void TabBar::setButtonStyle(const ButtonStyle &btn) {
     365           0 :         if (_buttonStyle != btn) {
     366           0 :                 _buttonStyle = btn;
     367           0 :                 _contentSizeDirty = true;
     368             :         }
     369           0 : }
     370           0 : const TabBar::ButtonStyle & TabBar::getButtonStyle() const {
     371           0 :         return _buttonStyle;
     372             : }
     373             : 
     374           0 : void TabBar::setBarStyle(const BarStyle &bar) {
     375           0 :         if (_barStyle != bar) {
     376           0 :                 _barStyle = bar;
     377           0 :                 _contentSizeDirty = true;
     378             :         }
     379           0 : }
     380           0 : const TabBar::BarStyle & TabBar::getBarStyle() const {
     381           0 :         return _barStyle;
     382             : }
     383             : 
     384           0 : void TabBar::setAlignment(const Alignment &a) {
     385           0 :         if (_alignment != a) {
     386           0 :                 _alignment = a;
     387           0 :                 _contentSizeDirty = true;
     388             :         }
     389           0 : }
     390           0 : const TabBar::Alignment & TabBar::getAlignment() const {
     391           0 :         return _alignment;
     392             : }
     393             : 
     394           2 : void TabBar::onMenuSource() {
     395           2 :         _buttonCount = 0;
     396           2 :         auto &items = _source->getSubscription()->getItems();
     397           8 :         for (auto &item : items) {
     398           6 :                 if (dynamic_cast<MenuSourceButton *>(item.get())) {
     399           6 :                         ++ _buttonCount;
     400             :                 }
     401             :         }
     402           2 :         _contentSizeDirty = true;
     403           2 : }
     404             : 
     405           6 : float TabBar::getItemSize(const String &name, bool extended, bool selected) const {
     406           6 :         auto fc = _director->getApplication()->getExtension<font::FontController>();
     407           6 :         if (_buttonStyle == ButtonStyle::Icon) {
     408           0 :                 return TAB_MIN_WIDTH;
     409           6 :         } else if (_buttonStyle == ButtonStyle::Title) {
     410           0 :                 auto desc = TypescaleLabel::getTypescaleRoleStyle(TypescaleRole::BodyLarge, _inputDensity);
     411           0 :                 desc.font.fontWeight = selected?font::FontWeight::SemiBold:font::FontWeight::Regular;
     412             : 
     413           0 :                 float width = Label::getStringWidth(fc, desc, name, true) + 32.0f;
     414           0 :                 if (extended) {
     415           0 :                         width += 16.0f;
     416             :                 }
     417           0 :                 return std::max(width, TAB_MIN_WIDTH);
     418             :         } else {
     419           6 :                 auto desc = TypescaleLabel::getTypescaleRoleStyle(TypescaleRole::BodyLarge, _inputDensity);
     420           6 :                 desc.font.fontSize = font::FontSize(12);
     421           6 :                 desc.font.fontWeight = selected?font::FontWeight::SemiBold:font::FontWeight::Regular;
     422             : 
     423           6 :                 float width = Label::getStringWidth(fc, desc, name, true);
     424           6 :                 if (width > 72.0f) {
     425           0 :                         desc.font.fontSize = font::FontSize(8);
     426           0 :                         width = ceilf(Label::getStringWidth(fc, desc, name, true));
     427             :                 }
     428           6 :                 return std::max(width + 16.0f, TAB_MIN_WIDTH);
     429             :         }
     430             : }
     431             : 
     432           6 : Rc<Node> TabBar::onItem(MenuSourceButton *btn, bool wrapped) {
     433           6 :         Rc<TabBarButton> ret = nullptr;
     434           6 :         if (btn) {
     435           6 :                 ret = Rc<TabBarButton>::create(btn, [this] (Button *b, MenuSourceButton *btn) {
     436           6 :                         onTabButton(b, btn);
     437          12 :                 }, _buttonStyle, _barStyle == BarStyle::Layout, wrapped);
     438             :         } else {
     439           0 :                 ret = Rc<TabBarButton>::create(_extra, [this] (Button *b, MenuSourceButton *btn) {
     440           0 :                         onTabButton(b, btn);
     441           0 :                 }, _buttonStyle, _barStyle == BarStyle::Layout, wrapped);
     442             :         }
     443             : 
     444             :         /*if (ret) {
     445             :                 ret->setTextColor(_textColor);
     446             :                 ret->setSelectedColor(_selectedColor);
     447             :         }*/
     448             : 
     449          12 :         return ret;
     450           6 : }
     451             : 
     452           6 : void TabBar::onTabButton(Button *b, MenuSourceButton *btn) {
     453           6 :         auto &items = _source->getSubscription()->getItems();
     454          24 :         for (auto &it : items) {
     455          18 :                 auto b = dynamic_cast<MenuSourceButton *>(it.get());
     456          18 :                 b->setSelected(false);
     457             :         }
     458           6 :         btn->setSelected(true);
     459           6 :         size_t idx = 0;
     460          12 :         for (auto &it : items) {
     461          12 :                 if (btn == it) {
     462           6 :                         break;
     463             :                 }
     464           6 :                 ++ idx;
     465             :         }
     466           6 :         if (idx != _selectedIndex) {
     467           6 :                 auto &cb = btn->getCallback();
     468           6 :                 if (cb) {
     469           6 :                         cb(b, btn);
     470             :                 }
     471             :         }
     472           6 :         if (idx < size_t(items.size())) {
     473           6 :                 setSelectedTabIndex(idx);
     474             :         } else {
     475           0 :                 setSelectedTabIndex(maxOf<size_t>());
     476             :         }
     477           6 : }
     478             : 
     479           0 : void TabBar::onScrollPosition() {
     480           0 :         if (_scrollWidth > _contentSize.width) {
     481           0 :                 onScrollPositionProgress(_scroll->getScrollRelativePosition());
     482             :         }
     483           0 : }
     484             : 
     485           0 : void TabBar::onScrollPositionProgress(float pos) {
     486           0 :         if (pos <= 0.01f) {
     487           0 :                 _left->setOpacity(64);
     488           0 :                 _right->setOpacity(222);
     489           0 :         } else if (pos >= 0.99f) {
     490           0 :                 _left->setOpacity(222);
     491           0 :                 _right->setOpacity(64);
     492             :         } else {
     493           0 :                 _left->setOpacity(222);
     494           0 :                 _right->setOpacity(222);
     495             :         }
     496           0 : }
     497             : 
     498           8 : void TabBar::setSelectedTabIndex(size_t idx) {
     499           8 :         if (idx == maxOf<size_t>() || idx >= _positions.size()) {
     500           0 :                 _layer->setVisible(false);
     501             :         } else {
     502           8 :                 auto pos = _positions[idx];
     503             : 
     504           8 :                 if (_selectedIndex == maxOf<size_t>()) {
     505           0 :                         _layer->setVisible(true);
     506           0 :                         _layer->setOpacity(1.0f);
     507             : 
     508           0 :                         _layer->setPosition(Vec2(pos.first, 0.0f));
     509           0 :                         _layer->setContentSize(Size2(pos.second, 2.0f));
     510             : 
     511           0 :                         _layer->stopAllActionsByTag("TabBarAction"_tag);
     512           0 :                         auto spawn = Rc<EaseQuarticActionOut>::create(Rc<FadeTo>::create(0.15f, 1.0f));
     513           0 :                         _layer->runAction(spawn, "TabBarAction"_tag);
     514           0 :                 } else {
     515           8 :                         _layer->setVisible(true);
     516           8 :                         _layer->setOpacity(1.0f);
     517           8 :                         _layer->stopAllActionsByTag("TabBarAction"_tag);
     518             :                         auto spawn = Rc<EaseQuarticActionOut>::create(
     519          16 :                                         Rc<Spawn>::create(
     520          16 :                                                         Rc<MoveTo>::create(0.35f, Vec2(pos.first, 0.0f)),
     521          16 :                                                         Rc<ResizeTo>::create(0.35f, Size2(pos.second, 2.0f)),
     522           8 :                                                         Rc<FadeTo>::create(0.15f, 1.0f)
     523             :                                         )
     524           8 :                         );
     525           8 :                         _layer->runAction(spawn, "TabBarAction"_tag);
     526             : 
     527           8 :                         float scrollPos = _scroll->getScrollPosition();
     528           8 :                         float scrollSize = _scroll->getScrollSize();
     529             : 
     530           8 :                         if (scrollPos > pos.first) {
     531           0 :                                 if (idx == 0) {
     532           0 :                                         onScrollPositionProgress(0.0f);
     533           0 :                                         _scroll->runAdjustPosition(pos.first);
     534             :                                 } else {
     535           0 :                                         _scroll->runAdjustPosition(pos.first - _positions[idx - 1].second / 2.0f);
     536             :                                 }
     537           8 :                         } else if (scrollPos + scrollSize < pos.first + pos.second) {
     538           0 :                                 if (idx + 1 == _positions.size()) {
     539           0 :                                         onScrollPositionProgress(1.0f);
     540           0 :                                         _scroll->runAdjustPosition(pos.first + pos.second - _scroll->getScrollSize());
     541             :                                 } else {
     542           0 :                                         _scroll->runAdjustPosition(pos.first + pos.second - _scroll->getScrollSize() + _positions[idx + 1].second / 2.0f);
     543             :                                 }
     544             :                         }
     545           8 :                 }
     546             :         }
     547           8 :         _selectedIndex = idx;
     548           8 : }
     549             : 
     550           0 : void TabBar::setSelectedIndex(size_t nidx) {
     551           0 :         if (_source) {
     552           0 :                 auto &items = _source->getSubscription()->getItems();
     553             : 
     554           0 :                 size_t idx = 0;
     555           0 :                 for (auto &it : items) {
     556           0 :                         if (auto b = dynamic_cast<MenuSourceButton *>(it.get())) {
     557           0 :                                 if (idx == nidx) {
     558           0 :                                         b->setSelected(true);
     559           0 :                                         setSelectedTabIndex(idx);
     560             :                                 } else {
     561           0 :                                         b->setSelected(false);
     562             :                                 }
     563           0 :                                 ++ idx;
     564             :                         }
     565             :                 }
     566             :         }
     567           0 : }
     568             : 
     569           0 : size_t TabBar::getSelectedIndex() const {
     570           0 :         return _selectedIndex;
     571             : }
     572             : 
     573           0 : void TabBar::setProgress(float prog) {
     574           0 :         if (_layer->getActionByTag("TabBarAction"_tag) == nullptr) {
     575           0 :                 if (prog > 0.0f) {
     576           0 :                         if (_selectedIndex + 1 < _positions.size()) {
     577           0 :                                 auto &origin = _positions[_selectedIndex];
     578           0 :                                 auto &next = _positions[_selectedIndex + 1];
     579           0 :                                 _layer->setPositionX(progress(origin.first, next.first, prog));
     580           0 :                                 _layer->setContentSize(Size2(progress(origin.second, next.second, prog), _layer->getContentSize().height));
     581             :                         }
     582           0 :                 } else if (prog < 0.0f) {
     583           0 :                         prog = -prog;
     584           0 :                         if (_selectedIndex > 0) {
     585           0 :                                 auto &origin = _positions[_selectedIndex];
     586           0 :                                 auto &next = _positions[_selectedIndex - 1];
     587           0 :                                 _layer->setPositionX(progress(origin.first, next.first, prog));
     588           0 :                                 _layer->setContentSize(Size2(progress(origin.second, next.second, prog), _layer->getContentSize().height));
     589             :                         }
     590             :                 }
     591             :         }
     592           0 : }
     593             : 
     594           2 : void TabBar::applyStyle(StyleContainer *c, const SurfaceStyleData &style) {
     595           2 :         Surface::applyStyle(c, style);
     596             : 
     597           2 :         if (auto scheme = c->getScheme(style.schemeTag)) {
     598           2 :                 auto c = scheme->get(_accentColor);
     599           2 :                 _layer->setColor(c, false);
     600             : 
     601           2 :                 _left->setColor(style.colorOn);
     602           2 :                 _right->setColor(style.colorOn);
     603             :         }
     604           2 : }
     605             : 
     606             : }

Generated by: LCOV version 1.14