From 85d0ffbc0f2aa095806d2cd359cc0a257a6192d1 Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Sun, 23 Mar 2025 21:55:45 +0100 Subject: [PATCH 01/10] Implement secondary keybindings --- src/client/inputhandler.cpp | 25 +++++++++++++++++-------- src/client/inputhandler.h | 3 ++- src/client/keycode.cpp | 21 +++++++++++++++------ src/client/keycode.h | 9 ++++++++- src/gui/guiChatConsole.cpp | 2 +- src/gui/guiFormSpecMenu.cpp | 8 ++++---- src/gui/touchcontrols.cpp | 6 +++--- 7 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index d79bee4d6..877de8d1c 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -26,7 +26,7 @@ void MyEventReceiver::reloadKeybindings() keybindings[KeyType::DIG] = getKeySetting("keymap_dig"); keybindings[KeyType::PLACE] = getKeySetting("keymap_place"); - keybindings[KeyType::ESC] = EscapeKey; + keybindings[KeyType::ESC] = std::vector{EscapeKey}; keybindings[KeyType::AUTOFORWARD] = getKeySetting("keymap_autoforward"); @@ -76,7 +76,9 @@ void MyEventReceiver::reloadKeybindings() // First clear all keys, then re-add the ones we listen for keysListenedFor.clear(); for (int i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) { - listenForKey(keybindings[i], static_cast(i)); + for (auto key: keybindings[i]) { + listenForKey(key, static_cast(i)); + } } } @@ -85,13 +87,11 @@ bool MyEventReceiver::setKeyDown(KeyPress keyCode, bool is_down) if (keysListenedFor.find(keyCode) == keysListenedFor.end()) // ignore irrelevant key input return false; auto action = keysListenedFor[keyCode]; - if (is_down) { + if (is_down) physicalKeyDown.insert(keyCode); - setKeyDown(action, true); - } else { + else physicalKeyDown.erase(keyCode); - setKeyDown(action, false); - } + setKeyDown(action, checkKeyDown(action)); return true; } @@ -109,6 +109,15 @@ void MyEventReceiver::setKeyDown(GameKeyType action, bool is_down) } } +bool MyEventReceiver::checkKeyDown(GameKeyType action) +{ + for (auto key: keybindings[action]) { + if (physicalKeyDown.find(key) != physicalKeyDown.end()) + return true; + } + return false; +} + bool MyEventReceiver::OnEvent(const SEvent &event) { if (event.EventType == irr::EET_LOG_TEXT_EVENT) { @@ -137,7 +146,7 @@ bool MyEventReceiver::OnEvent(const SEvent &event) // This is separate from other keyboard handling so that it also works in menus. if (event.EventType == EET_KEY_INPUT_EVENT) { KeyPress keyCode(event.KeyInput); - if (keyCode == getKeySetting("keymap_fullscreen")) { + if (keySettingHasMatch("keymap_fullscreen", keyCode)) { if (event.KeyInput.PressedDown && !fullscreen_is_down) { IrrlichtDevice *device = RenderingEngine::get_raw_device(); diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index 85da30ff8..bc0891c9f 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -97,13 +97,14 @@ private: bool setKeyDown(KeyPress keyCode, bool is_down); void setKeyDown(GameKeyType action, bool is_down); + bool checkKeyDown(GameKeyType action); /* This is faster than using getKeySetting with the tradeoff that functions * using it must make sure that it's initialised before using it and there is * no error handling (for example bounds checking). This is useful here as the * faster (up to 10x faster) key lookup is an asset. */ - std::array keybindings; + std::array, KeyType::INTERNAL_ENUM_COUNT> keybindings; s32 mouse_wheel = 0; diff --git a/src/client/keycode.cpp b/src/client/keycode.cpp index 71e068e30..86404708c 100644 --- a/src/client/keycode.cpp +++ b/src/client/keycode.cpp @@ -380,23 +380,32 @@ KeyPress KeyPress::getSpecialKey(const std::string &name) */ // A simple cache for quicker lookup -static std::unordered_map g_key_setting_cache; +static std::unordered_map> g_key_setting_cache; -KeyPress getKeySetting(const std::string &settingname) +const std::vector &getKeySetting(const std::string &settingname) { auto n = g_key_setting_cache.find(settingname); if (n != g_key_setting_cache.end()) return n->second; - auto keysym = g_settings->get(settingname); + auto setting_value = g_settings->get(settingname); auto &ref = g_key_setting_cache[settingname]; - ref = KeyPress(keysym); - if (!keysym.empty() && !ref) { - warningstream << "Invalid key '" << keysym << "' for '" << settingname << "'." << std::endl; + for (const auto &keysym: str_split(setting_value, '|')) { + if (KeyPress kp = keysym) { + ref.push_back(kp); + } else { + warningstream << "Invalid key '" << keysym << "' for '" << settingname << "'." << std::endl; + } } return ref; } +bool keySettingHasMatch(const std::string &settingname, KeyPress kp) +{ + const auto &keylist = getKeySetting(settingname); + return std::find(keylist.begin(), keylist.end(), kp) != keylist.end(); +} + void clearKeyCache() { g_key_setting_cache.clear(); diff --git a/src/client/keycode.h b/src/client/keycode.h index 503529b52..0fd4f24b9 100644 --- a/src/client/keycode.h +++ b/src/client/keycode.h @@ -9,6 +9,7 @@ #include #include #include +#include /* A key press, consisting of a scancode or a keycode. * This fits into 64 bits, so prefer passing this by value. @@ -92,7 +93,13 @@ struct std::hash #define RMBKey KeyPress::getSpecialKey("KEY_RBUTTON") // Key configuration getter -KeyPress getKeySetting(const std::string &settingname); +// Note that the reference may be invalidated by a next call to getKeySetting +// or a related function, so the value should either be used immediately or +// copied elsewhere before calling this again. +const std::vector &getKeySetting(const std::string &settingname); + +// Check whether the key setting includes a key. +bool keySettingHasMatch(const std::string &settingname, KeyPress kp); // Clear fast lookup cache void clearKeyCache(); diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp index 31df2a944..65df6ecfe 100644 --- a/src/gui/guiChatConsole.cpp +++ b/src/gui/guiChatConsole.cpp @@ -436,7 +436,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event) } // Key input - if (KeyPress(event.KeyInput) == getKeySetting("keymap_console")) { + if (keySettingHasMatch("keymap_console", event.KeyInput)) { closeConsole(); // inhibit open so the_game doesn't reopen immediately diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 97d86b05f..67c9a2ace 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3996,7 +3996,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) if (event.EventType == EET_KEY_INPUT_EVENT) { KeyPress kp(event.KeyInput); if (kp == EscapeKey - || kp == getKeySetting("keymap_inventory") + || keySettingHasMatch("keymap_inventory", kp) || event.KeyInput.Key==KEY_RETURN) { gui::IGUIElement *focused = Environment->getFocus(); if (focused && isMyChild(focused) && @@ -4070,17 +4070,17 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) KeyPress kp(event.KeyInput); if (event.KeyInput.PressedDown && ( (kp == EscapeKey) || - ((m_client != NULL) && (kp == getKeySetting("keymap_inventory"))))) { + ((m_client != NULL) && (keySettingHasMatch("keymap_inventory", kp))))) { tryClose(); return true; } if (m_client != NULL && event.KeyInput.PressedDown && - (kp == getKeySetting("keymap_screenshot"))) { + (keySettingHasMatch("keymap_screenshot", kp))) { m_client->makeScreenshot(); } - if (event.KeyInput.PressedDown && kp == getKeySetting("keymap_toggle_debug")) { + if (event.KeyInput.PressedDown && keySettingHasMatch("keymap_toggle_debug", kp)) { if (!m_client || m_client->checkPrivilege("debug")) m_show_debug = !m_show_debug; } diff --git a/src/gui/touchcontrols.cpp b/src/gui/touchcontrols.cpp index 6a3aeb108..c6a6d8b88 100644 --- a/src/gui/touchcontrols.cpp +++ b/src/gui/touchcontrols.cpp @@ -212,11 +212,11 @@ static KeyPress id_to_keypress(touch_gui_button_id id) auto setting_name = id_to_setting(id); assert(!setting_name.empty()); - auto kp = getKeySetting(setting_name); - if (!kp) + const auto &keylist = getKeySetting(setting_name); + if (keylist.empty()) warningstream << "TouchControls: Unbound or invalid key for " << setting_name << ", hiding button." << std::endl; - return kp; + return keylist[0]; } From 40c0825bba647af665aaa9f3f37fcb3a6f70d50a Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Sat, 24 May 2025 17:37:28 +0200 Subject: [PATCH 02/10] Correctly normalize secondary keybindings --- builtin/mainmenu/dlg_rebind_keys.lua | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/builtin/mainmenu/dlg_rebind_keys.lua b/builtin/mainmenu/dlg_rebind_keys.lua index ec4d1357f..a1e033531 100644 --- a/builtin/mainmenu/dlg_rebind_keys.lua +++ b/builtin/mainmenu/dlg_rebind_keys.lua @@ -74,6 +74,17 @@ local function create_rebind_keys_dlg() return dlg end +local function normalize_key_setting(str) + if str == "|" then + return core.normalize_keycode(str) + end + local t = string.split(str, "|") + for k, v in pairs(t) do + t[k] = core.normalize_keycode(v) + end + return table.concat(t, "|") +end + function migrate_keybindings(parent) -- Show migration dialog if the user upgraded from an earlier version -- and this has not yet been shown before, *or* if keys settings had to be changed @@ -86,7 +97,7 @@ function migrate_keybindings(parent) local settings = core.settings:to_table() for name, value in pairs(settings) do if name:match("^keymap_") then - local normalized = core.normalize_keycode(value) + local normalized = normalize_key_setting(value) if value ~= normalized then has_migration = true core.settings:set(name, normalized) From 7e31de882d80e440a624a70d58bf6134428dc5f0 Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Mon, 28 Apr 2025 23:51:18 +0200 Subject: [PATCH 03/10] partial: indicate multiple keybindings in setting menu --- builtin/common/settings/components.lua | 37 ++++++++++++++++++-------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/builtin/common/settings/components.lua b/builtin/common/settings/components.lua index 99fb0cd76..c4a065c7e 100644 --- a/builtin/common/settings/components.lua +++ b/builtin/common/settings/components.lua @@ -430,16 +430,26 @@ local function make_noise_params(setting) } end +local function has_keybinding_conflict(t1, t2) + for _, v1 in pairs(t1) do + for _, v2 in pairs(t2) do + if core.are_keycodes_equal(v1, v2) then + return true + end + end + end + return false +end + function make.key(setting) local btn_bind = "bind_" .. setting.name + local btn_edit = "edit_" .. setting.name local btn_clear = "unbind_" .. setting.name local function add_conflict_warnings(fs, height) - local value = core.settings:get(setting.name) - if value == "" then - return height - end + local value = core.settings:get(setting.name):split("|") for _, o in ipairs(core.full_settingtypes) do - if o.type == "key" and o.name ~= setting.name and core.are_keycodes_equal(core.settings:get(o.name), value) then + if o.type == "key" and o.name ~= setting.name + and has_keybinding_conflict(core.settings:get(o.name):split("|"), value) then table.insert(fs, ("label[0,%f;%s]"):format(height + 0.3, core.colorize(mt_color_orange, fgettext([[Conflicts with "$1"]], fgettext(o.readable_name))))) height = height + 0.6 @@ -455,17 +465,22 @@ function make.key(setting) get_formspec = function(self, avail_w) self.resettable = core.settings:has(setting.name) local btn_bind_width = math.max(2.5, avail_w/2) - local value = core.settings:get(setting.name) + local value = core.settings:get(setting.name):split("|") local fs = { ("label[0,0.4;%s]"):format(get_label(setting)), - ("button_key[%f,0;%f,0.8;%s;%s]"):format( - btn_bind_width, btn_bind_width-0.8, - btn_bind, core.formspec_escape(value)), ("image_button[%f,0;0.8,0.8;%s;%s;]"):format(avail_w - 0.8, - core.formspec_escape(defaulttexturedir .. "clear.png"), - btn_clear), + core.formspec_escape(defaulttexturedir .. "clear.png"), btn_clear), ("tooltip[%s;%s]"):format(btn_clear, fgettext("Remove keybinding")), } + if #value < 2 then + table.insert(fs, ("button_key[%f,0;%f,0.8;%s;%s]"):format( + btn_bind_width, btn_bind_width-0.8, + btn_bind, core.formspec_escape(value[1] or ""))) + else + table.insert(fs, ("button[%f,0;%f,0.8;%s;%s]"):format( + btn_bind_width, btn_bind_width-0.8, + btn_edit, fgettext("Edit"))) + end local height = 0.8 height = add_conflict_warnings(fs, height) return table.concat(fs), height From c9e28d39e9a05b9168e9ac1b313c886c6400e169 Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Sat, 24 May 2025 23:16:14 +0200 Subject: [PATCH 04/10] partial 2 --- builtin/common/settings/components.lua | 4 +- .../common/settings/dlg_change_keybinding.lua | 78 +++++++++++++++++++ builtin/common/settings/dlg_settings.lua | 6 +- builtin/common/settings/init.lua | 1 + 4 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 builtin/common/settings/dlg_change_keybinding.lua diff --git a/builtin/common/settings/components.lua b/builtin/common/settings/components.lua index c4a065c7e..52440d202 100644 --- a/builtin/common/settings/components.lua +++ b/builtin/common/settings/components.lua @@ -486,10 +486,12 @@ function make.key(setting) return table.concat(fs), height end, - on_submit = function(self, fields) + on_submit = function(self, fields, tabview) if fields[btn_bind] then core.settings:set(setting.name, fields[btn_bind]) return true + elseif fields[btn_edit] then + return show_change_keybinding_dlg(setting, tabview) elseif fields[btn_clear] then core.settings:set(setting.name, "") return true diff --git a/builtin/common/settings/dlg_change_keybinding.lua b/builtin/common/settings/dlg_change_keybinding.lua new file mode 100644 index 000000000..893e65b7e --- /dev/null +++ b/builtin/common/settings/dlg_change_keybinding.lua @@ -0,0 +1,78 @@ +-- Luanti +-- SPDX-License-Identifier: LGPL-2.1-or-later +local function get_formspec(dialogdata) + local name = dialogdata.setting.name + local readable_name = name + if dialogdata.setting.readable_name then + readable_name = ("%s (%s)"):format(fgettext(dialogdata.setting.readable_name), name) + end + + local fs = { + "formspec_version[4]", + "size[6,2]", -- size element; height can only be determined later + ("label[0.5,0.8;%s]"):format(readable_name), + } + + return table.concat(fs) +end + +local function buttonhandler(self, fields) + local name = self.data.setting.name + if fields.quit or fields.btn_close then + self:delete() + return true + end + return false +end + +local formspec_handlers = {} +if INIT == "pause_menu" then + core.register_on_formspec_input(function(formname, fields) + if formspec_handlers[formname] then + formspec_handlers[formname](fields) + return true + end + end) +end + +local is_mainmenu = INIT == "mainmenu" +function show_change_keybinding_dlg(setting, tabview) + local dlg + if is_mainmenu then + dlg = dialog_create("dlg_change_keybinding", + get_formspec, buttonhandler) + else + local name = "__builtin:rebind_" .. setting.name + dlg = { + show = function() + if dlg.removed then + --core.open_settings("controls_keyboard_and_mouse") + tabview:show() + else + core.show_formspec(name, get_formspec(dlg.data)) + end + end, + delete = function() + core.show_formspec(name, "") + formspec_handlers[name] = nil + dlg.removed = true + end, + data = {}, + } + formspec_handlers[name] = function(fields) + if buttonhandler(dlg, fields) then + dlg:show() + end + end + end + + dlg.data.setting = setting + + if is_mainmenu then + dlg:set_parent(tabview) + tabview:hide() + end + dlg:show() + + return is_mainmenu +end diff --git a/builtin/common/settings/dlg_settings.lua b/builtin/common/settings/dlg_settings.lua index 77fc8be3f..f92af751a 100644 --- a/builtin/common/settings/dlg_settings.lua +++ b/builtin/common/settings/dlg_settings.lua @@ -814,7 +814,9 @@ else -- case it's a no-op core.show_formspec("__builtin:settings", "") end - - core.show_formspec("__builtin:settings", get_formspec(dialog.data)) + dialog.show = function() -- Used by the keybinding form + core.show_formspec("__builtin:settings", get_formspec(dialog.data)) + end + dialog:show() end end diff --git a/builtin/common/settings/init.lua b/builtin/common/settings/init.lua index 71a95424b..d61d07492 100644 --- a/builtin/common/settings/init.lua +++ b/builtin/common/settings/init.lua @@ -6,6 +6,7 @@ local path = core.get_builtin_path() .. "common" .. DIR_DELIM .. "settings" .. D dofile(path .. "settingtypes.lua") dofile(path .. "dlg_change_mapgen_flags.lua") +dofile(path .. "dlg_change_keybinding.lua") dofile(path .. "dlg_settings.lua") -- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'. From 77142181f256f3bb582cfe36b2e245f61a07d580 Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Sun, 25 May 2025 00:33:52 +0200 Subject: [PATCH 05/10] complement keybinding form --- .../common/settings/dlg_change_keybinding.lua | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/builtin/common/settings/dlg_change_keybinding.lua b/builtin/common/settings/dlg_change_keybinding.lua index 893e65b7e..99c453223 100644 --- a/builtin/common/settings/dlg_change_keybinding.lua +++ b/builtin/common/settings/dlg_change_keybinding.lua @@ -7,12 +7,27 @@ local function get_formspec(dialogdata) readable_name = ("%s (%s)"):format(fgettext(dialogdata.setting.readable_name), name) end + local value = dialogdata.value + local selection = dialogdata.selection + local fs = { "formspec_version[4]", - "size[6,2]", -- size element; height can only be determined later + "size[6,9]", ("label[0.5,0.8;%s]"):format(readable_name), + ("button[0.5,5.7;5,0.8;btn_add;%s]"):format(fgettext("Add keybinding")), + ("button_key[0.5,6.7;4.2,0.8;btn_bind;%s]"):format(core.formspec_escape(value[selection] or "")), + ("image_button[4.6,6.7;0.8,0.8;%s;btn_clear;]"):format( + core.formspec_escape(defaulttexturedir .. "clear.png")), + ("button[3.1,7.7;2.4,0.8;btn_close;%s]"):format(fgettext("Cancel")), + ("button[0.5,7.7;2.4,0.8;btn_save;%s]"):format(fgettext("Save")), } + local cells = {} + for _, key in ipairs(value) do + table.insert(cells, core.formspec_escape(key)) + end + table.insert(fs, ("textlist[0.5,1.3;5,4;keylist;%s;%d;false]"):format(table.concat(cells, ","), selection)) + return table.concat(fs) end @@ -21,6 +36,34 @@ local function buttonhandler(self, fields) if fields.quit or fields.btn_close then self:delete() return true + elseif fields.btn_save then + local value = {} + for _, v in ipairs(self.data.value) do + if v ~= "" then -- filter out "empty" keybindings + table.insert(value, v) + end + end + core.settings:set(name, table.concat(value, "|")) + self:delete() + return true + elseif fields.btn_clear then + local selection = self.data.selection + table.remove(self.data.value, selection) + self.data.selection = math.max(1, math.min(selection, #self.data.value)) + return true + elseif fields.btn_add then + table.insert(self.data.value, "") + self.data.selection = #self.data.value + return true + elseif fields.btn_bind then + self.data.value[self.data.selection] = fields.btn_bind + return true + elseif fields.keylist then + local event = core.explode_textlist_event(fields.keylist) + if event.type == "CHG" or event.type == "DCL" then + self.data.selection = event.index + return true + end end return false end @@ -67,6 +110,8 @@ function show_change_keybinding_dlg(setting, tabview) end dlg.data.setting = setting + dlg.data.value = core.settings:get(setting.name):split("|") + dlg.data.selection = 1 if is_mainmenu then dlg:set_parent(tabview) From 5a8bae5fffc76490dae562104b0754ae4ddad366 Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Sun, 25 May 2025 00:42:47 +0200 Subject: [PATCH 06/10] show key name in keybinding form --- builtin/common/settings/dlg_change_keybinding.lua | 2 +- src/script/lua_api/l_menu_common.cpp | 9 +++++++++ src/script/lua_api/l_menu_common.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/builtin/common/settings/dlg_change_keybinding.lua b/builtin/common/settings/dlg_change_keybinding.lua index 99c453223..6c317ba5d 100644 --- a/builtin/common/settings/dlg_change_keybinding.lua +++ b/builtin/common/settings/dlg_change_keybinding.lua @@ -24,7 +24,7 @@ local function get_formspec(dialogdata) local cells = {} for _, key in ipairs(value) do - table.insert(cells, core.formspec_escape(key)) + table.insert(cells, core.formspec_escape(core.get_keycode_name(key))) end table.insert(fs, ("textlist[0.5,1.3;5,4;keylist;%s;%d;false]"):format(table.concat(cells, ","), selection)) diff --git a/src/script/lua_api/l_menu_common.cpp b/src/script/lua_api/l_menu_common.cpp index 428b902c9..3a512fcd3 100644 --- a/src/script/lua_api/l_menu_common.cpp +++ b/src/script/lua_api/l_menu_common.cpp @@ -42,6 +42,14 @@ int ModApiMenuCommon::l_normalize_keycode(lua_State *L) return 1; } +int ModApiMenuCommon::l_get_keycode_name(lua_State *L) +{ + auto keystr = luaL_checkstring(L, 1); + auto name = KeyPress(keystr).name(); + lua_pushstring(L, name.empty() ? "" : gettext(name.c_str())); + return 1; +} + void ModApiMenuCommon::Initialize(lua_State *L, int top) { @@ -49,6 +57,7 @@ void ModApiMenuCommon::Initialize(lua_State *L, int top) API_FCT(get_active_driver); API_FCT(irrlicht_device_supports_touch); API_FCT(normalize_keycode); + API_FCT(get_keycode_name); } diff --git a/src/script/lua_api/l_menu_common.h b/src/script/lua_api/l_menu_common.h index 2c1756fa5..b0ea9cb5e 100644 --- a/src/script/lua_api/l_menu_common.h +++ b/src/script/lua_api/l_menu_common.h @@ -14,6 +14,7 @@ private: static int l_get_active_driver(lua_State *L); static int l_irrlicht_device_supports_touch(lua_State *L); static int l_normalize_keycode(lua_State *L); + static int l_get_keycode_name(lua_State *L); public: static void Initialize(lua_State *L, int top); From 8eba9ffa2c6becd8bce5a16e7e02dde5f08125be Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Sun, 25 May 2025 00:57:36 +0200 Subject: [PATCH 07/10] add conflict warnings --- builtin/common/settings/components.lua | 11 ----- .../common/settings/dlg_change_keybinding.lua | 40 +++++++++++++++++-- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/builtin/common/settings/components.lua b/builtin/common/settings/components.lua index 52440d202..8f6eab820 100644 --- a/builtin/common/settings/components.lua +++ b/builtin/common/settings/components.lua @@ -430,17 +430,6 @@ local function make_noise_params(setting) } end -local function has_keybinding_conflict(t1, t2) - for _, v1 in pairs(t1) do - for _, v2 in pairs(t2) do - if core.are_keycodes_equal(v1, v2) then - return true - end - end - end - return false -end - function make.key(setting) local btn_bind = "bind_" .. setting.name local btn_edit = "edit_" .. setting.name diff --git a/builtin/common/settings/dlg_change_keybinding.lua b/builtin/common/settings/dlg_change_keybinding.lua index 6c317ba5d..545265649 100644 --- a/builtin/common/settings/dlg_change_keybinding.lua +++ b/builtin/common/settings/dlg_change_keybinding.lua @@ -22,11 +22,24 @@ local function get_formspec(dialogdata) ("button[0.5,7.7;2.4,0.8;btn_save;%s]"):format(fgettext("Save")), } + local warning = "" local cells = {} - for _, key in ipairs(value) do - table.insert(cells, core.formspec_escape(core.get_keycode_name(key))) + for idx, key in ipairs(value) do + local prefix = "" + for _, o in ipairs(core.full_settingtypes) do + if o.type == "key" and o.name ~= name + and has_keybinding_conflict(core.settings:get(o.name):split("|"), key) then + prefix = mt_color_orange + if idx == selection then + warning = core.colorize(mt_color_orange, fgettext([[Conflicts with "$1"]], fgettext(o.readable_name))) + end + break + end + end + table.insert(cells, core.formspec_escape(prefix .. core.get_keycode_name(key))) end - table.insert(fs, ("textlist[0.5,1.3;5,4;keylist;%s;%d;false]"):format(table.concat(cells, ","), selection)) + table.insert(fs, ("textlist[0.5,1.3;5,3.8;keylist;%s;%d;false]"):format(table.concat(cells, ","), selection)) + table.insert(fs, ("label[0.5,5.4;%s]"):format(warning)) return table.concat(fs) end @@ -121,3 +134,24 @@ function show_change_keybinding_dlg(setting, tabview) return is_mainmenu end + +function has_keybinding_conflict(t1, t2) -- not local as it is also used by the make.key component + if type(t2) == "string" then + if type(t1) == "string" then + return core.are_keycodes_equal(t1, t2) + else + for _, v1 in pairs(t1) do + if core.are_keycodes_equal(v1, t2) then + return true + end + end + end + else + for _, v2 in pairs(t2) do + if has_keybinding_conflict(t1, v2) then + return true + end + end + end + return false +end From 9a9fced642e6909bee58e64eb831dceed4871651 Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Sun, 25 May 2025 01:11:19 +0200 Subject: [PATCH 08/10] allow adding keybindings from setting menu --- builtin/common/settings/components.lua | 27 +++++-------------- .../common/settings/dlg_change_keybinding.lua | 3 ++- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/builtin/common/settings/components.lua b/builtin/common/settings/components.lua index 8f6eab820..fa688a1ee 100644 --- a/builtin/common/settings/components.lua +++ b/builtin/common/settings/components.lua @@ -431,9 +431,7 @@ local function make_noise_params(setting) end function make.key(setting) - local btn_bind = "bind_" .. setting.name local btn_edit = "edit_" .. setting.name - local btn_clear = "unbind_" .. setting.name local function add_conflict_warnings(fs, height) local value = core.settings:get(setting.name):split("|") for _, o in ipairs(core.full_settingtypes) do @@ -457,33 +455,22 @@ function make.key(setting) local value = core.settings:get(setting.name):split("|") local fs = { ("label[0,0.4;%s]"):format(get_label(setting)), - ("image_button[%f,0;0.8,0.8;%s;%s;]"):format(avail_w - 0.8, - core.formspec_escape(defaulttexturedir .. "clear.png"), btn_clear), - ("tooltip[%s;%s]"):format(btn_clear, fgettext("Remove keybinding")), } - if #value < 2 then - table.insert(fs, ("button_key[%f,0;%f,0.8;%s;%s]"):format( - btn_bind_width, btn_bind_width-0.8, - btn_bind, core.formspec_escape(value[1] or ""))) - else - table.insert(fs, ("button[%f,0;%f,0.8;%s;%s]"):format( - btn_bind_width, btn_bind_width-0.8, - btn_edit, fgettext("Edit"))) + local edit_label = fgettext("Edit") + if #value <= 1 then + edit_label = core.formspec_escape(core.get_keycode_name(value[1] or "")) end + table.insert(fs, ("button[%f,0;%f,0.8;%s;%s]"):format( + btn_bind_width, btn_bind_width, + btn_edit, edit_label)) local height = 0.8 height = add_conflict_warnings(fs, height) return table.concat(fs), height end, on_submit = function(self, fields, tabview) - if fields[btn_bind] then - core.settings:set(setting.name, fields[btn_bind]) - return true - elseif fields[btn_edit] then + if fields[btn_edit] then return show_change_keybinding_dlg(setting, tabview) - elseif fields[btn_clear] then - core.settings:set(setting.name, "") - return true end end, } diff --git a/builtin/common/settings/dlg_change_keybinding.lua b/builtin/common/settings/dlg_change_keybinding.lua index 545265649..df857a96f 100644 --- a/builtin/common/settings/dlg_change_keybinding.lua +++ b/builtin/common/settings/dlg_change_keybinding.lua @@ -18,6 +18,7 @@ local function get_formspec(dialogdata) ("button_key[0.5,6.7;4.2,0.8;btn_bind;%s]"):format(core.formspec_escape(value[selection] or "")), ("image_button[4.6,6.7;0.8,0.8;%s;btn_clear;]"):format( core.formspec_escape(defaulttexturedir .. "clear.png")), + ("tooltip[btn_clear;%s]"):format(fgettext("Remove keybinding")), ("button[3.1,7.7;2.4,0.8;btn_close;%s]"):format(fgettext("Cancel")), ("button[0.5,7.7;2.4,0.8;btn_save;%s]"):format(fgettext("Save")), } @@ -31,7 +32,7 @@ local function get_formspec(dialogdata) and has_keybinding_conflict(core.settings:get(o.name):split("|"), key) then prefix = mt_color_orange if idx == selection then - warning = core.colorize(mt_color_orange, fgettext([[Conflicts with "$1"]], fgettext(o.readable_name))) + warning = core.colorize(mt_color_orange, fgettext([[Conflicts with "$1"]], fgettext_ne(o.readable_name))) end break end From b7b67e978fed0182c534456147c94a3960ef357e Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Tue, 27 May 2025 09:14:01 +0200 Subject: [PATCH 09/10] minor dimensions --- builtin/common/settings/dlg_change_keybinding.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/common/settings/dlg_change_keybinding.lua b/builtin/common/settings/dlg_change_keybinding.lua index df857a96f..d59b0760e 100644 --- a/builtin/common/settings/dlg_change_keybinding.lua +++ b/builtin/common/settings/dlg_change_keybinding.lua @@ -16,7 +16,7 @@ local function get_formspec(dialogdata) ("label[0.5,0.8;%s]"):format(readable_name), ("button[0.5,5.7;5,0.8;btn_add;%s]"):format(fgettext("Add keybinding")), ("button_key[0.5,6.7;4.2,0.8;btn_bind;%s]"):format(core.formspec_escape(value[selection] or "")), - ("image_button[4.6,6.7;0.8,0.8;%s;btn_clear;]"):format( + ("image_button[4.7,6.7;0.8,0.8;%s;btn_clear;]"):format( core.formspec_escape(defaulttexturedir .. "clear.png")), ("tooltip[btn_clear;%s]"):format(fgettext("Remove keybinding")), ("button[3.1,7.7;2.4,0.8;btn_close;%s]"):format(fgettext("Cancel")), From 88d887cf8db3a7b4279bcdedd252f8ce34c5ae3b Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Tue, 27 May 2025 10:51:22 +0200 Subject: [PATCH 10/10] allow directly rebindings keys for actions with one binding --- builtin/common/settings/components.lua | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/builtin/common/settings/components.lua b/builtin/common/settings/components.lua index fa688a1ee..7e47b790c 100644 --- a/builtin/common/settings/components.lua +++ b/builtin/common/settings/components.lua @@ -431,6 +431,7 @@ local function make_noise_params(setting) end function make.key(setting) + local btn_bind = "bind_" .. setting.name local btn_edit = "edit_" .. setting.name local function add_conflict_warnings(fs, height) local value = core.settings:get(setting.name):split("|") @@ -451,25 +452,31 @@ function make.key(setting) get_formspec = function(self, avail_w) self.resettable = core.settings:has(setting.name) - local btn_bind_width = math.max(2.5, avail_w/2) + local btn_width = math.max(2.5, avail_w/2) local value = core.settings:get(setting.name):split("|") local fs = { ("label[0,0.4;%s]"):format(get_label(setting)), + ("tooltip[%s;%s]"):format(btn_edit, fgettext("Edit keybindings")), } - local edit_label = fgettext("Edit") if #value <= 1 then - edit_label = core.formspec_escape(core.get_keycode_name(value[1] or "")) + table.insert(fs, ("button_key[%f,0;%f,0.8;%s;%s]"):format( + btn_width, btn_width-0.8, btn_bind, value[1] or "")) + table.insert(fs, ("image_button[%f,0;0.8,0.8;%s;%s;]"):format(avail_w - 0.8, + core.formspec_escape(defaulttexturedir.."overflow_btn.png"), btn_edit)) + else + table.insert(fs, ("button[%f,0;%f,0.8;%s;%s]"):format( + btn_width, btn_width, btn_edit, fgettext("Edit"))) end - table.insert(fs, ("button[%f,0;%f,0.8;%s;%s]"):format( - btn_bind_width, btn_bind_width, - btn_edit, edit_label)) local height = 0.8 height = add_conflict_warnings(fs, height) return table.concat(fs), height end, on_submit = function(self, fields, tabview) - if fields[btn_edit] then + if fields[btn_bind] then + core.settings:set(setting.name, fields[btn_bind]) + return true + elseif fields[btn_edit] then return show_change_keybinding_dlg(setting, tabview) end end,