aboutsummaryrefslogtreecommitdiff
path: root/src/Text.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Text.cpp')
-rw-r--r--src/Text.cpp590
1 files changed, 350 insertions, 240 deletions
diff --git a/src/Text.cpp b/src/Text.cpp
index 14aebb1..69d6664 100644
--- a/src/Text.cpp
+++ b/src/Text.cpp
@@ -3,13 +3,13 @@
#include "../include/Config.hpp"
#include "../include/Theme.hpp"
#include "../generated/Emoji.hpp"
-#include <SFML/Graphics/RectangleShape.hpp>
-#include <SFML/Window/Clipboard.hpp>
-#include <SFML/Window/Event.hpp>
-#include <SFML/Graphics/RenderTarget.hpp>
-#include <SFML/Graphics/Font.hpp>
+#include <mglpp/graphics/Rectangle.hpp>
+#include <mglpp/window/Event.hpp>
+#include <mglpp/window/Window.hpp>
+#include <mglpp/graphics/Font.hpp>
+#include <mglpp/graphics/Texture.hpp>
+#include <mglpp/system/Utf8.hpp>
#include <cmath>
-#include <functional>
namespace QuickMedia
{
@@ -21,19 +21,8 @@ namespace QuickMedia
static const size_t FONT_INDEX_SYMBOLS = 2;
static const size_t FONT_INDEX_EMOJI = 3;
static const size_t FONT_ARRAY_SIZE = 4;
-
- size_t StringViewUtf32::find(const StringViewUtf32 &other, size_t offset) const {
- if(offset >= size)
- return -1;
-
- auto it = std::search(data + offset, data + size - offset, std::boyer_moore_searcher(other.data, other.data + other.size));
- if(it != data + size)
- return it - data;
-
- return -1;
- }
- Text::Text(sf::String _str, bool bold_font, unsigned int characterSize, float maxWidth, bool highlight_urls) :
+ Text::Text(std::string _str, bool bold_font, unsigned int characterSize, float maxWidth, bool highlight_urls) :
bold_font(bold_font),
characterSize(characterSize),
maxWidth(maxWidth),
@@ -50,52 +39,54 @@ namespace QuickMedia
caretIndex(0),
caret_offset_x(0.0f)
{
- for(size_t i = 0; i < FONT_ARRAY_SIZE; ++i) {
- vertices[i].setPrimitiveType(sf::PrimitiveType::Triangles);
- vertex_buffers[i] = sf::VertexBuffer(sf::PrimitiveType::Triangles, sf::VertexBuffer::Static);
- }
setString(std::move(_str));
}
- void Text::setString(const sf::String &str)
+ void Text::setString(std::string str)
{
//if(str != this->str)
//{
- size_t prev_str_size = this->str.getSize();
- this->str = str;
+ size_t prev_str_size = this->str.size();
+ this->str = std::move(str);
dirty = true;
dirtyText = true;
- if((int)this->str.getSize() < caretIndex || prev_str_size == 0)
+ if((int)this->str.size() < caretIndex || prev_str_size == 0)
{
- caretIndex = this->str.getSize();
+ caretIndex = this->str.size();
dirtyCaret = true;
}
// }
}
- const sf::String& Text::getString() const
+ const std::string& Text::getString() const
{
return str;
}
- void Text::appendText(const sf::String &str) {
+ void Text::appendText(const std::string &str) {
this->str += str;
dirty = true;
dirtyText = true;
}
+
+ void Text::insert_text_at_caret_position(const std::string &str) {
+ this->str.insert(caretIndex, str);
+ dirty = true;
+ dirtyText = true;
+ }
- void Text::setPosition(float x, float y)
+ void Text::set_position(float x, float y)
{
position.x = x;
position.y = y;
}
- void Text::setPosition(const sf::Vector2f &position)
+ void Text::set_position(const mgl::vec2f &position)
{
this->position = position;
}
- sf::Vector2f Text::getPosition() const
+ mgl::vec2f Text::get_position() const
{
return position;
}
@@ -106,7 +97,7 @@ namespace QuickMedia
if(std::abs(maxWidth - this->maxWidth) > 1.0f)
{
this->maxWidth = maxWidth;
- if(num_lines > 1 || maxWidth < boundingBox.width) {
+ if(num_lines > 1 || maxWidth < boundingBox.size.x) {
dirty = true;
dirtyCaret = true;
}
@@ -127,13 +118,13 @@ namespace QuickMedia
}
}
- unsigned int Text::getCharacterSize() const
+ unsigned int Text::get_character_size() const
{
return characterSize;
}
- void Text::replace(size_t start_index, size_t length, const sf::String &insert_str) {
- int string_diff = (int)insert_str.getSize() - (int)length;
+ void Text::replace(size_t start_index, size_t length, const std::string &insert_str) {
+ int string_diff = (int)insert_str.size() - (int)length;
str.replace(start_index, length, insert_str);
dirty = true;
dirtyText = true;
@@ -147,7 +138,7 @@ namespace QuickMedia
return caretIndex;
}
- void Text::setFillColor(sf::Color color)
+ void Text::set_color(mgl::Color color)
{
if(color != this->color)
{
@@ -197,16 +188,16 @@ namespace QuickMedia
float Text::getWidth() const
{
- return boundingBox.width;
+ return boundingBox.size.x;
}
float Text::getHeight() const
{
- return boundingBox.height;
+ return boundingBox.size.y;
}
// TODO: Is there a more efficient way to do this? maybe japanese characters have a specific bit-pattern?
- static bool is_japanese_codepoint(sf::Uint32 codepoint) {
+ static bool is_japanese_codepoint(uint32_t codepoint) {
return (codepoint >= 0x2E80 && codepoint <= 0x2FD5) // Kanji radicals
|| (codepoint >= 0x3000 && codepoint <= 0x303F) // Punctuation
|| (codepoint >= 0x3041 && codepoint <= 0x3096) // Hiragana
@@ -221,12 +212,12 @@ namespace QuickMedia
|| (codepoint >= 0xFF5F && codepoint <= 0xFF9F); // Katakana and punctuation (half width)
}
- static bool is_korean_codepoint(sf::Uint32 codepoint) {
+ static bool is_korean_codepoint(uint32_t codepoint) {
return codepoint >= 0xAC00 && codepoint <= 0xD7A3;
}
// TODO: Is there a more efficient way to do this? maybe chinese characters have a specific bit-pattern?
- static bool is_chinese_codepoint(sf::Uint32 codepoint) {
+ static bool is_chinese_codepoint(uint32_t codepoint) {
return (codepoint >= 0x4E00 && codepoint <= 0x9FFF) // CJK Unified Ideographs
|| (codepoint >= 0x3400 && codepoint <= 0x4DBF) // CJK Unified Ideographs Extension A
|| (codepoint >= 0x20000 && codepoint <= 0x2A6DF) // CJK Unified Ideographs Extension B
@@ -238,44 +229,84 @@ namespace QuickMedia
}
// TODO: Merge chinese, japanese and korean codepoints into one function since they share ranges
- static bool is_cjk_codepoint(sf::Uint32 codepoint) {
+ static bool is_cjk_codepoint(uint32_t codepoint) {
return is_chinese_codepoint(codepoint) || is_japanese_codepoint(codepoint) || is_korean_codepoint(codepoint);
}
- static bool is_symbol_codepoint(sf::Uint32 codepoint) {
+ static bool is_symbol_codepoint(uint32_t codepoint) {
// TODO: Add the remaining NotoSansSymbols2-Regular.ttf codepoints as well.
// See codepoint ranges with: fc-query --format='%{charset}\n' /usr/share/fonts/noto/NotoSansSymbols2-Regular.ttf.
return codepoint >= 0x2800 && codepoint <= 0x28FF; // Braille
}
- static size_t find_end_of_cjk(const sf::Uint32 *str, size_t size) {
- for(size_t i = 0; i < size; ++i) {
- if(!is_cjk_codepoint(str[i]))
+ static size_t find_end_of_cjk(const char *str, size_t size) {
+ for(size_t i = 0; i < size;) {
+ const unsigned char *cp = (const unsigned char*)&str[i];
+ uint32_t codepoint;
+ size_t clen;
+ if(!mgl::utf8_decode(cp, size - i, &codepoint, &clen)) {
+ codepoint = *cp;
+ clen = 1;
+ }
+
+ if(!is_cjk_codepoint(codepoint))
return i;
+
+ i += clen;
}
return size;
}
- static size_t find_end_of_emoji(const sf::Uint32 *str, size_t size) {
- for(size_t i = 0; i < size; ++i) {
- if(!codepoint_is_emoji(str[i]))
+ static size_t find_end_of_emoji(const char *str, size_t size) {
+ for(size_t i = 0; i < size;) {
+ const unsigned char *cp = (const unsigned char*)&str[i];
+ uint32_t codepoint;
+ size_t clen;
+ if(!mgl::utf8_decode(cp, size - i, &codepoint, &clen)) {
+ codepoint = *cp;
+ clen = 1;
+ }
+
+ if(!codepoint_is_emoji(codepoint))
return i;
+
+ i += clen;
}
return size;
}
- static size_t find_end_of_symbol(const sf::Uint32 *str, size_t size) {
- for(size_t i = 0; i < size; ++i) {
- if(!is_symbol_codepoint(str[i]))
+ static size_t find_end_of_symbol(const char *str, size_t size) {
+ for(size_t i = 0; i < size;) {
+ const unsigned char *cp = (const unsigned char*)&str[i];
+ uint32_t codepoint;
+ size_t clen;
+ if(!mgl::utf8_decode(cp, size - i, &codepoint, &clen)) {
+ codepoint = *cp;
+ clen = 1;
+ }
+
+ if(!is_symbol_codepoint(codepoint))
return i;
+
+ i += clen;
}
return size;
}
- static size_t find_end_latin(const sf::Uint32 *str, size_t size) {
- for(size_t i = 0; i < size; ++i) {
- if(is_cjk_codepoint(str[i]) || codepoint_is_emoji(str[i]) || is_symbol_codepoint(str[i]))
+ static size_t find_end_latin(const char *str, size_t size) {
+ for(size_t i = 0; i < size;) {
+ const unsigned char *cp = (const unsigned char*)&str[i];
+ uint32_t codepoint;
+ size_t clen;
+ if(!mgl::utf8_decode(cp, size - i, &codepoint, &clen)) {
+ codepoint = *cp;
+ clen = 1;
+ }
+
+ if(is_cjk_codepoint(codepoint) || codepoint_is_emoji(codepoint) || is_symbol_codepoint(codepoint))
return i;
+
+ i += clen;
}
return size;
}
@@ -283,30 +314,39 @@ namespace QuickMedia
void Text::splitTextByFont() {
textElements.clear();
size_t index = 0;
- size_t size = str.getSize();
+ size_t size = str.size();
while(index < size) {
+ const unsigned char *cp = (const unsigned char*)&str[index];
+ uint32_t codepoint;
+ size_t clen;
+ if(!mgl::utf8_decode(cp, size - index, &codepoint, &clen)) {
+ codepoint = *cp;
+ clen = 1;
+ }
+
size_t offset;
TextElement::TextType text_type = TextElement::TextType::LATIN;
- if(is_symbol_codepoint(str[index])) {
+ if(is_symbol_codepoint(codepoint)) {
text_type = TextElement::TextType::SYMBOL;
- offset = find_end_of_symbol(str.getData() + index + 1, size - index - 1);
- } else if(is_cjk_codepoint(str[index])) {
+ offset = find_end_of_symbol(str.data() + index, size - index);
+ } else if(is_cjk_codepoint(codepoint)) {
text_type = TextElement::TextType::CJK;
- offset = find_end_of_cjk(str.getData() + index + 1, size - index - 1);
- } else if(codepoint_is_emoji(str[index])) {
+ offset = find_end_of_cjk(str.data() + index, size - index);
+ } else if(codepoint_is_emoji(codepoint)) {
text_type = TextElement::TextType::EMOJI;
- offset = find_end_of_emoji(str.getData() + index + 1, size - index - 1);
+ offset = find_end_of_emoji(str.data() + index, size - index);
} else {
- offset = find_end_latin(str.getData() + index + 1, size - index - 1);
+ offset = find_end_latin(str.data() + index, size - index);
}
- textElements.push_back({ StringViewUtf32(str.getData() + index, offset + 1), TextElement::Type::TEXT });
+
+ textElements.push_back({ std::string_view(str.data() + index, offset), TextElement::Type::TEXT });
textElements.back().text_type = text_type;
- index += 1 + offset;
+ index += offset;
}
}
- float Text::font_get_real_height(sf::Font *font) {
- return font->getGlyph('|', characterSize, false).bounds.height + std::floor(4.0f * ((float)characterSize / (float)14.0f));
+ float Text::font_get_real_height(mgl::Font *font) {
+ return font->get_glyph('|').size.y + std::floor(4.0f * ((float)characterSize / (float)14.0f));
}
float Text::get_text_quad_left_side(const VertexRef &vertex_ref) const {
@@ -350,7 +390,7 @@ namespace QuickMedia
}
}
- sf::Uint32 Text::get_vertex_codepoint(int index) const {
+ uint32_t Text::get_vertex_codepoint(int index) const {
const int num_vertices = vertices_linear.size();
if(num_vertices == 0) {
return 0;
@@ -368,10 +408,7 @@ namespace QuickMedia
splitTextByFont();
// TODO: Optimize
if(highlight_urls) {
- auto u8 = str.toUtf8();
- std::string *u8_str = (std::string*)&u8;
- url_ranges = extract_urls(*u8_str);
- convert_utf8_to_utf32_ranges(*u8_str, url_ranges);
+ url_ranges = extract_urls(str);
} else {
url_ranges.clear();
}
@@ -384,81 +421,88 @@ namespace QuickMedia
vertices_linear.clear();
for(size_t i = 0; i < FONT_ARRAY_SIZE; ++i) {
vertices[i].clear();
- vertices[i].resize(0);
+ vertices[i].shrink_to_fit();
}
- boundingBox = sf::FloatRect();
+ boundingBox = mgl::FloatRect(mgl::vec2f(0.0f, 0.0f), mgl::vec2f(0.0f, 0.0f));
- sf::Font *latin_font;
+ mgl::Font *latin_font;
if(bold_font)
- latin_font = FontLoader::get_font(FontLoader::FontType::LATIN_BOLD);
+ latin_font = FontLoader::get_font(FontLoader::FontType::LATIN_BOLD, characterSize);
else
- latin_font = FontLoader::get_font(FontLoader::FontType::LATIN);
+ latin_font = FontLoader::get_font(FontLoader::FontType::LATIN, characterSize);
- const float latin_font_width = latin_font->getGlyph(' ', characterSize, false).advance;
+ const float latin_font_width = latin_font->get_glyph(' ').advance;
const float hspace = latin_font_width + characterSpacing;
const float vspace = font_get_real_height(latin_font);
const float emoji_scale = vspace / 20.0f;
- const sf::Color url_color = get_theme().url_text_color;
+ const mgl::Color url_color = get_theme().url_text_color;
size_t url_range_index = 0;
- sf::Vector2f glyphPos;
- sf::Uint32 prevCodePoint = 0;
+ mgl::vec2f glyphPos;
+ uint32_t prevCodePoint = 0;
// TODO: Only do this if dirtyText (then the Text object shouldn't be reset in Body. There should be a cleanup function in text instead)
for(usize textElementIndex = 0; textElementIndex < textElements.size(); ++textElementIndex)
{
TextElement &textElement = textElements[textElementIndex];
- const sf::Font *ff = latin_font;
+ mgl::Font *ff = latin_font;
int vertices_index = FONT_INDEX_LATIN;
prevCodePoint = 0;
if(textElement.text_type == TextElement::TextType::CJK) {
- ff = FontLoader::get_font(FontLoader::FontType::CJK);
+ ff = FontLoader::get_font(FontLoader::FontType::CJK, characterSize);
vertices_index = FONT_INDEX_CJK;
} else if(textElement.text_type == TextElement::TextType::SYMBOL) {
- ff = FontLoader::get_font(FontLoader::FontType::SYMBOLS);
+ ff = FontLoader::get_font(FontLoader::FontType::SYMBOLS, characterSize);
vertices_index = FONT_INDEX_SYMBOLS;
} else if(textElement.text_type == TextElement::TextType::EMOJI) {
vertices_index = FONT_INDEX_EMOJI;
- textElement.position = glyphPos;
- sf::Color emoji_color(255, 255, 255, color.a);
- for(size_t i = 0; i < textElement.text.size; ++i)
+ mgl::Color emoji_color(255, 255, 255, color.a);
+ for(size_t i = 0; i < textElement.text.size();)
{
- sf::Uint32 codePoint = textElement.text[i];
- int vertexStart = vertices[vertices_index].getVertexCount();
- EmojiRectangle emoji_rec = emoji_get_extents(codePoint);
-
+ const unsigned char *cp = (const unsigned char*)&textElement.text[i];
+ uint32_t codepoint;
+ size_t clen;
+ if(!mgl::utf8_decode(cp, textElement.text.size() - i, &codepoint, &clen)) {
+ codepoint = *cp;
+ clen = 1;
+ }
+
+ int vertexStart = vertices[vertices_index].size();
+ EmojiRectangle emoji_rec = emoji_get_extents(codepoint);
+
const float font_height_offset = std::floor(-vspace * 0.2f);
- sf::Vector2f vertexTopLeft(glyphPos.x, glyphPos.y + font_height_offset - std::floor(emoji_rec.height * emoji_scale) * 0.5f);
- sf::Vector2f vertexTopRight(glyphPos.x + std::floor(emoji_rec.width * emoji_scale), glyphPos.y + font_height_offset - std::floor(emoji_rec.height * emoji_scale) * 0.5f);
- sf::Vector2f vertexBottomLeft(glyphPos.x, glyphPos.y + font_height_offset + emoji_rec.height * emoji_scale * 0.5f);
- sf::Vector2f vertexBottomRight(glyphPos.x + std::floor(emoji_rec.width * emoji_scale), glyphPos.y + font_height_offset + std::floor(emoji_rec.height * emoji_scale) * 0.5f);
+ mgl::vec2f vertexTopLeft(glyphPos.x, glyphPos.y + font_height_offset - std::floor(emoji_rec.height * emoji_scale) * 0.5f);
+ mgl::vec2f vertexTopRight(glyphPos.x + std::floor(emoji_rec.width * emoji_scale), glyphPos.y + font_height_offset - std::floor(emoji_rec.height * emoji_scale) * 0.5f);
+ mgl::vec2f vertexBottomLeft(glyphPos.x, glyphPos.y + font_height_offset + emoji_rec.height * emoji_scale * 0.5f);
+ mgl::vec2f vertexBottomRight(glyphPos.x + std::floor(emoji_rec.width * emoji_scale), glyphPos.y + font_height_offset + std::floor(emoji_rec.height * emoji_scale) * 0.5f);
- sf::Vector2f textureTopLeft(emoji_rec.x, emoji_rec.y);
- sf::Vector2f textureTopRight(emoji_rec.x + emoji_rec.width, emoji_rec.y);
- sf::Vector2f textureBottomLeft(emoji_rec.x, emoji_rec.y + emoji_rec.height);
- sf::Vector2f textureBottomRight(emoji_rec.x + emoji_rec.width, emoji_rec.y + emoji_rec.height);
+ mgl::vec2f textureTopLeft(emoji_rec.x, emoji_rec.y);
+ mgl::vec2f textureTopRight(emoji_rec.x + emoji_rec.width, emoji_rec.y);
+ mgl::vec2f textureBottomLeft(emoji_rec.x, emoji_rec.y + emoji_rec.height);
+ mgl::vec2f textureBottomRight(emoji_rec.x + emoji_rec.width, emoji_rec.y + emoji_rec.height);
- vertices[vertices_index].append({ vertexTopRight, emoji_color, textureTopRight });
- vertices[vertices_index].append({ vertexTopLeft, emoji_color, textureTopLeft });
- vertices[vertices_index].append({ vertexBottomLeft, emoji_color, textureBottomLeft });
- vertices[vertices_index].append({ vertexBottomLeft, emoji_color, textureBottomLeft });
- vertices[vertices_index].append({ vertexBottomRight, emoji_color, textureBottomRight });
- vertices[vertices_index].append({ vertexTopRight, emoji_color, textureTopRight });
+ vertices[vertices_index].emplace_back(vertexTopRight, textureTopRight, emoji_color);
+ vertices[vertices_index].emplace_back(vertexTopLeft, textureTopLeft, emoji_color);
+ vertices[vertices_index].emplace_back(vertexBottomLeft, textureBottomLeft, emoji_color);
+ vertices[vertices_index].emplace_back(vertexBottomLeft, textureBottomLeft, emoji_color);
+ vertices[vertices_index].emplace_back(vertexBottomRight, textureBottomRight, emoji_color);
+ vertices[vertices_index].emplace_back(vertexTopRight, textureTopRight, emoji_color);
glyphPos.x += std::floor(emoji_rec.width * emoji_scale) + characterSpacing;
- vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint});
+ vertices_linear.push_back({vertices_index, vertexStart, 0, codepoint});
+
+ i += clen;
}
continue;
}
- //vertices[vertices_index].resize(vertices[vertices_index].getVertexCount() + 4 * textElement.text.size); // TODO: Precalculate
- textElement.position = glyphPos;
- for(size_t i = 0; i < textElement.text.size; ++i)
+ //vertices[vertices_index].resize(vertices[vertices_index].size() + 4 * textElement.text.size); // TODO: Precalculate
+ for(size_t i = 0; i < textElement.text.size();)
{
- sf::Color text_color = color;
+ mgl::Color text_color = color;
if(url_range_index < url_ranges.size()) {
- size_t string_offset = (textElement.text.data + i) - str.getData();
+ size_t string_offset = (textElement.text.data() + i) - str.data();
if(string_offset >= url_ranges[url_range_index].start && string_offset < url_ranges[url_range_index].start + url_ranges[url_range_index].length) {
text_color = url_color;
text_color.a = color.a;
@@ -467,96 +511,107 @@ namespace QuickMedia
}
}
- sf::Uint32 codePoint = textElement.text[i];
+ const unsigned char *cp = (const unsigned char*)&textElement.text[i];
+ uint32_t codepoint;
+ size_t clen;
+ if(!mgl::utf8_decode(cp, textElement.text.size() - i, &codepoint, &clen)) {
+ codepoint = *cp;
+ clen = 1;
+ }
// TODO: Make this work when combining multiple different fonts (for example latin and japanese).
// For japanese we could use a hack, because all japanese characters are monospace (exception being half-width characters).
- float kerning = ff->getKerning(prevCodePoint, codePoint, characterSize);
- prevCodePoint = codePoint;
+ // TODO:
+ float kerning = ff->get_kerning(prevCodePoint, codepoint);
+ //float kerning = 0.0f;
+ prevCodePoint = codepoint;
glyphPos.x += kerning;
- int vertexStart = vertices[vertices_index].getVertexCount();
+ int vertexStart = vertices[vertices_index].size();
- switch(codePoint)
+ i += clen;
+ switch(codepoint)
{
case ' ':
{
- sf::Vector2f vertexTopLeft(glyphPos.x, glyphPos.y - vspace);
- sf::Vector2f vertexTopRight(glyphPos.x + hspace, glyphPos.y - vspace);
- sf::Vector2f vertexBottomLeft(glyphPos.x, glyphPos.y);
- sf::Vector2f vertexBottomRight(glyphPos.x + hspace, glyphPos.y);
-
- vertices[vertices_index].append({ vertexTopRight, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexTopLeft, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexBottomLeft, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexBottomLeft, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexBottomRight, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexTopRight, sf::Color::Transparent, sf::Vector2f() });
+ mgl::vec2f vertexTopLeft(glyphPos.x, glyphPos.y - vspace);
+ mgl::vec2f vertexTopRight(glyphPos.x + hspace, glyphPos.y - vspace);
+ mgl::vec2f vertexBottomLeft(glyphPos.x, glyphPos.y);
+ mgl::vec2f vertexBottomRight(glyphPos.x + hspace, glyphPos.y);
+
+ vertices[vertices_index].emplace_back(vertexTopRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexTopLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexBottomLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexBottomLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexBottomRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexTopRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
glyphPos.x += hspace;
- vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint});
- continue;
+ vertices_linear.push_back({vertices_index, vertexStart, 0, codepoint});
+ break;
}
case '\t':
{
const float char_width = hspace * TAB_WIDTH;
- sf::Vector2f vertexTopLeft(glyphPos.x, glyphPos.y - vspace);
- sf::Vector2f vertexTopRight(glyphPos.x + char_width, glyphPos.y - vspace);
- sf::Vector2f vertexBottomLeft(glyphPos.x, glyphPos.y);
- sf::Vector2f vertexBottomRight(glyphPos.x + char_width, glyphPos.y);
-
- vertices[vertices_index].append({ vertexTopRight, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexTopLeft, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexBottomLeft, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexBottomLeft, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexBottomRight, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexTopRight, sf::Color::Transparent, sf::Vector2f() });
+ mgl::vec2f vertexTopLeft(glyphPos.x, glyphPos.y - vspace);
+ mgl::vec2f vertexTopRight(glyphPos.x + char_width, glyphPos.y - vspace);
+ mgl::vec2f vertexBottomLeft(glyphPos.x, glyphPos.y);
+ mgl::vec2f vertexBottomRight(glyphPos.x + char_width, glyphPos.y);
+
+ vertices[vertices_index].emplace_back(vertexTopRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexTopLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexBottomLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexBottomLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexBottomRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexTopRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
glyphPos.x += char_width;
- vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint});
- continue;
+ vertices_linear.push_back({vertices_index, vertexStart, 0, codepoint});
+ break;
}
case '\n':
{
- sf::Vector2f vertexTopLeft(glyphPos.x, glyphPos.y - vspace);
- sf::Vector2f vertexTopRight(glyphPos.x, glyphPos.y - vspace);
- sf::Vector2f vertexBottomLeft(glyphPos.x, glyphPos.y);
- sf::Vector2f vertexBottomRight(glyphPos.x, glyphPos.y);
-
- vertices[vertices_index].append({ vertexTopRight, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexTopLeft, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexBottomLeft, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexBottomLeft, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexBottomRight, sf::Color::Transparent, sf::Vector2f() });
- vertices[vertices_index].append({ vertexTopRight, sf::Color::Transparent, sf::Vector2f() });
+ mgl::vec2f vertexTopLeft(glyphPos.x, glyphPos.y - vspace);
+ mgl::vec2f vertexTopRight(glyphPos.x, glyphPos.y - vspace);
+ mgl::vec2f vertexBottomLeft(glyphPos.x, glyphPos.y);
+ mgl::vec2f vertexBottomRight(glyphPos.x, glyphPos.y);
+
+ vertices[vertices_index].emplace_back(vertexTopRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexTopLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexBottomLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexBottomLeft, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexBottomRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
+ vertices[vertices_index].emplace_back(vertexTopRight, mgl::vec2f(), mgl::Color(0, 0, 0, 0));
glyphPos.x = 0.0f;
glyphPos.y += floor(vspace + lineSpacing);
- vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint});
- continue;
+ vertices_linear.push_back({vertices_index, vertexStart, 0, codepoint});
+ break;
}
- }
+ default: {
+ mgl::FontGlyph glyph = ff->get_glyph(codepoint);
- const sf::Glyph &glyph = ff->getGlyph(codePoint, characterSize, false);
-
- sf::Vector2f vertexTopLeft(glyphPos.x + glyph.bounds.left, glyphPos.y + glyph.bounds.top);
- sf::Vector2f vertexTopRight(glyphPos.x + glyph.bounds.left + glyph.bounds.width, glyphPos.y + glyph.bounds.top);
- sf::Vector2f vertexBottomLeft(glyphPos.x + glyph.bounds.left, glyphPos.y + glyph.bounds.top + glyph.bounds.height);
- sf::Vector2f vertexBottomRight(glyphPos.x + glyph.bounds.left + glyph.bounds.width, glyphPos.y + glyph.bounds.top + glyph.bounds.height);
-
- sf::Vector2f textureTopLeft(glyph.textureRect.left, glyph.textureRect.top);
- sf::Vector2f textureTopRight(glyph.textureRect.left + glyph.textureRect.width, glyph.textureRect.top);
- sf::Vector2f textureBottomLeft(glyph.textureRect.left, glyph.textureRect.top + glyph.textureRect.height);
- sf::Vector2f textureBottomRight(glyph.textureRect.left + glyph.textureRect.width, glyph.textureRect.top + glyph.textureRect.height);
-
- vertices[vertices_index].append({ vertexTopRight, text_color, textureTopRight });
- vertices[vertices_index].append({ vertexTopLeft, text_color, textureTopLeft });
- vertices[vertices_index].append({ vertexBottomLeft, text_color, textureBottomLeft });
- vertices[vertices_index].append({ vertexBottomLeft, text_color, textureBottomLeft });
- vertices[vertices_index].append({ vertexBottomRight, text_color, textureBottomRight });
- vertices[vertices_index].append({ vertexTopRight, text_color, textureTopRight });
-
- glyphPos.x += glyph.advance + characterSpacing;
- vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint});
+ mgl::vec2f vertexTopLeft(glyphPos.x + glyph.position.x, glyphPos.y + glyph.position.y);
+ mgl::vec2f vertexTopRight(glyphPos.x + glyph.position.x + glyph.size.x, glyphPos.y + glyph.position.y);
+ mgl::vec2f vertexBottomLeft(glyphPos.x + glyph.position.x, glyphPos.y + glyph.position.y + glyph.size.y);
+ mgl::vec2f vertexBottomRight(glyphPos.x + glyph.position.x + glyph.size.x, glyphPos.y + glyph.position.y + glyph.size.y);
+
+ mgl::vec2f textureTopLeft(glyph.texture_position.x, glyph.texture_position.y);
+ mgl::vec2f textureTopRight(glyph.texture_position.x + glyph.texture_size.x, glyph.texture_position.y);
+ mgl::vec2f textureBottomLeft(glyph.texture_position.x, glyph.texture_position.y + glyph.texture_size.y);
+ mgl::vec2f textureBottomRight(glyph.texture_position.x + glyph.texture_size.x, glyph.texture_position.y + glyph.texture_size.y);
+
+ vertices[vertices_index].emplace_back(vertexTopRight, textureTopRight, text_color);
+ vertices[vertices_index].emplace_back(vertexTopLeft, textureTopLeft, text_color);
+ vertices[vertices_index].emplace_back(vertexBottomLeft, textureBottomLeft, text_color);
+ vertices[vertices_index].emplace_back(vertexBottomLeft, textureBottomLeft, text_color);
+ vertices[vertices_index].emplace_back(vertexBottomRight, textureBottomRight, text_color);
+ vertices[vertices_index].emplace_back(vertexTopRight, textureTopRight, text_color);
+
+ glyphPos.x += glyph.advance + characterSpacing;
+ vertices_linear.push_back({vertices_index, vertexStart, 0, codepoint});
+ break;
+ }
+ }
}
}
@@ -569,7 +624,7 @@ namespace QuickMedia
for(int i = 0; i < (int)vertices_linear.size(); ++i) {
VertexRef &vertex_ref = vertices_linear[i];
- sf::Vertex *vertex = &vertices[vertex_ref.vertices_index][vertex_ref.index];
+ mgl::Vertex *vertex = &vertices[vertex_ref.vertices_index][vertex_ref.index];
for(int v = 0; v < 6; ++v) {
vertex[v].position.x -= text_wrap_offset;
vertex[v].position.y += text_offset_y;
@@ -598,7 +653,7 @@ namespace QuickMedia
float vertex_left_side = get_text_quad_left_side(vertices_linear[last_space_index + 1]);
for(int j = last_space_index + 1; j <= i; ++j) {
VertexRef &vertex_ref_wrap = vertices_linear[j];
- sf::Vertex *vertex = &vertices[vertex_ref_wrap.vertices_index][vertex_ref_wrap.index];
+ mgl::Vertex *vertex = &vertices[vertex_ref_wrap.vertices_index][vertex_ref_wrap.index];
for(int v = 0; v < 6; ++v) {
vertex[v].position.x -= vertex_left_side;
vertex[v].position.y += line_height;
@@ -621,24 +676,23 @@ namespace QuickMedia
}
}
- boundingBox.width = 0.0f;
+ boundingBox.size.x = 0.0f;
for(VertexRef &vertex_ref : vertices_linear) {
- boundingBox.width = std::max(boundingBox.width, get_text_quad_right_side(vertex_ref));
+ boundingBox.size.x = std::max(boundingBox.size.x, get_text_quad_right_side(vertex_ref));
}
- boundingBox.height = num_lines * line_height;
+ boundingBox.size.y = num_lines * line_height;
// TODO: Clear |vertices| somehow even with editable text
for(size_t i = 0; i < FONT_ARRAY_SIZE; ++i) {
- vertex_buffers[i].create(vertices[i].getVertexCount());
- if(vertices[i].getVertexCount() > 0)
- vertex_buffers[i].update(&vertices[i][0], vertices[i].getVertexCount(), 0);
+ // TODO: Use VertexBuffer::Dynamic for editable text?
+ vertex_buffers[i].update(vertices[i].data(), vertices[i].size(), mgl::PrimitiveType::Triangles, mgl::VertexBuffer::Static);
}
//url_ranges.clear();
if(!editable) {
for(size_t i = 0; i < FONT_ARRAY_SIZE; ++i) {
vertices[i].clear();
- vertices[i].resize(0);
+ vertices[i].shrink_to_fit();
}
vertices_linear.clear();
vertices_linear.shrink_to_fit();
@@ -649,17 +703,17 @@ namespace QuickMedia
{
assert(!dirty && !dirtyText);
- sf::Font *latin_font;
+ mgl::Font *latin_font;
if(bold_font)
- latin_font = FontLoader::get_font(FontLoader::FontType::LATIN_BOLD);
+ latin_font = FontLoader::get_font(FontLoader::FontType::LATIN_BOLD, characterSize);
else
- latin_font = FontLoader::get_font(FontLoader::FontType::LATIN);
+ latin_font = FontLoader::get_font(FontLoader::FontType::LATIN, characterSize);
const float vspace = font_get_real_height(latin_font);
if(vertices_linear.empty()) {
caretIndex = 0;
- caretPosition = sf::Vector2f(0.0f, floor(vspace));
+ caretPosition = mgl::vec2f(0.0f, floor(vspace));
caret_offset_x = 0.0f;
return;
}
@@ -749,7 +803,7 @@ namespace QuickMedia
return num_vertices;
}
- static bool is_special_character(sf::Uint32 codepoint) {
+ static bool is_special_character(uint32_t codepoint) {
return (codepoint <= 47) || (codepoint >= 58 && codepoint <= 64) || (codepoint >= 91 && codepoint <= 96) || (codepoint >= 123 && codepoint <= 127);
}
@@ -817,18 +871,56 @@ namespace QuickMedia
return startIndex;
}
+ // TODO: Optimize
+ size_t Text::get_string_index_from_caret_index(size_t caret_index) const {
+ size_t codepoint_index = 0;
+ for(size_t i = 0; i < str.size();) {
+ if(codepoint_index == caret_index)
+ return i;
+
+ unsigned char *cp = (unsigned char*)&str[i];
+ uint32_t codepoint;
+ size_t clen;
+ if(!mgl::utf8_decode(cp, str.size() - i, &codepoint, &clen)) {
+ codepoint = *cp;
+ clen = 1;
+ }
+
+ i += clen;
+ ++codepoint_index;
+ }
+ return str.size();
+ }
+
+ static size_t utf8_get_length(const std::string &str) {
+ size_t codepoint_index = 0;
+ for(size_t i = 0; i < str.size();) {
+ unsigned char *cp = (unsigned char*)&str[i];
+ uint32_t codepoint;
+ size_t clen;
+ if(!mgl::utf8_decode(cp, str.size() - i, &codepoint, &clen)) {
+ codepoint = *cp;
+ clen = 1;
+ }
+
+ i += clen;
+ ++codepoint_index;
+ }
+ return codepoint_index;
+ }
+
// TODO: Optimize text editing by only processing the changed parts in updateGeometry.
// TODO: Split text into lines and add to vertices list so the lines that are cut off are not visible. This is good when using the text and as text input
// where there are a max number of rows shown at a time.
- void Text::processEvent(const sf::Event &event)
+ void Text::processEvent(mgl::Window &window, const mgl::Event &event)
{
if(!editable) return;
bool caretAtEnd = caretIndex == (int)vertices_linear.size();
- if(event.type == sf::Event::KeyPressed)
+ if(event.type == mgl::Event::KeyPressed)
{
- if(event.key.code == sf::Keyboard::Left && caretIndex > 0)
+ if(event.key.code == mgl::Keyboard::Left && caretIndex > 0)
{
if(event.key.control)
caretMoveDirection = CaretMoveDirection::LEFT_WORD;
@@ -836,7 +928,7 @@ namespace QuickMedia
caretMoveDirection = CaretMoveDirection::LEFT;
dirtyCaret = true;
}
- else if(event.key.code == sf::Keyboard::Right && !caretAtEnd)
+ else if(event.key.code == mgl::Keyboard::Right && !caretAtEnd)
{
if(event.key.control)
caretMoveDirection = CaretMoveDirection::RIGHT_WORD;
@@ -844,48 +936,60 @@ namespace QuickMedia
caretMoveDirection = CaretMoveDirection::RIGHT;
dirtyCaret = true;
}
- else if(event.key.code == sf::Keyboard::BackSpace && caretIndex > 0)
+ else if(event.key.code == mgl::Keyboard::Backspace && caretIndex > 0)
{
- str.erase(caretIndex - 1, 1);
- --caretIndex;
- dirty = true;
- dirtyText = true;
- dirtyCaret = true;
+ const size_t str_index = get_string_index_from_caret_index(caretIndex);
+ if(str_index > 0) {
+ const size_t codepoint_start = mgl::utf8_get_start_of_codepoint((const unsigned char*)str.c_str(), str.size(), str_index - 1);
+ str.erase(codepoint_start, str_index - codepoint_start);
+ --caretIndex;
+ dirty = true;
+ dirtyText = true;
+ dirtyCaret = true;
+ }
}
- else if(event.key.code == sf::Keyboard::Delete && !caretAtEnd)
+ else if(event.key.code == mgl::Keyboard::Delete && !caretAtEnd)
{
- str.erase(caretIndex, 1);
+ const size_t str_index = get_string_index_from_caret_index(caretIndex);
+ uint32_t decoded_codepoint = 0;
+ size_t decoded_length = 0;
+ mgl::utf8_decode((const unsigned char*)str.c_str() + str_index, str.size() - str_index, &decoded_codepoint, &decoded_length);
+ str.erase(str_index, decoded_length);
dirty = true;
dirtyText = true;
}
- else if(event.key.code == sf::Keyboard::D && event.key.control)
+ else if(event.key.code == mgl::Keyboard::D && event.key.control)
{
setString("");
}
- else if(event.key.code == sf::Keyboard::Up)
+ else if(event.key.code == mgl::Keyboard::Up)
{
caretMoveDirection = CaretMoveDirection::UP;
}
- else if(event.key.code == sf::Keyboard::Down)
+ else if(event.key.code == mgl::Keyboard::Down)
{
caretMoveDirection = CaretMoveDirection::DOWN;
}
- else if(event.key.code == sf::Keyboard::Home)
+ else if(event.key.code == mgl::Keyboard::Home)
{
caretMoveDirection = CaretMoveDirection::HOME;
}
- else if(event.key.code == sf::Keyboard::End)
+ else if(event.key.code == mgl::Keyboard::End)
{
caretMoveDirection = CaretMoveDirection::END;
}
- else if(event.key.code == sf::Keyboard::Enter)
+ else if(event.key.code == mgl::Keyboard::Enter)
{
if(event.key.shift && !single_line_edit)
{
- if(caretAtEnd)
+ if(caretAtEnd) {
str += '\n';
- else
- str.insert(caretIndex, '\n');
+ } else {
+ const size_t str_index = get_string_index_from_caret_index(caretIndex);
+ if(str_index > 0) {
+ str.insert(str_index, 1, '\n');
+ }
+ }
++caretIndex;
dirty = true;
@@ -894,34 +998,38 @@ namespace QuickMedia
}
}
}
- else if(event.type == sf::Event::TextEntered)
+ else if(event.type == mgl::Event::TextEntered)
{
- if(event.text.unicode == 8 || event.text.unicode == 127) // backspace, del
+ if(event.text.codepoint == 8 || event.text.codepoint == 127) // backspace, del
return;
- sf::String stringToAdd;
- if(event.text.unicode == 22) // ctrl+v
+ std::string stringToAdd;
+ if(event.text.codepoint == 22) // ctrl+v
{
- stringToAdd = sf::Clipboard::getString();
+ stringToAdd = window.get_clipboard();
}
- else if(event.text.unicode >= 32 || (event.text.unicode == '\t' && !single_line_edit))
- stringToAdd = event.text.unicode;
+ else if(event.text.codepoint >= 32 || (event.text.codepoint == '\t' && !single_line_edit))
+ stringToAdd.assign(event.text.str, event.text.size);
else
return;
- if(caretAtEnd)
+ if(caretAtEnd) {
str += stringToAdd;
- else
- str.insert(caretIndex, stringToAdd);
+ } else {
+ const size_t str_index = get_string_index_from_caret_index(caretIndex);
+ if(str_index > 0 && str_index != std::string::npos) {
+ str.insert(str_index, stringToAdd);
+ }
+ }
- caretIndex += stringToAdd.getSize();
+ caretIndex += utf8_get_length(stringToAdd);
dirty = true;
dirtyText = true;
dirtyCaret = true;
}
}
- bool Text::draw(sf::RenderTarget &target)
+ bool Text::draw(mgl::Window &target)
{
updateGeometry();
@@ -931,35 +1039,34 @@ namespace QuickMedia
caretMoveDirection = CaretMoveDirection::NONE;
}
- sf::Vector2f pos = position;
+ mgl::vec2f pos = position;
FontLoader::FontType latin_font_type;
if(bold_font)
latin_font_type = FontLoader::FontType::LATIN_BOLD;
else
latin_font_type = FontLoader::FontType::LATIN;
- sf::Font *latin_font = FontLoader::get_font(latin_font_type);
+ mgl::Font *latin_font = FontLoader::get_font(latin_font_type, characterSize);
const float vspace = font_get_real_height(latin_font);
pos.y += floor(vspace); // Origin is at bottom left, we want it to be at top left
const FontLoader::FontType font_types[] = { latin_font_type, FontLoader::FontType::CJK, FontLoader::FontType::SYMBOLS };
for(size_t i = 0; i < FONT_INDEX_EMOJI; ++i) {
- if(vertex_buffers[i].getVertexCount() == 0)
+ if(vertex_buffers[i].size() == 0)
continue;
- sf::Font *font = FontLoader::get_font(font_types[i]);
- sf::RenderStates states;
- states.transform.translate(pos);
- states.texture = &font->getTexture(characterSize);
- target.draw(vertex_buffers[i], states);
+ mgl::Font *font = FontLoader::get_font(font_types[i], characterSize);
+ mgl::Texture font_texture = font->get_texture();
+ vertex_buffers[i].set_texture(&font_texture);
+ vertex_buffers[i].set_position(pos);
+ target.draw(vertex_buffers[i]);
}
- if(vertex_buffers[FONT_INDEX_EMOJI].getVertexCount() > 0) {
- sf::RenderStates states;
- states.transform.translate(pos);
- states.texture = TextureLoader::get_texture("images/emoji.png");
- target.draw(vertex_buffers[FONT_INDEX_EMOJI], states);
+ if(vertex_buffers[FONT_INDEX_EMOJI].size() > 0) {
+ vertex_buffers[FONT_INDEX_EMOJI].set_texture(TextureLoader::get_texture("images/emoji.png", true));
+ vertex_buffers[FONT_INDEX_EMOJI].set_position(pos);
+ target.draw(vertex_buffers[FONT_INDEX_EMOJI]);
}
if(!editable) return true;
@@ -967,8 +1074,11 @@ namespace QuickMedia
const float caret_margin = std::floor(2.0f * get_config().scale);
- sf::RectangleShape caretRect(sf::Vector2f(std::floor(2.0f * get_config().scale), floor(vspace - caret_margin * 2.0f)));
- caretRect.setPosition(floor(pos.x + caretPosition.x), floor(pos.y + caretPosition.y + caret_margin + std::floor(4.0f * get_config().scale)));
+ mgl::Rectangle caretRect(mgl::vec2f(0.0f, 0.0f), mgl::vec2f(std::floor(2.0f * get_config().scale), floor(vspace - caret_margin * 2.0f)));
+ caretRect.set_position(mgl::vec2f(
+ floor(pos.x + caretPosition.x),
+ floor(pos.y + caretPosition.y + caret_margin + std::floor(4.0f * get_config().scale))
+ ));
target.draw(caretRect);
return true;
}