From 086a7d9bc5fdb8bbff60348257d916df107e31ea Mon Sep 17 00:00:00 2001 From: uramer Date: Sat, 29 Jan 2022 23:06:43 +0100 Subject: [PATCH] Wrap Lua settings widgets into an Adapter widget --- apps/openmw/mwgui/settingswindow.cpp | 28 ++++------ apps/openmw/mwgui/settingswindow.hpp | 7 ++- components/CMakeLists.txt | 2 +- components/lua_ui/adapter.cpp | 65 +++++++++++++++++++++++ components/lua_ui/adapter.hpp | 29 ++++++++++ components/lua_ui/element.cpp | 38 ++++++++----- components/lua_ui/element.hpp | 5 +- components/lua_ui/scriptsettings.cpp | 11 ++-- components/lua_ui/scriptsettings.hpp | 6 +-- components/lua_ui/util.cpp | 2 + components/lua_ui/widget.cpp | 9 ++++ components/lua_ui/widget.hpp | 9 ++++ files/mygui/openmw_settings_window.layout | 1 + 13 files changed, 168 insertions(+), 44 deletions(-) create mode 100644 components/lua_ui/adapter.cpp create mode 100644 components/lua_ui/adapter.hpp diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 2abea56064..f556bbdd5b 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -242,9 +242,9 @@ namespace MWGui getWidget(mScriptList, "ScriptList"); getWidget(mScriptBox, "ScriptBox"); getWidget(mScriptView, "ScriptView"); + getWidget(mScriptAdapter, "ScriptAdapter"); getWidget(mScriptDisabled, "ScriptDisabled"); getWidget(mScriptDescription, "ScriptDescription"); - mScriptChild = nullptr; #ifndef WIN32 // hide gamma controls since it currently does not work under Linux @@ -732,8 +732,7 @@ namespace MWGui void SettingsWindow::renderScriptSettings() { - if (mCurrentPage >= 0) - LuaUi::attachToWidget(mCurrentPage); + mScriptAdapter->detach(); mCurrentPage = -1; mScriptList->removeAllItems(); mScriptView->setCanvasSize({0, 0}); @@ -766,18 +765,14 @@ namespace MWGui void SettingsWindow::onScriptListSelection(MyGUI::ListBox*, size_t index) { - if (mCurrentPage >= 0) - LuaUi::attachToWidget(mCurrentPage); + mScriptAdapter->detach(); mCurrentPage = -1; if (index < mScriptList->getItemCount()) { mCurrentPage = *mScriptList->getItemDataAt(index); - LuaUi::attachToWidget(mCurrentPage, mScriptView); + LuaUi::attachPageAt(mCurrentPage, mScriptAdapter); } - mScriptChild = mScriptView->getChildCount() > 0 ? mScriptView->getChildAt(0) : nullptr; - MyGUI::IntSize canvasSize = mScriptChild ? mScriptChild->getSize() : MyGUI::IntSize(); - if (mScriptChild) - mScriptChild->setVisible(mScriptView->getVisible()); + MyGUI::IntSize canvasSize = mScriptAdapter->getSize(); mScriptView->setCanvasSize(canvasSize); } @@ -787,14 +782,13 @@ namespace MWGui { mScriptDescription->setVisible(false); mScriptView->setVisible(true); - if (mScriptChild) - mScriptChild->setVisible(true); - return; } - size_t page = *mScriptList->getItemDataAt(index); - mScriptDescription->setCaption(LuaUi::scriptSettingsPageAt(page).mDescription); - mScriptDescription->setVisible(true); - mScriptView->setVisible(false); + else { + size_t page = *mScriptList->getItemDataAt(index); + mScriptDescription->setCaption(LuaUi::scriptSettingsPageAt(page).mDescription); + mScriptDescription->setVisible(true); + mScriptView->setVisible(false); + } } void SettingsWindow::onRebindAction(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 22308f02b6..217dcf3051 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -1,6 +1,8 @@ #ifndef MWGUI_SETTINGS_H #define MWGUI_SETTINGS_H +#include + #include "windowbase.hpp" namespace MWGui @@ -48,14 +50,11 @@ namespace MWGui MyGUI::ListBox* mScriptList; MyGUI::Widget* mScriptBox; MyGUI::ScrollView* mScriptView; + LuaUi::LuaAdapter* mScriptAdapter; MyGUI::EditBox* mScriptDisabled; MyGUI::EditBox* mScriptDescription; int mCurrentPage; - // only necessary to work around a MyGUI bug - // adding a child to an invisible widget doesn't make the child invisible - MyGUI::Widget* mScriptChild; - void onTabChanged(MyGUI::TabControl* _sender, size_t index); void onOkButtonClicked(MyGUI::Widget* _sender); void onTextureFilteringChanged(MyGUI::ComboBox* _sender, size_t pos); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 3526386e69..58e6d52ead 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -166,7 +166,7 @@ add_component_dir (queries add_component_dir (lua_ui properties widget element util layers content scriptsettings - text textedit window image + adapter text textedit window image ) diff --git a/components/lua_ui/adapter.cpp b/components/lua_ui/adapter.cpp new file mode 100644 index 0000000000..9a109d5d1f --- /dev/null +++ b/components/lua_ui/adapter.cpp @@ -0,0 +1,65 @@ +#include "adapter.hpp" + +#include + +#include "element.hpp" + +namespace LuaUi +{ + namespace + { + sol::state luaState; + } + + LuaAdapter::LuaAdapter() + : mElement(nullptr) + , mContent(nullptr) + { + MyGUI::Widget* widget = MyGUI::Gui::getInstancePtr()->createWidgetT( + "LuaWidget", "", + MyGUI::IntCoord(), MyGUI::Align::Default, + std::string(), ""); + + mContent = dynamic_cast(widget); + if (!mContent) + throw std::runtime_error("Invalid widget!"); + mContent->initialize(luaState, widget); + mContent->onSizeChange([this](MyGUI::IntSize size) + { + setSize(size); + }); + mContent->widget()->attachToWidget(this); + } + + void LuaAdapter::attach(const std::shared_ptr& element) + { + detachElement(); + mElement = element; + attachElement(); + setSize(mContent->widget()->getSize()); + + // workaround for MyGUI bug + // parent visibility doesn't affect added children + setVisible(!getVisible()); + setVisible(!getVisible()); + } + + void LuaAdapter::detach() + { + detachElement(); + setSize({ 0, 0 }); + } + + void LuaAdapter::attachElement() + { + if (mElement.get()) + mElement->attachToWidget(mContent); + } + + void LuaAdapter::detachElement() + { + if (mElement.get()) + mElement->detachFromWidget(); + mElement = nullptr; + } +} diff --git a/components/lua_ui/adapter.hpp b/components/lua_ui/adapter.hpp new file mode 100644 index 0000000000..2ff206464d --- /dev/null +++ b/components/lua_ui/adapter.hpp @@ -0,0 +1,29 @@ +#ifndef OPENMW_LUAUI_ADAPTER +#define OPENMW_LUAUI_ADAPTER + +#include + +namespace LuaUi +{ + class WidgetExtension; + struct Element; + class LuaAdapter : public MyGUI::Widget + { + MYGUI_RTTI_DERIVED(LuaAdapter) + + public: + LuaAdapter(); + + void attach(const std::shared_ptr& element); + void detach(); + bool empty() { return mElement.get() == nullptr; } + + private: + WidgetExtension* mContent; + std::shared_ptr mElement; + void attachElement(); + void detachElement(); + }; +} + +#endif // !OPENMW_LUAUI_ADAPTER diff --git a/components/lua_ui/element.cpp b/components/lua_ui/element.cpp index 5baddb98d8..9baec82269 100644 --- a/components/lua_ui/element.cpp +++ b/components/lua_ui/element.cpp @@ -4,6 +4,7 @@ #include "content.hpp" #include "util.hpp" +#include "widget.hpp" namespace LuaUi { @@ -209,14 +210,26 @@ namespace LuaUi sAllElements.erase(this); } - void Element::attachToWidget(MyGUI::Widget* w) + void Element::attachToWidget(WidgetExtension* w) { - if (mAttachedTo && w) + if (mAttachedTo) throw std::logic_error("A UI element can't be attached to two widgets at once"); mAttachedTo = w; updateAttachment(); } + void Element::detachFromWidget() + { + if (mRoot) + { + mRoot->onSizeChange({}); + mRoot->widget()->detachFromWidget(); + } + if (mAttachedTo) + mAttachedTo->setChildren({}); + mAttachedTo = nullptr; + } + void Element::updateAttachment() { if (!mRoot) @@ -225,18 +238,17 @@ namespace LuaUi { if (!mLayer.empty()) Log(Debug::Warning) << "Ignoring element's layer " << mLayer << " because it's attached to a widget"; - if (mRoot->widget()->getParent() != mAttachedTo) + mAttachedTo->setChildren({ mRoot }); + auto callback = [this](MyGUI::IntSize size) { - mRoot->widget()->attachToWidget(mAttachedTo); - mRoot->updateCoord(); - } - } - else - { - if (mRoot->widget()->getParent() != nullptr) - { - mRoot->widget()->detachFromWidget(); - } + if (!mAttachedTo) + return; + mAttachedTo->setForcedSize(mRoot->widget()->getSize()); + mAttachedTo->updateCoord(); + }; + mRoot->onSizeChange(callback); + mRoot->updateCoord(); + callback(mRoot->widget()->getSize()); } } } diff --git a/components/lua_ui/element.hpp b/components/lua_ui/element.hpp index 95d0aa2ebc..13f4ea052d 100644 --- a/components/lua_ui/element.hpp +++ b/components/lua_ui/element.hpp @@ -10,7 +10,7 @@ namespace LuaUi static std::shared_ptr make(sol::table layout); WidgetExtension* mRoot; - MyGUI::Widget* mAttachedTo; + WidgetExtension* mAttachedTo; sol::table mLayout; std::string mLayer; bool mUpdate; @@ -24,7 +24,8 @@ namespace LuaUi friend void clearUserInterface(); - void attachToWidget(MyGUI::Widget* w = nullptr); + void attachToWidget(WidgetExtension* w); + void detachFromWidget(); private: Element(sol::table layout); diff --git a/components/lua_ui/scriptsettings.cpp b/components/lua_ui/scriptsettings.cpp index 2695b9f25d..bf7788597a 100644 --- a/components/lua_ui/scriptsettings.cpp +++ b/components/lua_ui/scriptsettings.cpp @@ -1,8 +1,10 @@ #include "scriptsettings.hpp" #include +#include #include "element.hpp" +#include "adapter.hpp" namespace LuaUi { @@ -19,7 +21,7 @@ namespace LuaUi if (!element.get()) Log(Debug::Warning) << "A script settings page has no UI element assigned"; return { - name, description, element.get() + name, description, element }; } } @@ -44,13 +46,14 @@ namespace LuaUi allPages.clear(); } - void attachToWidget(size_t index, MyGUI::Widget* widget) + void attachPageAt(size_t index, LuaAdapter* adapter) { if (index < allPages.size()) { ScriptSettingsPage page = parse(allPages[index]); - if (page.mElement) - page.mElement->attachToWidget(widget); + adapter->detach(); + if (page.mElement.get()) + adapter->attach(page.mElement); } } } diff --git a/components/lua_ui/scriptsettings.hpp b/components/lua_ui/scriptsettings.hpp index 3b51aff901..050722c249 100644 --- a/components/lua_ui/scriptsettings.hpp +++ b/components/lua_ui/scriptsettings.hpp @@ -1,7 +1,6 @@ #ifndef OPENMW_LUAUI_SCRIPTSETTINGS #define OPENMW_LUAUI_SCRIPTSETTINGS -#include #include #include @@ -9,18 +8,19 @@ namespace LuaUi { + class LuaAdapter; struct Element; struct ScriptSettingsPage { std::string mName; std::string mDescription; - Element* mElement; // TODO: figure out if this can lead to use after free + std::shared_ptr mElement; }; size_t scriptSettingsPageCount(); ScriptSettingsPage scriptSettingsPageAt(size_t index); void registerSettingsPage(const sol::table& options); void clearSettings(); - void attachToWidget(size_t index, MyGUI::Widget* widget = nullptr); + void attachPageAt(size_t index, LuaAdapter* adapter); } #endif // !OPENMW_LUAUI_SCRIPTSETTINGS diff --git a/components/lua_ui/util.cpp b/components/lua_ui/util.cpp index 3987765617..24a1442a53 100644 --- a/components/lua_ui/util.cpp +++ b/components/lua_ui/util.cpp @@ -2,6 +2,7 @@ #include +#include "adapter.hpp" #include "widget.hpp" #include "text.hpp" #include "textedit.hpp" @@ -16,6 +17,7 @@ namespace LuaUi void registerAllWidgets() { + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); diff --git a/components/lua_ui/widget.cpp b/components/lua_ui/widget.cpp index 027adc44ca..4224598ca1 100644 --- a/components/lua_ui/widget.cpp +++ b/components/lua_ui/widget.cpp @@ -194,6 +194,11 @@ namespace LuaUi mForcedCoord = offset; } + void WidgetExtension::setForcedSize(const MyGUI::IntSize& size) + { + mForcedCoord = size; + } + void WidgetExtension::updateCoord() { MyGUI::IntCoord oldCoord = mWidget->getCoord(); @@ -202,7 +207,11 @@ namespace LuaUi if (oldCoord != newCoord) mWidget->setCoord(newCoord); if (oldCoord.size() != newCoord.size()) + { updateChildrenCoord(); + if (mOnSizeChange.has_value()) + mOnSizeChange.value()(newCoord.size()); + } } void WidgetExtension::setProperties(sol::object props) diff --git a/components/lua_ui/widget.hpp b/components/lua_ui/widget.hpp index 4085963137..1eff2a113b 100644 --- a/components/lua_ui/widget.hpp +++ b/components/lua_ui/widget.hpp @@ -2,6 +2,7 @@ #define OPENMW_LUAUI_WIDGET #include +#include #include #include @@ -44,6 +45,7 @@ namespace LuaUi MyGUI::IntCoord forcedCoord(); void setForcedCoord(const MyGUI::IntCoord& offset); + void setForcedSize(const MyGUI::IntSize& size); void updateCoord(); const sol::table& getLayout() { return mLayout; } @@ -55,6 +57,11 @@ namespace LuaUi return parseExternal(mExternal, name, defaultValue); } + void onSizeChange(const std::optional>& callback) + { + mOnSizeChange = callback; + } + protected: virtual void initialize(); sol::table makeTable() const; @@ -119,6 +126,8 @@ namespace LuaUi void mouseRelease(MyGUI::Widget*, int, int, MyGUI::MouseButton); void focusGain(MyGUI::Widget*, MyGUI::Widget*); void focusLoss(MyGUI::Widget*, MyGUI::Widget*); + + std::optional> mOnSizeChange; }; class LuaWidget : public MyGUI::Widget, public WidgetExtension diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index da64735c79..d6ac4f43fa 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -664,6 +664,7 @@ +