GUI: tabs with scrollbars

This commit is contained in:
Die4Ever 2021-12-07 07:29:53 -06:00 committed by Filippos Karapetis
parent 552ea788ce
commit 30a8d927a1
15 changed files with 53 additions and 35 deletions

View file

@ -377,7 +377,7 @@ void ConfigDialog::apply() {
}
ExtraGuiOptionsWidget::ExtraGuiOptionsWidget(GuiObject *containerBoss, const Common::String &name, const Common::String &domain, const ExtraGuiOptions &options) :
OptionsContainerWidget(containerBoss, name, dialogLayout(domain), true, domain),
OptionsContainerWidget(containerBoss, name, dialogLayout(domain), false, domain),
_options(options) {
for (uint i = 0; i < _options.size(); i++) {

View file

@ -183,25 +183,21 @@ EditGameDialog::EditGameDialog(const Common::String &domain)
}
//
// 2) The engine tab (shown only if the engine implements one or there are custom engine options)
// 2) The engine's game settings (shown only if the engine implements one or there are custom engine options)
//
if (metaEnginePlugin) {
int tabId = tab->addTab(_("Engine"), "GameOptions_Engine");
const MetaEngineDetection &metaEngineDetection = metaEnginePlugin->get<MetaEngineDetection>();
metaEngineDetection.registerDefaultSettings(_domain);
if (enginePlugin) {
enginePlugin->get<MetaEngine>().registerDefaultSettings(_domain);
_engineOptions = enginePlugin->get<MetaEngine>().buildEngineOptionsWidgetDynamic(tab, "GameOptions_Engine.Container", _domain);
_engineOptions = enginePlugin->get<MetaEngine>().buildEngineOptionsWidgetDynamic(tab, "GameOptions_Game.Container", _domain);
}
if (!_engineOptions)
_engineOptions = metaEngineDetection.buildEngineOptionsWidgetStatic(tab, "GameOptions_Engine.Container", _domain);
_engineOptions = metaEngineDetection.buildEngineOptionsWidgetStatic(tab, "GameOptions_Game.Container", _domain);
if (_engineOptions) {
_engineOptions->setParentDialog(this);
} else {
tab->removeTab(tabId);
}
}

View file

@ -58,6 +58,12 @@ void GuiObject::resize(int x, int y, int w, int h, bool scale) {
}
}
Widget *GuiObject::addChild(Widget *newChild) {
Widget *oldFirstWidget = _firstWidget;
_firstWidget = newChild;
return oldFirstWidget;
}
void GuiObject::reflowLayout() {
if (!_name.empty()) {
int16 w, h;

View file

@ -77,6 +77,7 @@ public:
virtual void setTextDrawableArea(const Common::Rect &r) { _textDrawableArea = r; }
virtual void resize(int x, int y, int w, int h, bool scale = true);
virtual Widget *addChild(Widget *newChild);
virtual int16 getRelX() const { return _x; }
virtual int16 getRelY() const { return _y; }

View file

@ -1328,6 +1328,7 @@
type = 'PopUp'
/>
</layout>
<widget name = 'Container'/>
</layout>
</dialog>
@ -1368,12 +1369,6 @@
</layout>
</dialog>
<dialog name = 'GameOptions_Engine' overlays = 'Dialog.GameOptions.TabWidget'>
<layout type = 'vertical' padding = '0, 0, 0, 0'>
<widget name = 'Container'/>
</layout>
</dialog>
<dialog name = 'GlobalMenu' overlays = 'screen_center'>
<layout type = 'vertical' padding = '16, 16, 16, 16' align = 'center'>
<widget name = 'Logo'

View file

@ -1307,6 +1307,7 @@
type = 'PopUp'
/>
</layout>
<widget name = 'Container'/>
</layout>
</dialog>
@ -1347,12 +1348,6 @@
</layout>
</dialog>
<dialog name = 'GameOptions_Engine' overlays = 'Dialog.GameOptions.TabWidget'>
<layout type = 'vertical' padding = '0, 0, 0, 0'>
<widget name = 'Container'/>
</layout>
</dialog>
<dialog name = 'GlobalMenu' overlays = 'screen_center'>
<layout type = 'vertical' padding = '4, 4, 4, 4' align = 'center' spacing='2'>
<widget name = 'Title'

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -50,9 +50,7 @@ Widget::Widget(GuiObject *boss, const Common::String &name, const Common::U32Str
}
void Widget::init() {
// Insert into the widget list of the boss
_next = _boss->_firstWidget;
_boss->_firstWidget = this;
_next = _boss->addChild(this);
_needsRedraw = true;
}
@ -1045,10 +1043,15 @@ void OptionsContainerWidget::reflowLayout() {
}
Widget *w = _firstWidget;
int16 minY = getAbsY();
int maxY = minY;
while (w) {
w->reflowLayout();
minY = MIN(minY, w->getAbsY());
maxY = MAX(maxY, w->getAbsY() + w->getHeight());
w = w->next();
}
_h = maxY - minY;
}
bool OptionsContainerWidget::containsWidget(Widget *widget) const {

View file

@ -121,6 +121,7 @@ public:
void init();
void setNext(Widget *w) { _next = w; }
void setBoss(GuiObject *newBoss) { _boss = newBoss; }
Widget *next() { return _next; }
int16 getAbsX() const override { return _x + _boss->getChildX(); }

View file

@ -59,7 +59,6 @@ void ScrollContainerWidget::recalc() {
//calculate virtual height
const int spacing = g_gui.xmlEval()->getVar("Global.Font.Height", 16); //on the bottom
int h = 0;
int min = spacing, max = 0;
Widget *ptr = _firstWidget;
while (ptr) {
@ -70,7 +69,7 @@ void ScrollContainerWidget::recalc() {
}
ptr = ptr->next();
}
h = max - min;
int h = max - min;
if (h <= _limitH) _scrolledY = 0;
if (_scrolledY > h - _limitH) _scrolledY = 0;

View file

@ -22,6 +22,7 @@
#include "common/util.h"
#include "gui/widgets/tab.h"
#include "gui/gui-manager.h"
#include "gui/widgets/scrollcontainer.h"
#include "gui/ThemeEval.h"
@ -90,12 +91,10 @@ TabWidget::~TabWidget() {
// having been switched using setActiveTab() afterward, then the
// firstWidget in the _tabs list for the active tab may not be up to
// date. So update it now.
if (_activeTab != -1)
_tabs[_activeTab].firstWidget = _firstWidget;
_firstWidget = nullptr;
for (uint i = 0; i < _tabs.size(); ++i) {
delete _tabs[i].firstWidget;
_tabs[i].firstWidget = nullptr;
delete _tabs[i].scrollWidget;
_tabs[i].scrollWidget = nullptr;
}
_tabs.clear();
delete _navRight;
@ -121,6 +120,7 @@ int TabWidget::addTab(const Common::U32String &title, const Common::String &dial
newTab.title = title;
newTab.dialogName = dialogName;
newTab.firstWidget = nullptr;
newTab.scrollWidget = nullptr;
// Determine the new tab width
int newWidth = g_gui.getStringWidth(title) + _titleSpacing;
@ -132,24 +132,34 @@ int TabWidget::addTab(const Common::U32String &title, const Common::String &dial
int numTabs = _tabs.size();
// Activate the new tab
// Activate the new tab, also writes back our _firstWidget
setActiveTab(numTabs - 1);
_tabs.back().scrollWidget = new ScrollContainerWidget(this, "", dialogName);
return _activeTab;
}
Widget *TabWidget::addChild(Widget *newChild) {
if (_activeTab == -1 || _tabs[_activeTab].scrollWidget == nullptr)
return Widget::addChild(newChild);
newChild->setBoss(_tabs[_activeTab].scrollWidget);
_firstWidget = newChild;
return _tabs[_activeTab].scrollWidget->addChild(newChild);
}
void TabWidget::removeTab(int tabID) {
assert(0 <= tabID && tabID < (int)_tabs.size());
// Deactivate the tab if it's currently the active one
if (tabID == _activeTab) {
_tabs[tabID].firstWidget = _firstWidget;
releaseFocus();
_firstWidget = nullptr;
}
// Dispose the widgets in that tab and then the tab itself
delete _tabs[tabID].firstWidget;
delete _tabs[tabID].scrollWidget;
_tabs.remove_at(tabID);
// Adjust _firstVisibleTab if necessary
@ -333,11 +343,13 @@ void TabWidget::reflowLayout() {
_tabs[_activeTab].firstWidget = _firstWidget;
for (uint i = 0; i < _tabs.size(); ++i) {
_tabs[i].scrollWidget->setPos(_x, _y);
_tabs[i].scrollWidget->setSize(_w, _h);
if (!_tabs[i].dialogName.empty()) {
g_gui.xmlEval()->reflowDialogLayout(_tabs[i].dialogName, _tabs[i].firstWidget);
g_gui.xmlEval()->reflowDialogLayout(_tabs[i].dialogName, _tabs[i].scrollWidget);
}
Widget *w = _tabs[i].firstWidget;
Widget *w = _tabs[i].scrollWidget;
while (w) {
w->reflowLayout();
w = w->next();
@ -396,7 +408,14 @@ void TabWidget::drawWidget() {
}
void TabWidget::draw() {
Widget::draw();
if (_activeTab == -1) {
Widget::draw();
} else {
_tabs[_activeTab].firstWidget = _firstWidget;
_firstWidget = _tabs[_activeTab].scrollWidget;
Widget::draw();
_firstWidget = _tabs[_activeTab].firstWidget;
}
if (_navButtonsVisible) {
_navLeft->draw();

View file

@ -38,6 +38,7 @@ class TabWidget : public Widget {
Common::U32String title;
Common::String dialogName;
Widget *firstWidget;
ScrollContainerWidget *scrollWidget;
int _tabWidth;
};
typedef Common::Array<Tab> TabList;
@ -77,6 +78,8 @@ public:
*/
int addTab(const Common::U32String &title, const Common::String &dialogName);
virtual Widget *addChild(Widget *newChild);
/**
* Remove the tab with the given tab ID. Disposes all child widgets of that tab.
* TODO: This code is *unfinished*. In particular, it changes the