Skip to content

Instantly share code, notes, and snippets.

@badosu
Last active November 1, 2025 06:00
Show Gist options
  • Select an option

  • Save badosu/13b68ffe460aff233ce735e0817dd58b to your computer and use it in GitHub Desktop.

Select an option

Save badosu/13b68ffe460aff233ce735e0817dd58b to your computer and use it in GitHub Desktop.
local widget = widget ---@type Widget
-- mouse_trigger (left|right|mmb) [halt_key] | <action>
-- where halt_key (optional, default false) halts the key press chain on trigger
-- best used with chain_actions widget for halting engine actions, e.g.:
-- bind Any+ctrl mouse_trigger left | chain force select ...
function widget:GetInfo()
return {
name = "Mouse Trigger Actions",
desc = "Allows lua actions to be called from mouse triggers",
author = "whoever_made_this_work,badosu",
date = "October 2025",
license = "GNU GPL, v2 or later",
-- decision on layer:
-- - 0 or lower for overriding game mousepress
-- - highest to allow game to process the mousepress before action
layer = 9999,
enabled = true,
handler = true,
}
end
local activeModifiers = {
[1] = {},
[2] = {},
[3] = {},
}
-- not sure btw, mapping from argument name to button in MousePress
local mouseButtonNameIndex = {
left = 1,
mmb = 2,
right = 3,
}
-- FIXME: This does not handle multiple releases well, e.g.:
-- bind any+1 mouse_trigger left | say hi
-- press(1) -> active
-- press(ctrl) -> active
-- release(ctrl) -> inactive
-- Should be active since 1 is still pressed
-- Should be ok for most applications
-- NOTE: Define:
--array_bisect(array, index)
--array_contains(array, entry)
--array_add_unique(array, key, value)
--array_remove_unique(array, key)
--array_get_unique(array, key)
--e.g.:
--array_bisect({1, 2, 3, 4}, 2) -> {1}, {2, 3, 4}
--array_contains({"halt"}, "halt") -> true
--array_contains({"halt"}, "asd") -> false
--local entries = {}
--array_add_unique(entries, "1", 1) -> entries == {{"1", 1}}
--array_add_unique(entries, "1", 1) -> entries == {{"1", 1}}
--array_get_unique("1") -> 1
--array_remove_unique(entries, "1") -> entries == {}
local function mouseModifierHandler(_, extra, _, _, _, isRelease)
-- parses "left | asd bla | bla"
-- actionExtra = "left"
-- rest = "asd bla | bla"
local actionExtra, rest = string.match(extra, "^(%S+)%s*|%s*(.+)")
if rest == nil then
Spring.Echo("<Trigger Actions> Error: invalid argument", extra)
return false
end
local trigger, actionOpts = array_bisect(string.split(actionExtra, " "), 2)
trigger = mouseButtonNameIndex[trigger[1]]
if not activeModifiers[trigger] then
Spring.Echo("<Trigger Actions> Error: invalid mouse argument", actionExtra)
return false
end
if isRelease then
array_remove_unique(activeModifiers[trigger], rest)
return
end
local haltKey = array_contains(actionOpts, "halt_key")
-- asd zxc blabla // xxx
-- cmd = asd
-- extra = blabla
local cmd, cmdExtra = string.match(rest, "^(%w+)[%s]*(.*)(//.*)")
local keyAction = {
command = cmd,
extra = cmdExtra,
}
-- Ensure uniqueness of entries, keypresses can retrigger same entry
-- e.g. alt+1 triggers bind Any+1 two times on press(1), press(alt)
array_add_unique(activeModifiers[trigger], rest, keyAction)
-- we halt when halt_key is passed (i.e. we dont allow other actions to process)
return haltKey
end
-- function widget:MousePress(x, y, button)
function widget:MousePress(_, _, button)
local modifiers = activeModifiers[button]
if not next(modifiers) then
return
end
local actions = {}
for _, action in ipairs(modifiers) do
table.insert(actions, action)
end
widgetHandler.actionHandler:KeyAction(true, nil, nil, nil, _, { actions })
end
function widget:Initialize()
widgetHandler.actionHandler:AddAction(self, "mouse_trigger", mouseModifierHandler, nil, "pr")
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment