Allow closing a context from within its builder function.
This commit is contained in:
parent
48f7581a8f
commit
6e0f653928
2 changed files with 31 additions and 8 deletions
|
|
@ -19,6 +19,8 @@ Builder functions are used by either `imfs.show` (to show an interface to a play
|
||||||
|
|
||||||
You can pass a table as the last argument to an entry point like `imfs.show` to hold state external to the builder function; it will then be passed as an argument to the builder function during every rebuild. This way, you can declare the builder function itself in one place and use it in another without having to rely entirely on closure capture or global variables.
|
You can pass a table as the last argument to an entry point like `imfs.show` to hold state external to the builder function; it will then be passed as an argument to the builder function during every rebuild. This way, you can declare the builder function itself in one place and use it in another without having to rely entirely on closure capture or global variables.
|
||||||
|
|
||||||
|
Builder functions may also take a second argument, which is a function that will close the current context.
|
||||||
|
|
||||||
Imfs does not strictly require using a builder function: if an interface is purely static and need never change, you can use a prebuilt element tree instead of a builder function, which avoids rebuilding the tree every time the interface is shown.
|
Imfs does not strictly require using a builder function: if an interface is purely static and need never change, you can use a prebuilt element tree instead of a builder function, which avoids rebuilding the tree every time the interface is shown.
|
||||||
|
|
||||||
### Elements
|
### Elements
|
||||||
|
|
@ -198,13 +200,15 @@ Ends the current window, and returns it. Note: The name ends with an underscore
|
||||||
|
|
||||||
Show the interface defined by the provided builder function to the given player. The generated context will remain active until the player closes the formspec or leaves the game.
|
Show the interface defined by the provided builder function to the given player. The generated context will remain active until the player closes the formspec or leaves the game.
|
||||||
|
|
||||||
This returns a `Context` object, which is the the top-level dependent for all states used in the interface. Unlike in some other libraries, you don't really store state on the `Context` object (the builder function can't access it directly). Instead, you should store state as local variables in the function from which you call `show` (or somewhere else if they should be global) along with the builder function; that way, the builder function can capture the state specific to the action and the player that invoked the interface and only that. You can alternatively declare the builder function externally, then pass the new state objects in the `args` table, as it will be passed on to the builder function and will persist across rebuilds. This is arguably the better option since it avoids re-allocating the builder function every time it's first shown.
|
This returns a `Context` object, which is the the top-level dependent for all states used in the interface. Unlike in some other libraries, you don't really store state on the `Context` object (the builder function can't access it directly). Instead, you should store state as local variables in the function from which you call `show` (or somewhere else if they should be global) along with the builder function; that way, the builder function can capture the state specific to the action and the player that invoked the interface and only that. You can alternatively declare the builder function externally, then pass the new state objects in the `state` table, as it will be passed on to the builder function and will persist across rebuilds. This is arguably the better option since it avoids re-allocating the builder function every time it's first shown.
|
||||||
|
|
||||||
|
If `builder` is a function, it will be called with two arguments: 1) the `state` table, and 2) a function that will manually close the context. (Externally, you can call `:close()` on the context to close it manually.)
|
||||||
|
|
||||||
You can pass a static imfs tree instead of a builder if you so desire.
|
You can pass a static imfs tree instead of a builder if you so desire.
|
||||||
|
|
||||||
### `imfs.set_inventory(player, builder, state)`
|
### `imfs.set_inventory(player, builder, state)`
|
||||||
|
|
||||||
Set `player`'s inventory to the interface defined by `builder`. This context will persist indefinitely until it is manually removed or the player leaves the game. Otherwise, this behaves in the same way as `imfs.show`.
|
Set `player`'s inventory to the interface defined by `builder`. This context will persist indefinitely until it is manually removed or the player leaves the game. Otherwise, this behaves in the same way as `imfs.show`, with the exception that if `builder` is a function, it will not receive a `close` function as an argument (since inventories cannot be closed, only replaced).
|
||||||
|
|
||||||
### `imfs.clear_inventory(player)`
|
### `imfs.clear_inventory(player)`
|
||||||
|
|
||||||
|
|
|
||||||
31
init.lua
31
init.lua
|
|
@ -4,6 +4,7 @@ local hte = minetest.hypertext_escape
|
||||||
|
|
||||||
local ctx
|
local ctx
|
||||||
|
|
||||||
|
local player_contexts = {}
|
||||||
local contexts = {}
|
local contexts = {}
|
||||||
local inventories = {}
|
local inventories = {}
|
||||||
|
|
||||||
|
|
@ -1005,7 +1006,9 @@ local Context = {
|
||||||
local tracker = e._linked_states
|
local tracker = e._linked_states
|
||||||
table.insert(observers, tracker)
|
table.insert(observers, tracker)
|
||||||
|
|
||||||
local fs = type(e.formspec) == "function" and e.formspec(e.args) or e.formspec
|
local fs = type(e.formspec) == "function" and e.formspec(e.state, function() e:close() end) or e.formspec
|
||||||
|
|
||||||
|
e._window = fs
|
||||||
|
|
||||||
table.remove(observers)
|
table.remove(observers)
|
||||||
|
|
||||||
|
|
@ -1036,12 +1039,24 @@ local Context = {
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
deinit = function(e)
|
deinit = function(e)
|
||||||
|
if contexts[e.id] then
|
||||||
|
contexts[e.id] = nil
|
||||||
|
end
|
||||||
|
if e._window._onclose then
|
||||||
|
e._window:_onclose()
|
||||||
|
end
|
||||||
e:clear_state_bindings()
|
e:clear_state_bindings()
|
||||||
|
end,
|
||||||
|
close = function(e)
|
||||||
|
-- Inventories cannot be 'closed', only replaced.
|
||||||
|
if e._is_inventory then return end
|
||||||
|
minetest.close_formspec(e.target, e.id)
|
||||||
|
e:deinit()
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
Context.__index = Context
|
Context.__index = Context
|
||||||
|
|
||||||
local function fs_show(target, fs, args)
|
local function fs_show(target, fs, state)
|
||||||
local id = "form"..new_id()
|
local id = "form"..new_id()
|
||||||
local ctx = setmetatable({
|
local ctx = setmetatable({
|
||||||
formspec = fs,
|
formspec = fs,
|
||||||
|
|
@ -1049,17 +1064,22 @@ local function fs_show(target, fs, args)
|
||||||
target = type(target) == "string" and target or target:get_player_name(),
|
target = type(target) == "string" and target or target:get_player_name(),
|
||||||
id = id,
|
id = id,
|
||||||
_linked_states = {},
|
_linked_states = {},
|
||||||
args = args or {}
|
state = state or {}
|
||||||
}, Context)
|
}, Context)
|
||||||
|
|
||||||
|
if player_contexts[ctx.target] then
|
||||||
|
player_contexts[ctx.target]:deinit()
|
||||||
|
end
|
||||||
|
|
||||||
contexts[id] = ctx
|
contexts[id] = ctx
|
||||||
|
player_contexts[ctx.target] = ctx
|
||||||
|
|
||||||
ctx:rebuild()
|
ctx:rebuild()
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
end
|
end
|
||||||
|
|
||||||
local function fs_set_inventory(p, fs, args)
|
local function fs_set_inventory(p, fs, state)
|
||||||
local id = "form"..new_id()
|
local id = "form"..new_id()
|
||||||
local name = type(p) == "string" and p or p:get_player_name()
|
local name = type(p) == "string" and p or p:get_player_name()
|
||||||
local ctx = setmetatable({
|
local ctx = setmetatable({
|
||||||
|
|
@ -1070,7 +1090,7 @@ local function fs_set_inventory(p, fs, args)
|
||||||
target = name,
|
target = name,
|
||||||
id = id,
|
id = id,
|
||||||
_linked_states = {},
|
_linked_states = {},
|
||||||
args = args or {}
|
state = state or {}
|
||||||
}, Context)
|
}, Context)
|
||||||
|
|
||||||
inventories[name] = ctx
|
inventories[name] = ctx
|
||||||
|
|
@ -1125,7 +1145,6 @@ minetest.register_on_player_receive_fields(function(p, form, fields)
|
||||||
|
|
||||||
if fields.quit then
|
if fields.quit then
|
||||||
ctx:deinit()
|
ctx:deinit()
|
||||||
contexts[form] = nil
|
|
||||||
else
|
else
|
||||||
if ctx._dirty then
|
if ctx._dirty then
|
||||||
ctx:rebuild()
|
ctx:rebuild()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue