summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
authornat-goodspeed <nat@lindenlab.com>2024-08-28 10:23:53 -0400
committerGitHub <noreply@github.com>2024-08-28 10:23:53 -0400
commit6f454ad8366ed33bbe199c3fc3ed69e6d3448cec (patch)
tree05f856f85c00194c6c43ca23cacb7021a50ffa33 /indra/newview
parentc225b44a59ddd6b84105ace4181b3ca9f7439bfa (diff)
parent7b21acd39745d265548eeb62d687cde9febb1f7a (diff)
Merge pull request #2416 from secondlife/lua-lazymod
Allow UI to have lazily-loaded submodules.
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/scripts/lua/require/LLChatListener.lua2
-rw-r--r--indra/newview/scripts/lua/require/UI.lua7
-rw-r--r--indra/newview/scripts/lua/require/UI/Floater.lua (renamed from indra/newview/scripts/lua/require/Floater.lua)0
-rw-r--r--indra/newview/scripts/lua/require/UI/popup.lua82
-rw-r--r--indra/newview/scripts/lua/require/popup.lua53
-rw-r--r--indra/newview/scripts/lua/require/util.lua65
-rw-r--r--indra/newview/scripts/lua/test_LLAppearance.lua4
-rw-r--r--indra/newview/scripts/lua/test_camera_control.lua4
-rw-r--r--indra/newview/scripts/lua/test_group_chat.lua7
-rw-r--r--indra/newview/scripts/lua/test_luafloater_demo.lua6
-rw-r--r--indra/newview/scripts/lua/test_luafloater_gesture_list.lua4
-rw-r--r--indra/newview/scripts/lua/test_luafloater_speedometer.lua8
-rw-r--r--indra/newview/scripts/lua/test_popup.lua12
-rw-r--r--indra/newview/scripts/lua/test_toolbars.lua6
14 files changed, 169 insertions, 91 deletions
diff --git a/indra/newview/scripts/lua/require/LLChatListener.lua b/indra/newview/scripts/lua/require/LLChatListener.lua
index 428dca881e..82b28966ce 100644
--- a/indra/newview/scripts/lua/require/LLChatListener.lua
+++ b/indra/newview/scripts/lua/require/LLChatListener.lua
@@ -23,7 +23,7 @@ function LLChatListener:handleMessages(event_data)
end
function LLChatListener:start()
- waitfor = leap.WaitFor:new(-1, self.name)
+ waitfor = leap.WaitFor(-1, self.name)
function waitfor:filter(pump, data)
if pump == "LLNearbyChat" then
return data
diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua
index 464e6547ea..bbcae3514a 100644
--- a/indra/newview/scripts/lua/require/UI.lua
+++ b/indra/newview/scripts/lua/require/UI.lua
@@ -1,10 +1,13 @@
-- Engage the viewer's UI
local leap = require 'leap'
-local Timer = (require 'timers').Timer
local mapargs = require 'mapargs'
+local Timer = (require 'timers').Timer
+local util = require 'util'
-local UI = {}
+-- Allow lazily accessing UI submodules on demand, e.g. a reference to
+-- UI.Floater lazily loads the UI/Floater module.
+local UI = util.submoduledir({}, 'UI')
-- ***************************************************************************
-- registered menu actions
diff --git a/indra/newview/scripts/lua/require/Floater.lua b/indra/newview/scripts/lua/require/UI/Floater.lua
index d057a74386..d057a74386 100644
--- a/indra/newview/scripts/lua/require/Floater.lua
+++ b/indra/newview/scripts/lua/require/UI/Floater.lua
diff --git a/indra/newview/scripts/lua/require/UI/popup.lua b/indra/newview/scripts/lua/require/UI/popup.lua
new file mode 100644
index 0000000000..8ccf3b87f3
--- /dev/null
+++ b/indra/newview/scripts/lua/require/UI/popup.lua
@@ -0,0 +1,82 @@
+local leap = require 'leap'
+local mapargs = require 'mapargs'
+local util = require 'util'
+
+-- notification is any name defined in notifications.xml as
+-- <notification name=>
+-- vars is a table providing values for [VAR] substitution keys in the
+-- notification body
+-- payload prepopulates the response table
+-- wait=false means fire and forget, returning nil
+-- wait=true waits for user response:
+-- * If the viewer returns a table containing exactly one key=true pair,
+-- popup() returns just that key. If the key is a string containing an
+-- underscore, e.g. 'OK_okcancelbuttons', it's truncated at the first
+-- underscore, e.g. 'OK'.
+-- * Otherwise the viewer's response is returned unchanged. To suppress the
+-- above transformations, pass a non-empty payload table; this will cause
+-- the viewer to return a table with at least two keys.
+local popup = util.setmetamethods{
+ -- this gets called when a consumer calls popup(notification, vars, payload)
+ __call = function(self, ...)
+ local args = mapargs('notification,vars,payload,wait', ...)
+ -- we use convenience argument names different from 'LLNotifications'
+ -- listener
+ newargs = {op='requestAdd',
+ name=args.notification,
+ substitutions=args.vars,
+ payload=args.payload}
+ -- Specifically test (wait == false), NOT (not wait), because we treat
+ -- nil (omitted, default true) differently than false (explicitly
+ -- DON'T wait).
+ if args.wait == false then
+ leap.send('LLNotifications', newargs)
+ else
+ local response = leap.request('LLNotifications', newargs).response
+ -- response is typically a table. It might have multiple keys,
+ -- e.g. if caller passed non-empty payload. In that case, just
+ -- return the whole thing.
+ if type(response) ~= 'table' then
+ return response
+ end
+ -- get first key=value pair, if any
+ local key, value = next(response)
+ if (not key) or next(response, key) then
+ -- key == nil means response is empty
+ -- next(response, non-nil first key) ~= nil means at least two keys
+ return response
+ end
+ -- Here response is a table containing exactly one key. The
+ -- notifications system typically returns a table of the form
+ -- {OK_okcancelbuttons=true}, which is tricky to test for because it
+ -- varies with each set of buttons.
+ if value == true then
+ -- change {key=true} to plain key
+ response = key
+ if type(response) == 'string' then
+ -- change 'OK_okcancelbuttons' to plain 'OK'
+ response = string.split(response, '_')[1]
+ end
+ end
+ return response
+ end
+ end
+}
+
+function popup:alert(message, payload)
+ return self('GenericAlert', {MESSAGE=message, payload=payload})
+end
+
+function popup:alertOK(message, payload)
+ return self('GenericAlertOK', {MESSAGE=message, payload=payload})
+end
+
+function popup:alertYesCancel(message, payload)
+ return self('GenericAlertYesCancel', {MESSAGE=message, payload=payload})
+end
+
+function popup:tip(message, payload)
+ self{'SystemMessageTip', {MESSAGE=message, payload=payload}, wait=false}
+end
+
+return popup
diff --git a/indra/newview/scripts/lua/require/popup.lua b/indra/newview/scripts/lua/require/popup.lua
deleted file mode 100644
index 3aaadf85ba..0000000000
--- a/indra/newview/scripts/lua/require/popup.lua
+++ /dev/null
@@ -1,53 +0,0 @@
-local leap = require 'leap'
-local mapargs = require 'mapargs'
-
--- notification is any name defined in notifications.xml as
--- <notification name=>
--- vars is a table providing values for [VAR] substitution keys in the
--- notification body
--- payload prepopulates the response table
--- wait=false means fire and forget, otherwise wait for user response
-local popup_meta = {
- -- setting this function as getmetatable(popup).__call() means this gets
- -- called when a consumer calls popup(notification, vars, payload)
- __call = function(self, ...)
- local args = mapargs('notification,vars,payload,wait', ...)
- -- we use convenience argument names different from 'LLNotifications'
- -- listener
- args.name = args.notification
- args.notification = nil
- args.substitutions = args.vars
- args.vars = nil
- local wait = args.wait
- args.wait = nil
- args.op = 'requestAdd'
- -- Specifically test (wait == false), NOT (not wait), because we treat
- -- nil (omitted, default true) differently than false (explicitly
- -- DON'T wait).
- if wait == false then
- leap.send('LLNotifications', args)
- else
- return leap.request('LLNotifications', args).response
- end
- end
-}
-
-local popup = setmetatable({}, popup_meta)
-
-function popup:alert(message)
- return self('GenericAlert', {MESSAGE=message})
-end
-
-function popup:alertOK(message)
- return self('GenericAlertOK', {MESSAGE=message})
-end
-
-function popup:alertYesCancel(message)
- return self('GenericAlertYesCancel', {MESSAGE=message})
-end
-
-function popup:tip(message)
- self{'SystemMessageTip', {MESSAGE=message}, wait=false}
-end
-
-return popup
diff --git a/indra/newview/scripts/lua/require/util.lua b/indra/newview/scripts/lua/require/util.lua
index bfbfc8637c..40737a159a 100644
--- a/indra/newview/scripts/lua/require/util.lua
+++ b/indra/newview/scripts/lua/require/util.lua
@@ -15,16 +15,9 @@ local util = {}
-- util.classctor(MyClass, MyClass.construct)
-- return MyClass
function util.classctor(class, ctor)
- -- get the metatable for the passed class
- local mt = getmetatable(class)
- if mt == nil then
- -- if it doesn't already have a metatable, then create one
- mt = {}
- setmetatable(class, mt)
- end
- -- now that class has a metatable, set its __call method to the specified
- -- constructor method (class.new if not specified)
- mt.__call = ctor or class.new
+ -- set class's __call metamethod to the specified constructor function
+ -- (class.new if not specified)
+ util.setmetamethods{class, __call=(ctor or class.new)}
end
-- check if array-like table contains certain value
@@ -66,4 +59,56 @@ function util.equal(t1, t2)
return util.empty(temp)
end
+-- Find or create the metatable for a specified table (a new empty table if
+-- omitted), and to that metatable assign the specified keys.
+-- Setting multiple keys at once is more efficient than a function to set only
+-- one at a time, e.g. setametamethod().
+-- t = util.setmetamethods{__index=readfunc, __len=lenfunc}
+-- returns a new table with specified metamethods __index, __len
+-- util.setmetamethods{t, __call=action}
+-- finds or creates the metatable for existing table t and sets __call
+-- util.setmetamethods{table=t, __call=action}
+-- same as util.setmetamethods{t, __call=action}
+function util.setmetamethods(specs)
+ -- first determine the target table
+ assert(not (specs.table and specs[1]),
+ "Pass setmetamethods table either as positional or table=, not both")
+ local t = specs.table or specs[1] or {}
+ -- remove both ways of specifying table, leaving only the metamethods
+ specs.table = nil
+ specs[1] = nil
+ local mt = getmetatable(t)
+ if not mt then
+ -- t doesn't already have a metatable: just set specs
+ setmetatable(t, specs)
+ else
+ -- t already has a metatable: copy specs into it
+ local key, value
+ for key, value in pairs(specs) do
+ mt[key] = value
+ end
+ end
+ -- having set or enriched t's metatable, return t
+ return t
+end
+
+-- On the passed module (i.e. table), set an __index metamethod such that
+-- referencing module.submodule lazily requires(path/submodule).
+-- The loaded submodule is cached in the module table so it need not be passed
+-- to require() again.
+-- 'path', like any require() string, can be relative to LuaRequirePath.
+-- Returns the enriched module, permitting e.g.
+-- mymod = util.submoduledir({}, 'mymod')
+function util.submoduledir(module, path)
+ return util.setmetamethods{
+ module,
+ __index=function(t, key)
+ local mod = require(`{path}/{key}`)
+ -- cache the submodule
+ t[key] = mod
+ return mod
+ end
+ }
+end
+
return util
diff --git a/indra/newview/scripts/lua/test_LLAppearance.lua b/indra/newview/scripts/lua/test_LLAppearance.lua
index 5ddd9f15ff..a97ec4e0ca 100644
--- a/indra/newview/scripts/lua/test_LLAppearance.lua
+++ b/indra/newview/scripts/lua/test_LLAppearance.lua
@@ -1,7 +1,7 @@
-local Floater = require 'Floater'
local LLAppearance = require 'LLAppearance'
local startup = require 'startup'
local inspect = require 'inspect'
+local UI = require 'UI'
local SHOW_OUTFITS = true
local SELECTED_OUTFIT_ID = {}
@@ -15,7 +15,7 @@ local outfits_title = 'Outfits'
local wear_lbl = 'Wear item'
local detach_lbl = 'Detach item'
-local flt = Floater:new(
+local flt = UI.Floater(
"luafloater_outfits_list.xml",
{outfits_list = {"double_click"}})
diff --git a/indra/newview/scripts/lua/test_camera_control.lua b/indra/newview/scripts/lua/test_camera_control.lua
index 9b35cdf8cd..7ac0986ee6 100644
--- a/indra/newview/scripts/lua/test_camera_control.lua
+++ b/indra/newview/scripts/lua/test_camera_control.lua
@@ -1,8 +1,8 @@
-local Floater = require 'Floater'
local LLAgent = require 'LLAgent'
local startup = require 'startup'
+local UI = require 'UI'
-local flt = Floater('luafloater_camera_control.xml')
+local flt = UI.Floater('luafloater_camera_control.xml')
function getValue(ctrl_name)
return flt:request({action="get_value", ctrl_name=ctrl_name}).value
diff --git a/indra/newview/scripts/lua/test_group_chat.lua b/indra/newview/scripts/lua/test_group_chat.lua
index eaff07ed14..373411c26c 100644
--- a/indra/newview/scripts/lua/test_group_chat.lua
+++ b/indra/newview/scripts/lua/test_group_chat.lua
@@ -1,15 +1,14 @@
LLChat = require 'LLChat'
LLAgent = require 'LLAgent'
-popup = require 'popup'
+UI = require 'UI'
-local OK = 'OK_okcancelbuttons'
local GROUPS = LLAgent.getGroups()
-- Choose one of the groups randomly and send group message
math.randomseed(os.time())
group_info = GROUPS[math.random(#GROUPS)]
LLChat.startGroupChat(group_info.id)
-response = popup:alertYesCancel('Started group chat with ' .. group_info.name .. ' group. Send greetings?')
-if next(response) == OK then
+response = UI.popup:alertYesCancel('Started group chat with ' .. group_info.name .. ' group. Send greetings?')
+if response == 'OK' then
LLChat.sendGroupIM('Greetings', group_info.id)
end
diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua
index 3903d01e65..2158134511 100644
--- a/indra/newview/scripts/lua/test_luafloater_demo.lua
+++ b/indra/newview/scripts/lua/test_luafloater_demo.lua
@@ -1,8 +1,8 @@
-local Floater = require 'Floater'
local leap = require 'leap'
local startup = require 'startup'
+local UI = require 'UI'
-local flt = Floater(
+local flt = UI.Floater(
'luafloater_demo.xml',
{show_time_lbl = {"right_mouse_down", "double_click"}})
@@ -10,7 +10,7 @@ local flt = Floater(
function flt:handleEvents(event_data)
self:post({action="add_text", ctrl_name="events_editor", value = event_data})
-- forward the call to base-class handleEvents()
- return Floater.handleEvents(self, event_data)
+ return UI.Floater.handleEvents(self, event_data)
end
function flt:commit_disable_ctrl(event_data)
diff --git a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua
index bd397ef2a6..5f929c0d0c 100644
--- a/indra/newview/scripts/lua/test_luafloater_gesture_list.lua
+++ b/indra/newview/scripts/lua/test_luafloater_gesture_list.lua
@@ -1,8 +1,8 @@
-local Floater = require 'Floater'
local LLGesture = require 'LLGesture'
local startup = require 'startup'
+local UI = require 'UI'
-local flt = Floater(
+local flt = UI.Floater(
"luafloater_gesture_list.xml",
{gesture_list = {"double_click"}})
diff --git a/indra/newview/scripts/lua/test_luafloater_speedometer.lua b/indra/newview/scripts/lua/test_luafloater_speedometer.lua
index af7189a2cb..2cdd41fd7e 100644
--- a/indra/newview/scripts/lua/test_luafloater_speedometer.lua
+++ b/indra/newview/scripts/lua/test_luafloater_speedometer.lua
@@ -1,10 +1,10 @@
-local Floater = require 'Floater'
local leap = require 'leap'
-local popup = require 'popup'
local startup = require 'startup'
local Timer = (require 'timers').Timer
+local UI = require 'UI'
+local popup = UI.popup
local max_speed = 0
-local flt = Floater("luafloater_speedometer.xml")
+local flt = UI.Floater("luafloater_speedometer.xml")
startup.wait('STATE_STARTED')
local timer
@@ -25,7 +25,7 @@ end
msg = 'Are you sure you want to run this "speedometer" script?'
response = popup:alertYesCancel(msg)
-if response.OK_okcancelbuttons then
+if response == 'OK' then
flt:show()
timer = Timer(1, idle, true) -- iterate
end
diff --git a/indra/newview/scripts/lua/test_popup.lua b/indra/newview/scripts/lua/test_popup.lua
index e48f89c3a7..7a11895669 100644
--- a/indra/newview/scripts/lua/test_popup.lua
+++ b/indra/newview/scripts/lua/test_popup.lua
@@ -1,6 +1,10 @@
-popup = require 'popup'
+UI = require 'UI'
+popup = UI.popup
+startup = require 'startup'
+
+startup.wait('STATE_STARTED')
response = popup:alert('This just has a Close button')
-response = popup:alertOK(string.format('You said "%s", is that OK?', next(response)))
-response = popup:alertYesCancel(string.format('You said "%s"', next(response)))
-popup:tip(string.format('You said "%s"', next(response)))
+response = popup:alertOK(string.format('You said "%s", is that OK?', response))
+response = popup:alertYesCancel(string.format('You said "%s"', response))
+popup:tip(string.format('You said "%s"', response))
diff --git a/indra/newview/scripts/lua/test_toolbars.lua b/indra/newview/scripts/lua/test_toolbars.lua
index 9a832c5644..7683fca8a3 100644
--- a/indra/newview/scripts/lua/test_toolbars.lua
+++ b/indra/newview/scripts/lua/test_toolbars.lua
@@ -1,13 +1,11 @@
-popup = require 'popup'
UI = require 'UI'
-local OK = 'OK_okcancelbuttons'
local BUTTONS = UI.getToolbarBtnNames()
local TOOLBARS = {'left','right','bottom'}
-- Clear the toolbars and then add the toolbar buttons to the random toolbar
-response = popup:alertYesCancel('Toolbars will be randomly reshuffled. Proceed?')
-if next(response) == OK then
+response = UI.popup:alertYesCancel('Toolbars will be randomly reshuffled. Proceed?')
+if response == 'OK' then
UI.clearAllToolbars()
math.randomseed(os.time())