diff --git a/include/Helpers/SCollision.hpp b/include/Helpers/SCollision.hpp index 7ff6994..8e1e249 100644 --- a/include/Helpers/SCollision.hpp +++ b/include/Helpers/SCollision.hpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace Eigen; @@ -76,6 +77,9 @@ namespace SCollision { template void serialize(Archive& a) { a(min, max); } + SDL_Rect get_sdl_rect() const { + return SDL_Rect{static_cast(min.x()), static_cast(min.y()), static_cast(width()), static_cast(height())}; + } }; template class Circle { public: diff --git a/src/GUIStuff/Elements/Element.hpp b/src/GUIStuff/Elements/Element.hpp index 0de37e3..145d1ad 100644 --- a/src/GUIStuff/Elements/Element.hpp +++ b/src/GUIStuff/Elements/Element.hpp @@ -13,6 +13,7 @@ #include #include #include "../../RichText/TextBox.hpp" +#include "../../InputManager.hpp" using namespace Eigen; @@ -97,6 +98,8 @@ struct UpdateInputData { std::shared_ptr previousRichTextBoxToEdit; std::shared_ptr richTextBoxToEdit; std::shared_ptr richTextBoxToEditCursor; + InputManager::TextInputProperties richTextInputProperties; + std::shared_ptr> richTextBoxToEditRectangle; SkFont get_font(float fSize) const; @@ -110,6 +113,9 @@ struct UpdateInputData { float deltaTime = 0.0f; bool isTouch = true; + float guiScaleMultiplier = 1.0f; + Vector2f windowPos = {0.0f, 0.0f}; + std::shared_ptr fonts; sk_sp textTypeface; diff --git a/src/GUIStuff/Elements/TextBox.hpp b/src/GUIStuff/Elements/TextBox.hpp index a9d8635..c9a950a 100644 --- a/src/GUIStuff/Elements/TextBox.hpp +++ b/src/GUIStuff/Elements/TextBox.hpp @@ -4,12 +4,13 @@ #include #include #include "../../FontData.hpp" +#include "../../InputManager.hpp" namespace GUIStuff { template class TextBox : public Element { public: - bool update(UpdateInputData& io, T* newData, const std::function(const std::string&)>& newFromStr, const std::function newToStr, bool newSingleLine, bool updateEveryEdit, const std::function& elemUpdate) { + bool update(UpdateInputData& io, T* newData, const std::function(const std::string&)>& newFromStr, const std::function newToStr, bool newSingleLine, bool updateEveryEdit, const InputManager::TextInputProperties& textInputProps, const std::function& elemUpdate) { bool isUpdating = false; if(!textbox || !cur) @@ -33,6 +34,8 @@ template class TextBox : public Element { if(data && selection.selected) { io.richTextBoxToEdit = textbox; io.richTextBoxToEditCursor = cur; + io.richTextBoxToEditRectangle = rect; + io.richTextInputProperties = textInputProps; textbox->process_mouse_left_button(*cur, io.mouse.pos - bb.min, io.mouse.leftClick, io.mouse.leftHeld, io.key.leftShift); if((updateEveryEdit && textbox->inputChangedTextBox) || io.key.enter) { update_on_edit(); @@ -53,6 +56,8 @@ template class TextBox : public Element { if(textbox) textbox->inputChangedTextBox = false; + *rect = {(bb.min + io.windowPos) * io.guiScaleMultiplier, (bb.max + io.windowPos) * io.guiScaleMultiplier}; + return isUpdating; } @@ -108,6 +113,7 @@ template class TextBox : public Element { textbox->set_allow_newlines(false); cur = std::make_shared(); + rect = std::make_shared>(); if(data) { cur->pos = cur->selectionBeginPos = cur->selectionEndPos = textbox->insert({0, 0}, toStr(*data)); @@ -131,8 +137,9 @@ template class TextBox : public Element { std::shared_ptr textbox; std::shared_ptr cur; + std::shared_ptr> rect; - SCollision::AABB bb; + SCollision::AABB bb = {{1.0f, 1.0f}, {2.0f, 2.0f}}; }; } diff --git a/src/GUIStuff/GUIManager.cpp b/src/GUIStuff/GUIManager.cpp index b1afdbd..d1a279f 100644 --- a/src/GUIStuff/GUIManager.cpp +++ b/src/GUIStuff/GUIManager.cpp @@ -393,7 +393,15 @@ bool GUIManager::input_color_component_255(const char* id, float* val, const std std::stringstream ss; ss << convertTo255; return ss.str(); - }, true, false, elemUpdate); + }, true, false, + { + .inputType = SDL_TextInputType::SDL_TEXTINPUT_TYPE_NUMBER, + .capitalization = SDL_Capitalization::SDL_CAPITALIZE_NONE, + .autocorrect = false, + .multiline = false, + .androidInputType = InputManager::AndroidInputType::ANDROIDTEXT_TYPE_CLASS_NUMBER + }, + elemUpdate); pop_id(); return isUpdating; } @@ -406,7 +414,15 @@ bool GUIManager::input_text(const char* id, std::string* val, bool updateEveryEd }, [&](const std::string& str) { return str; - }, true, updateEveryEdit, elemUpdate); + }, true, updateEveryEdit, + { + .inputType = SDL_TextInputType::SDL_TEXTINPUT_TYPE_TEXT, + .capitalization = SDL_Capitalization::SDL_CAPITALIZE_NONE, + .autocorrect = false, + .multiline = false, + .androidInputType = InputManager::AndroidInputType::ANDROIDTEXT_TYPE_CLASS_TEXT + }, + elemUpdate); pop_id(); return toRet; } @@ -533,7 +549,15 @@ bool GUIManager::input_scalar(const char* id, uint8_t* val, uint8_t min, uint8_t }, [&](const uint8_t& a) { return std::to_string(static_cast(a)); - }, true, false, elemUpdate); + }, true, false, + { + .inputType = SDL_TextInputType::SDL_TEXTINPUT_TYPE_NUMBER, + .capitalization = SDL_Capitalization::SDL_CAPITALIZE_NONE, + .autocorrect = false, + .multiline = false, + .androidInputType = InputManager::AndroidInputType::ANDROIDTEXT_TYPE_CLASS_NUMBER + }, + elemUpdate); pop_id(); return isUpdating; } @@ -549,7 +573,15 @@ void GUIManager::input_path(const char* id, std::filesystem::path* val, std::fil }, [&](const std::filesystem::path& a) { return a.string(); - }, true, false, elemUpdate); + }, true, false, + { + .inputType = SDL_TextInputType::SDL_TEXTINPUT_TYPE_TEXT, + .capitalization = SDL_Capitalization::SDL_CAPITALIZE_NONE, + .autocorrect = false, + .multiline = false, + .androidInputType = InputManager::AndroidInputType::ANDROIDTEXT_TYPE_CLASS_TEXT + }, + elemUpdate); pop_id(); } diff --git a/src/GUIStuff/GUIManager.hpp b/src/GUIStuff/GUIManager.hpp index 8fd82f2..02787c2 100644 --- a/src/GUIStuff/GUIManager.hpp +++ b/src/GUIStuff/GUIManager.hpp @@ -147,15 +147,26 @@ class GUIManager { return toRet; } - template bool input_generic(const char* id, T* val, const std::function(const std::string&)>& fromStr, const std::function& toStr, bool singleLine, bool updateEveryEdit, const std::function& elemUpdate = nullptr) { + template bool input_generic(const char* id, T* val, const std::function(const std::string&)>& fromStr, const std::function& toStr, bool singleLine, bool updateEveryEdit, const InputManager::TextInputProperties& inputProps, const std::function& elemUpdate = nullptr) { bool isUpdating = false; push_id(id); - isUpdating = insert_element>()->update(*io, val, fromStr, toStr, singleLine, updateEveryEdit, elemUpdate); + isUpdating = insert_element>()->update(*io, val, fromStr, toStr, singleLine, updateEveryEdit, inputProps, elemUpdate); pop_id(); return isUpdating; } template bool input_scalar(const char* id, T* val, T min, T max, int decimalPrecision = 0, const std::function& elemUpdate = nullptr) { + InputManager::TextInputProperties textInputProps { + .inputType = SDL_TextInputType::SDL_TEXTINPUT_TYPE_NUMBER, + .capitalization = SDL_Capitalization::SDL_CAPITALIZE_NONE, + .autocorrect = false, + .multiline = false, + .androidInputType = InputManager::AndroidInputType::ANDROIDTEXT_TYPE_CLASS_NUMBER + }; + if(std::is_signed()) + textInputProps.androidInputType |= InputManager::AndroidInputType::ANDROIDTEXT_TYPE_NUMBER_FLAG_SIGNED; + if(std::is_floating_point()) + textInputProps.androidInputType |= InputManager::AndroidInputType::ANDROIDTEXT_TYPE_NUMBER_FLAG_DECIMAL; return input_generic(id, val, [&](const std::string& a) { if(a.empty()) @@ -177,7 +188,7 @@ class GUIManager { else ss << *val; return ss.str(); - }, true, false, elemUpdate); + }, true, false, textInputProps, elemUpdate); } bool input_scalar(const char* id, uint8_t* val, uint8_t min, uint8_t max, int decimalPrecision, const std::function& elemUpdate); @@ -231,7 +242,15 @@ class GUIManager { } return ss.str(); }, - true, false, elemUpdate); + true, false, + { + .inputType = SDL_TextInputType::SDL_TEXTINPUT_TYPE_TEXT, + .capitalization = SDL_Capitalization::SDL_CAPITALIZE_NONE, + .autocorrect = false, + .multiline = false, + .androidInputType = InputManager::AndroidInputType::ANDROIDTEXT_TYPE_CLASS_TEXT + }, + elemUpdate); } template bool color_picker(const char* id, T* val, bool selectAlpha, const std::function& elemUpdate = nullptr) { diff --git a/src/InputManager.cpp b/src/InputManager.cpp index 4eba2f1..e20b259 100644 --- a/src/InputManager.cpp +++ b/src/InputManager.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -87,32 +88,35 @@ InputManager::InputManager(MainProgram& initMain): keyAssignments = defaultKeyAssignments; } -void InputManager::set_rich_text_box_input_front(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor, bool isRichTextBox, const std::optional& nModMap) { - text.set_accepting_input(main.window.sdlWindow, true); +void InputManager::set_rich_text_box_input_front(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor, bool isRichTextBox, const std::shared_ptr>& textboxRect, const TextInputProperties& inputProps, const std::optional& nModMap) { Text::TextBoxInfo textBoxInfo; textBoxInfo.textBox = nTextBox; textBoxInfo.cursor = nCursor; textBoxInfo.modMap = nModMap; textBoxInfo.isRichTextBox = isRichTextBox; + textBoxInfo.rect = textboxRect; + textBoxInfo.textInputProperties = inputProps; text.textBoxes.emplace_front(textBoxInfo); + text.update_accepting_input(main.window.sdlWindow); } -void InputManager::set_rich_text_box_input_back(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor, bool isRichTextBox, const std::optional& nModMap) { - text.set_accepting_input(main.window.sdlWindow, true); +void InputManager::set_rich_text_box_input_back(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor, bool isRichTextBox, const std::shared_ptr>& textboxRect, const TextInputProperties& inputProps, const std::optional& nModMap) { Text::TextBoxInfo textBoxInfo; textBoxInfo.textBox = nTextBox; textBoxInfo.cursor = nCursor; textBoxInfo.modMap = nModMap; textBoxInfo.isRichTextBox = isRichTextBox; + textBoxInfo.rect = textboxRect; + textBoxInfo.textInputProperties = inputProps; text.textBoxes.emplace_back(textBoxInfo); + text.update_accepting_input(main.window.sdlWindow); } void InputManager::remove_rich_text_box_input(const std::shared_ptr& nTextBox) { std::erase_if(text.textBoxes, [&nTextBox](Text::TextBoxInfo& info) { return (info.textBox == nTextBox); }); - if(text.textBoxes.empty()) - text.set_accepting_input(main.window.sdlWindow, false); + text.update_accepting_input(main.window.sdlWindow); } void InputManager::Text::add_text_to_textbox(const std::string& inputText) { @@ -153,18 +157,24 @@ void InputManager::Text::do_textbox_operation_with_undo(const std::functionget_sdl_rect(); + SDL_SetTextInputArea(main.window.sdlWindow, &r, textbox.cursor->pos.fTextByteIndex); + } + else + SDL_SetTextInputArea(main.window.sdlWindow, nullptr, textbox.cursor->pos.fTextByteIndex); + } } void InputManager::frame_reset(const Vector2i& windowSize) { diff --git a/src/InputManager.hpp b/src/InputManager.hpp index 598a03a..ffd3e99 100644 --- a/src/InputManager.hpp +++ b/src/InputManager.hpp @@ -1,5 +1,7 @@ #pragma once #include +#include +#include #include #include #include "RichText/TextStyleModifier.hpp" @@ -16,6 +18,8 @@ #include #include +#include + using namespace Eigen; class MainProgram; @@ -103,8 +107,59 @@ struct InputManager { bool isTouchDevice = true; - void set_rich_text_box_input_front(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor, bool isRichTextBox, const std::optional& nModMap = std::nullopt); - void set_rich_text_box_input_back(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor, bool isRichTextBox, const std::optional& nModMap = std::nullopt); + enum AndroidInputType : int { + ANDROIDTEXT_TYPE_CLASS_DATETIME = 0x00000004, + ANDROIDTEXT_TYPE_CLASS_NUMBER = 0x00000002, + ANDROIDTEXT_TYPE_CLASS_PHONE = 0x00000003, + ANDROIDTEXT_TYPE_CLASS_TEXT = 0x00000001, + ANDROIDTEXT_TYPE_DATETIME_VARIATION_DATE = 0x00000010, + ANDROIDTEXT_TYPE_DATETIME_VARIATION_NORMAL = 0x00000000, + ANDROIDTEXT_TYPE_DATETIME_VARIATION_TIME = 0x00000020, + ANDROIDTEXT_TYPE_MASK_CLASS = 0x0000000f, + ANDROIDTEXT_TYPE_MASK_FLAGS = 0x00fff000, + ANDROIDTEXT_TYPE_MASK_VARIATION = 0x00000ff0, + ANDROIDTEXT_TYPE_NULL = 0x00000000, + ANDROIDTEXT_TYPE_NUMBER_FLAG_DECIMAL = 0x00002000, + ANDROIDTEXT_TYPE_NUMBER_FLAG_SIGNED = 0x00001000, + ANDROIDTEXT_TYPE_NUMBER_VARIATION_NORMAL = 0x00000000, + ANDROIDTEXT_TYPE_NUMBER_VARIATION_PASSWORD = 0x00000010, + ANDROIDTEXT_TYPE_TEXT_FLAG_AUTO_COMPLETE = 0x00010000, + ANDROIDTEXT_TYPE_TEXT_FLAG_AUTO_CORRECT = 0x00008000, + ANDROIDTEXT_TYPE_TEXT_FLAG_CAP_CHARACTERS = 0x00001000, + ANDROIDTEXT_TYPE_TEXT_FLAG_CAP_SENTENCES = 0x00004000, + ANDROIDTEXT_TYPE_TEXT_FLAG_CAP_WORDS = 0x00002000, + ANDROIDTEXT_TYPE_TEXT_FLAG_ENABLE_TEXT_CONVERSION_SUGGESTIONS = 0x00100000, + ANDROIDTEXT_TYPE_TEXT_FLAG_ENABLE_TEXT_SUGGESTION_SELECTED = 0x00200000, + ANDROIDTEXT_TYPE_TEXT_FLAG_IME_MULTI_LINE = 0x00040000, + ANDROIDTEXT_TYPE_TEXT_FLAG_MULTI_LINE = 0x00020000, + ANDROIDTEXT_TYPE_TEXT_FLAG_NO_SUGGESTIONS = 0x00080000, + ANDROIDTEXT_TYPE_TEXT_VARIATION_EMAIL_ADDRESS = 0x00000020, + ANDROIDTEXT_TYPE_TEXT_VARIATION_EMAIL_SUBJECT = 0x00000030, + ANDROIDTEXT_TYPE_TEXT_VARIATION_FILTER = 0x000000b0, + ANDROIDTEXT_TYPE_TEXT_VARIATION_LONG_MESSAGE = 0x00000050, + ANDROIDTEXT_TYPE_TEXT_VARIATION_NORMAL = 0x00000000, + ANDROIDTEXT_TYPE_TEXT_VARIATION_PASSWORD = 0x00000080, + ANDROIDTEXT_TYPE_TEXT_VARIATION_PERSON_NAME = 0x00000060, + ANDROIDTEXT_TYPE_TEXT_VARIATION_PHONETIC = 0x000000c0, + ANDROIDTEXT_TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 0x00000070, + ANDROIDTEXT_TYPE_TEXT_VARIATION_SHORT_MESSAGE = 0x00000040, + ANDROIDTEXT_TYPE_TEXT_VARIATION_URI = 0x00000010, + ANDROIDTEXT_TYPE_TEXT_VARIATION_VISIBLE_PASSWORD = 0x00000090, + ANDROIDTEXT_TYPE_TEXT_VARIATION_WEB_EDIT_TEXT = 0x000000a0, + ANDROIDTEXT_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS = 0x000000d0, + ANDROIDTEXT_TYPE_TEXT_VARIATION_WEB_PASSWORD = 0x000000e0 + }; + + struct TextInputProperties { + SDL_TextInputType inputType; + SDL_Capitalization capitalization; + bool autocorrect; + bool multiline; + int androidInputType; + }; + + void set_rich_text_box_input_front(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor, bool isRichTextBox, const std::shared_ptr>& textboxRect, const TextInputProperties& inputProps, const std::optional& nModMap = std::nullopt); + void set_rich_text_box_input_back(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor, bool isRichTextBox, const std::shared_ptr>& textboxRect, const TextInputProperties& inputProps, const std::optional& nModMap = std::nullopt); void remove_rich_text_box_input(const std::shared_ptr& nTextBox); struct Text { @@ -120,14 +175,17 @@ struct InputManager { bool isRichTextBox; std::shared_ptr textBox; std::shared_ptr cursor; + std::shared_ptr> rect; std::optional modMap; + TextInputProperties textInputProperties; UndoManager textboxUndo; }; - void set_accepting_input(SDL_Window* window, bool newAcceptingInputVal); + void update_accepting_input(SDL_Window* window); bool isNextPasteRich = false; - bool acceptingInput = false; + + std::optional propID; std::deque textBoxes; diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index 0d7bc40..1e8f786 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -2273,6 +2273,8 @@ void Toolbar::initialize_io() { io->previousRichTextBoxToEdit = io->richTextBoxToEdit; io->richTextBoxToEditCursor = nullptr; io->richTextBoxToEdit = nullptr; + io->richTextBoxToEditRectangle = nullptr; + io->guiScaleMultiplier = final_gui_scale(); } void Toolbar::end_io() { @@ -2280,7 +2282,7 @@ void Toolbar::end_io() { main.input.set_clipboard_str(*io->clipboard.textOut); if(io->richTextBoxToEdit != io->previousRichTextBoxToEdit) { if(io->richTextBoxToEdit) - main.input.set_rich_text_box_input_front(io->richTextBoxToEdit, io->richTextBoxToEditCursor, false); + main.input.set_rich_text_box_input_front(io->richTextBoxToEdit, io->richTextBoxToEditCursor, false, io->richTextBoxToEditRectangle, io->richTextInputProperties); if(io->previousRichTextBoxToEdit) main.input.remove_rich_text_box_input(io->previousRichTextBoxToEdit); } @@ -2409,11 +2411,11 @@ void Toolbar::input_finger_motion_callback(const InputManager::FingerMotionCallb void Toolbar::start_gui() { SDL_Rect windowRect; if(SDL_GetWindowSafeArea(main.window.sdlWindow, &windowRect)) { - gui.windowPos = Vector2f{windowRect.x, windowRect.y} / final_gui_scale(); + io->windowPos = gui.windowPos = Vector2f{windowRect.x, windowRect.y} / final_gui_scale(); gui.windowSize = Vector2f{windowRect.w, windowRect.h} / final_gui_scale(); } else { - gui.windowPos = Vector2f{0.0f, 0.0f}; + io->windowPos = gui.windowPos = Vector2f{0.0f, 0.0f}; gui.windowSize = main.window.size.cast() / final_gui_scale(); } initialize_io();