From fd857374603e63ab1092034dc8b82f7a3927caff Mon Sep 17 00:00:00 2001 From: Vincent Robinson Date: Wed, 16 Apr 2025 16:20:39 -0700 Subject: [PATCH] Add `allow_close[]` element to formspecs (#15971) --- README.md | 1 + doc/lua_api.md | 11 +++- games/devtest/mods/testformspec/formspec.lua | 9 ++- src/client/game.cpp | 8 +-- src/client/game_formspec.cpp | 3 + src/client/inputhandler.cpp | 4 ++ src/gui/guiEngine.cpp | 2 +- src/gui/guiFormSpecMenu.cpp | 67 ++++++++++---------- src/gui/guiFormSpecMenu.h | 10 +-- 9 files changed, 71 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 33340053a..2d5cdf890 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ Some can be changed in the key config dialog in the settings tab. | T | Chat | | / | Command | | Esc | Pause menu/abort/exit (pauses only singleplayer game) | +| Ctrl + Esc | Exit directly to main menu from anywhere, bypassing pause menu | | + | Increase view range | | - | Decrease view range | | K | Enable/disable fly mode (needs fly privilege) | diff --git a/doc/lua_api.md b/doc/lua_api.md index 882bfe341..d8e58fcde 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -2903,6 +2903,13 @@ Elements * For information on converting forms to the new coordinate system, see `Migrating to Real Coordinates`. +### `allow_close[]` + +* When set to false, the formspec will not close when the user tries to close + it with the Escape key or similar. Default true. +* The formspec can still be closed with `*_exit[]` elements and + `core.close_formspec()`, regardless of this setting. + ### `container[,]` * Start of a container block, moves all physical elements in the container by @@ -6206,8 +6213,10 @@ Call these functions only at load time! * `table`: See `core.explode_table_event` * `scrollbar`: See `core.explode_scrollbar_event` * Special case: `["quit"]="true"` is sent when the user actively - closed the form by mouse click, keypress or through a button_exit[] + closed the form by mouse click, keypress or through a `button_exit[]` element. + * Special case: `["try_quit"]="true"` is sent when the user tries to + close the formspec, but the formspec used `allow_close[false]`. * Special case: `["key_enter"]="true"` is sent when the user pressed the Enter key and the focus was either nowhere (causing the formspec to be closed) or on a button. If the focus was on a text field, diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 29014f1f2..f2f632fa0 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -339,10 +339,11 @@ local pages = { [[ formspec_version[3] size[12,13] + allow_close[false] image_button[0,0;1,1;logo.png;rc_image_button_1x1;1x1] - image_button[1,0;2,2;logo.png;rc_image_button_2x2;2x2] + image_button_exit[1,0;2,2;logo.png;rc_image_button_2x2;2x2 exit] button[0,2;1,1;rc_button_1x1;1x1] - button[1,2;2,2;rc_button_2x2;2x2] + button_exit[1,2;2,2;rc_button_2x2;2x2 exit] item_image[0,4;1,1;air] item_image[1,4;2,2;air] item_image_button[0,6;1,1;testformspec:node;rc_item_image_button_1x1;1x1] @@ -575,6 +576,10 @@ core.register_on_player_receive_fields(function(player, formname, fields) if fields.submit_window then show_test_formspec(player:get_player_name()) end + + if fields.try_quit then + core.chat_send_player(player:get_player_name(), "Quit attempt received") + end end) core.register_chatcommand("test_formspec", { diff --git a/src/client/game.cpp b/src/client/game.cpp index ee6cd0af4..78d009c7e 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1050,10 +1050,6 @@ void Game::shutdown() if (g_touchcontrols) g_touchcontrols->hide(); - // only if the shutdown progress bar isn't shown yet - if (m_shutdown_progress == 0.0f) - showOverlayMessage(N_("Shutting down..."), 0, 0); - clouds.reset(); gui_chat_console.reset(); @@ -1065,6 +1061,10 @@ void Game::shutdown() g_menumgr.deleteFront(); } + // only if the shutdown progress bar isn't shown yet + if (m_shutdown_progress == 0.0f) + showOverlayMessage(N_("Shutting down..."), 0, 0); + chat_backend->addMessage(L"", L"# Disconnected."); chat_backend->addMessage(L"", L""); diff --git a/src/client/game_formspec.cpp b/src/client/game_formspec.cpp index 4bd1a42bd..dc4be3699 100644 --- a/src/client/game_formspec.cpp +++ b/src/client/game_formspec.cpp @@ -205,6 +205,9 @@ void GameFormSpec::init(Client *client, RenderingEngine *rendering_engine, Input m_input = input; m_pause_script = std::make_unique(client); m_pause_script->loadBuiltin(); + + // Make sure any remaining game callback requests are cleared out. + *g_gamecallback = MainGameCallback(); } void GameFormSpec::deleteFormspec() diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index 492f2fd6c..e7bdd4f30 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -148,6 +148,10 @@ bool MyEventReceiver::OnEvent(const SEvent &event) } fullscreen_is_down = event.KeyInput.PressedDown; return true; + } else if (keyCode == EscapeKey && + event.KeyInput.PressedDown && event.KeyInput.Control) { + g_gamecallback->disconnect(); + return true; } } diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index a83a913ec..860fce665 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -168,7 +168,7 @@ GUIEngine::GUIEngine(JoystickController *joystick, "", false); - m_menu->allowClose(false); + m_menu->defaultAllowClose(false); m_menu->lockSize(true,v2u32(800,600)); // Initialize scripting diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 59d6dc5a7..39d8797b4 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -105,7 +105,6 @@ GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick, current_keys_pending.key_down = false; current_keys_pending.key_up = false; current_keys_pending.key_enter = false; - current_keys_pending.key_escape = false; m_tooltip_show_delay = (u32)g_settings->getS32("tooltip_show_delay"); m_tooltip_append_itemname = g_settings->getBool("tooltip_append_itemname"); @@ -2833,6 +2832,11 @@ void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) m_fields.push_back(spec); } +void GUIFormSpecMenu::parseAllowClose(parserData *data, const std::string &element) +{ + m_allowclose = is_yes(element); +} + void GUIFormSpecMenu::removeAll() { // Remove children @@ -2901,6 +2905,7 @@ const std::unordered_mapgotText(fields); return; + } else if (quitmode == quit_mode_try) { + fields["try_quit"] = "true"; } if (current_keys_pending.key_down) { @@ -3816,11 +3822,6 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode) current_field_enter_pending.clear(); } - if (current_keys_pending.key_escape) { - fields["key_escape"] = "true"; - current_keys_pending.key_escape = false; - } - for (const GUIFormSpecMenu::FieldSpec &s : m_fields) { if (s.send) { std::string name = s.fname; @@ -3997,6 +3998,8 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) if (m_allowclose) { acceptInput(quit_mode_accept); quitMenu(); + } else { + acceptInput(quit_mode_try); } } } @@ -4013,6 +4016,7 @@ void GUIFormSpecMenu::tryClose() acceptInput(quit_mode_cancel); quitMenu(); } else { + acceptInput(quit_mode_try); m_text_dst->gotText(L"MenuQuit"); } } @@ -4059,9 +4063,13 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) FATAL_ERROR("Reached a source line that can't ever been reached"); break; } - if (current_keys_pending.key_enter && m_allowclose) { - acceptInput(quit_mode_accept); - quitMenu(); + if (current_keys_pending.key_enter) { + if (m_allowclose) { + acceptInput(quit_mode_accept); + quitMenu(); + } else { + acceptInput(quit_mode_try); + } } else { acceptInput(); } @@ -4806,14 +4814,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) s32 caller_id = event.GUIEvent.Caller->getID(); if (caller_id == 257) { - if (m_allowclose) { - acceptInput(quit_mode_accept); - quitMenu(); - } else { - acceptInput(); - m_text_dst->gotText(L"ExitButton"); - } - // quitMenu deallocates menu + acceptInput(quit_mode_accept); + m_text_dst->gotText(L"ExitButton"); + quitMenu(); return true; } @@ -4842,12 +4845,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } if (s.is_exit) { - if (m_allowclose) { - acceptInput(quit_mode_accept); - quitMenu(); - } else { - m_text_dst->gotText(L"ExitButton"); - } + acceptInput(quit_mode_accept); + m_text_dst->gotText(L"ExitButton"); + quitMenu(); return true; } @@ -4910,15 +4910,18 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } } - if (m_allowclose && close_on_enter) { - current_keys_pending.key_enter = true; - acceptInput(quit_mode_accept); - quitMenu(); + current_keys_pending.key_enter = true; + + if (close_on_enter) { + if (m_allowclose) { + acceptInput(quit_mode_accept); + quitMenu(); + } else { + acceptInput(quit_mode_try); + } } else { - current_keys_pending.key_enter = true; acceptInput(); } - // quitMenu deallocates menu return true; } } diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 04b65a967..3cfd79cae 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -47,7 +47,8 @@ enum FormspecFieldType { enum FormspecQuitMode { quit_mode_no, quit_mode_accept, - quit_mode_cancel + quit_mode_cancel, + quit_mode_try, }; enum ButtonEventType : u8 @@ -203,9 +204,9 @@ public: m_text_dst = text_dst; } - void allowClose(bool value) + void defaultAllowClose(bool value) { - m_allowclose = value; + m_default_allowclose = value; } void setDebugView(bool value) @@ -363,6 +364,7 @@ protected: u64 m_hovered_time = 0; s32 m_old_tooltip_id = -1; + bool m_default_allowclose = true; bool m_allowclose = true; bool m_lock = false; v2u32 m_lockscreensize; @@ -422,7 +424,6 @@ private: bool key_up; bool key_down; bool key_enter; - bool key_escape; }; fs_key_pending current_keys_pending; @@ -484,6 +485,7 @@ private: void parseStyle(parserData *data, const std::string &element); void parseSetFocus(parserData *, const std::string &element); void parseModel(parserData *data, const std::string &element); + void parseAllowClose(parserData *data, const std::string &element); bool parseMiddleRect(const std::string &value, core::rect *parsed_rect);