M module-gui/gui/widgets/TextConstants.hpp => module-gui/gui/widgets/TextConstants.hpp +4 -3
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
@@ 9,8 9,9 @@ namespace gui
{
namespace text
{
- const unsigned int npos = std::numeric_limits<unsigned int>().max();
- const char newline = '\n';
+ constexpr auto npos = std::numeric_limits<unsigned int>::max();
+ const char newline = '\n';
+ const unsigned int word_detection_range = 10;
}; // namespace text
enum class TextLimitType
M module-gui/gui/widgets/TextLine.cpp => module-gui/gui/widgets/TextLine.cpp +43 -8
@@ 62,11 62,10 @@ namespace gui
return;
}
- auto canShow = textFormat->getFont()->getCharCountInSpace(text, maxWidth - widthUsed);
+ auto signsCountToShow = calculateSignsToShow(localCursor, text, maxWidth - widthUsed);
// we can show nothing - this is the end of this line
- if (canShow == 0) {
-
+ if (signsCountToShow == 0) {
auto item = buildUITextPart("", textFormat);
widthUsed = item->getTextNeedSpace();
heightUsed = std::max(heightUsed, item->getTextHeight());
@@ 79,15 78,16 @@ namespace gui
}
// create item for show and update Line data
- auto item = buildUITextPart(text.substr(0, canShow), textFormat);
- shownLetterCount += canShow;
+ auto item =
+ buildUITextPart(text.substr(0, signsCountToShow) + (breakLineDashAddition ? "-" : ""), textFormat);
+ shownLetterCount += signsCountToShow;
widthUsed += item->getTextNeedSpace();
heightUsed = std::max(heightUsed, item->getTextHeight());
lineContent.emplace_back(item);
setLineStartConditions(localCursor.getBlockNumber(), localCursor.getPosition());
- localCursor += canShow;
+ localCursor += signsCountToShow;
if (localCursor.checkAndInvalidateBlockChanged() && localCursor.checkPreviousBlockNewLine()) {
end = TextBlock::End::Newline;
@@ 95,7 95,7 @@ namespace gui
}
// not whole text shown, try again for next line if you want
- if (canShow < text.length()) {
+ if (signsCountToShow < text.length()) {
end = TextBlock::End::None;
return;
}
@@ 103,6 103,41 @@ namespace gui
} 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) {
+ // 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;
+ }
+
TextLine::TextLine(TextLine &&from) noexcept
{
lineContent = std::move(from.lineContent);
@@ 117,6 152,7 @@ namespace gui
lineEnd = from.lineEnd;
end = from.end;
maxWidth = from.maxWidth;
+ breakLineDashAddition = from.breakLineDashAddition;
lineStartBlockNumber = from.lineStartBlockNumber;
lineStartBlockPosition = from.lineStartBlockPosition;
lineVisible = from.lineVisible;
@@ 314,5 350,4 @@ namespace gui
underline->setArea({x, y, getWidth(), height()});
}
}
-
} // namespace gui
M module-gui/gui/widgets/TextLine.hpp => module-gui/gui/widgets/TextLine.hpp +3 -1
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
@@ 36,9 36,11 @@ namespace gui
Position storedYOffset = 0;
bool lineEnd = false;
bool lineVisible = true;
+ bool breakLineDashAddition = false;
unsigned int lineStartBlockNumber = text::npos;
unsigned int lineStartBlockPosition = text::npos;
+ unsigned int calculateSignsToShow(BlockCursor &localCursor, UTF8 &text, unsigned int space);
void createUnderline(unsigned int max_w, unsigned int max_height);
void updateUnderline(const short &x, const short &y);
void setLineStartConditions(unsigned int startBlockNumber, unsigned int startBlockPosition);
M module-gui/gui/widgets/TextLineCursor.cpp => module-gui/gui/widgets/TextLineCursor.cpp +9 -5
@@ 182,14 182,19 @@ namespace gui
}
/// right - corner case
- if ((checkNpos() || (direction == NavigationDirection::RIGHT && selectedLineNumber == lastVisibleLineNumber))) {
+ if ((checkNpos() || (direction == NavigationDirection::RIGHT && selectedLineNumber >= lastVisibleLineNumber))) {
- if (text->lines->stopCondition != LinesDrawStop::OutOfText &&
+ auto onLastLine = selectedLineNumber == lastVisibleLineNumber;
+ auto onLastLineLastCharacter =
+ onLastLine &&
(selectedLineCursorPosition ==
(text->lines->getLine(lastVisibleLineNumber)->length() -
- (text->lines->getLine(lastVisibleLineNumber)->getEnd() == TextBlock::End::Newline ? 1 : 0)))) {
+ (text->lines->getLine(lastVisibleLineNumber)->getEnd() == TextBlock::End::Newline ? 1 : 0)));
- if (checkNextLineDocumentEnd(selectedLineNumber)) {
+ if (text->lines->stopCondition != LinesDrawStop::OutOfText &&
+ (onLastLineLastCharacter || (selectedLineNumber > lastVisibleLineNumber))) {
+
+ if (onLastLine && checkNextLineDocumentEnd(selectedLineNumber)) {
return TextCursor::Move::End;
}
@@ 200,7 205,6 @@ namespace gui
return ret;
}
else {
-
return TextCursor::moveCursor(direction);
}
}
M module-gui/test/test-catch-text/test-gui-Text.cpp => module-gui/test/test-catch-text/test-gui-Text.cpp +66 -0
@@ 1369,3 1369,69 @@ TEST_CASE("Navigating down between input texts")
REQUIRE(layout.getFocusItemIndex() == 1);
}
}
+
+TEST_CASE("Text word line breaking tests")
+{
+ std::string testStringBlock1 = "Test ";
+ std::string testStringBlock2 = "String ";
+ std::string testStringBlock3 = "LongLongLong";
+ std::string testStringBlock4 = testStringBlock1 + testStringBlock3 + testStringBlock2;
+ std::string testStringBlock5 = testStringBlock1 + testStringBlock2;
+ std::string emptyParagraph = "<p></p>";
+
+ SECTION("Breaking lines on space on whole words with long words")
+ {
+ mockup::fontManager();
+ using namespace gui;
+ auto text = std::make_unique<gui::TestText>();
+ text->setMaximumSize(300, 200);
+
+ text->addRichText("<text>" + testStringBlock4 + testStringBlock5 + "</text>");
+
+ REQUIRE(text->linesSize() == 2);
+ REQUIRE((*text->lineGet(0)).getText(0) == testStringBlock4);
+ REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock5);
+ }
+
+ SECTION("Breaking lines on space on whole words with short word")
+ {
+ mockup::fontManager();
+ using namespace gui;
+ auto text = std::make_unique<gui::TestText>();
+ text->setMaximumSize(250, 200);
+
+ text->addRichText("<text>" + testStringBlock5 + testStringBlock5 + testStringBlock5 + "</text>");
+
+ REQUIRE(text->linesSize() == 2);
+ REQUIRE((*text->lineGet(0)).getText(0) == testStringBlock5 + testStringBlock5);
+ REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock5);
+ }
+
+ SECTION("Breaking lines on newline before breaking it on space")
+ {
+ mockup::fontManager();
+ using namespace gui;
+ auto text = std::make_unique<gui::TestText>();
+ text->setMaximumSize(250, 200);
+
+ text->addRichText("<text>" + testStringBlock5 + emptyParagraph + testStringBlock5 + "</text>");
+
+ REQUIRE(text->linesSize() == 2);
+ REQUIRE((*text->lineGet(0)).getText(0) == testStringBlock5 + "\n");
+ REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock5);
+ }
+
+ SECTION("Breaking lines by splitting word with dash")
+ {
+ mockup::fontManager();
+ using namespace gui;
+ auto text = std::make_unique<gui::TestText>();
+ text->setMaximumSize(220, 200);
+
+ text->addRichText("<text>" + testStringBlock4 + "</text>");
+
+ REQUIRE(text->linesSize() == 2);
+ REQUIRE((*text->lineGet(0)).getText(0) == testStringBlock1 + testStringBlock3 + "-");
+ REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock2);
+ }
+}
M module-gui/test/test-catch-text/test-gui-TextFixedSize.cpp => module-gui/test/test-catch-text/test-gui-TextFixedSize.cpp +1 -1
@@ 157,7 157,7 @@ TEST_CASE("TextFixedSize remove Char")
using namespace gui;
mockup::fontManager();
- std::string testString1 = "Test Line Line Line";
+ std::string testString1 = "Test Line Line Line ";
std::string testString2 = "End";
std::string testStringLongLine = testString1 + testString2;