From eb490906a074636ab26998ee8847c3a5d2499755 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Sat, 25 Apr 2026 19:01:19 +0300 Subject: [PATCH] Fixed many issues with text paragraphs in GUI --- .../TextBoxCanvasComponent.cpp | 2 +- src/GUIStuff/Elements/Element.cpp | 4 ++ src/GUIStuff/Elements/Element.hpp | 1 + src/GUIStuff/Elements/FontPicker.cpp | 38 ++++++++-------- src/GUIStuff/Elements/TextBox.impl.hpp | 2 +- src/GUIStuff/Elements/TextParagraph.cpp | 44 ++++++++++++++++--- src/GUIStuff/Elements/TextParagraph.hpp | 6 ++- src/GUIStuff/GUIManager.cpp | 2 +- src/RichText/TextBox.cpp | 30 ++++++++++++- src/RichText/TextBox.hpp | 10 ++++- src/Screens/FileSelectScreen.cpp | 23 +++++++++- src/Toolbar.cpp | 6 ++- src/main.cpp | 1 + 13 files changed, 132 insertions(+), 37 deletions(-) diff --git a/src/CanvasComponents/TextBoxCanvasComponent.cpp b/src/CanvasComponents/TextBoxCanvasComponent.cpp index cf9e016..5080859 100644 --- a/src/CanvasComponents/TextBoxCanvasComponent.cpp +++ b/src/CanvasComponents/TextBoxCanvasComponent.cpp @@ -76,7 +76,7 @@ void TextBoxCanvasComponent::set_data_from(const CanvasComponent& other) { void TextBoxCanvasComponent::init_text_box(DrawingProgram& drawP) { textBox->set_font_data(drawP.world.main.fonts); // Getting a segfault relating to the paragraph cache means that the font collection hasn't been set yet - textBox->set_width(d.p2.x() - d.p1.x() - TEXTBOX_PADDING * 2.0f); + textBox->set_max_width(d.p2.x() - d.p1.x() - TEXTBOX_PADDING * 2.0f); } void TextBoxCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const { diff --git a/src/GUIStuff/Elements/Element.cpp b/src/GUIStuff/Elements/Element.cpp index c6bfc06..85b490e 100644 --- a/src/GUIStuff/Elements/Element.cpp +++ b/src/GUIStuff/Elements/Element.cpp @@ -18,6 +18,10 @@ namespace GUIStuff { void Element::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) {} + void Element::clear_bb() { + boundingBox = std::nullopt; + } + void Element::set_bounding_box_from_elem_data(const Clay_ElementData& elemData) { if(elemData.found) boundingBox = {{elemData.boundingBox.x, elemData.boundingBox.y}, {elemData.boundingBox.x + elemData.boundingBox.width, elemData.boundingBox.y + elemData.boundingBox.height}}; diff --git a/src/GUIStuff/Elements/Element.hpp b/src/GUIStuff/Elements/Element.hpp index 4fbc431..ce93c08 100644 --- a/src/GUIStuff/Elements/Element.hpp +++ b/src/GUIStuff/Elements/Element.hpp @@ -14,6 +14,7 @@ namespace GUIStuff { void set_bounding_box_from_elem_data(const Clay_ElementData& elemData); void set_parent_clipping_region(const std::optional>& bb); const std::optional>& get_bb() const; + void clear_bb(); virtual ~Element() = default; int16_t zIndex; diff --git a/src/GUIStuff/Elements/FontPicker.cpp b/src/GUIStuff/Elements/FontPicker.cpp index 74e08b0..faf6e2b 100644 --- a/src/GUIStuff/Elements/FontPicker.cpp +++ b/src/GUIStuff/Elements/FontPicker.cpp @@ -90,17 +90,23 @@ void FontPicker::layout(const Clay_ElementId& id, std::string* newFontName, cons if(dropdownOpen) { gui.set_z_index(gui.get_z_index() + 1, [&] { - gui.element("Font picker floating element", [&] (LayoutElement*, const Clay_ElementId& lId) { + gui.element("DROPDOWN", [&](LayoutElement*, const Clay_ElementId& lId) { + Clay_ElementData dropdownElemData = Clay_GetElementData(lId); + float calculatedDropdownMaxHeight = 0.0f; + if(dropdownElemData.found) { + calculatedDropdownMaxHeight = std::max(gui.io.windowSize.y() - dropdownElemData.boundingBox.y - 2.0f, 0.0f); + calculatedDropdownMaxHeight = std::min(calculatedDropdownMaxHeight, 300); + } CLAY(lId, { .layout = { - .sizing = {.width = CLAY_SIZING_FIXED(250), .height = CLAY_SIZING_FIT(0, 300)}, - .layoutDirection = CLAY_TOP_TO_BOTTOM + .sizing = {.width = CLAY_SIZING_FIXED(250), .height = CLAY_SIZING_FIT(0, calculatedDropdownMaxHeight)}, + .childGap = 0 }, .backgroundColor = convert_vec4(gui.io.theme->backColor1), .cornerRadius = CLAY_CORNER_RADIUS(4), .floating = { .offset = { - .y = 2 + .y = 4 }, .zIndex = gui.get_z_index(), .attachPoints = { @@ -114,16 +120,17 @@ void FontPicker::layout(const Clay_ElementId& id, std::string* newFontName, cons .width = CLAY_BORDER_OUTSIDE(1) } }) { - scroll_area_many_entries(gui, "Font Selector", { + ElementHelpers::scroll_area_many_entries(gui, "dropdown scroll area", { .entryHeight = ENTRY_HEIGHT, .entryCount = sortedFontList.size(), .clipHorizontal = true, - .elementContent = [&] (size_t i) { + .growing = true, + .elementContent = [&](size_t i) { bool selectedEntry = val == i; - gui.element("elem", [&](LayoutElement*, const Clay_ElementId& lId2) { - CLAY(lId2, { + gui.element("elem", [&] (LayoutElement*, const Clay_ElementId& lId) { + CLAY(lId, { .layout = { - .sizing = {.width = CLAY_SIZING_FIXED(250), .height = CLAY_SIZING_FIXED(25)}, + .sizing = {.width = CLAY_SIZING_FIXED(250), .height = CLAY_SIZING_GROW(0)}, .childGap = 1, .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_TOP}, .layoutDirection = CLAY_TOP_TO_BOTTOM @@ -138,7 +145,7 @@ void FontPicker::layout(const Clay_ElementId& id, std::string* newFontName, cons entryColor = gui.io.theme->backColor1; CLAY_AUTO_ID({ .layout = { - .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(25)}, + .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, .padding = {.left = 2, .right = 2}, .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER} }, @@ -147,7 +154,9 @@ void FontPicker::layout(const Clay_ElementId& id, std::string* newFontName, cons TextParagraph::Data d; RichText::TextData::Paragraph& par = d.text.paragraphs.emplace_back(); par.text = sortedFontList[i]; - d.width = 10000.0f; // Setting width to numeric_limits::max will lead to multiplying it by any number greater than 1 to = infinity, which will cause issues + + d.allowNewlines = false; + d.ellipsis = false; RichText::PositionedTextStyleMod& positionedMod = d.text.tStyleMods.emplace_back(); positionedMod.pos = {0, 0}; @@ -174,13 +183,6 @@ void FontPicker::layout(const Clay_ElementId& id, std::string* newFontName, cons gui.set_to_layout(); } }); - }, - .innerContentExtraCallback = [&](const ScrollArea::InnerContentParameters& innerContentParams) { - if(jumpToFontName) { - if(val != std::numeric_limits::max()) - innerContentParams.scrollOffset->y() = -static_cast(val) * ENTRY_HEIGHT; - jumpToFontName = false; - } } }); } diff --git a/src/GUIStuff/Elements/TextBox.impl.hpp b/src/GUIStuff/Elements/TextBox.impl.hpp index c914e71..2d1937e 100644 --- a/src/GUIStuff/Elements/TextBox.impl.hpp +++ b/src/GUIStuff/Elements/TextBox.impl.hpp @@ -159,7 +159,7 @@ template void TextBox::input_mouse_motion_callback(const InputMa template void TextBox::init_textbox(UpdateInputData& io) { if(!textbox) { textbox = std::make_shared(); - textbox->set_width(std::numeric_limits::max()); + textbox->set_max_width(std::numeric_limits::max()); textbox->set_font_data(io.fonts); textbox->set_allow_newlines(false); cur = std::make_shared(); diff --git a/src/GUIStuff/Elements/TextParagraph.cpp b/src/GUIStuff/Elements/TextParagraph.cpp index d408f80..1c91929 100644 --- a/src/GUIStuff/Elements/TextParagraph.cpp +++ b/src/GUIStuff/Elements/TextParagraph.cpp @@ -2,6 +2,7 @@ #include #include #include "../GUIManager.hpp" +#include "Helpers/ConvertVec.hpp" namespace GUIStuff { @@ -14,20 +15,32 @@ void TextParagraph::layout(const Clay_ElementId& id, const Data& newData) { if(!textbox) { d = newData; textbox = std::make_unique(); - textbox->set_width(d.width); textbox->set_rich_text_data(d.text); + textbox->set_ellipsis(newData.ellipsis); textbox->set_font_data(gui.io.fonts); - textbox->set_allow_newlines(true); + textbox->set_allow_newlines(newData.allowNewlines); + textbox->set_max_width(newData.maxGrowX); + textbox->set_max_height(newData.maxGrowY); skia::textlayout::TextStyle tStyle; tStyle.setFontFamilies(gui.io.fonts->get_default_font_families()); tStyle.setFontSize(gui.io.fontSize); textbox->set_initial_text_style(tStyle); + fontSize = gui.io.fontSize; redraw = true; } - if(d.width != newData.width) { - d.width = newData.width; - textbox->set_width(d.width); + if(d.allowNewlines != newData.allowNewlines) { + d.allowNewlines = newData.allowNewlines; + textbox->set_allow_newlines(d.allowNewlines); + redraw = true; + } + + if(gui.io.fontSize != fontSize) { + fontSize = gui.io.fontSize; + skia::textlayout::TextStyle tStyle; + tStyle.setFontFamilies(gui.io.fonts->get_default_font_families()); + tStyle.setFontSize(gui.io.fontSize); + textbox->set_initial_text_style(tStyle); redraw = true; } @@ -37,12 +50,29 @@ void TextParagraph::layout(const Clay_ElementId& id, const Data& newData) { redraw = true; } + if(d.maxGrowX != newData.maxGrowX) { + d.maxGrowX = newData.maxGrowX; + textbox->set_max_width(d.maxGrowX); + redraw = true; + } + if(d.maxGrowY != newData.maxGrowY) { + d.maxGrowY = newData.maxGrowY; + textbox->set_max_height(d.maxGrowY); + redraw = true; + } + + if(d.ellipsis != newData.ellipsis) { + d.ellipsis = newData.ellipsis; + textbox->set_ellipsis(d.ellipsis); + redraw = true; + } + if(redraw) gui.invalidate_draw_element(this); CLAY(id, { .layout = { - .sizing = {.width = CLAY_SIZING_FIXED(d.width), .height = CLAY_SIZING_FIXED(textbox->get_height())} + .sizing = {.width = CLAY_SIZING_GROW(0, textbox->get_width()), .height = CLAY_SIZING_FIXED(textbox->get_height())} }, .custom = { .customData = this } }) {} @@ -54,7 +84,7 @@ void TextParagraph::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_Render canvas->translate(bb.min.x(), bb.min.y()); skia::textlayout::TextStyle tStyle; tStyle.setFontFamilies(gui.io.fonts->get_default_font_families()); - tStyle.setFontSize(gui.io.fontSize); + tStyle.setFontSize(fontSize); tStyle.setForegroundPaint(SkPaint{io.theme->frontColor1}); textbox->set_initial_text_style(tStyle); textbox->paint(canvas, { .skiaAA = skiaAA }); diff --git a/src/GUIStuff/Elements/TextParagraph.hpp b/src/GUIStuff/Elements/TextParagraph.hpp index a631d0c..8cb81ce 100644 --- a/src/GUIStuff/Elements/TextParagraph.hpp +++ b/src/GUIStuff/Elements/TextParagraph.hpp @@ -8,7 +8,10 @@ class TextParagraph : public Element { public: struct Data { RichText::TextData text; - float width; + float maxGrowX = 10000.0f; + float maxGrowY = 0.0f; + bool ellipsis = true; + bool allowNewlines = true; bool operator==(const Data& d) const = default; bool operator!=(const Data& d) const = default; }; @@ -18,6 +21,7 @@ class TextParagraph : public Element { virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) override; private: Data d; + float fontSize; std::unique_ptr textbox; }; diff --git a/src/GUIStuff/GUIManager.cpp b/src/GUIStuff/GUIManager.cpp index f02f97b..4323816 100644 --- a/src/GUIStuff/GUIManager.cpp +++ b/src/GUIStuff/GUIManager.cpp @@ -544,7 +544,7 @@ void GUIManager::layout_if_necessary() { } void GUIManager::layout() { - constexpr int LAYOUT_RUN_COUNT = 4; + constexpr int LAYOUT_RUN_COUNT = 6; for(int i = 0; i < LAYOUT_RUN_COUNT; i++) single_layout_run(); } diff --git a/src/RichText/TextBox.cpp b/src/RichText/TextBox.cpp index 91732c5..825db7e 100644 --- a/src/RichText/TextBox.cpp +++ b/src/RichText/TextBox.cpp @@ -3,7 +3,6 @@ #include "TextStyleModifier.hpp" #include "cereal/archives/portable_binary.hpp" #include -#include #include #include #include @@ -838,13 +837,36 @@ int TextBox::get_line_number_at_from_byte_text_pos(TextPosition pos) { return paragraphs[pos.fParagraphIndex].p->getLineNumberAt(renderPos.fTextByteIndex); } -void TextBox::set_width(float newWidth) { +void TextBox::set_max_width(float newWidth) { if(width != newWidth) { width = std::max(newWidth, 4.0f); needsRebuild = true; } } +void TextBox::set_max_height(float newMaxHeight) { + if(maxHeight != newMaxHeight) { + maxHeight = newMaxHeight; + needsRebuild = true; + } +} + +void TextBox::set_ellipsis(bool newEllipsis) { + if(ellipsis != newEllipsis) { + ellipsis = newEllipsis; + needsRebuild = true; + } +} + +float TextBox::get_width() { + rebuild(); + float toRet = 0.0f; + for(auto& p : paragraphs) + toRet = std::max(toRet, p.p->getLongestLine()); + toRet += 1.0f; + return toRet; +} + float TextBox::get_height() { rebuild(); return paragraphs.back().heightOffset + paragraphs.back().p->getHeight(); @@ -875,6 +897,10 @@ void TextBox::rebuild() { ParagraphData& pData = paragraphs[pIndex]; skia::textlayout::ParagraphStyle pStyle = pData.pStyleData.get_paragraph_style(); pStyle.setTextStyle(tStyle); + if(ellipsis) + pStyle.setEllipsis(SkString("...")); + if(maxHeight != 0.0f) + pStyle.setHeight(maxHeight); if(!newlinesAllowed) { pStyle.setTextHeightBehavior(skia::textlayout::kDisableAll); diff --git a/src/RichText/TextBox.hpp b/src/RichText/TextBox.hpp index b599ab8..2bc0ffe 100644 --- a/src/RichText/TextBox.hpp +++ b/src/RichText/TextBox.hpp @@ -107,7 +107,9 @@ class TextBox { }; TextPosition move(Movement movement, TextPosition pos, std::optional* previousX = nullptr, bool flipDependingOnTextDirection = false); - void set_width(float newWidth); + void set_max_width(float newWidth); + void set_max_height(float newMaxHeight); + void set_ellipsis(bool newEllipsis); TextPosition insert(TextPosition pos, std::string_view textToInsert, const std::optional& inputModMap = std::nullopt); TextPosition remove(TextPosition p1, TextPosition p2); void set_font_data(const std::shared_ptr& fD); @@ -129,6 +131,8 @@ class TextBox { SELECT_ALL }; + float get_width(); + void set_initial_text_style(const skia::textlayout::TextStyle& tStyle); void set_initial_text_style_modifier(const std::shared_ptr& modifier); void set_text_style_modifier_between(TextPosition p1, TextPosition p2, const std::shared_ptr& modifier); @@ -193,7 +197,9 @@ class TextBox { int get_line_number_at_from_byte_text_pos(TextPosition pos); bool newlinesAllowed = true; - float width = 0.0f; + bool ellipsis = false; + float width = 4.0f; + float maxHeight = 0.0f; bool needsRebuild = true; unsigned tabWidth = 4; std::shared_ptr fontData; diff --git a/src/Screens/FileSelectScreen.cpp b/src/Screens/FileSelectScreen.cpp index 2bee6dc..aa8da57 100644 --- a/src/Screens/FileSelectScreen.cpp +++ b/src/Screens/FileSelectScreen.cpp @@ -9,6 +9,7 @@ #include "../GUIStuff/Elements/ImageDisplay.hpp" #include "../GUIStuff/Elements/LayoutElement.hpp" #include "../GUIStuff/Elements/SVGIcon.hpp" +#include "../GUIStuff/Elements/TextParagraph.hpp" #include "../GUIStuff/Elements/PositionAdjustingPopupMenu.hpp" #include "../World.hpp" #include "Helpers/StringHelpers.hpp" @@ -527,7 +528,7 @@ void FileSelectScreen::file_view() { } CLAY_AUTO_ID({ .layout = { - .sizing = { .width = CLAY_SIZING_FIT(0), .height = CLAY_SIZING_FIT(0)}, + .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0)}, .childGap = 6, .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER }, .layoutDirection = CLAY_TOP_TO_BOTTOM @@ -556,7 +557,24 @@ void FileSelectScreen::file_view() { .radius = 20 }); } - text_label(gui, fileList[i].fileName); + gui.element("file name layout elem", [&](LayoutElement* l, const Clay_ElementId& lId) { + CLAY(lId, { + .layout = { + .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0)}, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP }, + }, + }) { + if(l->get_bb()) { + RichText::TextData fileNameText; + fileNameText.paragraphs.emplace_back(fileList[i].fileName); + gui.element("file name", TextParagraph::Data{ + .text = fileNameText, + .maxGrowX = l->get_bb().value().width(), + .maxGrowY = 0.0f + }); + } + } + }); text_label_light(gui, fileList[i].lastModifyDate); } } @@ -569,6 +587,7 @@ void FileSelectScreen::file_view() { .entryHeight = 150.0f, .entryCount = fileList.size(), .clipHorizontal = true, + .xElementSize = CLAY_SIZING_FIT(0), .elementContent = [&](size_t i) { fileButton(i, true, Vector2f{100.0f, 100.0f}); } diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index e0286f2..25bfcbd 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -1039,7 +1039,8 @@ void Toolbar::chat_box() { gui.new_id(id++, [&] { gui.element("text", TextParagraph::Data{ .text = build_paragraph_from_chat_message(chatMessage, 1.0f), - .width = CHATBOX_WIDTH + .maxGrowX = CHATBOX_WIDTH, + .ellipsis = false }); }); } @@ -1088,7 +1089,8 @@ void Toolbar::chat_box() { }) { gui.element("message", TextParagraph::Data{ .text = build_paragraph_from_chat_message(chatMessage, a), - .width = CHATBOX_WIDTH + .maxGrowX = CHATBOX_WIDTH, + .ellipsis = false }); } }); diff --git a/src/main.cpp b/src/main.cpp index 77977c6..142008e 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include "Helpers/StringHelpers.hpp" #include "Helpers/FileDownloader.hpp" #include "Screens/FileSelectScreen.hpp" +#include "Screens/DrawingProgramScreen.hpp" #include "VersionConstants.hpp" #include "include/gpu/GpuTypes.h" #include