Commit 964c421a authored by Jan Köster's avatar Jan Köster
Browse files

list icons

parent e058ec18
Loading
Loading
Loading
Loading
+151 −1
Changes for editor/widgets/textbox/textbox.cpp: 151 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -100,6 +100,11 @@ extern "C" {
            // Custom CSS
            std::string customCss = "";

            // Character counter
            bool showCharCount       = false;
            std::string charCountSymbol   = "none";   // none, checkmark, check_green, arrow, star, hash, bullet, eye
            std::string charCountPosition = "after";  // after, before, top_right, bottom_right

        public:

            // --- UI Settings ---
@@ -271,7 +276,15 @@ extern "C" {
                addField("white_space", "Zeilenumbruch", "select", nullptr, "all", mkOpts({"normal","nowrap","pre","pre-wrap","pre-line","break-spaces"}));
                addField("text_decoration", "Text Decoration", "select", nullptr, "all", mkOpts({"none","underline","overline","line-through","underline overline"}));
                addField("underline_color", "Underline Color", "color_var", nullptr, "all");
                addField("icon", "Icon", "select", nullptr, "all", mkOpts({"none","phone","address","email","arrow_left","arrow_right","arrow_up","arrow_down"}));
                addField("icon", "Icon", "select", nullptr, "all", mkOpts({
                    "none",
                    "phone","address","email",
                    "arrow_left","arrow_right","arrow_up","arrow_down","arrow_diagonal",
                    "double_arrow_right",
                    "checkmark","check_circle","check_green",
                    "star","star_outline",
                    "info","warning","heart","bullet","cross"
                }));
                addField("icon_color", "Icon Color", "color_var", nullptr, "all");
                addField("hidden", "Hidden", "checkbox", nullptr, "desktop");
                {
@@ -290,6 +303,16 @@ extern "C" {
                    json_object_object_add(hf, "default", json_object_new_boolean(false));
                }
                addField("custom_css", "Custom CSS", "textarea", ".we-xxx { color: red; }");
                // Character counter
                addField("show_char_count", "Zeichenzähler anzeigen", "checkbox", nullptr, "all");
                {
                    json_object *hf = json_object_array_get_idx(arr, json_object_array_length(arr) - 1);
                    json_object_object_add(hf, "default", json_object_new_boolean(false));
                }
                addField("char_count_symbol", "Zähler-Symbol", "select", nullptr, "all",
                    mkOpts({"none","checkmark","check_green","arrow","star","hash","bullet","eye"}));
                addField("char_count_position", "Zähler-Position", "select", nullptr, "all",
                    mkOpts({"after","before","top_right","bottom_right"}));
                return arr;
            }

@@ -329,6 +352,11 @@ extern "C" {
                element->SetAttribute("hidden", hidden ? "true" : "false");
                element->SetAttribute("m_hidden", m_hidden ? "true" : "false");

                // Character counter
                element->SetAttribute("show_char_count", showCharCount ? "true" : "false");
                element->SetAttribute("char_count_symbol", charCountSymbol.c_str());
                element->SetAttribute("char_count_position", charCountPosition.c_str());

                if (!customCss.empty()) {
                    tinyxml2::XMLElement* cssEl = doc->NewElement("CustomCss");
                    cssEl->SetText(customCss.c_str());
@@ -411,6 +439,14 @@ extern "C" {
                val = xml_data->Attribute("m_hidden");
                if (val) m_hidden = (strcmp(val, "true") == 0);

                // Character counter
                val = xml_data->Attribute("show_char_count");
                if (val) showCharCount = (strcmp(val, "true") == 0);
                val = xml_data->Attribute("char_count_symbol");
                if (val) charCountSymbol = val;
                val = xml_data->Attribute("char_count_position");
                if (val) charCountPosition = val;

                tinyxml2::XMLElement* cssEl = xml_data->FirstChildElement("CustomCss");
                if (cssEl) {
                    const char* cssVal = cssEl->GetText();
@@ -446,6 +482,9 @@ extern "C" {
                json_object_object_add(obj, "hidden", json_object_new_boolean(hidden));
                json_object_object_add(obj, "m_hidden", json_object_new_boolean(m_hidden));
                json_object_object_add(obj, "custom_css", json_object_new_string(customCss.c_str()));
                json_object_object_add(obj, "show_char_count", json_object_new_boolean(showCharCount));
                json_object_object_add(obj, "char_count_symbol", json_object_new_string(charCountSymbol.c_str()));
                json_object_object_add(obj, "char_count_position", json_object_new_string(charCountPosition.c_str()));
                const char *result = json_object_to_json_string_ext(obj, JSON_C_TO_STRING_NOSLASHESCAPE);
                std::string out(result);
                json_object_put(obj);
@@ -559,11 +598,58 @@ extern "C" {
                    val = json_object_get_string(val_obj);
                    if (val) customCss = val;
                }
                if (json_object_object_get_ex(obj, "show_char_count", &val_obj)) {
                    if (json_object_is_type(val_obj, json_type_string)) {
                        const char *s = json_object_get_string(val_obj);
                        showCharCount = (s && (strcmp(s, "true") == 0 || strcmp(s, "1") == 0));
                    } else {
                        showCharCount = json_object_get_boolean(val_obj);
                    }
                }
                if (json_object_object_get_ex(obj, "char_count_symbol", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) charCountSymbol = val;
                }
                if (json_object_object_get_ex(obj, "char_count_position", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) charCountPosition = val;
                }

                json_object_put(obj);
            }

            // --- HTML Rendering ---
            // Strip HTML tags and return visible character count
            static size_t countVisibleChars(const std::string &html) {
                size_t count = 0;
                bool inTag = false;
                for (char c : html) {
                    if (c == '<') { inTag = true; continue; }
                    if (c == '>') { inTag = false; continue; }
                    if (!inTag && c != '\r' && c != '\n') ++count;
                }
                return count;
            }

            // Returns the SVG symbol string for char count badge symbols
            static std::string charCountSymbolSvg(const std::string &sym) {
                if (sym == "checkmark")
                    return "<svg style=\"vertical-align:middle;margin-right:3px;\" width=\"0.9em\" height=\"0.9em\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/></svg>";
                if (sym == "check_green")
                    return "<svg style=\"vertical-align:middle;margin-right:3px;\" width=\"0.9em\" height=\"0.9em\" viewBox=\"0 0 24 24\" fill=\"#3ad43a\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>";
                if (sym == "arrow")
                    return "<svg style=\"vertical-align:middle;margin-right:3px;\" width=\"0.9em\" height=\"0.9em\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z\"/></svg>";
                if (sym == "star")
                    return "<svg style=\"vertical-align:middle;margin-right:3px;\" width=\"0.9em\" height=\"0.9em\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z\"/></svg>";
                if (sym == "hash")
                    return "<span style=\"margin-right:2px;font-weight:bold;\">#</span>";
                if (sym == "bullet")
                    return "<span style=\"margin-right:4px;\">&#8226;</span>";
                if (sym == "eye")
                    return "<svg style=\"vertical-align:middle;margin-right:3px;\" width=\"0.9em\" height=\"0.9em\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z\"/></svg>";
                return ""; // "none"
            }

            void Render(libhtmlpp::HtmlElement &el) const override {
                libhtmlpp::HtmlElement box("div");

@@ -645,6 +731,41 @@ extern "C" {
                    el.appendChild(cssStyle);
                }

                // --- Character counter badge helper ---
                auto appendCharCountBadge = [&](libhtmlpp::HtmlElement &target) {
                    size_t charCount = countVisibleChars(content);
                    std::string symHtml  = charCountSymbolSvg(charCountSymbol);
                    std::string badgeStyle =
                        "display:inline-flex;align-items:center;gap:2px;"
                        "font-size:0.78em;font-weight:600;letter-spacing:0.03em;"
                        "padding:2px 8px;border-radius:999px;"
                        "background:var(--blogi-surface,rgba(49,54,59,0.7));"
                        "color:var(--blogi-accent,#3ad43a);"
                        "border:1px solid var(--blogi-accent,#3ad43a);"
                        "line-height:1.5;user-select:none;";
                    if (charCountPosition == "top_right" || charCountPosition == "bottom_right") {
                        badgeStyle += "position:absolute;right:6px;";
                        badgeStyle += (charCountPosition == "top_right") ? "top:4px;" : "bottom:4px;";
                    }
                    std::string badgeHtml =
                        "<span class=\"we-charcount\" style=\"" + badgeStyle + "\">"
                        + symHtml + std::to_string(charCount) + "</span>";
                    try {
                        libhtmlpp::HtmlString buf(badgeHtml);
                        target.appendChild(buf.parse());
                    } catch (...) {}
                };

                // Absolute-positioned badges need box to be a positioned ancestor
                if (showCharCount && (charCountPosition == "top_right" || charCountPosition == "bottom_right")) {
                    if (style.find("position:") == std::string::npos)
                        box.setAttribute("style", (style + " position:relative;").c_str());
                }

                // "before": badge is the first child of box (before icon and content)
                if (showCharCount && charCountPosition == "before")
                    appendCharCountBadge(box);

                if (icon != "none" && !icon.empty()) {
                    std::string fill = iconColor.empty() ? "currentColor" : iconColor;
                    std::string svg_base = "<svg style=\"vertical-align: middle; margin-right: 8px;\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\" fill=\"" + fill + "\" xmlns=\"http://www.w3.org/2000/svg\">";
@@ -663,6 +784,31 @@ extern "C" {
                        iconHtml = svg_base + "<path d=\"M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6 1.41 1.41z\"/></svg>";
                    } else if (icon == "arrow_down") {
                        iconHtml = svg_base + "<path d=\"M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z\"/></svg>";
                    } else if (icon == "arrow_diagonal") {
                        iconHtml = svg_base + "<path d=\"M7 17l9.2-9.2M17 7H7v2h8v8h2V7z\"/></svg>";
                    } else if (icon == "double_arrow_right") {
                        iconHtml = svg_base + "<path d=\"M15 5l-1.41 1.41L18.17 11H2v2h16.17l-4.59 4.59L15 19l7-7-7-7z\"/><path d=\"M7 5L5.59 6.41 10.17 11H2v2h8.17l-4.59 4.59L7 19l7-7-7-7z\" opacity=\".5\"/></svg>";
                    } else if (icon == "checkmark") {
                        iconHtml = svg_base + "<path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/></svg>";
                    } else if (icon == "check_circle") {
                        iconHtml = svg_base + "<path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>";
                    } else if (icon == "check_green") {
                        std::string svg_green = "<svg style=\"vertical-align: middle; margin-right: 8px;\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\" fill=\"#3ad43a\" xmlns=\"http://www.w3.org/2000/svg\">";
                        iconHtml = svg_green + "<path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>";
                    } else if (icon == "star") {
                        iconHtml = svg_base + "<path d=\"M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z\"/></svg>";
                    } else if (icon == "star_outline") {
                        iconHtml = svg_base + "<path d=\"M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z\"/></svg>";
                    } else if (icon == "info") {
                        iconHtml = svg_base + "<path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\"/></svg>";
                    } else if (icon == "warning") {
                        iconHtml = svg_base + "<path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/></svg>";
                    } else if (icon == "heart") {
                        iconHtml = svg_base + "<path d=\"M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z\"/></svg>";
                    } else if (icon == "bullet") {
                        iconHtml = svg_base + "<circle cx=\"12\" cy=\"12\" r=\"6\"/></svg>";
                    } else if (icon == "cross") {
                        iconHtml = svg_base + "<path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/></svg>";
                    }
                    if (!iconHtml.empty()) {
                        try {
@@ -720,6 +866,10 @@ extern "C" {
                    }
                }

                // "after", "top_right", "bottom_right": badge appended after content
                if (showCharCount && charCountPosition != "before")
                    appendCharCountBadge(box);

                el.appendChild(box);
            }