diff --git a/components/lua/scriptscontainer.hpp b/components/lua/scriptscontainer.hpp index fcbd2ba0b7..d968c13a45 100644 --- a/components/lua/scriptscontainer.hpp +++ b/components/lua/scriptscontainer.hpp @@ -250,13 +250,14 @@ namespace LuaUtil sol::table mHiddenData; // same object as Script::mHiddenData in ScriptsContainer template - void operator()(Args&&... args) const + sol::object operator()(Args&&... args) const { if (mHiddenData[ScriptsContainer::sScriptIdKey] != sol::nil) - LuaUtil::call(mFunc, std::forward(args)...); + return LuaUtil::call(mFunc, std::forward(args)...); else Log(Debug::Debug) << "Ignored callback to the removed script " << mHiddenData.get(ScriptsContainer::sScriptDebugNameKey); + return sol::nil; } }; diff --git a/components/lua_ui/widget.cpp b/components/lua_ui/widget.cpp index 96a6d096bd..a11d3db852 100644 --- a/components/lua_ui/widget.cpp +++ b/components/lua_ui/widget.cpp @@ -10,7 +10,8 @@ namespace LuaUi { WidgetExtension::WidgetExtension() - : mLua(nullptr) + : mPropagateEvents(true) + , mLua(nullptr) , mWidget(nullptr) , mSlot(this) , mLayout(sol::nil) @@ -18,6 +19,7 @@ namespace LuaUi , mTemplateProperties(sol::nil) , mExternal(sol::nil) , mParent(nullptr) + , mTemplateChild(false) {} void WidgetExtension::initialize(lua_State* lua, MyGUI::Widget* self) @@ -81,12 +83,15 @@ namespace LuaUi void WidgetExtension::attach(WidgetExtension* ext) { ext->mParent = this; + ext->mTemplateChild = false; ext->widget()->attachToWidget(mSlot->widget()); ext->updateCoord(); } void WidgetExtension::attachTemplate(WidgetExtension* ext) { + ext->mParent = this; + ext->mTemplateChild = true; ext->widget()->attachToWidget(widget()); ext->updateCoord(); } @@ -133,7 +138,7 @@ namespace LuaUi sol::table WidgetExtension::makeTable() const { - return sol::table(mLua, sol::create); + return sol::table(lua(), sol::create); } sol::object WidgetExtension::keyEvent(MyGUI::KeyCode code) const @@ -142,7 +147,7 @@ namespace LuaUi keySym.sym = SDLUtil::myGuiKeyToSdl(code); keySym.scancode = SDL_GetScancodeFromKey(keySym.sym); keySym.mod = SDL_GetModState(); - return sol::make_object(mLua, keySym); + return sol::make_object(lua(), keySym); } sol::object WidgetExtension::mouseEvent(int left, int top, MyGUI::MouseButton button = MyGUI::MouseButton::None) const @@ -246,6 +251,7 @@ namespace LuaUi void WidgetExtension::updateProperties() { + mPropagateEvents = propertyValue("propagateEvents", true); mAbsoluteCoord = propertyValue("position", MyGUI::IntPoint()); mAbsoluteCoord = propertyValue("size", MyGUI::IntSize()); mRelativeCoord = propertyValue("relativePosition", MyGUI::FloatPoint()); @@ -265,7 +271,7 @@ namespace LuaUi MyGUI::IntSize WidgetExtension::parentSize() { - if (mParent) + if (mParent && !mTemplateChild) return mParent->childScalingSize(); else return widget()->getParentSize(); @@ -304,7 +310,7 @@ namespace LuaUi return mSlot->widget()->getSize(); } - void WidgetExtension::triggerEvent(std::string_view name, const sol::object& argument = sol::nil) const + void WidgetExtension::triggerEvent(std::string_view name, sol::object argument) const { auto it = mCallbacks.find(name); if (it != mCallbacks.end()) @@ -315,57 +321,58 @@ namespace LuaUi { if (code == MyGUI::KeyCode::None) { - // \todo decide how to handle unicode strings in Lua - MyGUI::UString uString; - uString.push_back(static_cast(ch)); - triggerEvent("textInput", sol::make_object(mLua, uString.asUTF8())); + propagateEvent("textInput", [ch](auto w) { + MyGUI::UString uString; + uString.push_back(static_cast(ch)); + return sol::make_object(w->lua(), uString.asUTF8()); + }); } else - triggerEvent("keyPress", keyEvent(code)); + propagateEvent("keyPress", [code](auto w){ return w->keyEvent(code); }); } void WidgetExtension::keyRelease(MyGUI::Widget*, MyGUI::KeyCode code) { - triggerEvent("keyRelease", keyEvent(code)); + propagateEvent("keyRelease", [code](auto w) { return w->keyEvent(code); }); } void WidgetExtension::mouseMove(MyGUI::Widget*, int left, int top) { - triggerEvent("mouseMove", mouseEvent(left, top)); + propagateEvent("mouseMove", [left, top](auto w) { return w->mouseEvent(left, top); }); } void WidgetExtension::mouseDrag(MyGUI::Widget*, int left, int top, MyGUI::MouseButton button) { - triggerEvent("mouseMove", mouseEvent(left, top, button)); + propagateEvent("mouseMove", [left, top, button](auto w) { return w->mouseEvent(left, top, button); }); } void WidgetExtension::mouseClick(MyGUI::Widget* _widget) { - triggerEvent("mouseClick"); + propagateEvent("mouseClick", [](auto){ return sol::nil; }); } void WidgetExtension::mouseDoubleClick(MyGUI::Widget* _widget) { - triggerEvent("mouseDoubleClick"); + propagateEvent("mouseDoubleClick", [](auto){ return sol::nil; }); } void WidgetExtension::mousePress(MyGUI::Widget*, int left, int top, MyGUI::MouseButton button) { - triggerEvent("mousePress", mouseEvent(left, top, button)); + propagateEvent("mousePress", [left, top, button](auto w) { return w->mouseEvent(left, top, button); }); } void WidgetExtension::mouseRelease(MyGUI::Widget*, int left, int top, MyGUI::MouseButton button) { - triggerEvent("mouseRelease", mouseEvent(left, top, button)); + propagateEvent("mouseRelease", [left, top, button](auto w) { return w->mouseEvent(left, top, button); }); } void WidgetExtension::focusGain(MyGUI::Widget*, MyGUI::Widget*) { - triggerEvent("focusGain"); + propagateEvent("focusGain", [](auto){ return sol::nil; }); } void WidgetExtension::focusLoss(MyGUI::Widget*, MyGUI::Widget*) { - triggerEvent("focusLoss"); + propagateEvent("focusLoss", [](auto){ return sol::nil; }); } } diff --git a/components/lua_ui/widget.hpp b/components/lua_ui/widget.hpp index 827993d47d..c712d18ccd 100644 --- a/components/lua_ui/widget.hpp +++ b/components/lua_ui/widget.hpp @@ -90,8 +90,28 @@ namespace LuaUi virtual void updateProperties(); virtual void updateChildren() {}; - lua_State* lua() { return mLua; } - void triggerEvent(std::string_view name, const sol::object& argument) const; + lua_State* lua() const { return mLua; } + + void triggerEvent(std::string_view name, sol::object argument) const; + template + void propagateEvent(std::string_view name, const ArgFactory& argumentFactory) const + { + const WidgetExtension* w = this; + while (w) + { + bool shouldPropagate = true; + auto it = w->mCallbacks.find(name); + if (it != w->mCallbacks.end()) + { + sol::object res = it->second(argumentFactory(w), w->mLayout); + shouldPropagate = res.is() && res.as(); + } + if (w->mParent && w->mPropagateEvents && shouldPropagate) + w = w->mParent; + else + w = nullptr; + } + } // offsets the position and size, used only in C++ widget code MyGUI::IntCoord mForcedCoord; @@ -103,6 +123,8 @@ namespace LuaUi // used in combination with relative coord to align the widget, e. g. center it MyGUI::FloatSize mAnchor; + bool mPropagateEvents; + private: // use lua_State* instead of sol::state_view because MyGUI requires a default constructor lua_State* mLua; @@ -116,6 +138,7 @@ namespace LuaUi sol::object mTemplateProperties; sol::object mExternal; WidgetExtension* mParent; + bool mTemplateChild; void attach(WidgetExtension* ext); void attachTemplate(WidgetExtension* ext); diff --git a/docs/source/reference/lua-scripting/widgets/widget.rst b/docs/source/reference/lua-scripting/widgets/widget.rst index 36cc0917d9..d114d0957b 100644 --- a/docs/source/reference/lua-scripting/widgets/widget.rst +++ b/docs/source/reference/lua-scripting/widgets/widget.rst @@ -32,12 +32,19 @@ Properties * - visible - boolean (true) - Defines if the widget is visible + * - propagateEvents + - boolean (true) + - Allows base widget events to propagate to the widget's parent. .. TODO: document the mouse pointer property, when API for reading / adding pointer types is available Events ------ +Base widget events are special, they can propagate up to the parent widget. +This can be prevented by changing the `propagateEvents` property, or by assigning an event handler. +The event is still allowed to propagate if the event handler returns `true`. + .. list-table:: :header-rows: 1 :widths: 20 20 60