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)
412 local function filter_settings(settings, searchstring)
413 if not searchstring or searchstring == "" then
417 -- Setup the keyword list
419 for word in searchstring:lower():gmatch("%S+") do
420 table.insert(keywords, word)
424 local category_stack = {}
425 local current_level = 0
426 local best_setting = nil
427 for _, entry in pairs(settings) do
428 if entry.type == "category" then
429 -- Remove all settingless categories
430 while #category_stack > 0 and entry.level <= current_level do
431 table.remove(category_stack, #category_stack)
432 if #category_stack > 0 then
433 current_level = category_stack[#category_stack].level
439 -- Push category onto stack
440 category_stack[#category_stack + 1] = entry
441 current_level = entry.level
443 -- See if setting matches keywords
444 local setting_score = 0
445 for k = 1, #keywords do
446 local keyword = keywords[k]
448 if string.find(entry.name:lower(), keyword, 1, true) then
449 setting_score = setting_score + 1
452 if entry.readable_name and
453 string.find(fgettext(entry.readable_name):lower(), keyword, 1, true) then
454 setting_score = setting_score + 1
458 string.find(fgettext_ne(entry.comment):lower(), keyword, 1, true) then
459 setting_score = setting_score + 1
463 -- Add setting to results if match
464 if setting_score > 0 then
465 -- Add parent categories
466 for _, category in pairs(category_stack) do
467 result[#result + 1] = category
472 result[#result + 1] = entry
473 entry.score = setting_score
475 if not best_setting or
476 setting_score > result[best_setting].score then
477 best_setting = #result
482 return result, best_setting or -1
485 local full_settings = parse_config_file(false, true)
486 local search_string = ""
487 local settings = full_settings
488 local selected_setting = 1
490 local function get_current_value(setting)
491 local value = core.settings:get(setting.name)
493 value = setting.default
498 local function get_current_np_group(setting)
499 local value = core.settings:get_np_group(setting.name)
504 table.insert(t, value.offset)
505 table.insert(t, value.scale)
506 table.insert(t, value.spread.x)
507 table.insert(t, value.spread.y)
508 table.insert(t, value.spread.z)
509 table.insert(t, value.seed)
510 table.insert(t, value.octaves)
511 table.insert(t, value.persistence)
512 table.insert(t, value.lacunarity)
513 table.insert(t, value.flags)
518 local function get_current_np_group_as_string(setting)
519 local value = core.settings:get_np_group(setting.name)
524 t = value.offset .. ", " ..
525 value.scale .. ", (" ..
526 value.spread.x .. ", " ..
527 value.spread.y .. ", " ..
528 value.spread.z .. "), " ..
529 value.seed .. ", " ..
530 value.octaves .. ", " ..
531 value.persistence .. ", " ..
533 if value.flags ~= "" then
534 t = t .. ", " .. value.flags
540 local checkboxes = {} -- handle checkboxes events
542 local function create_change_setting_formspec(dialogdata)
543 local setting = settings[selected_setting]
544 -- Final formspec will be created at the end of this function
545 -- Default values below, may be changed depending on setting type
548 local description_height = 3
551 -- Setting-specific formspec elements
552 if setting.type == "bool" then
553 local selected_index = 1
554 if core.is_yes(get_current_value(setting)) then
557 formspec = "dropdown[3," .. height .. ";4,1;dd_setting_value;"
558 .. fgettext("Disabled") .. "," .. fgettext("Enabled") .. ";"
559 .. selected_index .. "]"
560 height = height + 1.25
562 elseif setting.type == "enum" then
563 local selected_index = 0
564 formspec = "dropdown[3," .. height .. ";4,1;dd_setting_value;"
565 for index, value in ipairs(setting.values) do
566 -- translating value is not possible, since it's the value
567 -- that we set the setting to
568 formspec = formspec .. core.formspec_escape(value) .. ","
569 if get_current_value(setting) == value then
570 selected_index = index
573 if #setting.values > 0 then
574 formspec = formspec:sub(1, -2) -- remove trailing comma
576 formspec = formspec .. ";" .. selected_index .. "]"
577 height = height + 1.25
579 elseif setting.type == "path" or setting.type == "filepath" then
580 local current_value = dialogdata.selected_path
581 if not current_value then
582 current_value = get_current_value(setting)
584 formspec = "field[0.28," .. height + 0.15 .. ";8,1;te_setting_value;;"
585 .. core.formspec_escape(current_value) .. "]"
586 .. "button[8," .. height - 0.15 .. ";2,1;btn_browser_"
587 .. setting.type .. ";" .. fgettext("Browse") .. "]"
588 height = height + 1.15
590 elseif setting.type == "noise_params_2d" or setting.type == "noise_params_3d" then
591 local t = get_current_np_group(setting)
593 if setting.type == "noise_params_2d" then
597 -- More space for 3x3 fields
598 description_height = description_height - 1.5
599 height = height - 1.5
602 local function add_field(x, name, label, value)
603 fields[#fields + 1] = ("field[%f,%f;3.3,1;%s;%s;%s]"):format(
604 x, height, name, label, core.formspec_escape(value or "")
608 height = height + 0.3
609 add_field(0.3, "te_offset", fgettext("Offset"), t[1])
610 add_field(3.6, "te_scale", fgettext("Scale"), t[2])
611 add_field(6.9, "te_seed", fgettext("Seed"), t[6])
612 height = height + 1.1
615 add_field(0.3, "te_spreadx", fgettext("X spread"), t[3])
616 if dimension == 3 then
617 add_field(3.6, "te_spready", fgettext("Y spread"), t[4])
619 fields[#fields + 1] = "label[4," .. height - 0.2 .. ";" ..
620 fgettext("2D Noise") .. "]"
622 add_field(6.9, "te_spreadz", fgettext("Z spread"), t[5])
623 height = height + 1.1
626 add_field(0.3, "te_octaves", fgettext("Octaves"), t[7])
627 add_field(3.6, "te_persist", fgettext("Persistence"), t[8])
628 add_field(6.9, "te_lacun", fgettext("Lacunarity"), t[9])
629 height = height + 1.1
632 local enabled_flags = flags_to_table(t[10])
634 for _, name in ipairs(enabled_flags) do
635 -- Index by name, to avoid iterating over all enabled_flags for every possible flag.
638 for _, name in ipairs(setting.flags) do
639 local checkbox_name = "cb_" .. name
640 local is_enabled = flags[name] == true -- to get false if nil
641 checkboxes[checkbox_name] = is_enabled
644 formspec = table.concat(fields)
645 .. "checkbox[0.5," .. height - 0.6 .. ";cb_defaults;"
646 --[[~ "defaults" is a noise parameter flag.
647 It describes the default processing options
648 for noise settings in main menu -> "All Settings". ]]
649 .. fgettext("defaults") .. ";" -- defaults
650 .. tostring(flags["defaults"] == true) .. "]" -- to get false if nil
651 .. "checkbox[5," .. height - 0.6 .. ";cb_eased;"
652 --[[~ "eased" is a noise parameter flag.
653 It is used to make the map smoother and
654 can be enabled in noise settings in
655 main menu -> "All Settings". ]]
656 .. fgettext("eased") .. ";" -- eased
657 .. tostring(flags["eased"] == true) .. "]"
658 .. "checkbox[5," .. height - 0.15 .. ";cb_absvalue;"
659 --[[~ "absvalue" is a noise parameter flag.
660 It is short for "absolute value".
661 It can be enabled in noise settings in
662 main menu -> "All Settings". ]]
663 .. fgettext("absvalue") .. ";" -- absvalue
664 .. tostring(flags["absvalue"] == true) .. "]"
667 elseif setting.type == "v3f" then
668 local val = get_current_value(setting)
670 for line in val:gmatch("[+-]?[%d.-e]+") do -- All numeric characters
671 table.insert(v3f, line)
674 height = height + 0.3
676 .. "field[0.3," .. height .. ";3.3,1;te_x;"
677 .. fgettext("X") .. ";" -- X
678 .. core.formspec_escape(v3f[1] or "") .. "]"
679 .. "field[3.6," .. height .. ";3.3,1;te_y;"
680 .. fgettext("Y") .. ";" -- Y
681 .. core.formspec_escape(v3f[2] or "") .. "]"
682 .. "field[6.9," .. height .. ";3.3,1;te_z;"
683 .. fgettext("Z") .. ";" -- Z
684 .. core.formspec_escape(v3f[3] or "") .. "]"
685 height = height + 1.1
687 elseif setting.type == "flags" then
688 local current_flags = flags_to_table(get_current_value(setting))
690 for _, name in ipairs(current_flags) do
691 -- Index by name, to avoid iterating over all enabled_flags for every possible flag.
692 if name:sub(1, 2) == "no" then
693 flags[name:sub(3)] = false
698 local flags_count = #setting.possible / 2
699 local max_height = math.ceil(flags_count / 2) / 2
701 -- More space for flags
702 description_height = description_height - 1
705 local fields = {} -- To build formspec
707 for _, name in ipairs(setting.possible) do
708 if name:sub(1, 2) ~= "no" then
710 local y = height + j / 2 - 0.75
711 if j - 1 >= flags_count / 2 then -- 2nd column
716 local checkbox_name = "cb_" .. name
717 local is_enabled = flags[name] == true -- to get false if nil
718 checkboxes[checkbox_name] = is_enabled
720 fields[#fields + 1] = ("checkbox[%f,%f;%s;%s;%s]"):format(
721 x, y, checkbox_name, name, tostring(is_enabled)
725 formspec = table.concat(fields)
726 height = height + max_height + 0.25
729 -- TODO: fancy input for float, int
730 local text = get_current_value(setting)
731 if dialogdata.error_message and dialogdata.entered_text then
732 text = dialogdata.entered_text
734 formspec = "field[0.28," .. height + 0.15 .. ";" .. width .. ",1;te_setting_value;;"
735 .. core.formspec_escape(text) .. "]"
736 height = height + 1.15
739 -- Box good, textarea bad. Calculate textarea size from box.
740 local function create_textfield(size, label, text, bg_color)
745 h = size.h * 1.16 + 0.12
747 return ("box[%f,%f;%f,%f;%s]textarea[%f,%f;%f,%f;;%s;%s]"):format(
748 size.x, size.y, size.w, size.h, bg_color or "#000",
749 textarea.x, textarea.y, textarea.w, textarea.h,
750 core.formspec_escape(label), core.formspec_escape(text)
755 -- When there's an error: Shrink description textarea and add error below
756 if dialogdata.error_message then
759 y = description_height - 0.4,
763 formspec = formspec ..
764 create_textfield(error_box, "", dialogdata.error_message, "#600")
765 description_height = description_height - 0.75
768 -- Get description field
769 local description_box = {
773 h = description_height
776 local setting_name = setting.name
777 if setting.readable_name then
778 setting_name = fgettext_ne(setting.readable_name) ..
779 " (" .. setting.name .. ")"
783 if setting.comment == "" then
784 comment_text = fgettext_ne("(No description of setting given)")
786 comment_text = fgettext_ne(setting.comment)
790 "size[" .. width .. "," .. height + 0.25 .. ",true]" ..
791 create_textfield(description_box, setting_name, comment_text) ..
793 "button[" .. width / 2 - 2.5 .. "," .. height - 0.4 .. ";2.5,1;btn_done;" ..
794 fgettext("Save") .. "]" ..
795 "button[" .. width / 2 .. "," .. height - 0.4 .. ";2.5,1;btn_cancel;" ..
796 fgettext("Cancel") .. "]"
800 local function handle_change_setting_buttons(this, fields)
801 local setting = settings[selected_setting]
802 if fields["btn_done"] or fields["key_enter"] then
803 if setting.type == "bool" then
804 local new_value = fields["dd_setting_value"]
805 -- Note: new_value is the actual (translated) value shown in the dropdown
806 core.settings:set_bool(setting.name, new_value == fgettext("Enabled"))
808 elseif setting.type == "enum" then
809 local new_value = fields["dd_setting_value"]
810 core.settings:set(setting.name, new_value)
812 elseif setting.type == "int" then
813 local new_value = tonumber(fields["te_setting_value"])
814 if not new_value or math.floor(new_value) ~= new_value then
815 this.data.error_message = fgettext_ne("Please enter a valid integer.")
816 this.data.entered_text = fields["te_setting_value"]
817 core.update_formspec(this:get_formspec())
820 if setting.min and new_value < setting.min then
821 this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min)
822 this.data.entered_text = fields["te_setting_value"]
823 core.update_formspec(this:get_formspec())
826 if setting.max and new_value > setting.max then
827 this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max)
828 this.data.entered_text = fields["te_setting_value"]
829 core.update_formspec(this:get_formspec())
832 core.settings:set(setting.name, new_value)
834 elseif setting.type == "float" then
835 local new_value = tonumber(fields["te_setting_value"])
836 if not new_value then
837 this.data.error_message = fgettext_ne("Please enter a valid number.")
838 this.data.entered_text = fields["te_setting_value"]
839 core.update_formspec(this:get_formspec())
842 if setting.min and new_value < setting.min then
843 this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min)
844 this.data.entered_text = fields["te_setting_value"]
845 core.update_formspec(this:get_formspec())
848 if setting.max and new_value > setting.max then
849 this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max)
850 this.data.entered_text = fields["te_setting_value"]
851 core.update_formspec(this:get_formspec())
854 core.settings:set(setting.name, new_value)
856 elseif setting.type == "flags" then
858 for _, name in ipairs(setting.possible) do
859 if name:sub(1, 2) ~= "no" then
860 if checkboxes["cb_" .. name] then
861 table.insert(values, name)
863 table.insert(values, "no" .. name)
870 local new_value = table.concat(values, ", ")
871 core.settings:set(setting.name, new_value)
873 elseif setting.type == "noise_params_2d" or setting.type == "noise_params_3d" then
875 for _, name in ipairs(setting.flags) do
876 if checkboxes["cb_" .. name] then
877 table.insert(np_flags, name)
883 if setting.type == "noise_params_2d" then
884 fields["te_spready"] = fields["te_spreadz"]
887 offset = fields["te_offset"],
888 scale = fields["te_scale"],
890 x = fields["te_spreadx"],
891 y = fields["te_spready"],
892 z = fields["te_spreadz"]
894 seed = fields["te_seed"],
895 octaves = fields["te_octaves"],
896 persistence = fields["te_persist"],
897 lacunarity = fields["te_lacun"],
898 flags = table.concat(np_flags, ", ")
900 core.settings:set_np_group(setting.name, new_value)
902 elseif setting.type == "v3f" then
903 local new_value = "("
904 .. fields["te_x"] .. ", "
905 .. fields["te_y"] .. ", "
906 .. fields["te_z"] .. ")"
907 core.settings:set(setting.name, new_value)
910 local new_value = fields["te_setting_value"]
911 core.settings:set(setting.name, new_value)
913 core.settings:write()
918 if fields["btn_cancel"] then
923 if fields["btn_browser_path"] then
924 core.show_path_select_dialog("dlg_browse_path",
925 fgettext_ne("Select directory"), false)
928 if fields["btn_browser_filepath"] then
929 core.show_path_select_dialog("dlg_browse_path",
930 fgettext_ne("Select file"), true)
933 if fields["dlg_browse_path_accepted"] then
934 this.data.selected_path = fields["dlg_browse_path_accepted"]
935 core.update_formspec(this:get_formspec())
938 if setting.type == "flags"
939 or setting.type == "noise_params_2d"
940 or setting.type == "noise_params_3d" then
941 for name, value in pairs(fields) do
942 if name:sub(1, 3) == "cb_" then
943 checkboxes[name] = value == "true"
951 local function create_settings_formspec(tabview, _, tabdata)
952 local formspec = "size[12,5.4;true]" ..
953 "tablecolumns[color;tree;text,width=28;text]" ..
954 "tableoptions[background=#00000000;border=false]" ..
955 "field[0.3,0.1;10.2,1;search_string;;" .. core.formspec_escape(search_string) .. "]" ..
956 "field_close_on_enter[search_string;false]" ..
957 "button[10.2,-0.2;2,1;search;" .. fgettext("Search") .. "]" ..
958 "table[0,0.8;12,3.5;list_settings;"
960 local current_level = 0
961 for _, entry in ipairs(settings) do
963 if not core.settings:get_bool("main_menu_technical_settings") and entry.readable_name then
964 name = fgettext_ne(entry.readable_name)
969 if entry.type == "category" then
970 current_level = entry.level
971 formspec = formspec .. "#FFFF00," .. current_level .. "," .. fgettext(name) .. ",,"
973 elseif entry.type == "bool" then
974 local value = get_current_value(entry)
975 if core.is_yes(value) then
976 value = fgettext("Enabled")
978 value = fgettext("Disabled")
980 formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
983 elseif entry.type == "key" then --luacheck: ignore
984 -- ignore key settings, since we have a special dialog for them
986 elseif entry.type == "noise_params_2d" or entry.type == "noise_params_3d" then
987 formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
988 .. core.formspec_escape(get_current_np_group_as_string(entry)) .. ","
991 formspec = formspec .. "," .. (current_level + 1) .. "," .. core.formspec_escape(name) .. ","
992 .. core.formspec_escape(get_current_value(entry)) .. ","
996 if #settings > 0 then
997 formspec = formspec:sub(1, -2) -- remove trailing comma
999 formspec = formspec .. ";" .. selected_setting .. "]" ..
1000 "button[0,4.9;4,1;btn_back;".. fgettext("< Back to Settings page") .. "]" ..
1001 "button[10,4.9;2,1;btn_edit;" .. fgettext("Edit") .. "]" ..
1002 "button[7,4.9;3,1;btn_restore;" .. fgettext("Restore Default") .. "]" ..
1003 "checkbox[0,4.3;cb_tech_settings;" .. fgettext("Show technical names") .. ";"
1004 .. dump(core.settings:get_bool("main_menu_technical_settings")) .. "]"
1009 local function handle_settings_buttons(this, fields, tabname, tabdata)
1010 local list_enter = false
1011 if fields["list_settings"] then
1012 selected_setting = core.get_table_index("list_settings")
1013 if core.explode_table_event(fields["list_settings"]).type == "DCL" then
1014 -- Directly toggle booleans
1015 local setting = settings[selected_setting]
1016 if setting and setting.type == "bool" then
1017 local current_value = get_current_value(setting)
1018 core.settings:set_bool(setting.name, not core.is_yes(current_value))
1019 core.settings:write()
1029 if fields.search or fields.key_enter_field == "search_string" then
1030 if search_string == fields.search_string then
1031 if selected_setting > 0 then
1032 -- Go to next result on enter press
1033 local i = selected_setting + 1
1034 local looped = false
1035 while i > #settings or settings[i].type == "category" do
1037 if i > #settings then
1038 -- Stop infinte looping
1046 selected_setting = i
1047 core.update_formspec(this:get_formspec())
1051 -- Search for setting
1052 search_string = fields.search_string
1053 settings, selected_setting = filter_settings(full_settings, search_string)
1054 core.update_formspec(this:get_formspec())
1059 if fields["btn_edit"] or list_enter then
1060 local setting = settings[selected_setting]
1061 if setting and setting.type ~= "category" then
1062 local edit_dialog = dialog_create("change_setting",
1063 create_change_setting_formspec, handle_change_setting_buttons)
1064 edit_dialog:set_parent(this)
1071 if fields["btn_restore"] then
1072 local setting = settings[selected_setting]
1073 if setting and setting.type ~= "category" then
1074 core.settings:remove(setting.name)
1075 core.settings:write()
1076 core.update_formspec(this:get_formspec())
1081 if fields["btn_back"] then
1086 if fields["cb_tech_settings"] then
1087 core.settings:set("main_menu_technical_settings", fields["cb_tech_settings"])
1088 core.settings:write()
1089 core.update_formspec(this:get_formspec())
1096 function create_adv_settings_dlg()
1097 local dlg = dialog_create("settings_advanced",
1098 create_settings_formspec,
1099 handle_settings_buttons,
1105 -- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'.
1106 -- For RUN_IN_PLACE the generated files may appear in the 'bin' folder.
1107 -- See comment and alternative line at the end of 'generate_from_settingtypes.lua'.
1109 --assert(loadfile(core.get_builtin_path().."mainmenu"..DIR_DELIM..
1110 -- "generate_from_settingtypes.lua"))(parse_config_file(true, false))