M changelog.md => changelog.md +1 -0
@@ 17,6 17,7 @@
### Added
+* `[gui]` Added support for variable numbers in RichText
* `[gui][desktop]` Added SIM PIN basic flow implementation
* `[cellular]` Added CLIR, CLIP, COLP, call waiting, call barring MMIs support
M module-gui/gui/widgets/RichTextParser.cpp => module-gui/gui/widgets/RichTextParser.cpp +87 -8
@@ 13,6 13,7 @@
#include "TextFormat.hpp"
#include <module-utils/pugixml/src/pugixml.hpp>
+#include <module-utils/Utils.hpp>
#include <utility>
#ifdef DEBUG_RTP
@@ 265,7 266,7 @@ namespace text
return std::optional<std::string>(content == AttributeContent::Name ? attribute.first
: attribute.second);
}
- catch (std::out_of_range &) {
+ catch (const std::out_of_range &) {
LOG_ERROR("ShortTextNode: %s not found", nodeName);
return {};
}
@@ 274,17 275,61 @@ namespace text
const ShortTextNodes::SingleAttributedNode ShortTextNodes::nodes = {
{gui::text::short_bold, {gui::text::weight, gui::text::bold}}};
-}; // namespace text
+ class CustomTokens
+ {
+ public:
+ using TokenMap = std::map<std::string, std::variant<int, std::string>>;
+ explicit CustomTokens(TokenMap &&_tokens) : tokens{std::move(_tokens)}
+ {}
+
+ [[nodiscard]] static auto isCustomTokenNode(const std::string &nodeName) -> bool
+ {
+ return nodeName == gui::text::node_token;
+ }
+
+ [[nodiscard]] auto get(const std::string &contentName) -> std::optional<std::string>
+ {
+ try {
+ auto token = tokens.at(contentName);
+ return std::visit(
+ [](auto &&arg) {
+ using T = std::decay_t<decltype(arg)>;
+ if constexpr (std::is_same_v<T, int>) {
+ return std::make_optional(utils::to_string(arg));
+ }
+ else if constexpr (std::is_same_v<T, std::string>) {
+ return std::make_optional(arg);
+ }
+ else {
+ return std::nullopt;
+ }
+ },
+ std::move(token));
+ }
+ catch (const std::out_of_range &) {
+ LOG_ERROR("Tokens: %s not found", contentName.c_str());
+ }
+ return std::nullopt;
+ }
+
+ private:
+ TokenMap tokens;
+ };
+
+}; // namespace text
struct walker : pugi::xml_tree_walker
{
protected:
std::list<gui::TextBlock> blocks;
std::list<gui::TextFormat> style_stack;
+ text::CustomTokens tokens;
+
bool add_newline = false;
+ bool adding_tokens = false;
public:
- walker(gui::TextFormat entry_style)
+ walker(gui::TextFormat entry_style, ::text::CustomTokens::TokenMap &&tokens) : tokens{std::move(tokens)}
{
style_stack.push_back(entry_style);
}
@@ 319,6 364,11 @@ struct walker : pugi::xml_tree_walker
return text::ShortTextNodes::is(node.name());
}
+ auto is_custom_token_node(pugi::xml_node &node) const
+ {
+ return text::CustomTokens::isCustomTokenNode(node.name());
+ }
+
auto push_text_node(pugi::xml_node &node)
{
auto local_style = style_stack.back();
@@ 353,6 403,19 @@ struct walker : pugi::xml_tree_walker
}
}
+ auto start_custom_token_node(pugi::xml_node &)
+ {
+ adding_tokens = true;
+ }
+
+ auto push_custom_token_data_node(pugi::xml_node &node)
+ {
+ auto value = tokens.get(node.value());
+ if (value.has_value()) {
+ blocks.emplace_back(value.value(), std::make_unique<gui::TextFormat>(style_stack.back()));
+ }
+ }
+
auto push_data_node(pugi::xml_node &node)
{
blocks.emplace_back(node.value(), std::make_unique<gui::TextFormat>(style_stack.back()));
@@ 374,11 437,20 @@ struct walker : pugi::xml_tree_walker
push_newline_node(node);
return true;
}
+ if (is_custom_token_node(node)) {
+ start_custom_token_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);
+ if (adding_tokens) {
+ push_custom_token_data_node(node);
+ }
+ else {
+ push_data_node(node);
+ }
}
return true;
}
@@ 395,6 467,11 @@ struct walker : pugi::xml_tree_walker
}
}
+ auto end_custom_token_node(pugi::xml_node &node)
+ {
+ adding_tokens = false;
+ }
+
auto on_leave(pugi::xml_node &node) -> bool final
{
log_node(node, Action::Exit);
@@ 408,6 485,9 @@ struct walker : pugi::xml_tree_walker
pop_newline_node(node);
return true;
}
+ if (is_custom_token_node(node)) {
+ end_custom_token_node(node);
+ }
}
return true;
}
@@ 425,8 505,8 @@ struct walker : pugi::xml_tree_walker
namespace gui::text
{
-
- auto RichTextParser::parse(const UTF8 &text, TextFormat *base_style) -> std::unique_ptr<TextDocument>
+ auto RichTextParser::parse(const UTF8 &text, TextFormat *base_style, TokenMap &&tokenMap)
+ -> std::unique_ptr<TextDocument>
{
log_parser("parsing: %s", text.c_str());
if (text.empty() || base_style == nullptr) {
@@ 435,12 515,11 @@ namespace gui::text
}
pugi::xml_document doc;
- walker walker(*base_style);
+ walker walker(*base_style, std::move(tokenMap));
doc.load_string(text.c_str());
doc.traverse(walker);
return std::make_unique<TextDocument>(walker.souvenirs());
}
-
}; // namespace gui::text
M module-gui/gui/widgets/RichTextParser.hpp => module-gui/gui/widgets/RichTextParser.hpp +27 -18
@@ 4,7 4,10 @@
#pragma once
#include <memory>
+#include <map>
#include <utf8/UTF8.hpp>
+#include <string>
+#include <variant>
namespace gui
{
@@ 14,21 17,22 @@ 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";
- inline const auto short_bold = "b";
+ constexpr inline auto align = "align";
+ constexpr inline auto center = "center";
+ constexpr inline auto right = "right";
+ constexpr inline auto left = "left";
+ constexpr inline auto color = "color";
+ constexpr inline auto font = "font";
+ constexpr inline auto size = "size";
+ constexpr inline auto weight = "weight";
+ constexpr inline auto regular = "regular";
+ constexpr inline auto bold = "bold";
+ constexpr inline auto light = "light";
+ constexpr inline auto node_text = "text";
+ constexpr inline auto node_br = "br";
+ constexpr inline auto node_p = "p";
+ constexpr inline auto short_bold = "b";
+ constexpr inline auto node_token = "token";
/// Rich text parser utility
/// supported specifiers
@@ 41,12 45,17 @@ namespace gui::text
/// * 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>`, gui::text::node_br `<br> </br>` and gui::text::shortened_bold `<b> </b>` now
- /// working identical - marking start and end with newline
+ /// * gui::text::node_p `<p> </p>`, gui::text::node_br `<br> </br>` working identical - marking start and end with
+ /// newline
+ /// * gui::text::short_bold `<b> </b>` works identical as `<text weight=bold> </text weight=bold>`
+ /// * gui::text::node_value `<token>Pattern</token>' replaces /"Pattern/" with respective token value if such is
+ /// present it `tokens` argument of `parse` method
/// @return empty document on error
class RichTextParser
{
public:
- auto parse(const UTF8 &text, TextFormat *base_style) -> std::unique_ptr<TextDocument>;
+ using TokenMap = std::map<std::string, std::variant<int, std::string>>;
+ [[nodiscard]] auto parse(const UTF8 &text, TextFormat *base_style, TokenMap &&tokens = TokenMap{})
+ -> std::unique_ptr<TextDocument>;
};
} // namespace gui::text