diff options
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | TODO | 8 | ||||
-rw-r--r-- | include/Text.hpp | 24 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 6 | ||||
-rw-r--r-- | src/Text.cpp | 210 |
5 files changed, 142 insertions, 110 deletions
@@ -37,8 +37,8 @@ Press `Ctrl + R` to show/hide related videos menu when watching a video.\ Press `Ctrl + T` when hovering over a manga chapter to start tracking manga after that chapter. This only works if AutoMedia is installed and accessible in PATH environment variable.\ Press `Backspace` to return to the preview item when reading replies in image board threads.\ Press `R` to paste the post number of the selected post into the post field (image boards).\ -Press `Ctrl + C` to begin writing a post to a thread (image boards), press `ESC` to cancel.\ -Press `Ctrl + C` to begin writing a message in a matrix room, press `ESC` to cancel.\ +Press `Ctrl + M` to begin writing a post to a thread (image boards), press `ESC` to cancel.\ +Press `Ctrl + M` to begin writing a message in a matrix room, press `ESC` to cancel.\ Press `1 to 9` or `Numpad 1 to 9` to select google captcha image when posting a comment on 4chan.\ Press `P` to preview the 4chan image of the selected row in full screen view, press `ESC` or `Backspace` to go back.\ Press `I` to switch between single image and scroll image view mode when reading manga.\ @@ -38,8 +38,14 @@ Add option to edit input in vim (using temporary file). Scrolling in images still messes up the |current| page sometimes, need a way to fix this. Add ctrl+i keybind when viewing an image on 4chan to reverse image search it (using google, yandex and saucenao). Show filename at the bottom when viewing an image/video on 4chan. +Add ctrl+c keybinding to copy the url of the previewing image. +Add ctrl+c keybiding to copy the url to the currently selected post on 4chan. +Add ctrl+s to save the previewing image/video (for images that would be a copy, since its already stored in cache and for videos youtube-dl would be used). Show the last message in a room as the body item description in matrix room view. Show some kind of indication that there are new messages in a room in matrix room view, and also show another indication if somebody mentioned us (and how many times). Show the rooms menu on the left side when the window is large in matrix. Use https://github.com/simdjson/simdjson as a json library. -Sanitize check: do not allow pasting more than 2gb of text.
\ No newline at end of file +Sanitize check: do not allow pasting more than 2gb of text. +Add search bar for matrix rooms. +Put rooms with recent messages at the top and the ones that mention us further at the top (matrix). +Allow setting matrix room priority (if it should always be at top).
\ No newline at end of file diff --git a/include/Text.hpp b/include/Text.hpp index d34fd47..0244ba1 100644 --- a/include/Text.hpp +++ b/include/Text.hpp @@ -46,6 +46,13 @@ namespace QuickMedia //bool ownLine; // Currently only used for emoji, to make emoji bigger when it's the only thing on a line bool is_japanese; }; + + struct VertexRef { + int vertices_index; // index to |vertices| VertexArray + int index; // index within vertices[vertices_index] + int line; + sf::Uint32 codepoint; + }; class Text { @@ -94,14 +101,9 @@ namespace QuickMedia UP, DOWN, HOME, - END - }; - - struct VertexRef { - int vertices_index; // index to |vertices| VertexArray - int index; // index within vertices[vertices_index] - int line; - sf::Uint32 codepoint; + END, + LEFT, + RIGHT }; void updateCaret(); @@ -115,6 +117,11 @@ namespace QuickMedia float get_text_quad_left_side(const VertexRef &vertex_ref) const; float get_text_quad_right_side(const VertexRef &vertex_ref) const; + // If the index is past the end, then the caret offset is the right side of the last character, rather than the left side + float get_caret_offset_by_caret_index(int index) const; + VertexRef& get_vertex_ref_clamp(int index); + // Takes into consideration if index is the last vertex and the last vertex is a newline, then it should be on its own line + int get_vertex_line(int index) const; private: sf::String str; // TODO: Remove this for non-editable text??? const sf::Font *font; @@ -138,6 +145,7 @@ namespace QuickMedia std::vector<TextElement> textElements; int caretIndex; + float caret_offset_x; sf::Vector2f caretPosition; sf::Clock lastSeenTimer; sf::Vector2u renderTargetSize; diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 6a10da4..8a7cbf6 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -2984,7 +2984,7 @@ namespace QuickMedia { body->clamp_selection(); } body->set_page_scroll(previous_page_scroll); - } else if(event.key.code == sf::Keyboard::C && event.key.control && selected_item) { + } else if(event.key.code == sf::Keyboard::M && event.key.control && selected_item) { navigation_stage = NavigationStage::REPLYING; } else if(event.key.code == sf::Keyboard::R && selected_item) { std::string text_to_add = ">>" + selected_item->post_number; @@ -3348,7 +3348,7 @@ namespace QuickMedia { sf::Sprite logo_sprite(plugin_logo); - Entry chat_input("Press ctrl+c to begin writing a message...", font.get(), cjk_font.get()); + Entry chat_input("Press ctrl+m to begin writing a message...", font.get(), cjk_font.get()); chat_input.set_editable(false); chat_input.on_submit_callback = [matrix, &chat_input, &tabs, &selected_tab, ¤t_room_id, &new_page, &typing_message](const sf::String &text) { if(tabs[selected_tab].type == ChatTabType::MESSAGES) { @@ -3519,7 +3519,7 @@ namespace QuickMedia { } } - if(!typing_message && tabs[selected_tab].type == ChatTabType::MESSAGES && event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::C && event.key.control) { + if(!typing_message && tabs[selected_tab].type == ChatTabType::MESSAGES && event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::M && event.key.control) { chat_input.set_editable(true); typing_message = true; } diff --git a/src/Text.cpp b/src/Text.cpp index 3962374..5e0ad87 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -42,7 +42,8 @@ namespace QuickMedia caretMoveDirection(CaretMoveDirection::NONE), lineSpacing(0.0f), characterSpacing(0.0f), - caretIndex(0) + caretIndex(0), + caret_offset_x(0.0f) { vertices[0].setPrimitiveType(sf::PrimitiveType::Quads); vertices[1].setPrimitiveType(sf::PrimitiveType::Quads); @@ -229,6 +230,39 @@ namespace QuickMedia float Text::get_text_quad_right_side(const VertexRef &vertex_ref) const { return vertices[vertex_ref.vertices_index][vertex_ref.index + 1].position.x; } + + float Text::get_caret_offset_by_caret_index(int index) const { + const int num_vertices = vertices_linear.size(); + if(num_vertices == 0) + return 0.0f; + else if(index < num_vertices) + return get_text_quad_left_side(vertices_linear[index]); + else + return get_text_quad_right_side(vertices_linear[num_vertices - 1]); + } + + VertexRef& Text::get_vertex_ref_clamp(int index) { + const int num_vertices = vertices_linear.size(); + assert(num_vertices > 0); + if(index == num_vertices) + return vertices_linear.back(); + else + return vertices_linear[index]; + } + + int Text::get_vertex_line(int index) const { + const int num_vertices = vertices_linear.size(); + if(num_vertices == 0) { + return 0; + } else if(index < num_vertices) { + return vertices_linear[index].line; + } else { + if(vertices_linear.back().codepoint == '\n') + return vertices_linear.back().line + 1; + else + return vertices_linear.back().line; + } + } void Text::updateGeometry(bool update_even_if_not_dirty) { if(dirtyText) { @@ -299,12 +333,12 @@ namespace QuickMedia } 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(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() }; + vertices[vertices_index][vertexStart + 2] = { sf::Vector2f(glyphPos.x, 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 = 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; } @@ -349,6 +383,19 @@ namespace QuickMedia // TODO: Binary search? 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]; + 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; + switch(vertex_ref.codepoint) { case ' ': case '\t': @@ -363,18 +410,6 @@ namespace QuickMedia 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; @@ -423,58 +458,65 @@ namespace QuickMedia } boundingBox.height = num_lines * line_height; dirty = false; + + // TODO: Clear vertices_linear when not in edit mode, but take into consideration switching between edit/not-edit mode } - // TODO: Fix caret up/down navigation! its broken because of newlines void Text::updateCaret() { assert(!dirty && !dirtyText); if(vertices_linear.empty()) { caretIndex = 0; caretPosition = sf::Vector2f(0.0f, floor(font->getLineSpacing(characterSize))); + caret_offset_x = 0.0f; return; } - switch(caretMoveDirection) - { - case CaretMoveDirection::UP: - { + switch(caretMoveDirection) { + case CaretMoveDirection::NONE: { + caret_offset_x = get_caret_offset_by_caret_index(caretIndex); + break; + } + case CaretMoveDirection::UP: { caretIndex = getPreviousLineClosestPosition(caretIndex); break; } - case CaretMoveDirection::DOWN: - { + case CaretMoveDirection::DOWN: { caretIndex = getNextLineClosestPosition(caretIndex); break; } - case CaretMoveDirection::HOME: - { + case CaretMoveDirection::HOME: { caretIndex = getStartOfLine(caretIndex); + caret_offset_x = get_caret_offset_by_caret_index(caretIndex); break; } - case CaretMoveDirection::END: - { + case CaretMoveDirection::END: { caretIndex = getEndOfLine(caretIndex); + caret_offset_x = get_caret_offset_by_caret_index(caretIndex); + break; + } + case CaretMoveDirection::LEFT: { + caretIndex = std::max(0, caretIndex - 1); + caret_offset_x = get_caret_offset_by_caret_index(caretIndex); break; } - case CaretMoveDirection::NONE: - // Ignore... + case CaretMoveDirection::RIGHT: { + caretIndex = std::min((int)vertices_linear.size(), caretIndex + 1); + caret_offset_x = get_caret_offset_by_caret_index(caretIndex); break; + } } 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))); + VertexRef &last_vertex = get_vertex_ref_clamp(caretIndex); + if(last_vertex.codepoint == '\n') + caretPosition.x = 0.0f; + else + caretPosition.x = get_text_quad_right_side(last_vertex); + caretPosition.y = (1 + get_vertex_line(caretIndex)) * floor(font->getLineSpacing(characterSize) + lineSpacing); } 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); - } + caretPosition.x = get_caret_offset_by_caret_index(caretIndex); + caretPosition.y = (1 + get_vertex_line(caretIndex)) * floor(font->getLineSpacing(characterSize) + lineSpacing); } } @@ -482,15 +524,9 @@ namespace QuickMedia int Text::getStartOfLine(int startIndex) const { assert(!dirty && !dirtyText); - 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; + int start_line = get_vertex_line(startIndex); 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; + if(get_vertex_line(i) != start_line) { return i + 1; } } @@ -502,45 +538,32 @@ namespace QuickMedia { assert(!dirty && !dirtyText); 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; + int start_line = get_vertex_line(startIndex); + for(int i = startIndex + 1; i < num_vertices; ++i) { + if(get_vertex_line(i) != start_line) { return i - 1; } } - return (int)vertices_linear.size(); + return num_vertices; } // TODO: This can be optimized by using binary search int Text::getPreviousLineClosestPosition(int startIndex) const { assert(!dirty && !dirtyText); - 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); - } + const int start_line = get_vertex_line(startIndex); float closest_char = 999999.9f; - for(int i = getStartOfLine(startIndex) - 1; i >= 0; --i) { - //if(vertices_linear[i].codepoint == '\n') - // continue; + int closest_index = -1; + for(int i = getStartOfLine(startIndex) - 1; i >= 0 && get_vertex_line(i) == start_line - 1; --i) { 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; + const float pos_diff = std::abs(caret_offset_x - left_pos); + if(pos_diff < closest_char) { + closest_char = pos_diff; + closest_index = i; + } } + if(closest_index != -1) + return closest_index; return 0; } @@ -549,25 +572,20 @@ namespace QuickMedia { assert(!dirty && !dirtyText); 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; - } + const int start_line = get_vertex_line(startIndex); float closest_char = 999999.9f; - for(int i = getEndOfLine(startIndex) + 1; i < (int)vertices_linear.size(); ++i) { - //if(vertices_linear[i].codepoint == '\n') - // continue; + int closest_index = -1; + for(int i = getEndOfLine(startIndex) + 1; i < num_vertices && get_vertex_line(i) == start_line + 1; ++i) { 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; + const float pos_diff = std::abs(caret_offset_x - left_pos); + if(pos_diff < closest_char) { + closest_char = pos_diff; + closest_index = i; + } } - return (int)vertices_linear.size(); + if(closest_index != -1) + return closest_index; + return num_vertices; } // TODO: Optimize text editing by only processing the changed parts in updateGeometry. @@ -577,18 +595,18 @@ namespace QuickMedia { if(!editable) return; - bool caretAtEnd = textElements.size() == 0 || textElements[0].text.size == 0 || caretIndex == (int)textElements[0].text.size; + bool caretAtEnd = caretIndex == (int)vertices_linear.size(); if(event.type == sf::Event::KeyPressed) { if(event.key.code == sf::Keyboard::Left && caretIndex > 0) { - --caretIndex; + caretMoveDirection = CaretMoveDirection::LEFT; dirtyCaret = true; } else if(event.key.code == sf::Keyboard::Right && !caretAtEnd) { - ++caretIndex; + caretMoveDirection = CaretMoveDirection::RIGHT; dirtyCaret = true; } else if(event.key.code == sf::Keyboard::BackSpace && caretIndex > 0) @@ -621,7 +639,7 @@ namespace QuickMedia { caretMoveDirection = CaretMoveDirection::END; } - else if(event.key.code == sf::Keyboard::Return) + else if(event.key.code == sf::Keyboard::Enter) { if(event.key.shift) { |