Redesign/unify mainmenu settings interface

This commit is contained in:
rubenwardy 2022-06-25 17:33:20 +01:00
parent a421a1d764
commit d35672e78e
12 changed files with 1820 additions and 1535 deletions

@ -42,6 +42,7 @@ local function add_tab(self,tab)
event_handler = tab.cbf_events,
get_formspec = tab.cbf_formspec,
tabsize = tab.tabsize,
formspec_version = tab.formspec_version,
on_change = tab.on_change,
tabdata = {},
}
@ -69,6 +70,10 @@ local function get_formspec(self)
local tsize = tab.tabsize or {width=self.width, height=self.height}
prepend = string.format("size[%f,%f,%s]", tsize.width, tsize.height,
dump(self.fixed_size))
if tab.formspec_version then
prepend = ("formspec_version[%d]"):format(tab.formspec_version) .. prepend
end
end
local formspec = (prepend or "") .. self:tab_header() .. content

File diff suppressed because it is too large Load Diff

@ -40,7 +40,7 @@ dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua")
dofile(menupath .. DIR_DELIM .. "game_theme.lua")
dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
dofile(menupath .. DIR_DELIM .. "dlg_settings_advanced.lua")
dofile(menupath .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
dofile(menupath .. DIR_DELIM .. "dlg_contentstore.lua")
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
@ -51,7 +51,23 @@ dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua")
local tabs = {}
tabs.settings = dofile(menupath .. DIR_DELIM .. "tab_settings.lua")
tabs.settings = {
name = "settings",
caption = fgettext("Settings"),
cbf_formspec = function()
return "button[0.1,0.1;3,0.8;open_settings;" .. fgettext("Open Settings") .. "]"
end,
cbf_button_handler = function(tabview, fields)
if fields.open_settings then
local dlg = create_settings_dlg()
dlg:set_parent(tabview)
tabview:hide()
dlg:show()
return true
end
end,
}
tabs.content = dofile(menupath .. DIR_DELIM .. "tab_content.lua")
tabs.about = dofile(menupath .. DIR_DELIM .. "tab_about.lua")
tabs.local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua")
@ -103,7 +119,6 @@ local function init_globals()
tv_main:set_autosave_tab(true)
tv_main:add(tabs.local_game)
tv_main:add(tabs.play_online)
tv_main:add(tabs.content)
tv_main:add(tabs.settings)
tv_main:add(tabs.about)

@ -0,0 +1,384 @@
--Minetest
--Copyright (C) 2022 rubenwardy
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local make = {}
-- This file defines various component constructors, of the form:
--
-- make.component(setting)
--
-- `setting` is a table representing the settingtype.
--
-- A component is a table with the following:
--
-- * `full_width`: (Optional) true if the component shouldn't reserve space for info / reset.
-- * `info_text`: (Optional) string, informational text shown in an info icon.
-- * `setting`: (Optional) the setting.
-- * `max_w`: (Optional) maximum width, `avail_w` will never exceed this.
-- * `changed`: (Optional) true if the setting has changed from its default value.
-- * `get_formspec = function(self, avail_w)`:
-- * `avail_w` is the available width for the component.
-- * Returns `fs, used_height`.
-- * `fs` is a string for the formspec.
-- Components should be relative to `0,0`, and not exceed `avail_w` or the returned `used_height`.
-- * `used_height` is the space used by components in `fs`.
-- * `on_submit = function(self, fields, parent)`:
-- * `fields`: submitted formspec fields
-- * `parent`: the fstk element for the settings UI, use to show dialogs
-- * Return true if the event was handled, to prevent future components receiving it.
local function get_label(setting)
local show_technical_names = core.settings:get_bool("show_technical_names")
if not show_technical_names and setting.readable_name then
return fgettext(setting.readable_name)
end
return setting.name
end
local function is_valid_number(value)
return type(value) == "number" and not (value ~= value or value >= math.huge or value <= -math.huge)
end
function make.heading(text)
return {
full_width = true,
get_formspec = function(self, avail_w)
return ("label[0,0.6;%s]box[0,0.9;%f,0.05;#ccc6]"):format(core.formspec_escape(text), avail_w), 1.2
end,
}
end
--- Used for string and numeric style fields
---
--- @param converter Function to coerce values
--- @param validator Validator function, optional. Returns true when valid.
local function make_field(converter, validator)
return function(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local value = core.settings:get(setting.name) or setting.default
self.changed = converter(value) ~= converter(setting.default)
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
avail_w - 1.5, setting.name, get_label(setting), core.formspec_escape(value))
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
return fs, 1.1
end,
on_submit = function(self, fields)
if fields["set_" .. setting.name] or fields.key_enter_field == setting.name then
local value = converter(fields[setting.name])
if value == nil or (validator and not validator(value)) then
return true
end
if setting.min then
value = math.max(value, setting.min)
end
if setting.max then
value = math.min(value, setting.max)
end
core.settings:set(setting.name, tostring(value))
return true
end
end,
}
end
end
make.float = make_field(tonumber, is_valid_number)
make.int = make_field(function(x)
local value = tonumber(x)
return value and math.floor(value)
end, is_valid_number)
make.string = make_field(tostring, nil)
function make.bool(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local value = core.settings:get_bool(setting.name, core.is_yes(setting.default))
self.changed = tostring(value) ~= setting.default
local fs = ("checkbox[0,0.25;%s;%s;%s]"):format(
setting.name, get_label(setting), tostring(value))
return fs, 0.5
end,
on_submit = function(self, fields)
if fields[setting.name] == nil then
return false
end
core.settings:set_bool(setting.name, core.is_yes(fields[setting.name]))
return true
end,
}
end
function make.enum(setting)
return {
info_text = setting.comment,
setting = setting,
max_w = 4.5,
get_formspec = function(self, avail_w)
local value = core.settings:get(setting.name) or setting.default
self.changed = value ~= setting.default
local items = {}
for i, option in ipairs(setting.values) do
items[i] = core.formspec_escape(option)
end
local selected_idx = table.indexof(setting.values, value)
local fs = "label[0,0.1;" .. get_label(setting) .. "]"
fs = fs .. ("dropdown[0,0.3;%f,0.8;%s;%s;%d]"):format(
avail_w, setting.name, table.concat(items, ","), selected_idx, value)
return fs, 1.1
end,
on_submit = function(self, fields)
local old_value = core.settings:get(setting.name) or setting.default
local value = fields[setting.name]
if value == nil or value == old_value then
return false
end
core.settings:set(setting.name, value)
return true
end,
}
end
function make.path(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local value = core.settings:get(setting.name) or setting.default
self.changed = value ~= setting.default
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
avail_w - 3, setting.name, get_label(setting), value)
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 3, "pick_" .. setting.name, fgettext("Browse"))
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
return fs, 1.1
end,
on_submit = function(self, fields)
local dialog_name = "dlg_path_" .. setting.name
if fields["pick_" .. setting.name] then
local is_file = setting.type ~= "path"
core.show_path_select_dialog(dialog_name,
is_file and fgettext_ne("Select file") or fgettext_ne("Select directory"), is_file)
return true
end
if fields[dialog_name .. "_accepted"] then
local value = fields[dialog_name .. "_accepted"]
if value ~= nil then
core.settings:set(setting.name, value)
end
return true
end
if fields["set_" .. setting.name] or fields.key_enter_field == setting.name then
local value = fields[setting.name]
if value ~= nil then
core.settings:set(setting.name, value)
end
return true
end
end,
}
end
function make.v3f(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local value = vector.from_string(core.settings:get(setting.name) or setting.default)
self.changed = value ~= vector.from_string(setting.default)
-- Allocate space for "Set" button
avail_w = avail_w - 1
local fs = "label[0,0.1;" .. get_label(setting) .. "]"
local field_width = (avail_w - 3*0.25) / 3
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
0, field_width, setting.name .. "_x", "X", value.x)
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
field_width + 0.25, field_width, setting.name .. "_y", "Y", value.y)
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
2 * (field_width + 0.25), field_width, setting.name .. "_z", "Z", value.z)
fs = fs .. ("button[%f,0.6;1,0.8;%s;%s]"):format(avail_w, "set_" .. setting.name, fgettext("Set"))
return fs, 1.4
end,
on_submit = function(self, fields)
if fields["set_" .. setting.name] or
fields.key_enter_field == setting.name .. "_x" or
fields.key_enter_field == setting.name .. "_y" or
fields.key_enter_field == setting.name .. "_z" then
local x = tonumber(fields[setting.name .. "_x"])
local y = tonumber(fields[setting.name .. "_y"])
local z = tonumber(fields[setting.name .. "_z"])
if is_valid_number(x) and is_valid_number(y) and is_valid_number(z) then
core.settings:set(setting.name, vector.new(x, y, z):to_string())
else
core.log("error", "Invalid vector: " .. dump({x, y, z}))
end
return true
end
end,
}
end
function make.flags(setting)
local checkboxes = {}
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local fs = {
"label[0,0.1;" .. get_label(setting) .. "]",
}
local value = core.settings:get(setting.name) or setting.default
self.changed = value:gsub(" ", "") ~= setting.default:gsub(" ", "")
checkboxes = {}
for _, name in ipairs(value:split(",")) do
name = name:trim()
if name:sub(1, 2) == "no" then
checkboxes[name:sub(3)] = false
elseif name ~= "" then
checkboxes[name] = true
end
end
local columns = math.max(math.floor(avail_w / 2.5), 1)
local column_width = avail_w / columns
local x = 0
local y = 0.55
for _, possible in ipairs(setting.possible) do
if possible:sub(1, 2) ~= "no" then
if x >= avail_w then
x = 0
y = y + 0.5
end
local is_checked = checkboxes[possible]
fs[#fs + 1] = ("checkbox[%f,%f;%s;%s;%s]"):format(
x, y, setting.name .. "_" .. possible,
core.formspec_escape(possible), tostring(is_checked))
x = x + column_width
end
end
return table.concat(fs, ""), y + 0.25
end,
on_submit = function(self, fields)
local changed = false
for name, _ in pairs(checkboxes) do
local value = fields[setting.name .. "_" .. name]
if value ~= nil then
checkboxes[name] = core.is_yes(value)
changed = true
end
end
if changed then
local values = {}
for _, name in ipairs(setting.possible) do
if name:sub(1, 2) ~= "no" then
if checkboxes[name] then
table.insert(values, name)
else
table.insert(values, "no" .. name)
end
end
end
core.settings:set(setting.name, table.concat(values, ","))
end
return changed
end
}
end
local function noise_params(setting)
return {
info_text = setting.comment,
setting = setting,
get_formspec = function(self, avail_w)
local fs = "label[0,0.4;" .. get_label(setting) .. "]" ..
("button[%f,0;2.5,0.8;%s;%s]"):format(avail_w - 2.5, "edit_" .. setting.name, fgettext("Edit"))
return fs, 0.8
end,
on_submit = function(self, fields, tabview)
if fields["edit_" .. setting.name] then
local dlg = create_change_mapgen_flags_dlg(setting)
dlg:set_parent(tabview)
tabview:hide()
dlg:show()
return true
end
end,
}
end
make.filepath = make.path
make.noise_params_2d = noise_params
make.noise_params_3d = noise_params
return make

@ -0,0 +1,249 @@
--Minetest
--Copyright (C) 2015 PilzAdam
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local checkboxes = {}
local function flags_to_table(flags)
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
end
local function get_current_np_group(setting)
local value = core.settings:get_np_group(setting.name)
if value == nil then
return setting.values
end
local p = "%g"
return {
p:format(value.offset),
p:format(value.scale),
p:format(value.spread.x),
p:format(value.spread.y),
p:format(value.spread.z),
p:format(value.seed),
p:format(value.octaves),
p:format(value.persistence),
p:format(value.lacunarity),
value.flags
}
end
local function get_formspec(dialogdata)
local setting = dialogdata.setting
-- Final formspec will be created at the end of this function
-- Default values below, may be changed depending on setting type
local width = 10
local height = 3.5
local description_height = 3
local t = get_current_np_group(setting)
local dimension = 3
if setting.type == "noise_params_2d" then
dimension = 2
end
-- More space for 3x3 fields
description_height = description_height - 1.5
height = height - 1.5
local fields = {}
local function add_field(x, name, label, value)
fields[#fields + 1] = ("field[%f,%f;3.3,1;%s;%s;%s]"):format(
x, height, name, label, core.formspec_escape(value or "")
)
end
-- First row
height = height + 0.3
add_field(0.3, "te_offset", fgettext("Offset"), t[1])
add_field(3.6, "te_scale", fgettext("Scale"), t[2])
add_field(6.9, "te_seed", fgettext("Seed"), t[6])
height = height + 1.1
-- Second row
add_field(0.3, "te_spreadx", fgettext("X spread"), t[3])
if dimension == 3 then
add_field(3.6, "te_spready", fgettext("Y spread"), t[4])
else
fields[#fields + 1] = "label[4," .. height - 0.2 .. ";" ..
fgettext("2D Noise") .. "]"
end
add_field(6.9, "te_spreadz", fgettext("Z spread"), t[5])
height = height + 1.1
-- Third row
add_field(0.3, "te_octaves", fgettext("Octaves"), t[7])
add_field(3.6, "te_persist", fgettext("Persistence"), t[8])
add_field(6.9, "te_lacun", fgettext("Lacunarity"), t[9])
height = height + 1.1
local enabled_flags = flags_to_table(t[10])
local flags = {}
for _, name in ipairs(enabled_flags) do
-- Index by name, to avoid iterating over all enabled_flags for every possible flag.
flags[name] = true
end
for _, name in ipairs(setting.flags) do
local checkbox_name = "cb_" .. name
local is_enabled = flags[name] == true -- to get false if nil
checkboxes[checkbox_name] = is_enabled
end
local formspec = table.concat(fields)
.. "checkbox[0.5," .. height - 0.6 .. ";cb_defaults;"
--[[~ "defaults" is a noise parameter flag.
It describes the default processing options
for noise settings in main menu -> "All Settings". ]]
.. fgettext("defaults") .. ";" -- defaults
.. tostring(flags["defaults"] == true) .. "]" -- to get false if nil
.. "checkbox[5," .. height - 0.6 .. ";cb_eased;"
--[[~ "eased" is a noise parameter flag.
It is used to make the map smoother and
can be enabled in noise settings in
main menu -> "All Settings". ]]
.. fgettext("eased") .. ";" -- eased
.. tostring(flags["eased"] == true) .. "]"
.. "checkbox[5," .. height - 0.15 .. ";cb_absvalue;"
--[[~ "absvalue" is a noise parameter flag.
It is short for "absolute value".
It can be enabled in noise settings in
main menu -> "All Settings". ]]
.. fgettext("absvalue") .. ";" -- absvalue
.. tostring(flags["absvalue"] == true) .. "]"
height = height + 1
-- Box good, textarea bad. Calculate textarea size from box.
local function create_textfield(size, label, text, bg_color)
local textarea = {
x = size.x + 0.3,
y = size.y,
w = size.w + 0.25,
h = size.h * 1.16 + 0.12
}
return ("box[%f,%f;%f,%f;%s]textarea[%f,%f;%f,%f;;%s;%s]"):format(
size.x, size.y, size.w, size.h, bg_color or "#000",
textarea.x, textarea.y, textarea.w, textarea.h,
core.formspec_escape(label), core.formspec_escape(text)
)
end
-- When there's an error: Shrink description textarea and add error below
if dialogdata.error_message then
local error_box = {
x = 0,
y = description_height - 0.4,
w = width - 0.25,
h = 0.5
}
formspec = formspec ..
create_textfield(error_box, "", dialogdata.error_message, "#600")
description_height = description_height - 0.75
end
-- Get description field
local description_box = {
x = 0,
y = 0.2,
w = width - 0.25,
h = description_height
}
local setting_name = setting.name
if setting.readable_name then
setting_name = fgettext_ne(setting.readable_name) ..
" (" .. setting.name .. ")"
end
local comment_text
if setting.comment == "" then
comment_text = fgettext_ne("(No description of setting given)")
else
comment_text = fgettext_ne(setting.comment)
end
return (
"size[" .. width .. "," .. height + 0.25 .. ",true]" ..
create_textfield(description_box, setting_name, comment_text) ..
formspec ..
"button[" .. width / 2 - 2.5 .. "," .. height - 0.4 .. ";2.5,1;btn_done;" ..
fgettext("Save") .. "]" ..
"button[" .. width / 2 .. "," .. height - 0.4 .. ";2.5,1;btn_cancel;" ..
fgettext("Cancel") .. "]"
)
end
local function buttonhandler(this, fields)
local setting = this.data.setting
if fields["btn_done"] or fields["key_enter"] then
local np_flags = {}
for _, name in ipairs(setting.flags) do
if checkboxes["cb_" .. name] then
table.insert(np_flags, name)
end
end
checkboxes = {}
if setting.type == "noise_params_2d" then
fields["te_spready"] = fields["te_spreadz"]
end
local new_value = {
offset = fields["te_offset"],
scale = fields["te_scale"],
spread = {
x = fields["te_spreadx"],
y = fields["te_spready"],
z = fields["te_spreadz"]
},
seed = fields["te_seed"],
octaves = fields["te_octaves"],
persistence = fields["te_persist"],
lacunarity = fields["te_lacun"],
flags = table.concat(np_flags, ", ")
}
core.settings:set_np_group(setting.name, new_value)
core.settings:write()
this:delete()
return true
end
if fields["btn_cancel"] then
this:delete()
return true
end
return false
end
function create_change_mapgen_flags_dlg(setting)
assert(type(setting) == "table")
local retval = dialog_create("dlg_change_mapgen_flags",
get_formspec,
buttonhandler,
nil)
retval.data.setting = setting
return retval
end

@ -0,0 +1,520 @@
--Minetest
--Copyright (C) 2022 rubenwardy
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local component_funcs = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
"settings" .. DIR_DELIM .. "components.lua")
local quick_shader_component = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
"settings" .. DIR_DELIM .. "shader_component.lua")
local full_settings = settingtypes.parse_config_file(false, true)
local info_icon_path = core.formspec_escape(defaulttexturedir .. "settings_info.png")
local reset_icon_path = core.formspec_escape(defaulttexturedir .. "settings_reset.png")
local gettext = fgettext_ne
local all_pages = {}
local page_by_id = {}
local filtered_pages = all_pages
local filtered_page_by_id = page_by_id
local function add_page(page)
assert(type(page.id) == "string")
assert(type(page.title) == "string")
assert(page.section == nil or type(page.section) == "string")
assert(type(page.content) == "table")
assert(not page_by_id[page.id], "Page " .. page.id .. " already registered")
all_pages[#all_pages + 1] = page
page_by_id[page.id] = page
return page
end
local change_keys = {
query_text = "Change keys",
get_formspec = function(self, avail_w)
local btn_w = math.min(avail_w, 3)
return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Change keys")), 0.8
end,
on_submit = function(self, fields)
if fields.btn_change_keys then
core.show_keys_menu()
end
end,
}
add_page({
id = "most_used",
title = gettext("Most Used"),
content = {
change_keys,
"language",
"fullscreen",
PLATFORM ~= "Android" and "autosave_screensize" or false,
"touchscreen_threshold",
{ heading = gettext("Scaling") },
"gui_scaling",
"hud_scaling",
{ heading = gettext("Graphics / Performance") },
"smooth_lighting",
"enable_particles",
"enable_3d_clouds",
"opaque_water",
"connected_glass",
"node_highlighting",
"leaves_style",
{ heading = gettext("Shaders") },
quick_shader_component,
},
})
add_page({
id = "accessibility",
title = gettext("Accessibility"),
content = {
"font_size",
"chat_font_size",
"gui_scaling",
"hud_scaling",
"show_nametag_backgrounds",
{ heading = gettext("Chat") },
"console_height",
"console_alpha",
"console_color",
{ heading = gettext("Controls") },
"autojump",
"safe_dig_and_place",
{ heading = gettext("Movement") },
"arm_inertia",
"view_bobbing_amount",
"fall_bobbing_amount",
},
})
local tabsize = {
width = 15.5,
height= 12,
}
local function load_settingtypes()
local page = nil
local section = nil
local function ensure_page_started()
if not page then
page = add_page({
id = (section or "general"):lower():gsub(" ", "_"),
title = section or gettext("General"),
section = section,
content = {},
})
end
end
for _, entry in ipairs(full_settings) do
if entry.type == "category" then
if entry.level == 0 then
section = entry.name
page = nil
elseif entry.level == 1 then
page = {
id = ((section and section .. "_" or "") .. entry.name):lower():gsub(" ", "_"),
title = entry.readable_name or entry.name,
section = section,
content = {},
}
if page.title:sub(1, 5) ~= "Hide:" then
page = add_page(page)
end
elseif entry.level == 2 then
ensure_page_started()
page.content[#page.content + 1] = {
heading = gettext(entry.readable_name or entry.name),
}
end
else
ensure_page_started()
page.content[#page.content + 1] = entry.name
end
end
end
load_settingtypes()
table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys)
local function get_setting_info(name)
for _, entry in ipairs(full_settings) do
if entry.type ~= "category" and entry.name == name then
return entry
end
end
return nil
end
-- See if setting matches keywords
local function get_setting_match_weight(entry, query_keywords)
local setting_score = 0
for _, keyword in ipairs(query_keywords) do
if string.find(entry.name:lower(), keyword, 1, true) then
setting_score = setting_score + 1
end
if entry.readable_name and
string.find(fgettext(entry.readable_name):lower(), keyword, 1, true) then
setting_score = setting_score + 1
end
if entry.comment and
string.find(fgettext_ne(entry.comment):lower(), keyword, 1, true) then
setting_score = setting_score + 1
end
end
return setting_score
end
local function filter_page_content(page, query_keywords)
if #query_keywords == 0 then
return page.content
end
local retval = {}
local i = 1
local max_weight = 0
for _, content in ipairs(page.content) do
if type(content) == "string" then
local setting = get_setting_info(content)
assert(setting, "Unknown setting: " .. content)
local weight = get_setting_match_weight(setting, query_keywords)
if weight > 0 then
max_weight = math.max(max_weight, weight)
retval[i] = content
i = i + 1
end
elseif type(content) == "table" and content.query_text then
for _, keyword in ipairs(query_keywords) do
if string.find(fgettext(content.query_text), keyword, 1, true) then
max_weight = math.max(max_weight, 1)
retval[i] = content
i = i + 1
break
end
end
end
end
return retval, max_weight
end
local function update_filtered_pages(query)
filtered_pages = {}
filtered_page_by_id = {}
if query == "" or query == nil then
filtered_pages = all_pages
filtered_page_by_id = page_by_id
return filtered_pages[1].id
end
local query_keywords = {}
for word in query:lower():gmatch("%S+") do
table.insert(query_keywords, word)
end
local best_page = nil
local best_page_weight = -1
for _, page in ipairs(all_pages) do
local content, page_weight = filter_page_content(page, query_keywords)
if #content > 0 then
local new_page = table.copy(page)
new_page.content = content
filtered_pages[#filtered_pages + 1] = new_page
filtered_page_by_id[new_page.id] = new_page
if page_weight > best_page_weight then
best_page = new_page
best_page_weight = page_weight
end
end
end
return best_page and best_page.id or nil
end
local function build_page_components(page)
local retval = {}
local j = 1
for i, content in ipairs(page.content) do
if content == false then
-- false is used to disable components conditionally (ie: Android specific)
j = j - 1
elseif type(content) == "string" then
local setting = get_setting_info(content)
assert(setting, "Unknown setting: " .. content)
local component_func = component_funcs[setting.type]
assert(component_func, "Unknown setting type: " .. setting.type)
retval[j] = component_func(setting)
elseif content.get_formspec then
retval[j] = content
elseif content.heading then
retval[j] = component_funcs.heading(content.heading)
else
error("Unknown content in page: " .. dump(content))
end
j = j + 1
end
return retval
end
--- Creates a scrollbaroptions for a scroll_container
--
-- @param visible_l the length of the scroll_container and scrollbar
-- @param total_l length of the scrollable area
-- @param scroll_factor as passed to scroll_container
local function make_scrollbaroptions_for_scroll_container(visible_l, total_l, scroll_factor)
assert(total_l >= visible_l)
local max = total_l - visible_l
local thumb_size = (visible_l / total_l) * max
return ("scrollbaroptions[min=0;max=%f;thumbsize=%f]"):format(max / scroll_factor, thumb_size / scroll_factor)
end
local formspec_show_hack = false
local function get_formspec(dialogdata)
local page_id = dialogdata.page_id or "most_used"
local page = filtered_page_by_id[page_id]
local scrollbar_w = 0.4
if PLATFORM == "Android" then
scrollbar_w = 0.6
end
local left_pane_width = 4.25
local search_width = left_pane_width + scrollbar_w - (0.75 * 2)
local show_technical_names = core.settings:get_bool("show_technical_names")
formspec_show_hack = not formspec_show_hack
local fs = {
"formspec_version[6]",
"size[", tostring(tabsize.width), ",", tostring(tabsize.height + 1), "]",
"bgcolor[#0000]",
-- HACK: this is needed to allow resubmitting the same formspec
formspec_show_hack and " " or "",
"box[0,0;", tostring(tabsize.width), ",", tostring(tabsize.height), ";#0000008C]",
"button[0,", tostring(tabsize.height + 0.2), ";3,0.8;back;", fgettext("Back"), "]",
("box[%f,%f;5,0.8;#0000008C]"):format(tabsize.width - 5, tabsize.height + 0.2),
"checkbox[", tostring(tabsize.width - 4.75), ",", tostring(tabsize.height + 0.6), ";show_technical_names;",
fgettext("Show technical names"), ";", tostring(show_technical_names), "]",
"field[0.25,0.25;", tostring(search_width), ",0.75;search_query;;",
core.formspec_escape(dialogdata.query or ""), "]",
"container[", tostring(search_width + 0.25), ", 0.25]",
"image_button[0,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
"image_button[0.75,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";search_clear;]",
"tooltip[search;", fgettext("Search"), "]",
"tooltip[search_clear;", fgettext("Clear"), "]",
"container_end[]",
"scroll_container[0.25,1.25;", tostring(left_pane_width), ",",
tostring(tabsize.height - 1.5), ";leftscroll;vertical;0.1]",
"style_type[button;border=false;bgcolor=#3333]",
"style_type[button:hover;border=false;bgcolor=#6663]",
}
local y = 0
local last_section = nil
for _, other_page in ipairs(filtered_pages) do
if other_page.section ~= last_section then
fs[#fs + 1] = ("label[0.1,%f;%s]"):format(y + 0.41, core.colorize("#ff0", fgettext(other_page.section)))
last_section = other_page.section
y = y + 0.82
end
fs[#fs + 1] = ("box[0,%f;%f,0.8;%s]"):format(
y, left_pane_width, other_page.id == page_id and "#467832FF" or "#3339")
fs[#fs + 1] = ("button[0,%f;%f,0.8;page_%s;%s]")
:format(y, left_pane_width, other_page.id, fgettext(other_page.title))
y = y + 0.82
end
if #filtered_pages == 0 then
fs[#fs + 1] = "label[0.1,0.41;"
fs[#fs + 1] = fgettext("No results")
fs[#fs + 1] = "]"
end
fs[#fs + 1] = "scroll_container_end[]"
if y >= tabsize.height - 1.25 then
fs[#fs + 1] = make_scrollbaroptions_for_scroll_container(tabsize.height - 1.5, y, 0.1)
fs[#fs + 1] = ("scrollbar[%f,1.25;%f,%f;vertical;leftscroll;%f]"):format(
left_pane_width + 0.25, scrollbar_w, tabsize.height - 1.5, dialogdata.leftscroll or 0)
end
fs[#fs + 1] = "style_type[button;border=;bgcolor=]"
if not dialogdata.components then
dialogdata.components = page and build_page_components(page) or {}
end
local right_pane_width = tabsize.width - left_pane_width - 0.375 - 2*scrollbar_w - 0.25
fs[#fs + 1] = ("scroll_container[%f,0;%f,%f;rightscroll;vertical;0.1]"):format(
tabsize.width - right_pane_width - scrollbar_w, right_pane_width, tabsize.height)
y = 0.25
for i, comp in ipairs(dialogdata.components) do
fs[#fs + 1] = ("container[0,%f]"):format(y)
local avail_w = right_pane_width - 0.25
if not comp.full_width then
avail_w = avail_w - 1.4
end
if comp.max_w then
avail_w = math.min(avail_w, comp.max_w)
end
local comp_fs, used_h = comp:get_formspec(avail_w)
fs[#fs + 1] = comp_fs
fs[#fs + 1] = "style_type[image_button;border=false;padding=]"
local show_reset = comp.changed and comp.setting and comp.setting.default
local show_info = comp.info_text and comp.info_text ~= ""
if show_reset or show_info then
-- ensure there's enough space for reset/info
used_h = math.max(used_h, 0.5)
end
local info_reset_y = used_h / 2 - 0.25
if show_reset then
fs[#fs + 1] = ("image_button[%f,%f;0.5,0.5;%s;%s;]"):format(
right_pane_width - 1.4, info_reset_y, reset_icon_path, "reset_" .. i)
fs[#fs + 1] = ("tooltip[%s;%s]"):format(
"reset_" .. i, fgettext("Reset setting to default ($1)", tostring(comp.setting.default)))
end
if show_info then
local info_x = right_pane_width - 0.75
fs[#fs + 1] = ("image[%f,%f;0.5,0.5;%s]"):format(info_x, info_reset_y, info_icon_path)
fs[#fs + 1] = ("tooltip[%f,%f;0.5,0.5;%s]"):format(info_x, info_reset_y, fgettext(comp.info_text))
end
fs[#fs + 1] = "style_type[image_button;border=;padding=]"
fs[#fs + 1] = "container_end[]"
if used_h > 0 then
y = y + used_h + 0.25
end
end
fs[#fs + 1] = "scroll_container_end[]"
if y >= tabsize.height then
fs[#fs + 1] = make_scrollbaroptions_for_scroll_container(tabsize.height, y + 0.375, 0.1)
fs[#fs + 1] = ("scrollbar[%f,0;%f,%f;vertical;rightscroll;%f]"):format(
tabsize.width - scrollbar_w, scrollbar_w, tabsize.height, dialogdata.rightscroll or 0)
end
return table.concat(fs, "")
end
local function buttonhandler(this, fields)
local dialogdata = this.data
dialogdata.leftscroll = core.explode_scrollbar_event(fields.leftscroll).value or dialogdata.leftscroll
dialogdata.rightscroll = core.explode_scrollbar_event(fields.rightscroll).value or dialogdata.rightscroll
dialogdata.query = fields.search_query
if fields.back then
this:delete()
return true
end
if fields.show_technical_names ~= nil then
local value = core.is_yes(fields.show_technical_names)
core.settings:set_bool("show_technical_names", value)
return true
end
if fields.search or fields.key_enter_field == "search_query" then
dialogdata.components = nil
dialogdata.leftscroll = 0
dialogdata.rightscroll = 0
dialogdata.page_id = update_filtered_pages(dialogdata.query)
return true
end
if fields.search_clear then
dialogdata.query = ""
dialogdata.components = nil
dialogdata.leftscroll = 0
dialogdata.rightscroll = 0
dialogdata.page_id = update_filtered_pages("")
return true
end
for _, page in ipairs(all_pages) do
if fields["page_" .. page.id] then
dialogdata.page_id = page.id
dialogdata.components = nil
dialogdata.rightscroll = 0
return true
end
end
for i, comp in ipairs(dialogdata.components) do
if comp.on_submit and comp:on_submit(fields, this) then
return true
end
if comp.setting and fields["reset_" .. i] then
core.settings:set(comp.setting.name, comp.setting.default)
return true
end
end
return false
end
function create_settings_dlg()
return dialog_create("dlg_settings", get_formspec, buttonhandler, nil)
end

@ -0,0 +1,28 @@
--Minetest
--Copyright (C) 2022 rubenwardy
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local path = core.get_mainmenu_path() .. DIR_DELIM .. "settings"
dofile(path .. DIR_DELIM .. "settingtypes.lua")
dofile(path .. DIR_DELIM .. "dlg_change_mapgen_flags.lua")
dofile(path .. DIR_DELIM .. "dlg_settings.lua")
-- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'.
-- For RUN_IN_PLACE the generated files may appear in the 'bin' folder.
-- See comment and alternative line at the end of 'generate_from_settingtypes.lua'.
--assert(loadfile(path .. DIR_DELIM .. "generate_from_settingtypes.lua"))(parse_config_file(true, false))

@ -0,0 +1,456 @@
--Minetest
--Copyright (C) 2015 PilzAdam
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
settingtypes = {}
-- A Setting type is a table with the following keys:
--
-- name: Identifier
-- readable_name: Readable title
-- type: Category
--
-- name = mod.name,
-- readable_name = mod.title,
-- level = 1,
-- type = "category", "int", "string", ""
-- }
local FILENAME = "settingtypes.txt"
local CHAR_CLASSES = {
SPACE = "[%s]",
VARIABLE = "[%w_%-%.]",
INTEGER = "[+-]?[%d]",
FLOAT = "[+-]?[%d%.]",
FLAGS = "[%w_%-%.,]",
}
local function flags_to_table(flags)
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
end
-- returns error message, or nil
local function parse_setting_line(settings, line, read_all, base_level, allow_secure)
-- strip carriage returns (CR, /r)
line = line:gsub("\r", "")
-- comment
local comment = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$")
if comment then
if settings.current_comment == "" then
settings.current_comment = comment
else
settings.current_comment = settings.current_comment .. "\n" .. comment
end
return
end
-- clear current_comment so only comments directly above a setting are bound to it
-- but keep a local reference to it for variables in the current line
local current_comment = settings.current_comment
settings.current_comment = ""
-- empty lines
if line:match("^" .. CHAR_CLASSES.SPACE .. "*$") then
return
end
-- category
local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
if category then
table.insert(settings, {
name = category,
level = stars:len() + base_level,
type = "category",
})
return
end
-- settings
local first_part, name, readable_name, setting_type = line:match("^"
-- this first capture group matches the whole first part,
-- so we can later strip it from the rest of the line
.. "("
.. "([" .. CHAR_CLASSES.VARIABLE .. "+)" -- variable name
.. CHAR_CLASSES.SPACE .. "*"
.. "%(([^%)]*)%)" -- readable name
.. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
.. CHAR_CLASSES.SPACE .. "*"
.. ")")
if not first_part then
return "Invalid line"
end
if name:match("secure%.[.]*") and not allow_secure then
return "Tried to add \"secure.\" setting"
end
if readable_name == "" then
readable_name = nil
end
local remaining_line = line:sub(first_part:len() + 1)
if setting_type == "int" then
local default, min, max = remaining_line:match("^"
-- first int is required, the last 2 are optional
.. "(" .. CHAR_CLASSES.INTEGER .. "+)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.INTEGER .. "*)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.INTEGER .. "*)"
.. "$")
if not default or not tonumber(default) then
return "Invalid integer setting"
end
min = tonumber(min)
max = tonumber(max)
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "int",
default = default,
min = min,
max = max,
comment = current_comment,
})
return
end
if setting_type == "string"
or setting_type == "key" or setting_type == "v3f" then
local default = remaining_line:match("^(.*)$")
if not default then
return "Invalid string setting"
end
if setting_type == "key" and not read_all then
-- ignore key type if read_all is false
return
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = setting_type,
default = default,
comment = current_comment,
})
return
end
if setting_type == "noise_params_2d"
or setting_type == "noise_params_3d" then
local default = remaining_line:match("^(.*)$")
if not default then
return "Invalid string setting"
end
local values = {}
local ti = 1
local index = 1
for match in default:gmatch("[+-]?[%d.-e]+") do -- All numeric characters
index = default:find("[+-]?[%d.-e]+", index) + match:len()
table.insert(values, match)
ti = ti + 1
if ti > 9 then
break
end
end
index = default:find("[^, ]", index)
local flags = ""
if index then
flags = default:sub(index)
default = default:sub(1, index - 3) -- Make sure no flags in single-line format
end
table.insert(values, flags)
table.insert(settings, {
name = name,
readable_name = readable_name,
type = setting_type,
default = default,
default_table = {
offset = values[1],
scale = values[2],
spread = {
x = values[3],
y = values[4],
z = values[5]
},
seed = values[6],
octaves = values[7],
persistence = values[8],
lacunarity = values[9],
flags = values[10]
},
values = values,
comment = current_comment,
noise_params = true,
flags = flags_to_table("defaults,eased,absvalue")
})
return
end
if setting_type == "bool" then
if remaining_line ~= "false" and remaining_line ~= "true" then
return "Invalid boolean setting"
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "bool",
default = remaining_line,
comment = current_comment,
})
return
end
if setting_type == "float" then
local default, min, max = remaining_line:match("^"
-- first float is required, the last 2 are optional
.. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLOAT .. "*)"
.."$")
if not default or not tonumber(default) then
return "Invalid float setting"
end
min = tonumber(min)
max = tonumber(max)
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "float",
default = default,
min = min,
max = max,
comment = current_comment,
})
return
end
if setting_type == "enum" then
local default, values = remaining_line:match("^"
-- first value (default) may be empty (i.e. is optional)
.. "(" .. CHAR_CLASSES.VARIABLE .. "*)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLAGS .. "+)"
.. "$")
if not default or values == "" then
return "Invalid enum setting"
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "enum",
default = default,
values = values:split(",", true),
comment = current_comment,
})
return
end
if setting_type == "path" or setting_type == "filepath" then
local default = remaining_line:match("^(.*)$")
if not default then
return "Invalid path setting"
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = setting_type,
default = default,
comment = current_comment,
})
return
end
if setting_type == "flags" then
local default, possible = remaining_line:match("^"
-- first value (default) may be empty (i.e. is optional)
-- this is implemented by making the last value optional, and
-- swapping them around if it turns out empty.
.. "(" .. CHAR_CLASSES.FLAGS .. "+)" .. CHAR_CLASSES.SPACE .. "*"
.. "(" .. CHAR_CLASSES.FLAGS .. "*)"
.. "$")
if not default or not possible then
return "Invalid flags setting"
end
if possible == "" then
possible = default
default = ""
end
table.insert(settings, {
name = name,
readable_name = readable_name,
type = "flags",
default = default,
possible = flags_to_table(possible),
comment = current_comment,
})
return
end
return "Invalid setting type \"" .. setting_type .. "\""
end
local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
-- store this helper variable in the table so it's easier to pass to parse_setting_line()
result.current_comment = ""
local line = file:read("*line")
while line do
local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure)
if error_msg then
core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
end
line = file:read("*line")
end
result.current_comment = nil
end
--- Returns table of setting types
--
-- @param read_all Whether to ignore certain setting types for GUI or not
-- @parse_mods Whether to parse settingtypes.txt in mods and games
function settingtypes.parse_config_file(read_all, parse_mods)
local settings = {}
do
local builtin_path = core.get_builtin_path() .. FILENAME
local file = io.open(builtin_path, "r")
if not file then
core.log("error", "Can't load " .. FILENAME)
return settings
end
parse_single_file(file, builtin_path, read_all, settings, 0, true)
file:close()
end
if parse_mods then
-- Parse games
local games_category_initialized = false
for _, game in ipairs(pkgmgr.games) do
local path = game.path .. DIR_DELIM .. FILENAME
local file = io.open(path, "r")
if file then
if not games_category_initialized then
fgettext_ne("Content: Games") -- not used, but needed for xgettext
table.insert(settings, {
name = "Content: Games",
level = 0,
type = "category",
})
games_category_initialized = true
end
table.insert(settings, {
name = game.name,
level = 1,
type = "category",
})
parse_single_file(file, path, read_all, settings, 2, false)
file:close()
end
end
-- Parse mods
local mods_category_initialized = false
local mods = {}
pkgmgr.get_mods(core.get_modpath(), "mods", mods)
table.sort(mods, function(a, b) return a.name < b.name end)
for _, mod in ipairs(mods) do
local path = mod.path .. DIR_DELIM .. FILENAME
local file = io.open(path, "r")
if file then
if not mods_category_initialized then
fgettext_ne("Content: Mods") -- not used, but needed for xgettext
table.insert(settings, {
name = "Content: Mods",
level = 0,
type = "category",
})
mods_category_initialized = true
end
table.insert(settings, {
name = mod.name,
readable_name = mod.title,
level = 1,
type = "category",
})
parse_single_file(file, path, read_all, settings, 2, false)
file:close()
end
end
-- Parse client mods
local clientmods_category_initialized = false
local clientmods = {}
pkgmgr.get_mods(core.get_clientmodpath(), "clientmods", clientmods)
for _, mod in ipairs(clientmods) do
local path = mod.path .. DIR_DELIM .. FILENAME
local file = io.open(path, "r")
if file then
if not clientmods_category_initialized then
fgettext_ne("Client Mods") -- not used, but needed for xgettext
table.insert(settings, {
name = "Client Mods",
level = 0,
type = "category",
})
clientmods_category_initialized = true
end
table.insert(settings, {
name = mod.name,
level = 1,
type = "category",
})
parse_single_file(file, path, read_all, settings, 2, false)
file:close()
end
end
end
return settings
end

@ -0,0 +1,159 @@
--Minetest
--Copyright (C) 2022 rubenwardy
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local shadow_levels_labels = {
fgettext("Disabled"),
fgettext("Very Low"),
fgettext("Low"),
fgettext("Medium"),
fgettext("High"),
fgettext("Very High")
}
local shadow_levels_options = {
table.concat(shadow_levels_labels, ","),
{ "0", "1", "2", "3", "4", "5" }
}
local function get_shadow_mapping_id()
local shadow_setting = core.settings:get("shadow_levels")
for i = 1, #shadow_levels_options[2] do
if shadow_setting == shadow_levels_options[2][i] then
return i
end
end
return 1
end
return {
query_text = "Shaders",
get_formspec = function(self, avail_w)
local fs = ""
local video_driver = core.get_active_driver()
local shaders_enabled = core.settings:get_bool("enable_shaders")
if video_driver == "opengl" then
fs = fs ..
"checkbox[0,0.25;cb_shaders;" .. fgettext("Shaders") .. ";"
.. tostring(shaders_enabled) .. "]"
elseif video_driver == "ogles2" then
fs = fs ..
"checkbox[0,0.25;cb_shaders;" .. fgettext("Shaders (experimental)") .. ";"
.. tostring(shaders_enabled) .. "]"
else
core.settings:set_bool("enable_shaders", false)
shaders_enabled = false
fs = fs ..
"label[0.13,0.25;" .. core.colorize("#888888",
fgettext("Shaders (unavailable)")) .. "]"
end
if shaders_enabled then
fs = fs ..
"container[0,0.75]" ..
"checkbox[0,0;cb_tonemapping;" .. fgettext("Tone mapping") .. ";"
.. tostring(core.settings:get_bool("tone_mapping")) .. "]" ..
"checkbox[0,0.5;cb_waving_water;" .. fgettext("Waving liquids") .. ";"
.. tostring(core.settings:get_bool("enable_waving_water")) .. "]" ..
"checkbox[0,1;cb_waving_leaves;" .. fgettext("Waving leaves") .. ";"
.. tostring(core.settings:get_bool("enable_waving_leaves")) .. "]" ..
"checkbox[0,1.5;cb_waving_plants;" .. fgettext("Waving plants") .. ";"
.. tostring(core.settings:get_bool("enable_waving_plants")) .. "]"
if video_driver == "opengl" then
fs = fs ..
"label[0,2.2;" .. fgettext("Dynamic shadows") .. "]" ..
"dropdown[0,2.4;3,0.8;dd_shadows;" .. shadow_levels_options[1] .. ";" .. get_shadow_mapping_id() .. "]" ..
"label[0,3.5;" .. core.colorize("#bbb", fgettext("(The game will need to enable shadows as well)")) .. "]"
else
fs = fs ..
"label[0,2.2;" .. core.colorize("#888888", fgettext("Dynamic shadows")) .. "]"
end
fs = fs .. "container_end[]"
else
fs = fs ..
"container[0.35,0.75]" ..
"label[0,0;" .. core.colorize("#888888",
fgettext("Tone mapping")) .. "]" ..
"label[0,0.5;" .. core.colorize("#888888",
fgettext("Waving liquids")) .. "]" ..
"label[0,1;" .. core.colorize("#888888",
fgettext("Waving leaves")) .. "]" ..
"label[0,1.5;" .. core.colorize("#888888",
fgettext("Waving plants")) .. "]"..
"label[0,2;" .. core.colorize("#888888",
fgettext("Dynamic shadows")) .. "]" ..
"container_end[]"
end
return fs, 4.5
end,
on_submit = function(self, fields)
if fields.cb_shaders then
core.settings:set("enable_shaders", fields.cb_shaders)
return true
end
if fields.cb_tonemapping then
core.settings:set("tone_mapping", fields.cb_tonemapping)
return true
end
if fields.cb_waving_water then
core.settings:set("enable_waving_water", fields.cb_waving_water)
return true
end
if fields.cb_waving_leaves then
core.settings:set("enable_waving_leaves", fields.cb_waving_leaves)
return true
end
if fields.cb_waving_plants then
core.settings:set("enable_waving_plants", fields.cb_waving_plants)
return true
end
for i = 1, #shadow_levels_labels do
if fields.dd_shadows == shadow_levels_labels[i] then
core.settings:set("shadow_levels", shadow_levels_options[2][i])
end
end
if fields.dd_shadows == shadow_levels_labels[1] then
core.settings:set("enable_dynamic_shadows", "false")
else
local shadow_presets = {
[2] = { 62, 512, "true", 0, "false" },
[3] = { 93, 1024, "true", 0, "false" },
[4] = { 140, 2048, "true", 1, "false" },
[5] = { 210, 4096, "true", 2, "true" },
[6] = { 300, 8192, "true", 2, "true" },
}
local s = shadow_presets[table.indexof(shadow_levels_labels, fields.dd_shadows)]
if s then
core.settings:set("enable_dynamic_shadows", "true")
core.settings:set("shadow_map_max_distance", s[1])
core.settings:set("shadow_map_texture_size", s[2])
core.settings:set("shadow_map_texture_32bit", s[3])
core.settings:set("shadow_filters", s[4])
core.settings:set("shadow_map_color", s[5])
end
end
end,
}

@ -1,399 +0,0 @@
--Minetest
--Copyright (C) 2013 sapier
--
--This program is free software; you can redistribute it and/or modify
--it under the terms of the GNU Lesser General Public License as published by
--the Free Software Foundation; either version 2.1 of the License, or
--(at your option) any later version.
--
--This program is distributed in the hope that it will be useful,
--but WITHOUT ANY WARRANTY; without even the implied warranty of
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--GNU Lesser General Public License for more details.
--
--You should have received a copy of the GNU Lesser General Public License along
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--------------------------------------------------------------------------------
local labels = {
leaves = {
fgettext("Opaque Leaves"),
fgettext("Simple Leaves"),
fgettext("Fancy Leaves")
},
node_highlighting = {
fgettext("Node Outlining"),
fgettext("Node Highlighting"),
fgettext("None")
},
filters = {
fgettext("No Filter"),
fgettext("Bilinear Filter"),
fgettext("Trilinear Filter")
},
mipmap = {
fgettext("No Mipmap"),
fgettext("Mipmap"),
fgettext("Mipmap + Aniso. Filter")
},
antialiasing = {
fgettext("None"),
fgettext("2x"),
fgettext("4x"),
fgettext("8x")
},
shadow_levels = {
fgettext("Disabled"),
fgettext("Very Low"),
fgettext("Low"),
fgettext("Medium"),
fgettext("High"),
fgettext("Very High")
}
}
local dd_options = {
leaves = {
table.concat(labels.leaves, ","),
{"opaque", "simple", "fancy"}
},
node_highlighting = {
table.concat(labels.node_highlighting, ","),
{"box", "halo", "none"}
},
filters = {
table.concat(labels.filters, ","),
{"", "bilinear_filter", "trilinear_filter"}
},
mipmap = {
table.concat(labels.mipmap, ","),
{"", "mip_map", "anisotropic_filter"}
},
antialiasing = {
table.concat(labels.antialiasing, ","),
{"0", "2", "4", "8"}
},
shadow_levels = {
table.concat(labels.shadow_levels, ","),
{ "0", "1", "2", "3", "4", "5" }
}
}
local getSettingIndex = {
Leaves = function()
local style = core.settings:get("leaves_style")
for idx, name in pairs(dd_options.leaves[2]) do
if style == name then return idx end
end
return 1
end,
NodeHighlighting = function()
local style = core.settings:get("node_highlighting")
for idx, name in pairs(dd_options.node_highlighting[2]) do
if style == name then return idx end
end
return 1
end,
Filter = function()
if core.settings:get(dd_options.filters[2][3]) == "true" then
return 3
elseif core.settings:get(dd_options.filters[2][3]) == "false" and
core.settings:get(dd_options.filters[2][2]) == "true" then
return 2
end
return 1
end,
Mipmap = function()
if core.settings:get(dd_options.mipmap[2][3]) == "true" then
return 3
elseif core.settings:get(dd_options.mipmap[2][3]) == "false" and
core.settings:get(dd_options.mipmap[2][2]) == "true" then
return 2
end
return 1
end,
Antialiasing = function()
local antialiasing_setting = core.settings:get("fsaa")
for i = 1, #dd_options.antialiasing[2] do
if antialiasing_setting == dd_options.antialiasing[2][i] then
return i
end
end
return 1
end,
ShadowMapping = function()
local shadow_setting = core.settings:get("shadow_levels")
for i = 1, #dd_options.shadow_levels[2] do
if shadow_setting == dd_options.shadow_levels[2][i] then
return i
end
end
return 1
end
}
local function antialiasing_fname_to_name(fname)
for i = 1, #labels.antialiasing do
if fname == labels.antialiasing[i] then
return dd_options.antialiasing[2][i]
end
end
return 0
end
local function formspec(tabview, name, tabdata)
local tab_string =
"box[0,0;3.75,4.5;#999999]" ..
"checkbox[0.25,0;cb_smooth_lighting;" .. fgettext("Smooth Lighting") .. ";"
.. dump(core.settings:get_bool("smooth_lighting")) .. "]" ..
"checkbox[0.25,0.5;cb_particles;" .. fgettext("Particles") .. ";"
.. dump(core.settings:get_bool("enable_particles")) .. "]" ..
"checkbox[0.25,1;cb_3d_clouds;" .. fgettext("3D Clouds") .. ";"
.. dump(core.settings:get_bool("enable_3d_clouds")) .. "]" ..
"checkbox[0.25,1.5;cb_opaque_water;" .. fgettext("Opaque Water") .. ";"
.. dump(core.settings:get_bool("opaque_water")) .. "]" ..
"checkbox[0.25,2.0;cb_connected_glass;" .. fgettext("Connected Glass") .. ";"
.. dump(core.settings:get_bool("connected_glass")) .. "]" ..
"dropdown[0.25,2.8;3.5;dd_node_highlighting;" .. dd_options.node_highlighting[1] .. ";"
.. getSettingIndex.NodeHighlighting() .. "]" ..
"dropdown[0.25,3.6;3.5;dd_leaves_style;" .. dd_options.leaves[1] .. ";"
.. getSettingIndex.Leaves() .. "]" ..
"box[4,0;3.75,4.9;#999999]" ..
"label[4.25,0.1;" .. fgettext("Texturing:") .. "]" ..
"dropdown[4.25,0.55;3.5;dd_filters;" .. dd_options.filters[1] .. ";"
.. getSettingIndex.Filter() .. "]" ..
"dropdown[4.25,1.35;3.5;dd_mipmap;" .. dd_options.mipmap[1] .. ";"
.. getSettingIndex.Mipmap() .. "]" ..
"label[4.25,2.15;" .. fgettext("Antialiasing:") .. "]" ..
"dropdown[4.25,2.6;3.5;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";"
.. getSettingIndex.Antialiasing() .. "]" ..
"box[8,0;3.75,4.5;#999999]"
local video_driver = core.get_active_driver()
local shaders_enabled = core.settings:get_bool("enable_shaders")
if video_driver == "opengl" then
tab_string = tab_string ..
"checkbox[8.25,0;cb_shaders;" .. fgettext("Shaders") .. ";"
.. tostring(shaders_enabled) .. "]"
elseif video_driver == "ogles2" then
tab_string = tab_string ..
"checkbox[8.25,0;cb_shaders;" .. fgettext("Shaders (experimental)") .. ";"
.. tostring(shaders_enabled) .. "]"
else
core.settings:set_bool("enable_shaders", false)
shaders_enabled = false
tab_string = tab_string ..
"label[8.38,0.2;" .. core.colorize("#888888",
fgettext("Shaders (unavailable)")) .. "]"
end
tab_string = tab_string ..
"button[8,4.75;3.95,1;btn_change_keys;"
.. fgettext("Change Keys") .. "]"
tab_string = tab_string ..
"button[0,4.75;3.95,1;btn_advanced_settings;"
.. fgettext("All Settings") .. "]"
if core.settings:get("touchscreen_threshold") ~= nil then
tab_string = tab_string ..
"label[4.25,3.5;" .. fgettext("Touch threshold (px):") .. "]" ..
"dropdown[4.25,3.95;3.5;dd_touchthreshold;0,10,20,30,40,50;" ..
((tonumber(core.settings:get("touchscreen_threshold")) / 10) + 1) ..
"]"
else
tab_string = tab_string ..
"label[4.25,3.65;" .. fgettext("Screen:") .. "]" ..
"checkbox[4.25,3.9;cb_autosave_screensize;" .. fgettext("Autosave Screen Size") .. ";"
.. dump(core.settings:get_bool("autosave_screensize")) .. "]"
end
if shaders_enabled then
tab_string = tab_string ..
"checkbox[8.25,0.5;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";"
.. dump(core.settings:get_bool("tone_mapping")) .. "]" ..
"checkbox[8.25,1;cb_waving_water;" .. fgettext("Waving Liquids") .. ";"
.. dump(core.settings:get_bool("enable_waving_water")) .. "]" ..
"checkbox[8.25,1.5;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";"
.. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" ..
"checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
.. dump(core.settings:get_bool("enable_waving_plants")) .. "]"
if video_driver == "opengl" then
tab_string = tab_string ..
"label[8.25,2.8;" .. fgettext("Dynamic shadows:") .. "]" ..
"label[8.25,3.2;" .. fgettext("(game support required)") .. "]" ..
"dropdown[8.25,3.7;3.5;dd_shadows;" .. dd_options.shadow_levels[1] .. ";"
.. getSettingIndex.ShadowMapping() .. "]"
else
tab_string = tab_string ..
"label[8.38,2.7;" .. core.colorize("#888888",
fgettext("Dynamic shadows")) .. "]"
end
else
tab_string = tab_string ..
"label[8.38,0.7;" .. core.colorize("#888888",
fgettext("Tone Mapping")) .. "]" ..
"label[8.38,1.2;" .. core.colorize("#888888",
fgettext("Waving Liquids")) .. "]" ..
"label[8.38,1.7;" .. core.colorize("#888888",
fgettext("Waving Leaves")) .. "]" ..
"label[8.38,2.2;" .. core.colorize("#888888",
fgettext("Waving Plants")) .. "]"..
"label[8.38,2.7;" .. core.colorize("#888888",
fgettext("Dynamic shadows")) .. "]"
end
return tab_string
end
--------------------------------------------------------------------------------
local function handle_settings_buttons(this, fields, tabname, tabdata)
if fields["btn_advanced_settings"] ~= nil then
local adv_settings_dlg = create_adv_settings_dlg()
adv_settings_dlg:set_parent(this)
this:hide()
adv_settings_dlg:show()
--mm_game_theme.update("singleplayer", current_game())
return true
end
if fields["cb_smooth_lighting"] then
core.settings:set("smooth_lighting", fields["cb_smooth_lighting"])
return true
end
if fields["cb_particles"] then
core.settings:set("enable_particles", fields["cb_particles"])
return true
end
if fields["cb_3d_clouds"] then
core.settings:set("enable_3d_clouds", fields["cb_3d_clouds"])
return true
end
if fields["cb_opaque_water"] then
core.settings:set("opaque_water", fields["cb_opaque_water"])
return true
end
if fields["cb_connected_glass"] then
core.settings:set("connected_glass", fields["cb_connected_glass"])
return true
end
if fields["cb_autosave_screensize"] then
core.settings:set("autosave_screensize", fields["cb_autosave_screensize"])
return true
end
if fields["cb_shaders"] then
core.settings:set("enable_shaders", fields["cb_shaders"])
return true
end
if fields["cb_tonemapping"] then
core.settings:set("tone_mapping", fields["cb_tonemapping"])
return true
end
if fields["cb_waving_water"] then
core.settings:set("enable_waving_water", fields["cb_waving_water"])
return true
end
if fields["cb_waving_leaves"] then
core.settings:set("enable_waving_leaves", fields["cb_waving_leaves"])
end
if fields["cb_waving_plants"] then
core.settings:set("enable_waving_plants", fields["cb_waving_plants"])
return true
end
if fields["btn_change_keys"] then
core.show_keys_menu()
return true
end
--Note dropdowns have to be handled LAST!
local ddhandled = false
for i = 1, #labels.leaves do
if fields["dd_leaves_style"] == labels.leaves[i] then
core.settings:set("leaves_style", dd_options.leaves[2][i])
ddhandled = true
end
end
for i = 1, #labels.node_highlighting do
if fields["dd_node_highlighting"] == labels.node_highlighting[i] then
core.settings:set("node_highlighting", dd_options.node_highlighting[2][i])
ddhandled = true
end
end
if fields["dd_filters"] == labels.filters[1] then
core.settings:set("bilinear_filter", "false")
core.settings:set("trilinear_filter", "false")
ddhandled = true
elseif fields["dd_filters"] == labels.filters[2] then
core.settings:set("bilinear_filter", "true")
core.settings:set("trilinear_filter", "false")
ddhandled = true
elseif fields["dd_filters"] == labels.filters[3] then
core.settings:set("bilinear_filter", "false")
core.settings:set("trilinear_filter", "true")
ddhandled = true
end
if fields["dd_mipmap"] == labels.mipmap[1] then
core.settings:set("mip_map", "false")
core.settings:set("anisotropic_filter", "false")
ddhandled = true
elseif fields["dd_mipmap"] == labels.mipmap[2] then
core.settings:set("mip_map", "true")
core.settings:set("anisotropic_filter", "false")
ddhandled = true
elseif fields["dd_mipmap"] == labels.mipmap[3] then
core.settings:set("mip_map", "true")
core.settings:set("anisotropic_filter", "true")
ddhandled = true
end
if fields["dd_antialiasing"] then
core.settings:set("fsaa",
antialiasing_fname_to_name(fields["dd_antialiasing"]))
ddhandled = true
end
if fields["dd_touchthreshold"] then
core.settings:set("touchscreen_threshold", fields["dd_touchthreshold"])
ddhandled = true
end
for i = 1, #labels.shadow_levels do
if fields["dd_shadows"] == labels.shadow_levels[i] then
core.settings:set("shadow_levels", dd_options.shadow_levels[2][i])
ddhandled = true
end
end
if fields["dd_shadows"] == labels.shadow_levels[1] then
core.settings:set("enable_dynamic_shadows", "false")
else
local shadow_presets = {
[2] = { 62, 512, "true", 0, "false" },
[3] = { 93, 1024, "true", 0, "false" },
[4] = { 140, 2048, "true", 1, "false" },
[5] = { 210, 4096, "true", 2, "true" },
[6] = { 300, 8192, "true", 2, "true" },
}
local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])]
if s then
core.settings:set("enable_dynamic_shadows", "true")
core.settings:set("shadow_map_max_distance", s[1])
core.settings:set("shadow_map_texture_size", s[2])
core.settings:set("shadow_map_texture_32bit", s[3])
core.settings:set("shadow_filters", s[4])
core.settings:set("shadow_map_color", s[5])
end
end
return ddhandled
end
return {
name = "settings",
caption = fgettext("Settings"),
cbf_formspec = formspec,
cbf_button_handler = handle_settings_buttons
}

@ -2051,7 +2051,7 @@ joystick_deadzone (Joystick dead zone) int 2048 0 65535
joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170.0 0.001
[*Temporary Settings]
[*Hide: Temporary Settings]
# Path to texture directory. All textures are first searched from here.
texture_path (Texture path) path