add markdown dialect (WIP) & add strikethrough and underline font styles

This commit is contained in:
MihailRis
2024-12-06 17:35:03 +03:00
parent 605d7e7897
commit 80e809f97f
15 changed files with 296 additions and 58 deletions
+31 -9
View File
@@ -101,7 +101,7 @@ static inline void draw_text(
const glm::vec3& pos,
const glm::vec3& right,
const glm::vec3& up,
float glyphInterval,
float interval,
const FontStylesScheme* styles,
size_t styleMapOffset
) {
@@ -115,6 +115,7 @@ static inline void draw_text(
uint next = MAX_CODEPAGES;
int x = 0;
int y = 0;
bool hasLines = false;
do {
for (size_t i = 0; i < text.length(); i++) {
@@ -123,6 +124,9 @@ static inline void draw_text(
std::min(styles->map.size() - 1, i + styleMapOffset)
);
const FontStyle& style = styles->palette.at(styleIndex);
hasLines |= style.strikethrough;
hasLines |= style.underline;
if (!font.isPrintableChar(c)) {
x++;
continue;
@@ -131,14 +135,7 @@ static inline void draw_text(
if (charpage == page){
batch.texture(font.getPage(charpage));
draw_glyph(
batch,
pos,
glm::vec2(x, y),
c,
right,
up,
glyphInterval,
style
batch, pos, glm::vec2(x, y), c, right, up, interval, style
);
}
else if (charpage > page && charpage < next){
@@ -150,6 +147,31 @@ static inline void draw_text(
next = MAX_CODEPAGES;
x = 0;
} while (page < MAX_CODEPAGES);
if (!hasLines) {
return;
}
batch.texture(font.getPage(0));
for (size_t i = 0; i < text.length(); i++) {
uint c = text[i];
size_t styleIndex = styles->map.at(
std::min(styles->map.size() - 1, i + styleMapOffset)
);
const FontStyle& style = styles->palette.at(styleIndex);
FontStyle lineStyle = style;
lineStyle.bold = true;
if (style.strikethrough) {
draw_glyph(
batch, pos, glm::vec2(x, y), '-', right, up, interval, lineStyle
);
}
if (style.underline) {
draw_glyph(
batch, pos, glm::vec2(x, y), '_', right, up, interval, lineStyle
);
}
x++;
}
}
const Texture* Font::getPage(int charpage) const {
+18
View File
@@ -14,7 +14,25 @@ class Camera;
struct FontStyle {
bool bold = false;
bool italic = false;
bool strikethrough = false;
bool underline = false;
glm::vec4 color {1, 1, 1, 1};
FontStyle() = default;
FontStyle(
bool bold,
bool italic,
bool strikethrough,
bool underline,
glm::vec4 color
)
: bold(bold),
italic(italic),
strikethrough(strikethrough),
underline(underline),
color(std::move(color)) {
}
};
struct FontStylesScheme {
+17 -2
View File
@@ -6,6 +6,7 @@
#include "graphics/core/Font.hpp"
#include "assets/Assets.hpp"
#include "util/stringutil.hpp"
#include "../markdown.hpp"
using namespace gui;
@@ -80,11 +81,16 @@ glm::vec2 Label::calcSize() {
);
}
void Label::setText(const std::wstring& text) {
void Label::setText(std::wstring text) {
if (isMarkdown()) {
auto [processedText, styles] = markdown::process(text, true);
text = std::move(processedText);
setStyles(std::move(styles));
}
if (text == this->text && !cache.resetFlag) {
return;
}
this->text = text;
this->text = std::move(text);
cache.update(this->text, multiline, textWrap);
if (cache.font && autoresize) {
@@ -242,6 +248,15 @@ bool Label::isTextWrapping() const {
return textWrap;
}
void Label::setMarkdown(bool flag) {
markdown = flag;
setText(text);
}
bool Label::isMarkdown() const {
return markdown;
}
void Label::setStyles(std::unique_ptr<FontStylesScheme> styles) {
this->styles = std::move(styles);
}
+7 -1
View File
@@ -53,6 +53,9 @@ namespace gui {
/// @brief Auto resize label to fit text
bool autoresize = false;
/// @brief Enable text markdown
bool markdown = false;
std::unique_ptr<FontStylesScheme> styles;
public:
Label(const std::string& text, std::string fontName="normal");
@@ -60,7 +63,7 @@ namespace gui {
virtual ~Label();
virtual void setText(const std::wstring& text);
virtual void setText(std::wstring text);
const std::wstring& getText() const;
virtual void setFontName(std::string name);
@@ -113,6 +116,9 @@ namespace gui {
virtual void setTextWrapping(bool flag);
virtual bool isTextWrapping() const;
virtual void setMarkdown(bool flag);
virtual bool isMarkdown() const;
virtual void setStyles(std::unique_ptr<FontStylesScheme> styles);
};
}
+21 -1
View File
@@ -13,6 +13,7 @@
#include "util/stringutil.hpp"
#include "window/Events.hpp"
#include "window/Window.hpp"
#include "../markdown.hpp"
using namespace gui;
@@ -191,7 +192,18 @@ void TextBox::drawBackground(const DrawContext& pctx, const Assets&) {
void TextBox::refreshLabel() {
label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f));
label->setText(input.empty() && !hint.empty() ? hint : getText());
const auto& displayText = input.empty() && !hint.empty() ? hint : getText();
if (markdown) {
auto [processedText, styles] = markdown::process(displayText, !focused);
label->setText(std::move(processedText));
label->setStyles(std::move(styles));
} else {
label->setText(displayText);
if (syntax.empty()) {
label->setStyles(nullptr);
}
}
if (showLineNumbers) {
if (lineNumbersLabel->getLinesNumber() != label->getLinesNumber()) {
@@ -846,3 +858,11 @@ void TextBox::setSyntax(const std::string& lang) {
refreshSyntax();
}
}
void TextBox::setMarkdown(bool flag) {
markdown = flag;
}
bool TextBox::isMarkdown() const {
return markdown;
}
+4
View File
@@ -56,6 +56,7 @@ namespace gui {
bool editable = true;
bool autoresize = false;
bool showLineNumbers = false;
bool markdown = false;
std::string syntax;
@@ -227,5 +228,8 @@ namespace gui {
virtual void setOnDownPressed(const runnable &callback);
virtual void setSyntax(const std::string& lang);
virtual void setMarkdown(bool flag);
virtual bool isMarkdown() const;
};
}
+6
View File
@@ -278,6 +278,9 @@ static std::shared_ptr<UINode> readLabel(
if (element.has("text-wrap")) {
label->setTextWrapping(element.attr("text-wrap").asBool());
}
if (element.has("markdown")) {
label->setMarkdown(element.attr("markdown").asBool());
}
return label;
}
@@ -381,6 +384,9 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
if (element.has("line-numbers")) {
textbox->setShowLineNumbers(element.attr("line-numbers").asBool());
}
if (element.has("markdown")) {
textbox->setMarkdown(element.attr("markdown").asBool());
}
if (element.has("consumer")) {
textbox->setTextConsumer(scripting::create_wstring_consumer(
reader.getEnvironment(),
+109
View File
@@ -0,0 +1,109 @@
#include "markdown.hpp"
#include <sstream>
#include "graphics/core/Font.hpp"
using namespace markdown;
template <typename CharT>
static inline void emit(
CharT c, FontStylesScheme& styles, std::basic_stringstream<CharT>& ss
) {
ss << c;
styles.map.emplace_back(styles.palette.size()-1);
}
template <typename CharT>
static inline void emit_md(
CharT c, FontStylesScheme& styles, std::basic_stringstream<CharT>& ss
) {
ss << c;
styles.map.emplace_back(0);
}
template <typename CharT>
static inline void restyle(
CharT c,
FontStyle& style,
FontStylesScheme& styles,
std::basic_stringstream<CharT>& ss,
int& pos,
bool eraseMarkdown
) {
styles.palette.push_back(style);
if (!eraseMarkdown) {
emit_md(c, styles, ss);
}
pos++;
}
template <typename CharT>
Result<CharT> process_markdown(
std::basic_string_view<CharT> source, bool eraseMarkdown
) {
std::basic_stringstream<CharT> ss;
FontStylesScheme styles {
// markdown default
{{false, false, false, false, glm::vec4(1,1,1,0.5f)}, {}},
{}
};
FontStyle style;
int pos = 0;
while (pos < source.size()) {
CharT first = source[pos];
if (first == '\\') {
if (pos + 1 < source.size()) {
CharT second = source[++pos];
switch (second) {
case '*':
case '_':
case '~':
if (!eraseMarkdown) {
emit_md(first, styles, ss);
}
emit(second, styles, ss);
pos++;
continue;
}
}
} else if (first == '*') {
if (pos + 1 < source.size() && source[pos+1] == '*') {
pos++;
if (!eraseMarkdown)
emit_md(first, styles, ss);
style.bold = !style.bold;
restyle(first, style, styles, ss, pos, eraseMarkdown);
continue;
}
style.italic = !style.italic;
restyle(first, style, styles, ss, pos, eraseMarkdown);
continue;
} else if (first == '_' && pos + 1 < source.size() && source[pos+1] == '_') {
pos++;
if (!eraseMarkdown)
emit_md(first, styles, ss);
style.underline = !style.underline;
restyle(first, style, styles, ss, pos, eraseMarkdown);
continue;
} else if (first == '~' && pos + 1 < source.size() && source[pos+1] == '~') {
pos++;
if (!eraseMarkdown)
emit_md(first, styles, ss);
style.strikethrough = !style.strikethrough;
restyle(first, style, styles, ss, pos, eraseMarkdown);
continue;
}
emit(first, styles, ss);
pos++;
}
return {ss.str(), std::make_unique<FontStylesScheme>(std::move(styles))};
}
Result<char> markdown::process(std::string_view source, bool eraseMarkdown) {
return process_markdown(source, eraseMarkdown);
}
Result<wchar_t> markdown::process(std::wstring_view source, bool eraseMarkdown) {
return process_markdown(source, eraseMarkdown);
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <string>
#include <memory>
struct FontStylesScheme;
// VoxelCore Markdown dialect
namespace markdown {
template <typename CharT>
struct Result {
/// @brief Text with erased markdown
std::basic_string<CharT> text;
/// @brief Text styles scheme
std::unique_ptr<FontStylesScheme> styles;
};
Result<char> process(std::string_view source, bool eraseMarkdown);
Result<wchar_t> process(std::wstring_view source, bool eraseMarkdown);
}