multiline labels

This commit is contained in:
MihailRis
2024-02-25 21:03:21 +03:00
parent fcc5d11814
commit d189bdc107
7 changed files with 237 additions and 93 deletions
+2 -1
View File
@@ -20,7 +20,8 @@ namespace gui {
using onnumberchange = std::function<void(GUI*, double)>; using onnumberchange = std::function<void(GUI*, double)>;
enum class Align { enum class Align {
left, center, right left, center, right,
top=left, bottom=right,
}; };
class UINode { class UINode {
+86 -6
View File
@@ -1,5 +1,7 @@
#include "controls.h" #include "controls.h"
#include <queue>
#include <sstream>
#include <iostream> #include <iostream>
#include "../../window/Events.h" #include "../../window/Events.h"
@@ -30,9 +32,16 @@ Label::Label(std::wstring text, std::string fontName)
void Label::setText(std::wstring text) { void Label::setText(std::wstring text) {
this->text = 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; return text;
} }
@@ -44,18 +53,45 @@ const std::string& Label::getFontName() const {
return fontName; 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) { void Label::draw(const GfxContext* pctx, Assets* assets) {
if (supplier) { if (supplier) {
setText(supplier()); setText(supplier());
} }
auto batch = pctx->getBatch2D(); auto batch = pctx->getBatch2D();
auto font = assets->getFont(fontName);
batch->color = getColor(); batch->color = getColor();
Font* font = assets->getFont(fontName);
uint lineHeight = font->getLineHeight();
glm::vec2 size = getSize(); glm::vec2 size = getSize();
glm::vec2 newsize ( glm::vec2 newsize (
font->calcWidth(text), font->calcWidth(text),
font->getLineHeight()+font->getYOffset() (lines == 1 ? lineHeight : lineHeight*lineInterval)*lines + font->getYOffset()
); );
glm::vec2 coord = calcCoord(); glm::vec2 coord = calcCoord();
@@ -69,14 +105,48 @@ void Label::draw(const GfxContext* pctx, Assets* assets) {
coord.x += size.x-newsize.x; coord.x += size.x-newsize.x;
break; break;
} }
coord.y += (size.y-newsize.y)*0.5f; switch (valign) {
font->draw(batch, text, coord.x, coord.y); 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) { void Label::textSupplier(wstringsupplier supplier) {
this->supplier = supplier; this->supplier = supplier;
} }
void Label::setMultiline(bool multiline) {
this->multiline = multiline;
}
bool Label::isMultiline() const {
return multiline;
}
// ================================= Image ==================================== // ================================= Image ====================================
Image::Image(std::string texture, glm::vec2 size) : UINode(glm::vec2(), size), texture(texture) { Image::Image(std::string texture, glm::vec2 size) : UINode(glm::vec2(), size), texture(texture) {
setInteractive(false); setInteractive(false);
@@ -264,7 +334,7 @@ void TextBox::draw(const GfxContext* pctx, Assets* assets) {
if (!isFocused()) if (!isFocused())
return; return;
const int yoffset = 2; const int yoffset = 0;
const int lineHeight = font->getLineHeight(); const int lineHeight = font->getLineHeight();
glm::vec2 lcoord = label->calcCoord(); glm::vec2 lcoord = label->calcCoord();
auto batch = pctx->getBatch2D(); auto batch = pctx->getBatch2D();
@@ -390,6 +460,16 @@ bool TextBox::isValid() const {
return valid; 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) { void TextBox::setOnEditStart(runnable oneditstart) {
onEditStart = oneditstart; onEditStart = oneditstart;
} }
+36 -9
View File
@@ -24,19 +24,40 @@ namespace gui {
std::wstring text; std::wstring text;
std::string fontName; std::string fontName;
wstringsupplier supplier = nullptr; 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: public:
Label(std::string text, std::string fontName="normal"); Label(std::string text, std::string fontName="normal");
Label(std::wstring text, std::string fontName="normal"); Label(std::wstring text, std::string fontName="normal");
virtual void setText(std::wstring text); virtual void setText(std::wstring text);
std::wstring getText() const; const std::wstring& getText() const;
virtual void setFontName(std::string name); 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 draw(const GfxContext* pctx, Assets* assets) override;
virtual void textSupplier(wstringsupplier supplier); virtual void textSupplier(wstringsupplier supplier);
virtual void setMultiline(bool multiline);
virtual bool isMultiline() const;
}; };
class Image : public UINode { class Image : public UINode {
@@ -121,6 +142,8 @@ namespace gui {
size_t selectionEnd = 0; size_t selectionEnd = 0;
size_t selectionOrigin = 0; size_t selectionOrigin = 0;
bool multiline = false;
size_t normalizeIndex(int index); size_t normalizeIndex(int index);
int calcIndexAt(int x) const; int calcIndexAt(int x) const;
@@ -134,16 +157,9 @@ namespace gui {
TextBox(std::wstring placeholder, TextBox(std::wstring placeholder,
glm::vec4 padding=glm::vec4(4.0f)); glm::vec4 padding=glm::vec4(4.0f));
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> 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 setTextSupplier(wstringsupplier supplier);
virtual void setTextConsumer(wstringconsumer consumer); virtual void setTextConsumer(wstringconsumer consumer);
virtual void setTextValidator(wstringchecker validator); virtual void setTextValidator(wstringchecker validator);
virtual bool isFocuskeeper() const override {return true;}
virtual void setFocusedColor(glm::vec4 color); virtual void setFocusedColor(glm::vec4 color);
virtual glm::vec4 getFocusedColor() const; virtual glm::vec4 getFocusedColor() const;
virtual void setErrorColor(glm::vec4 color); virtual void setErrorColor(glm::vec4 color);
@@ -161,11 +177,22 @@ namespace gui {
virtual bool validate(); virtual bool validate();
virtual void setValid(bool valid); virtual void setValid(bool valid);
virtual bool isValid() const; 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 setOnEditStart(runnable oneditstart);
virtual void focus(GUI*) override; virtual void focus(GUI*) override;
virtual void refresh() override; virtual void refresh() override;
virtual void click(GUI*, int, int) override; virtual void click(GUI*, int, int) override;
virtual void mouseMove(GUI*, int x, int y) 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<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self) override;
}; };
class InputBindBox : public Panel { class InputBindBox : public Panel {
+19 -8
View File
@@ -17,6 +17,8 @@ static Align align_from_string(const std::string& str, Align def) {
if (str == "left") return Align::left; if (str == "left") return Align::left;
if (str == "center") return Align::center; if (str == "center") return Align::center;
if (str == "right") return Align::right; if (str == "right") return Align::right;
if (str == "top") return Align::top;
if (str == "bottom") return Align::bottom;
return def; return def;
} }
@@ -138,6 +140,11 @@ static std::shared_ptr<UINode> readLabel(UiXmlReader& reader, xml::xmlelement el
std::wstring text = readAndProcessInnerText(element); std::wstring text = readAndProcessInnerText(element);
auto label = std::make_shared<Label>(text); auto label = std::make_shared<Label>(text);
_readUINode(reader, element, *label); _readUINode(reader, element, *label);
if (element->has("valign")) {
label->setVerticalAlign(
align_from_string(element->attr("valign").getText(), label->getVerticalAlign())
);
}
return label; return label;
} }
@@ -163,7 +170,7 @@ static std::shared_ptr<UINode> readButton(UiXmlReader& reader, xml::xmlelement e
auto callback = scripting::create_runnable( auto callback = scripting::create_runnable(
reader.getEnvironment().getId(), reader.getEnvironment().getId(),
element->attr("onclick").getText(), element->attr("onclick").getText(),
reader.getFilename()+".lua" reader.getFilename()
); );
button->listenAction([callback](GUI*) { button->listenAction([callback](GUI*) {
callback(); callback();
@@ -188,7 +195,7 @@ static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, xml::xmlelement
auto consumer = scripting::create_bool_consumer( auto consumer = scripting::create_bool_consumer(
reader.getEnvironment().getId(), reader.getEnvironment().getId(),
element->attr("consumer").getText(), element->attr("consumer").getText(),
reader.getFilename()+".lua" reader.getFilename()
); );
checkbox->setConsumer(consumer); checkbox->setConsumer(consumer);
} }
@@ -197,7 +204,7 @@ static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, xml::xmlelement
auto supplier = scripting::create_bool_supplier( auto supplier = scripting::create_bool_supplier(
reader.getEnvironment().getId(), reader.getEnvironment().getId(),
element->attr("supplier").getText(), element->attr("supplier").getText(),
reader.getFilename()+".lua" reader.getFilename()
); );
checkbox->setSupplier(supplier); checkbox->setSupplier(supplier);
} }
@@ -210,12 +217,16 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, xml::xmlelement
auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f)); auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f));
_readPanel(reader, element, *textbox); _readPanel(reader, element, *textbox);
textbox->setText(text); textbox->setText(text);
if (element->has("multiline")) {
textbox->setMultiline(element->attr("multiline").asBool());
}
if (element->has("consumer")) { if (element->has("consumer")) {
auto consumer = scripting::create_wstring_consumer( auto consumer = scripting::create_wstring_consumer(
reader.getEnvironment().getId(), reader.getEnvironment().getId(),
element->attr("consumer").getText(), element->attr("consumer").getText(),
reader.getFilename()+".lua" reader.getFilename()
); );
textbox->setTextConsumer(consumer); textbox->setTextConsumer(consumer);
} }
@@ -224,7 +235,7 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, xml::xmlelement
auto supplier = scripting::create_wstring_supplier( auto supplier = scripting::create_wstring_supplier(
reader.getEnvironment().getId(), reader.getEnvironment().getId(),
element->attr("consumer").getText(), element->attr("consumer").getText(),
reader.getFilename()+".lua" reader.getFilename()
); );
textbox->setTextSupplier(supplier); textbox->setTextSupplier(supplier);
} }
@@ -238,7 +249,7 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, xml::xmlelement
auto validator = scripting::create_wstring_validator( auto validator = scripting::create_wstring_validator(
reader.getEnvironment().getId(), reader.getEnvironment().getId(),
element->attr("validator").getText(), element->attr("validator").getText(),
reader.getFilename()+".lua" reader.getFilename()
); );
textbox->setTextValidator(validator); textbox->setTextValidator(validator);
} }
@@ -265,7 +276,7 @@ static std::shared_ptr<UINode> readTrackBar(UiXmlReader& reader, xml::xmlelement
auto consumer = scripting::create_number_consumer( auto consumer = scripting::create_number_consumer(
reader.getEnvironment().getId(), reader.getEnvironment().getId(),
element->attr("consumer").getText(), element->attr("consumer").getText(),
reader.getFilename()+".lua" reader.getFilename()
); );
bar->setConsumer(consumer); bar->setConsumer(consumer);
} }
@@ -273,7 +284,7 @@ static std::shared_ptr<UINode> readTrackBar(UiXmlReader& reader, xml::xmlelement
auto supplier = scripting::create_number_supplier( auto supplier = scripting::create_number_supplier(
reader.getEnvironment().getId(), reader.getEnvironment().getId(),
element->attr("supplier").getText(), element->attr("supplier").getText(),
reader.getFilename()+".lua" reader.getFilename()
); );
bar->setSupplier(supplier); bar->setSupplier(supplier);
} }
+65 -56
View File
@@ -2,7 +2,9 @@
#include "Texture.h" #include "Texture.h"
#include "Batch2D.h" #include "Batch2D.h"
using glm::vec4; inline constexpr uint GLYPH_SIZE = 16;
inline constexpr uint MAX_CODEPAGES = 10000; // idk ho many codepages unicode has
inline constexpr glm::vec4 SHADOW_TINT(0.0f, 0.0f, 0.0f, 1.0f);
Font::Font(std::vector<std::unique_ptr<Texture>> pages, int lineHeight, int yoffset) Font::Font(std::vector<std::unique_ptr<Texture>> pages, int lineHeight, int yoffset)
: lineHeight(lineHeight), yoffset(yoffset), pages(std::move(pages)) { : lineHeight(lineHeight), yoffset(yoffset), pages(std::move(pages)) {
@@ -16,71 +18,78 @@ int Font::getYOffset() const {
} }
int Font::getLineHeight() const { int Font::getLineHeight() const {
return lineHeight; return lineHeight;
} }
bool Font::isPrintableChar(int c) { bool Font::isPrintableChar(uint codepoint) const {
switch (c){ switch (codepoint){
case ' ': case ' ':
case '\t': case '\t':
case '\n': case '\n':
case '\f': case '\f':
case '\r': case '\r':
return false; return false;
default: default:
return true; return true;
} }
} }
const int RES = 16;
int Font::calcWidth(std::wstring text, size_t length) { int Font::calcWidth(std::wstring text, size_t length) {
return std::min(text.length(), length) * 8; return std::min(text.length(), length) * 8;
} }
void Font::draw(Batch2D* batch, std::wstring text, int x, int y) { void Font::draw(Batch2D* batch, std::wstring text, int x, int y) {
draw(batch, text, x, y, STYLE_NONE); draw(batch, text, x, y, FontStyle::none);
} }
void Font::draw(Batch2D* batch, std::wstring text, int x, int y, int style) { static inline void drawGlyph(Batch2D* batch, int x, int y, uint c, FontStyle style) {
int page = 0; switch (style){
int next = 10000; case FontStyle::shadow:
int init_x = x; batch->sprite(x+1, y+1, GLYPH_SIZE, GLYPH_SIZE, 16, c, SHADOW_TINT);
do { break;
for (unsigned c : text){ case FontStyle::outline:
if (isPrintableChar(c)){ for (int oy = -1; oy <= 1; oy++){
int charpage = c >> 8; for (int ox = -1; ox <= 1; ox++){
if (charpage == page){ if (ox || oy) {
Texture* texture = pages[charpage].get(); batch->sprite(x+ox, y+oy, GLYPH_SIZE, GLYPH_SIZE, 16, c, SHADOW_TINT);
if (texture == nullptr){ }
texture = pages[0].get(); }
} }
batch->texture(texture); break;
}
batch->sprite(x, y, GLYPH_SIZE, GLYPH_SIZE, 16, c, batch->color);
}
switch (style){ void Font::draw(Batch2D* batch, std::wstring text, int x, int y, FontStyle style) {
case STYLE_SHADOW: draw(batch, std::wstring_view(text.c_str(), text.length()), x, y, style);
batch->sprite(x+1, y+1, RES, RES, 16, c, vec4(0.0f, 0.0f, 0.0f, 1.0f)); }
break;
case STYLE_OUTLINE:
for (int oy = -1; oy <= 1; oy++){
for (int ox = -1; ox <= 1; ox++){
if (ox || oy)
batch->sprite(x+ox, y+oy, RES, RES, 16, c, vec4(0.0f, 0.0f, 0.0f, 1.0f));
}
}
break;
}
batch->sprite(x, y, RES, RES, 16, c, batch->color); void Font::draw(Batch2D* batch, std::wstring_view text, int x, int y, FontStyle style) {
} uint page = 0;
else if (charpage > page && charpage < next){ uint next = MAX_CODEPAGES;
next = charpage; int init_x = x;
} do {
} for (uint c : text){
x += 8;//getGlyphWidth(c); if (!isPrintableChar(c)) {
} x += 8;
page = next; continue;
next = 10000; }
x = init_x; int charpage = c >> 8;
} while (page < 10000); if (charpage == page){
Texture* texture = pages[charpage].get();
if (texture == nullptr){
texture = pages[0].get();
}
batch->texture(texture);
drawGlyph(batch, x, y, c, style);
}
else if (charpage > page && charpage < next){
next = charpage;
}
x += 8;//getGlyphWidth(c);
}
page = next;
next = MAX_CODEPAGES;
x = init_x;
} while (page < MAX_CODEPAGES);
} }
+15 -13
View File
@@ -9,25 +9,27 @@
class Texture; class Texture;
class Batch2D; class Batch2D;
const uint STYLE_NONE = 0; enum class FontStyle {
const uint STYLE_SHADOW = 1; none,
const uint STYLE_OUTLINE = 2; shadow,
outline
};
class Font { class Font {
int lineHeight; int lineHeight;
int yoffset; int yoffset;
public: public:
std::vector<std::unique_ptr<Texture>> pages; std::vector<std::unique_ptr<Texture>> pages;
Font(std::vector<std::unique_ptr<Texture>> pages, int lineHeight, int yoffset); Font(std::vector<std::unique_ptr<Texture>> pages, int lineHeight, int yoffset);
~Font(); ~Font();
int getLineHeight() const; int getLineHeight() const;
int getYOffset() const; int getYOffset() const;
int calcWidth(std::wstring text, size_t length=-1); int calcWidth(std::wstring text, size_t length=-1);
// int getGlyphWidth(char c); bool isPrintableChar(uint codepoint) const;
bool isPrintableChar(int c); void draw(Batch2D* batch, std::wstring text, int x, int y);
void draw(Batch2D* batch, std::wstring text, int x, int y); void draw(Batch2D* batch, std::wstring text, int x, int y, FontStyle style);
void draw(Batch2D* batch, std::wstring text, int x, int y, int style); void draw(Batch2D* batch, std::wstring_view text, int x, int y, FontStyle style);
}; };
#endif /* GRAPHICS_FONT_H_ */ #endif /* GRAPHICS_FONT_H_ */
+14
View File
@@ -292,6 +292,10 @@ double util::parse_double(const std::string& str, size_t offset, size_t len) {
return parse_double(str.substr(offset, len)); return parse_double(str.substr(offset, len));
} }
/// @brief Split string by delimiter
/// @param str source string
/// @param delimiter split delimiter size
/// @return vector of string parts, containing at least one string
std::vector<std::string> util::split(const std::string& str, char delimiter) { std::vector<std::string> util::split(const std::string& str, char delimiter) {
std::vector<std::string> result; std::vector<std::string> result;
std::stringstream ss(str); std::stringstream ss(str);
@@ -299,9 +303,16 @@ std::vector<std::string> util::split(const std::string& str, char delimiter) {
while (std::getline(ss, tmp, delimiter)) { while (std::getline(ss, tmp, delimiter)) {
result.push_back(tmp); result.push_back(tmp);
} }
if (result.empty()) {
result.push_back("");
}
return result; return result;
} }
/// @brief Split wstring by delimiter
/// @param str source string
/// @param delimiter split delimiter size
/// @return vector of string parts, containing at least one string
std::vector<std::wstring> util::split(const std::wstring& str, char delimiter) { std::vector<std::wstring> util::split(const std::wstring& str, char delimiter) {
std::vector<std::wstring> result; std::vector<std::wstring> result;
std::wstringstream ss(str); std::wstringstream ss(str);
@@ -309,5 +320,8 @@ std::vector<std::wstring> util::split(const std::wstring& str, char delimiter) {
while (std::getline(ss, tmp, static_cast<wchar_t>(delimiter))) { while (std::getline(ss, tmp, static_cast<wchar_t>(delimiter))) {
result.push_back(tmp); result.push_back(tmp);
} }
if (result.empty()) {
result.push_back(L"");
}
return result; return result;
} }