infinipaint/src/GUIStuff/Elements/TextBox.hpp

135 lines
5 KiB
C++
Raw Normal View History

2025-07-06 20:43:53 +03:00
#pragma once
#include "Element.hpp"
#include "../../RichTextBox/RichTextBox.hpp"
#include <limits>
#include <modules/skparagraph/include/TextStyle.h>
2025-07-06 20:43:53 +03:00
namespace GUIStuff {
template <typename T> class TextBox : public Element {
public:
2025-11-05 14:55:22 +03:00
bool update(UpdateInputData& io, T* newData, const std::function<std::optional<T>(const std::string&)>& newFromStr, const std::function<std::string(const T&)> newToStr, bool newSingleLine, bool updateEveryEdit, const std::function<void(SelectionHelper&)>& elemUpdate) {
bool isUpdating = false;
if(!textbox || !cur)
force_update_textbox(io, false);
2025-07-06 20:43:53 +03:00
data = newData;
fromStr = newFromStr;
toStr = newToStr;
singleLine = newSingleLine;
force_update_textbox(io, false);
2025-07-06 20:43:53 +03:00
textbox->set_width(singleLine ? std::numeric_limits<float>::max() : bb.width());
2025-07-06 20:43:53 +03:00
CLAY({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(static_cast<float>(io.fontSize * 2)), .height = singleLine ? CLAY_SIZING_FIXED(static_cast<float>(io.fontSize * 1.25f)) : CLAY_SIZING_GROW(static_cast<float>(io.fontSize * 1.25f))}
2025-07-06 20:43:53 +03:00
},
.custom = { .customData = this }
}) {
selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld);
if(data && selection.selected) {
io.richTextBoxEdit(textbox, cur);
textbox->process_mouse_left_button(*cur, io.mouse.pos - bb.min, io.mouse.leftClick, io.mouse.leftHeld, io.key.leftShift);
2025-11-08 20:50:23 +03:00
if((updateEveryEdit && textbox->inputChangedTextBox) || io.key.enter) {
2025-11-05 14:55:22 +03:00
update_on_edit();
isUpdating = true;
}
2025-07-06 20:43:53 +03:00
}
if(data && selection.justUnselected) {
std::optional<T> dataToAssign = fromStr(textbox->get_string());
2025-07-06 20:43:53 +03:00
if(dataToAssign)
*data = dataToAssign.value();
force_update_textbox(io, true);
2025-11-05 14:55:22 +03:00
isUpdating = true;
2025-07-06 20:43:53 +03:00
}
if(elemUpdate)
elemUpdate(selection);
2025-07-06 20:43:53 +03:00
}
2025-11-05 14:55:22 +03:00
2025-11-08 20:50:23 +03:00
if(textbox)
textbox->inputChangedTextBox = false;
2025-11-05 14:55:22 +03:00
return isUpdating;
2025-07-06 20:43:53 +03:00
}
virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command) override {
if(!data || !textbox || !cur)
2025-07-06 20:43:53 +03:00
return;
bb = get_bb(command);
canvas->save();
SkRect r = SkRect::MakeXYWH(bb.min.x(), bb.min.y(), bb.width(), bb.height());
canvas->drawRect(r, SkPaint(io.theme->backColor2));
SkPaint outline(selection.selected ? io.theme->fillColor1 : io.theme->backColor2);
2025-07-06 20:43:53 +03:00
outline.setStroke(true);
outline.setStrokeWidth(2.0f);
canvas->drawRoundRect(r, 2.0f, 2.0f, outline);
canvas->clipRect(SkRect::MakeXYWH(bb.min.x(), bb.min.y(), bb.width(), bb.height()));
2025-11-04 10:58:26 +03:00
canvas->translate(bb.min.x() + 2.0f, bb.min.y());
2025-07-06 20:43:53 +03:00
RichTextBox::PaintOpts paintOpts;
if(selection.selected)
paintOpts.cursor = *cur;
paintOpts.cursorColor = {io.theme->fillColor1.fR, io.theme->fillColor1.fG, io.theme->fillColor1.fB};
2025-07-06 20:43:53 +03:00
skia::textlayout::TextStyle tStyle;
std::vector<SkString> families;
families.emplace_back("Roboto");
tStyle.setFontFamilies(families);
tStyle.setFontSize(io.fontSize);
tStyle.setForegroundPaint(SkPaint{io.theme->frontColor1});
textbox->set_initial_text_style(tStyle);
2025-11-04 10:58:26 +03:00
textbox->paint(canvas, paintOpts);
2025-07-06 20:43:53 +03:00
canvas->restore();
}
SelectionHelper selection;
private:
void force_update_textbox(UpdateInputData& io, bool reallyForce) {
if(data && (*data == oldData) && !reallyForce && textbox && cur)
2025-07-06 20:43:53 +03:00
return;
textbox = std::make_shared<RichTextBox>();
textbox->set_font_collection(io.fontCollection);
textbox->set_allow_newlines(!singleLine);
2025-11-04 10:58:26 +03:00
cur = std::make_shared<RichTextBox::Cursor>();
2025-07-06 20:43:53 +03:00
if(data) {
cur->pos = cur->selectionBeginPos = cur->selectionEndPos = textbox->insert({0, 0}, toStr(*data));
2025-07-06 20:43:53 +03:00
oldData = *data;
}
}
2025-11-05 14:55:22 +03:00
void update_on_edit() {
std::optional<T> dataToAssign = fromStr(textbox->get_string());
if(dataToAssign) {
*data = dataToAssign.value();
oldData = *data;
2025-09-06 19:06:30 +03:00
}
}
2025-07-06 20:43:53 +03:00
T* data = nullptr;
T oldData;
bool singleLine = true;
std::function<std::optional<T>(const std::string&)> fromStr;
std::function<std::string(const T&)> toStr;
std::shared_ptr<RichTextBox> textbox;
std::shared_ptr<RichTextBox::Cursor> cur;
2025-07-06 20:43:53 +03:00
SCollision::AABB<float> bb;
};
}