1 /*
2 * The ManaPlus Client
3 * Copyright (C) 2008-2009 The Mana World Development Team
4 * Copyright (C) 2009-2010 The Mana Developers
5 * Copyright (C) 2011-2019 The ManaPlus Developers
6 * Copyright (C) 2019-2021 Andrei Karas
7 *
8 * This file is part of The ManaPlus Client.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /* _______ __ __ __ ______ __ __ _______ __ __
25 * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
26 * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
27 * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
28 * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
29 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
30 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
31 *
32 * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
33 *
34 *
35 * Per Larsson a.k.a finalman
36 * Olof Naessén a.k.a jansem/yakslem
37 *
38 * Visit: http://guichan.sourceforge.net
39 *
40 * License: (BSD)
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 * notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 * notice, this list of conditions and the following disclaimer in
48 * the documentation and/or other materials provided with the
49 * distribution.
50 * 3. Neither the name of Guichan nor the names of its contributors may
51 * be used to endorse or promote products derived from this software
52 * without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
55 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
56 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
57 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
58 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
59 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
60 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
61 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
62 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
63 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
64 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65 */
66
67 #include "gui/widgets/tabbedarea.h"
68
69 #include "gui/gui.h"
70
71 #include "gui/widgets/button.h"
72 #include "gui/widgets/scrollarea.h"
73 #include "gui/widgets/tabs/tab.h"
74
75 #include "utils/delete2.h"
76 #include "utils/foreach.h"
77
78 #include "debug.h"
79
TabbedArea(const Widget2 * const widget)80 TabbedArea::TabbedArea(const Widget2 *const widget) :
81 ActionListener(),
82 BasicContainer(widget),
83 KeyListener(),
84 MouseListener(),
85 WidgetListener(),
86 mArrowButton(),
87 mSelectedTab(nullptr),
88 mTabContainer(new BasicContainer2(widget)),
89 mWidgetContainer(new BasicContainer2(widget)),
90 mTabsToDelete(),
91 mTabs(),
92 mTabsWidth(0),
93 mVisibleTabsWidth(0),
94 mTabScrollIndex(0),
95 mRightMargin(0),
96 mOpaque(Opaque_false),
97 mEnableScrollButtons(false),
98 mFollowDownScroll(false),
99 mBlockSwitching(true),
100 mResizeHeight(true)
101 {
102 setFocusable(true);
103 addKeyListener(this);
104 addMouseListener(this);
105 }
106
postInit()107 void TabbedArea::postInit()
108 {
109 mTabContainer->setOpaque(Opaque_false);
110
111 add(mTabContainer);
112 add(mWidgetContainer);
113
114 mWidgetContainer->setOpaque(Opaque_false);
115 addWidgetListener(this);
116
117 mArrowButton[0] = new Button(this,
118 "<",
119 "shift_left",
120 BUTTON_SKIN,
121 this);
122 mArrowButton[1] = new Button(this,
123 ">",
124 "shift_right",
125 BUTTON_SKIN,
126 this);
127
128 widgetResized(Event(nullptr));
129 }
130
~TabbedArea()131 TabbedArea::~TabbedArea()
132 {
133 if (gui != nullptr)
134 gui->removeDragged(this);
135
136 // +++ virtual method calls
137 remove(mTabContainer);
138 remove(mWidgetContainer);
139
140 delete2(mTabContainer)
141 delete2(mWidgetContainer)
142
143 for (size_t i = 0, sz = mTabsToDelete.size(); i < sz; i++)
144 delete2(mTabsToDelete[i])
145
146 delete2(mArrowButton[0])
147 delete2(mArrowButton[1])
148 }
149
enableScrollButtons(const bool enable)150 void TabbedArea::enableScrollButtons(const bool enable)
151 {
152 if (mEnableScrollButtons && !enable)
153 {
154 if (mArrowButton[0] != nullptr)
155 remove(mArrowButton[0]);
156 if (mArrowButton[1] != nullptr)
157 remove(mArrowButton[1]);
158 }
159 else if (!mEnableScrollButtons && enable)
160 {
161 if (mArrowButton[0] != nullptr)
162 add(mArrowButton[0]);
163 if (mArrowButton[1] != nullptr)
164 add(mArrowButton[1]);
165 }
166 mEnableScrollButtons = enable;
167 }
168
getNumberOfTabs() const169 int TabbedArea::getNumberOfTabs() const
170 {
171 return CAST_S32(mTabs.size());
172 }
173
getTab(const std::string & name) const174 Tab *TabbedArea::getTab(const std::string &name) const
175 {
176 TabContainer::const_iterator itr = mTabs.begin();
177 const TabContainer::const_iterator itr_end = mTabs.end();
178 while (itr != itr_end)
179 {
180 if ((*itr).first->getCaption() == name)
181 return static_cast<Tab*>((*itr).first);
182
183 ++itr;
184 }
185 return nullptr;
186 }
187
draw(Graphics * const graphics)188 void TabbedArea::draw(Graphics *const graphics)
189 {
190 BLOCK_START("TabbedArea::draw")
191 if (mTabs.empty())
192 {
193 BLOCK_END("TabbedArea::draw")
194 return;
195 }
196
197 drawChildren(graphics);
198 BLOCK_END("TabbedArea::draw")
199 }
200
safeDraw(Graphics * const graphics)201 void TabbedArea::safeDraw(Graphics *const graphics)
202 {
203 BLOCK_START("TabbedArea::draw")
204 if (mTabs.empty())
205 {
206 BLOCK_END("TabbedArea::draw")
207 return;
208 }
209
210 safeDrawChildren(graphics);
211 BLOCK_END("TabbedArea::draw")
212 }
213
getWidget(const std::string & name) const214 Widget *TabbedArea::getWidget(const std::string &name) const
215 {
216 TabContainer::const_iterator itr = mTabs.begin();
217 const TabContainer::const_iterator itr_end = mTabs.end();
218 while (itr != itr_end)
219 {
220 if ((*itr).first->getCaption() == name)
221 return (*itr).second;
222
223 ++itr;
224 }
225
226 return nullptr;
227 }
228
getCurrentWidget() const229 Widget *TabbedArea::getCurrentWidget() const
230 {
231 const Tab *const tab = getSelectedTab();
232
233 if (tab != nullptr)
234 return getWidget(tab->getCaption());
235 return nullptr;
236 }
237
addTab(Tab * const tab,Widget * const widget)238 void TabbedArea::addTab(Tab *const tab,
239 Widget *const widget)
240 {
241 if ((tab == nullptr) || (widget == nullptr))
242 return;
243
244 tab->setTabbedArea(this);
245 tab->addActionListener(this);
246
247 mTabContainer->add(tab);
248 mTabs.push_back(std::pair<Tab*, Widget*>(tab, widget));
249
250 if ((mSelectedTab == nullptr) && tab->mVisible == Visible_true)
251 setSelectedTab(tab);
252
253 adjustTabPositions();
254 adjustSize();
255
256 const int frameSize = 2 * mFrameSize;
257 widget->setSize(getWidth() - frameSize,
258 getHeight() - frameSize - mTabContainer->getHeight());
259
260 widgetResized(Event(nullptr));
261 updateTabsWidth();
262 updateArrowEnableState();
263 }
264
adjustWidget(Widget * const widget) const265 void TabbedArea::adjustWidget(Widget *const widget) const
266 {
267 if (widget == nullptr)
268 return;
269 const int frameSize = 2 * mFrameSize;
270 widget->setSize(getWidth() - frameSize,
271 getHeight() - frameSize - mTabContainer->getHeight());
272 }
273
addTab(const std::string & caption,Widget * const widget)274 void TabbedArea::addTab(const std::string &caption, Widget *const widget)
275 {
276 Tab *const tab = new Tab(this);
277 tab->setCaption(caption);
278 mTabsToDelete.push_back(tab);
279
280 addTab(tab, widget);
281 }
282
addTab(Image * const image,Widget * const widget)283 void TabbedArea::addTab(Image *const image, Widget *const widget)
284 {
285 Tab *const tab = new Tab(this);
286 tab->setImage(image);
287 mTabsToDelete.push_back(tab);
288
289 addTab(tab, widget);
290 }
291
isTabSelected(const size_t index) const292 bool TabbedArea::isTabSelected(const size_t index) const
293 {
294 if (index >= mTabs.size())
295 return false;
296
297 return mSelectedTab == mTabs[index].first;
298 }
299
isTabPresent(const Tab * const tab) const300 bool TabbedArea::isTabPresent(const Tab *const tab) const
301 {
302 FOR_EACH (TabContainer::const_iterator, it, mTabs)
303 {
304 if ((*it).first == tab || (*it).second == tab)
305 return true;
306 }
307 return false;
308 }
309
isTabSelected(const Tab * const tab) const310 bool TabbedArea::isTabSelected(const Tab *const tab) const
311 {
312 return mSelectedTab == tab;
313 }
314
setSelectedTabByIndex(const size_t index)315 void TabbedArea::setSelectedTabByIndex(const size_t index)
316 {
317 if (index >= mTabs.size())
318 return;
319
320 setSelectedTab(mTabs[index].first);
321 }
322
removeTab(Tab * const tab)323 void TabbedArea::removeTab(Tab *const tab)
324 {
325 int tabIndexToBeSelected = -1;
326
327 if (tab == mSelectedTab)
328 {
329 const int index = getSelectedTabIndex();
330 const size_t sz = mTabs.size();
331 if (index == CAST_S32(sz) - 1 && sz == 1)
332 tabIndexToBeSelected = -1;
333 else
334 tabIndexToBeSelected = index - 1;
335 }
336
337 for (TabContainer::iterator iter = mTabs.begin();
338 iter != mTabs.end(); ++iter)
339 {
340 if (iter->first == tab)
341 {
342 mTabContainer->remove(tab);
343 mTabs.erase(iter);
344 break;
345 }
346 }
347
348 for (STD_VECTOR<Tab*>::iterator iter2 = mTabsToDelete.begin();
349 iter2 != mTabsToDelete.end(); ++iter2)
350 {
351 if (*iter2 == tab)
352 {
353 mTabsToDelete.erase(iter2);
354 delete tab;
355 break;
356 }
357 }
358
359 const int tabsSize = CAST_S32(mTabs.size());
360 if (tabIndexToBeSelected >= tabsSize)
361 tabIndexToBeSelected = tabsSize - 1;
362 if (tabIndexToBeSelected < -1)
363 tabIndexToBeSelected = -1;
364
365 if (tabIndexToBeSelected == -1)
366 {
367 mSelectedTab = nullptr;
368 mWidgetContainer->clear();
369 }
370 else
371 {
372 setSelectedTabByIndex(tabIndexToBeSelected);
373 }
374
375 adjustSize();
376 updateTabsWidth();
377 widgetResized(Event(nullptr));
378 }
379
logic()380 void TabbedArea::logic()
381 {
382 BLOCK_START("TabbedArea::logic")
383 logicChildren();
384 BLOCK_END("TabbedArea::logic")
385 }
386
mousePressed(MouseEvent & event)387 void TabbedArea::mousePressed(MouseEvent &event)
388 {
389 if (event.isConsumed())
390 return;
391
392 if (event.getButton() == MouseButton::LEFT)
393 {
394 Widget *const widget = mTabContainer->getWidgetAt(
395 event.getX(), event.getY());
396 Tab *const tab = dynamic_cast<Tab *>(widget);
397
398 if (tab != nullptr)
399 {
400 event.consume();
401 setSelectedTab(tab);
402 requestFocus();
403 }
404 }
405 }
406
setSelectedTab(Tab * const tab)407 void TabbedArea::setSelectedTab(Tab *const tab)
408 {
409 for (size_t i = 0; i < mTabs.size(); i++)
410 {
411 if (mTabs[i].first == mSelectedTab)
412 mWidgetContainer->remove(mTabs[i].second);
413 }
414
415 for (size_t i = 0; i < mTabs.size(); i++)
416 {
417 if (mTabs[i].first == tab)
418 {
419 mSelectedTab = tab;
420 mWidgetContainer->add(mTabs[i].second);
421 }
422 }
423
424 Tab *const newTab = tab;
425
426 if (newTab != nullptr)
427 newTab->setCurrent();
428
429 widgetResized(Event(nullptr));
430 }
431
setSelectedTabDefault()432 void TabbedArea::setSelectedTabDefault()
433 {
434 if (mSelectedTab == nullptr ||
435 mSelectedTab->mVisible == Visible_false)
436 {
437 for (size_t i = 0; i < mTabs.size(); i++)
438 {
439 Tab *const tab = mTabs[i].first;
440 if ((tab != nullptr) && tab->mVisible == Visible_true)
441 {
442 setSelectedTab(tab);
443 return;
444 }
445 }
446 }
447 }
448
getSelectedTabIndex() const449 int TabbedArea::getSelectedTabIndex() const
450 {
451 for (unsigned int i = 0, fsz = CAST_U32(mTabs.size());
452 i < fsz;
453 i++)
454 {
455 if (mTabs[i].first == mSelectedTab)
456 return i;
457 }
458
459 return -1;
460 }
461
setSelectedTabByName(const std::string & name)462 void TabbedArea::setSelectedTabByName(const std::string &name)
463 {
464 FOR_EACH (TabContainer::const_iterator, itr, mTabs)
465 {
466 if (((*itr).first != nullptr) && (*itr).first->getCaption() == name)
467 {
468 setSelectedTab((*itr).first);
469 return;
470 }
471 }
472 }
473
widgetResized(const Event & event A_UNUSED)474 void TabbedArea::widgetResized(const Event &event A_UNUSED)
475 {
476 adjustSize();
477
478 const int frameSize = 2 * mFrameSize;
479 const int widgetFrameSize = 2 * mWidgetContainer->getFrameSize();
480 const int w1 = mDimension.width;
481 const int h1 = mDimension.height;
482 const int height = h1 - frameSize
483 - mWidgetContainer->getY() - widgetFrameSize;
484
485 Widget *const w = getCurrentWidget();
486 ScrollArea *const scr = dynamic_cast<ScrollArea *>(w);
487 if (scr != nullptr)
488 {
489 if (mFollowDownScroll && height != 0)
490 {
491 const Rect &rect = w->getDimension();
492 if (rect.height != 0 && rect.height > height + 2)
493 {
494 if (scr->getVerticalScrollAmount()
495 >= scr->getVerticalMaxScroll() - 2
496 && scr->getVerticalScrollAmount()
497 <= scr->getVerticalMaxScroll() + 2)
498 {
499 const int newScroll = scr->getVerticalScrollAmount()
500 + rect.height - height;
501 w->setSize(mWidgetContainer->getWidth() - frameSize,
502 mWidgetContainer->getHeight() - frameSize);
503 if (newScroll != 0)
504 scr->setVerticalScrollAmount(newScroll);
505 }
506 }
507 }
508 }
509
510 if (mArrowButton[1] != nullptr)
511 {
512 // Check whether there is room to show more tabs now.
513 int innerWidth = w1 - 4 - mArrowButton[0]->getWidth()
514 - mArrowButton[1]->getWidth() - mRightMargin;
515 if (innerWidth < 0)
516 innerWidth = 0;
517
518 int newWidth = mVisibleTabsWidth;
519 while ((mTabScrollIndex != 0U) && newWidth < innerWidth)
520 {
521 Tab *const tab = mTabs[mTabScrollIndex - 1].first;
522 if ((tab != nullptr) && tab->mVisible == Visible_true)
523 {
524 newWidth += tab->getWidth();
525 if (newWidth < innerWidth)
526 --mTabScrollIndex;
527 }
528 }
529
530 if (mArrowButton[1] != nullptr)
531 {
532 const int width = w1 - frameSize - widgetFrameSize;
533 // Move the right arrow to fit the windows content.
534 newWidth = width - mArrowButton[1]->getWidth() - mRightMargin;
535 if (newWidth < 0)
536 newWidth = 0;
537 mArrowButton[1]->setPosition(newWidth, 0);
538 }
539 }
540
541 updateArrowEnableState();
542 adjustTabPositions();
543 }
544
updateTabsWidth()545 void TabbedArea::updateTabsWidth()
546 {
547 mTabsWidth = 0;
548 FOR_EACH (TabContainer::const_iterator, itr, mTabs)
549 {
550 Tab *const tab = (*itr).first;
551 if ((tab != nullptr) && tab->mVisible == Visible_true)
552 mTabsWidth += tab->getWidth();
553 }
554 updateVisibleTabsWidth();
555 }
556
updateVisibleTabsWidth()557 void TabbedArea::updateVisibleTabsWidth()
558 {
559 unsigned int visibleTabsWidth = 0;
560 for (size_t i = mTabScrollIndex, sz = mTabs.size(); i < sz; ++i)
561 {
562 Tab *const tab = mTabs[i].first;
563 if (tab != nullptr && tab->mVisible == Visible_true)
564 visibleTabsWidth += CAST_S32(tab->getWidth());
565 }
566 mVisibleTabsWidth = visibleTabsWidth;
567 }
568
adjustSize()569 void TabbedArea::adjustSize()
570 {
571 int maxTabHeight = 0;
572
573 const int width = mDimension.width;
574 const int height = mDimension.height;
575
576 for (size_t i = 0, sz = mTabs.size(); i < sz; i++)
577 {
578 if (mTabs[i].first->getHeight() > maxTabHeight)
579 maxTabHeight = mTabs[i].first->getHeight();
580 }
581
582 mTabContainer->setSize(width - mRightMargin, maxTabHeight);
583
584 mWidgetContainer->setPosition(0, maxTabHeight);
585 mWidgetContainer->setSize(width, height - maxTabHeight);
586 Widget *const w = getCurrentWidget();
587 if (w != nullptr)
588 {
589 const int wFrameSize = w->getFrameSize();
590 const int frame2 = 2 * wFrameSize;
591
592 w->setPosition(wFrameSize, wFrameSize);
593 if (mResizeHeight)
594 {
595 w->setSize(mWidgetContainer->getWidth() - frame2,
596 mWidgetContainer->getHeight() - frame2);
597 }
598 else
599 {
600 w->setSize(mWidgetContainer->getWidth() - frame2,
601 w->getHeight());
602 }
603 }
604 }
605
adjustTabPositions()606 void TabbedArea::adjustTabPositions()
607 {
608 int maxTabHeight = 0;
609 const size_t sz = mTabs.size();
610 for (size_t i = 0; i < sz; ++i)
611 {
612 const Tab *const tab = mTabs[i].first;
613 if ((tab != nullptr) &&
614 tab->mVisible == Visible_true &&
615 tab->getHeight() > maxTabHeight)
616 {
617 maxTabHeight = tab->getHeight();
618 }
619 }
620
621 unsigned int x = (mEnableScrollButtons &&
622 mArrowButton[0]->mVisible == Visible_true) ?
623 mArrowButton[0]->getWidth() : 0U;
624 for (size_t i = mTabScrollIndex; i < sz; ++i)
625 {
626 Tab *const tab = mTabs[i].first;
627 if ((tab == nullptr) || tab->mVisible == Visible_false)
628 continue;
629 tab->setPosition(x, maxTabHeight - tab->getHeight());
630 x += tab->getWidth();
631 }
632
633 // If the tabs are scrolled, we hide them away.
634 if (mTabScrollIndex > 0)
635 {
636 x = 0;
637 for (unsigned i = 0; i < mTabScrollIndex; ++i)
638 {
639 Tab *const tab = mTabs[i].first;
640 if ((tab != nullptr) && tab->mVisible == Visible_true)
641 {
642 x -= tab->getWidth();
643 tab->setPosition(x, maxTabHeight - tab->getHeight());
644 }
645 }
646 }
647 }
648
action(const ActionEvent & actionEvent)649 void TabbedArea::action(const ActionEvent& actionEvent)
650 {
651 Widget *const source = actionEvent.getSource();
652 Tab *const tab = dynamic_cast<Tab *>(source);
653
654 if (tab != nullptr)
655 {
656 setSelectedTab(tab);
657 }
658 else
659 {
660 const std::string &eventId = actionEvent.getId();
661 if (eventId == "shift_left")
662 {
663 if (mTabScrollIndex != 0U)
664 --mTabScrollIndex;
665 }
666 else if (eventId == "shift_right")
667 {
668 if (CAST_SIZE(mTabScrollIndex) < mTabs.size() - 1)
669 ++mTabScrollIndex;
670 }
671 adjustTabPositions();
672 updateArrowEnableState();
673 }
674 }
675
updateArrowEnableState()676 void TabbedArea::updateArrowEnableState()
677 {
678 updateTabsWidth();
679 if ((mArrowButton[0] == nullptr) || (mArrowButton[1] == nullptr))
680 return;
681
682 const int width = mDimension.width;
683 if (mTabsWidth > width - 4
684 - mArrowButton[0]->getWidth()
685 - mArrowButton[1]->getWidth() - mRightMargin)
686 {
687 mArrowButton[0]->setVisible(Visible_true);
688 mArrowButton[1]->setVisible(Visible_true);
689 }
690 else
691 {
692 mArrowButton[0]->setVisible(Visible_false);
693 mArrowButton[1]->setVisible(Visible_false);
694 mTabScrollIndex = 0;
695 }
696
697 // Left arrow consistency check
698 if (mTabScrollIndex == 0U)
699 mArrowButton[0]->setEnabled(false);
700 else
701 mArrowButton[0]->setEnabled(true);
702
703 // Right arrow consistency check
704 if (mVisibleTabsWidth < width - 4
705 - mArrowButton[0]->getWidth()
706 - mArrowButton[1]->getWidth() - mRightMargin)
707 {
708 mArrowButton[1]->setEnabled(false);
709 }
710 else
711 {
712 mArrowButton[1]->setEnabled(true);
713 }
714 }
715
getTabByIndex(const int index) const716 Tab *TabbedArea::getTabByIndex(const int index) const
717 {
718 if (index < 0 || index >= CAST_S32(mTabs.size()))
719 return nullptr;
720 return static_cast<Tab*>(mTabs[index].first);
721 }
722
getWidgetByIndex(const int index) const723 Widget *TabbedArea::getWidgetByIndex(const int index) const
724 {
725 if (index < 0 || index >= CAST_S32(mTabs.size()))
726 return nullptr;
727 return mTabs[index].second;
728 }
729
removeAll(const bool del)730 void TabbedArea::removeAll(const bool del)
731 {
732 if (getSelectedTabIndex() != -1)
733 {
734 setSelectedTabByIndex(CAST_U32(0));
735 }
736 while (getNumberOfTabs() > 0)
737 {
738 const int idx = getNumberOfTabs() - 1;
739 Tab *tab = mTabs[idx].first;
740 Widget *widget = mTabs[idx].second;
741 removeTab(tab);
742 if (del)
743 {
744 delete tab;
745 delete widget;
746 }
747 }
748 }
749
setWidth(int width)750 void TabbedArea::setWidth(int width)
751 {
752 // +++ need use virtual
753 Widget::setWidth(width);
754 adjustSize();
755 }
756
setHeight(int height)757 void TabbedArea::setHeight(int height)
758 {
759 // +++ need use virtual
760 Widget::setHeight(height);
761 adjustSize();
762 }
763
setSize(int width,int height)764 void TabbedArea::setSize(int width, int height)
765 {
766 // +++ need use virtual
767 Widget::setSize(width, height);
768 adjustSize();
769 }
770
setDimension(const Rect & dimension)771 void TabbedArea::setDimension(const Rect &dimension)
772 {
773 // +++ need use virtual
774 Widget::setDimension(dimension);
775 adjustSize();
776 }
777
keyPressed(KeyEvent & event)778 void TabbedArea::keyPressed(KeyEvent& event)
779 {
780 if (mBlockSwitching || event.isConsumed() || !isFocused())
781 return;
782
783 const InputActionT actionId = event.getActionId();
784
785 if (actionId == InputAction::GUI_LEFT)
786 {
787 int index = getSelectedTabIndex();
788 index--;
789
790 if (index < 0)
791 return;
792
793 setSelectedTab(mTabs[index].first);
794 event.consume();
795 }
796 else if (actionId == InputAction::GUI_RIGHT)
797 {
798 int index = getSelectedTabIndex();
799 index++;
800
801 if (index >= CAST_S32(mTabs.size()))
802 return;
803
804 setSelectedTab(mTabs[index].first);
805 event.consume();
806 }
807 }
808
death(const Event & event)809 void TabbedArea::death(const Event &event)
810 {
811 Tab *const tab = dynamic_cast<Tab*>(event.getSource());
812
813 if (tab != nullptr)
814 removeTab(tab);
815 else
816 BasicContainer::death(event);
817 }
818
selectNextTab()819 void TabbedArea::selectNextTab()
820 {
821 int tab = getSelectedTabIndex();
822 tab++;
823 if (tab == CAST_S32(mTabs.size()))
824 tab = 0;
825 setSelectedTab(mTabs[tab].first);
826 }
827
selectPrevTab()828 void TabbedArea::selectPrevTab()
829 {
830 int tab = getSelectedTabIndex();
831
832 if (tab == 0)
833 tab = CAST_S32(mTabs.size());
834 if (tab < 0)
835 return;
836 tab--;
837 setSelectedTab(mTabs[tab].first);
838 }
839