Compare commits

...

4 Commits

@ -96,6 +96,8 @@ namespace NLGUI
// match An+B rule to child index (1 based)
bool matchNth(sint childNr, sint a, sint b) const;
// match :lang(xx)
bool matchLang(const CHtmlElement &elm, const std::string &pseudo) const;
// parse nth-child string to 'a' and 'b' components
// :nth-child(odd)

@ -188,6 +188,12 @@ namespace NLGUI
void applyLineStyle(const std::string &value, CSSLineStyle *dest, const CSSLineStyle &currentStyle) const;
void applyPaddingWidth(const std::string &value, uint32 *dest, const uint32 currentPadding, uint32 fontSize) const;
// parse and replace var(--name, fallback) function
// return false if property should be ignored
bool cssFuncVar(std::string &func, const TStyle &styleRules, const std::set<std::string> &seenProperties) const;
// return false if property was not defined
bool lookupPropertyValue(const std::string &name, std::string &value, const TStyle &styleRules) const;
public:
void reset();

@ -84,6 +84,9 @@ namespace NLGUI
TStyle getPseudo(const std::string &key) const;
void setPseudo(const std::string &key, const TStyle &style);
// return lang property for css :lang() pseudo class
std::string getInheritedLanguage() const;
private:
// pseudo elements like ":before" and ":after"
std::map<std::string, TStyle> _Pseudo;

@ -47,7 +47,11 @@ namespace NLGUI
pos = elements[i].find_first_of(':');
if (pos != std::string::npos)
{
std::string key = trim(toLower(elements[i].substr(0, pos)));
// css properties are case-insensitive, but
// custom properties (--name; ...;) are case sensitive
std::string key = trim(elements[i].substr(0, pos));
if (key.size() < 2 || (key[0] != '-' && key[1] != '-'))
key = toLower(key);
std::string value = trim(elements[i].substr(pos+1));
styles.push_back(TStylePair(key, value));
}
@ -363,6 +367,14 @@ namespace NLGUI
continue;
}
if (sel[pos] == '*' && current.empty())
{
pos++;
current.Element = "*";
start = pos;
continue;
}
if(sel[pos] == '#')
{
pos++;

@ -235,6 +235,11 @@ namespace NLGUI
// 1st child should be '1' and not '0'
if (!matchNth(elm.childIndex+1, a, b)) return false;
}
else if (startsWith(PseudoClass[i], "lang("))
{
std::string lang = PseudoClass[i].substr(5, PseudoClass[i].size() - 6);
if (lang.empty() || !matchLang(elm, lang)) return false;
}
else
{
return false;
@ -324,6 +329,29 @@ namespace NLGUI
}
}
bool CCssSelector::matchLang(const CHtmlElement &elm, const std::string &pseudo) const
{
// TODO: does not support comma separated, or escaped/quoted/wildcard tags
std::string lang = toLower(elm.getInheritedLanguage());
if (lang.empty() || pseudo.empty())
return false;
// lang = 'en', pseudo = 'en-US'
if (lang.size() < pseudo.size())
return false;
std::string selector = toLower(pseudo);
bool selectorHasRegion = selector.find("-") != std::string::npos;
bool langHasRegion = lang.find("-") != std::string::npos;
// both are 'en', or 'en-US' type
if (langHasRegion == selectorHasRegion)
return lang == selector;
// lang = 'en-US', selector = 'en'
return lang[selector.size()] == '-' && startsWith(lang, selector);
}
std::string CCssSelector::toString() const
{
std::string ret;

@ -386,27 +386,35 @@ namespace NLGUI
// - normalize values
void CCssStyle::normalize(const TStyle &styleRules, CStyleParams &style, const CStyleParams &current) const
{
std::set<std::string> seenProperties;
TStyle::const_iterator it;
for (it=styleRules.begin(); it != styleRules.end(); ++it)
{
std::string value = it->second;
// replace possible custom properties, ignore property if var() fails
if (!cssFuncVar(value, styleRules, seenProperties))
continue;
// update local copy of applied style
style.StyleRules[it->first] = it->second;
style.StyleRules[it->first] = value;
if (it->first == "color")
{
if (it->second == "inherit")
if (value == "inherit")
{
style.TextColor = current.TextColor;
}
else
{
scanHTMLColor(it->second.c_str(), style.TextColor);
scanHTMLColor(value.c_str(), style.TextColor);
}
}
else
if (it->first == "font")
{
if (it->second == "inherit")
if (value == "inherit")
{
style.FontSize = current.FontSize;
style.FontFamily = current.FontFamily;
@ -417,42 +425,42 @@ namespace NLGUI
else
if (it->first == "font-size")
{
if (it->second == "inherit")
if (value == "inherit")
{
style.FontSize = current.FontSize;
}
else if (it->second == "x-small")
else if (value == "x-small")
{
style.FontSize = 10; // 62.5%
}
else if (it->second == "small")
else if (value == "small")
{
style.FontSize = 13; // 80%;
}
else if (it->second == "medium")
else if (value == "medium")
{
style.FontSize = 16; // 100%;
}
else if (it->second == "large")
else if (value == "large")
{
style.FontSize = 18; // 112.5%
}
else if (it->second == "x-large")
else if (value == "x-large")
{
style.FontSize = 24; // 150%
}
else if (it->second == "xx-large")
else if (value == "xx-large")
{
style.FontSize = 32; // 200%;
}
else if (it->second == "smaller")
else if (value == "smaller")
{
if (style.FontSize < 5)
style.FontSize = 3;
else
style.FontSize -= 2;
}
else if (it->second == "larger")
else if (value == "larger")
{
style.FontSize += 2;
}
@ -460,7 +468,7 @@ namespace NLGUI
{
float tmpf;
std::string unit;
if (getCssLength(tmpf, unit, it->second.c_str()))
if (getCssLength(tmpf, unit, value.c_str()))
{
if (unit == "rem")
style.FontSize = Root.FontSize * tmpf;
@ -479,16 +487,16 @@ namespace NLGUI
if (it->first == "background-repeat")
{
// old ryzom specific value
if (it->second == "1")
if (value == "1")
style.StyleRules[it->first] = "repeat";
}
else
if (it->first == "display")
{
if (it->second == "inherit")
if (value == "inherit")
style.DisplayBlock = current.DisplayBlock;
else
style.DisplayBlock = (it->second == "block" || it->second == "table");
style.DisplayBlock = (value == "block" || value == "table");
}
}
}
@ -1668,5 +1676,181 @@ namespace NLGUI
}
}
// ***************************************************************************
static void skipString(const std::string &value, std::string::size_type &pos)
{
char quote = value[pos];
while(pos < value.size() && value[pos] != quote)
{
if (value[pos] == '\\')
pos++;
pos++;
}
}
static void skipBlock(const std::string &value, std::string::size_type &pos, bool isString)
{
char openChar = value[pos];
char closeChar = value[pos];
if (openChar == '(') closeChar = ')';
else if (openChar == '[') closeChar = ']';
else if (openChar == '{') closeChar = '}';
pos++;
while(pos < value.size())
{
char c = value[pos];
if (c == '\\')
pos++;
else if (!isString && (c == '(' || c == '[' || c == '{'))
skipBlock(value, pos, false);
else if (c == closeChar)
break;
else if (c == '"' || c == '\'')
{
if (isString)
break;
skipBlock(value, pos, true);
}
pos++;
}
}
static void skipWhitespace(const std::string &value, std::string::size_type &pos)
{
while(pos < value.size() && (value[pos] == ' ' || value[pos] == '\t' || value[pos] == '\r'))
pos++;
}
// ***************************************************************************
bool CCssStyle::cssFuncVar(std::string &func, const TStyle &styleRules, const std::set<std::string> &seenProperties) const
{
// TODO: fails if var() is inside string, ie '--text: ".. var(...) .."';
// start of 'var('
std::string::size_type pos = func.find("var(");
if (pos == std::string::npos)
return true;
// simple test to make sure 'var' is not substring
if (pos > 0 && (func[pos-1] != '_') && ((func[pos-1] >= 'a' && func[pos-1] <= 'z') || (func[pos-1] >= 'A' && func[pos-1] <='Z')))
return true;
// find closing ')'
std::string::size_type funcStart = pos;
std::string::size_type funcEnd = funcStart + 3;
skipBlock(func, funcEnd, false);
pos += 4;
// ',' separator
std::string::size_type sep = func.find_first_of(",)", pos);
if (sep > funcEnd)
{
// unlikely
sep = funcEnd;
}
else if (sep + 1 == funcEnd)
{
// no whitespace between ',' and ')', ie 'var(--name,)'
return false;
}
// extract name
std::string name = func.substr(funcStart + 4, sep - pos);
if (seenProperties.count(name) > 0)
return false;
std::string value;
// if name is not defined or resolves to 'initial', use fallback
bool found = lookupPropertyValue(name, value, styleRules);
if (found) {
// check if substituted value has 'var()'
std::set<std::string> newSeen = seenProperties;
newSeen.insert(name);
found = cssFuncVar(value, styleRules, newSeen);
if (value == "initial")
found = false;
}
// --name failed and we have fallback
if (!found && func[sep] == ',')
{
sep++;
skipWhitespace(func, sep);
value = func.substr(sep, funcEnd - sep);
if (value.empty())
{
found = true;
}
else
{
// check if substituted fallback has 'var()'
std::set<std::string> newSeen = seenProperties;
newSeen.insert(name);
found = cssFuncVar(value, styleRules, newSeen);
if (value == "initial")
found = false;
}
}
// invalidate property as both name and fallback failed
if (!found)
return false;
// everything before 'var(' and after ')'
std::string result;
if (funcStart > 0)
result = trim(func.substr(0, funcStart)) + " ";
result += trim(value);
if ((funcEnd+1) < func.size())
result += " " + trim(func.substr(funcEnd+1));
// check replaced string for var()
std::set<std::string> newSeen = seenProperties;
newSeen.insert(name);
bool success = cssFuncVar(result, styleRules, newSeen);
if (result == "initial")
success = false;
func = result;
return success;
}
// ***************************************************************************
bool CCssStyle::lookupPropertyValue(const std::string &name, std::string &value, const TStyle &styleRules) const
{
bool success = true;
TStyle::const_iterator it = styleRules.find(name);
if (it != styleRules.end())
value = it->second;
else if (Current.hasStyle(name))
value = Current.getStyle(name);
else
success = false;
if (success && value != "inherit")
return true;
std::vector<CStyleParams>::const_reverse_iterator rit = _StyleStack.rbegin();
for(; rit != _StyleStack.rend(); ++rit)
{
if (rit->hasStyle(name))
{
value = rit->getStyle(name);
if (value != "inherit")
{
return true;
}
}
}
return false;
}
} // namespace

@ -122,6 +122,21 @@ namespace NLGUI
}
}
// ***************************************************************************
std::string CHtmlElement::getInheritedLanguage() const
{
const CHtmlElement *node = this;
while(node)
{
if (node->hasAttribute("lang"))
return node->getAttribute("lang");
node = node->parent;
}
return "";
}
// ***************************************************************************
std::string CHtmlElement::toString(bool tree, uint depth) const
{

Loading…
Cancel
Save