M .gitmodules => .gitmodules +4 -1
@@ 46,4 46,7 @@
branch = rt1051
[submodule "module-bluetooth/lib/btstack"]
path = module-bluetooth/lib/btstack
- url = git@github.com:mudita/btstack.git
+ url = ../btstack.git
+[submodule "module-utils/pugixml"]
+ path = module-utils/pugixml
+ url = ../pugixml.git
M board/rt1051/ldscripts/memory.ld => board/rt1051/ldscripts/memory.ld +2 -2
@@ 14,6 14,6 @@ MEMORY
SRAM_OC (rwx) : ORIGIN = 0x20200000, LENGTH = 0x10000 /* 64K bytes (alias RAM) */
/*SRAM_ITC (rwx) : ORIGIN = 0x0, LENGTH = 0x0*/ /* 0K bytes (alias RAM2) */
SRAM_DTC (rwx) : ORIGIN = 0x20000000, LENGTH = 0x70000 /* 448K bytes (alias RAM3) */
- BOARD_SDRAM_TEXT (rx) : ORIGIN = 0x80000000, LENGTH = 0x0600000 /* 6M bytes for application code */
- BOARD_SDRAM_HEAP (rwx) : ORIGIN = 0x80600000, LENGTH = 0x0A00000 /* 10M bytes for heap (alias RAM4) */
+ BOARD_SDRAM_TEXT (rx) : ORIGIN = 0x80000000, LENGTH = 0x0620000 /* 5.something M bytes for application code */
+ BOARD_SDRAM_HEAP (rwx) : ORIGIN = 0x80620000, LENGTH = 0x9e0000 /* 9.somethin M bytes for heap (alias RAM4) */
}
M changelog.md => changelog.md +2 -0
@@ 4,6 4,8 @@
### Added
+* `[GUI]` Added rich text parsing for full text styling needs
+
### Other
M image/assets/lang/lang_en.json => image/assets/lang/lang_en.json +1 -1
@@ 189,7 189,7 @@
"app_messages_new_message": "New Message",
"app_messages_no_messages": "There are no messages yet.\nPress Left arrow to add new.",
"app_messages_thread_delete_confirmation": "Do you really want to delete\nthis conversation?",
- "app_messages_message_delete_confirmation": "Do you really want to delete\nthis message?",
+ "app_messages_message_delete_confirmation": "<text align='center'><p>Do you really want to <text weight='bold'>delete</text></p>this message?</text>",
"app_messages_thread_no_result" : "There are no results",
"app_messages_message": "Message",
"app_messages_templates": "Templates",
M module-apps/application-settings/windows/UITestWindow.cpp => module-apps/application-settings/windows/UITestWindow.cpp +9 -5
@@ 3,6 3,7 @@
#include "Label.hpp"
#include "Margins.hpp"
#include "i18/i18.hpp"
+#include "log/log.hpp"
#include "messages/AppMessage.hpp"
#include "service-appmgr/ApplicationManager.hpp"
#include <GridLayout.hpp>
@@ 24,13 25,16 @@ namespace gui
bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get(style::strings::common::select));
bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get(style::strings::common::back));
setTitle("UI TEST");
- text = new gui::Text(this,
- style::window::default_left_margin,
- title->offset_h(),
- style::window_width - 2 * style::window::default_left_margin,
- 300);
+ text = new gui::Text(
+ this, style::window::default_left_margin, title->offset_h(), style::window::default_body_width, 300);
text->setEditMode(EditMode::EDIT);
text->setFont(style::window::font::medium);
+ LOG_DEBUG(
+ "----------------------------------------------------------------------------------------------------");
+ text->addRichText("<p><text font='gt_pressura' color='12' size='30'>This</text><br>Text<text size='20' "
+ "weight='bold'> is rich </text><text color='3'>example</text></br></p>");
+ LOG_DEBUG(
+ "----------------------------------------------------------------------------------------------------");
text->addText(
TextBlock("!#$%&'()*+,-.0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~"
"ĄąĆćĘꣳŃńÓóŚśŹźŻżÀàÂâÇçÉéÈèÊêËëÎîÏïÔôÙûÛùÜüŸÿÄäÖößÁáÍíÚúÑñ¿¡",
M module-apps/windows/Dialog.cpp => module-apps/windows/Dialog.cpp +2 -2
@@ 55,7 55,7 @@ Dialog::Dialog(app::Application *app, const std::string &name, const Dialog::Met
icon = new Image(this, style::image::x, style::image::y, meta.icon);
text = new Text(this, style::text::x, style::text::y, style::text::w, style::text::h);
- text->setText(meta.text);
+ text->setRichText(meta.text);
text->setTextType(TextType::MULTI_LINE);
text->setEditMode(EditMode::BROWSE);
text->setEdges(RectangleEdgeFlags::GUI_RECT_EDGE_NO_EDGES);
@@ 67,7 67,7 @@ void Dialog::update(const Meta &meta)
{
this->meta = meta;
setTitle(meta.title);
- text->setText(meta.text);
+ text->setRichText(meta.text);
icon->set(meta.icon);
// meta.action not used
}
M module-bsp/board/rt1051/common/startup_mimxrt1052.cpp => module-bsp/board/rt1051/common/startup_mimxrt1052.cpp +0 -2
@@ 1049,8 1049,6 @@ extern "C"
LOG_FATAL(" - DTCM: Asynchronous fault on DTCM interface.");
if (abfsr.ahbp)
LOG_FATAL(" - AHBP: Asynchronous fault on AHBP interface");
- if (abfsr.axim)
- LOG_FATAL(" - AXIM: Asynchronous fault on AXIM interface, AXIMTYPE: 0x%0" PRIX32, abfsr.aximtype);
if (abfsr.eppb)
LOG_FATAL(" - EPPB: Asynchronous fault on EPPB interface");
#endif // (DEBUG_DETAILED_HARD_FAULT_INFO == 1)
M module-gui/gui/core/Font.cpp => module-gui/gui/core/Font.cpp +41 -0
@@ 1,9 1,15 @@
#include "Font.hpp"
#include "FontManager.hpp" // for FontManager
+#include "RawFont.hpp"
+#include "log/log.hpp"
+#include <algorithm>
+#include <sstream>
namespace gui
{
+ auto toWeight(const std::string &val) -> Font::Weight;
+
Font::Font(std::string name, unsigned int size, Weight weight)
{
setFont(name, size, weight);
@@ 12,6 18,27 @@ namespace gui
Font::Font(unsigned int size, Weight weight) : Font(font_default_type, size, weight)
{}
+ Font::Font(RawFont *rawfont)
+ {
+ if (rawfont == nullptr) {
+ font = FontManager::getInstance().getFont(""); // get default
+ }
+
+ auto name = rawfont->getName();
+ auto pos = name.npos;
+ auto parse = [&]() {
+ pos = name.rfind('_');
+ auto val = name.substr(pos + 1, name.length());
+ name.erase(pos);
+ return val;
+ };
+
+ unsigned int size = std::stoi(parse());
+ Weight weight = toWeight(parse());
+
+ setFont(name, size, weight);
+ }
+
void Font::setFont(std::string new_name, unsigned int new_size, Weight new_weight)
{
bool update = false;
@@ 49,4 76,18 @@ namespace gui
{
return font;
}
+
+ auto toWeight(const std::string &val) -> Font::Weight
+ {
+ if (val == c_str(Font::Weight::Regular)) {
+ return Font::Weight::Regular;
+ }
+ else if (val == c_str(Font::Weight::Bold)) {
+ return Font::Weight::Bold;
+ }
+ else if (val == c_str(Font::Weight::Light)) {
+ return Font::Weight::Light;
+ }
+ return Font::Weight::Regular;
+ }
}; // namespace gui
M module-gui/gui/core/Font.hpp => module-gui/gui/core/Font.hpp +13 -0
@@ 29,12 29,25 @@ namespace gui
public:
Font(std::string name, unsigned int size, Weight weight = Weight::Regular);
Font(unsigned int size, Weight weight = Weight::Regular);
+ Font(RawFont *font);
void setFont(std::string name, unsigned int size, Weight weight = Weight::Regular);
/// just for gt_pressura - we have it as default
void setFont(unsigned int size, Weight weight = Weight::Regular);
void setSize(unsigned int size);
void setWeight(Weight weight);
auto raw() -> RawFont *;
+ [[nodiscard]] auto getSize() const
+ {
+ return size;
+ }
+ [[nodiscard]] auto getWeight() const
+ {
+ return weight;
+ }
+ [[nodiscard]] auto getName() const
+ {
+ return name;
+ }
};
}; // namespace gui
M module-gui/gui/widgets/CMakeLists.txt => module-gui/gui/widgets/CMakeLists.txt +2 -0
@@ 34,6 34,8 @@ target_sources( ${PROJECT_NAME}
"${CMAKE_CURRENT_LIST_DIR}/Style.cpp"
"${CMAKE_CURRENT_LIST_DIR}/InputMode.cpp"
"${CMAKE_CURRENT_LIST_DIR}/GridLayout.cpp"
+ "${CMAKE_CURRENT_LIST_DIR}/RichTextParser.cpp"
+ "${CMAKE_CURRENT_LIST_DIR}/TextFormat.cpp"
PUBLIC
"${CMAKE_CURRENT_LIST_DIR}/Alignment.hpp"
"${CMAKE_CURRENT_LIST_DIR}/BottomBar.hpp"
A module-gui/gui/widgets/RichTextParser.cpp => module-gui/gui/widgets/RichTextParser.cpp +381 -0
@@ 0,0 1,381 @@
+#include "RichTextParser.hpp"
+#include "Color.hpp"
+#include "Font.hpp"
+#include "TextBlock.hpp"
+#include "TextDocument.hpp"
+#include <sstream>
+#include <string>
+#include <log/log.hpp>
+#include "TextFormat.hpp"
+
+#include <module-utils/pugixml/src/pugixml.hpp>
+#include <utility>
+
+#ifndef DEBUG_RTP
+const std::string node_types[] = {"null", "document", "element", "pcdata ", "cdata", "comment", "pi", "declaration"};
+#define log_parser(...) LOG_DEBUG(__VA_ARGS__)
+#endif
+
+namespace text
+{
+ class Attribute
+ {
+ std::string name;
+
+ protected:
+ explicit Attribute(std::string name) : name(std::move(name)){};
+
+ public:
+ Attribute() = delete;
+ /// return true on success, othervise set fallback value and return false
+ /// @note could run: preVisitHook -> visit -> postVisit hook
+ virtual auto visit(gui::TextFormat &fmt, std::string value) -> bool = 0;
+ [[nodiscard]] auto getName() const -> const std::string &
+ {
+ return name;
+ }
+
+ [[nodiscard]] auto is(const std::string &name) const
+ {
+ return getName() == name;
+ }
+ };
+
+ class AttributeAlign : public Attribute
+ {
+ const std::string center = gui::text::center;
+ const std::string left = gui::text::left;
+ const std::string right = gui::text::right;
+
+ public:
+ AttributeAlign() : Attribute(gui::text::align)
+ {}
+
+ auto visit(gui::TextFormat &fmt, std::string value) -> bool final
+ {
+ log_parser("append: Attr %s", getName().c_str());
+ using namespace gui;
+ auto alignment = Alignment(Alignment::Horizontal::Left, Alignment::Vertical::Bottom);
+ if (value == center) {
+ alignment.horizontal = Alignment::Horizontal::Center;
+ fmt.setAlignment(alignment);
+ return true;
+ }
+ else if (value == right) {
+ alignment.horizontal = Alignment::Horizontal::Right;
+ fmt.setAlignment(alignment);
+ return true;
+ }
+ else if (value == left) {
+ alignment.horizontal = Alignment::Horizontal::Left;
+ fmt.setAlignment(alignment);
+ return true;
+ }
+
+ fmt.setAlignment(alignment);
+ return false;
+ }
+ };
+
+ class AttributeColor : public Attribute
+ {
+
+ const int max = 15;
+
+ public:
+ AttributeColor() : Attribute(gui::text::color)
+ {}
+
+ auto visit(gui::TextFormat &fmt, std::string value) -> bool final
+ {
+ log_parser("append: Attr %s", getName().c_str());
+ using namespace gui;
+ if (value.empty()) {
+ fmt.setColor(ColorFullBlack);
+ return false;
+ }
+ try {
+ int val = std::stoi(value);
+ if ((val == 0 && value.c_str()[0] != '0') || val > max) {
+ fmt.setColor(ColorFullBlack);
+ return false;
+ }
+ fmt.setColor(Color{static_cast<uint8_t>(val), 0});
+ return true;
+ }
+ catch (const std::exception &exception) {
+ LOG_ERROR("%s", exception.what());
+ return false;
+ }
+ return false;
+ }
+ };
+
+ class AttributeFont : public Attribute
+ {
+ public:
+ AttributeFont() : Attribute(gui::text::font)
+ {}
+
+ auto visit(gui::TextFormat &fmt, std::string value) -> bool final
+ {
+ log_parser("append: Attr %s", getName().c_str());
+ using namespace gui;
+ // here it's tricky -> we need to get font copy from fmt -> change it -> set it
+ auto font = gui::Font(fmt.getFont());
+ font.setFont(value, font.getSize());
+ fmt.setFont(font.raw());
+ return true;
+ }
+ };
+
+ class AttributeSize : public Attribute
+ {
+ public:
+ AttributeSize() : Attribute(gui::text::size)
+ {}
+
+ auto visit(gui::TextFormat &fmt, std::string value) -> bool final
+ {
+ log_parser("append: Attr %s", getName().c_str());
+ if (value.empty()) {
+ return false;
+ }
+ try {
+ int val = std::stoi(value);
+
+ if (val == 0 && value.front() != '0') {
+ return false;
+ }
+
+ auto font = gui::Font(fmt.getFont());
+ font.setSize(val);
+ fmt.setFont(font.raw());
+ return true;
+ }
+ catch (const std::exception &exception) {
+ LOG_ERROR("%s", exception.what());
+ return false;
+ }
+ }
+ };
+
+ class AttributeWeight : public Attribute
+ {
+ const std::string regular = "regular";
+ const std::string bold = "bold";
+ const std::string light = "light";
+
+ public:
+ AttributeWeight() : Attribute("weight")
+ {}
+
+ auto visit(gui::TextFormat &fmt, std::string value) -> bool final
+ {
+ log_parser("append: Attr %s", getName().c_str());
+ using namespace gui;
+ auto font = gui::Font(fmt.getFont());
+ if (value == regular) {
+ font.setWeight(Font::Weight::Regular);
+ }
+ else if (value == light) {
+ font.setWeight(Font::Weight::Light);
+ }
+ else if (value == bold) {
+ font.setWeight(Font::Weight::Bold);
+ }
+ else {
+ font.setWeight(Font::Weight::Regular);
+ fmt.setFont(font.raw());
+ return false;
+ }
+
+ fmt.setFont(font.raw());
+ return true;
+ }
+ };
+
+ class NodeDecor
+ {
+ std::list<std::unique_ptr<Attribute>> attrs;
+ NodeDecor()
+ {
+ attrs.emplace_back(std::make_unique<AttributeAlign>());
+ attrs.emplace_back(std::make_unique<AttributeColor>());
+ attrs.emplace_back(std::make_unique<AttributeFont>());
+ attrs.emplace_back(std::make_unique<AttributeSize>());
+ attrs.emplace_back(std::make_unique<AttributeWeight>());
+ }
+
+ public:
+ // for each met style -> put it on stack to be used
+ // too deep -> can be optimized
+ auto stack_visit(gui::TextFormat &format, const std::string &name, const std::string &value) -> bool
+ {
+ for (auto &attr : attrs) {
+ if (attr->is(name)) {
+ if (!attr->visit(format, value)) {
+ LOG_ERROR("Attribute %s parsing error, default set", name.c_str());
+ }
+ return true;
+ }
+ }
+ LOG_ERROR("Attr: %s not found", name.c_str());
+ return false;
+ }
+
+ static auto get() -> NodeDecor &
+ {
+ static NodeDecor *ptr = nullptr;
+ if (ptr == nullptr) {
+ ptr = new NodeDecor();
+ }
+ return *ptr;
+ }
+ };
+}; // namespace text
+
+struct walker : pugi::xml_tree_walker
+{
+ protected:
+ std::list<gui::TextBlock> blocks;
+ std::list<gui::TextFormat> style_stack;
+ bool add_newline = false;
+
+ public:
+ walker(gui::TextFormat entry_style)
+ {
+ style_stack.push_back(entry_style);
+ }
+
+ enum class Action
+ {
+ Enter, /// enter/visit node
+ Exit /// exit/leave node
+ };
+
+ auto log_node(pugi::xml_node &node, Action dir)
+ {
+ log_parser(
+ "%s: %s format: %s",
+ dir == Action::Enter ? "enter" : "leave",
+ [&]() {
+ std::stringstream ss;
+ ss << node_types[node.type()] << ": name='" << node.name() << "', value='" << node.value() << "'";
+ return ss.str();
+ }()
+ .c_str(),
+ style_stack.back().str().c_str());
+ }
+
+ auto is_newline_node(pugi::xml_node &node) const
+ {
+ return std::string(node.name()) == gui::text::node_br || std::string(node.name()) == gui::text::node_p;
+ }
+
+ auto push_text_node(pugi::xml_node &node)
+ {
+ auto local_style = style_stack.back();
+ for (auto &attribute : node.attributes()) {
+ log_parser("attribute name: %s value: %s", attribute.name(), attribute.value());
+ auto &decor = text::NodeDecor::get();
+ decor.stack_visit(local_style, attribute.name(), attribute.value());
+ }
+ style_stack.push_back(local_style);
+ log_parser("Attr loaded: %s", style_stack.back().str().c_str());
+ }
+
+ auto push_newline_node(pugi::xml_node &)
+ {
+ if (blocks.size() != 0u) {
+ blocks.back().setEnd(gui::TextBlock::End::Newline);
+ }
+ }
+
+ auto push_data_node(pugi::xml_node &node)
+ {
+ blocks.emplace_back(node.value(), std::make_unique<gui::TextFormat>(style_stack.back()));
+ }
+
+ auto for_each(pugi::xml_node &node) -> bool final
+ {
+ log_node(node, Action::Enter);
+ if (node.type() == pugi::xml_node_type::node_element) {
+ if (std::string(node.name()) == gui::text::node_text) {
+ push_text_node(node);
+ return true;
+ }
+ if (is_newline_node(node)) {
+ push_newline_node(node);
+ return true;
+ }
+ }
+
+ std::string to_show = node.value();
+ if (node.type() == pugi::xml_node_type::node_pcdata && !to_show.empty()) {
+ push_data_node(node);
+ }
+ return true;
+ }
+
+ auto pop_text_node(pugi::xml_node &node)
+ {
+ style_stack.pop_back();
+ }
+
+ auto pop_newline_node(pugi::xml_node &node)
+ {
+ if (blocks.size() != 0u) {
+ blocks.back().setEnd(gui::TextBlock::End::Newline);
+ }
+ }
+
+ auto on_leave(pugi::xml_node &node) -> bool final
+ {
+ log_node(node, Action::Exit);
+
+ if (node.type() == pugi::xml_node_type::node_element) {
+ if (std::string(node.name()) == gui::text::node_text) {
+ pop_text_node(node);
+ return true;
+ }
+ if (is_newline_node(node)) {
+ pop_newline_node(node);
+ return true;
+ }
+ }
+ return true;
+ }
+
+ auto end(pugi::xml_node &node) -> bool final
+ {
+ return true;
+ }
+
+ auto souvenirs() -> std::list<gui::TextBlock> &
+ {
+ return blocks;
+ }
+};
+
+namespace gui::text
+{
+
+ auto RichTextParser::parse(const UTF8 &text, TextFormat *base_style) -> std::unique_ptr<TextDocument>
+ {
+ LOG_DEBUG("parsing: %s", text.c_str());
+ if (text.empty() || base_style == nullptr) {
+ LOG_ERROR("no: %s", text.empty() ? "text" : "base style");
+ return std::unique_ptr<TextDocument>();
+ }
+
+ pugi::xml_document doc;
+ walker walker(*base_style);
+
+ doc.load_string(text.c_str());
+ doc.traverse(walker);
+
+ return std::make_unique<TextDocument>(walker.souvenirs());
+ }
+
+}; // namespace gui::text
A module-gui/gui/widgets/RichTextParser.hpp => module-gui/gui/widgets/RichTextParser.hpp +48 -0
@@ 0,0 1,48 @@
+#pragma once
+
+#include <memory>
+#include <utf8/UTF8.hpp>
+
+namespace gui
+{
+ class TextDocument;
+ class TextFormat;
+} // namespace gui
+
+namespace gui::text
+{
+ inline const auto align = "align";
+ inline const auto center = "center";
+ inline const auto right = "right";
+ inline const auto left = "left";
+ inline const auto color = "color";
+ inline const auto font = "font";
+ inline const auto size = "size";
+ inline const auto weight = "weight";
+ inline const auto regular = "regular";
+ inline const auto bold = "bold";
+ inline const auto light = "light";
+ inline const auto node_text = "text";
+ inline const auto node_br = "br";
+ inline const auto node_p = "p";
+
+ /// Rich text parser utility
+ /// supported specifiers
+ /// * gui::text::node_text `<text> </text>` attributes:
+ /// * gui::text::align - alignment in Horizontal axis ( gui::text::center, gui::text::left, gui::text::right
+ /// )
+ /// * gui::text::color - Text color from 0 to 15 ( eink black color depth )
+ /// * gui::text::font - Font to use ( now we have gt_pressura and some dejavu glyphs)
+ /// * gui::text::size - size of font to use
+ /// * gui::text::weight - weight of font to use, one of: gui::text::bold, gui::text::regular,
+ /// gui::text::light
+ /// please mind that selected font must exist on system, othervise closest relative will be selected
+ /// * gui::text::node_p `<p> </p>` and gui::text::node_br `<br> </br>` now working identical - marking start and end
+ /// with newline
+ /// @return empty document on error
+ class RichTextParser
+ {
+ public:
+ auto parse(const UTF8 &text, TextFormat *base_style) -> std::unique_ptr<TextDocument>;
+ };
+} // namespace gui::text
M module-gui/gui/widgets/Text.cpp => module-gui/gui/widgets/Text.cpp +30 -9
@@ 19,6 19,7 @@
#include <cassert>
#include <FontManager.hpp>
#include <RawFont.hpp>
+#include <RichTextParser.hpp>
#if DEBUG_GUI_TEXT == 1
#define debug_text(...) LOG_DEBUG(__VA_ARGS__)
@@ 46,13 47,13 @@ namespace gui
const UTF8 &text,
ExpandMode expandMode,
TextType textType)
- : Rect(parent, x, y, w, h), lines(this), expandMode{expandMode}, textType{textType}
+ : Rect(parent, x, y, w, h), lines(this), expandMode{expandMode}, textType{textType},
+ format(FontManager::getInstance().getFont(style::window::font::small))
{
alignment = style::text::defaultTextAlignment;
setPenWidth(style::window::default_border_no_focus_w);
setPenFocusWidth(style::window::default_border_focus_w);
- font = FontManager::getInstance().getFont(style::window::font::small);
buildDocument(text);
setBorderColor(gui::ColorFullBlack);
@@ 90,7 91,8 @@ namespace gui
void Text::setText(const UTF8 &text)
{
debug_text("setText: %s", text.c_str());
- setText(std::make_unique<TextDocument>(textToTextBlocks(text, font, TextBlock::End::None)));
+ /// TODO here should be format passed
+ setText(std::make_unique<TextDocument>(textToTextBlocks(text, format.getFont(), TextBlock::End::None)));
}
void Text::setText(std::unique_ptr<TextDocument> &&document)
@@ 113,6 115,25 @@ namespace gui
drawLines();
}
+ void Text::setRichText(const UTF8 &text)
+ {
+ setText("");
+ addRichText(text);
+ }
+
+ void Text::addRichText(const UTF8 &text)
+ {
+ auto tmp_document = text::RichTextParser().parse(text, &format);
+ if (tmp_document->isEmpty()) {
+ LOG_ERROR("Nothing to parse/parser error in rich text: %s", text.c_str());
+ addText(text); // fallback
+ }
+ for (auto block : tmp_document->getBlockCursor(0)) {
+ *cursor << block;
+ }
+ drawLines();
+ }
+
void Text::clear()
{
buildDocument("");
@@ 142,13 163,13 @@ namespace gui
void Text::setFont(const UTF8 &fontName)
{
RawFont *newFont = FontManager::getInstance().getFont(fontName);
- font = newFont;
+ format.setFont(newFont);
buildCursor();
}
- void Text::setFont(RawFont *fontName)
+ void Text::setFont(RawFont *font)
{
- font = fontName;
+ format.setFont(font);
buildCursor();
}
@@ 367,8 388,8 @@ namespace gui
uint16_t w_used = lines.maxWidth();
if (lines.size() == 0) {
debug_text("No lines to show, try to at least fit in cursor");
- if (font != nullptr) {
- h_used += font->info.line_height;
+ if (format.getFont() != nullptr) {
+ h_used += format.getFont()->info.line_height;
w_used += TextCursor::default_width;
debug_text("empty line height: %d", h_used);
}
@@ 413,7 434,7 @@ namespace gui
void Text::buildDocument(const UTF8 &text)
{
- buildDocument(std::make_unique<TextDocument>(textToTextBlocks(text, font, TextBlock::End::None)));
+ buildDocument(std::make_unique<TextDocument>(textToTextBlocks(text, format.getFont(), TextBlock::End::None)));
}
void Text::buildDocument(std::unique_ptr<TextDocument> &&document_moved)
M module-gui/gui/widgets/Text.hpp => module-gui/gui/widgets/Text.hpp +12 -2
@@ 138,9 138,8 @@ namespace gui
protected:
TextType textType = TextType::MULTI_LINE;
/// points to default text font to use
- RawFont *font = nullptr;
- Color textColor;
bool underline = false;
+ TextFormat format;
bool moveCursor(const NavigationDirection &direction, std::unique_ptr<TextDocument> &document);
bool handleNavigation(const InputEvent &inputEvent);
@@ 173,6 172,13 @@ namespace gui
void addText(const UTF8 &text);
void addText(TextBlock text);
+ /// @defgroup richtext can be virtualized by parametrized RichTextParser virtual api ( as second param )
+ /// @{
+ /// set rich text with default RichTextParser - please see RichTextParser documentation on how to use format
+ void setRichText(const UTF8 &text);
+ /// add rich text with default RichTextParser - please see RichTextParser documentation on how to use format
+ void addRichText(const UTF8 &text);
+ /// @}
virtual void clear();
bool isEmpty();
virtual UTF8 getText();
@@ 196,6 202,10 @@ namespace gui
void setRadius(int value) override;
void setAlignment(const Alignment &value) override;
void setPadding(const Padding &value) override;
+ [[nodiscard]] auto getTextFormat() const noexcept -> const TextFormat &
+ {
+ return format;
+ }
private:
gui::KeyInputMappedTranslation translator;
M module-gui/gui/widgets/TextBlock.cpp => module-gui/gui/widgets/TextBlock.cpp +1 -1
@@ 11,7 11,7 @@ namespace gui
TextBlock::TextBlock(const UTF8 text, std::unique_ptr<TextFormat> format) : format(std::move(format)), text{text}
{}
- TextBlock::TextBlock(const UTF8 text, RawFont *font, TextBlock::End eol)
+ TextBlock::TextBlock(const UTF8 text, const RawFont *font, TextBlock::End eol)
: TextBlock(text, std::make_unique<TextFormat>(font))
{
if (getEnd() != End::Newline && eol == End::Newline) {
M module-gui/gui/widgets/TextBlock.hpp => module-gui/gui/widgets/TextBlock.hpp +1 -1
@@ 29,7 29,7 @@ namespace gui
End end = End::Newline;
public:
- TextBlock(const UTF8 text, RawFont *font, End eol = End::None);
+ TextBlock(const UTF8 text, const RawFont *font, End eol = End::None);
TextBlock(const UTF8 text, std::unique_ptr<TextFormat> format);
TextBlock(const TextBlock &);
M module-gui/gui/widgets/TextBlockCursor.cpp => module-gui/gui/widgets/TextBlockCursor.cpp +10 -0
@@ 235,4 235,14 @@ namespace gui
{
return *currentBlock();
}
+
+ auto BlockCursor::begin() -> std::list<TextBlock>::iterator
+ {
+ return document == nullptr ? document->blocks.end() : document->blocks.begin();
+ }
+
+ auto BlockCursor::end() -> std::list<TextBlock>::iterator
+ {
+ return document->blocks.end();
+ }
} // namespace gui
M module-gui/gui/widgets/TextBlockCursor.hpp => module-gui/gui/widgets/TextBlockCursor.hpp +7 -0
@@ 3,6 3,7 @@
#include "TextConstants.hpp"
#include <cstdio>
#include <stdint.h>
+#include <list>
namespace gui
{
@@ 77,5 78,11 @@ namespace gui
// return if handled ( this is not i.e. at begin/end)
bool removeChar();
const TextBlock &operator*();
+
+ /// iterable
+ /// {
+ auto begin() -> std::list<TextBlock>::iterator;
+ auto end() -> std::list<TextBlock>::iterator;
+ /// }
};
} // namespace gui
M module-gui/gui/widgets/TextCursor.cpp => module-gui/gui/widgets/TextCursor.cpp +6 -3
@@ 17,7 17,9 @@ namespace gui
TextCursor::TextCursor(gui::Text *parent, gui::TextDocument *document)
: Rect(parent, 0, 0, default_width, 1),
- BlockCursor(document, text::npos, text::npos, parent != nullptr ? parent->font : nullptr), text(parent)
+ BlockCursor(
+ document, text::npos, text::npos, parent != nullptr ? parent->getTextFormat().getFont() : nullptr),
+ text(parent)
{
setFilled(true);
setVisible(false);
@@ 123,8 125,9 @@ namespace gui
setArea({x, y, w, h});
return;
}
- if (document->isEmpty() && text->font != nullptr) {
- h += text->font->info.line_height;
+ auto default_font = text->format.getFont();
+ if (document->isEmpty() && default_font != nullptr) {
+ h += default_font->info.line_height;
x = getAxisAlignmentValue(Axis::X, w);
y = getAxisAlignmentValue(Axis::Y, h);
}
M module-gui/gui/widgets/TextDocument.cpp => module-gui/gui/widgets/TextDocument.cpp +2 -0
@@ 70,6 70,8 @@ namespace gui
loop_position += el.length();
++block_no;
}
+ // TODO ok... here we might want to return BlockCursor(this) <- but returning text::npos / empty/none block if
+ // we wanted to return anything
return BlockCursor();
}
M module-gui/gui/widgets/TextFixedSize.cpp => module-gui/gui/widgets/TextFixedSize.cpp +1 -1
@@ 47,7 47,7 @@ namespace gui
auto cursor = 0;
unsigned int currentLine = 0;
- unsigned int lineHeight = font->info.line_height + underlinePadding;
+ unsigned int lineHeight = format.getFont()->info.line_height + underlinePadding;
auto line_x_position = padding.left;
do {
A module-gui/gui/widgets/TextFormat.cpp => module-gui/gui/widgets/TextFormat.cpp +13 -0
@@ 0,0 1,13 @@
+#include "TextFormat.hpp"
+#include "RawFont.hpp"
+
+namespace gui
+{
+ auto TextFormat::str() const -> std::string
+ {
+ std::string ret = font->getName();
+ ret += " color: ";
+ ret += std::to_string(color.intensity);
+ return ret;
+ }
+} // namespace gui
M module-gui/gui/widgets/TextFormat.hpp => module-gui/gui/widgets/TextFormat.hpp +12 -2
@@ 1,7 1,9 @@
#pragma once
+#include "Alignment.hpp"
#include <Color.hpp>
#include <functional>
+#include <string>
namespace gui
{
@@ 10,8 12,9 @@ namespace gui
class TextFormat
{
private:
- RawFont *font = nullptr;
+ mutable RawFont *font = nullptr;
Color color = ColorFullBlack;
+ Alignment alignment = Alignment(Alignment::Horizontal::Left);
static constexpr auto setter = [](auto &local, auto &next) {
if (local != next) {
@@ 20,7 23,7 @@ namespace gui
};
public:
- TextFormat(RawFont *font, Color color = {}) : font(font), color(color){};
+ TextFormat(const RawFont *font, Color color = {}) : font(const_cast<RawFont *>(font)), color(color){};
TextFormat(const TextFormat &) = default;
[[nodiscard]] auto getFont() const
@@ 44,5 47,12 @@ namespace gui
{
setter(this->color, color);
}
+
+ void setAlignment(Alignment alignment)
+ {
+ setter(this->alignment, alignment);
+ }
+
+ auto str() const -> std::string;
};
}; // namespace gui
M module-gui/gui/widgets/TextParse.cpp => module-gui/gui/widgets/TextParse.cpp +1 -1
@@ 3,7 3,7 @@
namespace gui
{
- auto textToTextBlocks(const UTF8 &text, RawFont *font, TextBlock::End end) -> std::list<TextBlock>
+ auto textToTextBlocks(const UTF8 &text, const RawFont *font, TextBlock::End end) -> std::list<TextBlock>
{
std::list<TextBlock> blocks;
std::stringstream ss(text.c_str());
M module-gui/gui/widgets/TextParse.hpp => module-gui/gui/widgets/TextParse.hpp +1 -1
@@ 6,7 6,7 @@
namespace gui
{
/// @note with nullptr font - nullptr will be assigned as font
- auto textToTextBlocks(const UTF8 &text, RawFont *font, TextBlock::End end = TextBlock::End::Newline)
+ auto textToTextBlocks(const UTF8 &text, const RawFont *font, TextBlock::End end = TextBlock::End::Newline)
-> std::list<TextBlock>;
auto textToTextBlocks(const UTF8 &text, TextFormat format) -> std::list<TextBlock>;
} // namespace gui
M module-gui/test/test-catch-text/test-gui-Text.cpp => module-gui/test/test-catch-text/test-gui-Text.cpp +0 -18
@@ 97,24 97,6 @@ TEST_CASE("Text drawLines")
text.drawLines();
REQUIRE(text.linesSize() == lines_count);
}
-
- // Test deactivated
- // SECTION("get 2 lines out of N")
- // {
- // unsigned int lines_count = 5;
- // const unsigned int lines_to_show = 2;
- // REQUIRE(lines_to_show < lines_count);
- // auto testline = mockup::multiLineString(lines_count);
- // auto font = fontmanager.getFont(0);
- // auto line_height = font->info.line_height;
- // auto text = TestText();
- // text.setMaximumSize(6, line_height * lines_to_show);
- // text.setText(std::make_unique<TextDocument>(
- // textToTextBlocks(testline, fontmanager.getFont(0), TextBlock::End::Newline)));
- //
- // text.drawLines();
- // REQUIRE(text.linesSize() == lines_to_show);
- // }
}
TEST_CASE("Text buildDrawList")
M module-utils/CMakeLists.txt => module-utils/CMakeLists.txt +3 -1
@@ 44,7 44,9 @@ include(third-party/re2.cmake)
include(third-party/protobuf-lite.cmake)
include(third-party/libphonenumber.cmake)
-target_link_libraries(${PROJECT_NAME} PUBLIC module-os module-vfs)
+add_subdirectory(pugixml)
+
+target_link_libraries(${PROJECT_NAME} PUBLIC module-os module-vfs pugixml)
# Board specific compilation definitions,options,include directories and features
target_compile_definitions(${PROJECT_NAME} PUBLIC ${PROJECT_CONFIG_DEFINITIONS})
A module-utils/pugixml => module-utils/pugixml +1 -0
@@ 0,0 1,1 @@
+Subproject commit 3594fd01041663c4a2f3f149e47d71835863fb02
M module-utils/utf8/UTF8.hpp => module-utils/utf8/UTF8.hpp +2 -0
@@ 98,10 98,12 @@ class UTF8
{
return strLength;
}
+
bool empty() const noexcept
{
return strLength == 0U;
}
+
uint32_t used() const
{
return sizeUsed;
M module-vfs/board/cross/freeRTOS_FAT/portable/ff_eMMC_user_disk.cpp => module-vfs/board/cross/freeRTOS_FAT/portable/ff_eMMC_user_disk.cpp +0 -1
@@ 240,7 240,6 @@ BaseType_t xReturn = pdPASS;
/* It is better not to use the 64-bit format such as %Lu because it
might not be implemented. */
- LOG_PRINTF("Partition Nr %lu\r\n", pxDisk->xStatus.bPartitionNumber);
LOG_PRINTF("Type %8u (%s)\r\n", pxIOManager->xPartition.ucType, pcTypeName);
LOG_PRINTF( "VolLabel '%8s' \r\n", pxIOManager->xPartition.pcVolumeLabel );
LOG_PRINTF( "TotalSectors %8lu\r\n", pxIOManager->xPartition.ulTotalSectors );