2022-06-25 18:33:20 +02:00
|
|
|
|
--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")
|
|
|
|
|
|
2023-08-05 18:33:18 +02:00
|
|
|
|
local shadows_component = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
|
|
|
|
|
"settings" .. DIR_DELIM .. "shadows_component.lua")
|
2022-06-25 18:33:20 +02:00
|
|
|
|
|
2024-06-24 21:56:37 +02:00
|
|
|
|
local loaded = false
|
|
|
|
|
local full_settings
|
2022-06-25 18:33:20 +02:00
|
|
|
|
local info_icon_path = core.formspec_escape(defaulttexturedir .. "settings_info.png")
|
|
|
|
|
local reset_icon_path = core.formspec_escape(defaulttexturedir .. "settings_reset.png")
|
|
|
|
|
local all_pages = {}
|
|
|
|
|
local page_by_id = {}
|
|
|
|
|
local filtered_pages = all_pages
|
|
|
|
|
local filtered_page_by_id = page_by_id
|
|
|
|
|
|
2024-06-24 21:56:37 +02:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2022-06-25 18:33:20 +02:00
|
|
|
|
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 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(" ", "_"),
|
2023-11-10 01:00:17 +01:00
|
|
|
|
title = section or fgettext_ne("General"),
|
2022-06-25 18:33:20 +02:00
|
|
|
|
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 = {},
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 16:25:57 +01:00
|
|
|
|
page = add_page(page)
|
2022-06-25 18:33:20 +02:00
|
|
|
|
elseif entry.level == 2 then
|
|
|
|
|
ensure_page_started()
|
|
|
|
|
page.content[#page.content + 1] = {
|
2023-11-10 01:00:17 +01:00
|
|
|
|
heading = fgettext_ne(entry.readable_name or entry.name),
|
2022-06-25 18:33:20 +02:00
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
ensure_page_started()
|
|
|
|
|
page.content[#page.content + 1] = entry.name
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2024-06-24 21:56:37 +02:00
|
|
|
|
local function load()
|
|
|
|
|
if loaded then
|
|
|
|
|
return
|
2022-06-25 18:33:20 +02:00
|
|
|
|
end
|
2024-06-24 21:56:37 +02:00
|
|
|
|
loaded = true
|
2022-06-25 18:33:20 +02:00
|
|
|
|
|
2024-06-24 21:56:37 +02:00
|
|
|
|
full_settings = settingtypes.parse_config_file(false, true)
|
2022-06-25 18:33:20 +02:00
|
|
|
|
|
2024-06-24 21:56:37 +02:00
|
|
|
|
local change_keys = {
|
|
|
|
|
query_text = "Controls",
|
|
|
|
|
requires = {
|
2024-06-02 21:58:41 +02:00
|
|
|
|
touch_controls = false,
|
2024-06-24 21:56:37 +02:00
|
|
|
|
},
|
|
|
|
|
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("Controls")), 0.8
|
|
|
|
|
end,
|
|
|
|
|
on_submit = function(self, fields)
|
|
|
|
|
if fields.btn_change_keys then
|
|
|
|
|
core.show_keys_menu()
|
|
|
|
|
end
|
|
|
|
|
end,
|
|
|
|
|
}
|
2022-06-25 18:33:20 +02:00
|
|
|
|
|
2024-06-24 21:56:37 +02:00
|
|
|
|
add_page({
|
|
|
|
|
id = "accessibility",
|
|
|
|
|
title = fgettext_ne("Accessibility"),
|
|
|
|
|
content = {
|
|
|
|
|
"language",
|
|
|
|
|
{ heading = fgettext_ne("General") },
|
|
|
|
|
"font_size",
|
|
|
|
|
"chat_font_size",
|
|
|
|
|
"gui_scaling",
|
|
|
|
|
"hud_scaling",
|
|
|
|
|
"show_nametag_backgrounds",
|
|
|
|
|
{ heading = fgettext_ne("Chat") },
|
|
|
|
|
"console_height",
|
|
|
|
|
"console_alpha",
|
|
|
|
|
"console_color",
|
|
|
|
|
{ heading = fgettext_ne("Controls") },
|
|
|
|
|
"autojump",
|
|
|
|
|
"safe_dig_and_place",
|
|
|
|
|
{ heading = fgettext_ne("Movement") },
|
|
|
|
|
"arm_inertia",
|
|
|
|
|
"view_bobbing_amount",
|
|
|
|
|
"fall_bobbing_amount",
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
load_settingtypes()
|
|
|
|
|
|
|
|
|
|
table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys)
|
|
|
|
|
do
|
2024-10-03 11:37:04 +02:00
|
|
|
|
local content = page_by_id.graphics_and_audio_effects.content
|
2024-06-24 21:56:37 +02:00
|
|
|
|
local idx = table.indexof(content, "enable_dynamic_shadows")
|
|
|
|
|
table.insert(content, idx, shadows_component)
|
2024-10-03 11:37:04 +02:00
|
|
|
|
|
|
|
|
|
idx = table.indexof(content, "enable_auto_exposure") + 1
|
|
|
|
|
local note = component_funcs.note(fgettext_ne("(The game will need to enable automatic exposure as well)"))
|
|
|
|
|
note.requires = get_setting_info("enable_auto_exposure").requires
|
|
|
|
|
table.insert(content, idx, note)
|
|
|
|
|
|
|
|
|
|
idx = table.indexof(content, "enable_volumetric_lighting") + 1
|
|
|
|
|
note = component_funcs.note(fgettext_ne("(The game will need to enable volumetric lighting as well)"))
|
|
|
|
|
note.requires = get_setting_info("enable_volumetric_lighting").requires
|
|
|
|
|
table.insert(content, idx, note)
|
2024-06-24 21:56:37 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- These must not be translated, as they need to show in the local
|
|
|
|
|
-- language no matter the user's current language.
|
|
|
|
|
-- This list must be kept in sync with src/unsupported_language_list.txt.
|
|
|
|
|
get_setting_info("language").option_labels = {
|
|
|
|
|
[""] = fgettext_ne("(Use system language)"),
|
|
|
|
|
--ar = " [ar]", blacklisted
|
|
|
|
|
be = "Беларуская [be]",
|
|
|
|
|
bg = "Български [bg]",
|
|
|
|
|
ca = "Català [ca]",
|
|
|
|
|
cs = "Česky [cs]",
|
|
|
|
|
cy = "Cymraeg [cy]",
|
|
|
|
|
da = "Dansk [da]",
|
|
|
|
|
de = "Deutsch [de]",
|
|
|
|
|
--dv = " [dv]", blacklisted
|
|
|
|
|
el = "Ελληνικά [el]",
|
|
|
|
|
en = "English [en]",
|
|
|
|
|
eo = "Esperanto [eo]",
|
|
|
|
|
es = "Español [es]",
|
|
|
|
|
et = "Eesti [et]",
|
|
|
|
|
eu = "Euskara [eu]",
|
|
|
|
|
fi = "Suomi [fi]",
|
|
|
|
|
fil = "Wikang Filipino [fil]",
|
|
|
|
|
fr = "Français [fr]",
|
|
|
|
|
gd = "Gàidhlig [gd]",
|
|
|
|
|
gl = "Galego [gl]",
|
|
|
|
|
--he = " [he]", blacklisted
|
|
|
|
|
--hi = " [hi]", blacklisted
|
|
|
|
|
hu = "Magyar [hu]",
|
|
|
|
|
id = "Bahasa Indonesia [id]",
|
|
|
|
|
it = "Italiano [it]",
|
|
|
|
|
ja = "日本語 [ja]",
|
|
|
|
|
jbo = "Lojban [jbo]",
|
|
|
|
|
kk = "Қазақша [kk]",
|
|
|
|
|
--kn = " [kn]", blacklisted
|
|
|
|
|
ko = "한국어 [ko]",
|
|
|
|
|
ky = "Kırgızca / Кыргызча [ky]",
|
|
|
|
|
lt = "Lietuvių [lt]",
|
|
|
|
|
lv = "Latviešu [lv]",
|
|
|
|
|
mn = "Монгол [mn]",
|
|
|
|
|
mr = "मराठी [mr]",
|
|
|
|
|
ms = "Bahasa Melayu [ms]",
|
|
|
|
|
--ms_Arab = " [ms_Arab]", blacklisted
|
|
|
|
|
nb = "Norsk Bokmål [nb]",
|
|
|
|
|
nl = "Nederlands [nl]",
|
|
|
|
|
nn = "Norsk Nynorsk [nn]",
|
|
|
|
|
oc = "Occitan [oc]",
|
|
|
|
|
pl = "Polski [pl]",
|
|
|
|
|
pt = "Português [pt]",
|
|
|
|
|
pt_BR = "Português do Brasil [pt_BR]",
|
|
|
|
|
ro = "Română [ro]",
|
|
|
|
|
ru = "Русский [ru]",
|
|
|
|
|
sk = "Slovenčina [sk]",
|
|
|
|
|
sl = "Slovenščina [sl]",
|
|
|
|
|
sr_Cyrl = "Српски [sr_Cyrl]",
|
|
|
|
|
sr_Latn = "Srpski (Latinica) [sr_Latn]",
|
|
|
|
|
sv = "Svenska [sv]",
|
|
|
|
|
sw = "Kiswahili [sw]",
|
|
|
|
|
--th = " [th]", blacklisted
|
|
|
|
|
tr = "Türkçe [tr]",
|
|
|
|
|
tt = "Tatarça [tt]",
|
|
|
|
|
uk = "Українська [uk]",
|
|
|
|
|
vi = "Tiếng Việt [vi]",
|
|
|
|
|
zh_CN = "中文 (简体) [zh_CN]",
|
|
|
|
|
zh_TW = "正體中文 (繁體) [zh_TW]",
|
|
|
|
|
}
|
|
|
|
|
end
|
2023-08-24 01:33:23 +02:00
|
|
|
|
|
|
|
|
|
|
2022-06-25 18:33:20 +02:00
|
|
|
|
-- 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
|
2023-08-05 18:55:27 +02:00
|
|
|
|
return page.content, 0
|
2022-06-25 18:33:20 +02:00
|
|
|
|
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 = {}
|
|
|
|
|
|
|
|
|
|
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)
|
2023-10-07 21:34:59 +02:00
|
|
|
|
if page_has_contents(page, content) then
|
2022-06-25 18:33:20 +02:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2023-08-05 18:55:27 +02:00
|
|
|
|
local function check_requirements(name, requires)
|
|
|
|
|
if requires == nil then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local video_driver = core.get_active_driver()
|
2024-01-16 21:09:18 +01:00
|
|
|
|
local shaders_support = video_driver == "opengl" or video_driver == "opengl3" or video_driver == "ogles2"
|
2023-08-05 18:55:27 +02:00
|
|
|
|
local special = {
|
|
|
|
|
android = PLATFORM == "Android",
|
|
|
|
|
desktop = PLATFORM ~= "Android",
|
|
|
|
|
shaders_support = shaders_support,
|
|
|
|
|
shaders = core.settings:get_bool("enable_shaders") and shaders_support,
|
|
|
|
|
opengl = video_driver == "opengl",
|
|
|
|
|
gles = video_driver:sub(1, 5) == "ogles",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for req_key, req_value in pairs(requires) do
|
|
|
|
|
if special[req_key] == nil then
|
|
|
|
|
local required_setting = get_setting_info(req_key)
|
|
|
|
|
if required_setting == nil then
|
|
|
|
|
core.log("warning", "Unknown setting " .. req_key .. " required by " .. name)
|
|
|
|
|
end
|
|
|
|
|
local actual_value = core.settings:get_bool(req_key,
|
|
|
|
|
required_setting and core.is_yes(required_setting.default))
|
|
|
|
|
if actual_value ~= req_value then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
elseif special[req_key] ~= req_value then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2023-10-07 21:34:59 +02:00
|
|
|
|
function page_has_contents(page, actual_content)
|
|
|
|
|
local is_advanced =
|
|
|
|
|
page.id:sub(1, #"client_and_server") == "client_and_server" or
|
|
|
|
|
page.id:sub(1, #"mapgen") == "mapgen" or
|
|
|
|
|
page.id:sub(1, #"advanced") == "advanced"
|
|
|
|
|
local show_advanced = core.settings:get_bool("show_advanced")
|
|
|
|
|
if is_advanced and not show_advanced then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
for _, item in ipairs(actual_content) do
|
2023-08-05 18:55:27 +02:00
|
|
|
|
if item == false or item.heading then --luacheck: ignore
|
|
|
|
|
-- skip
|
|
|
|
|
elseif type(item) == "string" then
|
|
|
|
|
local setting = get_setting_info(item)
|
|
|
|
|
assert(setting, "Unknown setting: " .. item)
|
|
|
|
|
if check_requirements(setting.name, setting.requires) then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
elseif item.get_formspec then
|
|
|
|
|
if check_requirements(item.id, item.requires) then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
error("Unknown content in page: " .. dump(item))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2022-06-25 18:33:20 +02:00
|
|
|
|
local function build_page_components(page)
|
2023-08-05 18:55:27 +02:00
|
|
|
|
-- Filter settings based on requirements
|
|
|
|
|
local content = {}
|
|
|
|
|
local last_heading
|
|
|
|
|
for _, item in ipairs(page.content) do
|
|
|
|
|
if item == false then --luacheck: ignore
|
|
|
|
|
-- skip
|
|
|
|
|
elseif item.heading then
|
|
|
|
|
last_heading = item
|
|
|
|
|
else
|
|
|
|
|
local name, requires
|
|
|
|
|
if type(item) == "string" then
|
|
|
|
|
local setting = get_setting_info(item)
|
|
|
|
|
assert(setting, "Unknown setting: " .. item)
|
|
|
|
|
name = setting.name
|
|
|
|
|
requires = setting.requires
|
|
|
|
|
elseif item.get_formspec then
|
|
|
|
|
name = item.id
|
|
|
|
|
requires = item.requires
|
|
|
|
|
else
|
|
|
|
|
error("Unknown content in page: " .. dump(item))
|
|
|
|
|
end
|
2022-06-25 18:33:20 +02:00
|
|
|
|
|
2023-08-05 18:55:27 +02:00
|
|
|
|
if check_requirements(name, requires) then
|
|
|
|
|
if last_heading then
|
|
|
|
|
content[#content + 1] = last_heading
|
|
|
|
|
last_heading = nil
|
|
|
|
|
end
|
|
|
|
|
content[#content + 1] = item
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Create components
|
|
|
|
|
local retval = {}
|
|
|
|
|
for i, item in ipairs(content) do
|
|
|
|
|
if type(item) == "string" then
|
|
|
|
|
local setting = get_setting_info(item)
|
2022-06-25 18:33:20 +02:00
|
|
|
|
local component_func = component_funcs[setting.type]
|
|
|
|
|
assert(component_func, "Unknown setting type: " .. setting.type)
|
2023-08-05 18:55:27 +02:00
|
|
|
|
retval[i] = component_func(setting)
|
|
|
|
|
elseif item.get_formspec then
|
|
|
|
|
retval[i] = item
|
|
|
|
|
elseif item.heading then
|
|
|
|
|
retval[i] = component_funcs.heading(item.heading)
|
2022-06-25 18:33:20 +02:00
|
|
|
|
end
|
|
|
|
|
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)
|
2023-10-07 21:34:59 +02:00
|
|
|
|
local page_id = dialogdata.page_id or "accessibility"
|
2022-06-25 18:33:20 +02:00
|
|
|
|
local page = filtered_page_by_id[page_id]
|
|
|
|
|
|
2023-06-05 12:01:54 +02:00
|
|
|
|
local extra_h = 1 -- not included in tabsize.height
|
|
|
|
|
local tabsize = {
|
2024-06-02 21:58:41 +02:00
|
|
|
|
width = core.settings:get_bool("touch_gui") and 16.5 or 15.5,
|
|
|
|
|
height = core.settings:get_bool("touch_gui") and (10 - extra_h) or 12,
|
2023-06-05 12:01:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-02 21:58:41 +02:00
|
|
|
|
local scrollbar_w = core.settings:get_bool("touch_gui") and 0.6 or 0.4
|
2022-06-25 18:33:20 +02:00
|
|
|
|
|
2024-06-02 21:58:41 +02:00
|
|
|
|
local left_pane_width = core.settings:get_bool("touch_gui") and 4.5 or 4.25
|
2024-02-25 23:10:07 +01:00
|
|
|
|
local left_pane_padding = 0.25
|
2022-06-25 18:33:20 +02:00
|
|
|
|
local search_width = left_pane_width + scrollbar_w - (0.75 * 2)
|
|
|
|
|
|
2023-10-07 21:34:59 +02:00
|
|
|
|
local back_w = 3
|
|
|
|
|
local checkbox_w = (tabsize.width - back_w - 2*0.2) / 2
|
2022-06-25 18:33:20 +02:00
|
|
|
|
local show_technical_names = core.settings:get_bool("show_technical_names")
|
2023-10-07 21:34:59 +02:00
|
|
|
|
local show_advanced = core.settings:get_bool("show_advanced")
|
2022-06-25 18:33:20 +02:00
|
|
|
|
|
|
|
|
|
formspec_show_hack = not formspec_show_hack
|
|
|
|
|
|
|
|
|
|
local fs = {
|
|
|
|
|
"formspec_version[6]",
|
2023-06-05 12:01:54 +02:00
|
|
|
|
"size[", tostring(tabsize.width), ",", tostring(tabsize.height + extra_h), "]",
|
2024-06-02 21:58:41 +02:00
|
|
|
|
core.settings:get_bool("touch_gui") and "padding[0.01,0.01]" or "",
|
2022-06-25 18:33:20 +02:00
|
|
|
|
"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]",
|
|
|
|
|
|
2023-10-07 21:34:59 +02:00
|
|
|
|
("button[0,%f;%f,0.8;back;%s]"):format(
|
|
|
|
|
tabsize.height + 0.2, back_w, fgettext("Back")),
|
2022-06-25 18:33:20 +02:00
|
|
|
|
|
2023-06-05 12:01:54 +02:00
|
|
|
|
("box[%f,%f;%f,0.8;#0000008C]"):format(
|
2023-10-07 21:34:59 +02:00
|
|
|
|
back_w + 0.2, tabsize.height + 0.2, checkbox_w),
|
2023-06-05 12:01:54 +02:00
|
|
|
|
("checkbox[%f,%f;show_technical_names;%s;%s]"):format(
|
2023-10-07 21:34:59 +02:00
|
|
|
|
back_w + 2*0.2, tabsize.height + 0.6,
|
2023-06-05 12:01:54 +02:00
|
|
|
|
fgettext("Show technical names"), tostring(show_technical_names)),
|
2022-06-25 18:33:20 +02:00
|
|
|
|
|
2023-10-07 21:34:59 +02:00
|
|
|
|
("box[%f,%f;%f,0.8;#0000008C]"):format(
|
|
|
|
|
back_w + 2*0.2 + checkbox_w, tabsize.height + 0.2, checkbox_w),
|
|
|
|
|
("checkbox[%f,%f;show_advanced;%s;%s]"):format(
|
|
|
|
|
back_w + 3*0.2 + checkbox_w, tabsize.height + 0.6,
|
|
|
|
|
fgettext("Show advanced settings"), tostring(show_advanced)),
|
|
|
|
|
|
2022-06-25 18:33:20 +02:00
|
|
|
|
"field[0.25,0.25;", tostring(search_width), ",0.75;search_query;;",
|
|
|
|
|
core.formspec_escape(dialogdata.query or ""), "]",
|
2023-10-01 11:20:50 +02:00
|
|
|
|
"field_enter_after_edit[search_query;true]",
|
2022-06-25 18:33:20 +02:00
|
|
|
|
"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
|
2023-08-05 18:55:27 +02:00
|
|
|
|
fs[#fs + 1] = ("label[0.1,%f;%s]"):format(
|
|
|
|
|
y + 0.41, core.colorize("#ff0", fgettext(other_page.section)))
|
2022-06-25 18:33:20 +02:00
|
|
|
|
last_section = other_page.section
|
|
|
|
|
y = y + 0.82
|
|
|
|
|
end
|
|
|
|
|
fs[#fs + 1] = ("box[0,%f;%f,0.8;%s]"):format(
|
2024-02-25 23:10:07 +01:00
|
|
|
|
y, left_pane_width-left_pane_padding, other_page.id == page_id and "#467832FF" or "#3339")
|
2022-06-25 18:33:20 +02:00
|
|
|
|
fs[#fs + 1] = ("button[0,%f;%f,0.8;page_%s;%s]")
|
2024-02-25 23:10:07 +01:00
|
|
|
|
:format(y, left_pane_width-left_pane_padding, other_page.id, fgettext(other_page.title))
|
2022-06-25 18:33:20 +02:00
|
|
|
|
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=]"
|
|
|
|
|
|
2023-05-18 20:32:26 +02:00
|
|
|
|
local show_reset = comp.resettable and comp.setting
|
2022-06-25 18:33:20 +02:00
|
|
|
|
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
|
2023-05-18 20:32:26 +02:00
|
|
|
|
local default = comp.setting.default
|
|
|
|
|
local reset_tooltip = default and
|
|
|
|
|
fgettext("Reset setting to default ($1)", tostring(default)) or
|
|
|
|
|
fgettext("Reset setting to default")
|
2022-06-25 18:33:20 +02:00
|
|
|
|
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)
|
2023-05-18 20:32:26 +02:00
|
|
|
|
fs[#fs + 1] = ("tooltip[%s;%s]"):format("reset_" .. i, reset_tooltip)
|
2022-06-25 18:33:20 +02:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2024-01-23 21:33:27 +01:00
|
|
|
|
-- On Android, closing the app via the "Recents screen" won't result in a clean
|
|
|
|
|
-- exit, discarding any setting changes made by the user.
|
|
|
|
|
-- To avoid that, we write the settings file in more cases on Android.
|
|
|
|
|
function write_settings_early()
|
|
|
|
|
if PLATFORM == "Android" then
|
|
|
|
|
core.settings:write()
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2022-06-25 18:33:20 +02:00
|
|
|
|
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)
|
2024-01-23 21:33:27 +01:00
|
|
|
|
write_settings_early()
|
|
|
|
|
|
2022-06-25 18:33:20 +02:00
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
2023-10-07 21:34:59 +02:00
|
|
|
|
if fields.show_advanced ~= nil then
|
|
|
|
|
local value = core.is_yes(fields.show_advanced)
|
|
|
|
|
core.settings:set_bool("show_advanced", value)
|
2024-01-23 21:33:27 +01:00
|
|
|
|
write_settings_early()
|
2024-02-22 16:44:49 +01:00
|
|
|
|
end
|
|
|
|
|
|
2024-06-02 21:58:41 +02:00
|
|
|
|
-- touch_controls is a checkbox in a setting component. We handle this
|
2024-02-22 16:44:49 +01:00
|
|
|
|
-- setting differently so we can hide/show pages using the next if-statement
|
2024-06-02 21:58:41 +02:00
|
|
|
|
if fields.touch_controls ~= nil then
|
|
|
|
|
local value = core.is_yes(fields.touch_controls)
|
|
|
|
|
core.settings:set_bool("touch_controls", value)
|
2024-02-22 16:44:49 +01:00
|
|
|
|
write_settings_early()
|
|
|
|
|
end
|
2023-10-07 21:34:59 +02:00
|
|
|
|
|
2024-06-02 21:58:41 +02:00
|
|
|
|
if fields.show_advanced ~= nil or fields.touch_controls ~= nil then
|
2023-10-07 21:34:59 +02:00
|
|
|
|
local suggested_page_id = update_filtered_pages(dialogdata.query)
|
|
|
|
|
|
2024-02-22 16:44:49 +01:00
|
|
|
|
dialogdata.components = nil
|
|
|
|
|
|
2023-10-07 21:34:59 +02:00
|
|
|
|
if not filtered_page_by_id[dialogdata.page_id] then
|
|
|
|
|
dialogdata.leftscroll = 0
|
|
|
|
|
dialogdata.rightscroll = 0
|
|
|
|
|
|
|
|
|
|
dialogdata.page_id = suggested_page_id
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
2022-06-25 18:33:20 +02:00
|
|
|
|
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
|
2024-01-23 21:33:27 +01:00
|
|
|
|
write_settings_early()
|
|
|
|
|
|
2023-08-05 18:55:27 +02:00
|
|
|
|
-- Clear components so they regenerate
|
|
|
|
|
dialogdata.components = nil
|
2022-06-25 18:33:20 +02:00
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
if comp.setting and fields["reset_" .. i] then
|
2023-05-18 20:32:26 +02:00
|
|
|
|
core.settings:remove(comp.setting.name)
|
2024-01-23 21:33:27 +01:00
|
|
|
|
write_settings_early()
|
2023-08-05 18:55:27 +02:00
|
|
|
|
|
|
|
|
|
-- Clear components so they regenerate
|
|
|
|
|
dialogdata.components = nil
|
2022-06-25 18:33:20 +02:00
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2023-10-18 20:18:50 +02:00
|
|
|
|
local function eventhandler(event)
|
|
|
|
|
if event == "DialogShow" then
|
|
|
|
|
-- Don't show the "MINETEST" header behind the dialog.
|
|
|
|
|
mm_game_theme.set_engine(true)
|
|
|
|
|
return true
|
|
|
|
|
end
|
2024-06-02 21:05:16 +02:00
|
|
|
|
if event == "FullscreenChange" then
|
|
|
|
|
-- Refresh the formspec to keep the fullscreen checkbox up to date.
|
|
|
|
|
ui.update()
|
|
|
|
|
return true
|
|
|
|
|
end
|
2023-10-18 20:18:50 +02:00
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2022-06-25 18:33:20 +02:00
|
|
|
|
function create_settings_dlg()
|
2024-06-24 21:56:37 +02:00
|
|
|
|
load()
|
2023-10-18 20:18:50 +02:00
|
|
|
|
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)
|
2023-05-27 16:35:01 +02:00
|
|
|
|
|
|
|
|
|
dlg.data.page_id = update_filtered_pages("")
|
|
|
|
|
|
|
|
|
|
return dlg
|
2022-06-25 18:33:20 +02:00
|
|
|
|
end
|