forked from your-land-mirror/minetest-flow
Use mkdocs for documentation
This commit is contained in:
parent
fb069d203c
commit
71bbfd524e
16
.gitlab-ci.yml
Normal file
16
.gitlab-ci.yml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Modified from https://gitlab.com/pages/mkdocs/
|
||||||
|
|
||||||
|
image: python:3.13-slim
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- pip install mkdocs==1.6.1
|
||||||
|
|
||||||
|
pages:
|
||||||
|
stage: deploy
|
||||||
|
script:
|
||||||
|
- mkdocs build --strict --verbose -d public
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- public
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
|
545
README.md
545
README.md
@ -157,544 +157,9 @@ These utilities likely aren't compatible with flow.
|
|||||||
- kuto was the the source of the "on_event" function idea.
|
- kuto was the the source of the "on_event" function idea.
|
||||||
- [My web-based formspec editor](https://forum.luanti.org/viewtopic.php?f=14&t=24130) lets you add elements and drag+drop them, however it doesn't support all formspec features.
|
- [My web-based formspec editor](https://forum.luanti.org/viewtopic.php?f=14&t=24130) lets you add elements and drag+drop them, however it doesn't support all formspec features.
|
||||||
|
|
||||||
## Elements
|
## Documentation
|
||||||
|
|
||||||
You should do `local gui = flow.widgets` in your code.
|
More detailed documentation is available at
|
||||||
|
https://luk3yx.gitlab.io/minetest-flow/. Some code snippets have a "run" button
|
||||||
### Layouting elements
|
which will open them in a web-based playground, not all of these will work
|
||||||
|
properly as the playground doesn't support all formspec elements.
|
||||||
These elements are used to lay out elements in the form. They don't have a
|
|
||||||
direct equivalent in formspecs.
|
|
||||||
|
|
||||||
#### `gui.VBox`
|
|
||||||
|
|
||||||
A vertical box, similar to a VBox in GTK. Elements inside a VBox are stacked
|
|
||||||
vertically.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
gui.VBox{
|
|
||||||
-- These elements are documented later on.
|
|
||||||
gui.Label{label="I am a label!"},
|
|
||||||
|
|
||||||
-- The second label will be positioned underneath the first one.
|
|
||||||
gui.Label{label="I am a second label!"},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Elements inside boxes have a spacing of 0.2 between them. To change this, you
|
|
||||||
can add `spacing = <number>` to the box definition. For example, `spacing = 0`
|
|
||||||
will remove all spacing between the elements.
|
|
||||||
|
|
||||||
#### `gui.HBox`
|
|
||||||
|
|
||||||
Like `gui.VBox` but stacks elements horizontally instead.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
gui.HBox{
|
|
||||||
-- These elements are documented later on.
|
|
||||||
gui.Label{label="I am a label!"},
|
|
||||||
|
|
||||||
-- The second label will be positioned to the right of first one.
|
|
||||||
gui.Label{label="I am a second label!"},
|
|
||||||
|
|
||||||
-- You can nest HBox and VBox elements
|
|
||||||
gui.VBox{
|
|
||||||
gui.Image{w=1, h=1, texture_name="default_dirt.png", align_h="centre"},
|
|
||||||
gui.Label{label="Dirt", expand=true, align_h="centre"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `gui.ScrollableVBox`
|
|
||||||
|
|
||||||
Similar to `gui.VBox` but uses a scroll_container and automatically adds a
|
|
||||||
scrollbar. You must specify a width and height for the scroll container.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
gui.ScrollableVBox{
|
|
||||||
-- A name must be provided for ScrollableVBox elements. You don't
|
|
||||||
-- have to use this name anywhere else, it just makes sure flow
|
|
||||||
-- doesn't mix up scrollbar states if one gets removed or if the
|
|
||||||
-- order changes.
|
|
||||||
name = "vbox1",
|
|
||||||
|
|
||||||
-- Specifying a height is optional but is probably a good idea.
|
|
||||||
-- If you don't specify a height, it will default to
|
|
||||||
-- min(height_of_content, 5).
|
|
||||||
h = 10,
|
|
||||||
|
|
||||||
-- These elements are documented later on.
|
|
||||||
gui.Label{label="I am a label!"},
|
|
||||||
|
|
||||||
-- The second label will be positioned underneath the first one.
|
|
||||||
gui.Label{label="I am a second label!"},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `gui.Spacer`
|
|
||||||
|
|
||||||
A "flexible space" element that expands by default. Example usage:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
gui.HBox{
|
|
||||||
-- These buttons will be on the left-hand side of the screen
|
|
||||||
gui.Button{label = "Cancel"},
|
|
||||||
gui.Button{label = "< Back"},
|
|
||||||
|
|
||||||
gui.Spacer{},
|
|
||||||
|
|
||||||
-- These buttons will be on the right-hand side of the screen
|
|
||||||
gui.Button{label = "Next >"},
|
|
||||||
gui.Button{label = "Confirm"},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
I advise against using spacers when `expand = true` and `align = ...` would
|
|
||||||
work just as well since spacers are implemented hackily and won't account for
|
|
||||||
some special cases.
|
|
||||||
|
|
||||||
You can replicate the above example without spacers, however the code doesn't
|
|
||||||
look as clean:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
gui.HBox{
|
|
||||||
-- These buttons will be on the left-hand side of the screen
|
|
||||||
gui.Button{label = "Cancel"},
|
|
||||||
gui.Button{label = "< Back", expand = true, align_h = "left"},
|
|
||||||
|
|
||||||
-- These buttons will be on the right-hand side of the screen
|
|
||||||
gui.Button{label = "Next >"},
|
|
||||||
gui.Button{label = "Confirm"},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You should not use spacers to centre elements as it creates unnecessary boxes,
|
|
||||||
and labels may be slightly off-centre (because label widths depend on screen
|
|
||||||
size, DPI, etc and this code doesn't trigger the centering hack):
|
|
||||||
|
|
||||||
```lua
|
|
||||||
-- This is bad!
|
|
||||||
gui.HBox{
|
|
||||||
gui.Spacer{},
|
|
||||||
gui.Label{label="I am not properly centered!"},
|
|
||||||
gui.Spacer{},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You should do this instead:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
gui.Label{label="I am centered!", align_h = "centre"},
|
|
||||||
```
|
|
||||||
|
|
||||||
This applies to other elements as well, because using HBox and Spacer to centre
|
|
||||||
elements creates unnecessary containers.
|
|
||||||
|
|
||||||
#### `gui.Nil`
|
|
||||||
|
|
||||||
A tool to allow for ternary-ish conditional widgets:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
gui.VBox{
|
|
||||||
gui.Label{ label = "The box below is only present if the boolean is truthy" },
|
|
||||||
the_boolean and gui.Box{ color = "#FF0000" } or gui.Nil{},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Use sparingly, flow still has to process each `Nil` object to be able to know to
|
|
||||||
remove it, and thus could still slow things down. The fastest element is one
|
|
||||||
that doesn't exist, and thus doesn't need processing.
|
|
||||||
|
|
||||||
#### `gui.Stack`
|
|
||||||
|
|
||||||
This container element places its children on top of each other. All child
|
|
||||||
elements are expanded in both directions.
|
|
||||||
|
|
||||||
Note that some elements (such as centred labels) won't pass clicks through to
|
|
||||||
the element below them.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
gui.Stack{
|
|
||||||
min_w = 10,
|
|
||||||
gui.Button{label = "Hello world!"},
|
|
||||||
gui.Image{w = 1, h = 1, texture_name = "air.png", padding = 0.2, align_h = "left"},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Minetest formspec elements
|
|
||||||
|
|
||||||
There is an auto-generated
|
|
||||||
[`elements.md`](https://gitlab.com/luk3yx/minetest-flow/-/blob/main/elements.md)
|
|
||||||
file which contains a list of elements and parameters. Elements in this list
|
|
||||||
haven't been tested and might not work.
|
|
||||||
|
|
||||||
#### Dynamic element types
|
|
||||||
|
|
||||||
If you want to generate element types from a variable, you can use
|
|
||||||
`{type = "label", label = "Hello world!"}` instead of
|
|
||||||
`gui.Label{label="Hello world!"}`. HBoxes and VBoxes can be created
|
|
||||||
this way as well (with `type = "hbox"` and `type = "vbox"`), however other
|
|
||||||
layouting elements (such as ScrollableVBox and Spacer)
|
|
||||||
won't work correctly.
|
|
||||||
|
|
||||||
An example of this is in `example.lua`.
|
|
||||||
|
|
||||||
## Padding, spacing, and backgrounds
|
|
||||||
|
|
||||||
All elements can have a `padding` value, which will add the specified amount of
|
|
||||||
padding around the element. The "root" element of the form (the one returned by
|
|
||||||
`build_func`) has a default padding of 0.3, everything else has a default
|
|
||||||
padding of 0.
|
|
||||||
|
|
||||||
`HBox` and `VBox` have a `spacing` field which specifies how much spacing there
|
|
||||||
is between elements inside the box. If unspecified, `spacing` will default to
|
|
||||||
0.2.
|
|
||||||
|
|
||||||
Container elements (HBox and VBox) can optionally have `bgimg` and `bgimg_middle`
|
|
||||||
parameters that specify a background for the container. The background will be
|
|
||||||
drawn behind any padding that the container has.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
gui.VBox{
|
|
||||||
padding = 0.5,
|
|
||||||
spacing = 0.1,
|
|
||||||
|
|
||||||
-- bgimg can be used without bgimg_middle
|
|
||||||
bgimg = "air.png",
|
|
||||||
bgimg_middle = 2,
|
|
||||||
|
|
||||||
gui.Button{label="Button 1"},
|
|
||||||
gui.Button{label="Button 2"},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
The padding around the VBox is 0.5 and the spacing between the buttons inside
|
|
||||||
it is 0.1.
|
|
||||||
|
|
||||||
## Styling forms
|
|
||||||
|
|
||||||
### Inline syntax
|
|
||||||
|
|
||||||
At the moment I suggest only using this syntax if your form won't look broken
|
|
||||||
without the style - older versions of flow don't support this syntax, and I may
|
|
||||||
make breaking changes to the sub-style syntax in the future.
|
|
||||||
|
|
||||||
You can add inline styles to elements with the `style` field:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
gui.Button{
|
|
||||||
label = "Test",
|
|
||||||
style = {
|
|
||||||
bgcolor = "red",
|
|
||||||
|
|
||||||
-- You can style specific states of elements:
|
|
||||||
{sel = "$hovered", bgcolor = "green"},
|
|
||||||
|
|
||||||
-- Or a combination of states:
|
|
||||||
{sel = "$hovered, $pressed", bgcolor = "blue"},
|
|
||||||
{sel = "$hovered+pressed", bgcolor = "white"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If you need to style multiple elements, you can reuse the `style` table:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local my_style = {bgcolor = "red", {sel = "$hovered", bgcolor = "green"}}
|
|
||||||
|
|
||||||
local gui = flow.make_gui(function(player, ctx)
|
|
||||||
return gui.VBox{
|
|
||||||
gui.Button{label = "Styled button", style = my_style},
|
|
||||||
gui.Button{label = "Unstyled button"},
|
|
||||||
gui.Button{label = "Second styled button", style = my_style},
|
|
||||||
}
|
|
||||||
end)
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that this may inadvertently reset styles on subsequent elements if used on
|
|
||||||
elements without a name due to formspec limitations.
|
|
||||||
|
|
||||||
### Separate style elements
|
|
||||||
|
|
||||||
Alternatively, you can use the `gui.Style` and `gui.StyleType` elements if you
|
|
||||||
need to style a large group of elements or need to support older versions of
|
|
||||||
flow:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
gui.Style{
|
|
||||||
selectors = {"btn1"},
|
|
||||||
props = {
|
|
||||||
bgimg = "button.png",
|
|
||||||
border = false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
gui.Button{
|
|
||||||
name = "btn1",
|
|
||||||
label = "Button",
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
The `Style` and `StyleType` elements are invisible and won't affect padding.
|
|
||||||
|
|
||||||
## Other features
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><b>Tooltips</b></summary>
|
|
||||||
|
|
||||||
You can add tooltips to elements using the `tooltip` field:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
gui.Image{
|
|
||||||
w = 2, h = 2,
|
|
||||||
texture_name = "air.png",
|
|
||||||
tooltip = "Air",
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</details><details>
|
|
||||||
<summary><b>Hiding elements</b></summary>
|
|
||||||
|
|
||||||
Elements inside boxes can have `visible = false` set to hide them from the
|
|
||||||
player. Elements hidden this way will still take up space like with
|
|
||||||
`visibility: hidden;` in CSS.
|
|
||||||
|
|
||||||
</details><details>
|
|
||||||
<summary><b>Using a form as an inventory</b></summary>
|
|
||||||
|
|
||||||
> [!TIP]
|
|
||||||
> Consider using [Sway](https://content.luanti.org/packages/lazerbeak12345/sway/)
|
|
||||||
> instead if you want to use flow as an inventory replacement while still
|
|
||||||
> having some way for other mods to extend the inventory.
|
|
||||||
|
|
||||||
A form can be set as the player inventory. Flow internally generates the
|
|
||||||
formspec and passes it to `player:set_inventory_formspec()`. This will
|
|
||||||
completely replace your inventory and isn't compatible with inventory mods like
|
|
||||||
sfinv.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local example_inventory = flow.make_gui(function (player, context)
|
|
||||||
return gui.Label{ label = "Inventory goes here!" }
|
|
||||||
end)
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
|
||||||
example_inventory:set_as_inventory_for(player)
|
|
||||||
end)
|
|
||||||
```
|
|
||||||
|
|
||||||
Like with the `show_hud` function, `update*` functions don't do anything, so to
|
|
||||||
update it, call `set_as_inventory_for` again with the new context. If the
|
|
||||||
context is not provided, it will reuse the existing context.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
example_inventory:set_as_inventory_for(player, new_context)
|
|
||||||
```
|
|
||||||
|
|
||||||
While the form will of course be cleared when the player leaves, if you'd like
|
|
||||||
to unset the inventory manually, call `:unset_as_inventory_for(player)`,
|
|
||||||
analogue to `close_hud`:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
example_inventory:unset_as_inventory_for(player)
|
|
||||||
```
|
|
||||||
|
|
||||||
This will set the inventory formspec string to `""` and stop flow from
|
|
||||||
processing inventory formspec input.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### Experimental features
|
|
||||||
|
|
||||||
These features might be broken in the future.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><b><code>no_prepend[]</code></b></summary>
|
|
||||||
|
|
||||||
You can set `no_prepend = true` on the "root" element to disable formspec
|
|
||||||
prepends.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
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)
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</details><details>
|
|
||||||
<summary><b><code>bgcolor[]</code></b></summary>
|
|
||||||
|
|
||||||
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](https://api.luanti.org/formspec/#bgcolorbgcolorfullscreenfbgcolor).
|
|
||||||
|
|
||||||
</details><details>
|
|
||||||
<summary><b>Putting the form somewhere else on the screen (likely required for most HUDs)</b></summary>
|
|
||||||
|
|
||||||
These values allow the position of the displayed form to be moved around and
|
|
||||||
adjust how it is scaled.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
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](https://api.luanti.org/formspec/#positionxy)
|
|
||||||
for more information.
|
|
||||||
|
|
||||||
</details><details>
|
|
||||||
<summary><b>Rendering to a formspec</b></summary>
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
> [!CAUTION]
|
|
||||||
> Do not use this API with node meta formspecs, it can and will break!
|
|
||||||
|
|
||||||
</details><details>
|
|
||||||
<summary><b>Embedding a form into another form</b></summary>
|
|
||||||
|
|
||||||
You can embed form objects inside others like this:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
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.
|
|
||||||
|
|
||||||
</details><details>
|
|
||||||
<summary><b>Running code when a form is closed</b></summary>
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
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.
|
|
||||||
|
|
||||||
</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>
|
|
||||||
|
179
doc/experimental.md
Normal file
179
doc/experimental.md
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
# 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:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
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)
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## `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](https://api.luanti.org/formspec/#bgcolorbgcolorfullscreenfbgcolor).
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
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](https://api.luanti.org/formspec/#positionxy)
|
||||||
|
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:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
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.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
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:
|
||||||
|
|
||||||
|
```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.
|
20
doc/hiding-elements.md
Normal file
20
doc/hiding-elements.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Hiding elements
|
||||||
|
|
||||||
|
Elements inside boxes can have `visible = false` set to hide them from the
|
||||||
|
player. Elements hidden this way will still take up space like with
|
||||||
|
`visibility: hidden;` in CSS.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.VBox{
|
||||||
|
gui.Button{label = "First button"},
|
||||||
|
gui.Button{label = "Hidden", visible = false},
|
||||||
|
gui.Button{label = "Last button"},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alternatives
|
||||||
|
|
||||||
|
If you don't want hidden elements to take up any space, see the documentation
|
||||||
|
for [gui.Nil](layout-elements.md#guinil).
|
1
doc/index.md
Symbolic link
1
doc/index.md
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../README.md
|
38
doc/inventory.md
Normal file
38
doc/inventory.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Using a form as an inventory
|
||||||
|
|
||||||
|
> Consider using [Sway](https://content.luanti.org/packages/lazerbeak12345/sway/)
|
||||||
|
> instead if you want to use flow as an inventory replacement while still
|
||||||
|
> having some way for other mods to extend the inventory.
|
||||||
|
|
||||||
|
A form can be set as the player inventory. Flow internally generates the
|
||||||
|
formspec and passes it to `player:set_inventory_formspec()`. This will
|
||||||
|
completely replace your inventory and isn't compatible with inventory mods like
|
||||||
|
sfinv.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local example_inventory = flow.make_gui(function (player, context)
|
||||||
|
return gui.Label{ label = "Inventory goes here!" }
|
||||||
|
end)
|
||||||
|
minetest.register_on_joinplayer(function(player)
|
||||||
|
example_inventory:set_as_inventory_for(player)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
Like with the `show_hud` function, `update*` functions don't do anything, so to
|
||||||
|
update it, call `set_as_inventory_for` again with the new context. If the
|
||||||
|
context is not provided, it will reuse the existing context.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
example_inventory:set_as_inventory_for(player, new_context)
|
||||||
|
```
|
||||||
|
|
||||||
|
While the form will of course be cleared when the player leaves, if you'd like
|
||||||
|
to unset the inventory manually, call `:unset_as_inventory_for(player)`,
|
||||||
|
analogue to `close_hud`:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
example_inventory:unset_as_inventory_for(player)
|
||||||
|
```
|
||||||
|
|
||||||
|
This will set the inventory formspec string to `""` and stop flow from
|
||||||
|
processing inventory formspec input.
|
166
doc/layout-elements.md
Normal file
166
doc/layout-elements.md
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
# Layouting elements
|
||||||
|
|
||||||
|
You should do `local gui = flow.widgets` in your code to improve readability.
|
||||||
|
All examples will assume that this line exists.
|
||||||
|
|
||||||
|
These elements are used to lay out elements in the form. They don't have a
|
||||||
|
direct equivalent in formspecs.
|
||||||
|
|
||||||
|
## `gui.VBox`
|
||||||
|
|
||||||
|
A vertical box, similar to a VBox in GTK. Elements inside a VBox are stacked
|
||||||
|
vertically.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.VBox{
|
||||||
|
-- These elements are documented later on.
|
||||||
|
gui.Label{label="I am a label!"},
|
||||||
|
|
||||||
|
-- The second label will be positioned underneath the first one.
|
||||||
|
gui.Label{label="I am a second label!"},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Elements inside boxes have a spacing of 0.2 between them. To change this, you
|
||||||
|
can add `spacing = <number>` to the box definition. For example, `spacing = 0`
|
||||||
|
will remove all spacing between the elements.
|
||||||
|
|
||||||
|
## `gui.HBox`
|
||||||
|
|
||||||
|
Like `gui.VBox` but stacks elements horizontally instead.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.HBox{
|
||||||
|
-- These elements are documented later on.
|
||||||
|
gui.Label{label="I am a label!"},
|
||||||
|
|
||||||
|
-- The second label will be positioned to the right of first one.
|
||||||
|
gui.Label{label="I am a second label!"},
|
||||||
|
|
||||||
|
-- You can nest HBox and VBox elements
|
||||||
|
gui.VBox{
|
||||||
|
gui.Image{w=1, h=1, texture_name="default_dirt.png", align_h="centre"},
|
||||||
|
gui.Label{label="Dirt", expand=true, align_h="centre"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `gui.ScrollableVBox`
|
||||||
|
|
||||||
|
Similar to `gui.VBox` but uses a scroll_container and automatically adds a
|
||||||
|
scrollbar. You must specify a width and height for the scroll container.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.ScrollableVBox{
|
||||||
|
-- A name must be provided for ScrollableVBox elements. You don't
|
||||||
|
-- have to use this name anywhere else, it just makes sure flow
|
||||||
|
-- doesn't mix up scrollbar states if one gets removed or if the
|
||||||
|
-- order changes.
|
||||||
|
name = "vbox1",
|
||||||
|
|
||||||
|
-- Specifying a height is optional but is probably a good idea.
|
||||||
|
-- If you don't specify a height, it will default to
|
||||||
|
-- min(height_of_content, 5).
|
||||||
|
h = 10,
|
||||||
|
|
||||||
|
-- These elements are documented later on.
|
||||||
|
gui.Label{label="I am a label!"},
|
||||||
|
|
||||||
|
-- The second label will be positioned underneath the first one.
|
||||||
|
gui.Label{label="I am a second label!"},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `gui.Spacer`
|
||||||
|
|
||||||
|
A "flexible space" element that expands by default. Example usage:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.HBox{
|
||||||
|
-- These buttons will be on the left-hand side of the screen
|
||||||
|
gui.Button{label = "Cancel"},
|
||||||
|
gui.Button{label = "< Back"},
|
||||||
|
|
||||||
|
gui.Spacer{},
|
||||||
|
|
||||||
|
-- These buttons will be on the right-hand side of the screen
|
||||||
|
gui.Button{label = "Next >"},
|
||||||
|
gui.Button{label = "Confirm"},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
I advise against using spacers when `expand = true` and `align = ...` would
|
||||||
|
work just as well since spacers are implemented hackily and won't account for
|
||||||
|
some special cases.
|
||||||
|
|
||||||
|
You can replicate the above example without spacers, however the code doesn't
|
||||||
|
look as clean:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.HBox{
|
||||||
|
-- These buttons will be on the left-hand side of the screen
|
||||||
|
gui.Button{label = "Cancel"},
|
||||||
|
gui.Button{label = "< Back", expand = true, align_h = "left"},
|
||||||
|
|
||||||
|
-- These buttons will be on the right-hand side of the screen
|
||||||
|
gui.Button{label = "Next >"},
|
||||||
|
gui.Button{label = "Confirm"},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You should not use spacers to centre elements as it creates unnecessary boxes,
|
||||||
|
and labels may be slightly off-centre (because label widths depend on screen
|
||||||
|
size, DPI, etc and this code doesn't trigger the centering hack):
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- This is bad!
|
||||||
|
gui.HBox{
|
||||||
|
gui.Spacer{},
|
||||||
|
gui.Label{label="I am not properly centered!"},
|
||||||
|
gui.Spacer{},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You should do this instead:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.Label{label="I am centered!", align_h = "centre"},
|
||||||
|
```
|
||||||
|
|
||||||
|
This applies to other elements as well, because using HBox and Spacer to centre
|
||||||
|
elements creates unnecessary containers.
|
||||||
|
|
||||||
|
## `gui.Nil`
|
||||||
|
|
||||||
|
A tool to allow for ternary-ish conditional widgets:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.VBox{
|
||||||
|
gui.Label{ label = "The box below is only present if the boolean is truthy" },
|
||||||
|
the_boolean and gui.Box{ color = "#FF0000" } or gui.Nil{},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use sparingly, flow still has to process each `Nil` object to be able to know to
|
||||||
|
remove it, and thus could still slow things down. The fastest element is one
|
||||||
|
that doesn't exist, and thus doesn't need processing.
|
||||||
|
|
||||||
|
## `gui.Stack`
|
||||||
|
|
||||||
|
This container element places its children on top of each other. All child
|
||||||
|
elements are expanded in both directions.
|
||||||
|
|
||||||
|
Note that some elements (such as centred labels) won't pass clicks through to
|
||||||
|
the element below them.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.Stack{
|
||||||
|
min_w = 10,
|
||||||
|
gui.Button{label = "Hello world!"},
|
||||||
|
gui.Image{w = 1, h = 1, texture_name = "air.png", padding = 0.2, align_h = "left"},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|

|
40
doc/manual-positioning.md
Normal file
40
doc/manual-positioning.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Manual positioning
|
||||||
|
|
||||||
|
## Dynamic element types
|
||||||
|
|
||||||
|
This is only recommended for elements inside `gui.Container` (see below)
|
||||||
|
outside of some rare use cases that need it, as it does not support flow's
|
||||||
|
layouting elements.
|
||||||
|
|
||||||
|
If you want to generate element types from a variable, you can use
|
||||||
|
`{type = "label", label = "Hello world!"}` instead of
|
||||||
|
`gui.Label{label="Hello world!"}`. HBoxes and VBoxes can be created
|
||||||
|
this way as well (with `type = "hbox"` and `type = "vbox"`), however other
|
||||||
|
layouting elements (such as ScrollableVBox and Spacer) won't work correctly.
|
||||||
|
|
||||||
|
An example of this is in `example.lua`.
|
||||||
|
|
||||||
|
## Manual positioning of elements
|
||||||
|
|
||||||
|
You can use `gui.Container` elements to contain manually positioned elements.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.VBox{
|
||||||
|
gui.Label{label = "Automatically positioned"},
|
||||||
|
gui.Container{
|
||||||
|
-- You can specify a width and height if you don't want flow to try and
|
||||||
|
-- guess at the size of the container.
|
||||||
|
-- w = 3, h = 2,
|
||||||
|
|
||||||
|
-- You may embed most formspec_ast elements inside gui.Container
|
||||||
|
{type = "box", x = 0, y = 0, w = 1, h = 1, color = "red"},
|
||||||
|
{type = "box", x = 0.3, y = 0.3, w = 1, h = 1, color = "green"},
|
||||||
|
{type = "box", x = 0.6, y = 0.6, w = 1, h = 1, color = "blue"},
|
||||||
|
|
||||||
|
{type = "label", x = 2, y = 1.1, label = "Manually positioned"}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that you cannot nest layouted elements (like `gui.VBox`) inside
|
||||||
|
`gui.Container`.
|
35
doc/padding.md
Normal file
35
doc/padding.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
## Padding, spacing, and backgrounds
|
||||||
|
|
||||||
|
All elements can have a `padding` value, which will add the specified amount of
|
||||||
|
padding around the element. The "root" element of the form (the one returned by
|
||||||
|
`build_func`) has a default padding of 0.3, everything else has a default
|
||||||
|
padding of 0.
|
||||||
|
|
||||||
|
`HBox` and `VBox` have a `spacing` field which specifies how much spacing there
|
||||||
|
is between elements inside the box. If unspecified, `spacing` will default to
|
||||||
|
0.2.
|
||||||
|
|
||||||
|
Container elements (HBox and VBox) can optionally have `bgimg` and `bgimg_middle`
|
||||||
|
parameters that specify a background for the container. The background will be
|
||||||
|
drawn behind any padding that the container has.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.VBox{
|
||||||
|
padding = 0.5,
|
||||||
|
spacing = 0.1,
|
||||||
|
|
||||||
|
-- bgimg can be used without bgimg_middle
|
||||||
|
bgimg = "air.png",
|
||||||
|
bgimg_middle = 2,
|
||||||
|
|
||||||
|
gui.Button{label="Button 1"},
|
||||||
|
gui.Button{label="Button 2"},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The padding around the VBox is 0.5 and the spacing between the buttons inside
|
||||||
|
it is 0.1.
|
51
doc/playground-links.js
Normal file
51
doc/playground-links.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const PLAYGROUND_URL = "https://luk3yx.gitlab.io/minetest-flow-playground/";
|
||||||
|
|
||||||
|
function getPlaygroundLink(code) {
|
||||||
|
return PLAYGROUND_URL + "#code=" + encodeURIComponent(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPlaygroundBtn(el, code) {
|
||||||
|
const btn = document.createElement("button");
|
||||||
|
btn.onclick = () => {
|
||||||
|
window.open(getPlaygroundLink(code));
|
||||||
|
};
|
||||||
|
btn.textContent = "\u25b6";
|
||||||
|
btn.title = "Run this code online";
|
||||||
|
btn.style.float = "right";
|
||||||
|
|
||||||
|
el.insertBefore(btn, el.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TEMPLATE = `
|
||||||
|
local gui = flow.widgets
|
||||||
|
|
||||||
|
local form = flow.make_gui(function(player, ctx)
|
||||||
|
%
|
||||||
|
end)
|
||||||
|
|
||||||
|
form:show(core.get_player_by_name("playground"))
|
||||||
|
`.trim();
|
||||||
|
|
||||||
|
function addPlayBtn({el, result, text}) {
|
||||||
|
if (!el.classList.contains("language-lua"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The playground does not support styling
|
||||||
|
if (text.indexOf("style = {") > 0 || text.indexOf("gui.Style") > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (text.startsWith("gui.")) {
|
||||||
|
addPlaygroundBtn(el, TEMPLATE.replace("%",
|
||||||
|
" return " + text.trim().replaceAll("\n", "\n ")));
|
||||||
|
} else if (/^local (my_gui|form) = flow.make_gui\(function\(player, ctx\)\n/.test(text) &&
|
||||||
|
text.trim().endsWith("\nend)")) {
|
||||||
|
addPlaygroundBtn(el, TEMPLATE.replace("%",
|
||||||
|
text.trim().split("\n").slice(1, -1).join("\n")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hljs.addPlugin({
|
||||||
|
'after:highlightElement': addPlayBtn,
|
||||||
|
})
|
65
doc/styling.md
Normal file
65
doc/styling.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# Styling forms
|
||||||
|
|
||||||
|
## Inline syntax
|
||||||
|
|
||||||
|
At the moment I suggest only using this syntax if your form won't look broken
|
||||||
|
without the style - older versions of flow don't support this syntax, and I may
|
||||||
|
make breaking changes to the sub-style syntax in the future.
|
||||||
|
|
||||||
|
You can add inline styles to elements with the `style` field:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.Button{
|
||||||
|
label = "Test",
|
||||||
|
style = {
|
||||||
|
bgcolor = "red",
|
||||||
|
|
||||||
|
-- You can style specific states of elements:
|
||||||
|
{sel = "$hovered", bgcolor = "green"},
|
||||||
|
|
||||||
|
-- Or a combination of states:
|
||||||
|
{sel = "$hovered, $pressed", bgcolor = "blue"},
|
||||||
|
{sel = "$hovered+pressed", bgcolor = "white"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to style multiple elements, you can reuse the `style` table:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local my_style = {bgcolor = "red", {sel = "$hovered", bgcolor = "green"}}
|
||||||
|
|
||||||
|
local gui = flow.make_gui(function(player, ctx)
|
||||||
|
return gui.VBox{
|
||||||
|
gui.Button{label = "Styled button", style = my_style},
|
||||||
|
gui.Button{label = "Unstyled button"},
|
||||||
|
gui.Button{label = "Second styled button", style = my_style},
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that this may inadvertently reset styles on subsequent elements if used on
|
||||||
|
elements without a name due to formspec limitations.
|
||||||
|
|
||||||
|
## Separate style elements
|
||||||
|
|
||||||
|
Alternatively, you can use the `gui.Style` and `gui.StyleType` elements if you
|
||||||
|
need to style a large group of elements or need to support older versions of
|
||||||
|
flow:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.Style{
|
||||||
|
selectors = {"btn1"},
|
||||||
|
props = {
|
||||||
|
bgimg = "button.png",
|
||||||
|
border = false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
gui.Button{
|
||||||
|
name = "btn1",
|
||||||
|
label = "Button",
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
The `Style` and `StyleType` elements are invisible and won't affect padding.
|
11
doc/tooltips.md
Normal file
11
doc/tooltips.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Tooltips
|
||||||
|
|
||||||
|
You can add tooltips to elements using the `tooltip` field:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gui.Image{
|
||||||
|
w = 2, h = 2,
|
||||||
|
texture_name = "air.png",
|
||||||
|
tooltip = "Air",
|
||||||
|
}
|
||||||
|
```
|
@ -169,7 +169,7 @@ if __name__ == '__main__':
|
|||||||
elements = fetch_elements()
|
elements = fetch_elements()
|
||||||
print('Done.')
|
print('Done.')
|
||||||
|
|
||||||
with open('elements.md', 'w') as f:
|
with open('doc/elements.md', 'w') as f:
|
||||||
f.write('# Auto-generated elements list\n\n')
|
f.write('# Auto-generated elements list\n\n')
|
||||||
f.write('This is probably broken.')
|
f.write('This is probably broken.')
|
||||||
for element_name, variants in elements.items():
|
for element_name, variants in elements.items():
|
||||||
|
20
mkdocs.yml
Normal file
20
mkdocs.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
site_name: Flow Documentation
|
||||||
|
docs_dir: doc
|
||||||
|
theme: readthedocs
|
||||||
|
extra_javascript:
|
||||||
|
- playground-links.js
|
||||||
|
markdown_extensions:
|
||||||
|
- footnotes
|
||||||
|
nav:
|
||||||
|
- Introduction: index.md
|
||||||
|
- Elements/widgets:
|
||||||
|
- layout-elements.md
|
||||||
|
- All other elements: elements.md
|
||||||
|
- Other features:
|
||||||
|
- styling.md
|
||||||
|
- padding.md
|
||||||
|
- tooltips.md
|
||||||
|
- hiding-elements.md
|
||||||
|
- inventory.md
|
||||||
|
- manual-positioning.md
|
||||||
|
- experimental.md
|
Loading…
Reference in New Issue
Block a user