Commit 905cb000 authored by jan.koester's avatar jan.koester
Browse files

new layouts

parent 547e7890
Loading
Loading
Loading
Loading
+98 −8
Changes for editor/src/htmlimport.cpp: 98 added lines, 8 removed lines.
Original line number Diff line number Diff line
@@ -435,6 +435,47 @@ void normalizeShorthandProperties(std::map<std::string,std::string> &props)
    joinSides("margin", "margin-top", "margin-right", "margin-bottom", "margin-left");
}

// True if this element's own CSS says it shouldn't render at all. Checked
// against the widgets that have a real "hidden" boolean attribute (Section/
// Container/Grid/Article/List all do -- see each widget's own Render()) --
// "display" is otherwise always in these widgets' knownCss (dropped as
// "handled" regardless of its value, same as before this existed), so only
// the "none" case is worth singling out: it's the one value with a working,
// reliable place to go, since a widget's own dedicated attribute isn't
// subject to the CSS-specificity/scoping limitations of the CustomCss
// fallback (which needs a class or id, and can lose to the widget's own
// inline styling anyway).
bool cssMeansHidden(const std::map<std::string,std::string> &props)
{
    auto it = props.find("display");
    if (it != props.end() && it->second == "none") return true;
    it = props.find("visibility");
    if (it != props.end() && it->second == "hidden") return true;
    return false;
}

// Maps a CSS `justify-content`/`align-items` keyword value onto the
// constrained 3-way ("top"/"center"/"bottom") or 4-way (+"stretch")
// `vertical_align` select attribute that Container/Section/Article/Grid
// actually have -- none of them accept raw CSS values directly. Values with
// no equivalent in that enum (space-between, space-around, space-evenly,
// baseline, or "stretch" when @p allowStretch is false) return "" rather
// than being guessed; the caller leaves the source property out of
// `knownCss` in that case, so it still falls through to extractCustomCss
// instead of being silently dropped.
std::string mapToVerticalAlign(const std::string &cssValue, bool allowStretch)
{
    if (cssValue == "flex-start" || cssValue == "start" || cssValue == "left" || cssValue == "top")
        return "top";
    if (cssValue == "center")
        return "center";
    if (cssValue == "flex-end" || cssValue == "end" || cssValue == "right" || cssValue == "bottom")
        return "bottom";
    if (allowStretch && cssValue == "stretch")
        return "stretch";
    return "";
}

} // namespace

std::string blogi::htmlimport::extractCustomCss(
@@ -744,11 +785,20 @@ void blogi::htmlimport::htmlElementToWidgetXml(
                    w->SetAttribute("css_id", elemId.c_str());
                if (semanticTag != "section")
                    w->SetAttribute("semantic_tag", semanticTag.c_str());
                if (cssMeansHidden(cssProps))
                    w->SetAttribute("hidden", "true");

                std::vector<std::string> knownCss = {
                    "background-color","border-radius","border","opacity",
                    "padding","margin","max-width","min-height","width","height",
                    "display","flex-direction","justify-content"};
                    "display","visibility"};
                if (cssProps.count("justify-content")) {
                    std::string va = mapToVerticalAlign(cssProps["justify-content"], false);
                    if (!va.empty()) {
                        w->SetAttribute("vertical_align", va.c_str());
                        knownCss.push_back("justify-content");
                    }
                }
                std::string custom = extractCustomCss(cssProps, knownCss, mediaRules, cssClass, elemId);
                if (!custom.empty()) {
                    tinyxml2::XMLElement *ccEl = doc.NewElement("CustomCss");
@@ -777,11 +827,20 @@ void blogi::htmlimport::htmlElementToWidgetXml(
                    w->SetAttribute("margin", cssProps["margin"].c_str());
                if (!cssClass.empty())
                    w->SetAttribute("css_class", cssClass.c_str());
                if (cssMeansHidden(cssProps))
                    w->SetAttribute("hidden", "true");

                std::vector<std::string> knownCss = {
                    "background-color","border-radius","border","opacity",
                    "padding","margin","max-width","min-height","width","height",
                    "display","flex-direction","justify-content"};
                    "display","visibility"};
                if (cssProps.count("justify-content")) {
                    std::string va = mapToVerticalAlign(cssProps["justify-content"], false);
                    if (!va.empty()) {
                        w->SetAttribute("vertical_align", va.c_str());
                        knownCss.push_back("justify-content");
                    }
                }
                std::string custom = extractCustomCss(cssProps, knownCss, mediaRules, cssClass, elemId);
                if (!custom.empty()) {
                    tinyxml2::XMLElement *ccEl = doc.NewElement("CustomCss");
@@ -814,6 +873,8 @@ void blogi::htmlimport::htmlElementToWidgetXml(
                    w->SetAttribute("css_class", cssClass.c_str());
                if (!elemId.empty())
                    w->SetAttribute("css_id", elemId.c_str());
                if (cssMeansHidden(cssProps))
                    w->SetAttribute("hidden", "true");

                if (isGrid) {
                    if (cssProps.count("grid-template-columns"))
@@ -824,8 +885,15 @@ void blogi::htmlimport::htmlElementToWidgetXml(
                        w->SetAttribute("gap", cssProps["gap"].c_str());
                    std::vector<std::string> knownCss = {
                        "background-color","border-radius","border","opacity",
                        "padding","margin","width","height","display",
                        "grid-template-columns","grid-template-rows","gap","align-items"};
                        "padding","margin","width","height","display","visibility",
                        "grid-template-columns","grid-template-rows","gap"};
                    if (cssProps.count("align-items")) {
                        std::string va = mapToVerticalAlign(cssProps["align-items"], true);
                        if (!va.empty()) {
                            w->SetAttribute("vertical_align", va.c_str());
                            knownCss.push_back("align-items");
                        }
                    }
                    std::string custom = extractCustomCss(cssProps, knownCss, mediaRules, cssClass, elemId);
                    if (!custom.empty()) {
                        tinyxml2::XMLElement *ccEl = doc.NewElement("CustomCss");
@@ -835,8 +903,14 @@ void blogi::htmlimport::htmlElementToWidgetXml(
                } else {
                    std::vector<std::string> knownCss = {
                        "background-color","border-radius","border","opacity",
                        "padding","margin","width","height","display",
                        "flex-direction","justify-content"};
                        "padding","margin","width","height","display","visibility"};
                    if (cssProps.count("justify-content")) {
                        std::string va = mapToVerticalAlign(cssProps["justify-content"], false);
                        if (!va.empty()) {
                            w->SetAttribute("vertical_align", va.c_str());
                            knownCss.push_back("justify-content");
                        }
                    }
                    std::string custom = extractCustomCss(cssProps, knownCss, mediaRules, cssClass, elemId);
                    if (!custom.empty()) {
                        tinyxml2::XMLElement *ccEl = doc.NewElement("CustomCss");
@@ -1020,6 +1094,22 @@ void blogi::htmlimport::htmlElementToWidgetXml(
                    w->SetAttribute("padding", cssProps["padding"].c_str());
                if (cssProps.count("margin"))
                    w->SetAttribute("margin", cssProps["margin"].c_str());
                if (cssMeansHidden(cssProps))
                    w->SetAttribute("hidden", "true");
                // "row"/"column" match List's own "horizontal"/"vertical"
                // enum 1:1 in meaning even though the words differ.
                if (cssProps.count("flex-direction")) {
                    const std::string &fd = cssProps["flex-direction"];
                    if (fd == "row" || fd == "row-reverse")
                        w->SetAttribute("direction", "horizontal");
                    else if (fd == "column" || fd == "column-reverse")
                        w->SetAttribute("direction", "vertical");
                }
                // List's align_items field already uses raw CSS align-items
                // keywords (flex-start/center/flex-end/stretch/baseline), so
                // this is a direct passthrough, not a translation.
                if (cssProps.count("align-items"))
                    w->SetAttribute("align_items", cssProps["align-items"].c_str());

                libhtmlpp::Element *liEl = el->firstChild();
                while (liEl) {
@@ -1041,8 +1131,8 @@ void blogi::htmlimport::htmlElementToWidgetXml(

                std::vector<std::string> knownCss = {
                    "font-size","color","line-height","list-style-type",
                    "padding","margin","width","height","display",
                    "flex-direction","gap"};
                    "padding","margin","width","height","display","visibility",
                    "flex-direction","gap","align-items"};
                std::string custom = extractCustomCss(cssProps, knownCss, mediaRules, cssClass, elemId);
                if (!custom.empty()) {
                    tinyxml2::XMLElement *ccEl = doc.NewElement("CustomCss");
+81 −0
Changes for tests/htmlimport_smoke.cpp: 81 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -150,6 +150,87 @@ int main(){
              "font-style reaches the TextBox's font_style attribute (previously silently dropped)");
    }

    // --- display:none / visibility:hidden maps to the widget's hidden attr ---
    std::cout << "=== display:none / visibility:hidden ===" << std::endl;
    {
        std::string xml = importXml("<div style=\"display:none\">hi</div>");
        check(xml.find("hidden=\"true\"") != std::string::npos,
              "display:none on a Container maps to hidden=\"true\" (previously silently dropped)");
    }
    {
        std::string xml = importXml("<section style=\"visibility:hidden\">hi</section>");
        check(xml.find("hidden=\"true\"") != std::string::npos,
              "visibility:hidden on a Section maps to hidden=\"true\"");
    }
    {
        std::string xml = importXml("<div style=\"display:flex\">hi</div>");
        check(xml.find("hidden=\"true\"") == std::string::npos,
              "display:flex (not \"none\") does not mark the element hidden");
    }
    {
        std::string xml = importXml("<article style=\"display:none\">hi</article>");
        check(xml.find("hidden=\"true\"") != std::string::npos,
              "display:none on an Article maps to hidden=\"true\"");
    }
    {
        std::string xml = importXml(
            "<ul style=\"display:none\"><li>a</li></ul>");
        check(xml.find("hidden=\"true\"") != std::string::npos,
              "display:none on a List maps to hidden=\"true\"");
    }
    {
        // "display" can only hold one value, so grid-ness is signaled by
        // display:grid while visibility:hidden (independent property)
        // triggers the hidden attribute without the two fighting over the
        // same CSS property.
        std::string html =
            "<div style=\"display:grid; grid-template-columns:1fr 1fr; visibility:hidden\">hi</div>";
        std::string xml = importXml(html);
        check(xml.find("<Grid") != std::string::npos,
              "display:grid still produces a Grid widget");
        check(xml.find("hidden=\"true\"") != std::string::npos,
              "visibility:hidden on a Grid maps to hidden=\"true\"");
    }

    // --- justify-content -> vertical_align (Container/Section/Article) ---
    std::cout << "=== justify-content -> vertical_align ===" << std::endl;
    {
        std::string xml = importXml("<div style=\"justify-content:center\">hi</div>");
        check(xml.find("vertical_align=\"center\"") != std::string::npos,
              "justify-content:center maps to vertical_align=\"center\" on Container");
    }
    {
        std::string xml = importXml("<div class=\"x\" style=\"justify-content:space-between\">hi</div>");
        check(xml.find("vertical_align=") == std::string::npos,
              "justify-content:space-between has no vertical_align equivalent, isn't guessed");
        check(xml.find("justify-content") != std::string::npos,
              "unmappable justify-content still falls through to CustomCss");
    }

    // --- align-items -> vertical_align (Grid) / align_items (List) ---
    std::cout << "=== align-items ===" << std::endl;
    {
        std::string html = "<div style=\"display:grid; align-items:stretch\">hi</div>";
        std::string xml = importXml(html);
        check(xml.find("vertical_align=\"stretch\"") != std::string::npos,
              "align-items:stretch maps to vertical_align=\"stretch\" on Grid");
    }
    {
        std::string html = "<ul style=\"align-items:center\"><li>a</li></ul>";
        std::string xml = importXml(html);
        check(xml.find("align_items=\"center\"") != std::string::npos,
              "align-items passes through directly to List's align_items attribute");
    }

    // --- flex-direction -> direction (List only) ---
    std::cout << "=== flex-direction -> direction (List) ===" << std::endl;
    {
        std::string html = "<ul style=\"flex-direction:row\"><li>a</li></ul>";
        std::string xml = importXml(html);
        check(xml.find("direction=\"horizontal\"") != std::string::npos,
              "flex-direction:row maps to List's direction=\"horizontal\"");
    }

    // --- Summary ---
    std::cout << "\n=== " << passCount << "/" << testCount << " tests passed ===" << std::endl;