2 --Copyright (C) 2015 PilzAdam
4 --This program is free software; you can redistribute it and/or modify
5 --it under the terms of the GNU Lesser General Public License as published by
6 --the Free Software Foundation; either version 2.1 of the License, or
7 --(at your option) any later version.
9 --This program is distributed in the hope that it will be useful,
10 --but WITHOUT ANY WARRANTY; without even the implied warranty of
11 --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 --GNU Lesser General Public License for more details.
14 --You should have received a copy of the GNU Lesser General Public License along
15 --with this program; if not, write to the Free Software Foundation, Inc.,
16 --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 local FILENAME = "settingtypes.txt"
20 local CHAR_CLASSES = {
22 VARIABLE = "[%w_%-%.]",
23 INTEGER = "[+-]?[%d]",
24 FLOAT = "[+-]?[%d%.]",
28 local function flags_to_table(flags)
29 return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
32 -- returns error message, or nil
33 local function parse_setting_line(settings, line, read_all, base_level, allow_secure)
35 -- strip carriage returns (CR, /r)
36 line = line:gsub("\r", "")
39 local comment = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$")
41 if settings.current_comment == "" then
42 settings.current_comment = comment
44 settings.current_comment = settings.current_comment .. "\n" .. comment
49 -- clear current_comment so only comments directly above a setting are bound to it
50 -- but keep a local reference to it for variables in the current line
51 local current_comment = settings.current_comment
52 settings.current_comment = ""
55 if line:match("^" .. CHAR_CLASSES.SPACE .. "*$") then
60 local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
62 table.insert(settings, {
64 level = stars:len() + base_level,
71 local first_part, name, readable_name, setting_type = line:match("^"
72 -- this first capture group matches the whole first part,
73 -- so we can later strip it from the rest of the line
75 .. "([" .. CHAR_CLASSES.VARIABLE .. "+)" -- variable name
76 .. CHAR_CLASSES.SPACE .. "*"
77 .. "%(([^%)]*)%)" -- readable name
78 .. CHAR_CLASSES.SPACE .. "*"
79 .. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
80 .. CHAR_CLASSES.SPACE .. "*"
83 if not first_part then
87 if name:match("secure%.[.]*") and not allow_secure then
88 return "Tried to add \"secure.\" setting"
91 if readable_name == "" then
94 local remaining_line = line:sub(first_part:len() + 1)
96 if setting_type == "int" then
97 local default, min, max = remaining_line:match("^"
98 -- first int is required, the last 2 are optional
99 .. "(" .. CHAR_CLASSES.INTEGER .. "+)" .. CHAR_CLASSES.SPACE .. "*"
100 .. "(" .. CHAR_CLASSES.INTEGER .. "*)" .. CHAR_CLASSES.SPACE .. "*"
101 .. "(" .. CHAR_CLASSES.INTEGER .. "*)"
104 if not default or not tonumber(default) then
105 return "Invalid integer setting"
110 table.insert(settings, {
112 readable_name = readable_name,
117 comment = current_comment,
122 if setting_type == "string"
123 or setting_type == "key" or setting_type == "v3f" then
124 local default = remaining_line:match("^(.*)$")
127 return "Invalid string setting"
129 if setting_type == "key" and not read_all then
130 -- ignore key type if read_all is false
134 table.insert(settings, {
136 readable_name = readable_name,
139 comment = current_comment,
144 if setting_type == "noise_params_2d"
145 or setting_type == "noise_params_3d" then
146 local default = remaining_line:match("^(.*)$")
149 return "Invalid string setting"
155 for match in default:gmatch("[+-]?[%d.-e]+") do -- All numeric characters
156 index = default:find("[+-]?[%d.-e]+", index) + match:len()
157 table.insert(values, match)
163 index = default:find("[^, ]", index)
166 flags = default:sub(index)
167 default = default:sub(1, index - 3) -- Make sure no flags in single-line format
169 table.insert(values, flags)
171 table.insert(settings, {
173 readable_name = readable_name,
186 persistence = values[8],
187 lacunarity = values[9],
191 comment = current_comment,
193 flags = flags_to_table("defaults,eased,absvalue")
198 if setting_type == "bool" then
199 if remaining_line ~= "false" and remaining_line ~= "true" then
200 return "Invalid boolean setting"
203 table.insert(settings, {
205 readable_name = readable_name,
207 default = remaining_line,
208 comment = current_comment,
213 if setting_type == "float" then
214 local default, min, max = remaining_line:match("^"
215 -- first float is required, the last 2 are optional
216 .. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "*"
217 .. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "*"
218 .. "(" .. CHAR_CLASSES.FLOAT .. "*)"
221 if not default or not tonumber(default) then
222 return "Invalid float setting"
227 table.insert(settings, {
229 readable_name = readable_name,
234 comment = current_comment,
239 if setting_type == "enum" then
240 local default, values = remaining_line:match("^"
241 -- first value (default) may be empty (i.e. is optional)
242 .. "(" .. CHAR_CLASSES.VARIABLE .. "*)" .. CHAR_CLASSES.SPACE .. "*"
243 .. "(" .. CHAR_CLASSES.FLAGS .. "+)"
246 if not default or values == "" then
247 return "Invalid enum setting"
250 table.insert(settings, {
252 readable_name = readable_name,
255 values = values:split(",", true),
256 comment = current_comment,
261 if setting_type == "path" or setting_type == "filepath" then
262 local default = remaining_line:match("^(.*)$")
265 return "Invalid path setting"
268 table.insert(settings, {
270 readable_name = readable_name,
273 comment = current_comment,
278 if setting_type == "flags" then
279 local default, possible = remaining_line:match("^"
280 -- first value (default) may be empty (i.e. is optional)
281 -- this is implemented by making the last value optional, and
282 -- swapping them around if it turns out empty.
283 .. "(" .. CHAR_CLASSES.FLAGS .. "+)" .. CHAR_CLASSES.SPACE .. "*"
284 .. "(" .. CHAR_CLASSES.FLAGS .. "*)"
287 if not default or not possible then
288 return "Invalid flags setting"
291 if possible == "" then
296 table.insert(settings, {
298 readable_name = readable_name,
301 possible = flags_to_table(possible),
302 comment = current_comment,
307 return "Invalid setting type \"" .. setting_type .. "\""
310 local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
311 -- store this helper variable in the table so it's easier to pass to parse_setting_line()
312 result.current_comment = ""
314 local line = file:read("*line")
316 local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure)
318 core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
320 line = file:read("*line")
323 result.current_comment = nil
326 -- read_all: whether to ignore certain setting types for GUI or not
327 -- parse_mods: whether to parse settingtypes.txt in mods and games
328 local function parse_config_file(read_all, parse_mods)
332 local builtin_path = core.get_builtin_path() .. FILENAME
333 local file = io.open(builtin_path, "r")
335 core.log("error", "Can't load " .. FILENAME)
339 parse_single_file(file, builtin_path, read_all, settings, 0, true)
346 local games_category_initialized = false
348 local game = pkgmgr.get_game(index)
350 local path = game.path .. DIR_DELIM .. FILENAME
351 local file = io.open(path, "r")
353 if not games_category_initialized then
354 fgettext_ne("Games") -- not used, but needed for xgettext
355 table.insert(settings, {
360 games_category_initialized = true
363 table.insert(settings, {
369 parse_single_file(file, path, read_all, settings, 2, false)
375 game = pkgmgr.get_game(index)
379 local mods_category_initialized = false
381 get_mods(core.get_modpath(), mods)
382 for _, mod in ipairs(mods) do
383 local path = mod.path .. DIR_DELIM .. FILENAME
384 local file = io.open(path, "r")
386 if not mods_category_initialized then
387 fgettext_ne("Mods") -- not used, but needed for xgettext
388 table.insert(settings, {
393 mods_category_initialized = true
396 table.insert(settings, {
402 parse_single_file(file, path, read_all, settings, 2, false)
409 local clientmods_category_initialized = false
410 local clientmods = {}
411 get_mods(core.get_clientmodpath(), clientmods)
412 for _, clientmod in ipairs(clientmods) do
413 local path = clientmod.path .. DIR_DELIM .. FILENAME
414 local file = io.open(path, "r")
416 if not clientmods_category_initialized then
417 fgettext_ne("Clientmods") -- not used, but needed for xgettext
418 table.insert(settings, {
423 clientmods_category_initialized = true
426 table.insert(settings, {
427 name = clientmod.name,
432 parse_single_file(file, path, read_all, settings, 2, false)
442 local function filter_settings(settings, searchstring)
443 if not searchstring or searchstring == "" then
447 -- Setup the keyword list
449 for word in searchstring:lower():gmatch("%S+") do
450 table.insert(keywords, word)
454 local category_stack = {}
455 local current_level = 0
456 local best_setting = nil
457 for _, entry in pairs(settings) do
458 if entry.type == "category" then
459 -- Remove all settingless categories
460 while #category_stack > 0 and entry.level <= current_level do
461 table.remove(category_stack, #category_stack)
462 if #category_stack > 0 then
463 current_level = category_stack[#category_stack].level
469 -- Push category onto stack
470 category_stack[#category_stack + 1] = entry
471 current_level = entry.level
473 -- See if setting matches keywords
474 local setting_score = 0
475 for k = 1, #keywords do
476 local keyword = keywords[k]
478 if string.find(entry.name:lower(), keyword, 1, true) then
479 setting_score = setting_score + 1
482 if entry.readable_name and
483 string.find(fgettext(entry.readable_name):lower(), keyword, 1, true) then
484 setting_score = setting_score + 1
488 string.find(fgettext_ne(entry.comment):lower(), keyword, 1, true) then
489 setting_score = setting_score + 1
493 -- Add setting to results if match
494 if setting_score > 0 then
495 -- Add parent categories
496 for _, category in pairs(category_stack) do
497 result[#result + 1] = category
502 result[#result + 1] = entry
503 entry.score = setting_score
505 if not best_setting or
506 setting_score > result[best_setting].score then
507 best_setting = #result
512 return result, best_setting or -1
515 local full_settings = parse_config_file(false, true)
516 local search_string = ""
517 local settings = full_settings
518 local selected_setting = 1
520 local function get_current_value(setting)
521 local value = core.settings:get(setting.name)
523 value = setting.default
528 local function get_current_np_group(setting)
529 local value = core.settings:get_np_group(setting.name)
534 table.insert(t, value.offset)
535 table.insert(t, value.scale)
536 table.insert(t, value.spread.x)
537 table.insert(t, value.spread.y)
538 table.insert(t, value.spread.z)
539 table.insert(t, value.seed)
540 table.insert(t, value.octaves)
541 table.insert(t, value.persistence)
542 table.insert(t, value.lacunarity)
543 table.insert(t, value.flags)
548 local function get_current_np_group_as_string(setting)
549 local value = core.settings:get_np_group(setting.name)
554 t = value.offset .. ", " ..
555 value.scale .. ", (" ..
556 value.spread.x .. ", " ..
557 value.spread.y .. ", " ..
558 value.spread.z .. "), " ..
559 value.seed .. ", " ..
560 value.octaves .. ", " ..
561 value.persistence .. ", " ..
563 if value.flags ~= "" then
564 t = t .. ", " .. value.flags
570 local checkboxes = {} -- handle checkboxes events
572 local function create_change_setting_formspec(dialogdata)
573 local setting = settings[selected_setting]
574 -- Final formspec will be created at the end of this function
575 -- Default values below, may be changed depending on setting type
578 local description_height = 3
581 -- Setting-specific formspec elements
582 if setting.type == "bool" then
583 local selected_index = 1
584 if core.is_yes(get_current_value(setting)) then
587 formspec = "dropdown[3," .. height .. ";4,1;dd_setting_value;"
588 .. fgettext("Disabled") .. "," .. fgettext("Enabled") .. ";"
589 .. selected_index .. "]"
590 height = height + 1.25
592 elseif setting.type == "enum" then
593 local selected_index = 0
594 formspec = "dropdown[3," .. height .. ";4,1;dd_setting_value;"
595 for index, value in ipairs(setting.values) do
596 -- translating value is not possible, since it's the value
597 -- that we set the setting to
598 formspec = formspec .. core.formspec_escape(value) .. ","
599 if get_current_value(setting) == value then
600 selected_index = index
603 if #setting.values > 0 then
604 formspec = formspec:sub(1, -2) -- remove trailing comma
606 formspec = formspec .. ";" .. selected_index .. "]"
607 height = height + 1.25
609 elseif setting.type == "path" or setting.type == "filepath" then
610 local current_value = dialogdata.selected_path
611 if not current_value then
612 current_value = get_current_value(setting)
614 formspec = "field[0.28," .. height + 0.15 .. ";8,1;te_setting_value;;"
615 .. core.formspec_escape(current_value) .. "]"
616 .. "button[8," .. height - 0.15 .. ";2,1;btn_browser_"
617 .. setting.type .. ";" .. fgettext("Browse") .. "]"
618 height = height + 1.15
620 elseif setting.type == "noise_params_2d" or setting.type == "noise_params_3d" then
621 local t = get_current_np_group(setting)
623 if setting.type == "noise_params_2d" then
627 -- More space for 3x3 fields
628 description_height = description_height - 1.5
629 height = height - 1.5
632 local function add_field(x, name, label, value)
633 fields[#fields + 1] = ("field[%f,%f;3.3,1;%s;%s;%s]"):format(
634 x, height, name, label, core.formspec_escape(value or "")
638 height = height + 0.3
639 add_field(0.3, "te_offset", fgettext("Offset"), t[1])
640 add_field(3.6, "te_scale", fgettext("Scale"), t[2])
641 add_field(6.9, "te_seed", fgettext("Seed"), t[6])
642 height = height + 1.1
645 add_field(0.3, "te_spreadx", fgettext("X spread"), t[3])
646 if dimension == 3 then
647 add_field(3.6, "te_spready", fgettext("Y spread"), t[4])
649 fields[#fields + 1] = "label[4," .. height - 0.2 .. ";" ..
650 fgettext("2D Noise") .. "]"
652 add_field(6.9, "te_spreadz", fgettext("Z spread"), t[5])
653 height = height + 1.1
656 add_field(0.3, "te_octaves", fgettext("Octaves"), t[7])
657 add_field(3.6, "te_persist", fgettext("Persistence"), t[8])
658 add_field(6.9, "te_lacun", fgettext("Lacunarity"), t[9])
659 height = height + 1.1
662 local enabled_flags = flags_to_table(t[10])
664 for _, name in ipairs(enabled_flags) do
665 -- Index by name, to avoid iterating over all enabled_flags for every possible flag.
668 for _, name in ipairs(setting.flags) do
669 local checkbox_name = "cb_" .. name
670 local is_enabled = flags[name] == true -- to get false if nil
671 checkboxes[checkbox_name] = is_enabled
674 formspec = table.concat(fields)
675 .. "checkbox[0.5," .. height - 0.6 .. ";cb_defaults;"
676 --[[~ "defaults" is a noise parameter flag.
677 It describes the default processing options
678 for noise settings in main menu -> "All Settings". ]]
679 .. fgettext("defaults") .. ";" -- defaults
680 .. tostring(flags["defaults"] == true) .. "]" -- to get false if nil
681 .. "checkbox[5," .. height - 0.6 .. ";cb_eased;"
682 --[[~ "eased" is a noise parameter flag.
683 It is used to make the map smoother and
684 can be enabled in noise settings in
685 main menu -> "All Settings". ]]
686 .. fgettext("eased") .. ";" -- eased
687 .. tostring(flags["eased"] == true) .. "]"
688 .. "checkbox[5," .. height - 0.15 .. ";cb_absvalue;"
689 --[[~ "absvalue" is a noise parameter flag.
690 It is short for "absolute value".
691 It can be enabled in noise settings in
692 main menu -> "All Settings". ]]
693 .. fgettext("absvalue") .. ";" -- absvalue
694 .. tostring(flags["absvalue"] == true) .. "]"
697 elseif setting.type == "v3f" then
698 local val = get_current_value(setting)
700 for line in val:gmatch("[+-]?[%d.-e]+") do -- All numeric characters
701 table.insert(v3f, line)
704 height = height + 0.3
706 .. "field[0.3," .. height .. ";3.3,1;te_x;"
707 .. fgettext("X") .. ";" -- X
708 .. core.formspec_escape(v3f[1] or "") .. "]"
709 .. "field[3.6," .. height .. ";3.3,1;te_y;"
710 .. fgettext("Y") .. ";" -- Y
711 .. core.formspec_escape(v3f[2] or "") .. "]"
712 .. "field[6.9," .. height .. ";3.3,1;te_z;"
713 .. fgettext("Z") .. ";" -- Z
714 .. core.formspec_escape(v3f[3] or "") .. "]"
715 height = height + 1.1
717 elseif setting.type == "flags" then
718 local current_flags = flags_to_table(get_current_value(setting))
720 for _, name in ipairs(current_flags) do
721 -- Index by name, to avoid iterating over all enabled_flags for every possible flag.
722 if name:sub(1, 2) == "no" then
723 flags[name:sub(3)] = false
728 local flags_count = #setting.possible / 2
729 local max_height = math.ceil(flags_count / 2) / 2
731 -- More space for flags
732 description_height = description_height - 1
735 local fields = {} -- To build formspec
737 for _, name in ipairs(setting.possible) do
738 if name:sub(1, 2) ~= "no" then
740 local y = height + j / 2 - 0.75
741 if j - 1 >= flags_count / 2 then -- 2nd column
746 local checkbox_name = "cb_" .. name
747 local is_enabled = flags[name] == true -- to get false if nil
748 checkboxes[checkbox_name] = is_enabled
750 fields[#fields + 1] = ("checkbox[%f,%f;%s;%s;%s]"):format(
751 x, y, checkbox_name, name, tostring(is_enabled)
755 formspec = table.concat(fields)
756 height = height + max_height + 0.25
759 -- TODO: fancy input for float, int
760 local text = get_current_value(setting)
761 if dialogdata.error_message and dialogdata.entered_text then
762 text = dialogdata.entered_text
764 formspec = "field[0.28," .. height + 0.15 .. ";" .. width .. ",1;te_setting_value;;"
765 .. core.formspec_escape(text) .. "]"
766 height = height + 1.15
769 -- Box good, textarea bad. Calculate textarea size from box.
770 local function create_textfield(size, label, text, bg_color)
775 h = size.h * 1.16 + 0.12
777 return ("box[%f,%f;%f,%f;%s]textarea[%f,%f;%f,%f;;%s;%s]"):format(
778 size.x, size.y, size.w, size.h, bg_color or "#000",
779 textarea.x, textarea.y, textarea.w, textarea.h,
780 core.formspec_escape(label), core.formspec_escape(text)
785 -- When there's an error: Shrink description textarea and add error below
786 if dialogdata.error_message then
789 y = description_height - 0.4,
793 formspec = formspec ..
794 create_textfield(error_box, "", dialogdata.error_message, "#600")
795 description_height = description_height - 0.75
798 -- Get description field
799 local description_box = {
803 h = description_height
806 local setting_name = setting.name
807 if setting.readable_name then
808 setting_name = fgettext_ne(setting.readable_name) ..
809 " (" .. setting.name .. ")"
813 if setting.comment == "" then
814 comment_text = fgettext_ne("(No description of setting given)")
816 comment_text = fgettext_ne(setting.comment)
820 "size[" .. width .. "," .. height + 0.25 .. ",true]" ..
821 create_textfield(description_box, setting_name, comment_text) ..
823 "button[" .. width / 2 - 2.5 .. "," .. height - 0.4 .. ";2.5,1;btn_done;" ..
824 fgettext("Save") .. "]" ..
825 "button[" .. width / 2 .. "," .. height - 0.4 .. ";2.5,1;btn_cancel;" ..
826 fgettext("Cancel") .. "]"
830 local function handle_change_setting_buttons(this, fields)
831 local setting = settings[selected_setting]
832 if fields["btn_done"] or fields["key_enter"] then
833 if setting.type == "bool" then
834 local new_value = fields["dd_setting_value"]
835 -- Note: new_value is the actual (translated) value shown in the dropdown
836 core.settings:set_bool(setting.name, new_value == fgettext("Enabled"))
838 elseif setting.type == "enum" then
839 local new_value = fields["dd_setting_value"]
840 core.settings:set(setting.name, new_value)
842 elseif setting.type == "int" then
843 local new_value = tonumber(fields["te_setting_value"])
844 if not new_value or math.floor(new_value) ~= new_value then
845 this.data.error_message = fgettext_ne("Please enter a valid integer.")
846 this.data.entered_text = fields["te_setting_value"]
847 core.update_formspec(this:get_formspec())
850 if setting.min and new_value < setting.min then
851 this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min)
852 this.data.entered_text = fields["te_setting_value"]
853 core.update_formspec(this:get_formspec())
856 if setting.max and new_value > setting.max then
857 this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max)
858 this.data.entered_text = fields["te_setting_value"]
859 core.update_formspec(this:get_formspec())
862 core.settings:set(setting.name, new_value)
864 elseif setting.type == "float" then
865 local new_value = tonumber(fields["te_setting_value"])
866 if not new_value then
867 this.data.error_message = fgettext_ne("Please enter a valid number.")
868 this.data.entered_text = fields["te_setting_value"]
869 core.update_formspec(this:get_formspec())
872 if setting.min and new_value < setting.min then
873 this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min)
874 this.data.entered_text = fields["te_setting_value"]
875 core.update_formspec(this:get_formspec())
878 if setting.max and new_value > setting.max then
879 this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max)
880 this.data.entered_text = fields["te_setting_value"]
881 core.update_formspec(this:get_formspec())
884 core.settings:set(setting.name, new_value)
886 elseif setting.type == "flags" then
888 for _, name in ipairs(setting.possible) do
889 if name:sub(1, 2) ~= "no" then
890 if checkboxes["cb_" .. name] then
891 table.insert(values, name)
893 table.insert(values, "no" .. name)
900 local new_value = table.concat(values, ", ")
901 core.settings:set(setting.name, new_value)
903 elseif setting.type == "noise_params_2d" or setting.type == "noise_params_3d" then
905 for _, name in ipairs(setting.flags) do
906 if checkboxes["cb_" .. name] then
907 table.insert(np_flags, name)
913 if setting.type == "noise_params_2d" then
914 fields["te_spready"] = fields["te_spreadz"]
917 offset = fields["te_offset"],
918 scale = fields["te_scale"],
920 x = fields["te_spreadx"],
921 y = fields["te_spready"],
922 z = fields["te_spreadz"]
924 seed = fields["te_seed"],
925 octaves = fields["te_octaves"],
926 persistence = fields["te_persist"],
927 lacunarity = fields["te_lacun"],
928 flags = table.concat(np_flags, ", ")
930 core.settings:set_np_group(setting.name, new_value)
932 elseif setting.type == "v3f" then
933 local new_value = "("
934 .. fields["te_x"] .. ", "
935 .. fields["te_y"] .. ", "
936 .. fields["te_z"] .. ")"
937 core.settings:set(setting.name, new_value)
940 local new_value = fields["te_setting_value"]
941 core.settings:set(setting.name, new_value)
943 core.settings:write()
948 if fields["btn_cancel"] then
953 if fields["btn_browser_path"] then
954 core.show_path_select_dialog("dlg_browse_path",
955 fgettext_ne("Select directory"), false)
958 if fields["btn_browser_filepath"] then
959 core.show_path_select_dialog("dlg_browse_path",
960 fgettext_ne("Select file"), true)
963 if fields["dlg_browse_path_accepted"] then
964 this.data.selected_path = fields["dlg_browse_path_accepted"]
965 core.update_formspec(this:get_formspec())
968 if setting.type == "flags"
969 or setting.type == "noise_params_2d"
970 or setting.type == "noise_params_3d" then
971 for name, value in pairs(fields) do
972 if name:sub(1, 3) == "cb_" then
973 checkboxes[name] = value == "true"
981 local function create_settings_formspec(tabview, _, tabdata)
982 local formspec = "size[12,5.4;true]" ..
983 "tablecolumns[color;tree;text,width=28;text]" ..
984 "tableoptions[background=#00000000;border=false]" ..
985 "field[0.3,0.1;10.2,1;search_string;;" .. core.formspec_escape(search_string) .. "]" ..
986 "field_close_on_enter[search_string;false]" ..
987 "button[10.2,-0.2;2,1;search;" .. fgettext("Search") .. "]" ..
988 "table[0,0.8;12,3.5;list_settings;"
990 local current_level = 0
991 for _, entry in ipairs(settings) do
993 if not core.settings:get_bool("main_menu_technical_settings") and entry.readable_name then
994 name = fgettext_ne(entry.readable_name)
999 if entry.type == "category" then
1000 current_level = entry.level
1001 formspec = formspec .. "#FFFF00," .. current_level .. "," .. fgettext(name) .. ",,"
1003 elseif entry.type == "bool" then
1004 local value = get_current_value(entry)
1005 if core.is_yes(value) then
1006 value = fgettext("Enabled")
1008 value = fgettext("Disabled")
1010 formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
1013 elseif entry.type == "key" then --luacheck: ignore
1014 -- ignore key settings, since we have a special dialog for them
1016 elseif entry.type == "noise_params_2d" or entry.type == "noise_params_3d" then
1017 formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
1018 .. core.formspec_escape(get_current_np_group_as_string(entry)) .. ","
1021 formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
1022 .. core.formspec_escape(get_current_value(entry)) .. ","
1026 if #settings > 0 then
1027 formspec = formspec:sub(1, -2) -- remove trailing comma
1029 formspec = formspec .. ";" .. selected_setting .. "]" ..
1030 "button[0,4.9;4,1;btn_back;".. fgettext("< Back to Settings page") .. "]" ..
1031 "button[10,4.9;2,1;btn_edit;" .. fgettext("Edit") .. "]" ..
1032 "button[7,4.9;3,1;btn_restore;" .. fgettext("Restore Default") .. "]" ..
1033 "checkbox[0,4.3;cb_tech_settings;" .. fgettext("Show technical names") .. ";"
1034 .. dump(core.settings:get_bool("main_menu_technical_settings")) .. "]"
1039 local function handle_settings_buttons(this, fields, tabname, tabdata)
1040 local list_enter = false
1041 if fields["list_settings"] then
1042 selected_setting = core.get_table_index("list_settings")
1043 if core.explode_table_event(fields["list_settings"]).type == "DCL" then
1044 -- Directly toggle booleans
1045 local setting = settings[selected_setting]
1046 if setting and setting.type == "bool" then
1047 local current_value = get_current_value(setting)
1048 core.settings:set_bool(setting.name, not core.is_yes(current_value))
1049 core.settings:write()
1059 if fields.search or fields.key_enter_field == "search_string" then
1060 if search_string == fields.search_string then
1061 if selected_setting > 0 then
1062 -- Go to next result on enter press
1063 local i = selected_setting + 1
1064 local looped = false
1065 while i > #settings or settings[i].type == "category" do
1067 if i > #settings then
1068 -- Stop infinte looping
1076 selected_setting = i
1077 core.update_formspec(this:get_formspec())
1081 -- Search for setting
1082 search_string = fields.search_string
1083 settings, selected_setting = filter_settings(full_settings, search_string)
1084 core.update_formspec(this:get_formspec())
1089 if fields["btn_edit"] or list_enter then
1090 local setting = settings[selected_setting]
1091 if setting and setting.type ~= "category" then
1092 local edit_dialog = dialog_create("change_setting",
1093 create_change_setting_formspec, handle_change_setting_buttons)
1094 edit_dialog:set_parent(this)
1101 if fields["btn_restore"] then
1102 local setting = settings[selected_setting]
1103 if setting and setting.type ~= "category" then
1104 core.settings:remove(setting.name)
1105 core.settings:write()
1106 core.update_formspec(this:get_formspec())
1111 if fields["btn_back"] then
1116 if fields["cb_tech_settings"] then
1117 core.settings:set("main_menu_technical_settings", fields["cb_tech_settings"])
1118 core.settings:write()
1119 core.update_formspec(this:get_formspec())
1126 function create_adv_settings_dlg()
1127 local dlg = dialog_create("settings_advanced",
1128 create_settings_formspec,
1129 handle_settings_buttons,
1135 -- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'.
1136 -- For RUN_IN_PLACE the generated files may appear in the 'bin' folder.
1137 -- See comment and alternative line at the end of 'generate_from_settingtypes.lua'.
1139 --assert(loadfile(core.get_builtin_path().."mainmenu"..DIR_DELIM..
1140 -- "generate_from_settingtypes.lua"))(parse_config_file(true, false))