From 6309e29ca8cb39126eea8ace6f89fd6972952371 Mon Sep 17 00:00:00 2001 From: Przemyslaw Brudny Date: Mon, 13 Dec 2021 23:41:41 +0100 Subject: [PATCH] [EGD-7979] TextLine split and cleanup to support SingleLine text TextLine split and cleanup to support SingleLine text. --- module-gui/gui/widgets/text/CMakeLists.txt | 1 + module-gui/gui/widgets/text/Text.cpp | 27 ++- module-gui/gui/widgets/text/Text.hpp | 4 +- module-gui/gui/widgets/text/TextFixedSize.cpp | 3 +- .../widgets/text/core/cursors/TextCursor.cpp | 9 + .../widgets/text/core/cursors/TextCursor.hpp | 2 + .../text/core/cursors/TextLineCursor.cpp | 9 + .../text/core/cursors/TextLineCursor.hpp | 3 + .../gui/widgets/text/core/lines/Lines.cpp | 33 +++- .../gui/widgets/text/core/lines/Lines.hpp | 22 ++- .../widgets/text/core/lines/MultiTextLine.cpp | 150 ++++++++++++++++ .../widgets/text/core/lines/MultiTextLine.hpp | 37 ++++ .../text/core/lines/ScopedParentDisown.hpp | 31 ++++ .../gui/widgets/text/core/lines/TextLine.cpp | 163 +----------------- .../gui/widgets/text/core/lines/TextLine.hpp | 25 +-- .../test/test-catch-text/CMakeLists.txt | 2 +- ...extLine.cpp => test-gui-MultiTextLine.cpp} | 28 +-- .../test-gui-TextLineCursor.cpp | 2 +- 18 files changed, 339 insertions(+), 212 deletions(-) create mode 100644 module-gui/gui/widgets/text/core/lines/MultiTextLine.cpp create mode 100644 module-gui/gui/widgets/text/core/lines/MultiTextLine.hpp create mode 100644 module-gui/gui/widgets/text/core/lines/ScopedParentDisown.hpp rename module-gui/test/test-catch-text/{test-gui-TextLine.cpp => test-gui-MultiTextLine.cpp} (85%) diff --git a/module-gui/gui/widgets/text/CMakeLists.txt b/module-gui/gui/widgets/text/CMakeLists.txt index c5ab6351f03c4058366d1e61597aac503e5b2346..92fc50a960368fa9ad27aff3033535f35accb0ea 100644 --- a/module-gui/gui/widgets/text/CMakeLists.txt +++ b/module-gui/gui/widgets/text/CMakeLists.txt @@ -5,6 +5,7 @@ target_sources( ${PROJECT_NAME} "${CMAKE_CURRENT_LIST_DIR}/core/cursors/TextLineCursor.cpp" "${CMAKE_CURRENT_LIST_DIR}/core/lines/Lines.cpp" "${CMAKE_CURRENT_LIST_DIR}/core/lines/TextLine.cpp" + "${CMAKE_CURRENT_LIST_DIR}/core/lines/MultiTextLine.cpp" "${CMAKE_CURRENT_LIST_DIR}/core/RawText.cpp" "${CMAKE_CURRENT_LIST_DIR}/core/TextBlock.cpp" "${CMAKE_CURRENT_LIST_DIR}/core/TextDocument.cpp" diff --git a/module-gui/gui/widgets/text/Text.cpp b/module-gui/gui/widgets/text/Text.cpp index 49d0e1a2702661ecb42e63c158eedaa00d93d6da..ac9d5ff17a96f0193eab9bc4ff1f5fa7197e187d 100644 --- a/module-gui/gui/widgets/text/Text.cpp +++ b/module-gui/gui/widgets/text/Text.cpp @@ -75,7 +75,12 @@ namespace gui } void Text::setTextType(TextType type) - {} + { + if (textType != type) { + textType = type; + drawLines(); + } + } void Text::setTextLimitType(TextLimitType limitType, unsigned int val) { @@ -369,7 +374,8 @@ namespace gui getSizeMinusPadding(Axis::X, Area::Max) - TextCursor::defaultWidth, getSizeMinusPadding(Axis::Y, Area::Max), padding.top, - padding.left); + padding.left, + textType); calculateAndRequestSize(); @@ -452,10 +458,15 @@ namespace gui drawLines(); } + TextCursor *Text::createCursor() + { + return new TextLineCursor(this); + } + void Text::buildCursor() { erase(cursor); - cursor = new TextLineCursor(this); + cursor = createCursor(); cursor->setCursorStartPosition(cursorStartPosition); cursor->setAlignment(this->getAlignment()); cursor->setMargins(this->getPadding()); @@ -504,7 +515,7 @@ namespace gui return false; } - if (isMode(EditMode::Scroll)) { + if (isMode(EditMode::Scroll) && textType == TextType::MultiLine) { debug_text("Text in scroll mode ignores left/right navigation"); if (inputEvent.is(KeyCode::KEY_LEFT) || inputEvent.is(KeyCode::KEY_RIGHT)) { @@ -512,11 +523,11 @@ namespace gui } if (inputEvent.is(KeyCode::KEY_DOWN)) { - return cursor->displayNextLine(); + return cursor->handleNextLine(); } if (inputEvent.is(KeyCode::KEY_UP)) { - return cursor->displayPreviousLine(); + return cursor->handlePreviousLine(); } } @@ -643,7 +654,7 @@ namespace gui preDrawCursor.addChar(utfVal); preDrawLines->draw( - preDrawCursor, getSizeMinusPadding(Axis::X, Area::Max), text::npos, padding.top, padding.left); + preDrawCursor, getSizeMinusPadding(Axis::X, Area::Max), text::npos, padding.top, padding.left, textType); return preDrawLines; } @@ -661,7 +672,7 @@ namespace gui preDrawCursor.addTextBlock(std::move(preDrawTextBlock)); preDrawLines->draw( - preDrawCursor, getSizeMinusPadding(Axis::X, Area::Max), text::npos, padding.top, padding.left); + preDrawCursor, getSizeMinusPadding(Axis::X, Area::Max), text::npos, padding.top, padding.left, textType); return preDrawLines; } diff --git a/module-gui/gui/widgets/text/Text.hpp b/module-gui/gui/widgets/text/Text.hpp index a819b4c7fb7a8414f9738807364a5420e9886f8b..76ee86b70aae1c29d9366ccd5209e415662682a6 100644 --- a/module-gui/gui/widgets/text/Text.hpp +++ b/module-gui/gui/widgets/text/Text.hpp @@ -48,7 +48,7 @@ namespace gui protected: // holds list of labels for displaying currently visible text lines. - TextLineCursor *cursor = nullptr; + TextCursor *cursor = nullptr; CursorStartPosition cursorStartPosition = CursorStartPosition::DocumentEnd; std::unique_ptr document = std::make_unique(std::list()); InputMode *mode = nullptr; @@ -57,6 +57,8 @@ namespace gui void buildDocument(const UTF8 &text); void buildDocument(std::unique_ptr &&document); void buildCursor(); + TextCursor *createCursor(); + /// show cursor if cursor should be visible void showCursor(bool focus); diff --git a/module-gui/gui/widgets/text/TextFixedSize.cpp b/module-gui/gui/widgets/text/TextFixedSize.cpp index 02da3e3751c3a0ff0fba947171192f1a5f7331fa..e97db47c5b1efb8fc0044dc2d2b23f035291643a 100644 --- a/module-gui/gui/widgets/text/TextFixedSize.cpp +++ b/module-gui/gui/widgets/text/TextFixedSize.cpp @@ -58,7 +58,8 @@ namespace gui getSizeMinusPadding(Axis::Y, Area::Normal), padding.top, padding.left, - linesCount); + linesCount, + textType); lines->linesHAlign(getSizeMinusPadding(Axis::X, Area::Normal)); lines->linesVAlign(getSizeMinusPadding(Axis::Y, Area::Normal)); diff --git a/module-gui/gui/widgets/text/core/cursors/TextCursor.cpp b/module-gui/gui/widgets/text/core/cursors/TextCursor.cpp index 24cf8f35dcbabe38e28634b990931e589f82cf9e..9a43316020ddccbf9b3b14993c8e60717d2a8b55 100644 --- a/module-gui/gui/widgets/text/core/cursors/TextCursor.cpp +++ b/module-gui/gui/widgets/text/core/cursors/TextCursor.cpp @@ -225,6 +225,15 @@ namespace gui return pos + BlockCursor::getPosition(); } + bool TextCursor::handleNextLine() + { + return false; + } + bool TextCursor::handlePreviousLine() + { + return false; + } + } // namespace gui const char *c_str(enum gui::TextCursor::Move what) diff --git a/module-gui/gui/widgets/text/core/cursors/TextCursor.hpp b/module-gui/gui/widgets/text/core/cursors/TextCursor.hpp index 000d3943885431c8b04111de85e171fb4dadc62a..85d8c881c39649210c2e3d8a297f3adad97a1a3c 100644 --- a/module-gui/gui/widgets/text/core/cursors/TextCursor.hpp +++ b/module-gui/gui/widgets/text/core/cursors/TextCursor.hpp @@ -49,6 +49,8 @@ namespace gui /// removeChar) virtual Move moveCursor(NavigationDirection direction); virtual Move moveCursor(NavigationDirection direction, unsigned int n); + [[nodiscard]] virtual bool handleNextLine(); + [[nodiscard]] virtual bool handlePreviousLine(); // TODO note to self - here should be too UTF8 char handling, not in document... // cursor can pass processing char directly to TextBlock we are interested in... diff --git a/module-gui/gui/widgets/text/core/cursors/TextLineCursor.cpp b/module-gui/gui/widgets/text/core/cursors/TextLineCursor.cpp index 95b05832cb8227e45d2b0e659a72a0738220850e..e6b26516009ee639297a45b571936f9b9be330d8 100644 --- a/module-gui/gui/widgets/text/core/cursors/TextLineCursor.cpp +++ b/module-gui/gui/widgets/text/core/cursors/TextLineCursor.cpp @@ -108,6 +108,15 @@ namespace gui return false; } + bool TextLineCursor::handleNextLine() + { + return displayNextLine(); + } + bool TextLineCursor::handlePreviousLine() + { + return displayPreviousLine(); + } + auto TextLineCursor::moveCursor(gui::NavigationDirection direction) -> gui::TextCursor::Move { debug_text_cursor("Before move cursor: screen pos: %d block: %d pos: %d %s", diff --git a/module-gui/gui/widgets/text/core/cursors/TextLineCursor.hpp b/module-gui/gui/widgets/text/core/cursors/TextLineCursor.hpp index c7114bdc5919c99c72e3c56cd5f167ccd12da2a8..8d3e19aa8c61023a783b0d6d8ff1785de28013e0 100644 --- a/module-gui/gui/widgets/text/core/cursors/TextLineCursor.hpp +++ b/module-gui/gui/widgets/text/core/cursors/TextLineCursor.hpp @@ -17,6 +17,9 @@ namespace gui void handleDownNavigation(unsigned int selectedLineNumber, unsigned int selectedLineCursorPosition); void handleUpNavigation(unsigned int selectedLineNumber, unsigned int selectedLineCursorPosition); + [[nodiscard]] bool handleNextLine() override; + [[nodiscard]] bool handlePreviousLine() override; + public: explicit TextLineCursor(gui::Text *parent, unsigned int pos = text::npos, unsigned int block = text::npos); TextLineCursor() = delete; diff --git a/module-gui/gui/widgets/text/core/lines/Lines.cpp b/module-gui/gui/widgets/text/core/lines/Lines.cpp index 21316323be0606594b933ff9460153b476e23e17..f8ba5d722b6c6a37987752759129889de121a5cb 100644 --- a/module-gui/gui/widgets/text/core/lines/Lines.cpp +++ b/module-gui/gui/widgets/text/core/lines/Lines.cpp @@ -2,6 +2,7 @@ // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "Lines.hpp" +#include "MultiTextLine.hpp" #include #if DEBUG_GUI_TEXT_LINES == 1 @@ -33,13 +34,26 @@ namespace gui } } - auto Lines::draw(BlockCursor &drawCursor, Length w, Length h, Position lineYPosition, Position lineXPosition) + auto Lines::draw( + BlockCursor &drawCursor, Length w, Length h, Position lineYPosition, Position lineXPosition, TextType drawType) -> void + { + drawMultiLine(drawCursor, w, h, lineYPosition, lineXPosition); + } + + auto Lines::drawSingleLine( + BlockCursor &drawCursor, Length w, Length h, Position lineYPosition, Position lineXPosition) -> void + { + // TODO in EGD-3411 + } + + auto Lines::drawMultiLine( + BlockCursor &drawCursor, Length w, Length h, Position lineYPosition, Position lineXPosition) -> void { Position initialTopPadding = lineYPosition; while (true) { - auto textLine = gui::TextLine(drawCursor, w); + auto textLine = gui::MultiTextLine(drawCursor, w); if (textLine.length() == 0 && textLine.getLineEnd()) { debug_text_lines("cant show more text from this document"); @@ -72,13 +86,24 @@ namespace gui Length h, Position lineYPosition, Position lineXPosition, - unsigned int linesCount) -> void + unsigned int linesCount, + TextType drawType) -> void + { + drawMultiLine(drawCursor, w, h, lineYPosition, lineXPosition, linesCount); + } + + auto Lines::drawMultiLine(BlockCursor &drawCursor, + Length w, + Length h, + Position lineYPosition, + Position lineXPosition, + unsigned int linesCount) -> void { Position initialTopPadding = lineYPosition; Length initHeight = text->getTextFormat().getFont()->info.line_height; while (true) { - auto textLine = gui::TextLine(drawCursor, w, initHeight, underLineProperties); + auto textLine = gui::MultiTextLine(drawCursor, w, initHeight, underLineProperties); if ((textLine.height() > 0) && initHeight != textLine.height()) { initHeight = textLine.height(); diff --git a/module-gui/gui/widgets/text/core/lines/Lines.hpp b/module-gui/gui/widgets/text/core/lines/Lines.hpp index 3245bba8d34442e4597e9b6e6de46a16e61eebfe..6ec3adf6db48f84981dceb12605441ba3cf93800 100644 --- a/module-gui/gui/widgets/text/core/lines/Lines.hpp +++ b/module-gui/gui/widgets/text/core/lines/Lines.hpp @@ -12,6 +12,7 @@ namespace gui class Lines { + private: Text *text = nullptr; std::list lines; UnderLineProperties underLineProperties; @@ -19,6 +20,17 @@ namespace gui void addToInvisibleLines(TextLine line); + auto drawMultiLine(BlockCursor &drawCursor, Length w, Length h, Position lineYPosition, Position lineXPosition) + -> void; + auto drawMultiLine(BlockCursor &drawCursor, + Length w, + Length h, + Position lineYPosition, + Position lineXPosition, + unsigned int linesCount) -> void; + auto drawSingleLine(BlockCursor &drawCursor, Length w, Length h, Position lineYPosition, Position lineXPosition) + -> void; + public: explicit Lines(Text *text) : text(text) {} @@ -136,13 +148,19 @@ namespace gui linesSpacing = val; } - auto draw(BlockCursor &drawCursor, Length w, Length h, Position lineYPosition, Position lineXPosition) -> void; auto draw(BlockCursor &drawCursor, Length w, Length h, Position lineYPosition, Position lineXPosition, - unsigned int linesCount) -> void; + TextType drawType) -> void; + auto draw(BlockCursor &drawCursor, + Length w, + Length h, + Position lineYPosition, + Position lineXPosition, + unsigned int linesCount, + TextType drawType) -> void; auto linesHAlign(Length parentSize) -> void; auto linesVAlign(Length parentSize) -> void; diff --git a/module-gui/gui/widgets/text/core/lines/MultiTextLine.cpp b/module-gui/gui/widgets/text/core/lines/MultiTextLine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6825cd68d3212e5dc923e33bb20772aafc727d3c --- /dev/null +++ b/module-gui/gui/widgets/text/core/lines/MultiTextLine.cpp @@ -0,0 +1,150 @@ +// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "MultiTextLine.hpp" + +namespace gui +{ + /// Note - line breaking could be done here with different TextLines to return + /// or via different block types (i.e. numeric block tyle could be not "breakable" + MultiTextLine::MultiTextLine(BlockCursor &localCursor, unsigned int maxWidth) : TextLine(maxWidth) + { + do { + if (!localCursor) { // cursor is faulty + lineEnd = true; + return; + } + + if (localCursor.atEnd() && !localCursor.checkLastNewLine()) { + lineEnd = true; + return; + } + + // take text we want to show + auto text = localCursor.getUTF8Text(); + + if (text.length() == 0 && localCursor.checkCurrentBlockNoNewLine() && !localCursor.atEnd()) { + setLineStartConditions(localCursor.getBlockNumber(), localCursor.getPosition()); + localCursor.goToNextBlock(); + continue; + } + + auto textFormat = localCursor->getFormat(); + if (textFormat->getFont() == nullptr) { + lineEnd = true; + return; + } + + // check if max provided width is enough to enter one char at least + if (maxWidth < textFormat->getFont()->getCharPixelWidth(text[0])) { + lineEnd = true; + return; + } + + auto signsCountToShow = calculateSignsToShow(localCursor, text, maxWidth - widthUsed); + + // we can show nothing - this is the end of this line + if (signsCountToShow == 0) { + auto item = buildUITextPart("", textFormat); + widthUsed = item->widgetArea.w; + heightUsed = std::max(heightUsed, item->widgetArea.h); + lineContent.emplace_back(item); + end = TextBlock::End::None; + + setLineStartConditions(localCursor.getBlockNumber(), localCursor.getPosition()); + + return; + } + + // create item for show and update Line data + auto item = buildUITextPart(textToPrint(signsCountToShow, text), textFormat); + shownLetterCount += signsCountToShow; + widthUsed += item->widgetArea.w; + heightUsed = std::max(heightUsed, item->widgetArea.h); + lineContent.emplace_back(item); + + setLineStartConditions(localCursor.getBlockNumber(), localCursor.getPosition()); + + localCursor += signsCountToShow; + + if (removeTrailingSpace) { + end = TextBlock::End::Newline; + return; + } + + if (localCursor.checkAndInvalidateBlockChanged() && localCursor.checkPreviousBlockNewLine()) { + end = TextBlock::End::Newline; + return; + } + + // not whole text shown, try again for next line if you want + if (signsCountToShow < text.length()) { + end = TextBlock::End::None; + return; + } + + } while (true); + } + + unsigned int MultiTextLine::calculateSignsToShow(BlockCursor &localCursor, UTF8 &text, unsigned int space) + { + auto signsCountToShow = localCursor->getFormat()->getFont()->getCharCountInSpace(text, space); + + // additional one sign to detect potential space as last character in line + auto searchSubstring = text.substr(0, signsCountToShow + 1); + + auto newlinePos = searchSubstring.findLast("\n", signsCountToShow); + auto spacePos = searchSubstring.findLast(" ", signsCountToShow); + + // check if space or newline in word detection range + if (spacePos <= signsCountToShow - text::word_detection_range) { + spacePos = UTF8::npos; + } + + if (newlinePos <= signsCountToShow - text::word_detection_range) { + newlinePos = UTF8::npos; + } + + // check if space found and no newline at end + if (spacePos != UTF8::npos && newlinePos == UTF8::npos) { + // if line ends on space remove it when drawing + if (spacePos >= signsCountToShow) { + removeTrailingSpace = true; + } + + // add one to include space in the line + signsCountToShow = spacePos + 1; + + breakLineDashAddition = false; + } + // if sings still left in text add dash sign + else if (signsCountToShow != 0 && signsCountToShow < text.length()) { + // decrease character shown count by one to fit dash + signsCountToShow = signsCountToShow - 1; + breakLineDashAddition = true; + } + + return signsCountToShow; + } + + UTF8 MultiTextLine::textToPrint(unsigned int signsCountToShow, UTF8 &text) + { + auto textToPrint = text.substr(0, signsCountToShow); + + if (removeTrailingSpace) { + textToPrint = text.substr(0, signsCountToShow - 1); + } + + if (breakLineDashAddition) { + textToPrint = textToPrint + "-"; + } + + return textToPrint; + } + + MultiTextLine::MultiTextLine(MultiTextLine &&from) noexcept : TextLine(std::move(from)) + { + breakLineDashAddition = from.breakLineDashAddition; + removeTrailingSpace = from.removeTrailingSpace; + } +} // namespace gui diff --git a/module-gui/gui/widgets/text/core/lines/MultiTextLine.hpp b/module-gui/gui/widgets/text/core/lines/MultiTextLine.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1d1ed2ce92eafe4c6a6823abe694601e417ffa9d --- /dev/null +++ b/module-gui/gui/widgets/text/core/lines/MultiTextLine.hpp @@ -0,0 +1,37 @@ +// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +#include "TextLine.hpp" + +namespace gui +{ + class MultiTextLine : public TextLine + { + private: + bool breakLineDashAddition = false; + bool removeTrailingSpace = false; + + unsigned int calculateSignsToShow(BlockCursor &localCursor, UTF8 &text, unsigned int space); + UTF8 textToPrint(unsigned int signsCountToShow, UTF8 &text); + + public: + /// creates TextLine with data from text based on TextCursor position filling max_width + MultiTextLine(BlockCursor &, unsigned int maxWidth); + MultiTextLine(MultiTextLine &) = delete; + MultiTextLine(MultiTextLine &&) noexcept; + + MultiTextLine(BlockCursor &cursor, + unsigned int maxWidth, + unsigned int initHeight, + UnderLineProperties underLineProperties) + : MultiTextLine(cursor, maxWidth) + { + this->underLineProperties = underLineProperties; + this->underLineProperties.underlineHeight = initHeight; + } + + ~MultiTextLine() = default; + }; +} // namespace gui diff --git a/module-gui/gui/widgets/text/core/lines/ScopedParentDisown.hpp b/module-gui/gui/widgets/text/core/lines/ScopedParentDisown.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e0bc11e11d5b1e97ecd6160da81e3ecb72148a85 --- /dev/null +++ b/module-gui/gui/widgets/text/core/lines/ScopedParentDisown.hpp @@ -0,0 +1,31 @@ +// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +#include + +namespace gui +{ + /// class to disown Item temporary to ignore callback + class ScopedParentDisown + { + Item *parent = nullptr; + Item *item = nullptr; + + public: + explicit ScopedParentDisown(Item *it) : item(it) + { + if (item != nullptr) { + parent = item->parent; + } + } + + ~ScopedParentDisown() + { + if (item != nullptr) { + item->parent = parent; + } + } + }; +} // namespace gui diff --git a/module-gui/gui/widgets/text/core/lines/TextLine.cpp b/module-gui/gui/widgets/text/core/lines/TextLine.cpp index 4397e5f1e977913085ca50015de797c908f73c6c..495d31eea3dfda3febed6b4a5de77c11236a9374 100644 --- a/module-gui/gui/widgets/text/core/lines/TextLine.cpp +++ b/module-gui/gui/widgets/text/core/lines/TextLine.cpp @@ -7,149 +7,12 @@ namespace gui { /// helper function to get our text representation - RawText *buildUITextPart(const UTF8 &text, const TextFormat *format) + RawText *TextLine::buildUITextPart(const UTF8 &text, const TextFormat *format) { auto item = new gui::RawText(text, format->getFont(), format->getColor()); return item; } - /// Note - line breaking could be done here with different TextLines to return - /// or via different block types (i.e. numeric block tyle could be not "breakable" - TextLine::TextLine(BlockCursor &localCursor, unsigned int maxWidth) : maxWidth(maxWidth) - { - do { - if (!localCursor) { // cursor is faulty - lineEnd = true; - return; - } - - if (localCursor.atEnd() && !localCursor.checkLastNewLine()) { - lineEnd = true; - return; - } - - // take text we want to show - auto text = localCursor.getUTF8Text(); - - if (text.length() == 0 && localCursor.checkCurrentBlockNoNewLine() && !localCursor.atEnd()) { - setLineStartConditions(localCursor.getBlockNumber(), localCursor.getPosition()); - localCursor.goToNextBlock(); - continue; - } - - auto textFormat = localCursor->getFormat(); - if (textFormat->getFont() == nullptr) { - lineEnd = true; - return; - } - - // check if max provided width is enough to enter one char at least - if (maxWidth < textFormat->getFont()->getCharPixelWidth(text[0])) { - lineEnd = true; - return; - } - - auto signsCountToShow = calculateSignsToShow(localCursor, text, maxWidth - widthUsed); - - // we can show nothing - this is the end of this line - if (signsCountToShow == 0) { - auto item = buildUITextPart("", textFormat); - widthUsed = item->widgetArea.w; - heightUsed = std::max(heightUsed, item->widgetArea.h); - lineContent.emplace_back(item); - end = TextBlock::End::None; - - setLineStartConditions(localCursor.getBlockNumber(), localCursor.getPosition()); - - return; - } - - // create item for show and update Line data - auto item = buildUITextPart(textToPrint(signsCountToShow, text), textFormat); - shownLetterCount += signsCountToShow; - widthUsed += item->widgetArea.w; - heightUsed = std::max(heightUsed, item->widgetArea.h); - lineContent.emplace_back(item); - - setLineStartConditions(localCursor.getBlockNumber(), localCursor.getPosition()); - - localCursor += signsCountToShow; - - if (removeTrailingSpace) { - end = TextBlock::End::Newline; - return; - } - - if (localCursor.checkAndInvalidateBlockChanged() && localCursor.checkPreviousBlockNewLine()) { - end = TextBlock::End::Newline; - return; - } - - // not whole text shown, try again for next line if you want - if (signsCountToShow < text.length()) { - end = TextBlock::End::None; - return; - } - - } while (true); - } - - unsigned int TextLine::calculateSignsToShow(BlockCursor &localCursor, UTF8 &text, unsigned int space) - { - auto signsCountToShow = localCursor->getFormat()->getFont()->getCharCountInSpace(text, space); - - // additional one sign to detect potential space as last character in line - auto searchSubstring = text.substr(0, signsCountToShow + 1); - - auto newlinePos = searchSubstring.findLast("\n", signsCountToShow); - auto spacePos = searchSubstring.findLast(" ", signsCountToShow); - - // check if space or newline in word detection range - if (spacePos <= signsCountToShow - text::word_detection_range) { - spacePos = UTF8::npos; - } - - if (newlinePos <= signsCountToShow - text::word_detection_range) { - newlinePos = UTF8::npos; - } - - // check if space found and no newline at end - if (spacePos != UTF8::npos && newlinePos == UTF8::npos) { - // if line ends on space remove it when drawing - if (spacePos >= signsCountToShow) { - removeTrailingSpace = true; - } - - // add one to include space in the line - signsCountToShow = spacePos + 1; - - breakLineDashAddition = false; - } - // if sings still left in text add dash sign - else if (signsCountToShow != 0 && signsCountToShow < text.length()) { - // decrease character shown count by one to fit dash - signsCountToShow = signsCountToShow - 1; - breakLineDashAddition = true; - } - - return signsCountToShow; - } - - UTF8 TextLine::textToPrint(unsigned int signsCountToShow, UTF8 &text) - { - auto textToPrint = text.substr(0, signsCountToShow); - - if (removeTrailingSpace) { - textToPrint = text.substr(0, signsCountToShow - 1); - } - - if (breakLineDashAddition) { - textToPrint = textToPrint + "-"; - } - - return textToPrint; - } - TextLine::TextLine(TextLine &&from) noexcept { lineContent = std::move(from.lineContent); @@ -161,8 +24,6 @@ namespace gui lineEnd = from.lineEnd; end = from.end; maxWidth = from.maxWidth; - breakLineDashAddition = from.breakLineDashAddition; - removeTrailingSpace = from.removeTrailingSpace; lineStartBlockNumber = from.lineStartBlockNumber; lineStartBlockPosition = from.lineStartBlockPosition; lineVisible = from.lineVisible; @@ -181,28 +42,6 @@ namespace gui } } - /// class to disown Item temporary to ignore callback - class ScopedParentDisown - { - Item *parent = nullptr; - Item *item = nullptr; - - public: - ScopedParentDisown(Item *it) : item(it) - { - if (item != nullptr) { - parent = item->parent; - } - } - - ~ScopedParentDisown() - { - if (item != nullptr) { - item->parent = parent; - } - } - }; - void TextLine::setPosition(const short &x, const short &y) { auto lineXPosition = x; diff --git a/module-gui/gui/widgets/text/core/lines/TextLine.hpp b/module-gui/gui/widgets/text/core/lines/TextLine.hpp index 7d6092f45a13e9631a98f1b51d576bd20f2e1b2e..6159d6f169ece23a748044d2c6d547ce791f4ce6 100644 --- a/module-gui/gui/widgets/text/core/lines/TextLine.hpp +++ b/module-gui/gui/widgets/text/core/lines/TextLine.hpp @@ -12,6 +12,8 @@ #include #include +#include "ScopedParentDisown.hpp" + namespace gui { enum class UnderlineDrawMode @@ -32,6 +34,7 @@ namespace gui /// interface element for TextDocument->getLine() <-- Text class TextLine { + protected: unsigned int shownLetterCount = 0; Length widthUsed = 0; Length heightUsed = 0; @@ -43,33 +46,19 @@ namespace gui Position storedYOffset = 0; bool lineEnd = false; bool lineVisible = true; - bool breakLineDashAddition = false; - bool removeTrailingSpace = false; unsigned int lineStartBlockNumber = text::npos; unsigned int lineStartBlockPosition = text::npos; - unsigned int calculateSignsToShow(BlockCursor &localCursor, UTF8 &text, unsigned int space); - UTF8 textToPrint(unsigned int signsCountToShow, UTF8 &text); - void createUnderline(unsigned int max_w, unsigned int max_height); + void createUnderline(unsigned int maxWidth, unsigned int maxHeight); void updateUnderline(const short &x, const short &y); void setLineStartConditions(unsigned int startBlockNumber, unsigned int startBlockPosition); + RawText *buildUITextPart(const UTF8 &text, const TextFormat *format); + + explicit TextLine(Length maxWidth) : maxWidth(maxWidth){}; public: - /// creates TextLine with data from text based on TextCursor position filling max_width - TextLine(BlockCursor &, unsigned int max_width); TextLine(TextLine &) = delete; TextLine(TextLine &&) noexcept; - - TextLine(BlockCursor &cursor, - unsigned int max_width, - unsigned int init_height, - UnderLineProperties underLineProperties) - : TextLine(cursor, max_width) - { - this->underLineProperties = underLineProperties; - this->underLineProperties.underlineHeight = init_height; - } - ~TextLine(); /// number of letters in Whole TextLines diff --git a/module-gui/test/test-catch-text/CMakeLists.txt b/module-gui/test/test-catch-text/CMakeLists.txt index 98073ccdbfe54dc15d33c19e14f0ab5652a49d69..78014361050f6a2e1464d3ef397e225a0ee74bfa 100644 --- a/module-gui/test/test-catch-text/CMakeLists.txt +++ b/module-gui/test/test-catch-text/CMakeLists.txt @@ -12,7 +12,7 @@ add_catch2_executable( test-gui-TextBlock.cpp test-gui-TextBlockCursor.cpp test-gui-TextDocument.cpp - test-gui-TextLine.cpp + test-gui-MultiTextLine.cpp test-gui-TextParse.cpp test-gui-Font.cpp test-gui-TextLineCursor.cpp diff --git a/module-gui/test/test-catch-text/test-gui-TextLine.cpp b/module-gui/test/test-catch-text/test-gui-MultiTextLine.cpp similarity index 85% rename from module-gui/test/test-catch-text/test-gui-TextLine.cpp rename to module-gui/test/test-catch-text/test-gui-MultiTextLine.cpp index 1205dfdbc86136ad2da86d8896f14d9a00dfdf8e..74646b7db2e73326caa6ef3b59c8243b04e2e618 100644 --- a/module-gui/test/test-catch-text/test-gui-TextLine.cpp +++ b/module-gui/test/test-catch-text/test-gui-MultiTextLine.cpp @@ -4,7 +4,7 @@ #include "InitializedFontManager.hpp" #include -#include +#include #include #include @@ -22,7 +22,7 @@ TEST_CASE("TextLine - ctor") { Text text; auto cursor = new TextCursor(&text); - auto line = TextLine(*cursor, maxWidth); + auto line = MultiTextLine(*cursor, maxWidth); REQUIRE(line.length() == 0); } @@ -32,7 +32,7 @@ TEST_CASE("TextLine - ctor") auto [document, font] = mockup::buildMultilineTestDocument(texts); auto cursor = std::make_unique(&document, 0, 0, font); - auto line = gui::TextLine(*cursor, maxWidth); + auto line = gui::MultiTextLine(*cursor, maxWidth); REQUIRE(line.length() > 0); REQUIRE(line.length() == texts.front().length()); @@ -44,14 +44,14 @@ TEST_CASE("TextLine - ctor") auto [document, font] = mockup::buildOnelineTestDocument(test_text); auto cursor = std::make_unique(&document, 0, 0, font); - auto line = gui::TextLine(*cursor, maxWidth); + auto line = gui::MultiTextLine(*cursor, maxWidth); REQUIRE(line.length() > 0); REQUIRE(line.length() == test_text.length()); } } -TEST_CASE("TextLine - non fitting text") +TEST_CASE("MultiTextLine - non fitting text") { using namespace gui; @@ -61,14 +61,14 @@ TEST_CASE("TextLine - non fitting text") auto [document, font] = mockup::buildOnelineTestDocument(test_text); auto cursor = std::make_unique(&document, 0, 0, font); - auto line = gui::TextLine(*cursor, maxWidth); + auto line = gui::MultiTextLine(*cursor, maxWidth); REQUIRE(line.length() != 0); REQUIRE(line.length() <= test_text.length()); } } -TEST_CASE("TextLine - multiple styles text") +TEST_CASE("MultiTextLine - multiple styles text") { using namespace gui; mockup::fontManager(); @@ -87,7 +87,7 @@ TEST_CASE("TextLine - multiple styles text") auto document = TextDocument(testblock); auto cursor = std::make_unique(&document, 0, 0, nullptr); - auto line = gui::TextLine(*cursor, maxWidth); + auto line = gui::MultiTextLine(*cursor, maxWidth); REQUIRE(line.width() > 0); REQUIRE(line.length() == getTextLen(testblock)); @@ -103,7 +103,7 @@ TEST_CASE("TextLine - multiple styles text") auto document = TextDocument(testblock); auto cursor = std::make_unique(&document, 0, 0, nullptr); - auto line = gui::TextLine(*cursor, maxWidth); + auto line = gui::MultiTextLine(*cursor, maxWidth); REQUIRE(line.length() == getTextLen(block0)); } @@ -119,7 +119,7 @@ TEST_CASE("TextLine - multiple styles text") auto document = TextDocument(testblock); auto cursor = std::make_unique(&document, 0, block0.size(), nullptr); - auto line = gui::TextLine(*cursor, maxWidth); + auto line = gui::MultiTextLine(*cursor, maxWidth); REQUIRE(line.length() == getTextLen(block1)); } @@ -130,7 +130,7 @@ TEST_CASE("TextLine - multiple styles text") auto document = TextDocument(testblock); auto cursor = std::make_unique(&document, 0, 0, nullptr); - auto line = gui::TextLine(*cursor, maxWidth); + auto line = gui::MultiTextLine(*cursor, maxWidth); REQUIRE(line.length() == 0); REQUIRE(line.width() == 0); @@ -142,14 +142,14 @@ TEST_CASE("TextLine - multiple styles text") auto document = TextDocument(testblock); auto cursor = std::make_unique(&document, 0, 0, nullptr); - auto line = gui::TextLine(*cursor, maxWidth); + auto line = gui::MultiTextLine(*cursor, maxWidth); REQUIRE(line.length() == 0); REQUIRE(line.width() == 0); } } -TEST_CASE("TextLine - elements sizes checkup") +TEST_CASE("MultiTextLine - elements sizes checkup") { using namespace gui; @@ -157,7 +157,7 @@ TEST_CASE("TextLine - elements sizes checkup") auto document = TextDocument(testblock); auto cursor = std::make_unique(&document, 0, 0, nullptr); - auto text_line = TextLine(*cursor, maxWidth); + auto text_line = MultiTextLine(*cursor, maxWidth); REQUIRE(text_line.length() > 0); const Item *it = nullptr; diff --git a/module-gui/test/test-catch-text/test-gui-TextLineCursor.cpp b/module-gui/test/test-catch-text/test-gui-TextLineCursor.cpp index e68285a53d7dcd878d08046e033a20a285d62b78..ae456218c0bfaeb0b5ff08a5566f4893d49c28d8 100644 --- a/module-gui/test/test-catch-text/test-gui-TextLineCursor.cpp +++ b/module-gui/test/test-catch-text/test-gui-TextLineCursor.cpp @@ -27,7 +27,7 @@ namespace gui auto moveCursor(NavigationDirection direction, unsigned int n) { - cursor->TextLineCursor::moveCursor(direction, n); + cursor->moveCursor(direction, n); } auto removeNChars(unsigned int n)