From e3dc6d675049a9e165c0470c5c78b62d3f7056dd Mon Sep 17 00:00:00 2001 From: luk3yx Date: Fri, 21 Feb 2025 17:16:54 +1300 Subject: [PATCH] Add on_key_enter callback and close_on_enter Closes https://gitlab.com/luk3yx/minetest-flow/-/issues/4 --- README.md | 38 ++++++++++++++++++++++++++++++++++++++ init.lua | 29 +++++++++++++++++++++-------- input.lua | 23 +++++++++++++++++++++-- 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a6d5c5a..5d9ecfa 100644 --- a/README.md +++ b/README.md @@ -622,4 +622,42 @@ end) If multiple `on_quit` callbacks are specified in different elements, they will all get called. +
+Handling enter keypresses in fields + +`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. +
diff --git a/init.lua b/init.lua index 0928eee..92ce5b4 100644 --- a/init.lua +++ b/init.lua @@ -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 diff --git a/input.lua b/input.lua index b4b756a..2f3dc04 100644 --- a/input.lua +++ b/input.lua @@ -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