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