diff --git a/doc/en/scripting/ui.md b/doc/en/scripting/ui.md index 49c96edf..c6da89c4 100644 --- a/doc/en/scripting/ui.md +++ b/doc/en/scripting/ui.md @@ -79,7 +79,7 @@ Properties: | hint | string | yes | yes | text to display when nothing is entered | | caret | int | yes | yes | carriage position. `textbox.caret = -1` will set the position to the end of the text | | editable | bool | yes | yes | text mutability | -| edited | bool | yes | no | is text edited since the last set (history is not empty) | +| edited | bool | yes | yes\* | is text edited since the last set / edited status reset | | multiline | bool | yes | yes | multiline support | | lineNumbers | bool | yes | yes | display line numbers | | textWrap | bool | yes | yes | automatic text wrapping (only with multiline: "true") | @@ -88,6 +88,8 @@ Properties: | syntax | string | yes | yes | syntax highlighting ("lua" - Lua) | | markup | string | yes | yes | text markup language ("md" - Markdown) | +\* - false only + Methods: | Method | Description | diff --git a/doc/en/xml-ui-layouts.md b/doc/en/xml-ui-layouts.md index 6f687328..64579db1 100644 --- a/doc/en/xml-ui-layouts.md +++ b/doc/en/xml-ui-layouts.md @@ -110,6 +110,8 @@ Inner text - initially entered text - `supplier` - text supplier (called every frame) - `consumer` - lua function that receives the entered text. Called only when input is complete - `sub-consumer` - lua function-receiver of the input text. Called during text input or deletion. +- `oncontrolkey` - lua function called for combinations of the form (Ctrl + ?). The codepoint of the second key is given as the first argument. +The key code for comparison can be obtained via `input.keycode("key_name")` - `autoresize` - automatic change of element size (default - false). Does not affect font size. - `multiline` - allows display of multiline text. - `text-wrap` - allows automatic text wrapping (works only with multiline: "true") diff --git a/doc/ru/scripting/ui.md b/doc/ru/scripting/ui.md index 02789378..62697c8f 100644 --- a/doc/ru/scripting/ui.md +++ b/doc/ru/scripting/ui.md @@ -79,7 +79,7 @@ document["worlds-panel"]:clear() | hint | string | да | да | текст, отображаемый, когда ничего не введено | | caret | int | да | да | позиция каретки. `textbox.caret = -1` установит позицию в конец текста | | editable | bool | да | да | изменяемость текста | -| edited | bool | да | нет | был ли изменён текст с последней установки (история не пуста) | +| edited | bool | да | да\* | был ли изменён текст с последней установки/сброса свойства | | multiline | bool | да | да | поддержка многострочности | | lineNumbers | bool | да | да | отображение номеров строк | | textWrap | bool | да | да | автоматический перенос текста (только при multiline: "true") | @@ -88,6 +88,8 @@ document["worlds-panel"]:clear() | syntax | string | да | да | подсветка синтаксиса ("lua" - Lua) | | markup | string | да | да | язык разметки текста ("md" - Markdown) | +\* - только false + Методы: | Метод | Описание | diff --git a/doc/ru/xml-ui-layouts.md b/doc/ru/xml-ui-layouts.md index 30c17315..e0524793 100644 --- a/doc/ru/xml-ui-layouts.md +++ b/doc/ru/xml-ui-layouts.md @@ -111,6 +111,8 @@ - `supplier` - поставщик текста (вызывается каждый кадр) - `consumer` - lua функция-приемник введенного текста. Вызывается только при завершении ввода - `sub-consumer` - lua функция-приемник вводимого текста. Вызывается во время ввода или удаления текста. +- `oncontrolkey` - lua функция вызываемая для сочетаний вида (Ctrl + ?). На вход подаётся числовой код второй клавиши. +Код клавиши для сравнения можно получить через `input.keycode("имя_клавиши")` - `autoresize` - автоматическое изменение размера элемента (по-умолчанию - false). Не влияет на размер шрифта. - `multiline` - разрешает отображение многострочного текста. - `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true") diff --git a/res/layouts/console.xml b/res/layouts/console.xml index 133ccb60..00ce2a87 100644 --- a/res/layouts/console.xml +++ b/res/layouts/console.xml @@ -57,6 +57,7 @@ padding='5' multiline='true' line-numbers='true' + oncontrolkey='on_control_combination' syntax='lua' size-func="-1,40" text-wrap='false' diff --git a/res/layouts/console.xml.lua b/res/layouts/console.xml.lua index 18e72690..62e9b6dd 100644 --- a/res/layouts/console.xml.lua +++ b/res/layouts/console.xml.lua @@ -81,6 +81,12 @@ local function refresh_file_title() ..(edited and ' *' or '') end +function on_control_combination(keycode) + if keycode == input.keycode("s") then + save_current_file() + end +end + function unlock_access() if current_file.filename == "" then return @@ -102,6 +108,7 @@ function save_current_file() current_file.modified = false document.saveIcon.enabled = false document.title.text = gui.str('File')..' - '..current_file.filename + document.editor.edited = false end function open_file_in_editor(filename, line, mutable) diff --git a/src/delegates.hpp b/src/delegates.hpp index 0e670b41..f583f951 100644 --- a/src/delegates.hpp +++ b/src/delegates.hpp @@ -15,6 +15,7 @@ using wstringsupplier = std::function; using doublesupplier = std::function; using boolsupplier = std::function; using vec2supplier = std::function; +using key_handler = std::function; using stringconsumer = std::function; using wstringconsumer = std::function; diff --git a/src/devtools/actions.hpp b/src/devtools/actions.hpp index abe76bfc..fffe9d67 100644 --- a/src/devtools/actions.hpp +++ b/src/devtools/actions.hpp @@ -145,6 +145,10 @@ public: : history(history), historySize(history.size()) { } + Combination(const Combination&) = delete; + + Combination(Combination&&) = default; + ~Combination() { history.squash(history.size() - historySize); } diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 0f85fa32..96d9ea01 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -564,7 +564,12 @@ bool TextBox::isEditable() const { } bool TextBox::isEdited() const { - return history->size() != 0 || !historian->isSynced(); + return history->size() != editedHistorySize || !historian->isSynced(); +} + +void TextBox::setUnedited() { + historian->sync(); + editedHistorySize = history->size(); } size_t TextBox::getSelectionStart() const { @@ -575,7 +580,6 @@ size_t TextBox::getSelectionEnd() const { return selectionEnd; } - void TextBox::setOnEditStart(runnable oneditstart) { onEditStart = oneditstart; } @@ -849,7 +853,12 @@ void TextBox::keyPressed(keycode key) { if (editable) { performEditingKeyboardEvents(key); } - if (Events::pressed(keycode::LEFT_CONTROL)) { + if (Events::pressed(keycode::LEFT_CONTROL) && key != keycode::LEFT_CONTROL) { + if (controlCombinationsHandler) { + if (controlCombinationsHandler(static_cast(key))) { + return; + } + } // Copy selected text to clipboard if (key == keycode::C || key == keycode::X) { std::string text = util::wstr2str_utf8(getSelection()); @@ -963,6 +972,10 @@ void TextBox::setTextValidator(wstringchecker validator) { this->validator = std::move(validator); } +void TextBox::setOnControlCombination(key_handler handler) { + this->controlCombinationsHandler = std::move(handler); +} + void TextBox::setFocusedColor(glm::vec4 color) { this->focusedColor = color; } @@ -999,6 +1012,7 @@ void TextBox::setText(const std::wstring& value) { input.erase(std::remove(input.begin(), input.end(), '\r'), input.end()); historian->reset(); history->clear(); + editedHistorySize = 0; refreshSyntax(); } diff --git a/src/graphics/ui/elements/TextBox.hpp b/src/graphics/ui/elements/TextBox.hpp index 9ebea29d..7fddfacb 100644 --- a/src/graphics/ui/elements/TextBox.hpp +++ b/src/graphics/ui/elements/TextBox.hpp @@ -12,6 +12,7 @@ namespace gui { LabelCache rawTextCache; std::shared_ptr history; std::unique_ptr historian; + int editedHistorySize = 0; protected: glm::vec4 focusedColor {0.0f, 0.0f, 0.0f, 1.0f}; glm::vec4 invalidColor {0.1f, 0.05f, 0.03f, 1.0f}; @@ -33,6 +34,7 @@ namespace gui { wstringconsumer subconsumer = nullptr; /// @brief Text validator returning boolean value wstringchecker validator = nullptr; + key_handler controlCombinationsHandler = nullptr; /// @brief Function called on focus runnable onEditStart = nullptr; /// @brief Function called on up arrow pressed @@ -117,6 +119,8 @@ namespace gui { /// @param validator std::wstring consumer returning boolean virtual void setTextValidator(wstringchecker validator); + virtual void setOnControlCombination(key_handler handler); + virtual void setFocusedColor(glm::vec4 color); virtual glm::vec4 getFocusedColor() const; @@ -205,6 +209,7 @@ namespace gui { virtual bool isEditable() const; virtual bool isEdited() const; + virtual void setUnedited(); virtual void setPadding(glm::vec4 padding); glm::vec4 getPadding() const; diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index abe59c2c..9cf14f54 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -493,6 +493,13 @@ static std::shared_ptr read_text_box( reader.getFilename() )); } + if (element.has("oncontrolkey")) { + textbox->setOnControlCombination(scripting::create_key_handler( + reader.getEnvironment(), + element.attr("oncontrolkey").getText(), + reader.getFilename() + )); + } if (auto onUpPressed = create_runnable(reader, element, "onup")) { textbox->setOnUpPressed(onUpPressed); } diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index 6b13f72c..52f3c283 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -553,6 +553,13 @@ static void p_set_editable(UINode* node, lua::State* L, int idx) { box->setEditable(lua::toboolean(L, idx)); } } +static void p_set_edited(UINode* node, lua::State* L, int idx) { + if (auto box = dynamic_cast(node)) { + if (!lua::toboolean(L, idx)) { + box->setUnedited(); + } + } +} static void p_set_line_numbers(UINode* node, lua::State* L, int idx) { if (auto box = dynamic_cast(node)) { box->setShowLineNumbers(lua::toboolean(L, idx)); @@ -675,6 +682,7 @@ static int l_gui_setattr(lua::State* L) { {"hint", p_set_hint}, {"text", p_set_text}, {"editable", p_set_editable}, + {"edited", p_set_edited}, {"lineNumbers", p_set_line_numbers}, {"syntax", p_set_syntax}, {"markup", p_set_markup}, diff --git a/src/logic/scripting/scripting_functional.cpp b/src/logic/scripting/scripting_functional.cpp index d09c9432..96023d6f 100644 --- a/src/logic/scripting/scripting_functional.cpp +++ b/src/logic/scripting/scripting_functional.cpp @@ -36,6 +36,28 @@ static lua::State* process_callback( return nullptr; } +key_handler scripting::create_key_handler( + const scriptenv& env, const std::string& src, const std::string& file +) { + return [=](int code) { + if (auto L = process_callback(env, src, file)) { + int top = lua::gettop(L); + if (lua::isfunction(L, -1)) { + lua::pushinteger(L, code); + lua::call_nothrow(L, 1); + } + int returned = lua::gettop(L) - top + 1; + if (returned) { + bool x = lua::toboolean(L, -1); + lua::pop(L, returned); + return x; + } + return false; + } + return false; + }; +} + wstringconsumer scripting::create_wstring_consumer( const scriptenv& env, const std::string& src, const std::string& file ) { diff --git a/src/logic/scripting/scripting_functional.hpp b/src/logic/scripting/scripting_functional.hpp index f2d4c68b..be325740 100644 --- a/src/logic/scripting/scripting_functional.hpp +++ b/src/logic/scripting/scripting_functional.hpp @@ -17,6 +17,12 @@ namespace scripting { const std::string& file = "[string]" ); + key_handler create_key_handler( + const scriptenv& env, + const std::string& src, + const std::string& file = "[string]" + ); + wstringconsumer create_wstring_consumer( const scriptenv& env, const std::string& src,