From 4277763df5c1dac8ff389d3bfd138f03acc7f1e2 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 28 Sep 2020 13:32:34 +0200 Subject: Implement text editing with navigation and multilingual fonts --- src/Text.cpp | 418 ++++++++++++++++++++++++++++++----------------------------- 1 file changed, 215 insertions(+), 203 deletions(-) (limited to 'src/Text.cpp') diff --git a/src/Text.cpp b/src/Text.cpp index 8f58e3d..3962374 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -1,6 +1,9 @@ #include "../include/Text.hpp" #include #include +#include +#include +#include #include #include @@ -48,8 +51,8 @@ namespace QuickMedia void Text::setString(sf::String str) { - if(str != this->str) - { + //if(str != this->str) + //{ this->str = std::move(str); dirty = true; dirtyText = true; @@ -58,7 +61,7 @@ namespace QuickMedia caretIndex = this->str.getSize(); dirtyCaret = true; } - } + // } } const sf::String& Text::getString() const @@ -156,6 +159,10 @@ namespace QuickMedia } } + bool Text::isEditable() const { + return editable; + } + float Text::getWidth() const { return boundingBox.width; @@ -214,10 +221,18 @@ namespace QuickMedia index += 1 + offset; } } + + float Text::get_text_quad_left_side(const VertexRef &vertex_ref) const { + return vertices[vertex_ref.vertices_index][vertex_ref.index].position.x; + } + + float Text::get_text_quad_right_side(const VertexRef &vertex_ref) const { + return vertices[vertex_ref.vertices_index][vertex_ref.index + 1].position.x; + } - // Logic loosely based on https://github.com/SFML/SFML/wiki/Source:-CurvedText void Text::updateGeometry(bool update_even_if_not_dirty) { if(dirtyText) { + assert(dirty); dirtyText = false; splitTextByFont(); } @@ -225,10 +240,11 @@ namespace QuickMedia if(!update_even_if_not_dirty && !dirty) return; + vertices_linear.clear(); vertices[0].clear(); vertices[1].clear(); float hspace = font->getGlyph(' ', characterSize, false).advance + characterSpacing; - float vspace = font->getLineSpacing(characterSize); + float vspace = font->getLineSpacing(characterSize); // TODO: What about japanese font??? boundingBox = sf::FloatRect(); @@ -236,27 +252,27 @@ namespace QuickMedia sf::Uint32 prevCodePoint = 0; for(usize textElementIndex = 0; textElementIndex < textElements.size(); ++textElementIndex) { - size_t lastSpacingWordWrapIndex = -1; - float lastSpacingAccumulatedOffset = 0.0f; TextElement &textElement = textElements[textElementIndex]; const sf::Font *ff = font; - size_t vertices_index = 0; + int vertices_index = 0; if(textElement.is_japanese) { ff = cjk_font; vertices_index = 1; } usize vertexOffset = vertices[vertices_index].getVertexCount(); - vertices[vertices_index].resize(vertices[vertices_index].getVertexCount() + 4 * (textElement.text.size + 1)); + 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) { sf::Uint32 codePoint = textElement.text[i]; + // 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; glyphPos.x += kerning; - usize vertexStart = vertexOffset + i * 4; + int vertexStart = vertexOffset + i * 4; switch(codePoint) { @@ -267,67 +283,34 @@ namespace QuickMedia vertices[vertices_index][vertexStart + 2] = { sf::Vector2f(glyphPos.x + hspace, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; vertices[vertices_index][vertexStart + 3] = { sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; glyphPos.x += hspace; - if(glyphPos.x > maxWidth * 0.5f) - { - lastSpacingWordWrapIndex = i; - lastSpacingAccumulatedOffset = glyphPos.x; - } + vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint}); continue; } case '\t': { + const float char_width = hspace * TAB_WIDTH; vertices[vertices_index][vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; - vertices[vertices_index][vertexStart + 1] = { sf::Vector2f(glyphPos.x + hspace * TAB_WIDTH, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; - vertices[vertices_index][vertexStart + 2] = { sf::Vector2f(glyphPos.x + hspace * TAB_WIDTH, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertices_index][vertexStart + 1] = { sf::Vector2f(glyphPos.x + char_width, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertices_index][vertexStart + 2] = { sf::Vector2f(glyphPos.x + char_width, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; vertices[vertices_index][vertexStart + 3] = { sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; - glyphPos.x += (hspace * TAB_WIDTH); - if(glyphPos.x > maxWidth * 0.5f) - { - lastSpacingWordWrapIndex = i; - lastSpacingAccumulatedOffset = glyphPos.x; - } + glyphPos.x += char_width; + vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint}); continue; } case '\n': { - vertices[vertices_index][vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; - vertices[vertices_index][vertexStart + 1] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; - vertices[vertices_index][vertexStart + 2] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; - vertices[vertices_index][vertexStart + 3] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; glyphPos.x = 0.0f; glyphPos.y += floor(vspace + lineSpacing); + vertices[vertices_index][vertexStart + 0] = { sf::Vector2f(0.0f, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertices_index][vertexStart + 1] = { sf::Vector2f(0.0f, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertices_index][vertexStart + 2] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertices_index][vertexStart + 3] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() }; + vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint}); continue; } } const sf::Glyph &glyph = ff->getGlyph(codePoint, characterSize, false); - // TODO: Fix wrap-around with multiple textElements. Right now it only wrap-arounds within the same textElement, so with mixed latin-japanese it will - // wrap at character size rather than at whitespace - if(glyphPos.x + glyph.advance > maxWidth) - { - // If there was a space in the text and text width is too long, then we need to word wrap at space index instead, - // which means we need to change the position of all vertices after the space to the current vertex - if(lastSpacingWordWrapIndex != (size_t)-1) - { - for(size_t j = lastSpacingWordWrapIndex; j < i; ++j) - { - for(size_t k = 0; k < 4; ++k) - { - sf::Vector2f &vertexPos = vertices[vertices_index][vertexOffset + j * 4 + k].position; - vertexPos.x -= lastSpacingAccumulatedOffset; - vertexPos.y += floor(vspace + lineSpacing); - } - } - - glyphPos.x -= lastSpacingAccumulatedOffset; - lastSpacingWordWrapIndex = -1; - lastSpacingAccumulatedOffset = 0.0f; - } - else - glyphPos.x = 0.0f; - - glyphPos.y += floor(vspace + lineSpacing); - } 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); @@ -347,43 +330,110 @@ namespace QuickMedia vertices[vertices_index][vertexStart + 3] = { vertexBottomLeft, fontColor, textureBottomLeft }; glyphPos.x += glyph.advance + characterSpacing; + vertices_linear.push_back({vertices_index, vertexStart, 0, codePoint}); } - vertices[vertices_index][vertices[vertices_index].getVertexCount() - 4] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; - vertices[vertices_index][vertices[vertices_index].getVertexCount() - 3] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; - vertices[vertices_index][vertices[vertices_index].getVertexCount() - 2] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; - vertices[vertices_index][vertices[vertices_index].getVertexCount() - 1] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + //vertices[vertices_index][vertices[vertices_index].getVertexCount() - 4] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + //vertices[vertices_index][vertices[vertices_index].getVertexCount() - 3] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + //vertices[vertices_index][vertices[vertices_index].getVertexCount() - 2] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + //vertices[vertices_index][vertices[vertices_index].getVertexCount() - 1] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; prevCodePoint = 0; } - - boundingBox.height = glyphPos.y + lineSpacing; - boundingBox.height += vspace; - for(size_t vertices_index = 0; vertices_index < 2; ++vertices_index) { - usize numVertices = vertices[vertices_index].getVertexCount(); - for(usize i = 0; i < numVertices; i += 4) - { - const sf::Vertex &bottomRight = vertices[vertices_index][i + 2]; - boundingBox.width = std::max(boundingBox.width, bottomRight.position.x); + const float line_height = floor(vspace + lineSpacing); + float text_wrap_offset = 0.0f; + float text_offset_y = 0.0f; + int last_space_index = -1; + int num_lines = 1; + // TODO: Binary search? + for(int i = 0; i < (int)vertices_linear.size(); ++i) { + VertexRef &vertex_ref = vertices_linear[i]; + switch(vertex_ref.codepoint) { + case ' ': + case '\t': + last_space_index = i; + break; + case '\n': + text_wrap_offset = 0.0f; + last_space_index = -1; + ++num_lines; + break; + default: + break; + } + + sf::Vertex *vertex = &vertices[vertex_ref.vertices_index][vertex_ref.index]; + vertex[0].position.x -= text_wrap_offset; + vertex[1].position.x -= text_wrap_offset; + vertex[2].position.x -= text_wrap_offset; + vertex[3].position.x -= text_wrap_offset; + + vertex[0].position.y += text_offset_y; + vertex[1].position.y += text_offset_y; + vertex[2].position.y += text_offset_y; + vertex[3].position.y += text_offset_y; + vertex_ref.line = num_lines - 1; + + float vertex_right_side = get_text_quad_right_side(vertex_ref); + if(vertex_right_side > maxWidth) { + ++num_lines; + // TODO: Ignore line wrap on space + if(last_space_index != -1 && last_space_index != i) { + 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]; + vertex[0].position.x -= vertex_left_side; + vertex[1].position.x -= vertex_left_side; + vertex[2].position.x -= vertex_left_side; + vertex[3].position.x -= vertex_left_side; + + vertex[0].position.y += line_height; + vertex[1].position.y += line_height; + vertex[2].position.y += line_height; + vertex[3].position.y += line_height; + + vertex_ref_wrap.line = num_lines - 1; + } + last_space_index = -1; + text_wrap_offset += vertex_left_side; + } else { + float vertex_left_side = get_text_quad_left_side(vertex_ref); + vertex[0].position.x -= vertex_left_side; + vertex[1].position.x -= vertex_left_side; + vertex[2].position.x -= vertex_left_side; + vertex[3].position.x -= vertex_left_side; + + vertex[0].position.y += line_height; + vertex[1].position.y += line_height; + vertex[2].position.y += line_height; + vertex[3].position.y += line_height; + + text_wrap_offset += vertex_left_side; + vertex_ref.line = num_lines - 1; + } + text_offset_y += line_height; } } + boundingBox.width = 0.0f; + for(VertexRef &vertex_ref : vertices_linear) { + boundingBox.width = std::max(boundingBox.width, get_text_quad_right_side(vertex_ref)); + } + boundingBox.height = num_lines * line_height; dirty = false; } - -#if 0 + + // TODO: Fix caret up/down navigation! its broken because of newlines void Text::updateCaret() { assert(!dirty && !dirtyText); - if(textElements.size() == 0) - { - float vspace = font->getLineSpacing(characterSize); + if(vertices_linear.empty()) { caretIndex = 0; - caretPosition = sf::Vector2f(0.0f, -vspace); + caretPosition = sf::Vector2f(0.0f, floor(font->getLineSpacing(characterSize))); return; } - switch(caretMoveDirection) { @@ -407,49 +457,41 @@ namespace QuickMedia caretIndex = getEndOfLine(caretIndex); break; } - default: + case CaretMoveDirection::NONE: // Ignore... break; } - - caretIndex = std::min(std::max(0, caretIndex), (int)textElements[0].text.size); - - usize vertexIndex = caretIndex * 4; - 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 - { - assert(!dirty && !dirtyText); - return textElements[0].text.size == 0 || caretIndex == (int)textElements[0].text.size; + if(caretIndex == (int)vertices_linear.size()) { + caretPosition.x = get_text_quad_right_side(vertices_linear.back()); + caretPosition.y = (1 + vertices_linear.back().line) * floor(font->getLineSpacing(characterSize) + lineSpacing); + } else if(caretIndex == 0) { + caretPosition = sf::Vector2f(0.0f, floor(font->getLineSpacing(characterSize))); + } else { + if(vertices_linear[caretIndex].codepoint == '\n') { + caretPosition.x = get_text_quad_right_side(vertices_linear[caretIndex - 1]); + caretPosition.y = (1 + vertices_linear[caretIndex - 1].line) * floor(font->getLineSpacing(characterSize) + lineSpacing); + } else { + caretPosition.x = get_text_quad_left_side(vertices_linear[caretIndex]); + caretPosition.y = (1 + vertices_linear[caretIndex].line) * floor(font->getLineSpacing(characterSize) + lineSpacing); + } + } } // TODO: This can be optimized by using binary search int Text::getStartOfLine(int startIndex) const { assert(!dirty && !dirtyText); - int numVertices = vertices.getVertexCount(); - if(numVertices < 4) return 0; - - usize vertexIndex = startIndex * 4; - const sf::Vertex &startTopLeftVertex = vertices[vertexIndex]; - int startRow = getRowByPosition(startTopLeftVertex.position); - for(int i = startIndex * 4; i > 0; i -= 4) - { - const sf::Vertex &topLeftVertex = vertices[i]; - int row = getRowByPosition(topLeftVertex.position); - if(row != startRow) - { - return std::max(0, i / 4 + 1); + const int num_vertices = vertices_linear.size(); + const int start_index_wrap = startIndex < num_vertices ? startIndex : num_vertices - 1; + int start_line = vertices_linear[start_index_wrap].line; + if(vertices_linear[start_index_wrap].codepoint == '\n') + --start_line; + for(int i = startIndex - 1; i >= 0; --i) { + if(vertices_linear[i].line != start_line) { + if(i + 2 <= num_vertices && vertices_linear[i + 1].codepoint == '\n') + return i + 2; + return i + 1; } } return 0; @@ -459,55 +501,46 @@ namespace QuickMedia int Text::getEndOfLine(int startIndex) const { assert(!dirty && !dirtyText); - int numVertices = vertices.getVertexCount(); - if(numVertices < 4) return 0; - - usize vertexIndex = startIndex * 4; - const sf::Vertex &startTopLeftVertex = vertices[vertexIndex]; - int startRow = getRowByPosition(startTopLeftVertex.position); - for(int i = startIndex * 4; i < numVertices; i += 4) - { - const sf::Vertex &topLeftVertex = vertices[i]; - int row = getRowByPosition(topLeftVertex.position); - if(row != startRow) - { - return std::max(0, i / 4 - 1); + const int num_vertices = vertices_linear.size(); + const int start_index_wrap = startIndex < num_vertices ? startIndex : num_vertices - 1; + int start_line = vertices_linear[start_index_wrap].line; + if(vertices_linear[start_index_wrap].codepoint == '\n') + return startIndex; + for(int i = startIndex + 1; i < (int)vertices_linear.size(); ++i) { + if(vertices_linear[i].line != start_line) { + if(vertices_linear[i].codepoint == '\n') + return i; + return i - 1; } } - return numVertices / 4; + return (int)vertices_linear.size(); } // TODO: This can be optimized by using binary search int Text::getPreviousLineClosestPosition(int startIndex) const { assert(!dirty && !dirtyText); - int numVertices = vertices.getVertexCount(); - if(numVertices < 4) return 0; - - usize vertexIndex = startIndex * 4; - const sf::Vertex &startTopLeftVertex = vertices[vertexIndex]; - int startRow = getRowByPosition(startTopLeftVertex.position); - int closestIndex = -1; - float closestAbsoluteDiffX = 0.0f; - for(int i = startIndex * 4; i >= 0; i -= 4) - { - const sf::Vertex &topLeftVertex = vertices[i]; - int row = getRowByPosition(topLeftVertex.position); - float absoluteDiffX = fabs(topLeftVertex.position.x - startTopLeftVertex.position.x); - int rowDiff = abs(row - startRow); - if(rowDiff > 1) - break; - - if(rowDiff == 1 && (closestIndex == -1 || absoluteDiffX < closestAbsoluteDiffX)) - { - closestIndex = i; - closestAbsoluteDiffX = absoluteDiffX; - } + const int num_vertices = vertices_linear.size(); + float start_left_pos; + if(startIndex == num_vertices) { + start_left_pos = get_text_quad_right_side(vertices_linear.back()); + if(vertices_linear.back().codepoint == '\n') + return getStartOfLine(startIndex - 1); + } else { + start_left_pos = get_text_quad_left_side(vertices_linear[startIndex]); + if(vertices_linear[startIndex].codepoint == '\n') + return getStartOfLine(startIndex - 1); + } + float closest_char = 999999.9f; + for(int i = getStartOfLine(startIndex) - 1; i >= 0; --i) { + //if(vertices_linear[i].codepoint == '\n') + // continue; + const float left_pos = get_text_quad_left_side(vertices_linear[i]); + const float pos_diff = std::abs(start_left_pos - left_pos); + if(pos_diff > closest_char) + return i + 1; + closest_char = pos_diff; } - - if(closestIndex != -1) - return closestIndex / 4; - return 0; } @@ -515,43 +548,31 @@ namespace QuickMedia int Text::getNextLineClosestPosition(int startIndex) const { assert(!dirty && !dirtyText); - int numVertices = vertices.getVertexCount(); - if(numVertices < 4) return 0; - - usize vertexIndex = startIndex * 4; - const sf::Vertex &startTopLeftVertex = vertices[vertexIndex]; - int startRow = getRowByPosition(startTopLeftVertex.position); - int closestIndex = -1; - float closestAbsoluteDiffX = 0.0f; - for(int i = startIndex * 4; i < numVertices; i += 4) - { - const sf::Vertex &topLeftVertex = vertices[i]; - int row = getRowByPosition(topLeftVertex.position); - float absoluteDiffX = fabs(topLeftVertex.position.x - startTopLeftVertex.position.x); - int rowDiff = abs(row - startRow); - if(rowDiff > 1) - break; - - if(rowDiff == 1 && (closestIndex == -1 || absoluteDiffX < closestAbsoluteDiffX)) - { - closestIndex = i; - closestAbsoluteDiffX = absoluteDiffX; - } + const int num_vertices = vertices_linear.size(); + float start_left_pos; + if(startIndex == num_vertices) { + return startIndex; + } else { + start_left_pos = get_text_quad_left_side(vertices_linear[startIndex]); + if(vertices_linear[startIndex].codepoint == '\n') + return startIndex + 1; } - - if(closestIndex != -1) - return closestIndex / 4; - - return numVertices / 4; - } - - int Text::getRowByPosition(const sf::Vector2f &position) const - { - assert(!dirty && !dirtyText); - const float vspace = font->getLineSpacing(characterSize); - return static_cast(1.0f + position.y / (vspace + lineSpacing)); + float closest_char = 999999.9f; + for(int i = getEndOfLine(startIndex) + 1; i < (int)vertices_linear.size(); ++i) { + //if(vertices_linear[i].codepoint == '\n') + // continue; + const float left_pos = get_text_quad_left_side(vertices_linear[i]); + const float pos_diff = std::abs(start_left_pos - left_pos); + if(pos_diff > closest_char) + return i - 1; + closest_char = pos_diff; + } + return (int)vertices_linear.size(); } - + + // 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) { if(!editable) return; @@ -602,7 +623,7 @@ namespace QuickMedia } else if(event.key.code == sf::Keyboard::Return) { - if(sf::Keyboard::isKeyPressed(sf::Keyboard::LShift) || sf::Keyboard::isKeyPressed(sf::Keyboard::RShift)) + if(event.key.shift) { if(caretAtEnd) str += '\n'; @@ -650,24 +671,19 @@ namespace QuickMedia dirtyCaret = true; } } -#endif + bool Text::draw(sf::RenderTarget &target) { updateGeometry(); - -#if 0 + if(dirtyCaret || caretMoveDirection != CaretMoveDirection::NONE) { updateCaret(); dirtyCaret = false; caretMoveDirection = CaretMoveDirection::NONE; } -#endif - - float vspace = font->getLineSpacing(characterSize); sf::Vector2f pos = position; - pos.y += floor(vspace); // Origin is at bottom left, we want it to be at top left // TODO: Do not use maxWidth here. Max width might be set to 99999 and actual text width might be 200. Text width should be calculated instead //sf::FloatRect targetRect(0.0f, 0.0f, maxWidth, target.getSize().y); @@ -685,6 +701,9 @@ namespace QuickMedia } return false; } + + const float vspace = font->getLineSpacing(characterSize); + pos.y += floor(vspace); // Origin is at bottom left, we want it to be at top left if(!visible) { visible = true; @@ -700,21 +719,14 @@ namespace QuickMedia } lastSeenTimer.restart(); - pos.y -= floor(vspace); - -#if 0 if(!editable) return true; - - //float rows = floor(totalHeight / (vspace + lineSpacing)); - const float caretRow = getRowByPosition(caretPosition); - - sf::RectangleShape caretRect(sf::Vector2f(2.0f, floor(vspace))); - caretRect.setFillColor(sf::Color::White); - caretRect.setPosition(sf::Vector2f(floor(pos.x + caretPosition.x), floor(pos.y + caretRow * (vspace + lineSpacing)))); + pos.y -= floor(vspace * 2.0f); + + const float caret_margin = 2.0f; + + sf::RectangleShape caretRect(sf::Vector2f(2.0f, floor(vspace - caret_margin * 2.0f))); + caretRect.setPosition(floor(pos.x + caretPosition.x), floor(pos.y + caretPosition.y + caret_margin + 4.0f)); target.draw(caretRect); return true; -#else - return true; -#endif } } -- cgit v1.2.3