#pragma once #include "Element.hpp" #include "../../RichTextBox/RichTextBox.hpp" #include #include namespace GUIStuff { template class TextBox : public Element { public: void update(UpdateInputData& io, T* newData, const std::function(const std::string&)>& newFromStr, const std::function newToStr, bool newSingleLine, bool updateEveryEdit, const std::function& elemUpdate) { if(!textbox || !cur) force_update_textbox(io, false); data = newData; fromStr = newFromStr; toStr = newToStr; singleLine = newSingleLine; force_update_textbox(io, false); textbox->set_width(singleLine ? std::numeric_limits::max() : bb.width()); CLAY({ .layout = { .sizing = {.width = CLAY_SIZING_GROW(static_cast(io.fontSize * 2)), .height = singleLine ? CLAY_SIZING_FIXED(static_cast(io.fontSize * 1.25f)) : CLAY_SIZING_GROW(static_cast(io.fontSize * 1.25f))} }, .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); update_on_edit(updateEveryEdit); } if(data && selection.justUnselected) { std::optional dataToAssign = fromStr(textbox->get_string()); if(dataToAssign) *data = dataToAssign.value(); force_update_textbox(io, true); } if(elemUpdate) elemUpdate(selection); } } virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command) override { if(!data || !textbox || !cur) 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); 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())); canvas->translate(bb.min.x() + 2.0f, bb.min.y()); RichTextBox::PaintOpts paintOpts; if(selection.selected) paintOpts.cursor = *cur; paintOpts.cursorColor = {io.theme->fillColor1.fR, io.theme->fillColor1.fG, io.theme->fillColor1.fB}; skia::textlayout::TextStyle tStyle; std::vector families; families.emplace_back("Roboto"); tStyle.setFontFamilies(families); tStyle.setFontSize(io.fontSize); tStyle.setForegroundPaint(SkPaint{io.theme->frontColor1}); textbox->set_initial_text_style(tStyle); textbox->paint(canvas, paintOpts); canvas->restore(); } SelectionHelper selection; private: void force_update_textbox(UpdateInputData& io, bool reallyForce) { if(data && (*data == oldData) && !reallyForce && textbox && cur) return; textbox = std::make_shared(); textbox->set_font_collection(io.fontCollection); textbox->set_allow_newlines(!singleLine); cur = std::make_shared(); if(data) { cur->pos = cur->selectionBeginPos = cur->selectionEndPos = textbox->insert({0, 0}, toStr(*data)); oldData = *data; } } void update_on_edit(bool updateEveryEdit) { if(updateEveryEdit) { std::optional dataToAssign = fromStr(textbox->get_string()); if(dataToAssign) { *data = dataToAssign.value(); oldData = *data; } } } T* data = nullptr; T oldData; bool singleLine = true; std::function(const std::string&)> fromStr; std::function toStr; std::shared_ptr textbox; std::shared_ptr cur; SCollision::AABB bb; }; }