#pragma once #include "../GUIManager.hpp" #include "../Elements/TextBox.hpp" #include "TextLabelHelpers.hpp" #include "LayoutHelpers.hpp" namespace GUIStuff { namespace ElementHelpers { template TextBoxData textbox_options_to_data(const OptT& options) { TextBoxData toRet; toRet.onEnter = options.onEnter; toRet.onEdit = options.onEdit; toRet.immutable = options.immutable; toRet.onSelect = options.onSelect; toRet.onDeselect = options.onDeselect; return toRet; } struct TextBoxOptions { bool immutable = false; std::function onEnter; std::function onEdit; std::function onSelect; std::function onDeselect; }; struct TextBoxScalarOptions { int decimalPrecision = 0; bool immutable = false; std::function onEnter; std::function onEdit; std::function onSelect; std::function onDeselect; }; struct TextBoxScalarsOptions { int decimalPrecision = 0; bool immutable = false; std::function onEnter; std::function onEdit; std::function onSelect; std::function onDeselect; }; struct TextBoxPathOptions { std::filesystem::file_type fileTypeRestriction; bool immutable = false; std::function onEnter; std::function onEdit; std::function onSelect; std::function onDeselect; }; struct TextBoxHexColorOptions { bool hasAlpha = true; bool immutable = false; std::function onEnter; std::function onEdit; std::function onSelect; std::function onDeselect; }; TextBox* input_color_component_255(GUIManager& gui, const char* id, float* val, const TextBoxOptions& options = {}); TextBox* input_text(GUIManager& gui, const char* id, std::string* val, const TextBoxOptions& options = {}); TextBox* input_text_field(GUIManager& gui, const char* id, std::string_view name, std::string* val, const TextBoxOptions& options = {}); TextBox* input_path(GUIManager& gui, const char* id, std::filesystem::path* val, const TextBoxPathOptions& options = {}); TextBox* input_path_field(GUIManager& gui, const char* id, std::string_view name, std::filesystem::path* val, const TextBoxPathOptions& options = {}); template void input_scalar(GUIManager& gui, const char* id, T* val, T minVal, T maxVal, const TextBoxScalarOptions& options = {}) { 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; TextBoxData d = textbox_options_to_data(options); d.data = val; d.textInputProps = textInputProps; d.fromStr = [minVal, maxVal](const std::string& a) { if(a.empty()) return minVal; T toRet; std::stringstream ss; ss << a; ss >> toRet; if(ss.fail()) return minVal; return std::clamp(toRet, minVal, maxVal); }; d.toStr = [decimalPrecision = options.decimalPrecision](const T& a) { std::stringstream ss; if(std::is_floating_point()) { ss.precision(decimalPrecision); ss << std::fixed << a; } else ss << a; return ss.str(); }; gui.element>(id, d); } template <> void input_scalar(GUIManager& gui, const char* id, uint8_t* val, uint8_t minVal, uint8_t maxVal, const TextBoxScalarOptions& options); template void input_scalars_field(GUIManager& gui, const char* id, std::string_view name, TContainer* val, size_t elemCount, T minVal, T maxVal, const TextBoxScalarsOptions& options = {}) { gui.new_id(id, [&] { left_to_right_line_layout(gui, [&]() { text_label(gui, name); for(size_t i = 0; i < elemCount; i++) { gui.new_id(i, [&] { input_scalar(gui, "field", &(*val)[i], minVal, maxVal, TextBoxScalarOptions { .decimalPrecision = options.decimalPrecision, .immutable = options.immutable, .onEnter = [f = options.onEnter, i] { if(f) f(i); }, .onEdit = [f = options.onEdit, i] { if(f) f(i); }, .onSelect = [f = options.onSelect, i] { if(f) f(i); }, .onDeselect = [f = options.onDeselect, i] { if(f) f(i); }, }); }); } }); }); } template void input_scalar_field(GUIManager& gui, const char* id, std::string_view name, T* val, T minVal, T maxVal, const TextBoxScalarOptions& options = {}) { left_to_right_line_layout(gui, [&]() { text_label(gui, name); input_scalar(gui, id, val, minVal, maxVal, options); }); } template void input_color_hex(GUIManager& gui, const char* id, T* val, const TextBoxHexColorOptions& options = {}) { InputManager::TextInputProperties textInputProps { .inputType = SDL_TextInputType::SDL_TEXTINPUT_TYPE_TEXT, .capitalization = SDL_Capitalization::SDL_CAPITALIZE_NONE, .autocorrect = false, .multiline = false, .androidInputType = InputManager::AndroidInputType::ANDROIDTEXT_TYPE_CLASS_TEXT }; TextBoxData d = textbox_options_to_data(options); d.data = val; d.textInputProps = textInputProps; d.fromStr = [hasAlpha = options.hasAlpha](const std::string& a) { T def; def[0] = 0.0f; def[1] = 0.0f; def[2] = 0.0f; if(hasAlpha) def[3] = 1.0f; unsigned startIndex = 0; if(a.empty()) return def; if(a[0] == '#') startIndex++; for(unsigned i = 0; i < (hasAlpha ? 4 : 3); i++) { int byteColor; std::stringstream ss; size_t startingAt = startIndex + i * 2; if(startingAt + 1 >= a.size()) break; ss << a.substr(startingAt, 2); ss >> std::hex >> byteColor; if(ss.fail()) break; def[i] = byteColor / 255.0f; } return def; }; d.toStr = [hasAlpha = options.hasAlpha](const T& a) { std::stringstream ss; ss << "#" << std::setfill('0') << std::setw(2) << std::hex; for(size_t i = 0; i < (hasAlpha ? 4 : 3); i++) { int convertTo255 = static_cast(std::clamp(a[i], 0, 1) * 255); ss << std::setfill('0') << std::setw(2) << std::hex << convertTo255; } return ss.str(); }; gui.element>(id, d); } } }