From fba82eb1a7fca59420f69577224116d093ae4567 Mon Sep 17 00:00:00 2001 From: uramer Date: Tue, 25 Jan 2022 21:53:00 +0100 Subject: [PATCH 01/17] Script settings tab --- apps/openmw/mwgui/settingswindow.cpp | 85 +++++++++++++++++++++-- apps/openmw/mwgui/settingswindow.hpp | 10 +++ apps/openmw/mwlua/uibindings.cpp | 17 ++++- components/CMakeLists.txt | 2 +- components/lua_ui/scriptsettings.cpp | 37 ++++++++++ components/lua_ui/scriptsettings.hpp | 25 +++++++ components/lua_ui/util.cpp | 2 + files/mygui/openmw_settings_window.layout | 13 ++++ 8 files changed, 183 insertions(+), 8 deletions(-) create mode 100644 components/lua_ui/scriptsettings.cpp create mode 100644 components/lua_ui/scriptsettings.hpp diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 69d09f7260..8c3740d15d 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -1,18 +1,20 @@ #include "settingswindow.hpp" +#include +#include +#include +#include + #include #include #include #include #include #include +#include #include -#include -#include -#include - #include #include #include @@ -21,6 +23,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -33,7 +36,6 @@ namespace { - std::string textureMipmappingToStr(const std::string& val) { if (val == "linear") return "Trilinear"; @@ -236,6 +238,10 @@ namespace MWGui getWidget(mLightingMethodButton, "LightingMethodButton"); getWidget(mLightsResetButton, "LightsResetButton"); getWidget(mMaxLights, "MaxLights"); + getWidget(mScriptFilter, "ScriptFilter"); + getWidget(mScriptList, "ScriptList"); + getWidget(mScriptView, "ScriptView"); + getWidget(mScriptDisabled, "ScriptDisabled"); #ifndef WIN32 // hide gamma controls since it currently does not work under Linux @@ -321,9 +327,12 @@ namespace MWGui mKeyboardSwitch->setStateSelected(true); mControllerSwitch->setStateSelected(false); + + mScriptFilter->eventEditTextChange += MyGUI::newDelegate(this, &SettingsWindow::onScriptFilterChange); + mScriptList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SettingsWindow::onScriptListSelection); } - void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t /*index*/) + void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t index) { resetScrollbars(); } @@ -699,6 +708,67 @@ namespace MWGui mControlsBox->setVisibleVScroll(true); } + void SettingsWindow::resizeScriptSettings() + { + static int minListWidth = 150; + static float relativeListWidth = 0.2f; + MyGUI::IntSize parentSize = mScriptFilter->getParent()->getClientCoord().size(); + int listWidth = std::max(minListWidth, static_cast(parentSize.width * relativeListWidth)); + int filterHeight = mScriptFilter->getSize().height; + int listBorder = (mScriptList->getSize().height - mScriptList->getClientCoord().height) / 2; + int listHeight = parentSize.height - listBorder - mScriptList->getPosition().top; + mScriptFilter->setSize({ listWidth, filterHeight }); + mScriptList->setSize({ listWidth, listHeight }); + mScriptView->setPosition({ listWidth, 0 }); + mScriptView->setSize({ parentSize.width - listWidth, parentSize.height }); + mScriptDisabled->setPosition({0, 0}); + mScriptDisabled->setSize(parentSize); + } + + void SettingsWindow::renderScriptSettings() + { + while (mScriptView->getChildCount() > 0) + mScriptView->getChildAt(0)->detachFromWidget(); + mScriptList->removeAllItems(); + + std::string filter(".*"); + filter += mScriptFilter->getCaption(); + filter += ".*"; + auto flags = std::regex_constants::icase; + std::regex filterRegex(filter, flags); + + auto scriptSettings = LuaUi::scriptSettings(); + for (size_t i = 0; i < scriptSettings.size(); ++i) + { + LuaUi::ScriptSettings script = scriptSettings[i]; + if (std::regex_match(script.mName, filterRegex) || std::regex_match(script.mSearchHints, filterRegex)) + mScriptList->addItem(script.mName, i); + } + + // Hide script settings tab when the game world isn't loaded and scripts couldn't add their settings + bool disabled = scriptSettings.empty(); + mScriptDisabled->setVisible(disabled); + mScriptFilter->setVisible(!disabled); + mScriptList->setVisible(!disabled); + mScriptView->setVisible(!disabled); + } + + void SettingsWindow::onScriptFilterChange(MyGUI::Widget*) + { + renderScriptSettings(); + } + + void SettingsWindow::onScriptListSelection(MyGUI::Widget*, size_t index) + { + while (mScriptView->getChildCount() > 0) + mScriptView->getChildAt(0)->detachFromWidget(); + if (index >= mScriptList->getItemCount()) + return; + size_t scriptIndex = *mScriptList->getItemDataAt(index); + LuaUi::ScriptSettings script = LuaUi::scriptSettings()[scriptIndex]; + LuaUi::attachToWidget(script, mScriptView); + } + void SettingsWindow::onRebindAction(MyGUI::Widget* _sender) { int actionId = *_sender->getUserData(); @@ -744,12 +814,15 @@ namespace MWGui updateControlsBox(); updateLightSettings(); resetScrollbars(); + renderScriptSettings(); + resizeScriptSettings(); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton); } void SettingsWindow::onWindowResize(MyGUI::Window *_sender) { layoutControlsBox(); + resizeScriptSettings(); } void SettingsWindow::computeMinimumWindowSize() diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index d58e94e84a..f002a67306 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -44,6 +44,11 @@ namespace MWGui MyGUI::Button* mControllerSwitch; bool mKeyboardMode; //if true, setting up the keyboard. Otherwise, it's controller + MyGUI::EditBox* mScriptFilter; + MyGUI::ListBox* mScriptList; + MyGUI::Widget* mScriptView; + MyGUI::EditBox* mScriptDisabled; + void onTabChanged(MyGUI::TabControl* _sender, size_t index); void onOkButtonClicked(MyGUI::Widget* _sender); void onTextureFilteringChanged(MyGUI::ComboBox* _sender, size_t pos); @@ -71,12 +76,17 @@ namespace MWGui void onWindowResize(MyGUI::Window* _sender); + void onScriptFilterChange(MyGUI::Widget*); + void onScriptListSelection(MyGUI::Widget*, size_t index); + void apply(); void configureWidgets(MyGUI::Widget* widget, bool init); void updateSliderLabel(MyGUI::ScrollBar* scroller, const std::string& value); void layoutControlsBox(); + void resizeScriptSettings(); + void renderScriptSettings(); void computeMinimumWindowSize(); diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index 61e7c471c1..5fe54a4d35 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "context.hpp" #include "actions.hpp" @@ -43,7 +44,7 @@ namespace MWLua break; } } - catch (std::exception& e) + catch (std::exception&) { // prevent any actions on a potentially corrupted widget mElement->mRoot = nullptr; @@ -238,6 +239,20 @@ namespace MWLua typeTable.set(it.second, it.first); api["TYPE"] = LuaUtil::makeReadOnly(typeTable); + api["registerSettings"] = [](sol::table options) + { + LuaUi::ScriptSettings script; + script.mName = options.get_or("name", std::string()); + if (script.mName.empty()) + throw std::logic_error("No name provided for script settings"); + script.mSearchHints = options.get_or("searchHints", std::string()); + auto element = options.get_or>("element", nullptr); + if (!element) + throw std::logic_error("No UI element provided for script settings"); + script.mElement = element.get(); + LuaUi::registerSettings(script); + }; + return LuaUtil::makeReadOnly(api); } } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 2d986660bc..3526386e69 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -165,7 +165,7 @@ add_component_dir (queries ) add_component_dir (lua_ui - properties widget element util layers content + properties widget element util layers content scriptsettings text textedit window image ) diff --git a/components/lua_ui/scriptsettings.cpp b/components/lua_ui/scriptsettings.cpp new file mode 100644 index 0000000000..071ded1b5d --- /dev/null +++ b/components/lua_ui/scriptsettings.cpp @@ -0,0 +1,37 @@ +#include "scriptsettings.hpp" + +#include + +#include "element.hpp" + +namespace LuaUi +{ + namespace + { + std::vector allSettings; + } + + const std::vector& scriptSettings() + { + return allSettings; + } + + void registerSettings(const ScriptSettings& script) + { + allSettings.push_back(script); + } + + void clearSettings() + { + allSettings.clear(); + } + + void attachToWidget(const ScriptSettings& script, MyGUI::Widget* widget) + { + WidgetExtension* root = script.mElement->mRoot; + if (!root) + return; + root->widget()->attachToWidget(widget); + root->updateCoord(); + } +} diff --git a/components/lua_ui/scriptsettings.hpp b/components/lua_ui/scriptsettings.hpp new file mode 100644 index 0000000000..eadcec430f --- /dev/null +++ b/components/lua_ui/scriptsettings.hpp @@ -0,0 +1,25 @@ +#ifndef OPENMW_LUAUI_SCRIPTSETTINGS +#define OPENMW_LUAUI_SCRIPTSETTINGS + +#include +#include +#include + +#include + +namespace LuaUi +{ + class Element; + struct ScriptSettings + { + std::string mName; + std::string mSearchHints; + Element* mElement; // TODO: figure out if this can lead to use after free + }; + const std::vector& scriptSettings(); + void registerSettings(const ScriptSettings& script); + void clearSettings(); + void attachToWidget(const ScriptSettings& script, MyGUI::Widget* widget); +} + +#endif // !OPENMW_LUAUI_SCRIPTSETTINGS diff --git a/components/lua_ui/util.cpp b/components/lua_ui/util.cpp index 1e13c97a80..3987765617 100644 --- a/components/lua_ui/util.cpp +++ b/components/lua_ui/util.cpp @@ -9,6 +9,7 @@ #include "image.hpp" #include "element.hpp" +#include "scriptsettings.hpp" namespace LuaUi { @@ -37,6 +38,7 @@ namespace LuaUi void clearUserInterface() { + clearSettings(); while (!Element::sAllElements.empty()) Element::sAllElements.begin()->second->destroy(); } diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 8e81764f60..6a276ddd22 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -656,6 +656,19 @@ --> + + + + + + + + + + + + + From 64df4f54c649c1f9a381db0c99b61d665e766afa Mon Sep 17 00:00:00 2001 From: uramer Date: Thu, 27 Jan 2022 23:12:30 +0100 Subject: [PATCH 02/17] Add scrolling to the script settings view --- apps/openmw/mwgui/settingswindow.cpp | 25 +++++++++++++++-------- apps/openmw/mwgui/settingswindow.hpp | 3 ++- files/mygui/openmw_scroll.skin.xml | 7 +++++++ files/mygui/openmw_settings_window.layout | 14 +++++++++---- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 8c3740d15d..567e603e53 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -240,6 +240,7 @@ namespace MWGui getWidget(mMaxLights, "MaxLights"); getWidget(mScriptFilter, "ScriptFilter"); getWidget(mScriptList, "ScriptList"); + getWidget(mScriptBox, "ScriptBox"); getWidget(mScriptView, "ScriptView"); getWidget(mScriptDisabled, "ScriptDisabled"); @@ -332,7 +333,7 @@ namespace MWGui mScriptList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SettingsWindow::onScriptListSelection); } - void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t index) + void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t /*index*/) { resetScrollbars(); } @@ -710,26 +711,28 @@ namespace MWGui void SettingsWindow::resizeScriptSettings() { - static int minListWidth = 150; - static float relativeListWidth = 0.2f; + static const int minListWidth = 150; + static const float relativeListWidth = 0.2f; + static const int padding = 2; + static const int outerPadding = padding * 2; MyGUI::IntSize parentSize = mScriptFilter->getParent()->getClientCoord().size(); int listWidth = std::max(minListWidth, static_cast(parentSize.width * relativeListWidth)); int filterHeight = mScriptFilter->getSize().height; - int listBorder = (mScriptList->getSize().height - mScriptList->getClientCoord().height) / 2; - int listHeight = parentSize.height - listBorder - mScriptList->getPosition().top; + int listHeight = parentSize.height - mScriptList->getPosition().top - outerPadding; mScriptFilter->setSize({ listWidth, filterHeight }); mScriptList->setSize({ listWidth, listHeight }); - mScriptView->setPosition({ listWidth, 0 }); - mScriptView->setSize({ parentSize.width - listWidth, parentSize.height }); + mScriptBox->setPosition({ listWidth + padding, 0 }); + mScriptBox->setSize({ parentSize.width - listWidth - padding, parentSize.height - outerPadding }); mScriptDisabled->setPosition({0, 0}); mScriptDisabled->setSize(parentSize); } - + void SettingsWindow::renderScriptSettings() { while (mScriptView->getChildCount() > 0) mScriptView->getChildAt(0)->detachFromWidget(); mScriptList->removeAllItems(); + mScriptView->setCanvasSize({0, 0}); std::string filter(".*"); filter += mScriptFilter->getCaption(); @@ -750,7 +753,7 @@ namespace MWGui mScriptDisabled->setVisible(disabled); mScriptFilter->setVisible(!disabled); mScriptList->setVisible(!disabled); - mScriptView->setVisible(!disabled); + mScriptBox->setVisible(!disabled); } void SettingsWindow::onScriptFilterChange(MyGUI::Widget*) @@ -767,6 +770,10 @@ namespace MWGui size_t scriptIndex = *mScriptList->getItemDataAt(index); LuaUi::ScriptSettings script = LuaUi::scriptSettings()[scriptIndex]; LuaUi::attachToWidget(script, mScriptView); + MyGUI::IntSize canvasSize; + if (mScriptView->getChildCount() > 0) + canvasSize = mScriptView->getChildAt(0)->getSize(); + mScriptView->setCanvasSize(canvasSize); } void SettingsWindow::onRebindAction(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index f002a67306..c7ba1e8ece 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -46,7 +46,8 @@ namespace MWGui MyGUI::EditBox* mScriptFilter; MyGUI::ListBox* mScriptList; - MyGUI::Widget* mScriptView; + MyGUI::Widget* mScriptBox; + MyGUI::ScrollView* mScriptView; MyGUI::EditBox* mScriptDisabled; void onTabChanged(MyGUI::TabControl* _sender, size_t index); diff --git a/files/mygui/openmw_scroll.skin.xml b/files/mygui/openmw_scroll.skin.xml index f946a61ac9..a2c2cd9150 100644 --- a/files/mygui/openmw_scroll.skin.xml +++ b/files/mygui/openmw_scroll.skin.xml @@ -14,4 +14,11 @@ + + + + + + + diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 6a276ddd22..cf55550fbe 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -658,10 +658,16 @@ - - - - + + + + + + + + + + From a972a54ea9f2e000d6054c66eaadf599a56ad5a9 Mon Sep 17 00:00:00 2001 From: uramer Date: Fri, 28 Jan 2022 21:35:05 +0100 Subject: [PATCH 03/17] Allow changing element root widget type, prevent use after free in script settings --- apps/openmw/mwgui/settingswindow.cpp | 20 +++++----- apps/openmw/mwgui/settingswindow.hpp | 1 + components/lua_ui/element.cpp | 60 ++++++++++++++++++++++++---- components/lua_ui/element.hpp | 7 +++- components/lua_ui/scriptsettings.cpp | 9 ++--- components/lua_ui/scriptsettings.hpp | 2 +- 6 files changed, 73 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 567e603e53..3ea60b92bb 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -207,9 +207,9 @@ namespace MWGui } } - SettingsWindow::SettingsWindow() : - WindowBase("openmw_settings_window.layout"), - mKeyboardMode(true) + SettingsWindow::SettingsWindow() : WindowBase("openmw_settings_window.layout") + , mKeyboardMode(true) + , mCurrentPage(-1) { bool terrain = Settings::Manager::getBool("distant terrain", "Terrain"); const std::string widgetName = terrain ? "RenderingDistanceSlider" : "LargeRenderingDistanceSlider"; @@ -729,8 +729,8 @@ namespace MWGui void SettingsWindow::renderScriptSettings() { - while (mScriptView->getChildCount() > 0) - mScriptView->getChildAt(0)->detachFromWidget(); + LuaUi::attachToWidget(mCurrentPage); + mCurrentPage = -1; mScriptList->removeAllItems(); mScriptView->setCanvasSize({0, 0}); @@ -763,13 +763,13 @@ namespace MWGui void SettingsWindow::onScriptListSelection(MyGUI::Widget*, size_t index) { - while (mScriptView->getChildCount() > 0) - mScriptView->getChildAt(0)->detachFromWidget(); + if (mCurrentPage >= 0) + LuaUi::attachToWidget(mCurrentPage); + mCurrentPage = -1; if (index >= mScriptList->getItemCount()) return; - size_t scriptIndex = *mScriptList->getItemDataAt(index); - LuaUi::ScriptSettings script = LuaUi::scriptSettings()[scriptIndex]; - LuaUi::attachToWidget(script, mScriptView); + mCurrentPage = *mScriptList->getItemDataAt(index); + LuaUi::attachToWidget(mCurrentPage, mScriptView); MyGUI::IntSize canvasSize; if (mScriptView->getChildCount() > 0) canvasSize = mScriptView->getChildAt(0)->getSize(); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index c7ba1e8ece..2db8f2e19c 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -49,6 +49,7 @@ namespace MWGui MyGUI::Widget* mScriptBox; MyGUI::ScrollView* mScriptView; MyGUI::EditBox* mScriptDisabled; + int mCurrentPage; void onTabChanged(MyGUI::TabControl* _sender, size_t index); void onOkButtonClicked(MyGUI::Widget* _sender); diff --git a/components/lua_ui/element.cpp b/components/lua_ui/element.cpp index c9e0fc309e..5baddb98d8 100644 --- a/components/lua_ui/element.cpp +++ b/components/lua_ui/element.cpp @@ -138,7 +138,7 @@ namespace LuaUi ext->setChildren(updateContent(ext->children(), layout.get(LayoutKeys::content))); } - void setLayer(WidgetExtension* ext, const sol::table& layout) + std::string setLayer(WidgetExtension* ext, const sol::table& layout) { MyGUI::ILayer* layerNode = ext->widget()->getLayer(); std::string currentLayer = layerNode ? layerNode->getName() : std::string(); @@ -149,15 +149,18 @@ namespace LuaUi { MyGUI::LayerManager::getInstance().attachToLayerNode(newLayer, ext->widget()); } + return newLayer; } std::map> Element::sAllElements; Element::Element(sol::table layout) - : mRoot{ nullptr } - , mLayout{ std::move(layout) } - , mUpdate{ false } - , mDestroy{ false } + : mRoot(nullptr) + , mAttachedTo(nullptr) + , mLayout(std::move(layout)) + , mLayer() + , mUpdate(false) + , mDestroy(false) {} @@ -174,7 +177,8 @@ namespace LuaUi if (!mRoot) { mRoot = createWidget(mLayout); - setLayer(mRoot, mLayout); + mLayer = setLayer(mRoot, mLayout); + updateAttachment(); } } @@ -182,8 +186,17 @@ namespace LuaUi { if (mRoot && mUpdate) { - updateWidget(mRoot, mLayout); - setLayer(mRoot, mLayout); + if (mRoot->widget()->getTypeName() != widgetType(mLayout)) + { + destroyWidget(mRoot); + mRoot = createWidget(mLayout); + } + else + { + updateWidget(mRoot, mLayout); + } + mLayer = setLayer(mRoot, mLayout); + updateAttachment(); } mUpdate = false; } @@ -195,4 +208,35 @@ namespace LuaUi mRoot = nullptr; sAllElements.erase(this); } + + void Element::attachToWidget(MyGUI::Widget* w) + { + if (mAttachedTo && w) + throw std::logic_error("A UI element can't be attached to two widgets at once"); + mAttachedTo = w; + updateAttachment(); + } + + void Element::updateAttachment() + { + if (!mRoot) + return; + if (mAttachedTo) + { + if (!mLayer.empty()) + Log(Debug::Warning) << "Ignoring element's layer " << mLayer << " because it's attached to a widget"; + if (mRoot->widget()->getParent() != mAttachedTo) + { + mRoot->widget()->attachToWidget(mAttachedTo); + mRoot->updateCoord(); + } + } + else + { + if (mRoot->widget()->getParent() != nullptr) + { + mRoot->widget()->detachFromWidget(); + } + } + } } diff --git a/components/lua_ui/element.hpp b/components/lua_ui/element.hpp index daaf340660..95d0aa2ebc 100644 --- a/components/lua_ui/element.hpp +++ b/components/lua_ui/element.hpp @@ -9,8 +9,10 @@ namespace LuaUi { static std::shared_ptr make(sol::table layout); - LuaUi::WidgetExtension* mRoot; + WidgetExtension* mRoot; + MyGUI::Widget* mAttachedTo; sol::table mLayout; + std::string mLayer; bool mUpdate; bool mDestroy; @@ -22,9 +24,12 @@ namespace LuaUi friend void clearUserInterface(); + void attachToWidget(MyGUI::Widget* w = nullptr); + private: Element(sol::table layout); static std::map> sAllElements; + void updateAttachment(); }; } diff --git a/components/lua_ui/scriptsettings.cpp b/components/lua_ui/scriptsettings.cpp index 071ded1b5d..c8a7d849c9 100644 --- a/components/lua_ui/scriptsettings.cpp +++ b/components/lua_ui/scriptsettings.cpp @@ -26,12 +26,9 @@ namespace LuaUi allSettings.clear(); } - void attachToWidget(const ScriptSettings& script, MyGUI::Widget* widget) + void attachToWidget(size_t index, MyGUI::Widget* widget) { - WidgetExtension* root = script.mElement->mRoot; - if (!root) - return; - root->widget()->attachToWidget(widget); - root->updateCoord(); + if (0 <= index && index < allSettings.size()) + allSettings[index].mElement->attachToWidget(widget); } } diff --git a/components/lua_ui/scriptsettings.hpp b/components/lua_ui/scriptsettings.hpp index eadcec430f..b13bfc578c 100644 --- a/components/lua_ui/scriptsettings.hpp +++ b/components/lua_ui/scriptsettings.hpp @@ -19,7 +19,7 @@ namespace LuaUi const std::vector& scriptSettings(); void registerSettings(const ScriptSettings& script); void clearSettings(); - void attachToWidget(const ScriptSettings& script, MyGUI::Widget* widget); + void attachToWidget(size_t index, MyGUI::Widget* widget = nullptr); } #endif // !OPENMW_LUAUI_SCRIPTSETTINGS From e78b8402fac084f40ae82a86060820b8ea0c17a3 Mon Sep 17 00:00:00 2001 From: uramer Date: Fri, 28 Jan 2022 21:45:13 +0100 Subject: [PATCH 04/17] Fix warnings --- apps/openmw/mwgui/settingswindow.cpp | 7 ++++--- apps/openmw/mwgui/settingswindow.hpp | 4 ++-- components/lua_ui/scriptsettings.cpp | 2 +- components/lua_ui/scriptsettings.hpp | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 3ea60b92bb..f5d35bc13f 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -729,7 +729,8 @@ namespace MWGui void SettingsWindow::renderScriptSettings() { - LuaUi::attachToWidget(mCurrentPage); + if (mCurrentPage >= 0) + LuaUi::attachToWidget(mCurrentPage); mCurrentPage = -1; mScriptList->removeAllItems(); mScriptView->setCanvasSize({0, 0}); @@ -756,12 +757,12 @@ namespace MWGui mScriptBox->setVisible(!disabled); } - void SettingsWindow::onScriptFilterChange(MyGUI::Widget*) + void SettingsWindow::onScriptFilterChange(MyGUI::EditBox*) { renderScriptSettings(); } - void SettingsWindow::onScriptListSelection(MyGUI::Widget*, size_t index) + void SettingsWindow::onScriptListSelection(MyGUI::ListBox*, size_t index) { if (mCurrentPage >= 0) LuaUi::attachToWidget(mCurrentPage); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 2db8f2e19c..ff86c96f60 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -78,8 +78,8 @@ namespace MWGui void onWindowResize(MyGUI::Window* _sender); - void onScriptFilterChange(MyGUI::Widget*); - void onScriptListSelection(MyGUI::Widget*, size_t index); + void onScriptFilterChange(MyGUI::EditBox*); + void onScriptListSelection(MyGUI::ListBox*, size_t index); void apply(); diff --git a/components/lua_ui/scriptsettings.cpp b/components/lua_ui/scriptsettings.cpp index c8a7d849c9..1bc93f4e84 100644 --- a/components/lua_ui/scriptsettings.cpp +++ b/components/lua_ui/scriptsettings.cpp @@ -28,7 +28,7 @@ namespace LuaUi void attachToWidget(size_t index, MyGUI::Widget* widget) { - if (0 <= index && index < allSettings.size()) + if (index < allSettings.size()) allSettings[index].mElement->attachToWidget(widget); } } diff --git a/components/lua_ui/scriptsettings.hpp b/components/lua_ui/scriptsettings.hpp index b13bfc578c..7075a3b189 100644 --- a/components/lua_ui/scriptsettings.hpp +++ b/components/lua_ui/scriptsettings.hpp @@ -9,7 +9,7 @@ namespace LuaUi { - class Element; + struct Element; struct ScriptSettings { std::string mName; From a005f25c4b0d3e4beef00df534f42295b920a325 Mon Sep 17 00:00:00 2001 From: uramer Date: Sat, 29 Jan 2022 13:22:08 +0100 Subject: [PATCH 05/17] Use page terminology for script settings --- apps/openmw/mwgui/settingswindow.cpp | 8 ++++---- apps/openmw/mwlua/uibindings.cpp | 18 +++++++++--------- components/lua_ui/scriptsettings.cpp | 16 ++++++++-------- components/lua_ui/scriptsettings.hpp | 6 +++--- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index f5d35bc13f..646fe0d19b 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -741,12 +741,12 @@ namespace MWGui auto flags = std::regex_constants::icase; std::regex filterRegex(filter, flags); - auto scriptSettings = LuaUi::scriptSettings(); + auto scriptSettings = LuaUi::scriptSettingsPages(); for (size_t i = 0; i < scriptSettings.size(); ++i) { - LuaUi::ScriptSettings script = scriptSettings[i]; - if (std::regex_match(script.mName, filterRegex) || std::regex_match(script.mSearchHints, filterRegex)) - mScriptList->addItem(script.mName, i); + LuaUi::ScriptSettingsPage page = scriptSettings[i]; + if (std::regex_match(page.mName, filterRegex) || std::regex_match(page.mSearchHints, filterRegex)) + mScriptList->addItem(page.mName, i); } // Hide script settings tab when the game world isn't loaded and scripts couldn't add their settings diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index 5fe54a4d35..7737f36ec4 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -239,18 +239,18 @@ namespace MWLua typeTable.set(it.second, it.first); api["TYPE"] = LuaUtil::makeReadOnly(typeTable); - api["registerSettings"] = [](sol::table options) + api["registerSettingsPage"] = [](sol::table options) { - LuaUi::ScriptSettings script; - script.mName = options.get_or("name", std::string()); - if (script.mName.empty()) - throw std::logic_error("No name provided for script settings"); - script.mSearchHints = options.get_or("searchHints", std::string()); + LuaUi::ScriptSettingsPage page; + page.mName = options.get_or("name", std::string()); + if (page.mName.empty()) + throw std::logic_error("No name provided for the settings page"); + page.mSearchHints = options.get_or("searchHints", std::string()); auto element = options.get_or>("element", nullptr); if (!element) - throw std::logic_error("No UI element provided for script settings"); - script.mElement = element.get(); - LuaUi::registerSettings(script); + throw std::logic_error("No UI element provided for the settings page"); + page.mElement = element.get(); + LuaUi::registerSettingsPage(page); }; return LuaUtil::makeReadOnly(api); diff --git a/components/lua_ui/scriptsettings.cpp b/components/lua_ui/scriptsettings.cpp index 1bc93f4e84..1a016381cd 100644 --- a/components/lua_ui/scriptsettings.cpp +++ b/components/lua_ui/scriptsettings.cpp @@ -8,27 +8,27 @@ namespace LuaUi { namespace { - std::vector allSettings; + std::vector allPages; } - const std::vector& scriptSettings() + const std::vector& scriptSettingsPages() { - return allSettings; + return allPages; } - void registerSettings(const ScriptSettings& script) + void registerSettingsPage(const ScriptSettingsPage& page) { - allSettings.push_back(script); + allPages.push_back(page); } void clearSettings() { - allSettings.clear(); + allPages.clear(); } void attachToWidget(size_t index, MyGUI::Widget* widget) { - if (index < allSettings.size()) - allSettings[index].mElement->attachToWidget(widget); + if (index < allPages.size()) + allPages[index].mElement->attachToWidget(widget); } } diff --git a/components/lua_ui/scriptsettings.hpp b/components/lua_ui/scriptsettings.hpp index 7075a3b189..852e0df1ec 100644 --- a/components/lua_ui/scriptsettings.hpp +++ b/components/lua_ui/scriptsettings.hpp @@ -10,14 +10,14 @@ namespace LuaUi { struct Element; - struct ScriptSettings + struct ScriptSettingsPage { std::string mName; std::string mSearchHints; Element* mElement; // TODO: figure out if this can lead to use after free }; - const std::vector& scriptSettings(); - void registerSettings(const ScriptSettings& script); + const std::vector& scriptSettingsPages(); + void registerSettingsPage(const ScriptSettingsPage& page); void clearSettings(); void attachToWidget(size_t index, MyGUI::Widget* widget = nullptr); } From 5f7ab4988072a4a2a4e6f2ba34885566e1b9b406 Mon Sep 17 00:00:00 2001 From: uramer Date: Sat, 29 Jan 2022 16:59:43 +0100 Subject: [PATCH 06/17] Implement script setting pages' descriptions --- apps/openmw/mwgui/settingswindow.cpp | 37 ++++++++++++++++++----- apps/openmw/mwgui/settingswindow.hpp | 6 ++++ apps/openmw/mwlua/uibindings.cpp | 2 +- components/lua_ui/scriptsettings.hpp | 2 +- files/mygui/openmw_settings_window.layout | 8 +++++ 5 files changed, 45 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 646fe0d19b..dfd0e6d9df 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -243,6 +243,8 @@ namespace MWGui getWidget(mScriptBox, "ScriptBox"); getWidget(mScriptView, "ScriptView"); getWidget(mScriptDisabled, "ScriptDisabled"); + getWidget(mScriptDescription, "ScriptDescription"); + mScriptChild = nullptr; #ifndef WIN32 // hide gamma controls since it currently does not work under Linux @@ -331,6 +333,7 @@ namespace MWGui mScriptFilter->eventEditTextChange += MyGUI::newDelegate(this, &SettingsWindow::onScriptFilterChange); mScriptList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SettingsWindow::onScriptListSelection); + mScriptList->eventListMouseItemFocus += MyGUI::newDelegate(this, &SettingsWindow::onScriptListFocus); } void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t /*index*/) @@ -745,7 +748,7 @@ namespace MWGui for (size_t i = 0; i < scriptSettings.size(); ++i) { LuaUi::ScriptSettingsPage page = scriptSettings[i]; - if (std::regex_match(page.mName, filterRegex) || std::regex_match(page.mSearchHints, filterRegex)) + if (std::regex_match(page.mName, filterRegex) || std::regex_match(page.mDescription, filterRegex)) mScriptList->addItem(page.mName, i); } @@ -767,16 +770,34 @@ namespace MWGui if (mCurrentPage >= 0) LuaUi::attachToWidget(mCurrentPage); mCurrentPage = -1; - if (index >= mScriptList->getItemCount()) - return; - mCurrentPage = *mScriptList->getItemDataAt(index); - LuaUi::attachToWidget(mCurrentPage, mScriptView); - MyGUI::IntSize canvasSize; - if (mScriptView->getChildCount() > 0) - canvasSize = mScriptView->getChildAt(0)->getSize(); + if (index < mScriptList->getItemCount()) + { + mCurrentPage = *mScriptList->getItemDataAt(index); + LuaUi::attachToWidget(mCurrentPage, mScriptView); + } + mScriptChild = mScriptView->getChildCount() > 0 ? mScriptView->getChildAt(0) : nullptr; + MyGUI::IntSize canvasSize = mScriptChild ? mScriptChild->getSize() : MyGUI::IntSize(); + if (mScriptChild) + mScriptChild->setVisible(mScriptView->isVisible()); mScriptView->setCanvasSize(canvasSize); } + void SettingsWindow::onScriptListFocus(MyGUI::ListBox*, size_t index) + { + if (index >= mScriptList->getItemCount()) + { + mScriptDescription->setVisible(false); + mScriptView->setVisible(true); + if (mScriptChild) + mScriptChild->setVisible(true); + return; + } + size_t page = *mScriptList->getItemDataAt(index); + mScriptDescription->setCaption(LuaUi::scriptSettingsPages()[page].mDescription); + mScriptDescription->setVisible(true); + mScriptView->setVisible(false); + } + void SettingsWindow::onRebindAction(MyGUI::Widget* _sender) { int actionId = *_sender->getUserData(); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index ff86c96f60..22308f02b6 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -49,8 +49,13 @@ namespace MWGui MyGUI::Widget* mScriptBox; MyGUI::ScrollView* mScriptView; 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); @@ -80,6 +85,7 @@ namespace MWGui void onScriptFilterChange(MyGUI::EditBox*); void onScriptListSelection(MyGUI::ListBox*, size_t index); + void onScriptListFocus(MyGUI::ListBox*, size_t index); void apply(); diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index 7737f36ec4..7b11e1770c 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -245,7 +245,7 @@ namespace MWLua page.mName = options.get_or("name", std::string()); if (page.mName.empty()) throw std::logic_error("No name provided for the settings page"); - page.mSearchHints = options.get_or("searchHints", std::string()); + page.mDescription = options.get_or("description", std::string()); auto element = options.get_or>("element", nullptr); if (!element) throw std::logic_error("No UI element provided for the settings page"); diff --git a/components/lua_ui/scriptsettings.hpp b/components/lua_ui/scriptsettings.hpp index 852e0df1ec..28525bb1da 100644 --- a/components/lua_ui/scriptsettings.hpp +++ b/components/lua_ui/scriptsettings.hpp @@ -13,7 +13,7 @@ namespace LuaUi struct ScriptSettingsPage { std::string mName; - std::string mSearchHints; + std::string mDescription; Element* mElement; // TODO: figure out if this can lead to use after free }; const std::vector& scriptSettingsPages(); diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index cf55550fbe..da64735c79 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -665,6 +665,14 @@ + + + + + + + + From 1455aa3e026f1a71ff511a387e0f92a895b0907a Mon Sep 17 00:00:00 2001 From: uramer Date: Sat, 29 Jan 2022 18:33:14 +0100 Subject: [PATCH 07/17] Allow changing script settings pages after registering them --- apps/openmw/mwgui/settingswindow.cpp | 9 ++++---- apps/openmw/mwlua/uibindings.cpp | 11 +-------- components/lua_ui/scriptsettings.cpp | 34 +++++++++++++++++++++++----- components/lua_ui/scriptsettings.hpp | 5 ++-- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index dfd0e6d9df..d41d9cc4f0 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -744,16 +744,15 @@ namespace MWGui auto flags = std::regex_constants::icase; std::regex filterRegex(filter, flags); - auto scriptSettings = LuaUi::scriptSettingsPages(); - for (size_t i = 0; i < scriptSettings.size(); ++i) + for (size_t i = 0; i < LuaUi::scriptSettingsPageCount(); ++i) { - LuaUi::ScriptSettingsPage page = scriptSettings[i]; + LuaUi::ScriptSettingsPage page = LuaUi::scriptSettingsPageAt(i); if (std::regex_match(page.mName, filterRegex) || std::regex_match(page.mDescription, filterRegex)) mScriptList->addItem(page.mName, i); } // Hide script settings tab when the game world isn't loaded and scripts couldn't add their settings - bool disabled = scriptSettings.empty(); + bool disabled = LuaUi::scriptSettingsPageCount() == 0; mScriptDisabled->setVisible(disabled); mScriptFilter->setVisible(!disabled); mScriptList->setVisible(!disabled); @@ -793,7 +792,7 @@ namespace MWGui return; } size_t page = *mScriptList->getItemDataAt(index); - mScriptDescription->setCaption(LuaUi::scriptSettingsPages()[page].mDescription); + mScriptDescription->setCaption(LuaUi::scriptSettingsPageAt(page).mDescription); mScriptDescription->setVisible(true); mScriptView->setVisible(false); } diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index 7b11e1770c..bd19dde114 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -241,16 +241,7 @@ namespace MWLua api["registerSettingsPage"] = [](sol::table options) { - LuaUi::ScriptSettingsPage page; - page.mName = options.get_or("name", std::string()); - if (page.mName.empty()) - throw std::logic_error("No name provided for the settings page"); - page.mDescription = options.get_or("description", std::string()); - auto element = options.get_or>("element", nullptr); - if (!element) - throw std::logic_error("No UI element provided for the settings page"); - page.mElement = element.get(); - LuaUi::registerSettingsPage(page); + LuaUi::registerSettingsPage(options); }; return LuaUtil::makeReadOnly(api); diff --git a/components/lua_ui/scriptsettings.cpp b/components/lua_ui/scriptsettings.cpp index 1a016381cd..2695b9f25d 100644 --- a/components/lua_ui/scriptsettings.cpp +++ b/components/lua_ui/scriptsettings.cpp @@ -8,17 +8,35 @@ namespace LuaUi { namespace { - std::vector allPages; + std::vector allPages; + ScriptSettingsPage parse(const sol::table& options) + { + auto name = options.get_or("name", std::string()); + auto description = options.get_or("description", std::string()); + auto element = options.get_or>("element", nullptr); + if (name.empty()) + Log(Debug::Warning) << "A script settings page has an empty name"; + if (!element.get()) + Log(Debug::Warning) << "A script settings page has no UI element assigned"; + return { + name, description, element.get() + }; + } } - const std::vector& scriptSettingsPages() + size_t scriptSettingsPageCount() { - return allPages; + return allPages.size(); } - void registerSettingsPage(const ScriptSettingsPage& page) + ScriptSettingsPage scriptSettingsPageAt(size_t index) { - allPages.push_back(page); + return parse(allPages[index]); + } + + void registerSettingsPage(const sol::table& options) + { + allPages.push_back(options); } void clearSettings() @@ -29,6 +47,10 @@ namespace LuaUi void attachToWidget(size_t index, MyGUI::Widget* widget) { if (index < allPages.size()) - allPages[index].mElement->attachToWidget(widget); + { + ScriptSettingsPage page = parse(allPages[index]); + if (page.mElement) + page.mElement->attachToWidget(widget); + } } } diff --git a/components/lua_ui/scriptsettings.hpp b/components/lua_ui/scriptsettings.hpp index 28525bb1da..3b51aff901 100644 --- a/components/lua_ui/scriptsettings.hpp +++ b/components/lua_ui/scriptsettings.hpp @@ -16,8 +16,9 @@ namespace LuaUi std::string mDescription; Element* mElement; // TODO: figure out if this can lead to use after free }; - const std::vector& scriptSettingsPages(); - void registerSettingsPage(const ScriptSettingsPage& page); + 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); } From db9e734a6ad6ca112914cf87f4fd934f322d4118 Mon Sep 17 00:00:00 2001 From: uramer Date: Sat, 29 Jan 2022 18:46:07 +0100 Subject: [PATCH 08/17] Fix warning --- apps/openmw/mwgui/settingswindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index d41d9cc4f0..2abea56064 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -777,7 +777,7 @@ namespace MWGui mScriptChild = mScriptView->getChildCount() > 0 ? mScriptView->getChildAt(0) : nullptr; MyGUI::IntSize canvasSize = mScriptChild ? mScriptChild->getSize() : MyGUI::IntSize(); if (mScriptChild) - mScriptChild->setVisible(mScriptView->isVisible()); + mScriptChild->setVisible(mScriptView->getVisible()); mScriptView->setCanvasSize(canvasSize); } From 086a7d9bc5fdb8bbff60348257d916df107e31ea Mon Sep 17 00:00:00 2001 From: uramer Date: Sat, 29 Jan 2022 23:06:43 +0100 Subject: [PATCH 09/17] 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 @@ + From f07f05ddd37389413128a0f12a0342360eee17d7 Mon Sep 17 00:00:00 2001 From: uramer Date: Sat, 29 Jan 2022 23:40:31 +0100 Subject: [PATCH 10/17] Add Container widget type, use it to make Adapter code less hacky --- components/CMakeLists.txt | 2 +- components/lua_ui/adapter.cpp | 17 ++++++--------- components/lua_ui/adapter.hpp | 4 ++-- components/lua_ui/container.cpp | 35 ++++++++++++++++++++++++++++++ components/lua_ui/container.hpp | 21 ++++++++++++++++++ components/lua_ui/element.cpp | 12 ----------- components/lua_ui/util.cpp | 3 +++ components/lua_ui/widget.cpp | 38 +++++++++++++++++++++++++-------- components/lua_ui/widget.hpp | 10 +++++++++ 9 files changed, 107 insertions(+), 35 deletions(-) create mode 100644 components/lua_ui/container.cpp create mode 100644 components/lua_ui/container.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 58e6d52ead..3ca08c4b7b 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 - adapter text textedit window image + adapter text textedit window image container ) diff --git a/components/lua_ui/adapter.cpp b/components/lua_ui/adapter.cpp index 9a109d5d1f..9d37e2cb1a 100644 --- a/components/lua_ui/adapter.cpp +++ b/components/lua_ui/adapter.cpp @@ -3,6 +3,7 @@ #include #include "element.hpp" +#include "container.hpp" namespace LuaUi { @@ -15,18 +16,12 @@ namespace LuaUi : 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) + mContent = MyGUI::Gui::getInstancePtr()->createWidget( + "", MyGUI::IntCoord(), MyGUI::Align::Default, "", ""); + mContent->initialize(luaState, mContent); + mContent->onCoordChange([this](WidgetExtension* ext, MyGUI::IntCoord coord) { - setSize(size); + setSize(coord.size()); }); mContent->widget()->attachToWidget(this); } diff --git a/components/lua_ui/adapter.hpp b/components/lua_ui/adapter.hpp index 2ff206464d..e18f04c1f9 100644 --- a/components/lua_ui/adapter.hpp +++ b/components/lua_ui/adapter.hpp @@ -5,7 +5,7 @@ namespace LuaUi { - class WidgetExtension; + class LuaContainer; struct Element; class LuaAdapter : public MyGUI::Widget { @@ -19,7 +19,7 @@ namespace LuaUi bool empty() { return mElement.get() == nullptr; } private: - WidgetExtension* mContent; + LuaContainer* mContent; std::shared_ptr mElement; void attachElement(); void detachElement(); diff --git a/components/lua_ui/container.cpp b/components/lua_ui/container.cpp new file mode 100644 index 0000000000..867378744b --- /dev/null +++ b/components/lua_ui/container.cpp @@ -0,0 +1,35 @@ +#include "container.hpp" + +#include + +namespace LuaUi +{ + void LuaContainer::updateChildren() + { + WidgetExtension::updateChildren(); + for (auto w : children()) + { + w->onCoordChange([this](WidgetExtension* child, MyGUI::IntCoord coord) + { updateSizeToFit(); }); + } + updateSizeToFit(); + } + + MyGUI::IntSize LuaContainer::childScalingSize() + { + return MyGUI::IntSize(); + } + + void LuaContainer::updateSizeToFit() + { + MyGUI::IntSize size; + for (auto w : children()) + { + MyGUI::IntCoord coord = w->widget()->getCoord(); + size.width = std::max(size.width, coord.left + coord.width); + size.height = std::max(size.height, coord.top + coord.height); + } + setForcedSize(size); + updateCoord(); + } +} diff --git a/components/lua_ui/container.hpp b/components/lua_ui/container.hpp new file mode 100644 index 0000000000..a005b7ae53 --- /dev/null +++ b/components/lua_ui/container.hpp @@ -0,0 +1,21 @@ +#ifndef OPENMW_LUAUI_CONTAINER +#define OPENMW_LUAUI_CONTAINER + +#include "widget.hpp" + +namespace LuaUi +{ + class LuaContainer : public WidgetExtension, public MyGUI::Widget + { + MYGUI_RTTI_DERIVED(LuaContainer) + + protected: + virtual void updateChildren() override; + virtual MyGUI::IntSize childScalingSize() override; + + private: + void updateSizeToFit(); + }; +} + +#endif // !OPENMW_LUAUI_CONTAINER diff --git a/components/lua_ui/element.cpp b/components/lua_ui/element.cpp index 9baec82269..59a3b13d55 100644 --- a/components/lua_ui/element.cpp +++ b/components/lua_ui/element.cpp @@ -221,10 +221,7 @@ namespace LuaUi void Element::detachFromWidget() { if (mRoot) - { - mRoot->onSizeChange({}); mRoot->widget()->detachFromWidget(); - } if (mAttachedTo) mAttachedTo->setChildren({}); mAttachedTo = nullptr; @@ -239,16 +236,7 @@ namespace LuaUi if (!mLayer.empty()) Log(Debug::Warning) << "Ignoring element's layer " << mLayer << " because it's attached to a widget"; mAttachedTo->setChildren({ mRoot }); - auto callback = [this](MyGUI::IntSize size) - { - 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/util.cpp b/components/lua_ui/util.cpp index 24a1442a53..7e81fc579c 100644 --- a/components/lua_ui/util.cpp +++ b/components/lua_ui/util.cpp @@ -8,6 +8,7 @@ #include "textedit.hpp" #include "window.hpp" #include "image.hpp" +#include "container.hpp" #include "element.hpp" #include "scriptsettings.hpp" @@ -23,6 +24,7 @@ namespace LuaUi MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("BasisSkin"); } @@ -34,6 +36,7 @@ namespace LuaUi { "LuaTextEdit", "TextEdit" }, { "LuaWindow", "Window" }, { "LuaImage", "Image" }, + { "LuaContainer", "Container" }, }; return types; } diff --git a/components/lua_ui/widget.cpp b/components/lua_ui/widget.cpp index 4224598ca1..b021c688fa 100644 --- a/components/lua_ui/widget.cpp +++ b/components/lua_ui/widget.cpp @@ -14,10 +14,11 @@ namespace LuaUi , mAbsoluteCoord() , mRelativeCoord() , mAnchor() - , mLua{ nullptr } - , mWidget{ nullptr } + , mLua(nullptr) + , mWidget(nullptr) , mSlot(this) - , mLayout{ sol::nil } + , mParent(nullptr) + , mLayout(sol::nil) {} void WidgetExtension::initialize(lua_State* lua, MyGUI::Widget* self) @@ -64,6 +65,8 @@ namespace LuaUi mWidget->eventKeySetFocus.clear(); mWidget->eventKeyLostFocus.clear(); + mOnSizeChange.reset(); + for (WidgetExtension* w : mChildren) w->deinitialize(); for (WidgetExtension* w : mTemplateChildren) @@ -72,6 +75,7 @@ namespace LuaUi void WidgetExtension::attach(WidgetExtension* ext) { + ext->mParent = this; ext->widget()->attachToWidget(mSlot->widget()); ext->updateCoord(); } @@ -150,6 +154,7 @@ namespace LuaUi mChildren[i] = children[i]; attach(mChildren[i]); } + updateChildren(); } void WidgetExtension::setTemplateChildren(const std::vector& children) @@ -212,6 +217,8 @@ namespace LuaUi if (mOnSizeChange.has_value()) mOnSizeChange.value()(newCoord.size()); } + if (oldCoord != newCoord && mOnCoordChange.has_value()) + mOnCoordChange.value()(this, newCoord); } void WidgetExtension::setProperties(sol::object props) @@ -240,23 +247,31 @@ namespace LuaUi w->updateCoord(); } + MyGUI::IntSize WidgetExtension::parentSize() + { + if (mParent) + return mParent->childScalingSize(); + else + return widget()->getParentSize(); + } + MyGUI::IntSize WidgetExtension::calculateSize() { - const MyGUI::IntSize& parentSize = mWidget->getParentSize(); + MyGUI::IntSize pSize = parentSize(); MyGUI::IntSize newSize; newSize = mAbsoluteCoord.size() + mForcedCoord.size(); - newSize.width += mRelativeCoord.width * parentSize.width; - newSize.height += mRelativeCoord.height * parentSize.height; + newSize.width += mRelativeCoord.width * pSize.width; + newSize.height += mRelativeCoord.height * pSize.height; return newSize; } MyGUI::IntPoint WidgetExtension::calculatePosition(const MyGUI::IntSize& size) { - const MyGUI::IntSize& parentSize = mWidget->getParentSize(); + MyGUI::IntSize pSize = parentSize(); MyGUI::IntPoint newPosition; newPosition = mAbsoluteCoord.point() + mForcedCoord.point(); - newPosition.left += mRelativeCoord.left * parentSize.width - mAnchor.width * size.width; - newPosition.top += mRelativeCoord.top * parentSize.height - mAnchor.height * size.height; + newPosition.left += mRelativeCoord.left * pSize.width - mAnchor.width * size.width; + newPosition.top += mRelativeCoord.top * pSize.height - mAnchor.height * size.height; return newPosition; } @@ -268,6 +283,11 @@ namespace LuaUi return newCoord; } + MyGUI::IntSize WidgetExtension::childScalingSize() + { + return widget()->getSize(); + } + void WidgetExtension::triggerEvent(std::string_view name, const sol::object& argument = sol::nil) const { auto it = mCallbacks.find(name); diff --git a/components/lua_ui/widget.hpp b/components/lua_ui/widget.hpp index 1eff2a113b..a5adb33130 100644 --- a/components/lua_ui/widget.hpp +++ b/components/lua_ui/widget.hpp @@ -57,6 +57,11 @@ namespace LuaUi return parseExternal(mExternal, name, defaultValue); } + void onCoordChange(const std::optional>& callback) + { + mOnCoordChange = callback; + } + void onSizeChange(const std::optional>& callback) { mOnSizeChange = callback; @@ -68,9 +73,11 @@ namespace LuaUi sol::object keyEvent(MyGUI::KeyCode) const; sol::object mouseEvent(int left, int top, MyGUI::MouseButton button) const; + MyGUI::IntSize parentSize(); virtual MyGUI::IntSize calculateSize(); virtual MyGUI::IntPoint calculatePosition(const MyGUI::IntSize& size); MyGUI::IntCoord calculateCoord(); + virtual MyGUI::IntSize childScalingSize(); template T propertyValue(std::string_view name, const T& defaultValue) @@ -83,6 +90,7 @@ namespace LuaUi virtual void updateTemplate(); virtual void updateProperties(); + virtual void updateChildren() {}; void triggerEvent(std::string_view name, const sol::object& argument) const; @@ -108,6 +116,7 @@ namespace LuaUi sol::object mProperties; sol::object mTemplateProperties; sol::object mExternal; + WidgetExtension* mParent; void attach(WidgetExtension* ext); @@ -127,6 +136,7 @@ namespace LuaUi void focusGain(MyGUI::Widget*, MyGUI::Widget*); void focusLoss(MyGUI::Widget*, MyGUI::Widget*); + std::optional> mOnCoordChange; std::optional> mOnSizeChange; }; From 67641dcdb7106cf7ea528ae305aef4a61186c142 Mon Sep 17 00:00:00 2001 From: uramer Date: Sat, 29 Jan 2022 23:43:08 +0100 Subject: [PATCH 11/17] Fix compile errors --- apps/openmw/mwlua/uibindings.cpp | 2 +- components/CMakeLists.txt | 3 ++- components/lua_ui/adapter.cpp | 14 +++++++------- components/lua_ui/adapter.hpp | 6 ++++-- components/lua_ui/registerscriptsettings.hpp | 13 +++++++++++++ components/lua_ui/scriptsettings.cpp | 1 + components/lua_ui/scriptsettings.hpp | 3 +-- components/lua_ui/util.cpp | 2 +- components/lua_ui/widget.cpp | 2 +- 9 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 components/lua_ui/registerscriptsettings.hpp diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index bd19dde114..073ecc7822 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include "context.hpp" #include "actions.hpp" diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 3ca08c4b7b..9a1ffc847c 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -165,7 +165,8 @@ add_component_dir (queries ) add_component_dir (lua_ui - properties widget element util layers content scriptsettings + registerscriptsettings scriptsettings + properties widget element util layers content adapter text textedit window image container ) diff --git a/components/lua_ui/adapter.cpp b/components/lua_ui/adapter.cpp index 9d37e2cb1a..aa04f4c374 100644 --- a/components/lua_ui/adapter.cpp +++ b/components/lua_ui/adapter.cpp @@ -14,16 +14,16 @@ namespace LuaUi LuaAdapter::LuaAdapter() : mElement(nullptr) - , mContent(nullptr) + , mContainer(nullptr) { - mContent = MyGUI::Gui::getInstancePtr()->createWidget( + mContainer = MyGUI::Gui::getInstancePtr()->createWidget( "", MyGUI::IntCoord(), MyGUI::Align::Default, "", ""); - mContent->initialize(luaState, mContent); - mContent->onCoordChange([this](WidgetExtension* ext, MyGUI::IntCoord coord) + mContainer->initialize(luaState, mContainer); + mContainer->onCoordChange([this](WidgetExtension* ext, MyGUI::IntCoord coord) { setSize(coord.size()); }); - mContent->widget()->attachToWidget(this); + mContainer->widget()->attachToWidget(this); } void LuaAdapter::attach(const std::shared_ptr& element) @@ -31,7 +31,7 @@ namespace LuaUi detachElement(); mElement = element; attachElement(); - setSize(mContent->widget()->getSize()); + setSize(mContainer->widget()->getSize()); // workaround for MyGUI bug // parent visibility doesn't affect added children @@ -48,7 +48,7 @@ namespace LuaUi void LuaAdapter::attachElement() { if (mElement.get()) - mElement->attachToWidget(mContent); + mElement->attachToWidget(mContainer); } void LuaAdapter::detachElement() diff --git a/components/lua_ui/adapter.hpp b/components/lua_ui/adapter.hpp index e18f04c1f9..2cb08b7367 100644 --- a/components/lua_ui/adapter.hpp +++ b/components/lua_ui/adapter.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_LUAUI_ADAPTER #define OPENMW_LUAUI_ADAPTER +#include + #include namespace LuaUi @@ -16,11 +18,11 @@ namespace LuaUi void attach(const std::shared_ptr& element); void detach(); - bool empty() { return mElement.get() == nullptr; } private: - LuaContainer* mContent; std::shared_ptr mElement; + LuaContainer* mContainer; + void attachElement(); void detachElement(); }; diff --git a/components/lua_ui/registerscriptsettings.hpp b/components/lua_ui/registerscriptsettings.hpp new file mode 100644 index 0000000000..fb794468da --- /dev/null +++ b/components/lua_ui/registerscriptsettings.hpp @@ -0,0 +1,13 @@ +#ifndef OPENMW_LUAUI_REGISTERSCRIPTSETTINGS +#define OPENMW_LUAUI_REGISTERSCRIPTSETTINGS + +#include + +namespace LuaUi +{ + // implemented in scriptsettings.cpp + void registerSettingsPage(const sol::table& options); + void clearSettings(); +} + +#endif // !OPENMW_LUAUI_REGISTERSCRIPTSETTINGS diff --git a/components/lua_ui/scriptsettings.cpp b/components/lua_ui/scriptsettings.cpp index bf7788597a..a1671186a4 100644 --- a/components/lua_ui/scriptsettings.cpp +++ b/components/lua_ui/scriptsettings.cpp @@ -3,6 +3,7 @@ #include #include +#include "registerscriptsettings.hpp" #include "element.hpp" #include "adapter.hpp" diff --git a/components/lua_ui/scriptsettings.hpp b/components/lua_ui/scriptsettings.hpp index 050722c249..245c3e0449 100644 --- a/components/lua_ui/scriptsettings.hpp +++ b/components/lua_ui/scriptsettings.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -18,8 +19,6 @@ namespace LuaUi }; size_t scriptSettingsPageCount(); ScriptSettingsPage scriptSettingsPageAt(size_t index); - void registerSettingsPage(const sol::table& options); - void clearSettings(); void attachPageAt(size_t index, LuaAdapter* adapter); } diff --git a/components/lua_ui/util.cpp b/components/lua_ui/util.cpp index 7e81fc579c..8a7fd4b65b 100644 --- a/components/lua_ui/util.cpp +++ b/components/lua_ui/util.cpp @@ -11,7 +11,7 @@ #include "container.hpp" #include "element.hpp" -#include "scriptsettings.hpp" +#include "registerscriptsettings.hpp" namespace LuaUi { diff --git a/components/lua_ui/widget.cpp b/components/lua_ui/widget.cpp index b021c688fa..f9f6dc41ff 100644 --- a/components/lua_ui/widget.cpp +++ b/components/lua_ui/widget.cpp @@ -17,8 +17,8 @@ namespace LuaUi , mLua(nullptr) , mWidget(nullptr) , mSlot(this) - , mParent(nullptr) , mLayout(sol::nil) + , mParent(nullptr) {} void WidgetExtension::initialize(lua_State* lua, MyGUI::Widget* self) From 01d65a14f125fd652a33b8eb859a5b4a43beb5a4 Mon Sep 17 00:00:00 2001 From: uramer Date: Sun, 30 Jan 2022 14:25:20 +0100 Subject: [PATCH 12/17] Implement more advanced search, sort script setting pages by alphabet and filter match quality --- apps/openmw/mwgui/settingswindow.cpp | 60 ++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index f556bbdd5b..a8c430f2a2 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -729,6 +729,42 @@ namespace MWGui mScriptDisabled->setPosition({0, 0}); mScriptDisabled->setSize(parentSize); } + + namespace + { + std::string escapeRegex(const std::string& str) + { + static std::regex specialChars(R"r([\^\.\[\$\(\)\|\*\+\?\{])r", std::regex_constants::extended); + return std::regex_replace(str, specialChars, R"(\$&)"); + } + + std::regex wordSearch(const std::string& query) + { + static std::regex wordsRegex(R"([^[:space:]]+)", std::regex_constants::extended); + auto wordsBegin = std::sregex_iterator(query.begin(), query.end(), wordsRegex); + auto wordsEnd = std::sregex_iterator(); + std::string searchRegex("("); + for (auto it = wordsBegin; it != wordsEnd; ++it) + { + if (it != wordsBegin) + searchRegex += '|'; + searchRegex += escapeRegex(query.substr(it->position(), it->length())); + } + searchRegex += ')'; + // query had only whitespace characters + if (searchRegex == "()") + searchRegex = "^(.*)$"; + static auto flags = std::regex_constants::icase | std::regex_constants::extended; + return std::regex(searchRegex, flags); + } + + int weightedSearch(const std::regex& regex, const std::string& text) + { + std::smatch matches; + std::regex_search(text, matches, regex); + return matches.size(); + } + } void SettingsWindow::renderScriptSettings() { @@ -737,18 +773,26 @@ namespace MWGui mScriptList->removeAllItems(); mScriptView->setCanvasSize({0, 0}); - std::string filter(".*"); - filter += mScriptFilter->getCaption(); - filter += ".*"; - auto flags = std::regex_constants::icase; - std::regex filterRegex(filter, flags); - + std::regex searchRegex = wordSearch(mScriptFilter->getCaption()); + std::vector> weightedPages; + weightedPages.reserve(LuaUi::scriptSettingsPageCount()); for (size_t i = 0; i < LuaUi::scriptSettingsPageCount(); ++i) { LuaUi::ScriptSettingsPage page = LuaUi::scriptSettingsPageAt(i); - if (std::regex_match(page.mName, filterRegex) || std::regex_match(page.mDescription, filterRegex)) - mScriptList->addItem(page.mName, i); + int nameSearch = 2 * weightedSearch(searchRegex, page.mName); + int descriptionSearch = weightedSearch(searchRegex, page.mDescription); + int search = nameSearch + descriptionSearch; + if (search > 0) + weightedPages.push_back({ i, page, search }); } + std::sort(weightedPages.begin(), weightedPages.end(), [](const auto& a, const auto& b) + { + const auto& [iA, pageA, weightA] = a; + const auto& [iB, pageB, weightB] = b; + return weightA == weightB ? pageA.mName < pageB.mName : weightA > weightB; + }); + for (const auto & [i, page, weight] : weightedPages) + mScriptList->addItem(page.mName, i); // Hide script settings tab when the game world isn't loaded and scripts couldn't add their settings bool disabled = LuaUi::scriptSettingsPageCount() == 0; From 7f796d148e042ba362de0dcab413ef1800566969 Mon Sep 17 00:00:00 2001 From: uramer Date: Sun, 30 Jan 2022 15:50:04 +0100 Subject: [PATCH 13/17] Clean up --- components/lua_ui/util.cpp | 1 - components/lua_ui/widget.cpp | 6 +----- components/lua_ui/widget.hpp | 6 ------ 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/components/lua_ui/util.cpp b/components/lua_ui/util.cpp index 8a7fd4b65b..cd2cb2c077 100644 --- a/components/lua_ui/util.cpp +++ b/components/lua_ui/util.cpp @@ -36,7 +36,6 @@ namespace LuaUi { "LuaTextEdit", "TextEdit" }, { "LuaWindow", "Window" }, { "LuaImage", "Image" }, - { "LuaContainer", "Container" }, }; return types; } diff --git a/components/lua_ui/widget.cpp b/components/lua_ui/widget.cpp index f9f6dc41ff..70ed6b64fe 100644 --- a/components/lua_ui/widget.cpp +++ b/components/lua_ui/widget.cpp @@ -65,7 +65,7 @@ namespace LuaUi mWidget->eventKeySetFocus.clear(); mWidget->eventKeyLostFocus.clear(); - mOnSizeChange.reset(); + mOnCoordChange.reset(); for (WidgetExtension* w : mChildren) w->deinitialize(); @@ -212,11 +212,7 @@ namespace LuaUi if (oldCoord != newCoord) mWidget->setCoord(newCoord); if (oldCoord.size() != newCoord.size()) - { updateChildrenCoord(); - if (mOnSizeChange.has_value()) - mOnSizeChange.value()(newCoord.size()); - } if (oldCoord != newCoord && mOnCoordChange.has_value()) mOnCoordChange.value()(this, newCoord); } diff --git a/components/lua_ui/widget.hpp b/components/lua_ui/widget.hpp index a5adb33130..63af5b3cae 100644 --- a/components/lua_ui/widget.hpp +++ b/components/lua_ui/widget.hpp @@ -62,11 +62,6 @@ namespace LuaUi mOnCoordChange = callback; } - void onSizeChange(const std::optional>& callback) - { - mOnSizeChange = callback; - } - protected: virtual void initialize(); sol::table makeTable() const; @@ -137,7 +132,6 @@ namespace LuaUi void focusLoss(MyGUI::Widget*, MyGUI::Widget*); std::optional> mOnCoordChange; - std::optional> mOnSizeChange; }; class LuaWidget : public MyGUI::Widget, public WidgetExtension From ef1e72dc17900a882c7dd8993ab80b08c4dc73b9 Mon Sep 17 00:00:00 2001 From: uramer Date: Tue, 1 Feb 2022 19:20:47 +0100 Subject: [PATCH 14/17] Revert from settings description to searchHints --- apps/openmw/mwgui/settingswindow.cpp | 19 +------------------ apps/openmw/mwgui/settingswindow.hpp | 2 -- components/lua_ui/scriptsettings.cpp | 4 ++-- components/lua_ui/scriptsettings.hpp | 2 +- files/mygui/openmw_settings_window.layout | 8 -------- 5 files changed, 4 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index a8c430f2a2..14587513df 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -244,7 +244,6 @@ namespace MWGui getWidget(mScriptView, "ScriptView"); getWidget(mScriptAdapter, "ScriptAdapter"); getWidget(mScriptDisabled, "ScriptDisabled"); - getWidget(mScriptDescription, "ScriptDescription"); #ifndef WIN32 // hide gamma controls since it currently does not work under Linux @@ -333,7 +332,6 @@ namespace MWGui mScriptFilter->eventEditTextChange += MyGUI::newDelegate(this, &SettingsWindow::onScriptFilterChange); mScriptList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SettingsWindow::onScriptListSelection); - mScriptList->eventListMouseItemFocus += MyGUI::newDelegate(this, &SettingsWindow::onScriptListFocus); } void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t /*index*/) @@ -780,7 +778,7 @@ namespace MWGui { LuaUi::ScriptSettingsPage page = LuaUi::scriptSettingsPageAt(i); int nameSearch = 2 * weightedSearch(searchRegex, page.mName); - int descriptionSearch = weightedSearch(searchRegex, page.mDescription); + int descriptionSearch = weightedSearch(searchRegex, page.mSearchHints); int search = nameSearch + descriptionSearch; if (search > 0) weightedPages.push_back({ i, page, search }); @@ -820,21 +818,6 @@ namespace MWGui mScriptView->setCanvasSize(canvasSize); } - void SettingsWindow::onScriptListFocus(MyGUI::ListBox*, size_t index) - { - if (index >= mScriptList->getItemCount()) - { - mScriptDescription->setVisible(false); - mScriptView->setVisible(true); - } - 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) { int actionId = *_sender->getUserData(); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 217dcf3051..1a888257a8 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -52,7 +52,6 @@ namespace MWGui MyGUI::ScrollView* mScriptView; LuaUi::LuaAdapter* mScriptAdapter; MyGUI::EditBox* mScriptDisabled; - MyGUI::EditBox* mScriptDescription; int mCurrentPage; void onTabChanged(MyGUI::TabControl* _sender, size_t index); @@ -84,7 +83,6 @@ namespace MWGui void onScriptFilterChange(MyGUI::EditBox*); void onScriptListSelection(MyGUI::ListBox*, size_t index); - void onScriptListFocus(MyGUI::ListBox*, size_t index); void apply(); diff --git a/components/lua_ui/scriptsettings.cpp b/components/lua_ui/scriptsettings.cpp index a1671186a4..f2ab84bde0 100644 --- a/components/lua_ui/scriptsettings.cpp +++ b/components/lua_ui/scriptsettings.cpp @@ -15,14 +15,14 @@ namespace LuaUi ScriptSettingsPage parse(const sol::table& options) { auto name = options.get_or("name", std::string()); - auto description = options.get_or("description", std::string()); + auto searchHints = options.get_or("searchHints", std::string()); auto element = options.get_or>("element", nullptr); if (name.empty()) Log(Debug::Warning) << "A script settings page has an empty name"; if (!element.get()) Log(Debug::Warning) << "A script settings page has no UI element assigned"; return { - name, description, element + name, searchHints, element }; } } diff --git a/components/lua_ui/scriptsettings.hpp b/components/lua_ui/scriptsettings.hpp index 245c3e0449..c468f9eb0b 100644 --- a/components/lua_ui/scriptsettings.hpp +++ b/components/lua_ui/scriptsettings.hpp @@ -14,7 +14,7 @@ namespace LuaUi struct ScriptSettingsPage { std::string mName; - std::string mDescription; + std::string mSearchHints; std::shared_ptr mElement; }; size_t scriptSettingsPageCount(); diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index d6ac4f43fa..13f5ada24b 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -666,14 +666,6 @@ - - - - - - - - From 2185fd29c5ecd149fabeea83f84a82b6beb54d6a Mon Sep 17 00:00:00 2001 From: uramer Date: Tue, 1 Feb 2022 19:26:05 +0100 Subject: [PATCH 15/17] Document ui.registerSettingsPage --- files/lua_api/openmw/ui.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/files/lua_api/openmw/ui.lua b/files/lua_api/openmw/ui.lua index c60a4e1abd..997c39780e 100644 --- a/files/lua_api/openmw/ui.lua +++ b/files/lua_api/openmw/ui.lua @@ -36,6 +36,11 @@ -- @param #Layout layout -- @return #Element +--- +-- Adds a settings page to main menu setting's Scripts tab. +-- @function [parent=#ui] regregisterSettingsPageister +-- @param #SettingsPage page + --- -- Layout -- @type Layout @@ -137,4 +142,11 @@ -- @field openmw.util#Vector2 offset Position of the mouse cursor relative to the widget -- @field #number button Mouse button which triggered the event (could be nil) +--- +-- Settings page parameters, passed as an argument to uyi.registerSettingsPage +-- @type SettingsPage +-- @field #string name Name of the page, displayed in the list, used for search +-- @field #string searchHints Additional keywords used in search, not displayed anywhere +-- @field #Element element The page's UI, which will be attached to the settings tab. The root widget has to have a fixed size (set `size` field in `props`, see Widget documentation, `relativeSize` is ignored) + return nil From f68c0c41a9875cfa7413b3e89ff33d4bb28d8d9f Mon Sep 17 00:00:00 2001 From: uramer Date: Tue, 1 Feb 2022 22:03:01 +0100 Subject: [PATCH 16/17] Prioritize setting pages with hits in the name when searching --- apps/openmw/mwgui/settingswindow.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 14587513df..80a034fd2a 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -756,7 +756,8 @@ namespace MWGui return std::regex(searchRegex, flags); } - int weightedSearch(const std::regex& regex, const std::string& text) + // use double to guarantee no overflow when casting from size_t + double weightedSearch(const std::regex& regex, const std::string& text) { std::smatch matches; std::regex_search(text, matches, regex); @@ -772,24 +773,23 @@ namespace MWGui mScriptView->setCanvasSize({0, 0}); std::regex searchRegex = wordSearch(mScriptFilter->getCaption()); - std::vector> weightedPages; + std::vector> weightedPages; weightedPages.reserve(LuaUi::scriptSettingsPageCount()); for (size_t i = 0; i < LuaUi::scriptSettingsPageCount(); ++i) { LuaUi::ScriptSettingsPage page = LuaUi::scriptSettingsPageAt(i); - int nameSearch = 2 * weightedSearch(searchRegex, page.mName); - int descriptionSearch = weightedSearch(searchRegex, page.mSearchHints); - int search = nameSearch + descriptionSearch; - if (search > 0) - weightedPages.push_back({ i, page, search }); + double nameSearch = 2 * weightedSearch(searchRegex, page.mName); + double hintSearch = weightedSearch(searchRegex, page.mSearchHints); + if ((nameSearch + hintSearch) > 0) + weightedPages.push_back({ i, page, -nameSearch, -hintSearch }); } std::sort(weightedPages.begin(), weightedPages.end(), [](const auto& a, const auto& b) { - const auto& [iA, pageA, weightA] = a; - const auto& [iB, pageB, weightB] = b; - return weightA == weightB ? pageA.mName < pageB.mName : weightA > weightB; + const auto& [iA, pageA, nameA, hintA] = a; + const auto& [iB, pageB, nameB, hintB] = b; + return std::tie(nameA, hintA, pageA.mName) < std::tie(nameB, hintB, pageB.mName); }); - for (const auto & [i, page, weight] : weightedPages) + for (const auto & [i, page, name, hint] : weightedPages) mScriptList->addItem(page.mName, i); // Hide script settings tab when the game world isn't loaded and scripts couldn't add their settings @@ -814,8 +814,7 @@ namespace MWGui mCurrentPage = *mScriptList->getItemDataAt(index); LuaUi::attachPageAt(mCurrentPage, mScriptAdapter); } - MyGUI::IntSize canvasSize = mScriptAdapter->getSize(); - mScriptView->setCanvasSize(canvasSize); + mScriptView->setCanvasSize(mScriptAdapter->getSize()); } void SettingsWindow::onRebindAction(MyGUI::Widget* _sender) From 946b8b804cca2a004a80b5391102c436b914bef7 Mon Sep 17 00:00:00 2001 From: uramer Date: Wed, 2 Feb 2022 15:27:48 +0100 Subject: [PATCH 17/17] MR feedback --- apps/openmw/mwgui/settingswindow.cpp | 51 +++++++++++++++------------- apps/openmw/mwlua/uibindings.cpp | 5 +-- files/lua_api/openmw/ui.lua | 4 +-- 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 80a034fd2a..e79132f0ab 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -712,10 +712,10 @@ namespace MWGui void SettingsWindow::resizeScriptSettings() { - static const int minListWidth = 150; - static const float relativeListWidth = 0.2f; - static const int padding = 2; - static const int outerPadding = padding * 2; + constexpr int minListWidth = 150; + constexpr float relativeListWidth = 0.2f; + constexpr int padding = 2; + constexpr int outerPadding = padding * 2; MyGUI::IntSize parentSize = mScriptFilter->getParent()->getClientCoord().size(); int listWidth = std::max(minListWidth, static_cast(parentSize.width * relativeListWidth)); int filterHeight = mScriptFilter->getSize().height; @@ -732,13 +732,13 @@ namespace MWGui { std::string escapeRegex(const std::string& str) { - static std::regex specialChars(R"r([\^\.\[\$\(\)\|\*\+\?\{])r", std::regex_constants::extended); + static const std::regex specialChars(R"r([\^\.\[\$\(\)\|\*\+\?\{])r", std::regex_constants::extended); return std::regex_replace(str, specialChars, R"(\$&)"); } std::regex wordSearch(const std::string& query) { - static std::regex wordsRegex(R"([^[:space:]]+)", std::regex_constants::extended); + static const std::regex wordsRegex(R"([^[:space:]]+)", std::regex_constants::extended); auto wordsBegin = std::sregex_iterator(query.begin(), query.end(), wordsRegex); auto wordsEnd = std::sregex_iterator(); std::string searchRegex("("); @@ -752,16 +752,15 @@ namespace MWGui // query had only whitespace characters if (searchRegex == "()") searchRegex = "^(.*)$"; - static auto flags = std::regex_constants::icase | std::regex_constants::extended; - return std::regex(searchRegex, flags); + return std::regex(searchRegex, std::regex_constants::extended | std::regex_constants::icase); } - // use double to guarantee no overflow when casting from size_t double weightedSearch(const std::regex& regex, const std::string& text) { std::smatch matches; std::regex_search(text, matches, regex); - return matches.size(); + // need a signed value, so cast to double (not an integer type to guarantee no overflow) + return static_cast(matches.size()); } } @@ -772,25 +771,31 @@ namespace MWGui mScriptList->removeAllItems(); mScriptView->setCanvasSize({0, 0}); + struct WeightedPage { + size_t mIndex; + std::string mName; + double mNameWeight; + double mHintWeight; + + constexpr auto tie() const { return std::tie(mNameWeight, mHintWeight, mName); } + + constexpr bool operator<(const WeightedPage& rhs) const { return tie() < rhs.tie(); } + }; + std::regex searchRegex = wordSearch(mScriptFilter->getCaption()); - std::vector> weightedPages; + std::vector weightedPages; weightedPages.reserve(LuaUi::scriptSettingsPageCount()); for (size_t i = 0; i < LuaUi::scriptSettingsPageCount(); ++i) { LuaUi::ScriptSettingsPage page = LuaUi::scriptSettingsPageAt(i); - double nameSearch = 2 * weightedSearch(searchRegex, page.mName); - double hintSearch = weightedSearch(searchRegex, page.mSearchHints); - if ((nameSearch + hintSearch) > 0) - weightedPages.push_back({ i, page, -nameSearch, -hintSearch }); + double nameWeight = weightedSearch(searchRegex, page.mName); + double hintWeight = weightedSearch(searchRegex, page.mSearchHints); + if ((nameWeight + hintWeight) > 0) + weightedPages.push_back({ i, page.mName, -nameWeight, -hintWeight }); } - std::sort(weightedPages.begin(), weightedPages.end(), [](const auto& a, const auto& b) - { - const auto& [iA, pageA, nameA, hintA] = a; - const auto& [iB, pageB, nameB, hintB] = b; - return std::tie(nameA, hintA, pageA.mName) < std::tie(nameB, hintB, pageB.mName); - }); - for (const auto & [i, page, name, hint] : weightedPages) - mScriptList->addItem(page.mName, i); + std::sort(weightedPages.begin(), weightedPages.end()); + for (const WeightedPage& weightedPage : weightedPages) + mScriptList->addItem(weightedPage.mName, weightedPage.mIndex); // Hide script settings tab when the game world isn't loaded and scripts couldn't add their settings bool disabled = LuaUi::scriptSettingsPageCount() == 0; diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index 073ecc7822..6f1d34072a 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -239,10 +239,7 @@ namespace MWLua typeTable.set(it.second, it.first); api["TYPE"] = LuaUtil::makeReadOnly(typeTable); - api["registerSettingsPage"] = [](sol::table options) - { - LuaUi::registerSettingsPage(options); - }; + api["registerSettingsPage"] = &LuaUi::registerSettingsPage; return LuaUtil::makeReadOnly(api); } diff --git a/files/lua_api/openmw/ui.lua b/files/lua_api/openmw/ui.lua index 997c39780e..8049df860a 100644 --- a/files/lua_api/openmw/ui.lua +++ b/files/lua_api/openmw/ui.lua @@ -38,7 +38,7 @@ --- -- Adds a settings page to main menu setting's Scripts tab. --- @function [parent=#ui] regregisterSettingsPageister +-- @function [parent=#ui] registerSettingsPage -- @param #SettingsPage page --- @@ -143,7 +143,7 @@ -- @field #number button Mouse button which triggered the event (could be nil) --- --- Settings page parameters, passed as an argument to uyi.registerSettingsPage +-- Settings page parameters, passed as an argument to ui.registerSettingsPage -- @type SettingsPage -- @field #string name Name of the page, displayed in the list, used for search -- @field #string searchHints Additional keywords used in search, not displayed anywhere