Add on_key_enter callback and close_on_enter

Closes https://gitlab.com/luk3yx/minetest-flow/-/issues/4
This commit is contained in:
luk3yx 2025-02-21 17:16:54 +13:00
parent 7bad88cdcd
commit e3dc6d6750
3 changed files with 80 additions and 10 deletions

View File

@ -622,4 +622,42 @@ end)
If multiple `on_quit` callbacks are specified in different elements, they will
all get called.
</details><details>
<summary><b>Handling enter keypresses in fields</b></summary>
`gui.Field` and `gui.Pwdfield` support an `on_key_enter` callback that gets
called if enter is pressed:
```lua
local form = flow.make_gui(function(player, ctx)
return gui.VBox{
gui.Field{
label = "Press enter!",
name = "field",
on_key_enter = function(player, ctx)
core.chat_send_player(player:get_player_name(),
"Field value: " .. dump(ctx.form.field))
end,
-- You can also specify close_on_enter to close the form when enter
-- is pressed.
close_on_enter = true,
},
}
end)
```
Notes:
- If you're using this callback, please make sure there's some other way to
trigger the enter action (like a button) to support older flow versions and
in case I replace this API with a better one in the future.
- If you want recent mobile clients to call this callback when editing text,
add `enter_after_edit = true` to the field definition.
- The similarly named `on_event` gets called whenever the client submits the
field to the server, which could be at any time, and is not very useful, but
is still supported for compatibility (and there may be uses for it, such as
sanitising field values). Be careful not to accidentally use the wrong
callback.
</details>

View File

@ -200,11 +200,13 @@ local function insert_shorthand_elements(tree)
if node.type == "container" or node.type == "scroll_container" then
insert_shorthand_elements(node)
elseif node.type == "field" then
table.insert(tree, i, {
type = 'field_close_on_enter',
name = node.name,
close_on_enter = false,
})
if not node.close_on_enter then
table.insert(tree, i, {
type = 'field_close_on_enter',
name = node.name,
close_on_enter = false,
})
end
if node.enter_after_edit then
table.insert(tree, i, {
@ -250,9 +252,8 @@ function Form:_render(player, ctx, formspec_version, id1, embedded, lang_code)
if not id1 or id1 > 1e6 then id1 = 0 end
local tree = render_ast(box, embedded)
local callbacks, btn_callbacks, saved_fields, id2 = parse_callbacks(
tree, orig_form, id1, embedded, formspec_version
)
local callbacks, btn_callbacks, saved_fields, id2, on_key_enters =
parse_callbacks(tree, orig_form, id1, embedded, formspec_version)
-- This should be after parse_callbacks so it can take advantage of
-- automatic field naming
@ -274,6 +275,7 @@ function Form:_render(player, ctx, formspec_version, id1, embedded, lang_code)
callbacks = callbacks,
btn_callbacks = btn_callbacks,
saved_fields = saved_fields,
on_key_enters = on_key_enters,
redraw_if_changed = redraw_if_changed,
ctx = ctx,
auto_name_id = id2,
@ -496,6 +498,17 @@ function fs_process_events(player, form_info, fields)
end
end
-- Run on_key_enter callbacks
if fields.key_enter and form_info.on_key_enters then
local callback = form_info.on_key_enters[fields.key_enter_field]
if callback then
-- Enter callbacks and button fields can't be sent at the same
-- time (except from a hacked client), so make sure they aren't
-- both processed at once by returning now.
return callback(player, ctx) or redraw_fs
end
end
-- Run button callbacks after all other callbacks as that seems to be the
-- most intuitive thing to do
-- Note: Try not to rely on the order of on_event callbacks, I may change

View File

@ -155,7 +155,7 @@ local button_types = {
-- Removes on_event from a formspec_ast tree and returns a callbacks table
local function parse_callbacks(tree, ctx_form, auto_name_id,
replace_backgrounds, formspec_version)
local callbacks
local callbacks, on_key_enters
local btn_callbacks = {}
local saved_fields = {}
local tablecolumn_count = 1
@ -297,13 +297,32 @@ local function parse_callbacks(tree, ctx_form, auto_name_id,
node.on_event = nil
end
local is_field = node.type == "field" or node.type == "pwdfield"
if node.on_key_enter and node_name then
if is_field then
on_key_enters = on_key_enters or {}
if on_key_enters[node_name] then
error(("Multiple on_key_enter callbacks registered for " ..
"elements with the same name (%q)"):format(node_name))
end
on_key_enters[node_name] = node.on_key_enter
else
core.log("warning",
"[flow] on_key_enter only works with Field and Pwdfield")
end
node.on_key_enter = nil
elseif is_field and node.close_on_enter then
core.log("warning", ("[flow] Field %q has close_on_enter " ..
"enabled but no on_key_enter callback."):format(node_name))
end
-- Call _after_positioned (used internally for ScrollableVBox)
if node._after_positioned then
node:_after_positioned()
node._after_positioned = nil
end
end
return callbacks, btn_callbacks, saved_fields, auto_name_id
return callbacks, btn_callbacks, saved_fields, auto_name_id, on_key_enters
end
return parse_callbacks