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