summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
authorMaxim Nikolenko <maximnproductengine@lindenlab.com>2024-08-02 18:47:44 +0300
committerGitHub <noreply@github.com>2024-08-02 18:47:44 +0300
commitd6abce3968925c5cb58c11f1c6fc936605f55c57 (patch)
tree16142170d61f2ecf0ad8a4d6194a152658527589 /indra/newview
parent2390b0d623a09c0ec4f46fa10567bb02b7d49cfe (diff)
parent365030aa33d991006e093557cb7ce93d18ea2e4e (diff)
Merge pull request #2047 from secondlife/lua-top-menu
Lua api for adding new menu items to the Top menu
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/lluilistener.cpp162
-rw-r--r--indra/newview/lluilistener.h13
-rw-r--r--indra/newview/scripts/lua/require/UI.lua34
-rw-r--r--indra/newview/scripts/lua/test_callables.lua6
-rw-r--r--indra/newview/scripts/lua/test_top_menu.lua34
5 files changed, 244 insertions, 5 deletions
diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp
index 4afd7f1766..b81859a764 100644
--- a/indra/newview/lluilistener.cpp
+++ b/indra/newview/lluilistener.cpp
@@ -34,10 +34,13 @@
// std headers
// external library headers
// other Linden headers
+#include "llmenugl.h"
#include "llui.h" // getRootView(), resolvePath()
#include "lluictrl.h"
#include "llerror.h"
+extern LLMenuBarGL* gMenuBarView;
+
#define THROTTLE_PERIOD 1.5 // required seconds between throttled functions
#define MIN_THROTTLE 0.5
@@ -52,11 +55,48 @@ LLUIListener::LLUIListener():
&LLUIListener::call,
llsd::map("function", LLSD(), "reply", LLSD()));
+ add("callables",
+ "Return a list [\"callables\"] of dicts {name, access} of functions registered to\n"
+ "invoke with \"call\".\n"
+ "access has values \"allow\", \"block\" or \"throttle\".",
+ &LLUIListener::callables,
+ llsd::map("reply", LLSD::String()));
+
add("getValue",
"For the UI control identified by the path in [\"path\"], return the control's\n"
"current value as [\"value\"] reply.",
&LLUIListener::getValue,
llsd::map("path", LLSD(), "reply", LLSD()));
+
+ add("getParents",
+ "List names of Top menus suitable for passing as \"parent_menu\"",
+ &LLUIListener::getParents,
+ llsd::map("reply", LLSD::String()));
+
+ LLSD required_args = llsd::map("name", LLSD(), "label", LLSD(), "reply", LLSD());
+ add("addMenu",
+ "Add new drop-down menu [\"name\"] with displayed [\"label\"] to the Top menu.",
+ &LLUIListener::addMenu,
+ required_args);
+
+ required_args.insert("parent_menu", LLSD());
+ add("addMenuBranch",
+ "Add new menu branch [\"name\"] with displayed [\"label\"]\n"
+ "to the [\"parent_menu\"] within the Top menu.",
+ &LLUIListener::addMenuBranch,
+ required_args);
+
+ add("addMenuItem",
+ "Add new menu item [\"name\"] with displayed [\"label\"]\n"
+ "and call-on-click UI function [\"func\"] with optional [\"param\"]\n"
+ "to the [\"parent_menu\"] within the Top menu.",
+ &LLUIListener::addMenuItem,
+ required_args.with("func", LLSD()));
+
+ add("addMenuSeparator",
+ "Add menu separator to the [\"parent_menu\"] within the Top menu.",
+ &LLUIListener::addMenuSeparator,
+ llsd::map("parent_menu", LLSD(), "reply", LLSD()));
}
typedef LLUICtrl::CommitCallbackInfo cb_info;
@@ -103,7 +143,43 @@ void LLUIListener::call(const LLSD& event)
(info->callback_func)(NULL, event["parameter"]);
}
-void LLUIListener::getValue(const LLSD&event) const
+void LLUIListener::callables(const LLSD& event) const
+{
+ Response response(LLSD(), event);
+
+ using Registry = LLUICtrl::CommitCallbackRegistry;
+ using Method = Registry::Registrar& (*)();
+ static Method registrars[] =
+ {
+ &Registry::defaultRegistrar,
+ &Registry::currentRegistrar,
+ };
+ LLSD list;
+ for (auto method : registrars)
+ {
+ auto& registrar{ (*method)() };
+ for (auto it = registrar.beginItems(), end = registrar.endItems(); it != end; ++it)
+ {
+ LLSD entry{ llsd::map("name", it->first) };
+ switch (it->second.handle_untrusted)
+ {
+ case LLUICtrl::CommitCallbackInfo::UNTRUSTED_ALLOW:
+ entry["access"] = "allow";
+ break;
+ case LLUICtrl::CommitCallbackInfo::UNTRUSTED_BLOCK:
+ entry["access"] = "block";
+ break;
+ case LLUICtrl::CommitCallbackInfo::UNTRUSTED_THROTTLE:
+ entry["access"] = "throttle";
+ break;
+ }
+ list.append(entry);
+ }
+ }
+ response["callables"] = list;
+}
+
+void LLUIListener::getValue(const LLSD& event) const
{
Response response(LLSD(), event);
@@ -120,3 +196,87 @@ void LLUIListener::getValue(const LLSD&event) const
response.error(stringize("UI control ", std::quoted(event["path"].asString()), " was not found"));
}
}
+
+void LLUIListener::getParents(const LLSD& event) const
+{
+ Response response(LLSD(), event);
+ response["parents"] = llsd::toArray(
+ *gMenuBarView->getChildList(),
+ [](auto childp) {return childp->getName(); });
+}
+
+LLMenuGL::Params get_params(const LLSD&event)
+{
+ LLMenuGL::Params item_params;
+ item_params.name = event["name"];
+ item_params.label = event["label"];
+ item_params.can_tear_off = true;
+ return item_params;
+}
+
+LLMenuGL* get_parent_menu(LLEventAPI::Response& response, const LLSD&event)
+{
+ LLMenuGL* parent_menu = gMenuBarView->findChildMenuByName(event["parent_menu"], true);
+ if(!parent_menu)
+ {
+ response.error(stringize("Parent menu ", std::quoted(event["parent_menu"].asString()), " was not found"));
+ }
+ return parent_menu;
+}
+
+void LLUIListener::addMenu(const LLSD&event) const
+{
+ Response response(LLSD(), event);
+ LLMenuGL::Params item_params = get_params(event);
+ if(!gMenuBarView->appendMenu(LLUICtrlFactory::create<LLMenuGL>(item_params)))
+ {
+ response.error(stringize("Menu ", std::quoted(event["name"].asString()), " was not added"));
+ }
+}
+
+void LLUIListener::addMenuBranch(const LLSD&event) const
+{
+ Response response(LLSD(), event);
+ if(LLMenuGL* parent_menu = get_parent_menu(response, event))
+ {
+ LLMenuGL::Params item_params = get_params(event);
+ if(!parent_menu->appendMenu(LLUICtrlFactory::create<LLMenuGL>(item_params)))
+ {
+ response.error(stringize("Menu branch ", std::quoted(event["name"].asString()), " was not added"));
+ }
+ }
+}
+
+void LLUIListener::addMenuItem(const LLSD&event) const
+{
+ Response response(LLSD(), event);
+ LLMenuItemCallGL::Params item_params;
+ item_params.name = event["name"];
+ item_params.label = event["label"];
+ LLUICtrl::CommitCallbackParam item_func;
+ item_func.function_name = event["func"];
+ if (event.has("param"))
+ {
+ item_func.parameter = event["param"];
+ }
+ item_params.on_click = item_func;
+ if(LLMenuGL* parent_menu = get_parent_menu(response, event))
+ {
+ if(!parent_menu->append(LLUICtrlFactory::create<LLMenuItemCallGL>(item_params)))
+ {
+ response.error(stringize("Menu item ", std::quoted(event["name"].asString()), " was not added"));
+ }
+ }
+}
+
+void LLUIListener::addMenuSeparator(const LLSD&event) const
+{
+ Response response(LLSD(), event);
+ if(LLMenuGL* parent_menu = get_parent_menu(response, event))
+ {
+ if(!parent_menu->addSeparator())
+ {
+ response.error("Separator was not added");
+ }
+ }
+}
diff --git a/indra/newview/lluilistener.h b/indra/newview/lluilistener.h
index 0df2afb3fe..671eb5f29b 100644
--- a/indra/newview/lluilistener.h
+++ b/indra/newview/lluilistener.h
@@ -39,12 +39,17 @@ class LLUIListener: public LLEventAPI
public:
LLUIListener();
-// FIXME These fields are intended to be private, changed here to support very hacky code in llluamanager.cpp
-public:
+private:
void call(const LLSD& event);
- void getValue(const LLSD&event) const;
+ void callables(const LLSD& event) const;
+ void getValue(const LLSD& event) const;
+ void getParents(const LLSD& event) const;
+
+ void addMenu(const LLSD&event) const;
+ void addMenuBranch(const LLSD&event) const;
+ void addMenuItem(const LLSD&event) const;
+ void addMenuSeparator(const LLSD&event) const;
- private:
F64 mLastUntrustedThrottle {0};
F64 mLastMinThrottle {0};
};
diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua
index 1eee4657f4..06b49c6269 100644
--- a/indra/newview/scripts/lua/require/UI.lua
+++ b/indra/newview/scripts/lua/require/UI.lua
@@ -14,6 +14,10 @@ function UI.call(func, parameter)
leap.request('UI', {op='call', ['function']=func, parameter=parameter})
end
+function UI.callables()
+ return leap.request('UI', {op='callables'}).callables
+end
+
function UI.getValue(path)
return leap.request('UI', {op='getValue', path=path})['value']
end
@@ -135,4 +139,34 @@ function UI.snapshot(...)
args.op = 'saveSnapshot'
return leap.request('LLViewerWindow', args).result
end
+
+-- ***************************************************************************
+-- Top menu
+-- ***************************************************************************
+
+function UI.addMenu(...)
+ local args = mapargs('name,label', ...)
+ args.op = 'addMenu'
+ return leap.request('UI', args)
+end
+
+function UI.addMenuBranch(...)
+ local args = mapargs('name,label,parent_menu', ...)
+ args.op = 'addMenuBranch'
+ return leap.request('UI', args)
+end
+
+-- see UI.callables() for valid values of 'func'
+function UI.addMenuItem(...)
+ local args = mapargs('name,label,parent_menu,func,param', ...)
+ args.op = 'addMenuItem'
+ return leap.request('UI', args)
+end
+
+function UI.addMenuSeparator(...)
+ local args = mapargs('parent_menu', ...)
+ args.op = 'addMenuSeparator'
+ return leap.request('UI', args)
+end
+
return UI
diff --git a/indra/newview/scripts/lua/test_callables.lua b/indra/newview/scripts/lua/test_callables.lua
new file mode 100644
index 0000000000..1bee062db8
--- /dev/null
+++ b/indra/newview/scripts/lua/test_callables.lua
@@ -0,0 +1,6 @@
+startup=require 'startup'
+UI=require 'UI'
+startup.wait('STATE_LOGIN_WAIT')
+for _, cbl in pairs(UI.callables()) do
+ print(`{cbl.name} ({cbl.access})`)
+end
diff --git a/indra/newview/scripts/lua/test_top_menu.lua b/indra/newview/scripts/lua/test_top_menu.lua
new file mode 100644
index 0000000000..780a384c92
--- /dev/null
+++ b/indra/newview/scripts/lua/test_top_menu.lua
@@ -0,0 +1,34 @@
+UI = require 'UI'
+
+--Add new drop-down 'LUA Menu' to the Top menu.
+local MENU_NAME = "lua_menu"
+UI.addMenu{name=MENU_NAME,label="LUA Menu"}
+
+--Add two new menu items to the 'LUA Menu': 'Debug console' and 'Scripts'
+UI.addMenuItem{name="lua_debug",label="Debug console",
+ param="lua_debug",
+ func="Floater.ToggleOrBringToFront",
+ parent_menu=MENU_NAME}
+
+UI.addMenuItem{name="lua_scripts",label="Scripts",
+ param="lua_scripts",
+ func="Floater.ToggleOrBringToFront",
+ parent_menu=MENU_NAME}
+
+--Add menu separator to the 'LUA Menu' under added menu items
+UI.addMenuSeparator{parent_menu=MENU_NAME}
+
+--Add two new menu branch 'About...' to the 'LUA Menu'
+local BRANCH_NAME = "about_branch"
+UI.addMenuBranch{name="about_branch",label="About...",parent_menu=MENU_NAME}
+
+--Add two new menu items to the 'About...' branch
+UI.addMenuItem{name="lua_info",label="Lua...",
+ param="https://www.lua.org/about.html",
+ func="Advanced.ShowURL",
+ parent_menu=BRANCH_NAME}
+
+UI.addMenuItem{name="lua_info",label="Luau...",
+ param="https://luau-lang.org/",
+ func="Advanced.ShowURL",
+ parent_menu=BRANCH_NAME}