diff options
Diffstat (limited to 'indra/llui')
-rw-r--r-- | indra/llui/CMakeLists.txt | 2 | ||||
-rw-r--r-- | indra/llui/llfloaterreglistener.cpp | 14 | ||||
-rw-r--r-- | indra/llui/llfloaterreglistener.h | 2 | ||||
-rw-r--r-- | indra/llui/llluafloater.cpp | 295 | ||||
-rw-r--r-- | indra/llui/llluafloater.h | 54 | ||||
-rw-r--r-- | indra/llui/llmenugl.h | 16 | ||||
-rw-r--r-- | indra/llui/lltexteditor.cpp | 3 | ||||
-rw-r--r-- | indra/llui/lltexteditor.h | 16 |
8 files changed, 392 insertions, 10 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index a0314cb5f2..69e1b57245 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -49,6 +49,7 @@ set(llui_SOURCE_FILES lllineeditor.cpp llloadingindicator.cpp lllocalcliprect.cpp + llluafloater.cpp llmenubutton.cpp llmenugl.cpp llmodaldialog.cpp @@ -164,6 +165,7 @@ set(llui_HEADER_FILES lllineeditor.h llloadingindicator.h lllocalcliprect.h + llluafloater.h llmenubutton.h llmenugl.h llmodaldialog.h diff --git a/indra/llui/llfloaterreglistener.cpp b/indra/llui/llfloaterreglistener.cpp index 7525b8cab3..8316101264 100644 --- a/indra/llui/llfloaterreglistener.cpp +++ b/indra/llui/llfloaterreglistener.cpp @@ -37,6 +37,7 @@ #include "llfloaterreg.h" #include "llfloater.h" #include "llbutton.h" +#include "llluafloater.h" LLFloaterRegListener::LLFloaterRegListener(): LLEventAPI("LLFloaterReg", @@ -72,6 +73,13 @@ LLFloaterRegListener::LLFloaterRegListener(): "Simulate clicking the named [\"button\"] in the visible floater named in [\"name\"]", &LLFloaterRegListener::clickButton, requiredNameButton); + + add("showLuaFloater", + "Open the new floater using XML file specified in [\"xml_path\"] with ID in [\"reqid\"]", + &LLLuaFloater::showLuaFloater, {llsd::map("xml_path", LLSD(), "reqid", LLSD())}); + add("getFloaterEvents", + "Return the table of Lua Floater events which are send to the script", + &LLFloaterRegListener::getLuaFloaterEvents); } void LLFloaterRegListener::getBuildMap(const LLSD& event) const @@ -154,3 +162,9 @@ void LLFloaterRegListener::clickButton(const LLSD& event) const LLEventPumps::instance().obtain(replyPump).post(reply); } } + +void LLFloaterRegListener::getLuaFloaterEvents(const LLSD &event) const +{ + Response response(llsd::map("events", LLLuaFloater::getEventsData()), event); +} + diff --git a/indra/llui/llfloaterreglistener.h b/indra/llui/llfloaterreglistener.h index 24311a2dfa..9cb0af2de5 100644 --- a/indra/llui/llfloaterreglistener.h +++ b/indra/llui/llfloaterreglistener.h @@ -49,6 +49,8 @@ private: void toggleInstance(const LLSD& event) const; void instanceVisible(const LLSD& event) const; void clickButton(const LLSD& event) const; + + void getLuaFloaterEvents(const LLSD &event) const; }; #endif /* ! defined(LL_LLFLOATERREGLISTENER_H) */ diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp new file mode 100644 index 0000000000..e584a67a00 --- /dev/null +++ b/indra/llui/llluafloater.cpp @@ -0,0 +1,295 @@ +/** + * @file llluafloater.cpp + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + * + */ + +#include "llluafloater.h" + +#include "fsyspath.h" +#include "llevents.h" + +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "llscrolllistctrl.h" +#include "lltexteditor.h" + +const std::string LISTENER_NAME("LLLuaFloater"); + +std::set<std::string> EVENT_LIST = { + "commit", + "double_click", + "mouse_enter", + "mouse_leave", + "mouse_down", + "mouse_up", + "right_mouse_down", + "right_mouse_up", + "post_build", + "floater_close", + "keystroke" +}; + +LLLuaFloater::LLLuaFloater(const LLSD &key) : + LLFloater(key), + mDispatchListener(LLUUID::generateNewID().asString(), "action"), + mReplyPumpName(key["reply"].asString()), + mReqID(key) +{ + auto ctrl_lookup = [this](const LLSD &event, std::function<LLSD(LLUICtrl*,const LLSD&)> cb) + { + LLUICtrl *ctrl = getChild<LLUICtrl>(event["ctrl_name"].asString()); + if (!ctrl) + { + LL_WARNS("LuaFloater") << "Control not found: " << event["ctrl_name"] << LL_ENDL; + return LLSD(); + } + return cb(ctrl, event); + }; + + LLSD requiredParams = llsd::map("ctrl_name", LLSD(), "value", LLSD()); + mDispatchListener.add("set_enabled", "", [ctrl_lookup](const LLSD &event) + { + return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setEnabled(event["value"].asBoolean()); return LLSD(); }); + }, requiredParams); + mDispatchListener.add("set_visible", "", [ctrl_lookup](const LLSD &event) + { + return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setVisible(event["value"].asBoolean()); return LLSD(); }); + }, requiredParams); + mDispatchListener.add("set_value", "", [ctrl_lookup](const LLSD &event) + { + return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { ctrl->setValue(event["value"]); return LLSD(); }); + }, requiredParams); + + mDispatchListener.add("add_list_element", "", [this](const LLSD &event) + { + LLScrollListCtrl *ctrl = getChild<LLScrollListCtrl>(event["ctrl_name"].asString()); + if(ctrl) + { + LLSD element_data = event["value"]; + if (element_data.isArray()) + { + for (const auto &row : llsd::inArray(element_data)) + { + ctrl->addElement(row); + } + } + else + { + ctrl->addElement(element_data); + } + } + }, requiredParams); + + mDispatchListener.add("add_text", "", [this](const LLSD &event) + { + LLTextEditor *editor = getChild<LLTextEditor>(event["ctrl_name"].asString()); + if (editor) + { + editor->pasteTextWithLinebreaks(stringize(event["value"])); + editor->addLineBreakChar(true); + } + }, requiredParams); + + mDispatchListener.add("set_title", "", [this](const LLSD &event) + { + setTitle(event["value"].asString()); + }, llsd::map("value", LLSD())); + + mDispatchListener.add("get_value", "", [ctrl_lookup](const LLSD &event) + { + return ctrl_lookup(event, [](LLUICtrl *ctrl, const LLSD &event) { return llsd::map("value", ctrl->getValue()); }); + }, llsd::map("ctrl_name", LLSD(), "reqid", LLSD())); +} + +LLLuaFloater::~LLLuaFloater() +{ + //post empty LLSD() to indicate done, in case it wasn't handled by the script after CLOSE_EVENT + post(LLSD()); +} + +BOOL LLLuaFloater::postBuild() +{ + for (LLView *view : *getChildList()) + { + LLUICtrl *ctrl = dynamic_cast<LLUICtrl*>(view); + if (ctrl) + { + LLSD data; + data["ctrl_name"] = view->getName(); + + ctrl->setCommitCallback([this, data](LLUICtrl *ctrl, const LLSD ¶m) + { + LLSD event(data); + event["value"] = ctrl->getValue(); + postEvent(event, "commit"); + }); + } + } + + //optional field to send additional specified events to the script + if (mKey.has("extra_events")) + { + //the first value is ctrl name, the second contains array of events to send + for (const auto &[name, data] : llsd::inMap(mKey["extra_events"])) + { + for (const auto &event : llsd::inArray(data)) + { + registerCallback(name, event); + } + } + } + + //send pump name to the script after the floater is built + postEvent(llsd::map("command_name", mDispatchListener.getPumpName()), "post_build"); + + return true; +} + +void LLLuaFloater::onClose(bool app_quitting) +{ + postEvent(llsd::map("app_quitting", app_quitting), "floater_close"); +} + +bool event_is(const std::string &event_name, const std::string &list_event) +{ + llassert(EVENT_LIST.find(list_event) != EVENT_LIST.end()); + return (event_name == list_event); +} + +void LLLuaFloater::registerCallback(const std::string &ctrl_name, const std::string &event) +{ + LLUICtrl *ctrl = getChild<LLUICtrl>(ctrl_name); + if (!ctrl) return; + + LLSD data; + data["ctrl_name"] = ctrl_name; + data["event"] = event; + + auto mouse_event_cb = [this, data](LLUICtrl *ctrl, const LLSD ¶m) { post(data); }; + + auto mouse_event_coords_cb = [this, data](LLUICtrl *ctrl, S32 x, S32 y, MASK mask) + { + LLSD event(data); + post(event.with("x", x).with("y", y)); + }; + + auto post_with_value = [this, data](LLSD value) + { + LLSD event(data); + post(event.with("value", value)); + }; + + if (event_is(event, "mouse_enter")) + { + ctrl->setMouseEnterCallback(mouse_event_cb); + } + else if (event_is(event, "mouse_leave")) + { + ctrl->setMouseLeaveCallback(mouse_event_cb); + } + else if (event_is(event, "mouse_down")) + { + ctrl->setMouseDownCallback(mouse_event_coords_cb); + } + else if (event_is(event, "mouse_up")) + { + ctrl->setMouseUpCallback(mouse_event_coords_cb); + } + else if (event_is(event, "right_mouse_down")) + { + ctrl->setRightMouseDownCallback(mouse_event_coords_cb); + } + else if (event_is(event, "right_mouse_up")) + { + ctrl->setRightMouseUpCallback(mouse_event_coords_cb); + } + else if (event_is(event, "double_click")) + { + LLScrollListCtrl *list = dynamic_cast<LLScrollListCtrl *>(ctrl); + if (list) + { + list->setDoubleClickCallback( [post_with_value, list](){ post_with_value(LLSD(list->getCurrentID())); }); + } + else + { + ctrl->setDoubleClickCallback(mouse_event_coords_cb); + } + } + else if (event_is(event, "keystroke")) + { + LLTextEditor* text_editor = dynamic_cast<LLTextEditor*>(ctrl); + if (text_editor) + { + text_editor->setKeystrokeCallback([post_with_value](LLTextEditor *editor) { post_with_value(editor->getValue()); }); + } + LLLineEditor* line_editor = dynamic_cast<LLLineEditor*>(ctrl); + if (line_editor) + { + line_editor->setKeystrokeCallback([post_with_value](LLLineEditor *editor, void* userdata) { post_with_value(editor->getValue()); }, NULL); + } + } + else + { + LL_WARNS("LuaFloater") << "Can't register callback for unknown event: " << event << " , control: " << ctrl_name << LL_ENDL; + } +} + +void LLLuaFloater::post(const LLSD &data) +{ + // send event data to the script signed with ["reqid"] key + LLSD stamped_data(data); + mReqID.stamp(stamped_data); + LLEventPumps::instance().obtain(mReplyPumpName).post(stamped_data); +} + +void LLLuaFloater::postEvent(LLSD data, const std::string &event_name) +{ + llassert(EVENT_LIST.find(event_name) != EVENT_LIST.end()); + post(data.with("event", event_name)); +} + +void LLLuaFloater::showLuaFloater(const LLSD &data) +{ + fsyspath fs_path(data["xml_path"].asString()); + std::string path = fs_path.lexically_normal().u8string(); + if (!fs_path.is_absolute()) + { + std::string lib_path = gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua"); + path = (fsyspath(lib_path) / path).u8string(); + } + + LLLuaFloater *floater = new LLLuaFloater(data); + floater->buildFromFile(path); + floater->openFloater(floater->getKey()); +} + +LLSD LLLuaFloater::getEventsData() +{ + LLSD event_data; + for (auto &it : EVENT_LIST) + { + event_data.append(it); + } + return event_data; +} diff --git a/indra/llui/llluafloater.h b/indra/llui/llluafloater.h new file mode 100644 index 0000000000..ccc3ccb39b --- /dev/null +++ b/indra/llui/llluafloater.h @@ -0,0 +1,54 @@ +/** + * @file llluafloater.h + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLLUAFLOATER_H +#define LL_LLLUAFLOATER_H + +#include "llfloater.h" +#include "lleventdispatcher.h" +#include "llevents.h" + +class LLLuaFloater : public LLFloater +{ +public: + LLLuaFloater(const LLSD &key); + BOOL postBuild(); + virtual ~LLLuaFloater(); + + void registerCallback(const std::string &ctrl_name, const std::string &event); + void onClose(bool app_quitting); + + void post(const LLSD &data); + void postEvent(LLSD data, const std::string &event); + static void showLuaFloater(const LLSD &data); + static LLSD getEventsData(); + +private: + LLReqID mReqID; + LLDispatchListener mDispatchListener; + + std::string mReplyPumpName; +}; +#endif diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 5c51cf8465..93d5e30ae1 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -562,14 +562,15 @@ public: // add a context menu branch BOOL appendContextSubMenu(LLMenuGL *menu); + // Add the menu item to this menu. + virtual BOOL append( LLMenuItemGL* item ); + + // add a menu - this will create a cascading menu + virtual BOOL appendMenu(LLMenuGL *menu); + protected: void createSpilloverBranch(); void cleanupSpilloverBranch(); - // Add the menu item to this menu. - virtual BOOL append( LLMenuItemGL* item ); - - // add a menu - this will create a cascading menu - virtual BOOL appendMenu( LLMenuGL* menu ); // Used in LLContextMenu and in LLTogleableMenu // to add an item of context menu branch @@ -805,9 +806,10 @@ public: void resetMenuTrigger() { mAltKeyTrigger = FALSE; } + // add a menu - this will create a drop down menu. + virtual BOOL appendMenu(LLMenuGL *menu); + private: - // add a menu - this will create a drop down menu. - virtual BOOL appendMenu( LLMenuGL* menu ); // rearrange the child rects so they fit the shape of the menu // bar. virtual void arrange( void ); diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index a247e8700a..c0eb0f1e8b 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1582,7 +1582,8 @@ void LLTextEditor::cleanStringForPaste(LLWString & clean_string) } -void LLTextEditor::pasteTextWithLinebreaks(LLWString & clean_string) +template <> +void LLTextEditor::pasteTextWithLinebreaks<LLWString>(const LLWString & clean_string) { std::basic_string<llwchar>::size_type start = 0; std::basic_string<llwchar>::size_type pos = clean_string.find('\n',start); diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 521405ec25..658bfb9295 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -34,6 +34,7 @@ #include "llstyle.h" #include "lleditmenuhandler.h" #include "llviewborder.h" // for params +#include "llstring.h" #include "lltextbase.h" #include "lltextvalidate.h" @@ -249,7 +250,9 @@ protected: // Undoable operations void addChar(llwchar c); // at mCursorPos S32 addChar(S32 pos, llwchar wc); +public: void addLineBreakChar(BOOL group_together = FALSE); +protected: S32 overwriteChar(S32 pos, llwchar wc); void removeChar(); S32 removeChar(S32 pos); @@ -301,10 +304,19 @@ private: // // Methods // - void pasteHelper(bool is_primary); + void pasteHelper(bool is_primary); void cleanStringForPaste(LLWString & clean_string); - void pasteTextWithLinebreaks(LLWString & clean_string); +public: + template <typename STRINGTYPE> + void pasteTextWithLinebreaks(const STRINGTYPE& clean_string) + { + pasteTextWithLinebreaks<LLWString>(ll_convert(clean_string)); + } + template <> + void pasteTextWithLinebreaks<LLWString>(const LLWString & clean_string); + +private: void onKeyStroke(); // Concrete TextCmd sub-classes used by the LLTextEditor base class |