mirror of
https://github.com/minetest/minetest.git
synced 2024-11-27 01:53:45 +01:00
Redesign/unify mainmenu settings interface
This commit is contained in:
parent
a421a1d764
commit
d35672e78e
@ -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)
|
||||
|
384
builtin/mainmenu/settings/components.lua
Normal file
384
builtin/mainmenu/settings/components.lua
Normal file
@ -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
|
249
builtin/mainmenu/settings/dlg_change_mapgen_flags.lua
Normal file
249
builtin/mainmenu/settings/dlg_change_mapgen_flags.lua
Normal file
@ -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
|
520
builtin/mainmenu/settings/dlg_settings.lua
Normal file
520
builtin/mainmenu/settings/dlg_settings.lua
Normal file
@ -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
|
28
builtin/mainmenu/settings/init.lua
Normal file
28
builtin/mainmenu/settings/init.lua
Normal file
@ -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))
|
456
builtin/mainmenu/settings/settingtypes.lua
Normal file
456
builtin/mainmenu/settings/settingtypes.lua
Normal file
@ -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
|
159
builtin/mainmenu/settings/shader_component.lua
Normal file
159
builtin/mainmenu/settings/shader_component.lua
Normal file
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user