M enabled_unittests => enabled_unittests +1 -0
@@ 143,6 143,7 @@ TESTS_LIST["catch2-gui-text"]="
Text addition bounds - text widget size restricted;
Text addition bounds - text widget line size restricted;
Text addition bounds - multiple limits tests;
+ RichText newline and empty lines tests;
TextBlock Ctor/Dtor ;
Text block - set/update/get text;
Text block - remove text;
M module-gui/gui/widgets/RichTextParser.cpp => module-gui/gui/widgets/RichTextParser.cpp +19 -5
@@ 325,8 325,8 @@ struct walker : pugi::xml_tree_walker
std::list<gui::TextFormat> style_stack;
text::CustomTokens tokens;
- bool add_newline = false;
- bool adding_tokens = false;
+ bool add_empty_line = false;
+ bool adding_tokens = false;
public:
walker(gui::TextFormat entry_style, ::text::CustomTokens::TokenMap &&tokens) : tokens{std::move(tokens)}
@@ 398,8 398,17 @@ struct walker : pugi::xml_tree_walker
auto push_newline_node(pugi::xml_node &)
{
- if (blocks.size() != 0u) {
- blocks.back().setEnd(gui::TextBlock::End::Newline);
+ if (!blocks.empty()) {
+ if (blocks.back().getEnd() != gui::TextBlock::End::Newline) {
+ blocks.back().setEnd(gui::TextBlock::End::Newline);
+ add_empty_line = false;
+ }
+ else {
+ add_empty_line = true;
+ }
+ }
+ else {
+ add_empty_line = true;
}
}
@@ 462,7 471,12 @@ struct walker : pugi::xml_tree_walker
auto pop_newline_node(pugi::xml_node &node)
{
- if (blocks.size() != 0u) {
+ if (add_empty_line) {
+ blocks.emplace_back(gui::TextBlock("", std::make_unique<gui::TextFormat>(style_stack.back())));
+ add_empty_line = false;
+ }
+
+ if (!blocks.empty()) {
blocks.back().setEnd(gui::TextBlock::End::Newline);
}
}
M module-gui/gui/widgets/TextLine.hpp => module-gui/gui/widgets/TextLine.hpp +9 -1
@@ 3,6 3,7 @@
#pragma once
+#include <numeric>
#include <list>
#include "Common.hpp"
#include "Label.hpp"
@@ 73,7 74,14 @@ namespace gui
/// count of elements in whole TextLine
[[nodiscard]] unsigned int count() const noexcept
{
- return lineContent.size();
+ return std::accumulate(lineContent.begin(), lineContent.end(), 0U, [](const auto sum, const auto &content) {
+ return sum + content->getTextLength();
+ });
+ }
+
+ [[nodiscard]] bool empty() const noexcept
+ {
+ return ((end == TextBlock::End::Newline && count() == 1) || (end == TextBlock::End::None && count() == 0));
}
[[nodiscard]] Length width() const noexcept
M module-gui/gui/widgets/TextLineCursor.cpp => module-gui/gui/widgets/TextLineCursor.cpp +5 -1
@@ 52,7 52,11 @@ namespace gui
: 0;
auto newLineAtBeginningSubtraction =
- previousLine->getEnd() == TextBlock::End::Newline && selectedLineCursorPosition == 0 ? 1 : 0;
+ (previousLine->getEnd() == TextBlock::End::Newline) && (selectedLineCursorPosition == 0) &&
+ (selectedLineNumber >= 2) &&
+ (text->lines->getLine(selectedLineNumber - 2)->getEnd() == TextBlock::End::None)
+ ? 1
+ : 0;
auto moveCount = selectedLineCursorPosition + previousLineEndAddition + previousLineMoveCount -
newLineAtBeginningSubtraction;
M module-gui/test/test-catch-text/test-gui-Text.cpp => module-gui/test/test-catch-text/test-gui-Text.cpp +114 -0
@@ 65,6 65,18 @@ namespace gui
return lines->get();
}
+ [[nodiscard]] auto lineGet(unsigned int nr)
+ {
+ auto line = lines->get().begin();
+
+ if (nr >= lines->size()) {
+ nr = lines->size() - 1;
+ }
+
+ std::advance(line, nr);
+ return line;
+ }
+
[[nodiscard]] auto *getInputMode()
{
return mode;
@@ 957,3 969,105 @@ TEST_CASE("Text addition bounds - multiple limits tests")
REQUIRE(text->getText().length() != signsLimit);
}
}
+
+TEST_CASE("RichText newline and empty lines tests")
+{
+ std::string testStringBlock1 = "Test String 1";
+ std::string testStringBlock2 = "Test String 2";
+ std::string testStringBlock3 = "Test String 3";
+ std::string emptyParagraph = "<p></p>";
+
+ SECTION("Paragraph inside no newlines blocks")
+ {
+ mockup::fontManager();
+ using namespace gui;
+ auto text = std::make_unique<gui::TestText>();
+ text->setMaximumSize(600, 200);
+
+ text->addRichText("<text>" + testStringBlock1 + "<p>" + testStringBlock2 + "</p>" + testStringBlock3 +
+ "</text>");
+
+ REQUIRE(text->linesSize() == 3);
+ REQUIRE((*text->lineGet(0)).getText(0) == testStringBlock1 + "\n");
+ REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock2 + "\n");
+ REQUIRE((*text->lineGet(2)).getText(0) == testStringBlock3);
+ }
+
+ SECTION("Two empty paragraphs at beginning")
+ {
+ mockup::fontManager();
+ using namespace gui;
+ auto text = std::make_unique<gui::TestText>();
+ text->setMaximumSize(600, 200);
+
+ text->addRichText("<text>" + emptyParagraph + emptyParagraph + testStringBlock1 + testStringBlock2 +
+ testStringBlock3 + "</text>");
+
+ REQUIRE(text->linesSize() == 3);
+ REQUIRE((*text->lineGet(0)).getText(0) == "\n");
+ REQUIRE((*text->lineGet(1)).getText(0) == "\n");
+ REQUIRE((*text->lineGet(2)).getText(0) == testStringBlock1 + testStringBlock2 + testStringBlock3);
+ }
+
+ SECTION("Two empty paragraphs at end")
+ {
+ mockup::fontManager();
+ using namespace gui;
+ auto text = std::make_unique<gui::TestText>();
+ text->setMaximumSize(600, 200);
+
+ text->addRichText("<text>" + testStringBlock1 + testStringBlock2 + testStringBlock3 + emptyParagraph +
+ emptyParagraph + "</text>");
+
+ REQUIRE(text->linesSize() == 3);
+ REQUIRE((*text->lineGet(0)).getText(0) == testStringBlock1 + testStringBlock2 + testStringBlock3 + "\n");
+ REQUIRE((*text->lineGet(1)).getText(0) == "\n");
+ REQUIRE((*text->lineGet(2)).getText(0) == "");
+ }
+
+ SECTION("One empty paragraphs at beginning, one in center and one at end")
+ {
+ mockup::fontManager();
+ using namespace gui;
+ auto text = std::make_unique<gui::TestText>();
+ text->setMaximumSize(600, 200);
+
+ text->addRichText("<text>" + emptyParagraph + testStringBlock1 + emptyParagraph + testStringBlock2 +
+ testStringBlock3 + emptyParagraph + "</text>");
+
+ REQUIRE(text->linesSize() == 4);
+ REQUIRE((*text->lineGet(0)).getText(0) == "\n");
+ REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock1 + "\n");
+ REQUIRE((*text->lineGet(2)).getText(0) == testStringBlock2 + testStringBlock3 + "\n");
+ REQUIRE((*text->lineGet(3)).getText(0) == "");
+ }
+
+ SECTION("Text inside paragraph at beginning")
+ {
+ mockup::fontManager();
+ using namespace gui;
+ auto text = std::make_unique<gui::TestText>();
+ text->setMaximumSize(600, 200);
+
+ text->addRichText("<text><p>" + testStringBlock1 + "</p>" + testStringBlock2 + testStringBlock3 + "</text>");
+
+ REQUIRE(text->linesSize() == 2);
+ REQUIRE((*text->lineGet(0)).getText(0) == testStringBlock1 + "\n");
+ REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock2 + testStringBlock3);
+ }
+
+ SECTION("Text empty paragraph and text inside paragraph at beginning")
+ {
+ mockup::fontManager();
+ using namespace gui;
+ auto text = std::make_unique<gui::TestText>();
+ text->setMaximumSize(600, 200);
+
+ text->addRichText("<text>" + emptyParagraph + "<p>" + testStringBlock1 + "</p></text>");
+
+ REQUIRE(text->linesSize() == 3);
+ REQUIRE((*text->lineGet(0)).getText(0) == "\n");
+ REQUIRE((*text->lineGet(1)).getText(0) == testStringBlock1 + "\n");
+ REQUIRE((*text->lineGet(2)).getText(0) == "");
+ }
+}