minetest-flow/doc/experimental.md
2025-02-27 17:33:13 +13:00

6.2 KiB

Experimental features

These features might be broken in the future.

no_prepend[]

You can set no_prepend = true on the "root" element to disable formspec prepends.

Example:

local my_gui = flow.make_gui(function(player, ctx)
    return gui.VBox{
        no_prepend = true,

        gui.Button{label = "Button 1"},

        -- There will be an empty space where the second button would be
        gui.Button{label = "Button 2", visible = false},

        gui.Button{label = "Button 3"},
    }
end)

Screenshot

bgcolor[]

You can set bgcolor = "#123", fbgcolor = "#123", and bg_fullscreen = true on the root element to set a background colour. The values for these correspond to the bgcolor formspec element.

Putting the form somewhere else on the screen (likely required for most HUDs)

These values allow the position of the displayed form to be moved around and adjust how it is scaled.

Example:

local my_gui = flow.make_gui(function(player, ctx)
    return gui.VBox{
        -- Adjusts where on the screen the form/HUD is rendered.
        -- 0 is the top/left, 1 is the bottom/right
        -- You probably want to set `window_position` and `window_anchor` to
        -- the same value.
        -- This puts the form in the bottom-right corner.
        window_position = {x = 1, y = 1},
        window_anchor = {x = 1, y = 1},

        -- Equivalent to padding[0.1,0.2], adjusts the minimum amount of
        -- padding around the form in terms of total screen size. If the form
        -- is too big, it will be scaled down
        -- Default for formspecs: {x = 0.05, y = 0.05} (i.e. 5% of screen size)
        -- HUDs default to a hardcoded pixel size, if you want them to roughly
        -- line up with formspecs then you may explicitly specify this.
        window_padding = {x = 0.1, y = 0.2},

        gui.Label{label = "Hello world"},
    }
end)

See the formspec documentation for more information.

Rendering to a formspec

This API should only be used when necessary and may have breaking changes in the future.

Some APIs in other mods, such as sfinv, expect formspec strings. You can use this API to embed flow forms inside them. To use flow with these mods, you can call form:render_to_formspec_string(player, ctx, standalone).

  • By default the the formspec_version and size elements aren't included in the returned formspec and are included in a third return value. Set standalone to include them in the returned formspec string. The third return value will not be returned.
  • Returns formspec, process_event[, info]
  • The process_event(fields) callback will return true if the formspec should be redrawn, where render_to_formspec_string should be called and the new process_event should be used in the future. This function may return true even if fields.quit is sent.

Warning: Do not use this API with node meta formspecs, it can and will break!

Embedding a form into another form

You can embed form objects inside others like this:

local parent_form = flow.make_gui(function(player, ctx)
    return gui.VBox{
        gui.Label{label = "Hello world"},
        other_form:embed{
            -- Passing in the player is required for now. You must use the same
            -- player object that you get sent by flow to avoid breakages in
            -- the future if this becomes optional.
            player = player,

            -- A name for the embed. If this is specified, the embedded form
            -- will get its own context (accessible at ctx.my_embed_name) and
            -- field names will be rewritten to avoid conflicts with the
            -- parent form. If name is not specified, the embedded form will
            -- share ctx and ctx.form with the parent, and will not have field
            -- names rewritten.
            name = "my_embed_name",
        },
    }
end)

Special characters (excluding - and _) are not allowed in embed names.

Running code when a form is closed

gui.Container, gui.HBox, gui.VBox, and gui.Stack elements support an on_quit callback which gets run when a player closes a form.

Note that this function is not called in some cases, such as when the player leaves without closing the form or when another form/formspec is shown.

This function must not return anything, behaviour may get added to return values in the future.

local parent_form = flow.make_gui(function(player, ctx)
    return gui.VBox{
        on_quit = function(player, ctx)
            core.chat_send_player(player:get_player_name(), "Form closed!")
        end,
    }
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:

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.