From 2b7d030551a357272f9cc39e347855bd2e3faba2 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 7 May 2018 23:00:21 +0200 Subject: Improve text editing, use Text object for chatbar Improve text rendering by not using floating point position --- src/Text.cpp | 140 ++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 115 insertions(+), 25 deletions(-) (limited to 'src/Text.cpp') diff --git a/src/Text.cpp b/src/Text.cpp index f78c6b7..6fa1fcc 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -5,6 +5,7 @@ #include "../include/ColorScheme.hpp" #include #include +#include namespace dchat { @@ -59,9 +60,19 @@ namespace dchat this->str = str; dirty = true; dirtyText = true; + if(str.getSize() < caretIndex) + { + caretIndex = str.getSize(); + dirtyCaret = true; + } } } + const sf::String& Text::getString() const + { + return str; + } + void Text::setPosition(float x, float y) { position.x = x; @@ -91,6 +102,16 @@ namespace dchat } } + unsigned int Text::getCharacterSize() const + { + return characterSize; + } + + const sf::Font* Text::getFont() const + { + return font; + } + void Text::setFillColor(sf::Color color) { if(color != this->color) @@ -323,7 +344,7 @@ namespace dchat { textElement.position.y = glyphPos.y; // TODO: Find a better way to do this, @totalHeight is wrong because we add emojiSize and then vspace - glyphPos.y += emojiSize - vspace; + glyphPos.y += floor(emojiSize - vspace); } else { @@ -333,7 +354,7 @@ namespace dchat if(glyphPos.x > maxWidth) { glyphPos.x = 0.0f; - glyphPos.y += vspace + lineSpacing; + glyphPos.y += floor(vspace + lineSpacing); } boundingBox.width = std::max(boundingBox.width, glyphPos.x); @@ -341,7 +362,7 @@ namespace dchat } usize vertexOffset = vertices.getVertexCount(); - vertices.resize(vertices.getVertexCount() + (4 * textElement.text.size)); + vertices.resize(vertices.getVertexCount() + 4 * (textElement.text.size + 1)); textElement.position = glyphPos; for(size_t i = 0; i < textElement.text.size; ++i) { @@ -356,7 +377,10 @@ namespace dchat { case ' ': { - vertices[vertexStart].position = sf::Vector2f(glyphPos.x, glyphPos.y - vspace); + vertices[vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertexStart + 1] = { sf::Vector2f(glyphPos.x + hspace, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertexStart + 2] = { sf::Vector2f(glyphPos.x + hspace, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertexStart + 3] = { sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; glyphPos.x += hspace; if(glyphPos.x > maxWidth * 0.5f) { @@ -367,7 +391,10 @@ namespace dchat } case '\t': { - vertices[vertexStart].position = sf::Vector2f(glyphPos.x, glyphPos.y - vspace); + vertices[vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertexStart + 1] = { sf::Vector2f(glyphPos.x + hspace * TAB_WIDTH, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertexStart + 2] = { sf::Vector2f(glyphPos.x + hspace * TAB_WIDTH, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertexStart + 3] = { sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; glyphPos.x += (hspace * TAB_WIDTH); if(glyphPos.x > maxWidth * 0.5f) { @@ -378,15 +405,21 @@ namespace dchat } case '\n': { - vertices[vertexStart].position = sf::Vector2f(glyphPos.x, glyphPos.y - vspace); + vertices[vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertexStart + 1] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertexStart + 2] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertexStart + 3] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; glyphPos.x = 0.0f; - glyphPos.y += vspace + lineSpacing; + glyphPos.y += floor(vspace + lineSpacing); continue; } case '\v': { - vertices[vertexStart].position = sf::Vector2f(glyphPos.x, glyphPos.y - vspace); - glyphPos.y += (vspace * TAB_WIDTH) + lineSpacing; + vertices[vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertexStart + 1] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertexStart + 2] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertexStart + 3] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; + glyphPos.y += floor(vspace * TAB_WIDTH + lineSpacing); continue; } } @@ -405,7 +438,7 @@ namespace dchat { sf::Vector2f &vertexPos = vertices[vertexOffset + j * 4 + k].position; vertexPos.x -= lastSpacingAccumulatedOffset; - vertexPos.y += vspace + lineSpacing; + vertexPos.y += floor(vspace + lineSpacing); } } @@ -416,7 +449,7 @@ namespace dchat else glyphPos.x = 0.0f; - glyphPos.y += vspace + lineSpacing; + glyphPos.y += floor(vspace + lineSpacing); } sf::Vector2f vertexTopLeft(glyphPos.x + glyph.bounds.left, glyphPos.y + glyph.bounds.top); @@ -439,6 +472,11 @@ namespace dchat glyphPos.x += glyph.advance; } + vertices[vertices.getVertexCount() - 4] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertices.getVertexCount() - 3] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertices.getVertexCount() - 2] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertices.getVertexCount() - 1] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + if(textElement.type != TextElement::Type::TEXT) { prevCodePoint = 0; @@ -494,8 +532,16 @@ namespace dchat } usize vertexIndex = caretIndex * 4; - const sf::Vertex &topLeftVertex = vertices[vertexIndex]; - caretPosition = topLeftVertex.position; + if(vertexIndex == 0) + { + float vspace = font->getLineSpacing(characterSize); + caretPosition = sf::Vector2f(0.0f, -vspace); + } + else + { + const sf::Vertex &topLeftVertex = vertices[vertexIndex]; + caretPosition = topLeftVertex.position; + } } bool Text::isCaretAtEnd() const @@ -511,15 +557,16 @@ namespace dchat int numVertices = vertices.getVertexCount(); if(numVertices < 4) return 0; - usize vertexIndex = caretIndex * 4; + usize vertexIndex = startIndex * 4; const sf::Vertex &startTopLeftVertex = vertices[vertexIndex]; int startRow = getRowByPosition(startTopLeftVertex.position); - for(int i = startIndex * 4; i >= 0; i -= 4) + for(int i = startIndex * 4; i > 0; i -= 4) { const sf::Vertex &topLeftVertex = vertices[i]; int row = getRowByPosition(topLeftVertex.position); if(row != startRow) { + printf("start of line %u, startIndex: %u\n", i, startIndex * 4); return std::max(0, i / 4 + 1); } } @@ -533,7 +580,7 @@ namespace dchat int numVertices = vertices.getVertexCount(); if(numVertices < 4) return 0; - usize vertexIndex = caretIndex * 4; + usize vertexIndex = startIndex * 4; const sf::Vertex &startTopLeftVertex = vertices[vertexIndex]; int startRow = getRowByPosition(startTopLeftVertex.position); for(int i = startIndex * 4; i < numVertices; i += 4) @@ -555,7 +602,7 @@ namespace dchat int numVertices = vertices.getVertexCount(); if(numVertices < 4) return 0; - usize vertexIndex = caretIndex * 4; + usize vertexIndex = startIndex * 4; const sf::Vertex &startTopLeftVertex = vertices[vertexIndex]; int startRow = getRowByPosition(startTopLeftVertex.position); int closestIndex = -1; @@ -589,7 +636,7 @@ namespace dchat int numVertices = vertices.getVertexCount(); if(numVertices < 4) return 0; - usize vertexIndex = caretIndex * 4; + usize vertexIndex = startIndex * 4; const sf::Vertex &startTopLeftVertex = vertices[vertexIndex]; int startRow = getRowByPosition(startTopLeftVertex.position); int closestIndex = -1; @@ -623,11 +670,23 @@ namespace dchat return static_cast(1.0f + position.y / (vspace + lineSpacing)); } + static std::string getClipboard() + { + std::string result; + TinyProcessLib::Process process("xsel -o -b", "", [&result](const char *bytes, size_t n) + { + result.append(bytes, n); + }); + if(process.get_exit_status() != 0) + fprintf(stderr, "Failed to get clipboard content\n"); + return result; + } + void Text::processEvent(const sf::Event &event) { - if(!editable || textElements.size() == 0) return; + if(!editable) return; - bool caretAtEnd = textElements[0].text.size == 0 || caretIndex == textElements[0].text.size; + bool caretAtEnd = textElements.size() == 0 || textElements[0].text.size == 0 || caretIndex == textElements[0].text.size; if(event.type == sf::Event::KeyPressed) { @@ -645,8 +704,8 @@ namespace dchat { auto strBefore = str.substring(0, caretIndex - 1); auto strAfter = str.substring(caretIndex); - setString(strBefore + strAfter); --caretIndex; + setString(strBefore + strAfter); dirtyCaret = true; } else if(event.key.code == sf::Keyboard::Delete && !caretAtEnd) @@ -671,22 +730,52 @@ namespace dchat { caretMoveDirection = CaretMoveDirection::END; } + else if(event.key.code == sf::Keyboard::Return) + { + if(sf::Keyboard::isKeyPressed(sf::Keyboard::LShift) || sf::Keyboard::isKeyPressed(sf::Keyboard::RShift)) + { + if(caretAtEnd) + str += '\n'; + else + { + auto strBefore = str.substring(0, caretIndex); + auto strAfter = str.substring(caretIndex); + str = strBefore + '\n' + strAfter; + } + + ++caretIndex; + dirty = true; + dirtyText = true; + dirtyCaret = true; + } + } } else if(event.type == sf::Event::TextEntered) { if(event.text.unicode == 8 || event.text.unicode == 127) // backspace, del return; + sf::String stringToAdd; + if(event.text.unicode == 22) // ctrl+v + { + auto clipboardString = getClipboard(); + stringToAdd = sf::String::fromUtf8(clipboardString.begin(), clipboardString.end()); + } + else if(event.text.unicode >= 32 || event.text.unicode == 9) // 9 == tab + stringToAdd = event.text.unicode; + else + return; + if(caretAtEnd) - str += event.text.unicode; + str += stringToAdd; else { auto strBefore = str.substring(0, caretIndex); auto strAfter = str.substring(caretIndex); - str = strBefore + event.text.unicode + strAfter; + str = strBefore + stringToAdd + strAfter; } - ++caretIndex; + caretIndex += stringToAdd.getSize(); dirty = true; dirtyText = true; dirtyCaret = true; @@ -727,7 +816,7 @@ namespace dchat //colRect.contains() //if(pos.x + maxWidth <= 0.0f || pos.x >= maxWidth || pos.y + totalHeight <= 0.0f || pos.y >= target.getSize().y) return; if(pos.y + getHeight() <= 0.0f || pos.y >= target.getSize().y) return; - + /* if(editable) { sf::RectangleShape editBox(sf::Vector2f(std::max(maxWidth, boundingBox.width), boundingBox.height)); @@ -735,6 +824,7 @@ namespace dchat editBox.setFillColor(ColorScheme::getBackgroundColor() + sf::Color(10, 10, 10)); target.draw(editBox); } + */ states.transform.translate(pos); states.texture = &font->getTexture(characterSize); -- cgit v1.2.3