~aleteoryx/muditaos

1981c8b2cbea74a6147c0d3f4ce985eb1f86973e — Michał Kamoń 5 years ago 6533aba
[EGD-4507] RichText CustomTokens support added (#1083)

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