infinipaint/src/GUIStuff/ElementHelpers/TextBoxHelpers.hpp
2026-04-12 17:31:32 +03:00

190 lines
7.1 KiB
C++

#pragma once
#include "../GUIManager.hpp"
#include "../Elements/TextBox.hpp"
#include "TextLabelHelpers.hpp"
#include "LayoutHelpers.hpp"
namespace GUIStuff { namespace ElementHelpers {
template <typename T, typename OptT> TextBoxData<T> textbox_options_to_data(const OptT& options) {
TextBoxData<T> 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<void()> onEnter;
std::function<void()> onEdit;
std::function<void()> onSelect;
std::function<void()> onDeselect;
};
struct TextBoxScalarOptions {
int decimalPrecision = 0;
bool immutable = false;
std::function<void()> onEnter;
std::function<void()> onEdit;
std::function<void()> onSelect;
std::function<void()> onDeselect;
};
struct TextBoxScalarsOptions {
int decimalPrecision = 0;
bool immutable = false;
std::function<void(size_t)> onEnter;
std::function<void(size_t)> onEdit;
std::function<void(size_t)> onSelect;
std::function<void(size_t)> onDeselect;
};
struct TextBoxPathOptions {
std::filesystem::file_type fileTypeRestriction;
bool immutable = false;
std::function<void()> onEnter;
std::function<void()> onEdit;
std::function<void()> onSelect;
std::function<void()> onDeselect;
};
struct TextBoxHexColorOptions {
bool hasAlpha = true;
bool immutable = false;
std::function<void()> onEnter;
std::function<void()> onEdit;
std::function<void()> onSelect;
std::function<void()> onDeselect;
};
TextBox<float>* input_color_component_255(GUIManager& gui, const char* id, float* val, const TextBoxOptions& options = {});
TextBox<std::string>* input_text(GUIManager& gui, const char* id, std::string* val, const TextBoxOptions& options = {});
TextBox<std::string>* input_text_field(GUIManager& gui, const char* id, std::string_view name, std::string* val, const TextBoxOptions& options = {});
TextBox<std::filesystem::path>* input_path(GUIManager& gui, const char* id, std::filesystem::path* val, const TextBoxPathOptions& options = {});
TextBox<std::filesystem::path>* input_path_field(GUIManager& gui, const char* id, std::string_view name, std::filesystem::path* val, const TextBoxPathOptions& options = {});
template <typename T> 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<T>())
textInputProps.androidInputType |= InputManager::AndroidInputType::ANDROIDTEXT_TYPE_NUMBER_FLAG_SIGNED;
if(std::is_floating_point<T>())
textInputProps.androidInputType |= InputManager::AndroidInputType::ANDROIDTEXT_TYPE_NUMBER_FLAG_DECIMAL;
TextBoxData<T> d = textbox_options_to_data<T>(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<T>()) {
ss.precision(decimalPrecision);
ss << std::fixed << a;
}
else
ss << a;
return ss.str();
};
gui.element<TextBox<T>>(id, d);
}
template <> void input_scalar<uint8_t>(GUIManager& gui, const char* id, uint8_t* val, uint8_t minVal, uint8_t maxVal, const TextBoxScalarOptions& options);
template <typename TContainer, typename T> 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<T>(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 <typename T> 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<T>(gui, id, val, minVal, maxVal, options);
});
}
template <typename T> 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<T> d = textbox_options_to_data<T>(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<int>(std::clamp<float>(a[i], 0, 1) * 255);
ss << std::setfill('0') << std::setw(2) << std::hex << convertTo255;
}
return ss.str();
};
gui.element<TextBox<T>>(id, d);
}
} }