From d189bdc107e7c9ed2c5b9ff907448c91dd9244fc Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 25 Feb 2024 21:03:21 +0300 Subject: [PATCH] multiline labels --- src/frontend/gui/UINode.h | 3 +- src/frontend/gui/controls.cpp | 92 +++++++++++++++++++++++-- src/frontend/gui/controls.h | 45 +++++++++--- src/frontend/gui/gui_xml.cpp | 27 +++++--- src/graphics/Font.cpp | 125 ++++++++++++++++++---------------- src/graphics/Font.h | 28 ++++---- src/util/stringutil.cpp | 14 ++++ 7 files changed, 239 insertions(+), 95 deletions(-) diff --git a/src/frontend/gui/UINode.h b/src/frontend/gui/UINode.h index 6fb550e4..bb182993 100644 --- a/src/frontend/gui/UINode.h +++ b/src/frontend/gui/UINode.h @@ -20,7 +20,8 @@ namespace gui { using onnumberchange = std::function; enum class Align { - left, center, right + left, center, right, + top=left, bottom=right, }; class UINode { diff --git a/src/frontend/gui/controls.cpp b/src/frontend/gui/controls.cpp index c18f589c..f727e6d9 100644 --- a/src/frontend/gui/controls.cpp +++ b/src/frontend/gui/controls.cpp @@ -1,5 +1,7 @@ #include "controls.h" +#include +#include #include #include "../../window/Events.h" @@ -30,9 +32,16 @@ Label::Label(std::wstring text, std::string fontName) void Label::setText(std::wstring text) { this->text = text; + lines = 1; + for (size_t i = 0; i < text.length(); i++) { + if (text[i] == L'\n') { + lines++; + } + } + lines = std::max(lines, 1U); } -std::wstring Label::getText() const { +const std::wstring& Label::getText() const { return text; } @@ -44,18 +53,45 @@ const std::string& Label::getFontName() const { return fontName; } +void Label::setVerticalAlign(Align align) { + this->valign = align; +} + +Align Label::getVerticalAlign() const { + return valign; +} + +float Label::getLineInterval() const { + return lineInterval; +} + +void Label::setLineInterval(float interval) { + lineInterval = interval; +} + +int Label::getTextYOffset() const { + return textYOffset; +} + +int Label::getLineYOffset(uint line) const { + return line * totalLineHeight + textYOffset; +} + void Label::draw(const GfxContext* pctx, Assets* assets) { if (supplier) { setText(supplier()); } auto batch = pctx->getBatch2D(); + auto font = assets->getFont(fontName); + batch->color = getColor(); - Font* font = assets->getFont(fontName); + + uint lineHeight = font->getLineHeight(); glm::vec2 size = getSize(); glm::vec2 newsize ( font->calcWidth(text), - font->getLineHeight()+font->getYOffset() + (lines == 1 ? lineHeight : lineHeight*lineInterval)*lines + font->getYOffset() ); glm::vec2 coord = calcCoord(); @@ -69,14 +105,48 @@ void Label::draw(const GfxContext* pctx, Assets* assets) { coord.x += size.x-newsize.x; break; } - coord.y += (size.y-newsize.y)*0.5f; - font->draw(batch, text, coord.x, coord.y); + switch (valign) { + case Align::top: + break; + case Align::center: + coord.y += (size.y-newsize.y)*0.5f; + break; + case Align::bottom: + coord.y += size.y-newsize.y; + break; + } + textYOffset = coord.y; + totalLineHeight = lineHeight * lineInterval; + + if (multiline) { + size_t offset = 0; + for (uint i = 0; i < lines; i++) { + std::wstring_view view(text.c_str()+offset, text.length()-offset); + size_t end = view.find(L'\n'); + if (end != std::wstring::npos) { + view = std::wstring_view(text.c_str()+offset, end); + offset += end + 1; + } + font->draw(batch, view, coord.x, coord.y + i * totalLineHeight, FontStyle::none); + } + } else { + font->draw(batch, text, coord.x, coord.y, FontStyle::none); + } } void Label::textSupplier(wstringsupplier supplier) { this->supplier = supplier; } + +void Label::setMultiline(bool multiline) { + this->multiline = multiline; +} + +bool Label::isMultiline() const { + return multiline; +} + // ================================= Image ==================================== Image::Image(std::string texture, glm::vec2 size) : UINode(glm::vec2(), size), texture(texture) { setInteractive(false); @@ -264,7 +334,7 @@ void TextBox::draw(const GfxContext* pctx, Assets* assets) { if (!isFocused()) return; - const int yoffset = 2; + const int yoffset = 0; const int lineHeight = font->getLineHeight(); glm::vec2 lcoord = label->calcCoord(); auto batch = pctx->getBatch2D(); @@ -390,6 +460,16 @@ bool TextBox::isValid() const { return valid; } +void TextBox::setMultiline(bool multiline) { + this->multiline = multiline; + label->setMultiline(multiline); + label->setVerticalAlign(multiline ? Align::top : Align::center); +} + +bool TextBox::isMultiline() const { + return multiline; +} + void TextBox::setOnEditStart(runnable oneditstart) { onEditStart = oneditstart; } diff --git a/src/frontend/gui/controls.h b/src/frontend/gui/controls.h index bf06887f..dbebd9ba 100644 --- a/src/frontend/gui/controls.h +++ b/src/frontend/gui/controls.h @@ -24,19 +24,40 @@ namespace gui { std::wstring text; std::string fontName; wstringsupplier supplier = nullptr; + uint lines = 1; + float lineInterval = 1.5f; + Align valign = Align::center; + + bool multiline = false; + + // runtime values + int textYOffset = 0; + int totalLineHeight = 1; public: Label(std::string text, std::string fontName="normal"); Label(std::wstring text, std::string fontName="normal"); virtual void setText(std::wstring text); - std::wstring getText() const; + const std::wstring& getText() const; virtual void setFontName(std::string name); - const std::string& getFontName() const; + virtual const std::string& getFontName() const; + + virtual void setVerticalAlign(Align align); + virtual Align getVerticalAlign() const; + + virtual float getLineInterval() const; + virtual void setLineInterval(float interval); + + virtual int getTextYOffset() const; + virtual int getLineYOffset(uint line) const; virtual void draw(const GfxContext* pctx, Assets* assets) override; virtual void textSupplier(wstringsupplier supplier); + + virtual void setMultiline(bool multiline); + virtual bool isMultiline() const; }; class Image : public UINode { @@ -121,6 +142,8 @@ namespace gui { size_t selectionEnd = 0; size_t selectionOrigin = 0; + bool multiline = false; + size_t normalizeIndex(int index); int calcIndexAt(int x) const; @@ -134,16 +157,9 @@ namespace gui { TextBox(std::wstring placeholder, glm::vec4 padding=glm::vec4(4.0f)); - virtual std::shared_ptr getAt(glm::vec2 pos, std::shared_ptr self) override; - - virtual void draw(const GfxContext* pctx, Assets* assets) override; - virtual void drawBackground(const GfxContext* pctx, Assets* assets) override; - virtual void typed(unsigned int codepoint) override; - virtual void keyPressed(keycode key) override; virtual void setTextSupplier(wstringsupplier supplier); virtual void setTextConsumer(wstringconsumer consumer); virtual void setTextValidator(wstringchecker validator); - virtual bool isFocuskeeper() const override {return true;} virtual void setFocusedColor(glm::vec4 color); virtual glm::vec4 getFocusedColor() const; virtual void setErrorColor(glm::vec4 color); @@ -161,11 +177,22 @@ namespace gui { virtual bool validate(); virtual void setValid(bool valid); virtual bool isValid() const; + + // multiline mode is in development + virtual void setMultiline(bool multiline); + virtual bool isMultiline() const; virtual void setOnEditStart(runnable oneditstart); + virtual void focus(GUI*) override; virtual void refresh() override; virtual void click(GUI*, int, int) override; virtual void mouseMove(GUI*, int x, int y) override; + virtual bool isFocuskeeper() const override {return true;} + virtual void draw(const GfxContext* pctx, Assets* assets) override; + virtual void drawBackground(const GfxContext* pctx, Assets* assets) override; + virtual void typed(unsigned int codepoint) override; + virtual void keyPressed(keycode key) override; + virtual std::shared_ptr getAt(glm::vec2 pos, std::shared_ptr self) override; }; class InputBindBox : public Panel { diff --git a/src/frontend/gui/gui_xml.cpp b/src/frontend/gui/gui_xml.cpp index 94a0dc97..401090f0 100644 --- a/src/frontend/gui/gui_xml.cpp +++ b/src/frontend/gui/gui_xml.cpp @@ -17,6 +17,8 @@ static Align align_from_string(const std::string& str, Align def) { if (str == "left") return Align::left; if (str == "center") return Align::center; if (str == "right") return Align::right; + if (str == "top") return Align::top; + if (str == "bottom") return Align::bottom; return def; } @@ -138,6 +140,11 @@ static std::shared_ptr readLabel(UiXmlReader& reader, xml::xmlelement el std::wstring text = readAndProcessInnerText(element); auto label = std::make_shared