diff --git a/init.lua b/init.lua
index 5346889..bd34cee 100644
--- a/init.lua
+++ b/init.lua
@@ -20,7 +20,7 @@ local function lf(path)
end
i3 = {
- version = 1161,
+ version = 1162,
data = core.deserialize(storage:get_string"data") or {},
settings = {
@@ -102,6 +102,7 @@ i3 = {
plants = {},
modules = {},
minitabs = {},
+ footer_buttons = {},
craft_types = {},
recipe_filters = {},
diff --git a/src/api.lua b/src/api.lua
index 8b0c161..9c9dccc 100644
--- a/src/api.lua
+++ b/src/api.lua
@@ -1,5 +1,6 @@
local http = ...
-local make_fs, get_inventory_fs = i3.files.gui()
+local make_fs, get_inventory_fs, confirm_trash_footer_fs, settings_footer_fs = i3.files.gui()
+local home_footer_fields, confirm_trash_footer_fields, settings_footer_fields = i3.files.fields()
IMPORT("sorter", "sort_inventory", "play_sound")
IMPORT("sort", "concat", "copy", "insert", "remove")
@@ -257,6 +258,88 @@ function i3.override_tab(name, newdef)
end
end
+function i3.new_footer_button(name, def)
+ if not true_str(name) then
+ return err "i3.new_footer_button: button name missing"
+ elseif not true_table(def) then
+ return err "i3.new_footer_button: button definition missing"
+ elseif not true_str(def.description) then
+ return err "i3.new_footer_button: description missing"
+ elseif not def.image then
+ return err "i3.new_footer_button: image missing"
+ elseif #i3.footer_buttons == 16 then
+ return err(fmt("i3.new_footer_button: cannot add '%s' button. Limit reached (16).", def.name))
+ end
+
+ def.name = name
+ insert(i3.footer_buttons, def)
+end
+
+function i3.remove_footer_button(name)
+ if not true_str(name) then
+ return err "i3.remove_footer_button: tab name missing"
+ end
+
+ for i = #i3.footer_buttons, 2, -1 do
+ local def = i3.footer_buttons[i]
+ if def and name == def.name then
+ remove(i3.footer_buttons, i)
+ end
+ end
+end
+
+function i3.override_footer_button(name, newdef)
+ if not true_str(name) then
+ return err "i3.override_footer_button: button name missing"
+ elseif not true_table(newdef) then
+ return err "i3.override_footer_button: button definition missing"
+ elseif not true_str(newdef.description) then
+ return err "i3.override_footer_button: description missing"
+ elseif not true_str(newdef.image) then
+ return err "i3.override_footer_button: image missing"
+ end
+
+ newdef.name = name
+
+ for i, def in ipairs(i3.footer_buttons) do
+ if def.name == name then
+ i3.footer_buttons[i] = newdef
+ break
+ end
+ end
+end
+
+i3.new_footer_button("trash", {
+ description = S"Clear inventory",
+ image = "i3_trash.png",
+ formspec = confirm_trash_footer_fs,
+ fields = confirm_trash_footer_fields,
+})
+
+
+i3.new_footer_button("sort", {
+ description = S"Sort inventory",
+ image = "i3_sort.png",
+ fields = function(player, data, fields)
+ sort_inventory(player, data)
+ -- Immediately disable, since nothing is displayed.
+ return false
+ end,
+})
+
+i3.new_footer_button("settings", {
+ description = S"Settings",
+ image = "i3_settings.png",
+ formspec = settings_footer_fs,
+ fields = settings_footer_fields,
+})
+
+i3.new_footer_button("home", {
+ description = S"Go home",
+ image = "i3_home.png",
+ fields = home_footer_fields,
+})
+
i3.register_craft_type("digging", {
description = S"Digging",
icon = "i3_steelpick.png",
diff --git a/src/fields.lua b/src/fields.lua
index e696b6d..5f5c46c 100644
--- a/src/fields.lua
+++ b/src/fields.lua
@@ -9,6 +9,7 @@ IMPORT("valid_item", "get_stack", "craft_stack", "clean_name", "check_privs", "s
IMPORT("msg", "is_fav", "pos_to_str", "str_to_pos", "add_hud_waypoint", "play_sound", "reset_data")
IMPORT("search", "sort_inventory", "sort_by_category", "get_recipes", "get_detached_inv", "update_inv_size")
+-- Fields-handler for any tab displaying the inventory & footer (that is, with def.slots true).
local function inv_fields(player, data, fields)
local name = data.player_name
local inv = player:get_inventory()
@@ -19,6 +20,13 @@ local function inv_fields(player, data, fields)
data.font_size = tonumber(fields.sb_font_size:match"-?%d+$")
end
+ local footer = data.footer_button and i3.footer_buttons[data.footer_button]
+ if footer and footer.fields then
+ if not footer.fields(player, data, fields) then
+ data.footer_button = nil
+ end
+ end
+
for field in pairs(fields) do
if sub(field, 1, 4) == "btn_" then
data.subcat = indexof(i3.categories, sub(field, 5))
@@ -93,8 +101,7 @@ local function inv_fields(player, data, fields)
end
if fields.quit then
- data.confirm_trash = nil
- data.show_settings = nil
+ data.footer_button = nil
data.waypoint_see = nil
data.bag_rename = nil
data.goto_page = nil
@@ -103,58 +110,9 @@ local function inv_fields(player, data, fields)
data.enable_search = nil
end
- elseif fields.trash then
- data.show_settings = nil
- data.confirm_trash = true
-
- elseif fields.settings then
- if not data.show_settings then
- data.confirm_trash = nil
- data.show_settings = true
- else
- data.show_settings = nil
- end
-
- elseif fields.confirm_trash_yes or fields.confirm_trash_no then
- if fields.confirm_trash_yes then
- inv:set_list("main", {})
- inv:set_list("craft", {})
- end
-
- data.confirm_trash = nil
-
- elseif fields.close_settings then
- data.show_settings = nil
-
elseif fields.close_preview then
data.waypoint_see = nil
- elseif fields.sort then
- sort_inventory(player, data)
-
- elseif (fields.home or fields.set_home) and not check_privs(name, {home = true}) then
- return msg(name, "'home' privilege missing")
-
- elseif fields.home then
- if sethome then
- if not sethome.go(name) then
- return msg(name, "No home set")
- end
- elseif not data.home then
- return msg(name, "No home set")
- else
- safe_teleport(player, str_to_pos(data.home))
- end
-
- msg(name, S"Welcome back home!")
-
- elseif fields.set_home then
- if sethome then
- sethome.set(name, player:get_pos())
- else
- data.home = pos_to_str(player:get_pos(), 1)
- end
-
elseif fields.bag_rename then
data.bag_rename = true
@@ -429,6 +387,55 @@ local function rcp_fields(player, data, fields)
end
end
+-- Fields input-handler for the Home footer-button.
+local function home_footer_fields(player, data, fields)
+ local name = player:get_player_name()
+
+ -- Use minetest_game’s sethome API, if available.
+ if sethome then
+ if not sethome.go(name) then
+ msg(name, "No home set")
+ else
+ msg(name, S"Welcome back home!")
+ end
+ -- Otherwise, use i3’s home.
+ elseif not data.home then
+ msg(name, "No home set")
+ elseif name then
+ safe_teleport(player, str_to_pos(data.home))
+ msg(name, S"Welcome back home!")
+ end
+
+ -- Immediately disable this footer-dialogue, since nothing is displayed.
+ return false
+end
+
+-- Fields input-handler for the “Confirm trash” footer-dialogue.
+local function confirm_trash_footer_fields(player, data, fields)
+ local inv = player:get_inventory()
+ if fields.confirm_trash_yes then
+ inv:set_list("main", {})
+ inv:set_list("craft", {})
+ end
+
+ -- In any case, disable this footer-dialogue.
+ return not (fields.confirm_trash_yes or fields.confirm_trash_no)
+end
+
+-- Fields input-handler for the settings footer-dialogue.
+local function settings_footer_fields(player, data, fields)
+ if fields.set_home and not check_privs(name, {home = true}) then
+ return msg(name, "'home' privilege missing")
+ elseif fields.set_home then
+ if sethome then
+ sethome.set(player:get_player_name(), player:get_pos())
+ else
+ data.home = pos_to_str(player:get_pos(), 1)
+ end
+ end
+ return not fields.close_settings
+end
+
core.register_on_player_receive_fields(function(player, formname, fields)
local name = player:get_player_name()
@@ -465,6 +472,14 @@ core.register_on_player_receive_fields(function(player, formname, fields)
data.itab = tonumber(f:sub(-1))
sort_by_category(data)
break
+ elseif sub(f, 1, 7) == "footer_" then
+ local footer_name = sub(f, 8)
+ for i, footer in ipairs(i3.footer_buttons) do
+ if footer.name == footer_name then
+ data.footer_button = i
+ break
+ end
+ end
end
end
@@ -484,3 +499,5 @@ core.register_on_player_receive_fields(function(player, formname, fields)
return true, set_fs(player)
end)
+
+return home_footer_fields, confirm_trash_footer_fields, settings_footer_fields
diff --git a/src/gui.lua b/src/gui.lua
index c584fd3..84de221 100644
--- a/src/gui.lua
+++ b/src/gui.lua
@@ -569,144 +569,148 @@ local function get_container(fs, data, player, yoffset, ctn_len, award_list, awa
end
end
-local function show_settings(fs, data, player)
- if data.confirm_trash then
- image(2.8, 10.65, 4.6, 0.7, PNG.bg_goto)
- label(3.02, 11, "Confirm trash?")
- image_button(5.07, 10.75, 1, 0.5, "", "confirm_trash_yes", "Yes")
- image_button(6.17, 10.75, 1, 0.5, "", "confirm_trash_no", "No")
-
- elseif data.show_settings then
- fs"container[-0.06,0]"
- image(2.2, 9, 6.1, 2.35, PNG.bg_content)
-
- local show_home = data.show_setting == "home"
- local show_style = data.show_setting == "style"
- local show_sorting = data.show_setting == "sorting"
-
- fs"style[setting_home,setting_style,setting_sorting;font=bold;font_size=16;sound=i3_click]"
- fs("style[setting_home:hovered;textcolor=%s]", show_home and colors.yellow or "#fff")
- fs("style[setting_style:hovered;textcolor=%s]", show_style and colors.yellow or "#fff")
- fs("style[setting_sorting:hovered;textcolor=%s]", show_sorting and colors.yellow or "#fff")
-
- fs("style[setting_home;bgimg=%s;bgimg_hovered=%s;bgimg_middle=9;padding=-9;textcolor=%s]",
- show_home and PNG.pagenum_hover or "", PNG.pagenum_hover,
- show_home and colors.yellow or "#ddd")
- fs("style[setting_style;bgimg=%s;bgimg_hovered=%s;bgimg_middle=9;padding=-9;textcolor=%s]",
- show_style and PNG.pagenum_hover or "", PNG.pagenum_hover,
- show_style and colors.yellow or "#ddd")
- fs("style[setting_sorting;bgimg=%s;bgimg_hovered=%s;bgimg_middle=9;padding=-9;textcolor=%s]",
- show_sorting and PNG.pagenum_hover or "", PNG.pagenum_hover,
- show_sorting and colors.yellow or "#ddd")
-
- local X = 2.5
- button(X, 9.1, 1.6, 0.55, "setting_home", "Home")
- button(X + 1.7, 9.1, 1.6, 0.55, "setting_style", "Style")
- button(X + 3.4, 9.1, 1.6, 0.55, "setting_sorting", "Sorting")
- image_button(X + 5.12, 9.2, 0.25, 0.25, PNG.cancel_hover .. "^\\[brighten", "close_settings", "")
-
- if show_home then
- local coords, c, str = {"X", "Y", "Z"}, 0, ES"No home set"
-
- local home = data.home
- if sethome then
- -- i3 stores home coordinates with one decimal of precision, as the blow fmt() statement
- -- assumes. So we need to trim sethome’s coordinates to one decimal, likewise.
- local home_pos = sethome.get(player:get_player_name())
- home = string.format("(%.1f,%.1f,%.1f)", home_pos.x, home_pos.y, home_pos.z)
- end
-
- if home then
- str = home:gsub(",", " "):sub(2,-2):gsub("%.%d", ""):gsub(
- "(%-?%d+)", function(a)
- c++
- return fmt("%s: ",
- coords[c], colors.blue, a)
- end)
- end
-
- hypertext(2.2, 9.9, 6, 0.6, "home_pos", fmt("%s", str))
- fs("style[set_home;padding=20,10,-210,-10;fgimg=%s;fgimg_hovered=%s]", PNG.home_px, PNG.home_px_hover)
- image_button(4.1, 10.4, 2.2, 0.7, "", "set_home", "")
- label(4.9, 10.75, "Set home")
-
- elseif show_style then
- checkbox(2.6, 9.95, "cb_hide_tabs", "Hide tabs", tostring(data.hide_tabs))
- checkbox(2.6, 10.4, "cb_legacy_inventory", "Legacy inventory", tostring(data.legacy_inventory))
- checkbox(2.6, 10.85, "cb_wielditem_hud", "HUD description", tostring(data.wielditem_hud))
-
- if not recipe_filter_set() then
- checkbox(5.3, 10.85, "cb_collapse", "Collapse list", tostring(data.collapse))
- end
-
- local sign = (data.font_size > 0 and "+") or (data.font_size > 0 and "-") or ""
- label(5.3, 9.95, ES"Font size" .. fmt(": %s", sign .. data.font_size))
-
- local range = 8
- fs("scrollbaroptions[min=-%u;max=%u;smallstep=1;largestep=1;thumbsize=2]", range, range)
- fs("scrollbar[5.3,10.2;2.55,0.3;horizontal;sb_font_size;%d+]", data.font_size)
-
- fs("tooltip[cb_hide_tabs;%s;#32333899;#fff]",
- ES"Enable this option to change the style of the right panel")
- fs("tooltip[cb_legacy_inventory;%s;#32333899;#fff]",
- ES"Enable this option to set the classic inventory size in Minetest")
- fs("tooltip[cb_wielditem_hud;%s;#32333899;#fff]",
- ES"Enable this option to show the wielded item description in your HUD")
- fs("tooltip[cb_collapse;%s;#32333899;#fff]",
- ES"Enable this option to collapse the inventory list by grouping some items")
-
- elseif show_sorting then
- checkbox(2.6, 9.95, "cb_inv_compress", "Compression", tostring(data.inv_compress))
- checkbox(2.6, 10.4, "cb_reverse_sorting", "Reverse mode", tostring(data.reverse_sorting))
- checkbox(2.6, 10.85, "cb_ignore_hotbar", "Ignore hotbar", tostring(data.ignore_hotbar))
- checkbox(5.3, 9.95, "cb_auto_sorting", "Automation", tostring(data.auto_sorting))
-
- local methods = {}
-
- for _, v in ipairs(i3.sorting_methods) do
- local name = toupper(v.name)
- insert(methods, name)
- end
-
- label(5.3, 10.4, ES"Sorting method:")
- fs("dropdown[%f,%f;2.6,0.5;dd_sorting_method;%s;%u;true]", 5.3, 10.6, concat(methods, ","), data.sort)
-
- local desc = i3.sorting_methods[data.sort].description
- if desc then
- tooltip(5.3, 10.6, 2.4, 0.5, ESC(desc))
- end
-
- fs("tooltip[cb_inv_compress;%s;#32333899;#fff]",
- ES"Enable this option to compress your inventory")
- fs("tooltip[cb_reverse_sorting;%s;#32333899;#fff]",
- ES"Enable this option to sort your inventory in reverse order")
- fs("tooltip[cb_ignore_hotbar;%s;#32333899;#fff]",
- ES"Enable this option to sort your inventory except the hotbar slots")
- fs("tooltip[cb_auto_sorting;%s;#32333899;#fff]",
- ES"Enable this option to sort your inventory automatically")
- end
-
- fs"container_end[]"
- end
+-- Get the formspec of the footer “Confirm trash” dialogue.
+local function confirm_trash_footer_fs(player, data, fs)
+ image(2.8, 10.65, 4.6, 0.7, PNG.bg_goto)
+ label(3.02, 11, "Confirm trash?")
+ image_button(5.07, 10.75, 1, 0.5, "", "confirm_trash_yes", "Yes")
+ image_button(6.17, 10.75, 1, 0.5, "", "confirm_trash_no", "No")
end
-local function get_footer(fs, data, player)
- local btn = {
- {"trash", ES"Clear inventory"},
- {"sort", ES"Sort inventory"},
- {"settings", ES"Settings"},
- {"home", ES"Go home"},
- }
+-- Get the formspec of the footer settings-dialogue.
+local function settings_footer_fs(player, data, fs)
+ fs"container[-0.06,0]"
+ image(2.2, 9, 6.1, 2.35, PNG.bg_content)
- for i, v in ipairs(btn) do
- local btn_name, tooltip = unpack(v)
- fs("style[%s;fgimg=%s;fgimg_hovered=%s;content_offset=0]",
- btn_name, PNG[btn_name], PNG[fmt("%s_hover", btn_name)])
- image_button(i + 3.43 - (i * 0.4), 11.43, 0.35, 0.35, "", btn_name, "")
- fs("tooltip[%s;%s;#32333899;#fff]", btn_name, tooltip)
+ local show_home = data.show_setting == "home"
+ local show_style = data.show_setting == "style"
+ local show_sorting = data.show_setting == "sorting"
+
+ fs"style[setting_home,setting_style,setting_sorting;font=bold;font_size=16;sound=i3_click]"
+ fs("style[setting_home:hovered;textcolor=%s]", show_home and colors.yellow or "#fff")
+ fs("style[setting_style:hovered;textcolor=%s]", show_style and colors.yellow or "#fff")
+ fs("style[setting_sorting:hovered;textcolor=%s]", show_sorting and colors.yellow or "#fff")
+
+ fs("style[setting_home;bgimg=%s;bgimg_hovered=%s;bgimg_middle=9;padding=-9;textcolor=%s]",
+ show_home and PNG.pagenum_hover or "", PNG.pagenum_hover,
+ show_home and colors.yellow or "#ddd")
+ fs("style[setting_style;bgimg=%s;bgimg_hovered=%s;bgimg_middle=9;padding=-9;textcolor=%s]",
+ show_style and PNG.pagenum_hover or "", PNG.pagenum_hover,
+ show_style and colors.yellow or "#ddd")
+ fs("style[setting_sorting;bgimg=%s;bgimg_hovered=%s;bgimg_middle=9;padding=-9;textcolor=%s]",
+ show_sorting and PNG.pagenum_hover or "", PNG.pagenum_hover,
+ show_sorting and colors.yellow or "#ddd")
+
+ local X = 2.5
+ button(X, 9.1, 1.6, 0.55, "setting_home", "Home")
+ button(X + 1.7, 9.1, 1.6, 0.55, "setting_style", "Style")
+ button(X + 3.4, 9.1, 1.6, 0.55, "setting_sorting", "Sorting")
+ image_button(X + 5.12, 9.2, 0.25, 0.25, PNG.cancel_hover .. "^\\[brighten", "close_settings", "")
+
+ if show_home then
+ local coords, c, str = {"X", "Y", "Z"}, 0, ES"No home set"
+
+ local home = data.home
+ if sethome then
+ -- i3 stores home coordinates with one decimal of precision, as the blow fmt() statement
+ -- assumes. So we need to trim sethome’s coordinates to one decimal, likewise.
+ local home_pos = sethome.get(player:get_player_name())
+ home = string.format("(%.1f,%.1f,%.1f)", home_pos.x, home_pos.y, home_pos.z)
+ end
+
+ if home then
+ str = home:gsub(",", " "):sub(2,-2):gsub("%.%d", ""):gsub(
+ "(%-?%d+)", function(a)
+ c++
+ return fmt("%s: ",
+ coords[c], colors.blue, a)
+ end)
+ end
+
+ hypertext(2.2, 9.9, 6, 0.6, "home_pos", fmt("%s", str))
+ fs("style[set_home;padding=20,10,-210,-10;fgimg=%s;fgimg_hovered=%s]", PNG.home_px, PNG.home_px_hover)
+ image_button(4.1, 10.4, 2.2, 0.7, "", "set_home", "")
+ label(4.9, 10.75, "Set home")
+
+ elseif show_style then
+ checkbox(2.6, 9.95, "cb_hide_tabs", "Hide tabs", tostring(data.hide_tabs))
+ checkbox(2.6, 10.4, "cb_legacy_inventory", "Legacy inventory", tostring(data.legacy_inventory))
+ checkbox(2.6, 10.85, "cb_wielditem_hud", "HUD description", tostring(data.wielditem_hud))
+
+ if not recipe_filter_set() then
+ checkbox(5.3, 10.85, "cb_collapse", "Collapse list", tostring(data.collapse))
+ end
+
+ local sign = (data.font_size > 0 and "+") or (data.font_size > 0 and "-") or ""
+ label(5.3, 9.95, ES"Font size" .. fmt(": %s", sign .. data.font_size))
+
+ local range = 8
+ fs("scrollbaroptions[min=-%u;max=%u;smallstep=1;largestep=1;thumbsize=2]", range, range)
+ fs("scrollbar[5.3,10.2;2.55,0.3;horizontal;sb_font_size;%d+]", data.font_size)
+
+ fs("tooltip[cb_hide_tabs;%s;#32333899;#fff]",
+ ES"Enable this option to change the style of the right panel")
+ fs("tooltip[cb_legacy_inventory;%s;#32333899;#fff]",
+ ES"Enable this option to set the classic inventory size in Minetest")
+ fs("tooltip[cb_wielditem_hud;%s;#32333899;#fff]",
+ ES"Enable this option to show the wielded item description in your HUD")
+ fs("tooltip[cb_collapse;%s;#32333899;#fff]",
+ ES"Enable this option to collapse the inventory list by grouping some items")
+
+ elseif show_sorting then
+ checkbox(2.6, 9.95, "cb_inv_compress", "Compression", tostring(data.inv_compress))
+ checkbox(2.6, 10.4, "cb_reverse_sorting", "Reverse mode", tostring(data.reverse_sorting))
+ checkbox(2.6, 10.85, "cb_ignore_hotbar", "Ignore hotbar", tostring(data.ignore_hotbar))
+ checkbox(5.3, 9.95, "cb_auto_sorting", "Automation", tostring(data.auto_sorting))
+
+ local methods = {}
+
+ for _, v in ipairs(i3.sorting_methods) do
+ local name = toupper(v.name)
+ insert(methods, name)
+ end
+
+ label(5.3, 10.4, ES"Sorting method:")
+ fs("dropdown[%f,%f;2.6,0.5;dd_sorting_method;%s;%u;true]", 5.3, 10.6, concat(methods, ","), data.sort)
+
+ local desc = i3.sorting_methods[data.sort].description
+ if desc then
+ tooltip(5.3, 10.6, 2.4, 0.5, ESC(desc))
+ end
+
+ fs("tooltip[cb_inv_compress;%s;#32333899;#fff]",
+ ES"Enable this option to compress your inventory")
+ fs("tooltip[cb_reverse_sorting;%s;#32333899;#fff]",
+ ES"Enable this option to sort your inventory in reverse order")
+ fs("tooltip[cb_ignore_hotbar;%s;#32333899;#fff]",
+ ES"Enable this option to sort your inventory except the hotbar slots")
+ fs("tooltip[cb_auto_sorting;%s;#32333899;#fff]",
+ ES"Enable this option to sort your inventory automatically")
+ end
+
+ fs"container_end[]"
+end
+
+-- Get the inventory-footer’s formspec.
+local function get_footer(fs, data, player)
+ -- Render individual footer buttons.
+ local starting_x = 3.43 - (.3 * (#i3.footer_buttons - 4)) -- Center the icons
+ for i, btn in ipairs(i3.footer_buttons) do
+ local btn_name = "footer_" .. btn.name
+ fs("style[%s;fgimg=%s;fgimg_hovered=%s^\\[brighten^\\[colorize:#fff:100;content_offset=0]",
+ btn_name, btn.image, btn.image)
+ image_button(i + starting_x - (i * 0.4), 11.43, 0.35, 0.35, "", btn_name, "")
+ fs("tooltip[%s;%s;#32333899;#fff]", btn_name, btn.description)
+ end
+
+ -- Render the current-selected button’s formspec, if one is active.
+ footer = i3.footer_buttons[data.footer_button]
+ if footer then
+ if footer.formspec then
+ footer.formspec(player, data, fs)
+ end
end
- show_settings(fs, data, player)
end
local function get_slots(fs, data, player)
@@ -1821,4 +1825,4 @@ local function make_fs(player, data)
return fs
end
-return make_fs, get_inventory_fs
+return make_fs, get_inventory_fs, confirm_trash_footer_fs, settings_footer_fs