diff --git a/code/nel/include/nel/gui/css_style.h b/code/nel/include/nel/gui/css_style.h index dea4a1f43..74fcf240d 100644 --- a/code/nel/include/nel/gui/css_style.h +++ b/code/nel/include/nel/gui/css_style.h @@ -47,7 +47,7 @@ namespace NLGUI sint32 X; sint32 Y; NLMISC::CRGBA Color; - }; + }; public: CStyleParams () : FontFamily(""), TextColor(255,255,255,255), TextShadow() { @@ -110,7 +110,7 @@ namespace NLGUI // pseudo element like ':before' std::string PseudoElement; - + // returns selector specificity uint specificity() const; }; @@ -134,6 +134,12 @@ namespace NLGUI void getStyleParams(const std::string &styleString, CStyleParams &style, const CStyleParams ¤t) const; void getStyleParams(const TStyle &styleRules, CStyleParams &style, const CStyleParams ¤t) const; + // extract from styleRules into style.StyleRules (expand shorthand, normalize, calculate current font-size) + void normalize(const TStyle &styleRules, CStyleParams &style, const CStyleParams ¤t) const; + + // apply style.StyleRyles + void apply(CStyleParams &style, const CStyleParams ¤t) const; + // merge src into dest by overwriting key in dest void merge(TStyle &dst, const TStyle &src) const; @@ -171,7 +177,7 @@ namespace NLGUI Current.MaxWidth=-1; Current.MaxHeight=-1; Current.BorderWidth=1; - + Current.StyleRules.clear(); } diff --git a/code/nel/src/gui/css_style.cpp b/code/nel/src/gui/css_style.cpp index 88e438870..0147b06f5 100644 --- a/code/nel/src/gui/css_style.cpp +++ b/code/nel/src/gui/css_style.cpp @@ -227,7 +227,7 @@ namespace NLGUI // child of - immediate parent must match previous selector if (!child->parent) { - return false; + return false; } child = child->parent; mustMatchNext = true; @@ -332,10 +332,18 @@ namespace NLGUI return; } - // first pass: - // - get font-size for 'em' sizes - // - split shorthand to its parts - // - get TextColor value that could be used for 'currentcolor' + normalize(styleRules, style, current); + apply(style, current); + } + + // first pass + // - get font-size for 'em' sizes + // - split shorthand to its parts + // - get TextColor value that could be used for 'currentcolor' + // - normalize values + void CCssStyle::normalize(const TStyle &styleRules, CStyleParams &style, const CStyleParams ¤t) const + { + TStyle::const_iterator it; for (it=styleRules.begin(); it != styleRules.end(); ++it) { // update local copy of applied style @@ -407,6 +415,7 @@ namespace NLGUI } else { + float tmpf; std::string unit; if (getCssLength(tmpf, unit, it->second.c_str())) { @@ -428,10 +437,39 @@ namespace NLGUI { parseBackgroundShorthand(it->second, style); } + else + if (it->first == "background-repeat") + { + // old ryzom specific value + if (it->second == "1") + style.StyleRules[it->first] = "repeat"; + } + else + if (it->first == "background-scale") + { + // replace old ryzom specific rule with background-size + if (it->second != "1") + { + style.StyleRules["background-size"] = "auto"; + } + else + { + style.StyleRules["background-size"] = "100%"; + } + + TStyle::iterator pos = style.StyleRules.find(it->first); + if (pos != style.StyleRules.end()) + style.StyleRules.erase(pos); + } } + } - // second pass: rest of style - for (it=styleRules.begin(); it != styleRules.end(); ++it) + // apply style rules + void CCssStyle::apply(CStyleParams &style, const CStyleParams ¤t) const + { + float tmpf; + TStyle::const_iterator it; + for (it=style.StyleRules.begin(); it != style.StyleRules.end(); ++it) { if (it->first == "border" || it->first == "border-width") { @@ -456,8 +494,9 @@ namespace NLGUI { style.BorderWidth = 5; } - else + else { + float tmpf; std::string unit; if (getCssLength(tmpf, unit, it->second.c_str())) { @@ -763,10 +802,46 @@ namespace NLGUI else if (it->second == "transparent") style.BackgroundColorOver = CRGBA(0, 0, 0, 0); else if (it->second == "currentcolor") - style.BackgroundColorOver = style.TextColor; + style.BackgroundColorOver = style.TextColor; else scanHTMLColor(it->second.c_str(), style.BackgroundColorOver); } + else + if (it->first == "background-image") + { + // normalize + std::string image = trim(it->second); + if (toLower(image.substr(0, 4)) == "url(") + { + image = image.substr(4, image.size()-5); + } + style.StyleRules[it->first] = trimQuotes(image); + } + else + if (it->first == "background-repeat") + { + // normalize + std::string val = toLower(trim(it->second)); + std::vector parts; + NLMISC::splitString(val, " ", parts); + // check for "repeat repeat" + if (parts.size() == 2 && parts[0] == parts[1]) + val = parts[0]; + + style.StyleRules[it->first] = val; + } + else + if (it->first == "background-size") + { + // normalize + std::string val = toLower(trim(it->second)); + std::vector parts; + NLMISC::splitString(val, " ", parts); + if (parts.size() == 2 && parts[0] == parts[1]) + val = parts[0]; + + style.StyleRules[it->first] = val; + } } // if outer element has underline set, then inner element cannot remove it @@ -795,115 +870,278 @@ namespace NLGUI "background-attachment", "background-origin", "background-clip", "background-color"}; std::string values[nbProps]; bool found[nbProps] = {false}; - + bool bgClipFound = false; + std::string bgClipValue; + std::string bgPositionX; + std::string bgPositionY; uint partIndex = 0; std::vector parts; std::vector::iterator it; // FIXME: this will fail if url() contains ' ' chars + // FIXME: this will also fail on 'background: rgb(255, 0, 0)' NLMISC::splitString(value, " ", parts); bool failed = false; - for(uint index = 0; index < parts.size(); index++) + bool allowSize = false; + uint index = 0; + while(!failed && index < parts.size()) { - const std::string val = toLower(trim(parts[index])); - + std::string val = toLower(parts[index]); + bool matches = false; for(uint i = 0; i < nbProps; i++) { - if (found[i]) - { - continue; - } + if (found[i]) continue; if (props[i] == "background-image") { if (val.substr(0, 4) == "url(") { + matches = true; + found[i] = true; // use original value as 'val' is lowercase values[i] = parts[index]; - found[i] = true; } } else if (props[i] == "background-position") { - // TODO: + uint next = index; + bool loop = false; + do + { + float fval; + std::string unit; + + // first loop -> true + // second loop -> false && break + loop = !loop; + + val = toLower(parts[next]); + if (val == "center") + { + if (bgPositionX.empty()) bgPositionX = "center"; + if (bgPositionY.empty()) bgPositionY = "center"; + // consume 'center' + next++; + } + else if (val == "left" || val == "right") + { + bgPositionX = val; + // consume 'left|right' + next++; + if(next < parts.size() && getCssLength(fval, unit, parts[next])) + { + bgPositionX += " " + toString("%.0f%s", fval, unit.c_str()); + // consume css length + next++; + } + } + else if (val == "top" || val == "bottom") + { + bgPositionY = val; + // consume top|bottom + next++; + if (next < parts.size() && getCssLength(fval, unit, parts[next])) + { + bgPositionY += " " + toString("%.0f%s", fval, unit.c_str()); + // consume css length + next++; + } + } + } while (loop); + + // + if (!bgPositionX.empty() && !bgPositionY.empty()) + { + matches = true; + found[i] = true; + // consume position values if there were any + index = next-1; + + // look ahead to see if size is next + if (next < parts.size() && parts[next] == "/") + allowSize = true; + } } else if (props[i] == "background-size") { - // TODO: [ | auto ]{1,2} cover | contain + if (allowSize && val == "/") + { + uint next = index + 1; + if (next < parts.size()) + { + val = toLower(parts[next]); + if (val == "cover" || val == "contain") + { + matches = true; + found[i] = true; + values[i] = val; + index = next; + } + else + { + float fval; + std::string unit; + std::string h, v; + + if (val == "auto" || getCssLength(fval, unit, val)) + { + if (val == "auto") + h = v = "auto"; + else + h = v = toString("%.0f%s", fval, unit.c_str()); + + next++; + if (next < parts.size()) + { + val = toLower(parts[next]); + if (val == "auto") + v = "auto"; + else if (getCssLength(fval, unit, val)) + v = toString("%.0f%s", fval, unit.c_str()); + else + next--; // not size token + } + else + { + // not size token + next--; + } + } + + if (!h.empty() && !v.empty()) + { + matches = true; + found[i] = true; + values[i] = h + " " + v; + index = next; + } + } + } + else + { + // no size, just '/' + failed = true; + break; + } + } } else if (props[i] == "background-repeat") { if (val == "repeat-x" || val == "repeat-y" || val == "repeat" || val == "space" || val == "round" || val == "no-repeat") { + matches = true; + found[i] = true; + if (val == "repeat-x") { values[i] = "repeat no-repeat"; } else if (val == "repeat-y") { - values[i] = "no-repeat repeat"; + values[i] = "no-repeat repeat"; } else { std::string horiz = val; std::string vert = val; - if (index+1 < parts.size()) + uint next = index + 1; + if (next < parts.size()) { - std::string next = toLower(trim(parts[index+1])); - if (next == "repeat" || next == "space" || next == "round" || next == "no-repeat") + val = toLower(parts[next]); + if (val == "repeat" || val == "space" || val == "round" || val == "no-repeat") { - vert = next; - index++; + vert = val; + index = next; } } - - values[i] = horiz + " " + vert; + if (vert == horiz) + values[i] = vert; + else + values[i] = horiz + " " + vert; } - - found[i] = true; } } else if (props[i] == "background-attachment") { - // TODO: scroll | fixed | local + if (val == "scroll" || val == "fixed" || val == "local") + { + matches = true; + found[i] = true; + values[i] = val; + } } - else if (props[i] == "background-origin" || props[i] == "background-clip") + else if (props[i] == "background-origin") { - // same values for both if (val == "padding-box" || val == "border-box" || val == "content-box") { + matches = true; + found[i] = true; values[i] = val; + + // first time background-origin is set, also set background-clip + if (!bgClipFound) + bgClipValue = val; + } + } + else if (props[i] == "background-clip") + { + if (val == "text" || val == "padding-box" || val == "border-box" || val == "content-box") + { + matches = true; found[i] = true; + bgClipFound = true; + bgClipValue = val; } } else if (props[i] == "background-color") { CRGBA color; - if (!scanHTMLColor(val.c_str(), color)) + if (val == "transparent" || val == "currentcolor" || scanHTMLColor(val.c_str(), color)) { - failed = true; - break; + matches = true; + found[i] = true; + values[i] = val; } - values[i] = val; - // color should come as last item - break; } + + // prop was found and parsed + if (found[i]) + break; } + failed = !matches; + + index++; } // invalidate whole rule if (failed) { - return; + bgClipFound = false; + for(uint i = 0; i < nbProps; i++) + { + found[i] = false; + } } - // apply found styles + // apply found styles or use default for(uint i = 0; i < nbProps; i++) { if (found[i]) { - style.StyleRules[props[i]] = values[i]; + if (props[i] == "background-position") + { + style.StyleRules["background-position-x"] = bgPositionX; + style.StyleRules["background-position-y"] = bgPositionY; + } + else if (props[i] == "background-clip") + { + style.StyleRules["background-clip"] = bgClipValue; + } + else + { + style.StyleRules[props[i]] = values[i]; + } } else { @@ -914,27 +1152,32 @@ namespace NLGUI } else if (props[i] == "background-position") { - //style.StyleRules[props[i]] = "0% 0%"; + style.StyleRules[props[i]] = "0% 0%"; + style.StyleRules["background-position-x"] = "left 0%"; + style.StyleRules["background-position-y"] = "top 0%"; } else if (props[i] == "background-size") { - //style.StyleRules[props[i]] = "auto auto"; + style.StyleRules[props[i]] = "auto auto"; } else if (props[i] == "background-repeat") { - style.StyleRules[props[i]] = "repeat repeat"; + style.StyleRules[props[i]] = "repeat"; } else if(props[i] == "background-attachment") { - //style.StyleRules[props[i]] = "scroll"; + style.StyleRules[props[i]] = "scroll"; } else if(props[i] == "background-origin") { - //style.StyleRules[props[i]] = "padding-box"; + style.StyleRules[props[i]] = "padding-box"; } else if (props[i] == "background-clip") { - //style.StyleRules[props[i]] = "border-box"; + if (bgClipFound) + style.StyleRules[props[i]] = bgClipValue; + else + style.StyleRules[props[i]] = "border-box"; } else if (props[i] == "background-color") { diff --git a/code/nel/src/gui/group_html.cpp b/code/nel/src/gui/group_html.cpp index 38d3a2ea7..20f1c428f 100644 --- a/code/nel/src/gui/group_html.cpp +++ b/code/nel/src/gui/group_html.cpp @@ -1220,7 +1220,7 @@ namespace NLGUI { std::string::size_type start; std::string token; - + // not supported // counter, open-quote, close-quote, no-open-quote, no-close-quote if (content[pos] == '"' || content[pos] == '\'') @@ -3273,9 +3273,9 @@ namespace NLGUI setTitle(_TitleString); } - + std::string CGroupHTML::getTitle() const { - return _TitleString.toUtf8(); + return _TitleString.toUtf8(); }; // *************************************************************************** @@ -3547,7 +3547,7 @@ namespace NLGUI // todo handle unicode POST here if (form.Entries[i].Checkbox->getPushed ()) { - entryData = form.Entries[i].Value; + entryData = form.Entries[i].Value; addEntry = true; } } @@ -4077,7 +4077,7 @@ namespace NLGUI { // clear the page beginBuild(); - + // clear previous page and state removeContent(); @@ -4727,7 +4727,7 @@ namespace NLGUI nlwarning("BUG: unable to find current element iterator from parent"); return; } - + // where fragment should be moved std::list::iterator insertBefore; if (_CurrentHTMLNextSibling == NULL) @@ -5039,7 +5039,7 @@ namespace NLGUI valign = _Style.Current.VerticalAlign; else if (elm.hasNonEmptyAttribute("valign")) valign = toLower(elm.getAttribute("valign")); - + if (valign == "top") cellParams.VAlign = CGroupCell::Top; else if (valign == "middle") @@ -5047,7 +5047,7 @@ namespace NLGUI else if (valign == "bottom") cellParams.VAlign = CGroupCell::Bottom; } - + _CellParams.push_back (cellParams); } @@ -5059,15 +5059,9 @@ namespace NLGUI // non-empty image if (_Style.hasStyle("background-image")) { - // value '1' and 'background-scale' are ryzom only - bool repeat = _Style.checkStyle("background-repeat", "1") || _Style.checkStyle("background-repeat", "repeat"); - bool scale = _Style.checkStyle("background-scale", "1") || _Style.checkStyle("background-size", "100% 100%"); - std::string image = trim(_Style.getStyle("background-image")); - string::size_type texExt = toLower(image).find("url("); - if (texExt != string::npos) - { - image = image.substr(texExt+4, image.size()-texExt-5); - } + bool repeat = _Style.checkStyle("background-repeat", "repeat"); + bool scale = _Style.checkStyle("background-size", "100%"); + std::string image = _Style.getStyle("background-image"); if (!image.empty()) { if (root) @@ -5077,7 +5071,7 @@ namespace NLGUI // TODO: else // default background color is transparent, so image does not show - if (!_Style.hasStyle("background-color")) + if (!_Style.hasStyle("background-color") || _Style.checkStyle("background-color", "transparent")) { _Style.applyStyle("background-color: #fff;"); } @@ -5092,7 +5086,7 @@ namespace NLGUI { setBackgroundColor(bgColor); } - // TODO: else + // TODO: else } } @@ -5398,7 +5392,7 @@ namespace NLGUI { newParagraph(LIBeginSpace); } - + renderPseudoElement(":before", elm); } @@ -5626,7 +5620,7 @@ namespace NLGUI // no 'type' attribute, or empty return; } - + // Global color flag if (elm.hasAttribute("global_color")) _Style.Current.GlobalColor = true; @@ -5891,7 +5885,7 @@ namespace NLGUI _ParsingLua = _TrustedDomain; // Only parse lua if TrustedDomain _LuaScript.clear(); } - + void CGroupHTML::htmlLUAend(const CHtmlElement &elm) { if (_ParsingLua && _TrustedDomain) @@ -6218,7 +6212,7 @@ namespace NLGUI { if (elm.hasNonEmptyAttribute("cellspacing")) fromString(elm.getAttribute("cellspacing"), table->CellSpacing); - + // TODO: cssLength, horiz/vert values if (_Style.hasStyle("border-spacing")) fromString(_Style.getStyle("border-spacing"), table->CellSpacing); @@ -6325,29 +6319,16 @@ namespace NLGUI _Cells.back() = new CGroupCell(CViewBase::TCtorParam()); - if (_Style.checkStyle("background-repeat", "1") || _Style.checkStyle("background-repeat", "repeat")) + if (_Style.checkStyle("background-repeat", "repeat")) _Cells.back()->setTextureTile(true); - if (_Style.checkStyle("background-scale", "1") || _Style.checkStyle("background-size", "100% 100%")) + if (_Style.checkStyle("background-size", "100%")) _Cells.back()->setTextureScale(true); if (_Style.hasStyle("background-image")) { string image = _Style.getStyle("background-image"); - - string::size_type texExt = toLower(image).find("url("); - // Url image - if (texExt != string::npos) - { - // Remove url() - image = image.substr(4, image.size()-5); - addImageDownload(image, _Cells.back()); - // Image in BNP - } - else - { - _Cells.back()->setTexture(image); - } + addImageDownload(image, _Cells.back()); } if (elm.hasNonEmptyAttribute("colspan")) @@ -6368,7 +6349,7 @@ namespace NLGUI getPercentage (_Cells.back()->WidthWanted, _Cells.back()->TableRatio, _Style.getStyle("width").c_str()); else if (elm.hasNonEmptyAttribute("width")) getPercentage (_Cells.back()->WidthWanted, _Cells.back()->TableRatio, elm.getAttribute("width").c_str()); - + if (_Style.hasStyle("height")) getPercentage (_Cells.back()->Height, temp, _Style.getStyle("height").c_str()); else if (elm.hasNonEmptyAttribute("height"))