From 75f490d3944c717c12763e576e31f15dce72dff3 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Wed, 18 Aug 2021 14:19:36 +0300 Subject: [PATCH 01/24] Fix comparing same variable twice in CRingAccess --- ryzom/common/src/game_share/ring_access.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ryzom/common/src/game_share/ring_access.cpp b/ryzom/common/src/game_share/ring_access.cpp index 1d178efad..5c2f4d6d5 100644 --- a/ryzom/common/src/game_share/ring_access.cpp +++ b/ryzom/common/src/game_share/ring_access.cpp @@ -156,7 +156,7 @@ void CRingAccess::init() CXMLAutoPtr sheetClientPtr( (const char*) xmlGetProp(entityAccess, (xmlChar*) "sheetClient") ); CXMLAutoPtr sheetPtr( (const char*) xmlGetProp(entityAccess, (xmlChar*) "sheetServer") ); - if (!namePtr.getDatas()|| !packagePtr.getDatas() || !sheetPtr.getDatas() || !sheetPtr.getDatas()) + if (!namePtr.getDatas()|| !packagePtr.getDatas() || !sheetClientPtr.getDatas() || !sheetPtr.getDatas()) { nlerror( "Syntax error in %s", pathFileName.c_str()); return; From 95716b65ca39c016d7cbfdc221a0572766ae620d Mon Sep 17 00:00:00 2001 From: Nimetu Date: Wed, 18 Aug 2021 14:21:43 +0300 Subject: [PATCH 02/24] Fix comparing x twice in Direct3d setupScissor --- nel/src/3d/driver/direct3d/driver_direct3d_matrix.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/nel/src/3d/driver/direct3d/driver_direct3d_matrix.cpp b/nel/src/3d/driver/direct3d/driver_direct3d_matrix.cpp index 6e7520fb7..988f9c9eb 100644 --- a/nel/src/3d/driver/direct3d/driver_direct3d_matrix.cpp +++ b/nel/src/3d/driver/direct3d/driver_direct3d_matrix.cpp @@ -283,18 +283,16 @@ void CDriverD3D::setupScissor (const class CScissor& scissor) // Get viewport _ScissorTouched = false; float x= scissor.X; + float y= scissor.Y; float width= scissor.Width; float height= scissor.Height; - if(x==0 && x==0 && width==1 && height==1) + if(x==0 && y==0 && width==1 && height==1) { setRenderState (D3DRS_SCISSORTESTENABLE, FALSE); } else { - - float y= scissor.Y; - if (_HWnd) { // Get the render target size From 093702a70425c29082558ae2e65e3c88733e7131 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Thu, 2 Sep 2021 23:56:12 +0300 Subject: [PATCH 03/24] Fix web link in item customtext being visible --- .../src/interface_v3/action_handler_help.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ryzom/client/src/interface_v3/action_handler_help.cpp b/ryzom/client/src/interface_v3/action_handler_help.cpp index 3c5f47df6..4417966bb 100644 --- a/ryzom/client/src/interface_v3/action_handler_help.cpp +++ b/ryzom/client/src/interface_v3/action_handler_help.cpp @@ -1907,9 +1907,17 @@ void getItemText (CDBCtrlSheet *item, string &itemText, const CItemSheet*pIS) const CClientItemInfo &itemInfo = getInventory().getItemInfo(getInventory().getItemSlotId(item) ); if (!itemInfo.CustomText.empty()) { - strFindReplace(itemText, "%custom_text", "\n@{FFFF}" + itemInfo.CustomText.toUtf8() + "\n"); - string itemMFC = CI18N::get("uiItemTextMessageFromCrafter"); - strFindReplace(itemText, "%mfc", itemMFC); + std::string text = itemInfo.CustomText.toUtf8(); + if (text.size() > 3 && text[0]=='@' && ((text[1]=='W' && text[2]=='E' && text[3]=='B') || (text[1]=='L' && text[2]=='U' && text[3]=='A'))) + { + strFindReplace(itemText, "%custom_text", string() ); + } + else + { + strFindReplace(itemText, "%custom_text", "\n@{FFFF}" + itemInfo.CustomText.toUtf8() + "\n"); + string itemMFC = CI18N::get("uiItemTextMessageFromCrafter"); + strFindReplace(itemText, "%mfc", itemMFC); + } } else strFindReplace(itemText, "%custom_text", string() ); From cade609a3ad2d2ee0d7012fb748847bc29ff4e98 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Thu, 2 Sep 2021 23:54:32 +0300 Subject: [PATCH 04/24] Fix textarea not preserving linebreaks, keep tab char --- nel/src/gui/group_html.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nel/src/gui/group_html.cpp b/nel/src/gui/group_html.cpp index 6eeecfdb9..75d7eb64c 100644 --- a/nel/src/gui/group_html.cpp +++ b/nel/src/gui/group_html.cpp @@ -6668,8 +6668,7 @@ namespace NLGUI _TextAreaTemplate = !templateName.empty() ? templateName : DefaultFormTextAreaGroup; - std::string content = strFindReplaceAll(elm.serializeChilds(), std::string("\t"), std::string(" ")); - content = strFindReplaceAll(content, std::string("\n"), std::string(" ")); + std::string content = strFindReplaceAll(elm.serializeChilds(), std::string("\r"), std::string("")); CInterfaceGroup *textArea = addTextArea (_TextAreaTemplate, _TextAreaName.c_str (), _TextAreaRow, _TextAreaCols, true, content, _TextAreaMaxLength); if (textArea) From 565932c18d9312ea92f53b4f14809e97709d174e Mon Sep 17 00:00:00 2001 From: Nimetu Date: Tue, 1 Jun 2021 12:46:13 +0300 Subject: [PATCH 05/24] Expand css margin shorthand --- nel/include/nel/gui/css_style.h | 5 ++++ nel/src/gui/css_style.cpp | 53 +++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/nel/include/nel/gui/css_style.h b/nel/include/nel/gui/css_style.h index 7a1d11ee5..604fac431 100644 --- a/nel/include/nel/gui/css_style.h +++ b/nel/include/nel/gui/css_style.h @@ -72,6 +72,7 @@ namespace NLGUI // background BackgroundColor=NLMISC::CRGBA::Black; BackgroundColorOver=NLMISC::CRGBA::Black; + MarginTop = MarginRight = MarginBottom = MarginLeft = 0; PaddingTop = PaddingRight = PaddingBottom = PaddingLeft = 0; } @@ -106,6 +107,7 @@ namespace NLGUI NLMISC::CRGBA BorderTopColor, BorderRightColor, BorderBottomColor, BorderLeftColor; NLMISC::CRGBA BackgroundColor; NLMISC::CRGBA BackgroundColorOver; + uint32 MarginTop, MarginRight, MarginBottom, MarginLeft; uint32 PaddingTop, PaddingRight, PaddingBottom, PaddingLeft; std::string WhiteSpace; @@ -177,6 +179,7 @@ namespace NLGUI // parse 'padding' into 'padding-top', 'padding-left', etc void expandPaddingShorthand(const std::string &value, TStyle &style) const; + void expandMarginShorthand(const std::string &value, TStyle &style) const; // expand shorthand rule, ie "border", into longhand names, ie "border-top-width" // if shorthand is present in style, then its removed @@ -187,6 +190,7 @@ namespace NLGUI void applyBorderColor(const std::string &value, NLMISC::CRGBA *dest, const NLMISC::CRGBA ¤tColor, const NLMISC::CRGBA &textColor) const; void applyLineStyle(const std::string &value, CSSLineStyle *dest, const CSSLineStyle ¤tStyle) const; void applyPaddingWidth(const std::string &value, uint32 *dest, const uint32 currentPadding, uint32 fontSize) const; + void applyMarginWidth(const std::string &value, uint32 *dest, const uint32 current, uint32 fontSize) const; // parse and replace var(--name, fallback) function // return false if property should be ignored @@ -224,6 +228,7 @@ namespace NLGUI Current.BorderTopWidth = Current.BorderRightWidth = Current.BorderBottomWidth = Current.BorderLeftWidth = CSS_LINE_WIDTH_MEDIUM; Current.BorderTopStyle = Current.BorderRightStyle = Current.BorderBottomStyle = Current.BorderLeftStyle = CSS_LINE_STYLE_NONE; Current.BorderTopColor = Current.BorderRightColor = Current.BorderBottomColor = Current.BorderLeftColor = Current.TextColor; + Current.MarginTop = Current.MarginRight = Current.MarginBottom = Current.MarginLeft = 0; Current.PaddingTop = Current.PaddingRight = Current.PaddingBottom = Current.PaddingLeft = 0; Current.StyleRules.clear(); diff --git a/nel/src/gui/css_style.cpp b/nel/src/gui/css_style.cpp index e4a9751fa..c2b4a6ff1 100644 --- a/nel/src/gui/css_style.cpp +++ b/nel/src/gui/css_style.cpp @@ -606,6 +606,39 @@ namespace NLGUI } } + void CCssStyle::applyMarginWidth(const std::string &value, uint32 *dest, const uint32 current, uint32 fontSize) const + { + if (!dest) return; + + if (value == "inherit") + { + *dest = current; + return; + } + else if (value == "auto") + { + // TODO: requires content width; + *dest = 0; + return; + } + + float tmpf; + std::string unit; + if (getCssLength(tmpf, unit, value.c_str())) + { + if (unit == "rem") + *dest = fontSize * tmpf; + else if (unit == "em") + *dest = fontSize * tmpf; + else if (unit == "pt") + *dest = tmpf / 0.75f; + else if (unit == "%") + *dest = 0; // TODO: requires content width, must remember 'unit' type + else + *dest = tmpf; + } + } + // apply style rules void CCssStyle::apply(CStyleParams &style, const CStyleParams ¤t) const { @@ -625,6 +658,10 @@ namespace NLGUI else if (it->first == "border-left-width") applyBorderWidth(it->second, &style.BorderLeftWidth, current.BorderLeftWidth, current.FontSize); else if (it->first == "border-left-color") applyBorderColor(it->second, &style.BorderLeftColor, current.BorderLeftColor, current.TextColor); else if (it->first == "border-left-style") applyLineStyle(it->second, &style.BorderLeftStyle, current.BorderLeftStyle); + else if (it->first == "margin-top") applyMarginWidth(it->second, &style.MarginTop, current.MarginTop, current.FontSize); + else if (it->first == "margin-right") applyMarginWidth(it->second, &style.MarginRight, current.MarginRight, current.FontSize); + else if (it->first == "margin-bottom") applyMarginWidth(it->second, &style.MarginBottom, current.MarginBottom, current.FontSize); + else if (it->first == "margin-left") applyMarginWidth(it->second, &style.MarginLeft, current.MarginLeft, current.FontSize); else if (it->first == "padding-top") applyPaddingWidth(it->second, &style.PaddingTop, current.PaddingTop, current.FontSize); else if (it->first == "padding-right") applyPaddingWidth(it->second, &style.PaddingRight, current.PaddingRight, current.FontSize); else if (it->first == "padding-bottom") applyPaddingWidth(it->second, &style.PaddingBottom, current.PaddingBottom, current.FontSize); @@ -1556,6 +1593,22 @@ namespace NLGUI style["padding-left"] = parts[l]; } + // *************************************************************************** + void CCssStyle::expandMarginShorthand(const std::string &value, TStyle &style) const + { + std::vector parts; + splitParams(toLowerAscii(value), ' ', parts); + + uint8 t, r, b, l; + if (!getShorthandIndices(parts.size(), t, r, b, l)) + return; + + style["margin-top"] = parts[t]; + style["margin-right"] = parts[r]; + style["margin-bottom"] = parts[b]; + style["margin-left"] = parts[l]; + } + // *************************************************************************** void CCssStyle::expandShorthand(const std::string &prop, const std::string &value, TStyle &style) const { From a040f7a2a8427113af1b196c44b784a9a9760811 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Thu, 10 Jun 2021 12:23:45 +0300 Subject: [PATCH 06/24] CSS background color should not inherit its value --- nel/include/nel/gui/css_style.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nel/include/nel/gui/css_style.h b/nel/include/nel/gui/css_style.h index 604fac431..664d13c64 100644 --- a/nel/include/nel/gui/css_style.h +++ b/nel/include/nel/gui/css_style.h @@ -228,6 +228,10 @@ namespace NLGUI Current.BorderTopWidth = Current.BorderRightWidth = Current.BorderBottomWidth = Current.BorderLeftWidth = CSS_LINE_WIDTH_MEDIUM; Current.BorderTopStyle = Current.BorderRightStyle = Current.BorderBottomStyle = Current.BorderLeftStyle = CSS_LINE_STYLE_NONE; Current.BorderTopColor = Current.BorderRightColor = Current.BorderBottomColor = Current.BorderLeftColor = Current.TextColor; + + Current.BackgroundColor = NLMISC::CRGBA::Transparent; + Current.BackgroundColorOver = NLMISC::CRGBA::Transparent; + Current.MarginTop = Current.MarginRight = Current.MarginBottom = Current.MarginLeft = 0; Current.PaddingTop = Current.PaddingRight = Current.PaddingBottom = Current.PaddingLeft = 0; From 63f1fb73dd2b10ad87987f7b866b6dc1869c1be2 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Wed, 2 Jun 2021 15:03:08 +0300 Subject: [PATCH 07/24] Fix table background color and image draw order --- nel/src/gui/group_table.cpp | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/nel/src/gui/group_table.cpp b/nel/src/gui/group_table.cpp index 2144d0020..6b1d320aa 100644 --- a/nel/src/gui/group_table.cpp +++ b/nel/src/gui/group_table.cpp @@ -514,7 +514,24 @@ namespace NLGUI { CViewRenderer &rVR = *CViewRenderer::getInstance(); + // flush draw queue to force correct draw order for color+image + rVR.flush(); + bool flush = false; + + if (BgColor.A > 0) + { + CRGBA finalColor = BgColor; + if (_ModulateGlobalColor) + finalColor.modulateFromColor (finalColor, CWidgetManager::getInstance()->getGlobalColor()); + finalColor.A = (uint8) (((uint16) CurrentAlpha * (uint16) finalColor.A) >> 8); + + if (finalColor.A > 0) + rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal, _WReal, _HReal, 0, false, rVR.getBlankTextureId(), finalColor); + + flush = true; + } + if (CurrentAlpha > 0 && !_TextureId.empty()) { CRGBA col = CRGBA::White; @@ -540,19 +557,6 @@ namespace NLGUI flush = true; } - if (BgColor.A > 0) - { - CRGBA finalColor = BgColor; - if (_ModulateGlobalColor) - finalColor.modulateFromColor (finalColor, CWidgetManager::getInstance()->getGlobalColor()); - finalColor.A = (uint8) (((uint16) CurrentAlpha * (uint16) finalColor.A) >> 8); - - if (finalColor.A > 0) - rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal, _WReal, _HReal, 0, false, rVR.getBlankTextureId(), finalColor); - - flush = true; - } - if (flush) rVR.flush(); } @@ -1502,6 +1506,10 @@ namespace NLGUI bool flush = false; CViewRenderer &rVR = *CViewRenderer::getInstance(); + // flush draw queue to force correct draw order for color+image + if (BgColor.A >0 || !_TextureId.empty()) + rVR.flush(); + if (BgColor.A > 0) { CRGBA finalColor = BgColor; @@ -1539,7 +1547,6 @@ namespace NLGUI flush = true; } - // flush background color and image if (flush) rVR.flush(); From 2133f6d2b767de0e846120d5e0a8221d295cec21 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Thu, 3 Jun 2021 13:49:44 +0300 Subject: [PATCH 08/24] Fixed bad type for _RenderLayer --- nel/include/nel/gui/css_border_renderer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nel/include/nel/gui/css_border_renderer.h b/nel/include/nel/gui/css_border_renderer.h index 1c5db47d4..3e6c3bdc5 100644 --- a/nel/include/nel/gui/css_border_renderer.h +++ b/nel/include/nel/gui/css_border_renderer.h @@ -43,7 +43,7 @@ namespace NLGUI NLMISC::CQuadUV _QuadB; NLMISC::CQuadUV _QuadL; - uint8 _RenderLayer; + sint8 _RenderLayer; bool _ModulateGlobalColor; // if true, then updateCoords() is called from draw() From 234465387b1747a5e4be5196995947ea0ed9beff Mon Sep 17 00:00:00 2001 From: Nimetu Date: Sat, 12 Jun 2021 00:11:38 +0300 Subject: [PATCH 09/24] Fix getCssLength returning false for '0' --- nel/src/gui/libwww.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/nel/src/gui/libwww.cpp b/nel/src/gui/libwww.cpp index 362dce8e2..200e7baf1 100644 --- a/nel/src/gui/libwww.cpp +++ b/nel/src/gui/libwww.cpp @@ -215,6 +215,13 @@ namespace NLGUI return false; } + if (len == 1 && str[0] == '0') + { + value = 0; + unit.clear(); + return true; + } + while(pos < len) { bool isNumeric = (str[pos] >= '0' && str[pos] <= '9') From b2ec03d1a124486530a13b4596e4a07d5f1471b3 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Sat, 3 Jul 2021 23:56:43 +0300 Subject: [PATCH 10/24] Fix background shorthand for -position and -clip --- nel/src/gui/css_style.cpp | 44 ++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/nel/src/gui/css_style.cpp b/nel/src/gui/css_style.cpp index c2b4a6ff1..fa80e7b52 100644 --- a/nel/src/gui/css_style.cpp +++ b/nel/src/gui/css_style.cpp @@ -1075,7 +1075,6 @@ namespace NLGUI // first loop -> true // second loop -> false && break loop = !loop; - if (next < parts.size()) { val = toLowerAscii(parts[next]); @@ -1086,30 +1085,57 @@ namespace NLGUI // consume 'center' next++; } - else if (val == "left" || val == "right") + else if ((bgPositionX.empty() || bgPositionX == "center") && (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()); + bgPositionX += " " + parts[next]; // consume css length next++; } } - else if (val == "top" || val == "bottom") + else if ((bgPositionY.empty() || bgPositionY == "center") && (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()); + bgPositionY += " " + parts[next]; // consume css length next++; } } + else if (getCssLength(fval, unit, parts[next])) + { + // override X only on first loop + if (next == index) + { + bgPositionX = parts[next]; + } + else if (bgPositionY.empty()) + { + bgPositionY = parts[next]; + } + else + { + // invalid + bgPositionX.clear(); + bgPositionY.clear(); + break; + } + next++; + } + else + { + // invalid value + bgPositionX.clear(); + bgPositionY.clear(); + break; + } } } while (loop); @@ -1152,7 +1178,7 @@ namespace NLGUI if (val == "auto") h = v = "auto"; else - h = v = toString("%.0f%s", fval, unit.c_str()); + h = v = val; next++; if (next < parts.size()) @@ -1161,7 +1187,7 @@ namespace NLGUI if (val == "auto") v = "auto"; else if (getCssLength(fval, unit, val)) - v = toString("%.0f%s", fval, unit.c_str()); + v = val; else next--; // not size token } @@ -1244,7 +1270,10 @@ namespace NLGUI // first time background-origin is set, also set background-clip if (!bgClipFound) + { bgClipValue = val; + bgClipFound = true; + } } } else if (props[i] == "background-clip") @@ -1294,6 +1323,7 @@ namespace NLGUI { if (props[i] == "background-position") { + style["background-position"] = bgPositionX + " " + bgPositionY; style["background-position-x"] = bgPositionX; style["background-position-y"] = bgPositionY; } From 5d4a04169bdab9978e9a1bb0060d13a81bff66d7 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Sun, 4 Jul 2021 16:56:54 +0300 Subject: [PATCH 11/24] Update getCssLength for new types, enable reading negative values. --- nel/include/nel/gui/libwww.h | 2 +- nel/src/gui/libwww.cpp | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/nel/include/nel/gui/libwww.h b/nel/include/nel/gui/libwww.h index df9c20cd8..a3d4c25fd 100644 --- a/nel/include/nel/gui/libwww.h +++ b/nel/include/nel/gui/libwww.h @@ -184,7 +184,7 @@ namespace NLGUI // *************************************************************************** // Read a CSS length value, return true if one of supported units '%, rem, em, px, pt' // On failure: 'value' and 'unit' values are undefined - bool getCssLength (float &value, std::string &unit, const std::string &str); + bool getCssLength (float &value, std::string &unit, const std::string &str, bool neg = false); // Read a width HTML parameter. "100" or "100%". Returns true if percent (0 ~ 1) else false bool getPercentage (sint32 &width, float &percent, const char *str); diff --git a/nel/src/gui/libwww.cpp b/nel/src/gui/libwww.cpp index 200e7baf1..02217a541 100644 --- a/nel/src/gui/libwww.cpp +++ b/nel/src/gui/libwww.cpp @@ -206,11 +206,15 @@ namespace NLGUI // *************************************************************************** // *************************************************************************** - bool getCssLength (float &value, std::string &unit, const std::string &str) + bool getCssLength (float &value, std::string &unit, const std::string &str, bool neg) { + static const std::set knownUnits = { + "%", "rem", "em", "px", "pt", "vw", "vh", "vi", "vb", "vmin", "vmax" + }; + std::string::size_type pos = 0; std::string::size_type len = str.size(); - if (len == 1 && str[0] == '.') + if (len == 0) { return false; } @@ -222,6 +226,12 @@ namespace NLGUI return true; } + // +100px; -100px + if (str[0] == '+') + pos++; + else if (neg && str[0] == '-') + pos++; + while(pos < len) { bool isNumeric = (str[pos] >= '0' && str[pos] <= '9') @@ -236,7 +246,7 @@ namespace NLGUI } unit = toLowerAscii(str.substr(pos)); - if (unit == "%" || unit == "rem" || unit == "em" || unit == "px" || unit == "pt") + if (knownUnits.count(unit)) { std::string tmpstr = str.substr(0, pos); return fromString(tmpstr, value); From 2e816ddf6ed89bc4dc3f58aa47fd614ed1b34654 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Wed, 1 Sep 2021 18:10:23 +0300 Subject: [PATCH 12/24] Add "transparent" to html color list --- nel/src/gui/libwww.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nel/src/gui/libwww.cpp b/nel/src/gui/libwww.cpp index 02217a541..5d739863d 100644 --- a/nel/src/gui/libwww.cpp +++ b/nel/src/gui/libwww.cpp @@ -628,6 +628,12 @@ namespace NLGUI return false; } + if (nlstricmp(src, "transparent") == 0) + { + dest = CRGBA::Transparent; + return true; + } + { // slow but should suffice for now for(uint k = 0; k < sizeofarray(htmlColorNameToRGBA); ++k) From 487f9f060e79019a898ba445d17ceff56510a666 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Mon, 23 Aug 2021 11:45:06 +0300 Subject: [PATCH 13/24] Fix css rule declaration splitting --- nel/src/gui/css_parser.cpp | 62 +++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/nel/src/gui/css_parser.cpp b/nel/src/gui/css_parser.cpp index d4980784a..dcc82dfc6 100644 --- a/nel/src/gui/css_parser.cpp +++ b/nel/src/gui/css_parser.cpp @@ -41,25 +41,57 @@ namespace NLGUI TStyleVec CCssParser::parseDecls(const std::string &styleString) { TStyleVec styles; - std::vector elements; - NLMISC::splitString(styleString, ";", elements); - - for(uint i = 0; i < elements.size(); ++i) + size_t pos = 0; + size_t end = styleString.size(); + while(pos < end) { - std::string::size_type pos; - pos = elements[i].find_first_of(':'); - if (pos != std::string::npos) + size_t sep = styleString.find(':', pos); + if (sep == std::string::npos) + break; + + size_t keyIndex = pos; + size_t keyLength = sep - pos; + + sep++; + pos = sep; + while(sep < end) { - // 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 = toLowerAscii(key); - std::string value = trim(elements[i].substr(pos+1)); - styles.push_back(TStylePair(key, value)); + sep = styleString.find_first_of(";'\"(", sep); + if (sep == std::string::npos || styleString[sep] == ';') + break; + + if (styleString[sep] == '\'' || styleString[sep] == '"') + { + char ch = styleString[sep]; + // skip open quote + sep++; + while(sep < end && styleString[sep] != ch) + { + if (styleString[sep] == '\\') + sep++; + sep++; + } + // skip close quote + sep++; + } + else if (styleString[sep] == '(') + { + while(sep < end && styleString[sep] != ')') + { + sep++; + } + // skip close parenthesis + sep++; + } } - } + styles.push_back(TStylePair(trim(styleString.substr(keyIndex, keyLength)), trim(styleString.substr(pos, sep - pos)))); + + if (sep >= end) + break; + + pos = sep + 1; + } return styles; } From 65edd9f95d0e2bca794b2e0a02fe9f0d58fe5d26 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Tue, 15 Jun 2021 12:11:14 +0300 Subject: [PATCH 14/24] Draw double/groove/ridge border styles --- nel/include/nel/gui/css_border_renderer.h | 30 +- nel/include/nel/gui/css_types.h | 5 + nel/src/gui/css_border_renderer.cpp | 373 +++++++++++++--------- nel/src/gui/css_style.cpp | 14 +- 4 files changed, 254 insertions(+), 168 deletions(-) diff --git a/nel/include/nel/gui/css_border_renderer.h b/nel/include/nel/gui/css_border_renderer.h index 3e6c3bdc5..b70f1c169 100644 --- a/nel/include/nel/gui/css_border_renderer.h +++ b/nel/include/nel/gui/css_border_renderer.h @@ -35,22 +35,24 @@ namespace NLGUI { private: // parent element screen coordinates - sint32 _XReal, _YReal; - sint32 _WReal, _HReal; + sint32 m_XReal, m_YReal; + sint32 m_WReal, m_HReal; - NLMISC::CQuadUV _QuadT; - NLMISC::CQuadUV _QuadR; - NLMISC::CQuadUV _QuadB; - NLMISC::CQuadUV _QuadL; + struct SDrawBorder + { + NLMISC::CQuadUV Quad; + NLMISC::CRGBA Color; + }; + std::vector m_DrawBorders; - sint8 _RenderLayer; - bool _ModulateGlobalColor; + sint8 m_RenderLayer; + bool m_ModulateGlobalColor; // if true, then updateCoords() is called from draw() - bool _Dirty; + bool m_Dirty; // if true, then at least one border is set - bool _Border; - bool _BorderTop, _BorderRight, _BorderBottom, _BorderLeft; + bool m_Border; + bool m_BorderTop, m_BorderRight, m_BorderBottom, m_BorderLeft; public: uint32 TopWidth, RightWidth, BottomWidth, LeftWidth; @@ -73,8 +75,8 @@ namespace NLGUI void setColor(const NLMISC::CRGBA &top, const NLMISC::CRGBA &right, const NLMISC::CRGBA &bottom, const NLMISC::CRGBA &left); void updateCoords(); - void invalidateCoords() { _Dirty = _Border = true; } - void invalidateContent() { _Dirty = _Border = true; }; + void invalidateCoords() { m_Dirty = m_Border = true; } + void invalidateContent() { m_Dirty = m_Border = true; }; uint32 getTopWidth() const; uint32 getRightWidth() const; @@ -84,6 +86,8 @@ namespace NLGUI uint32 getLeftRightWidth() const; uint32 getTopBottomWidth() const; + bool hasInnerShape(CSSLineStyle style) const; + void draw(); }; // CSSBorderRenderer diff --git a/nel/include/nel/gui/css_types.h b/nel/include/nel/gui/css_types.h index fd526858e..dd80e308f 100644 --- a/nel/include/nel/gui/css_types.h +++ b/nel/include/nel/gui/css_types.h @@ -30,7 +30,12 @@ namespace NLGUI enum CSSLineStyle { CSS_LINE_STYLE_NONE = 0, CSS_LINE_STYLE_HIDDEN, + CSS_LINE_STYLE_DOTTED, + CSS_LINE_STYLE_DASHED, CSS_LINE_STYLE_SOLID, + CSS_LINE_STYLE_DOUBLE, + CSS_LINE_STYLE_GROOVE, + CSS_LINE_STYLE_RIDGE, CSS_LINE_STYLE_INSET, CSS_LINE_STYLE_OUTSET }; diff --git a/nel/src/gui/css_border_renderer.cpp b/nel/src/gui/css_border_renderer.cpp index e41d11857..aa44073f4 100644 --- a/nel/src/gui/css_border_renderer.cpp +++ b/nel/src/gui/css_border_renderer.cpp @@ -29,8 +29,6 @@ using namespace NLMISC; namespace NLGUI { - - // ---------------------------------------------------------------------------- CSSBorderRenderer::CSSBorderRenderer() { @@ -39,56 +37,39 @@ namespace NLGUI TopStyle = RightStyle = BottomStyle = LeftStyle = CSS_LINE_STYLE_SOLID; CurrentAlpha = 255; - _RenderLayer = 0; - _ModulateGlobalColor = false; - - _Border = true; - _Dirty = true; - _BorderTop = _BorderRight = _BorderBottom = _BorderLeft = false; - - // - _QuadT.Uv0.set(0.f, 0.f); - _QuadT.Uv1.set(0.f, 0.f); - _QuadT.Uv2.set(1.f, 1.f); - _QuadT.Uv3.set(0.f, 1.f); - // - _QuadR.Uv0.set(0.f, 0.f); - _QuadR.Uv1.set(0.f, 0.f); - _QuadR.Uv2.set(1.f, 1.f); - _QuadR.Uv3.set(0.f, 1.f); - // - _QuadB.Uv0.set(0.f, 0.f); - _QuadB.Uv1.set(0.f, 0.f); - _QuadB.Uv2.set(1.f, 1.f); - _QuadB.Uv3.set(0.f, 1.f); - // - _QuadL.Uv0.set(0.f, 0.f); - _QuadL.Uv1.set(0.f, 0.f); - _QuadL.Uv2.set(1.f, 1.f); - _QuadL.Uv3.set(0.f, 1.f); + m_RenderLayer = 0; + m_ModulateGlobalColor = false; + + m_Border = true; + m_Dirty = true; + m_BorderTop = m_BorderRight = m_BorderBottom = m_BorderLeft = false; + m_XReal = 0; + m_YReal = 0; + m_WReal = 0; + m_HReal = 0; } // ---------------------------------------------------------------------------- void CSSBorderRenderer::setRenderLayer(sint layer) { - _RenderLayer = layer; + m_RenderLayer = layer; } // ---------------------------------------------------------------------------- void CSSBorderRenderer::setModulateGlobalColor(bool s) { - _ModulateGlobalColor = s; + m_ModulateGlobalColor = s; } // ---------------------------------------------------------------------------- void CSSBorderRenderer::setRect(sint32 x, sint32 y, sint32 w, sint32 h) { - _XReal = x; - _YReal = y; - _WReal = w; - _HReal = h; + m_XReal = x; + m_YReal = y; + m_WReal = w; + m_HReal = h; - _Dirty = _Border = (w > 0 && h > 0); + m_Dirty = m_Border = (w > 0 && h > 0); } // ---------------------------------------------------------------------------- @@ -99,7 +80,7 @@ namespace NLGUI BottomWidth = bottom; LeftWidth = left; - _Dirty = _Border = (top > 0 || right > 0 || bottom > 0 || left > 0); + m_Dirty = m_Border = (top > 0 || right > 0 || bottom > 0 || left > 0); } // ---------------------------------------------------------------------------- @@ -110,7 +91,7 @@ namespace NLGUI BottomStyle = bottom; LeftStyle = left; - _Dirty = _Border = true; + m_Dirty = m_Border = true; } // ---------------------------------------------------------------------------- @@ -121,7 +102,7 @@ namespace NLGUI BottomColor = bottom; LeftColor = left; - _Dirty = true; + m_Dirty = true; } // ---------------------------------------------------------------------------- @@ -173,146 +154,232 @@ namespace NLGUI } // ---------------------------------------------------------------------------- - void CSSBorderRenderer::updateCoords() + bool CSSBorderRenderer::hasInnerShape(CSSLineStyle style) const { - _Dirty = false; - if (!_Border) return; - - sint dTop = getTopWidth(); _BorderTop = dTop > 0; - sint dRight = getRightWidth(); _BorderRight = dRight > 0; - sint dBottom = getBottomWidth(); _BorderBottom = dBottom > 0; - sint dLeft = getLeftWidth(); _BorderLeft = dLeft > 0; - - _Border = _BorderTop || _BorderRight || _BorderBottom || _BorderLeft; - if (!_Border) return; + return style == CSS_LINE_STYLE_DOUBLE || + style == CSS_LINE_STYLE_GROOVE || + style == CSS_LINE_STYLE_RIDGE; + } - if (_BorderTop) + // ---------------------------------------------------------------------------- + void CSSBorderRenderer::updateCoords() + { + m_Dirty = false; + m_DrawBorders.clear(); + if (!m_Border) return; + + sint dTop = getTopWidth(); m_BorderTop = dTop > 0; + sint dRight = getRightWidth(); m_BorderRight = dRight > 0; + sint dBottom = getBottomWidth(); m_BorderBottom = dBottom > 0; + sint dLeft = getLeftWidth(); m_BorderLeft = dLeft > 0; + + m_Border = m_BorderTop || m_BorderRight || m_BorderBottom || m_BorderLeft; + if (!m_Border) return; + + sint xTop = m_YReal + m_HReal; + sint xRight = m_XReal + m_WReal; + + sint bLeft = m_XReal + dLeft; + sint bRight = xRight - dRight; + sint bTop = xTop - dTop; + sint bBottom = m_YReal + dBottom; + + SDrawBorder shape; + shape.Quad.Uv0.set(0.f, 0.f); + shape.Quad.Uv1.set(1.f, 0.f); + shape.Quad.Uv2.set(1.f, 1.f); + shape.Quad.Uv3.set(0.f, 1.f); + + // V3 - top-left + // V2 - top-right + // V1 - bottom-right + // V0 - bottom-left + if (m_BorderTop) { - // top-left - _QuadT.V3.x = _XReal; - _QuadT.V3.y = _YReal + _HReal; - // top-right - _QuadT.V2.x = _XReal + _WReal; - _QuadT.V2.y = _YReal + _HReal; - // bottom-right - _QuadT.V1.x = _XReal + _WReal - dRight; - _QuadT.V1.y = _YReal + _HReal - dTop; - // bottom-left - _QuadT.V0.x = _XReal + dLeft; - _QuadT.V0.y = _YReal + _HReal - dTop; + if (TopStyle == CSS_LINE_STYLE_INSET || TopStyle == CSS_LINE_STYLE_GROOVE) + shape.Color = blend(TopColor, CRGBA::Black, 0.5f); + else + shape.Color = TopColor; + + shape.Quad.V3.x = m_XReal; shape.Quad.V3.y = xTop; + shape.Quad.V2.x = xRight; shape.Quad.V2.y = xTop; + shape.Quad.V1.x = bRight; shape.Quad.V1.y = bTop; + shape.Quad.V0.x = bLeft; shape.Quad.V0.y = bTop; + m_DrawBorders.push_back(shape); + + if (hasInnerShape(TopStyle)) + { + float iLeft, iTop, iRight; + if (TopStyle == CSS_LINE_STYLE_DOUBLE) + { + iLeft = 2*dLeft / 3.f; + iTop = 2*dBottom / 3.f; + iRight = 2*dRight / 3.f; + } else { + iLeft = dLeft / 2.f; + iTop = dTop / 2.f; + iRight = dRight / 2.f; + } + + if (TopStyle == CSS_LINE_STYLE_RIDGE) + shape.Color = blend(TopColor, CRGBA::Black, 0.5f); + else + shape.Color = TopColor; + + // create inner border shape and remove overlapping from outer shape + m_DrawBorders.back().Quad.V0.x -= iLeft; m_DrawBorders.back().Quad.V0.y += iTop; + m_DrawBorders.back().Quad.V1.x += iRight; m_DrawBorders.back().Quad.V1.y += iTop; + shape.Quad.V3.x += iLeft; shape.Quad.V3.y -= iTop; + shape.Quad.V2.x -= iRight; shape.Quad.V2.y -= iTop; + m_DrawBorders.push_back(shape); + } } - if (_BorderRight) + if (m_BorderBottom) { - // top-left - _QuadR.V3.x = _XReal + _WReal - dRight; - _QuadR.V3.y = _YReal + _HReal - dTop; - // top-right - _QuadR.V2.x = _XReal + _WReal; - _QuadR.V2.y = _YReal + _HReal; - // bottom-right - _QuadR.V1.x = _XReal + _WReal; - _QuadR.V1.y = _YReal; - // bottom-left - _QuadR.V0.x = _XReal + _WReal - dRight; - _QuadR.V0.y = _YReal + dBottom; + if (BottomStyle == CSS_LINE_STYLE_OUTSET || BottomStyle == CSS_LINE_STYLE_RIDGE) + shape.Color = blend(BottomColor, CRGBA::Black, 0.5f); + else + shape.Color = BottomColor; + + shape.Quad.V3.x = bLeft; shape.Quad.V3.y = bBottom; + shape.Quad.V2.x = bRight; shape.Quad.V2.y = bBottom; + shape.Quad.V1.x = xRight; shape.Quad.V1.y = m_YReal; + shape.Quad.V0.x = m_XReal; shape.Quad.V0.y = m_YReal; + m_DrawBorders.push_back(shape); + + if (hasInnerShape(BottomStyle)) + { + float iLeft, iBottom, iRight; + if (BottomStyle == CSS_LINE_STYLE_DOUBLE) + { + iLeft = 2*dLeft / 3.f; + iBottom = 2*dBottom / 3.f; + iRight = 2*dRight / 3.f; + } + else + { + iLeft = dLeft / 2.f; + iBottom = dBottom / 2.f; + iRight = dRight / 2.f; + } + + if (BottomStyle == CSS_LINE_STYLE_GROOVE) + shape.Color = blend(shape.Color, CRGBA::Black, 0.5f); + else + shape.Color = BottomColor; + + m_DrawBorders.back().Quad.V2.x += iRight; m_DrawBorders.back().Quad.V2.y -= iBottom; + m_DrawBorders.back().Quad.V3.x -= iLeft; m_DrawBorders.back().Quad.V3.y -= iBottom; + shape.Quad.V1.x -= iRight; shape.Quad.V1.y += iBottom; + shape.Quad.V0.x += iLeft; shape.Quad.V0.y += iBottom; + m_DrawBorders.push_back(shape); + } } - if (_BorderBottom) + if (m_BorderRight) { - // top-left - _QuadB.V3.x = _XReal + dLeft; - _QuadB.V3.y = _YReal + dBottom; - // top-right - _QuadB.V2.x = _XReal + _WReal - dRight; - _QuadB.V2.y = _YReal + dBottom; - // bottom-right - _QuadB.V1.x = _XReal + _WReal; - _QuadB.V1.y = _YReal; - // bottom-left - _QuadB.V0.x = _XReal; - _QuadB.V0.y = _YReal; + if (RightStyle == CSS_LINE_STYLE_OUTSET || RightStyle == CSS_LINE_STYLE_RIDGE) + shape.Color = blend(RightColor, CRGBA::Black, 0.5f); + else + shape.Color = RightColor; + + shape.Quad.V3.x = bRight; shape.Quad.V3.y = bTop; + shape.Quad.V2.x = xRight; shape.Quad.V2.y = xTop; + shape.Quad.V1.x = xRight; shape.Quad.V1.y = m_YReal; + shape.Quad.V0.x = bRight; shape.Quad.V0.y = bBottom; + m_DrawBorders.push_back(shape); + + if (hasInnerShape(RightStyle)) + { + float iTop, iRight, iBottom; + if (RightStyle == CSS_LINE_STYLE_DOUBLE) + { + iTop = 2*dTop / 3.f; + iRight = 2*dRight / 3.f; + iBottom = 2*dBottom / 3.f; + } else { + iTop = dTop / 2.f; + iRight = dRight / 2.f; + iBottom = dBottom / 2.f; + } + + if (RightStyle == CSS_LINE_STYLE_GROOVE) + shape.Color = blend(shape.Color, CRGBA::Black, 0.5f); + else + shape.Color = RightColor; + + m_DrawBorders.back().Quad.V3.x += iRight; m_DrawBorders.back().Quad.V3.y += iTop; + m_DrawBorders.back().Quad.V0.x += iRight; m_DrawBorders.back().Quad.V0.y -= iBottom; + shape.Quad.V2.x -= iRight; shape.Quad.V2.y -= iTop; + shape.Quad.V1.x -= iRight; shape.Quad.V1.y += iBottom; + m_DrawBorders.push_back(shape); + } } - if (_BorderLeft) + if (m_BorderLeft) { - // top-left - _QuadL.V3.x = _XReal; - _QuadL.V3.y = _YReal + _HReal; - // top-right - _QuadL.V2.x = _XReal + dLeft; - _QuadL.V2.y = _YReal + _HReal - dTop; - // bottom-right - _QuadL.V1.x = _XReal + dLeft; - _QuadL.V1.y = _YReal + dBottom; - // bottom-left - _QuadL.V0.x = _XReal; - _QuadL.V0.y = _YReal; + if (LeftStyle == CSS_LINE_STYLE_INSET || LeftStyle == CSS_LINE_STYLE_GROOVE) + shape.Color = blend(LeftColor, CRGBA::Black, 0.5f); + else + shape.Color = LeftColor; + + shape.Quad.V3.x = m_XReal; shape.Quad.V3.y = xTop; + shape.Quad.V2.x = bLeft; shape.Quad.V2.y = bTop; + shape.Quad.V1.x = bLeft; shape.Quad.V1.y = bBottom; + shape.Quad.V0.x = m_XReal; shape.Quad.V0.y = m_YReal; + + m_DrawBorders.push_back(shape); + + if (hasInnerShape(LeftStyle)) + { + if (LeftStyle == CSS_LINE_STYLE_RIDGE) + shape.Color = blend(LeftColor, CRGBA::Black, 0.5f); + else + shape.Color = LeftColor; + + float iTop, iLeft, iBottom; + if (LeftStyle == CSS_LINE_STYLE_DOUBLE) + { + iTop = 2*dTop / 3.f; + iLeft = 2*dLeft / 3.f; + iBottom = 2*dBottom / 3.f; + } else { + iTop = dTop / 2.f; + iLeft = dLeft / 2.f; + dBottom = dBottom / 2.f; + } + + m_DrawBorders.back().Quad.V2.x -= iLeft; m_DrawBorders.back().Quad.V2.y += iTop; + m_DrawBorders.back().Quad.V1.x -= iLeft; m_DrawBorders.back().Quad.V1.y -= iBottom; + shape.Quad.V3.x += iLeft; shape.Quad.V3.y -= iTop; + shape.Quad.V0.x += iLeft; shape.Quad.V0.y += iBottom; + m_DrawBorders.push_back(shape); + } } } // ---------------------------------------------------------------------------- void CSSBorderRenderer::draw() { - if (_Dirty) updateCoords(); - if (!_Border) return; + if (m_Dirty) updateCoords(); + if (!m_Border) return; CViewRenderer &rVR = *CViewRenderer::getInstance(); // TODO: no need for widget manager, if global color is set from parent CRGBA globalColor; - if (_ModulateGlobalColor) + if (m_ModulateGlobalColor) globalColor = CWidgetManager::getInstance()->getGlobalColor(); - // TODO: monitor ModulateGlobalColor and CurrentAlpha in table checkCoords and recalculate colors on change only - // OUTSET - bottom/right darker than normal (default table style) - // INSET - top/left darker than normal - if (_BorderTop) + sint32 texId = rVR.getBlankTextureId(); + for(uint i = 0; i < m_DrawBorders.size(); ++i) { - CRGBA borderColorT = TopColor; - if (TopStyle == CSS_LINE_STYLE_INSET) - borderColorT = blend(borderColorT, CRGBA::Black, 0.5f); - - if (_ModulateGlobalColor) - borderColorT.modulateFromColor (borderColorT, globalColor); - - borderColorT.A = (uint8) (((uint16) CurrentAlpha * (uint16) borderColorT.A) >> 8); - rVR.drawQuad(_RenderLayer, _QuadT, rVR.getBlankTextureId(), borderColorT, false); - } - if (_BorderRight) - { - CRGBA borderColorR = RightColor; - if (RightStyle == CSS_LINE_STYLE_OUTSET) - borderColorR = blend(borderColorR, CRGBA::Black, 0.5f); - - if (_ModulateGlobalColor) - borderColorR.modulateFromColor (borderColorR, globalColor); - - borderColorR.A = (uint8) (((uint16) CurrentAlpha * (uint16) borderColorR.A) >> 8); - rVR.drawQuad(_RenderLayer, _QuadR, rVR.getBlankTextureId(), borderColorR, false); - } - if (_BorderBottom) - { - CRGBA borderColorB = BottomColor; - if (BottomStyle == CSS_LINE_STYLE_OUTSET) - borderColorB = blend(borderColorB, CRGBA::Black, 0.5f); - - if (_ModulateGlobalColor) - borderColorB.modulateFromColor (borderColorB, globalColor); - - borderColorB.A = (uint8) (((uint16) CurrentAlpha * (uint16) borderColorB.A) >> 8); - rVR.drawQuad(_RenderLayer, _QuadB, rVR.getBlankTextureId(), borderColorB, false); - } - if (_BorderLeft) - { - CRGBA borderColorL = LeftColor; - if (LeftStyle == CSS_LINE_STYLE_INSET) - borderColorL = blend(borderColorL, CRGBA::Black, 0.5f); - - if (_ModulateGlobalColor) - borderColorL.modulateFromColor (borderColorL, globalColor); + CRGBA color = m_DrawBorders[i].Color; + if (m_ModulateGlobalColor) + color.modulateFromColor (color, globalColor); - borderColorL.A = (uint8) (((uint16) CurrentAlpha * (uint16) borderColorL.A) >> 8); - rVR.drawQuad(_RenderLayer, _QuadL, rVR.getBlankTextureId(), borderColorL, false); + color.A = (uint8) (((uint16) CurrentAlpha * (uint16) color.A) >> 8); + rVR.drawQuad(m_RenderLayer, m_DrawBorders[i].Quad, texId, color, false); } } diff --git a/nel/src/gui/css_style.cpp b/nel/src/gui/css_style.cpp index fa80e7b52..c7dae35ad 100644 --- a/nel/src/gui/css_style.cpp +++ b/nel/src/gui/css_style.cpp @@ -571,12 +571,22 @@ namespace NLGUI *dest = CSS_LINE_STYLE_NONE; else if (value == "hidden") *dest = CSS_LINE_STYLE_HIDDEN; + else if (value == "dotted") + *dest = CSS_LINE_STYLE_DOTTED; + else if (value == "dashed") + *dest = CSS_LINE_STYLE_DASHED; + else if (value == "solid") + *dest = CSS_LINE_STYLE_SOLID; + else if (value == "double") + *dest = CSS_LINE_STYLE_DOUBLE; + else if (value == "groove") + *dest = CSS_LINE_STYLE_GROOVE; + else if (value == "ridge") + *dest = CSS_LINE_STYLE_RIDGE; else if (value == "inset") *dest = CSS_LINE_STYLE_INSET; else if (value == "outset") *dest = CSS_LINE_STYLE_OUTSET; - else if (value == "solid") - *dest = CSS_LINE_STYLE_SOLID; } void CCssStyle::applyPaddingWidth(const std::string &value, uint32 *dest, const uint32 currentPadding, uint32 fontSize) const From dfe45029ab612bbb0df9ae09e4d2b3d7ed5b4b60 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Sun, 4 Jul 2021 16:55:50 +0300 Subject: [PATCH 15/24] Add CSSLength, CSSBackground, CSSBackgroundRenderer classes --- nel/include/nel/gui/css_background.h | 83 +++ nel/include/nel/gui/css_background_renderer.h | 193 ++++++ nel/include/nel/gui/css_length.h | 76 +++ nel/include/nel/gui/css_types.h | 38 ++ nel/src/gui/css_background.cpp | 427 ++++++++++++ nel/src/gui/css_background_renderer.cpp | 607 ++++++++++++++++++ nel/src/gui/css_length.cpp | 222 +++++++ 7 files changed, 1646 insertions(+) create mode 100644 nel/include/nel/gui/css_background.h create mode 100644 nel/include/nel/gui/css_background_renderer.h create mode 100644 nel/include/nel/gui/css_length.h create mode 100644 nel/src/gui/css_background.cpp create mode 100644 nel/src/gui/css_background_renderer.cpp create mode 100644 nel/src/gui/css_length.cpp diff --git a/nel/include/nel/gui/css_background.h b/nel/include/nel/gui/css_background.h new file mode 100644 index 000000000..87ce87d0e --- /dev/null +++ b/nel/include/nel/gui/css_background.h @@ -0,0 +1,83 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010-2019 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef CL_CSS_BACKGROUND_H +#define CL_CSS_BACKGROUND_H + +#include "nel/misc/types_nl.h" +#include "nel/misc/rgba.h" +#include "nel/gui/css_types.h" +#include "nel/gui/css_length.h" + +namespace NLGUI +{ + /** + * \brief CSS background info + * \date 2021-07-02 11:36 GMT + * \author Meelis Mägi (Nimetu) + */ + class CSSBackground + { + public: + CSSBackground() + :color(NLMISC::CRGBA::Transparent), + repeatX(CSS_VALUE_REPEAT), repeatY(CSS_VALUE_REPEAT), attachment(CSS_VALUE_SCROLL), + xAnchor(CSS_VALUE_LEFT), yAnchor(CSS_VALUE_TOP), + clip(CSS_VALUE_BORDER_BOX), origin(CSS_VALUE_PADDING_BOX), size(CSS_VALUE_AUTO) + {} + + void setImage(const std::string &value); + void setPosition(const std::string &value); + void setSize(const std::string &value); + void setRepeat(const std::string &value); + void setOrigin(const std::string &value); + void setClip(const std::string &value); + void setAttachment(const std::string &value); + void setColor(const std::string &value); + + public: + // TODO: only final layer has color + NLMISC::CRGBA color; + std::string image; + + CSSValueType repeatX; + CSSValueType repeatY; + CSSValueType attachment; + + CSSValueType xAnchor; + CSSValueType yAnchor; + CSSLength xPosition; + CSSLength yPosition; + + CSSValueType clip; + CSSValueType origin; + + CSSValueType size; + CSSLength width; + CSSLength height; + + private: + void positionFromOne(const std::vector &parts); + void positionFromTwo(const std::vector &parts); + void positionFromThree(const std::vector &parts); + void positionFromFour(const std::vector &parts); + }; + +}//namespace + +#endif // CL_CSS_BACKGROUND_H + + diff --git a/nel/include/nel/gui/css_background_renderer.h b/nel/include/nel/gui/css_background_renderer.h new file mode 100644 index 000000000..86b31cb3d --- /dev/null +++ b/nel/include/nel/gui/css_background_renderer.h @@ -0,0 +1,193 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010-2019 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + + +#ifndef NL_CSS_BACKGROUND_RENDERER_H +#define NL_CSS_BACKGROUND_RENDERER_H + +#include "nel/misc/types_nl.h" +#include "nel/misc/rgba.h" +#include "nel/misc/geom_ext.h" +#include "nel/gui/css_types.h" +#include "nel/gui/css_background.h" + +namespace NLGUI +{ +class CInterfaceElement; + +/** + * \brief Border renderer for GUI classes + * \date 2021-06-29 15:17 GMT + * \author Meelis Mägi (Nimetu) + */ +class CSSBackgroundRenderer +{ +public: + // alpha value from parent + uint8 CurrentAlpha; + + // TODO: should be moved to CSSBackground + sint32 TextureId; + +public: + CSSBackgroundRenderer(); + ~CSSBackgroundRenderer(); + + // return true if no background is set + bool isEmpty() const + { + return m_Background.image.empty() && m_Background.color.A == 0; + } + + void setModulateGlobalColor(bool m) { m_ModulateGlobalColor = m; } + + void updateCoords(); + void invalidateCoords() { m_Dirty = true; } + void invalidateContent() { m_Dirty = true; }; + + void clear(); + void setBackground(const CSSBackground &bg); + + // helper function to change background image + void setImage(const std::string &bgtex); + void setImageRepeat(bool b); + void setImageCover(bool b); + + // helper function to change background color + void setColor(const NLMISC::CRGBA &color) + { + m_Dirty = true; + m_Background.color = color; + } + + NLMISC::CRGBA getColor() const + { + return m_Background.color; + } + + // override painting area to be at least the size of viewport (ie, background) + void setFillViewport(bool b) { + m_Dirty = true; + m_FillViewport = b; + } + + void setViewport(CInterfaceElement *root) + { + m_Dirty = true; + m_Viewport = root; + } + + void setBorderArea(sint32 x, sint32 y, sint32 w, sint32 h) + { + m_Dirty = true; + m_BorderX = x; + m_BorderY = y; + m_BorderW = w; + m_BorderH = h; + } + + void setPaddingArea(sint32 x, sint32 y, sint32 w, sint32 h) + { + m_Dirty = true; + m_PaddingX = x; + m_PaddingY = y; + m_PaddingW = w; + m_PaddingH = h; + } + + void setContentArea(sint32 x, sint32 y, sint32 w, sint32 h) + { + m_Dirty = true; + m_ContentX = x; + m_ContentY = y; + m_ContentW = w; + m_ContentH = h; + } + + // sizes for em, rem units + void setFontSize(float rootFontSize, float fontSize) + { + m_Dirty = true; + m_RootFontSize = rootFontSize; + m_FontSize = fontSize; + } + + void draw(); + +private: + sint32 m_BorderX, m_BorderY, m_BorderW, m_BorderH; + sint32 m_PaddingX, m_PaddingY, m_PaddingW, m_PaddingH; + sint32 m_ContentX, m_ContentY, m_ContentW, m_ContentH; + + // font size for 'rem' + float m_RootFontSize; + + // font size for 'em' + float m_FontSize; + + // viewport element for vw,wh,vmin,vmax + CInterfaceElement* m_Viewport; + + struct SDrawQueue + { + sint32 TextureId; + NLMISC::CQuadUV Quad; + NLMISC::CRGBA Color; + }; + std::vector m_DrawQueue; + + const sint8 m_RenderLayer; + bool m_ModulateGlobalColor; + // if true, painting area returns area at least the size of viewport (ie, background) + bool m_FillViewport; + + // if true, then updateCoords() is called from draw() + bool m_Dirty; + + CSSBackground m_Background; + + // get clip area based on background-clip + void getPaintingArea(const CSSBackground &bg, sint32 &areaX, sint32 &areaY, sint32 &areaW, sint32 &areaH) const; + + // get positioning area based on background-origin + void getPositioningArea(const CSSBackground &bg, sint32 &areaX, sint32 &areaY, sint32 &areaW, sint32 &areaH) const; + + // calculate image size based on background-size + void calculateSize(const CSSBackground &bg, sint32 &texW, sint32 &texH) const; + + // calculate image position based on background-position + void calculatePosition(const CSSBackground &bg, sint32 &texX, sint32 &texY, sint32 &texW, sint32 &texH) const; + + // calculate image tile position, size, count, and spacing based on background-repeat + void calculateTiles(const CSSBackground &bg, sint32 &texX, sint32 &texY, sint32 &texW, sint32 &texH, sint32 &tilesX, sint32 &tilesY, sint32 &spacingX, sint32 &spacingY) const; + + // position, size, and count for first tile to cover an area + void getImageTile(sint32 &tilePos, sint32 &tileSize, sint32 &spacing, sint32 &tiles, sint32 areaPos, sint32 areaSize, CSSValueType repeat) const; + + // push background color to draw queue + void buildColorQuads(const CSSBackground &bg); + + // push background image to draw quque + void buildImageQuads(const CSSBackground &bg, sint32 textureId); + +}; // CSSBackgroundRenderer + +}//namespace + +#endif // NL_CSS_BACKGROUND_RENDERER_H + + diff --git a/nel/include/nel/gui/css_length.h b/nel/include/nel/gui/css_length.h new file mode 100644 index 000000000..d7b8c657b --- /dev/null +++ b/nel/include/nel/gui/css_length.h @@ -0,0 +1,76 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010-2021 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef CL_CSS_LENGTH_H +#define CL_CSS_LENGTH_H + +#include "nel/misc/types_nl.h" +#include "nel/gui/css_types.h" + +namespace NLGUI +{ + /** + * \brief CSS types used in GUI classes + * \date 2021-07-02 11:36 GMT + * \author Meelis Mägi (Nimetu) + */ + + class CSSLength + { + public: + enum Kind { + Auto, Relative, Fixed + }; + + CSSLength(float value = 0, CSSUnitType unit = CSS_UNIT_NONE, Kind kind = Auto) + : m_Value(value), m_Unit(unit), m_Kind(Auto) + {} + + void setAuto() { m_Kind = Auto; } + bool parseValue(const std::string &value, bool allowPercent = true, bool allowNegative = false); + void setFloatValue(float f, const std::string &unit); + + float getValue() const; + float getFloat() const { return m_Value; } + + bool isPercent() const { return m_Unit == CSS_UNIT_PERCENT; } + + bool isAuto() const { return m_Kind == Auto; } + bool isRelative() const { return m_Kind == Relative; } + + // % uses relValue + // em uses emSize + // rem uses remSize + // vw,vh,vi,vb,vmin,vmax uses vwSize/vhSize + float calculate(uint32 relValue, uint32 emSize, uint32 remSize, uint32 vwSize, uint32 whSize) const; + + CSSUnitType getUnit() const { return m_Unit; } + + std::string toString() const; + + private: + void setUnit(const std::string &unit); + + float m_Value; + CSSUnitType m_Unit; + Kind m_Kind; + }; + +}//namespace + +#endif // CL_CSS_LENGTH_H + + diff --git a/nel/include/nel/gui/css_types.h b/nel/include/nel/gui/css_types.h index dd80e308f..ac59af422 100644 --- a/nel/include/nel/gui/css_types.h +++ b/nel/include/nel/gui/css_types.h @@ -47,6 +47,44 @@ namespace NLGUI CSS_LINE_WIDTH_THICK = 5 }; + enum CSSUnitType { + CSS_UNIT_NONE = 0, + CSS_UNIT_EM, + CSS_UNIT_REM, + CSS_UNIT_PERCENT, + CSS_UNIT_PX, + CSS_UNIT_PT, + CSS_UNIT_VW, + CSS_UNIT_VH, + CSS_UNIT_VI, + CSS_UNIT_VB, + CSS_UNIT_VMIN, + CSS_UNIT_VMAX + }; + + enum CSSValueType { + CSS_VALUE_NONE = 0, + CSS_VALUE_REPEAT, + CSS_VALUE_SPACE, + CSS_VALUE_ROUND, + CSS_VALUE_NOREPEAT, + CSS_VALUE_FIXED, + CSS_VALUE_LOCAL, + CSS_VALUE_SCROLL, + CSS_VALUE_LEFT, + CSS_VALUE_CENTER, + CSS_VALUE_RIGHT, + CSS_VALUE_TOP, + CSS_VALUE_BOTTOM, + CSS_VALUE_BORDER_BOX, + CSS_VALUE_PADDING_BOX, + CSS_VALUE_CONTENT_BOX, + CSS_VALUE_LENGTH, + CSS_VALUE_AUTO, + CSS_VALUE_COVER, + CSS_VALUE_CONTAIN + }; + }//namespace #endif // CL_CSS_TYPES_H diff --git a/nel/src/gui/css_background.cpp b/nel/src/gui/css_background.cpp new file mode 100644 index 000000000..4665a0c42 --- /dev/null +++ b/nel/src/gui/css_background.cpp @@ -0,0 +1,427 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010-2021 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" + +#include +#include "nel/gui/libwww.h" +#include "nel/gui/css_length.h" +#include "nel/gui/css_background.h" + +using namespace NLMISC; + +#ifdef DEBUG_NEW +#define new DEBUG_NEW +#endif + +namespace NLGUI +{ + +void CSSBackground::setImage(const std::string &value) +{ + image = value; +} + +void CSSBackground::setPosition(const std::string &value) +{ + std::vector parts; + splitString(toLowerAscii(value), " ", parts); + + if (parts.empty() || parts.size() > 4) + return; + + switch(parts.size()) + { + case 1: + positionFromOne(parts); + break; + case 2: + positionFromTwo(parts); + break; + case 3: + positionFromThree(parts); + break; + case 4: + positionFromFour(parts); + break; + default: + return; + } +} + +void CSSBackground::setSize(const std::string &value) +{ + std::vector parts; + splitString(toLowerAscii(value), " ", parts); + if (parts.size() > 2) + return; + + if (parts.size() == 1 && (parts[0] == "cover" || parts[0] == "contain")) + { + if (parts[0] == "cover") + size = CSS_VALUE_COVER; + else + size = CSS_VALUE_CONTAIN; + + width.setAuto(); + height.setAuto(); + return; + } + + // height will default to 'auto' if not set + if (parts.size() == 1) + parts.push_back("auto"); + + if (parts[0] == "auto" && parts[1] == "auto") + { + size = CSS_VALUE_AUTO; + width.setAuto(); + height.setAuto(); + return; + } + + CSSLength newW, newH; + bool success = true; + if (parts[0] == "auto") + { + newW.setAuto(); + } + else + { + float fval; + std::string unit; + if (!getCssLength(fval, unit, parts[0])) + { + nlwarning("Failed to parse background-size[0] '%s'", parts[0].c_str()); + return; + } + newW.setFloatValue(fval, unit); + } + + if (parts[1] == "auto") + { + newH.setAuto(); + } + else + { + float fval; + std::string unit; + if (!getCssLength(fval, unit, parts[1])) + { + nlwarning("Failed to parse background-size[1] '%s'", parts[1].c_str()); + return; + } + newH.setFloatValue(fval, unit); + } + + size = CSS_VALUE_LENGTH; + width = newW; + height = newH; +} + +void CSSBackground::setRepeat(const std::string &value) +{ + std::vector parts; + splitString(toLowerAscii(value), " ", parts); + if (parts.size() == 0 || parts.size() > 2) + return; + + if (parts.size() == 1) + { + if (parts[0] == "repeat-x") + parts.push_back("no-repeat"); + else if (parts[0] == "repeat-y") + parts.insert(parts.begin(), "no-repeat"); + else //repeat, space, round, no-repeat + parts.push_back(parts[0]); + } + + + if (parts[0] == "repeat") repeatX = CSS_VALUE_REPEAT; + else if (parts[0] == "no-repeat") repeatX = CSS_VALUE_NOREPEAT; + else if (parts[0] == "space") repeatX = CSS_VALUE_SPACE; + else if (parts[0] == "round") repeatX = CSS_VALUE_ROUND; + else repeatX = CSS_VALUE_REPEAT; + + if (parts[1] == "repeat") repeatY = CSS_VALUE_REPEAT; + else if (parts[1] == "no-repeat") repeatY = CSS_VALUE_NOREPEAT; + else if (parts[1] == "space") repeatY = CSS_VALUE_SPACE; + else if (parts[1] == "round") repeatY = CSS_VALUE_ROUND; + else repeatY = CSS_VALUE_REPEAT; +} + +void CSSBackground::setOrigin(const std::string &value) +{ + if (value == "border-box") origin = CSS_VALUE_BORDER_BOX; + else if (value == "padding-box") origin = CSS_VALUE_PADDING_BOX; + else if (value == "content-box") origin = CSS_VALUE_CONTENT_BOX; + else origin = CSS_VALUE_PADDING_BOX; +} + +void CSSBackground::setClip(const std::string &value) +{ + if (value == "border-box") clip = CSS_VALUE_BORDER_BOX; + else if (value == "padding-box") clip = CSS_VALUE_PADDING_BOX; + else if (value == "content-box") clip = CSS_VALUE_CONTENT_BOX; + //else if (value == "text") clip = CSSValueType::Text; + else clip = CSS_VALUE_PADDING_BOX; +} + +void CSSBackground::setAttachment(const std::string &value) +{ + if (value == "fixed") attachment = CSS_VALUE_FIXED; + else if (value == "local") attachment = CSS_VALUE_LOCAL; + else if (value == "scroll") attachment = CSS_VALUE_SCROLL; + else attachment = CSS_VALUE_SCROLL; +} + +void CSSBackground::setColor(const std::string &value) +{ + NLMISC::CRGBA tmp; + if (scanHTMLColor(value.c_str(), tmp)) + color = tmp; +} + +static bool isHorizontalKeyword(const std::string &val) +{ + return val == "left" || val == "right"; +} + +static bool isVerticalKeyword(const std::string &val) +{ + return val == "top" || val == "bottom"; +} + +void CSSBackground::positionFromOne(const std::vector &parts) +{ + CSSValueType newH = CSS_VALUE_LEFT; + CSSValueType newV = CSS_VALUE_TOP; + CSSLength newX, newY; + newX.setFloatValue(0, "%"); + newY.setFloatValue(0, "%"); + + uint index = 0; + float fval; + std::string unit; + if (isHorizontalKeyword(parts[index])) + { + newH = parts[index] == "left" ? CSS_VALUE_LEFT : CSS_VALUE_RIGHT; + newV = CSS_VALUE_CENTER; + } + else if (isVerticalKeyword(parts[index])) + { + newH = CSS_VALUE_CENTER; + newV = parts[index] == "top" ? CSS_VALUE_TOP : CSS_VALUE_BOTTOM; + } + else if (parts[index] == "center") + { + newH = CSS_VALUE_CENTER; + newV = CSS_VALUE_CENTER; + } + else if (getCssLength(fval, unit, parts[index], true)) + { + newX.setFloatValue(fval, unit); + newV = CSS_VALUE_CENTER; + } + else + { + return; + } + + xAnchor = newH; + yAnchor = newV; + xPosition = newX; + yPosition = newY; +} + +void CSSBackground::positionFromTwo(const std::vector &parts) +{ + CSSValueType newH = CSS_VALUE_LEFT; + CSSValueType newV = CSS_VALUE_TOP; + CSSLength newX, newY; + newX.setFloatValue(0, "%"); + newY.setFloatValue(0, "%"); + + float fval; + std::string unit; + uint index = 0; + bool hasCenter = false; + bool hasX = false; + bool hasY = false; + for (uint index = 0; index < parts.size(); index++) + { + if (parts[index] == "center") + { + hasCenter = true; + } + else if (isHorizontalKeyword(parts[index])) + { + if (hasX) return; + hasX = true; + newH = parts[index] == "left" ? CSS_VALUE_LEFT : CSS_VALUE_RIGHT; + } + else if (isVerticalKeyword(parts[index])) + { + if (hasY) return; + hasY = true; + newV = parts[index] == "top" ? CSS_VALUE_TOP : CSS_VALUE_BOTTOM; + } + else if (getCssLength(fval, unit, parts[index], true)) + { + // invalid: 'top 50%'; + if (hasY) return; + if (!hasX) + { + hasX = true; + newX.setFloatValue(fval, unit); + } + else + { + hasY = true; + newY.setFloatValue(fval, unit); + } + } + else + { + return; + } + } + + if (hasCenter) + { + if (!hasX) + newH = CSS_VALUE_CENTER; + if (!hasY) + newV = CSS_VALUE_CENTER; + } + + xAnchor = newH; + yAnchor = newV; + xPosition = newX; + yPosition = newY; +} + +void CSSBackground::positionFromThree(const std::vector &parts) +{ + CSSValueType newH = CSS_VALUE_LEFT; + CSSValueType newV = CSS_VALUE_TOP; + CSSLength newX, newY; + newX.setFloatValue(0, "%"); + newY.setFloatValue(0, "%"); + + float fval; + std::string unit; + bool hasCenter = false; + bool hasX = false; + bool hasY = false; + for(uint index = 0; index < 3; index++) + { + if (parts[index] == "center") + { + if (hasCenter) return; + hasCenter = true; + } + else if (isHorizontalKeyword(parts[index])) + { + if (hasX) return; + hasX = true; + newH = parts[index] == "left" ? CSS_VALUE_LEFT : CSS_VALUE_RIGHT; + if ((index+1) < parts.size() && getCssLength(fval, unit, parts[index+1], true)) + { + newX.setFloatValue(fval, unit); + index++; + } + } + else if (isVerticalKeyword(parts[index])) + { + if (hasY) return; + hasY = true; + newV = parts[index] == "top" ? CSS_VALUE_TOP : CSS_VALUE_BOTTOM; + if ((index+1) < parts.size() && getCssLength(fval, unit, parts[index+1], true)) + { + newY.setFloatValue(fval, unit); + index++; + } + } + else + { + return; + } + } + if (hasCenter) + { + if (hasX && hasY) + return; + + if (!hasX) + newH = CSS_VALUE_CENTER; + else + newV = CSS_VALUE_CENTER; + } + + xAnchor = newH; + yAnchor = newV; + xPosition = newX; + yPosition = newY; +} + +void CSSBackground::positionFromFour(const std::vector &parts) +{ + CSSValueType newH = CSS_VALUE_LEFT; + CSSValueType newV = CSS_VALUE_TOP; + CSSLength newX, newY; + newX.setFloatValue(0, "%"); + newY.setFloatValue(0, "%"); + + float fval; + std::string unit; + bool hasX = false; + bool hasY = false; + for(uint index = 0; index<4; index+=2) + { + if (parts[index] == "center") + return; + + if (isHorizontalKeyword(parts[index])) + { + if (hasX) return; + hasX = true; + if (!getCssLength(fval, unit, parts[index+1], true)) return; + newH = parts[index] == "left" ? CSS_VALUE_LEFT : CSS_VALUE_RIGHT; + newX.setFloatValue(fval, unit); + } + else if (isVerticalKeyword(parts[index])) + { + if (hasY) return; + hasY = true; + if (!getCssLength(fval, unit, parts[index+1], true)) return; + newV = parts[index] == "top" ? CSS_VALUE_TOP : CSS_VALUE_BOTTOM; + newY.setFloatValue(fval, unit); + } + else + { + return; + } + } + + xAnchor = newH; + yAnchor = newV; + xPosition = newX; + yPosition = newY; +} + +} // namespace + diff --git a/nel/src/gui/css_background_renderer.cpp b/nel/src/gui/css_background_renderer.cpp new file mode 100644 index 000000000..4d7df021e --- /dev/null +++ b/nel/src/gui/css_background_renderer.cpp @@ -0,0 +1,607 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010-2019 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include "stdpch.h" +#include "nel/gui/css_background_renderer.h" +#include "nel/gui/css_border_renderer.h" +#include "nel/gui/view_renderer.h" +#include "nel/gui/widget_manager.h" +#include "nel/gui/view_bitmap.h" + +using namespace std; +using namespace NLMISC; + +#ifdef DEBUG_NEW +#define new DEBUG_NEW +#endif + +namespace NLGUI +{ + // ---------------------------------------------------------------------------- + CSSBackgroundRenderer::CSSBackgroundRenderer() + : CurrentAlpha(255), TextureId(-1), + m_BorderX(0), m_BorderY(0), m_BorderW(0), m_BorderH(0), + m_PaddingX(0), m_PaddingY(0), m_PaddingW(0), m_PaddingH(0), + m_ContentX(0), m_ContentY(0), m_ContentW(0), m_ContentH(0), + m_RootFontSize(16.f), m_FontSize(16.f), m_Viewport(NULL), + m_RenderLayer(0), m_ModulateGlobalColor(false), m_FillViewport(false), + m_Dirty(false) + { + } + + // ---------------------------------------------------------------------------- + CSSBackgroundRenderer::~CSSBackgroundRenderer() + { + if (TextureId != -1) + CViewRenderer::getInstance()->deleteTexture(TextureId); + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::clear() + { + m_Dirty = true; + + if (TextureId != -1) + CViewRenderer::getInstance()->deleteTexture(TextureId); + + TextureId = -1; + m_Background.image.clear(); + m_Background.color.A = 0; + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::setBackground(const CSSBackground &bg) + { + m_Dirty = true; + // TODO: CSSBackground should keep track of TextureId + CViewRenderer &rVR = *CViewRenderer::getInstance(); + if (bg.image != m_Background.image && TextureId != -1) + rVR.deleteTexture(TextureId); + + m_Background = bg; + // TODO: does not accept urls + if (TextureId == -1 && !bg.image.empty()) + { + // TODO: make CViewRenderer accept urls + if (bg.image.find("://") != std::string::npos) + TextureId = rVR.createTexture(bg.image, 0, 0, -1, -1, false); + } + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::setImage(const std::string &bgtex) + { + m_Dirty = true; + // TODO: CSSBackground should keep track of TextureId + CViewRenderer &rVR = *CViewRenderer::getInstance(); + if (bgtex != m_Background.image && TextureId != -1) + { + rVR.deleteTexture(TextureId); + TextureId = -1; + } + m_Background.image = bgtex; + + if (TextureId == -1 && !bgtex.empty()) + { + // TODO: make CViewRenderer accept urls + if (bgtex.find("://") != std::string::npos) + TextureId = rVR.createTexture(bgtex, 0, 0, -1, -1, false); + } + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::setImageRepeat(bool b) + { + m_Background.repeatX = b ? CSS_VALUE_REPEAT : CSS_VALUE_NOREPEAT; + m_Background.repeatY = b ? CSS_VALUE_REPEAT : CSS_VALUE_NOREPEAT; + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::setImageCover(bool b) + { + m_Background.size = b ? CSS_VALUE_COVER : CSS_VALUE_AUTO; + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::updateCoords() + { + m_Dirty = false; + m_DrawQueue.clear(); + + // TODO: color from last background layer + buildColorQuads(m_Background); + + // ------------------------------------------------------------------- + // background-image + buildImageQuads(m_Background, TextureId); + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::draw() { + if (m_Dirty) updateCoords(); + if (m_DrawQueue.empty()) return; + + CViewRenderer &rVR = *CViewRenderer::getInstance(); + + // flush draw cache to ensure correct draw order + rVR.flush(); + + sint32 clipX, clipY, clipW, clipH; + if (m_Viewport) + { + rVR.getClipWindow(clipX, clipY, clipW, clipH); + rVR.setClipWindow(m_Viewport->getXReal(), m_Viewport->getYReal(), m_Viewport->getWReal(), m_Viewport->getHReal()); + } + + // TODO: no need for widget manager, if global color is set from parent + CRGBA globalColor; + if (m_ModulateGlobalColor) + globalColor = CWidgetManager::getInstance()->getGlobalColor(); + + // TODO: there might be issue on draw order IF using multiple textures + // and second (top) texture is created before first one. + for(uint i = 0; i < m_DrawQueue.size(); ++i) + { + CRGBA color = m_DrawQueue[i].Color; + if (m_ModulateGlobalColor) + color.modulateFromColor (color, globalColor); + + color.A = (uint8) (((uint16) CurrentAlpha * (uint16) color.A) >> 8); + rVR.drawQuad(m_RenderLayer, m_DrawQueue[i].Quad, m_DrawQueue[i].TextureId, color, false); + } + + // flush draw cache to ensure correct draw order + rVR.flush(); + + if (m_Viewport) + rVR.setClipWindow(clipX, clipY, clipW, clipH); + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::getPositioningArea(const CSSBackground &bg, sint32 &areaX, sint32 &areaY, sint32 &areaW, sint32 &areaH) const + { + switch(bg.origin) + { + case CSS_VALUE_PADDING_BOX: + areaX = m_PaddingX; + areaY = m_PaddingY; + areaW = m_PaddingW; + areaH = m_PaddingH; + break; + case CSS_VALUE_CONTENT_BOX: + areaX = m_ContentX; + areaY = m_ContentY; + areaW = m_ContentW; + areaH = m_ContentH; + break; + case CSS_VALUE_BORDER_BOX: + // fall thru + default: + areaX = m_BorderX; + areaY = m_BorderY; + areaW = m_BorderW; + areaH = m_BorderH; + break; + } + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::getPaintingArea(const CSSBackground &bg, sint32 &areaX, sint32 &areaY, sint32 &areaW, sint32 &areaH) const + { + switch(bg.clip) + { + case CSS_VALUE_PADDING_BOX: + areaX = m_PaddingX; + areaY = m_PaddingY; + areaW = m_PaddingW; + areaH = m_PaddingH; + break; + case CSS_VALUE_CONTENT_BOX: + areaX = m_ContentX; + areaY = m_ContentY; + areaW = m_ContentW; + areaH = m_ContentH; + break; + case CSS_VALUE_BORDER_BOX: + // fall thru + default: + areaX = m_BorderX; + areaY = m_BorderY; + areaW = m_BorderW; + areaH = m_BorderH; + break; + } + + if (m_FillViewport && m_Viewport) + { + sint32 newX = std::min(areaX, m_Viewport->getXReal()); + sint32 newY = std::min(areaY, m_Viewport->getYReal()); + areaW = std::max(areaX + areaW, m_Viewport->getXReal() + m_Viewport->getWReal()) - newX; + areaH = std::max(areaY + areaH, m_Viewport->getYReal() + m_Viewport->getHReal()) - newY; + areaX = newX; + areaY = newY; + } + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::calculateSize(const CSSBackground &bg, sint32 &texW, sint32 &texH) const + { + sint32 areaX, areaY, areaW, areaH; + getPositioningArea(bg, areaX, areaY, areaW, areaH); + + sint32 vpW=0; + sint32 vpH=0; + if (m_Viewport) + { + vpW = m_Viewport->getWReal(); + vpH = m_Viewport->getHReal(); + } + + float whRatio = (float)texW / (float)texH; + switch(bg.size) + { + case CSS_VALUE_LENGTH: + { + if (bg.width.isAuto() && bg.height.isAuto()) + { + // no-op + } + else if (bg.width.isAuto()) + { + texH = bg.height.calculate(areaH, m_FontSize, m_RootFontSize, vpW, vpH); + texW = texH * whRatio; + } + else if (bg.height.isAuto()) + { + // calculate Height + texW = bg.width.calculate(areaW, m_FontSize, m_RootFontSize, vpW, vpH); + texH = texW / whRatio; + } + else + { + texW = bg.width.calculate(areaW, m_FontSize, m_RootFontSize, vpW, vpH); + texH = bg.height.calculate(areaH, m_FontSize, m_RootFontSize, vpW, vpH); + } + break; + } + case CSS_VALUE_AUTO: + { + // no-op + break; + } + case CSS_VALUE_COVER: + { + float canvasRatio = (float)areaW / (float)areaH; + if (whRatio < canvasRatio) + { + texW = areaW; + texH = areaW / whRatio; + } else { + texW = areaH * whRatio; + texH = areaH; + } + break; + } + case CSS_VALUE_CONTAIN: + { + // same as covert, but ratio check is reversed + float canvasRatio = (float)areaW / (float)areaH; + if (whRatio > canvasRatio) + { + texW = areaW; + texH = areaW / whRatio; + } else { + texW = areaH * whRatio; + texH = areaH; + } + break; + } + } + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::calculatePosition(const CSSBackground &bg, sint32 &texX, sint32 &texY, sint32 &texW, sint32 &texH) const + { + sint32 areaX, areaY, areaW, areaH; + getPositioningArea(bg, areaX, areaY, areaW, areaH); + + sint32 vpW=0; + sint32 vpH=0; + if (m_Viewport) + { + vpW = m_Viewport->getWReal(); + vpH = m_Viewport->getHReal(); + } + + float ofsX = bg.xPosition.calculate(1, m_FontSize, m_RootFontSize, vpW, vpH); + float ofsY = bg.yPosition.calculate(1, m_FontSize, m_RootFontSize, vpW, vpH); + + if (bg.xPosition.isPercent() || bg.xAnchor == CSS_VALUE_CENTER) + { + if (bg.xAnchor == CSS_VALUE_RIGHT) + ofsX = 1.f - ofsX; + else if (bg.xAnchor == CSS_VALUE_CENTER) + ofsX = 0.5f; + + ofsX = (float)(areaW - texW) * ofsX; + } + else if (bg.xAnchor == CSS_VALUE_RIGHT) + { + ofsX = areaW - texW - ofsX; + } + + // areaY is bottom edge, areaY+areaH is top edge + if (bg.yPosition.isPercent() || bg.yAnchor == CSS_VALUE_CENTER) + { + if (bg.yAnchor == CSS_VALUE_TOP) + ofsY = 1.f - ofsY; + else if (bg.yAnchor == CSS_VALUE_CENTER) + ofsY = 0.5f; + + ofsY = (float)(areaH - texH) * ofsY; + } + else if (bg.yAnchor == CSS_VALUE_TOP) + { + ofsY = areaH - texH - ofsY; + } + + texX = areaX + ofsX; + texY = areaY + ofsY; + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::getImageTile(sint32 &tilePos, sint32 &tileSize, sint32 &spacing, sint32 &tiles, sint32 areaPos, sint32 areaSize, CSSValueType repeat) const + { + switch(repeat) + { + case CSS_VALUE_NOREPEAT: + { + tiles = 1; + spacing = 0; + break; + } + case CSS_VALUE_SPACE: + { + // if no space for 2+ tiles, then show single one on calculated tilePos + if (tileSize * 2 > areaSize) + { + // set spacing large enough to only display single tile + tiles = 1; + spacing = areaSize; + } + else + { + // available for middle tiles + sint32 midSize = (areaSize - tileSize*2); + // number of middle tiles + sint32 midTiles = midSize / tileSize; + + tiles = 2 + midTiles; + tilePos = areaPos; + // int div for floor() + spacing = ( midSize - tileSize * midTiles) / (midTiles + 1); + } + break; + } + case CSS_VALUE_ROUND: + // fall-thru - size is already calculated + case CSS_VALUE_REPEAT: + // fall-thru + default: + { + tilePos -= std::ceil(abs(tilePos - areaPos)/(float)tileSize)*tileSize; + tiles = std::ceil((std::abs(areaPos - tilePos) + areaSize) / (float)tileSize); + spacing = 0; + break; + } + } + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::calculateTiles(const CSSBackground &bg, sint32 &texX, sint32 &texY, sint32 &texW, sint32 &texH, sint32 &tilesX, sint32 &tilesY, sint32 &spacingX, sint32 &spacingY) const + { + sint32 areaX, areaY, areaW, areaH; + getPositioningArea(bg, areaX, areaY, areaW, areaH); + + // texX,texY is for position area (ie content-box), but painting area can be bigger (ie border-box) + sint32 paintX, paintY, paintW, paintH; + getPaintingArea(bg, paintX, paintY, paintW, paintH); + if (paintX < areaX) + areaX -= std::ceil((areaX - paintX) / (float)texW) * texW; + if ((paintX + paintW) > (areaX + areaW)) + areaW += std::ceil(((paintX + paintW) - (areaX + areaW)) / (float)texW) * texW; + if (paintY < areaY) + areaY -= std::ceil((areaY - paintY) / (float)texH) * texH; + if ((paintY + paintH) > (areaY + areaH)) + areaH += std::ceil(((paintY + paintH) - (areaY + areaH)) / (float)texH) * texH; + + if (texW <= 0 || texH <= 0 || areaW <= 0 || areaH <= 0) + { + tilesX = tilesY = 0; + spacingX = spacingY = 0; + return; + } + + if (bg.repeatX == CSS_VALUE_ROUND) + { + sint numTiles = std::max(1, (sint)std::round((float)areaW / texW)); + texW = areaW / numTiles; + if (bg.height.isAuto() && bg.repeatY != CSS_VALUE_ROUND) + { + float aspect = (float)areaW / (numTiles * texW); + texH = texW * aspect; + } + } + + if (bg.repeatY == CSS_VALUE_ROUND) + { + sint numTiles = std::max(1, (sint)std::round((float)areaH / texH)); + texH = areaH / numTiles; + if (bg.width.isAuto() && bg.repeatX != CSS_VALUE_ROUND) + { + float aspect = (float)areaH / (numTiles * texH); + texW = texH * aspect; + } + } + + getImageTile(texX, texW, spacingX, tilesX, areaX, areaW, bg.repeatX); + getImageTile(texY, texH, spacingY, tilesY, areaY, areaH, bg.repeatY); + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::buildColorQuads(const CSSBackground &bg) + { + if (bg.color.A == 0) + return; + + // painting area defined with background-clip + sint32 x, y, w, h; + getPaintingArea(bg, x, y, w, h); + + if (w <= 0 || h <= 0) + return; + + CViewRenderer &rVR = *CViewRenderer::getInstance(); + + SDrawQueue shape; + shape.Quad.Uv0.set(0.f, 1.f); + shape.Quad.Uv1.set(1.f, 1.f); + shape.Quad.Uv2.set(1.f, 0.f); + shape.Quad.Uv3.set(0.f, 0.f); + + shape.Quad.V0.set(x, y, 0); + shape.Quad.V1.set(x+w, y, 0); + shape.Quad.V2.set(x+w, y+h, 0); + shape.Quad.V3.set(x, y+h, 0); + + shape.Color = bg.color; + shape.TextureId = rVR.getBlankTextureId(); + + m_DrawQueue.push_back(shape); + } + + // ---------------------------------------------------------------------------- + void CSSBackgroundRenderer::buildImageQuads(const CSSBackground &bg, sint32 textureId) + { + // TODO: m_Background should have textureId and that should be "reserved" in CViewRenderer + // even before download is finished + if (textureId < 0) + return; + + CViewRenderer &rVR = *CViewRenderer::getInstance(); + + sint32 texW = 0; + sint32 texH = 0; + rVR.getTextureSizeFromId(textureId, texW, texH); + if (texW <= 0 || texH <= 0) + return; + + // get requested texture size + calculateSize(m_Background, texW, texH); + if(texW <= 0 || texH <= 0) + return; + + // get texture left/top corner + sint32 texX, texY; + calculatePosition(m_Background, texX, texY, texW, texH); + + sint32 tilesX, tilesY; + sint32 spacingX, spacingY; + calculateTiles(m_Background, texX, texY, texW, texH, tilesX, tilesY, spacingX, spacingY); + + sint32 clipL, clipB, clipR, clipT; + getPaintingArea(m_Background, clipL, clipB, clipR, clipT); + clipR += clipL; + clipT += clipB; + + m_DrawQueue.reserve(tilesX * tilesY + m_DrawQueue.size()); + for(sint32 tileX = 0; tileX < tilesX; tileX++) + { + for(sint32 tileY = 0; tileY < tilesY; tileY++) + { + sint32 tileL = texX + tileX * (texW + spacingX); + sint32 tileB = texY + tileY * (texH + spacingY); + sint32 tileR = tileL + texW; + sint32 tileT = tileB + texH; + + // tile is outside clip area + if (tileT <= clipB || tileB >= clipT || tileR <= clipL || tileL >= clipR) + continue; + + CUV uv0(0,1); + CUV uv1(1,1); + CUV uv2(1,0); + CUV uv3(0,0); + + // clip if tile not totally inside clip area + if (!(tileL >= clipL && tileR <= clipR && tileB >= clipB && tileT <= clipT)) + { + float ratio; + if (tileL < clipL) + { + ratio = ((float)(clipL - tileL))/((float)(tileR - tileL)); + tileL = clipL; + uv0.U += ratio*(uv1.U-uv0.U); + uv3.U += ratio*(uv2.U-uv3.U); + } + + if (tileB < clipB) + { + ratio = ((float)(clipB - tileB))/((float)(tileT - tileB)); + tileB = clipB; + uv0.V += ratio*(uv3.V-uv0.V); + uv1.V += ratio*(uv2.V-uv1.V); + } + + if (tileR > clipR) + { + ratio = ((float)(clipR - tileR))/((float)(tileL - tileR)); + tileR = clipR; + uv2.U += ratio*(uv3.U-uv2.U); + uv1.U += ratio*(uv0.U-uv1.U); + } + + if (tileT > clipT) + { + ratio = ((float)(clipT - tileT))/((float)(tileB - tileT)); + tileT = clipT; + uv2.V += ratio*(uv1.V-uv2.V); + uv3.V += ratio*(uv0.V-uv3.V); + } + } + + SDrawQueue shape; + shape.Quad.Uv0 = uv0; + shape.Quad.Uv1 = uv1; + shape.Quad.Uv2 = uv2; + shape.Quad.Uv3 = uv3; + + shape.Color = CRGBA::White; + shape.TextureId = textureId; + + shape.Quad.V0.set(tileL, tileB, 0); + shape.Quad.V1.set(tileR, tileB, 0); + shape.Quad.V2.set(tileR, tileT, 0); + shape.Quad.V3.set(tileL, tileT, 0); + + m_DrawQueue.push_back(shape); + } + } + } + +}//namespace + diff --git a/nel/src/gui/css_length.cpp b/nel/src/gui/css_length.cpp new file mode 100644 index 000000000..fde47b644 --- /dev/null +++ b/nel/src/gui/css_length.cpp @@ -0,0 +1,222 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010-2021 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" + +#include +#include "nel/gui/css_length.h" + +using namespace NLMISC; + +#ifdef DEBUG_NEW +#define new DEBUG_NEW +#endif + +namespace NLGUI +{ + +bool CSSLength::parseValue(const std::string &value, bool allowPercent, bool allowNegative) +{ + static const std::set knownUnits = { + "%", "rem", "em", "px", "pt", "vw", "vh", "vi", "vb", "vmin", "vmax" + }; + + std::string::size_type pos = 0; + std::string::size_type len = value.size(); + if (len == 0) + return false; + + if (len == 1 && value[0] == '0') + { + m_Value = 0; + m_Kind = Auto; + return true; + } + + // +100px; -100px + if (value[0] == '+') + pos++; + else if (allowNegative && value[0] == '-') + pos++; + + while(pos < len) + { + bool isNumeric = (value[pos] >= '0' && value[pos] <= '9') + || (pos == 0 && value[pos] == '.') + || (pos > 0 && value[pos] == '.' && value[pos-1] >= '0' && value[pos-1] <= '9'); + + if (!isNumeric) + break; + + pos++; + } + + std::string unit = toLowerAscii(value.substr(pos)); + if (knownUnits.count(unit)) + { + std::string tmpstr = value.substr(0, pos); + return fromString(tmpstr, m_Value); + } + + return false; +} + +float CSSLength::getValue() const +{ + if (m_Unit == CSS_UNIT_PERCENT) + return m_Value / 100.f; + + return m_Value; +} +void CSSLength::setFloatValue(float f, const std::string &unit) +{ + m_Value = f; + setUnit(unit); +} + +void CSSLength::setUnit(const std::string &unit) +{ + if (unit.empty()) + { + m_Unit = CSS_UNIT_NONE; + m_Kind = Fixed; + } + else if (unit == "px") + { + m_Unit = CSS_UNIT_PX; + m_Kind = Fixed; + } else if (unit == "pt") + { + m_Unit = CSS_UNIT_PT; + m_Kind = Fixed; + } else if (unit == "%") + { + m_Unit = CSS_UNIT_PERCENT; + m_Kind = Relative; + } else if (unit == "em") + { + m_Unit = CSS_UNIT_EM; + m_Kind = Relative; + } else if (unit == "rem") + { + m_Unit = CSS_UNIT_REM; + m_Kind = Relative; + } else if (unit == "vw") + { + m_Unit = CSS_UNIT_VW; + m_Kind = Relative; + } else if (unit == "vh") + { + m_Unit = CSS_UNIT_VH; + m_Kind = Relative; + } else if (unit == "vi") + { + m_Unit = CSS_UNIT_VI; + m_Kind = Relative; + } else if (unit == "vb") + { + m_Unit = CSS_UNIT_VB; + m_Kind = Relative; + } else if (unit == "vmin") + { + m_Unit = CSS_UNIT_VMIN; + m_Kind = Relative; + } else if (unit == "vmax") + { + m_Unit = CSS_UNIT_VMAX; + m_Kind = Relative; + } else if (unit == "auto") + { + m_Unit = CSS_UNIT_NONE; + m_Kind = Auto; + } else + { + // fallback to auto + m_Unit = CSS_UNIT_NONE; + m_Kind = Auto; + } +} + +float CSSLength::calculate(uint32 relValue, uint32 emSize, uint32 remSize, uint32 vwSize, uint32 vhSize = 0) const +{ + float value = getValue(); + switch(m_Unit) + { + case CSS_UNIT_EM: + return emSize * value; + case CSS_UNIT_REM: + return remSize * value; + case CSS_UNIT_PERCENT: + return relValue * value; + case CSS_UNIT_PX: + case CSS_UNIT_PT: + return value; + case CSS_UNIT_VW: + case CSS_UNIT_VI: + // Vi for horizontal writing mode only + return (float)vwSize*0.01f; + case CSS_UNIT_VH: + case CSS_UNIT_VB: + // Vb for horizontal writing mode only + return (float)vhSize*0.01f; + case CSS_UNIT_VMIN: + return (float)std::min(vwSize, vhSize)*0.01f; + case CSS_UNIT_VMAX: + return (float)std::max(vwSize, vhSize)*0.01f; + } + + nldebug("Unknown CSS unit '%s'", toString().c_str()); + return value; +} + +std::string CSSLength::toString() const +{ + if (m_Kind == Auto) + return "auto"; + + std::string ret; + ret += NLMISC::toString("%f", m_Value); + + size_t pos = ret.find("."); + for( ; pos < ret.size(); ++pos) + { + if (ret[pos] != '0') + break; + } + if (pos == ret.size()) + ret = ret.substr(0, ret.find(".")); + + switch(m_Unit) + { + case CSS_UNIT_NONE: break; + case CSS_UNIT_EM: ret += "em"; break; + case CSS_UNIT_REM: ret += "rem"; break; + case CSS_UNIT_PERCENT: ret += "%"; break; + case CSS_UNIT_PX: ret += "px"; break; + case CSS_UNIT_PT: ret += "pt"; break; + case CSS_UNIT_VW: ret += "vw"; break; + case CSS_UNIT_VH: ret += "vh"; break; + case CSS_UNIT_VI: ret += "vi"; break; + case CSS_UNIT_VB: ret += "vb"; break; + case CSS_UNIT_VMIN: ret += "vmin"; break; + case CSS_UNIT_VMAX: ret += "vmax"; break; + } + + return ret; +} + +} // namespace + From f26dc534c83a7d54169c7aeb2e5ead8ee3f9cdd5 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Wed, 30 Jun 2021 13:48:41 +0300 Subject: [PATCH 16/24] Split CDataDownload into subclasses --- nel/include/nel/gui/group_html.h | 114 +++++--- nel/include/nel/gui/view_bitmap.h | 7 +- nel/src/gui/group_html.cpp | 422 ++++++++++++++++-------------- nel/src/gui/view_bitmap.cpp | 10 +- 4 files changed, 319 insertions(+), 234 deletions(-) diff --git a/nel/include/nel/gui/group_html.h b/nel/include/nel/gui/group_html.h index 3c94e7fa9..c94bec024 100644 --- a/nel/include/nel/gui/group_html.h +++ b/nel/include/nel/gui/group_html.h @@ -47,6 +47,20 @@ namespace NLGUI extern std::string CurrentCookie; + class ICurlDownloadCB + { + public: + ICurlDownloadCB(const std::string &url) + : url(url) + {} + + virtual ~ICurlDownloadCB() {}; + + virtual void finish() = 0; + + std::string url; + }; + // HTML group /** * Widget to have a resizable scrolltext and its scrollbar @@ -133,9 +147,8 @@ namespace NLGUI void endParagraph(); // add image download (used by view_bitmap.cpp to load web images) - void addImageDownload(const std::string &url, CViewBase *img, const CStyleParams &style = CStyleParams(), const TImageType type = NormalImage, const std::string &placeholder = "web_del.tga"); - // remove image from download list if present - void removeImageDownload(CViewBase *img); + ICurlDownloadCB *addImageDownload(const std::string &url, CViewBase *img, const CStyleParams &style = CStyleParams(), const TImageType type = NormalImage, const std::string &placeholder = "web_del.tga"); + void removeImageDownload(ICurlDownloadCB *handle, CViewBase *img); std::string localImageName(const std::string &url); // Timeout @@ -817,48 +830,89 @@ namespace NLGUI // decode all HTML entities static std::string decodeHTMLEntities(const std::string &str); - struct CDataImageDownload + class CDataDownload : public ICurlDownloadCB { public: - CDataImageDownload(CViewBase *img, CStyleParams style, TImageType type): Image(img), Style(style), Type(type) - { - } + CDataDownload(const std::string &u, const std::string &d) + : ICurlDownloadCB(u), data(NULL), fp(NULL), dest(d), redirects(0), ConnectionTimeout(60) + {} + virtual ~CDataDownload(); + public: - CViewBase * Image; - CStyleParams Style; - TImageType Type; + CCurlWWWData *data; + std::string dest; + std::string tmpdest; + uint32 redirects; + FILE *fp; + uint32 ConnectionTimeout; + }; + + class StylesheetDownloadCB : public CDataDownload + { + public: + StylesheetDownloadCB(const std::string &url, const std::string &dest, CGroupHTML *parent) + : CDataDownload(url, dest), Parent(parent) + {} + + virtual void finish() NL_OVERRIDE; + + private: + CGroupHTML *Parent; }; - struct CDataDownload + class ImageDownloadCB : public CDataDownload { public: - CDataDownload(const std::string &u, const std::string &d, TDataType t, CViewBase *i, const std::string &s, const std::string &m, const CStyleParams &style = CStyleParams(), const TImageType imagetype = NormalImage) - : data(NULL), fp(NULL), url(u), dest(d), type(t), luaScript(s), md5sum(m), redirects(0), ConnectionTimeout(60) + struct SImageInfo + { + SImageInfo(CViewBase *img, const CStyleParams &style, TImageType type) + : Image(img), Style(style), Type(type) + {} + + CViewBase *Image; + CStyleParams Style; + TImageType Type; + }; + + ImageDownloadCB(const std::string &url, const std::string &dest, CViewBase *img, const CStyleParams &style, TImageType type, CGroupHTML *parent) + : CDataDownload(url, dest), Parent(parent) { - if (t == ImgType) imgs.push_back(CDataImageDownload(i, style, imagetype)); + addImage(img, style, type); } - ~CDataDownload(); + virtual void finish() NL_OVERRIDE; + + void addImage(CViewBase *img, const CStyleParams &style, TImageType type); + void removeImage(CViewBase *img); + + private: + std::vector Images; + CGroupHTML *Parent; + CStyleParams Style; + TImageType Type; + }; + + class BnpDownloadCB : public CDataDownload + { public: - CCurlWWWData *data; - std::string url; - std::string dest; - std::string tmpdest; - std::string luaScript; - std::string md5sum; - TDataType type; - uint32 redirects; - FILE *fp; - std::vector imgs; - uint32 ConnectionTimeout; + BnpDownloadCB(const std::string &url, const std::string &dest, const std::string md5sum, const std::string lua, CGroupHTML *parent) + : CDataDownload(url, dest), Parent(parent), m_md5sum(md5sum), m_lua(lua) + {} + + virtual void finish() NL_OVERRIDE; + + private: + CGroupHTML *Parent; + std::string m_md5sum; + std::string m_lua; }; - std::list Curls; + std::list Curls; CURLM *MultiCurl; int RunningCurls; - bool startCurlDownload(CDataDownload &download); - void finishCurlDownload(const CDataDownload &download); + bool startCurlDownload(CDataDownload *download); + void finishCurlDownload(CDataDownload *download); void pumpCurlQueue(); void initImageDownload(); @@ -888,7 +942,7 @@ namespace NLGUI // _CurlWWW download finished void htmlDownloadFinished(bool success, const std::string &error); // images, stylesheets, etc finished downloading - void dataDownloadFinished(bool success, const std::string &error, CDataDownload &data); + void dataDownloadFinished(bool success, const std::string &error, CDataDownload *data); // HtmlType download finished void htmlDownloadFinished(const std::string &content, const std::string &type, long code); diff --git a/nel/include/nel/gui/view_bitmap.h b/nel/include/nel/gui/view_bitmap.h index f7a77cb30..958fdcc5c 100644 --- a/nel/include/nel/gui/view_bitmap.h +++ b/nel/include/nel/gui/view_bitmap.h @@ -28,6 +28,7 @@ namespace NLGUI { + class ICurlDownloadCB; /** * class implementing a bitmap view @@ -61,7 +62,7 @@ namespace NLGUI _TxtHeight = -1; // Support for https://.. textures - _HtmlDownload = false; + _HtmlDownload = NULL; } /// Destructor @@ -141,7 +142,9 @@ namespace NLGUI bool _Flip : 1; bool _Tile : 1; bool _InheritGCAlpha : 1; - bool _HtmlDownload : 1; + + // pointer to active curl download object + ICurlDownloadCB *_HtmlDownload; // For single texture diff --git a/nel/src/gui/group_html.cpp b/nel/src/gui/group_html.cpp index 75d7eb64c..0c48b92a8 100644 --- a/nel/src/gui/group_html.cpp +++ b/nel/src/gui/group_html.cpp @@ -266,6 +266,120 @@ namespace NLGUI data = NULL; } + void CGroupHTML::StylesheetDownloadCB::finish() + { + if (CFile::fileExists(tmpdest)) + { + if (CFile::fileExists(dest)) + { + CFile::deleteFile(dest); + } + CFile::moveFile(dest, tmpdest); + } + Parent->cssDownloadFinished(url, dest); + } + + void CGroupHTML::ImageDownloadCB::addImage(CViewBase *img, const CStyleParams &style, TImageType type) + { + Images.push_back(SImageInfo(img, style, type)); + } + + void CGroupHTML::ImageDownloadCB::removeImage(CViewBase *img) + { + for(std::vector::iterator it = Images.begin(); it != Images.end(); ++it) + { + if (it->Image == img) + { + Images.erase(it); + break; + } + } + } + + void CGroupHTML::ImageDownloadCB::finish() + { + // tmpdest file does not exist if download skipped (ie cache was used) + if (CFile::fileExists(tmpdest) || CFile::getFileSize(tmpdest) == 0) + { + try { + // verify that image is not corrupted + uint32 w, h; + CBitmap::loadSize(tmpdest, w, h); + if (w != 0 && h != 0) + { + if (CFile::fileExists(dest)) + CFile::deleteFile(dest); + } + } + catch(const NLMISC::Exception &e) + { + // exception message has .tmp file name, so keep it for further analysis + nlwarning("Invalid image (%s) from url (%s): %s", tmpdest.c_str(), url.c_str(), e.what()); + } + + // to reload image on page, the easiest seems to be changing texture + // to temp file temporarily. that forces driver to reload texture from disk + // ITexture::touch() seem not to do this. + // cache was updated, first set texture as temp file + for(std::vector::iterator it = Images.begin(); it != Images.end(); ++it) + { + SImageInfo &img = *it; + Parent->setImage(img.Image, tmpdest, img.Type); + Parent->setImageSize(img.Image, img.Style); + } + + CFile::moveFile(dest, tmpdest); + } + + if (!CFile::fileExists(dest) || CFile::getFileSize(dest) == 0) + { + // placeholder if cached image failed + dest = "web_del.tga"; + } + + // even if image was cached, incase there was 'http://' image set to CViewBitmap + for(std::vector::iterator it = Images.begin(); it != Images.end(); ++it) + { + SImageInfo &img = *it; + Parent->setImage(img.Image, dest, img.Type); + Parent->setImageSize(img.Image, img.Style); + } + } + + void CGroupHTML::BnpDownloadCB::finish() + { + bool verified = false; + // no tmpfile if file was already in cache + if (CFile::fileExists(tmpdest)) + { + verified = m_md5sum.empty() || (m_md5sum != getMD5(tmpdest).toString()); + if (verified) + { + if (CFile::fileExists(dest)) + { + CFile::deleteFile(dest); + } + CFile::moveFile(dest, tmpdest); + } + else + { + CFile::deleteFile(tmpdest); + } + } + else if (CFile::fileExists(dest)) + { + verified = m_md5sum.empty() || (m_md5sum != getMD5(dest).toString()); + } + + if (!m_lua.empty()) + { + std::string script = "\nlocal __CURRENT_WINDOW__ = \""+Parent->getId()+"\""; + script += toString("\nlocal __DOWNLOAD_STATUS__ = %s\n", verified ? "true" : "false"); + script += m_lua; + CLuaManager::getInstance().executeLuaScript(script, true ); + } + } + // Check if domain is on TrustedDomain bool CGroupHTML::isTrustedDomain(const string &domain) { @@ -479,17 +593,17 @@ namespace NLGUI { if (RunningCurls < options.curlMaxConnections) { - std::list::iterator it=Curls.begin(); - uint c = 0; + std::list::iterator it=Curls.begin(); while(it != Curls.end() && RunningCurls < options.curlMaxConnections) { - if (it->data == NULL) + if ((*it)->data == NULL) { LOG_DL("(%s) starting new download '%s'", _Id.c_str(), it->url.c_str()); if (!startCurlDownload(*it)) { LOG_DL("(%s) failed to start '%s)'", _Id.c_str(), it->url.c_str()); finishCurlDownload(*it); + it = Curls.erase(it); continue; } @@ -504,11 +618,11 @@ namespace NLGUI } // Add url to MultiCurl queue and return cURL handle - bool CGroupHTML::startCurlDownload(CDataDownload &download) + bool CGroupHTML::startCurlDownload(CDataDownload *download) { if (!MultiCurl) { - nlwarning("Invalid MultiCurl handle, unable to download '%s'", download.url.c_str()); + nlwarning("Invalid MultiCurl handle, unable to download '%s'", download->url.c_str()); return false; } @@ -516,28 +630,28 @@ namespace NLGUI time(¤tTime); CHttpCacheObject cache; - if (CFile::fileExists(download.dest)) - cache = CHttpCache::getInstance()->lookup(download.dest); + if (CFile::fileExists(download->dest)) + cache = CHttpCache::getInstance()->lookup(download->dest); if (cache.Expires > currentTime) { - LOG_DL("Cache for (%s) is not expired (%s, expires:%d)", download.url.c_str(), download.dest.c_str(), cache.Expires - currentTime); + LOG_DL("Cache for (%s) is not expired (%s, expires:%d)", download->url.c_str(), download->dest.c_str(), cache.Expires - currentTime); return false; } // use browser Id so that two browsers would not use same temp file - download.tmpdest = localImageName(_Id + download.dest) + ".tmp"; + download->tmpdest = localImageName(_Id + download->dest) + ".tmp"; // erase the tmp file if exists - if (CFile::fileExists(download.tmpdest)) + if (CFile::fileExists(download->tmpdest)) { - CFile::deleteFile(download.tmpdest); + CFile::deleteFile(download->tmpdest); } - FILE *fp = nlfopen (download.tmpdest, "wb"); + FILE *fp = nlfopen (download->tmpdest, "wb"); if (fp == NULL) { - nlwarning("Can't open file '%s' for writing: code=%d '%s'", download.tmpdest.c_str (), errno, strerror(errno)); + nlwarning("Can't open file '%s' for writing: code=%d '%s'", download->tmpdest.c_str (), errno, strerror(errno)); return false; } @@ -545,28 +659,28 @@ namespace NLGUI if (!curl) { fclose(fp); - CFile::deleteFile(download.tmpdest); + CFile::deleteFile(download->tmpdest); - nlwarning("Creating cURL handle failed, unable to download '%s'", download.url.c_str()); + nlwarning("Creating cURL handle failed, unable to download '%s'", download->url.c_str()); return false; } - LOG_DL("curl easy handle %p created for '%s'", curl, download.url.c_str()); + LOG_DL("curl easy handle %p created for '%s'", curl, download->url.c_str()); // https:// - if (toLowerAscii(download.url.substr(0, 8)) == "https://") + if (toLowerAscii(download->url.substr(0, 8)) == "https://") { // if supported, use custom SSL context function to load certificates NLWEB::CCurlCertificates::useCertificates(curl); } - download.data = new CCurlWWWData(curl, download.url); - download.fp = fp; + download->data = new CCurlWWWData(curl, download->url); + download->fp = fp; // initial connection timeout, curl default is 300sec - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, download.ConnectionTimeout); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, download->ConnectionTimeout); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, true); - curl_easy_setopt(curl, CURLOPT_URL, download.url.c_str()); + curl_easy_setopt(curl, CURLOPT_URL, download->url.c_str()); // limit curl to HTTP and HTTPS protocols only curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); @@ -580,16 +694,16 @@ namespace NLGUI headers.push_back("If-Modified-Since: " + cache.LastModified); if (headers.size() > 0) - download.data->sendHeaders(headers); + download->data->sendHeaders(headers); // catch headers curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, NLGUI::curlHeaderCallback); - curl_easy_setopt(curl, CURLOPT_WRITEHEADER, download.data); + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, download->data); std::string userAgent = options.appName + "/" + options.appVersion; curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.c_str()); - CUrlParser uri(download.url); + CUrlParser uri(download->url); if (!uri.host.empty()) sendCookies(curl, uri.host, isTrustedDomain(uri.host)); @@ -599,7 +713,7 @@ namespace NLGUI CURLMcode ret = curl_multi_add_handle(MultiCurl, curl); if (ret != CURLM_OK) { - nlwarning("cURL multi handle %p error %d on '%s'", curl, ret, download.url.c_str()); + nlwarning("cURL multi handle %p error %d on '%s'", curl, ret, download->url.c_str()); return false; } @@ -607,118 +721,21 @@ namespace NLGUI return true; } - void CGroupHTML::finishCurlDownload(const CDataDownload &download) + void CGroupHTML::finishCurlDownload(CDataDownload *download) { - if (download.type == ImgType) + if (download) { - if (CFile::fileExists(download.tmpdest) && CFile::getFileSize(download.tmpdest) > 0) - { - try - { - // verify that image is not corrupted - uint32 w, h; - CBitmap::loadSize(download.tmpdest, w, h); - if (w != 0 && h != 0) - { - if (CFile::fileExists(download.dest)) - CFile::deleteFile(download.dest); - - // to reload image on page, the easiest seems to be changing texture - // to temp file temporarily. that forces driver to reload texture from disk - // ITexture::touch() seem not to do this. - // cache was updated, first set texture as temp file - for(uint i = 0; i < download.imgs.size(); i++) - { - setImage(download.imgs[i].Image, download.tmpdest, download.imgs[i].Type); - setImageSize(download.imgs[i].Image, download.imgs[i].Style); - } - - CFile::moveFile(download.dest, download.tmpdest); - } - } - catch(const NLMISC::Exception &e) - { - // exception message has .tmp file name, so keep it for further analysis - nlwarning("Invalid image (%s) from url (%s): %s", download.tmpdest.c_str(), download.url.c_str(), e.what()); - } - } - - if (CFile::fileExists(download.dest) && CFile::getFileSize(download.dest) > 0) - { - try - { - // verify that image is not corrupted - uint32 w, h; - CBitmap::loadSize(download.dest, w, h); - if (w != 0 && h != 0) - for(uint i = 0; i < download.imgs.size(); i++) - { - setImage(download.imgs[i].Image, download.dest, download.imgs[i].Type); - setImageSize(download.imgs[i].Image, download.imgs[i].Style); - } - } - catch(const NLMISC::Exception &e) - { - nlwarning("Invalid image (%s) from url (%s): %s", download.dest.c_str(), download.url.c_str(), e.what()); - } - } - - return; + download->finish(); + delete download; } - - if (download.type == StylesheetType) - { - if (CFile::fileExists(download.tmpdest)) - { - if (CFile::fileExists(download.dest)) - { - CFile::deleteFile(download.dest); - } - CFile::moveFile(download.dest, download.tmpdest); - } - cssDownloadFinished(download.url, download.dest); - - return; - } - - if (download.type == BnpType) + else { - bool verified = false; - // no tmpfile if file was already in cache - if (CFile::fileExists(download.tmpdest)) - { - verified = download.md5sum.empty() || (download.md5sum != getMD5(download.tmpdest).toString()); - if (verified) - { - if (CFile::fileExists(download.dest)) - { - CFile::deleteFile(download.dest); - } - CFile::moveFile(download.dest, download.tmpdest); - } - else - { - CFile::deleteFile(download.tmpdest); - } - } - else if (CFile::fileExists(download.dest)) - { - verified = download.md5sum.empty() || (download.md5sum != getMD5(download.dest).toString()); - } - - std::string script = "\nlocal __CURRENT_WINDOW__ = \""+this->_Id+"\""; - script += toString("\nlocal __DOWNLOAD_STATUS__ = %s\n", verified ? "true" : "false"); - script += download.luaScript; - CLuaManager::getInstance().executeLuaScript(script, true ); - - return; + nlwarning("Unknown CURL download (nullptr)"); } - - nlwarning("Unknown CURL download type (%d) finished '%s'", download.type, download.url.c_str()); } // Add a image download request in the multi_curl - void CGroupHTML::addImageDownload(const string &url, CViewBase *img, const CStyleParams &style, TImageType type, const std::string &placeholder) + ICurlDownloadCB *CGroupHTML::addImageDownload(const string &url, CViewBase *img, const CStyleParams &style, TImageType type, const std::string &placeholder) { std::string finalUrl; img->setModulateGlobalColor(style.GlobalColor); @@ -728,7 +745,7 @@ namespace NLGUI { setImage(img, decodeURIComponent(url), type); setImageSize(img, style); - return; + return NULL; } // load the image from local files/bnp @@ -737,7 +754,7 @@ namespace NLGUI { setImage(img, image, type); setImageSize(img, style); - return; + return NULL; } finalUrl = upgradeInsecureUrl(getAbsoluteUrl(url)); @@ -759,36 +776,40 @@ namespace NLGUI } // Search if we are not already downloading this url. - for(std::list::iterator it = Curls.begin(); it != Curls.end(); ++it) + for(std::list::iterator it = Curls.begin(); it != Curls.end(); ++it) { - if(it->url == finalUrl) + if((*it)->url == finalUrl) { LOG_DL("already downloading '%s' img %p", finalUrl.c_str(), img); - it->imgs.push_back(CDataImageDownload(img, style, type)); - return; + ImageDownloadCB *cb = dynamic_cast(*it); + if (cb) + { + cb->addImage(img, style, type); + // return pointer to shared ImageDownloadCB + return cb; + } + else + { + nlwarning("Found image download '%s', but casting to ImageDownloadCB failed", finalUrl.c_str()); + } } } - Curls.push_back(CDataDownload(finalUrl, dest, ImgType, img, "", "", style, type)); - pumpCurlQueue(); + Curls.push_back(new ImageDownloadCB(finalUrl, dest, img, style, type, this)); + // as we return pointer to callback, skip starting downloads just now + //pumpCurlQueue(); + return Curls.back(); } - void CGroupHTML::removeImageDownload(CViewBase *img) + void CGroupHTML::removeImageDownload(ICurlDownloadCB *handle, CViewBase *img) { - for(std::list::iterator it = Curls.begin(); it != Curls.end(); ++it) - { - // check all active downloads because image does not keep url around - std::vector::iterator imgIter = it->imgs.begin(); - while(imgIter != it->imgs.end()) - { - if (imgIter->Image == img) - { - it->imgs.erase(imgIter); - break; - } - ++imgIter; - } + ImageDownloadCB *cb = dynamic_cast(handle); + if (!cb) { + nlwarning("Trying to remove image from downloads, but ICurlDownloadCB pointer did not cast to ImageDownloadCB"); + return; } + // image will be removed from handle, but handle is kept and image will be downloaded + cb->removeImage(img); } void CGroupHTML::initImageDownload() @@ -815,9 +836,9 @@ namespace NLGUI url = upgradeInsecureUrl(getAbsoluteUrl(url)); // Search if we are not already downloading this url. - for(std::list::const_iterator it = Curls.begin(); it != Curls.end(); ++it) + for(std::list::const_iterator it = Curls.begin(); it != Curls.end(); ++it) { - if(it->url == url) + if((*it)->url == url) { LOG_DL("already downloading '%s'", url.c_str()); return false; @@ -842,7 +863,7 @@ namespace NLGUI } if (action != "delete") { - Curls.push_back(CDataDownload(url, dest, BnpType, NULL, script, md5sum)); + Curls.push_back(new BnpDownloadCB(url, dest, md5sum, script, this)); pumpCurlQueue(); } else @@ -871,7 +892,7 @@ namespace NLGUI _StylesheetQueue.back().Url = url; // push to the front of the queue - Curls.push_front(CDataDownload(url, localImageName(url), StylesheetType, NULL, "", "")); + Curls.push_front(new StylesheetDownloadCB(url, localImageName(url), this)); } pumpCurlQueue(); } @@ -915,9 +936,9 @@ namespace NLGUI } else { - for(std::list::iterator it = Curls.begin(); it != Curls.end(); ++it) + for(std::list::iterator it = Curls.begin(); it != Curls.end(); ++it) { - if(it->data && it->data->Request == msg->easy_handle) + if((*it)->data && (*it)->data->Request == msg->easy_handle) { std::string error; bool success = msg->data.result == CURLE_OK; @@ -956,27 +977,30 @@ namespace NLGUI } // remove all queued and already started downloads - for(std::list::iterator it = Curls.begin(); it != Curls.end(); ++it) + for(std::list::iterator it = Curls.begin(); it != Curls.end(); ++it) { - if (it->data) + CDataDownload &dl = *(*it); + if (dl.data) { - LOG_DL("(%s) stop data url '%s'", _Id.c_str(), it->url.c_str()); + LOG_DL("(%s) stop data url '%s'", _Id.c_str(), dl.url.c_str()); if (MultiCurl) { - curl_multi_remove_handle(MultiCurl, it->data->Request); + curl_multi_remove_handle(MultiCurl, dl.data->Request); } // close and remove temp file - if (it->fp) + if (dl.fp) { - fclose(it->fp); + fclose(dl.fp); - if (CFile::fileExists(it->tmpdest)) + if (CFile::fileExists(dl.tmpdest)) { - CFile::deleteFile(it->tmpdest); + CFile::deleteFile(dl.tmpdest); } } } + // release CDataDownload + delete *it; } Curls.clear(); @@ -2935,10 +2959,10 @@ namespace NLGUI LOG_DL("Clear pointers to %d curls", Curls.size()); // remove image refs from downloads - for(std::list::iterator it = Curls.begin(); it != Curls.end(); ++it) + /*for(std::list::iterator it = Curls.begin(); it != Curls.end(); ++it) { it->imgs.clear(); - } + }*/ } // *************************************************************************** @@ -3691,96 +3715,98 @@ namespace NLGUI updateRefreshButton(); } - void CGroupHTML::dataDownloadFinished(bool success, const std::string &error, CDataDownload &data) + void CGroupHTML::dataDownloadFinished(bool success, const std::string &error, CDataDownload *data) { - fclose(data.fp); + fclose(data->fp); - CUrlParser uri(data.url); + CUrlParser uri(data->url); if (!uri.host.empty()) { - receiveCookies(data.data->Request, uri.host, isTrustedDomain(uri.host)); + receiveCookies(data->data->Request, uri.host, isTrustedDomain(uri.host)); } long code = -1; - curl_easy_getinfo(data.data->Request, CURLINFO_RESPONSE_CODE, &code); + curl_easy_getinfo(data->data->Request, CURLINFO_RESPONSE_CODE, &code); - LOG_DL("(%s) transfer '%p' completed with http code %d, url (len %d) '%s'", _Id.c_str(), data.data->Request, code, data.url.size(), data.url.c_str()); - curl_multi_remove_handle(MultiCurl, data.data->Request); + LOG_DL("(%s) transfer '%p' completed with http code %d, url (len %d) '%s'", _Id.c_str(), data->data->Request, code, data->url.size(), data->url.c_str()); + curl_multi_remove_handle(MultiCurl, data->data->Request); // save HSTS header from all requests regardless of HTTP code if (success) { - if (data.data->hasHSTSHeader()) + if (data->data->hasHSTSHeader()) { - CStrictTransportSecurity::getInstance()->setFromHeader(uri.host, data.data->getHSTSHeader()); + CStrictTransportSecurity::getInstance()->setFromHeader(uri.host, data->data->getHSTSHeader()); } // 2XX success, 304 Not Modified if ((code >= 200 && code <= 204) || code == 304) { CHttpCacheObject obj; - obj.Expires = data.data->getExpires(); - obj.Etag = data.data->getEtag(); - obj.LastModified = data.data->getLastModified(); + obj.Expires = data->data->getExpires(); + obj.Etag = data->data->getEtag(); + obj.LastModified = data->data->getLastModified(); - CHttpCache::getInstance()->store(data.dest, obj); - if (code == 304 && CFile::fileExists(data.tmpdest)) + CHttpCache::getInstance()->store(data->dest, obj); + if (code == 304 && CFile::fileExists(data->tmpdest)) { - CFile::deleteFile(data.tmpdest); + CFile::deleteFile(data->tmpdest); } } else if ((code >= 301 && code <= 303) || code == 307 || code == 308) { - if (data.redirects < DEFAULT_RYZOM_REDIRECT_LIMIT) + if (data->redirects < DEFAULT_RYZOM_REDIRECT_LIMIT) { - std::string location(data.data->getLocationHeader()); + std::string location(data->data->getLocationHeader()); if (!location.empty()) { CUrlParser uri(location); if (!uri.isAbsolute()) { - uri.inherit(data.url); + uri.inherit(data->url); location = uri.toString(); } + // clear old request state, and curl easy handle + delete data->data; + data->data = NULL; + data->fp = NULL; + data->url = location; + data->redirects++; + // push same request in the front of the queue // cache filename is based of original url Curls.push_front(data); - // clear old request state - Curls.front().data = NULL; - Curls.front().fp = NULL; - Curls.front().url = location; - Curls.front().redirects++; LOG_DL("Redirect '%s'", location.c_str()); // no finished callback called, so cleanup old temp - if (CFile::fileExists(data.tmpdest)) + if (CFile::fileExists(data->tmpdest)) { - CFile::deleteFile(data.tmpdest); + CFile::deleteFile(data->tmpdest); } return; } - nlwarning("Redirected to empty url '%s'", data.url.c_str()); + nlwarning("Redirected to empty url '%s'", data->url.c_str()); } else { - nlwarning("Redirect limit reached for '%s'", data.url.c_str()); + nlwarning("Redirect limit reached for '%s'", data->url.c_str()); } } else { - nlwarning("HTTP request failed with code [%d] for '%s'\n",code, data.url.c_str()); + nlwarning("HTTP request failed with code [%d] for '%s'\n",code, data->url.c_str()); // 404, 500, etc - if (CFile::fileExists(data.dest)) + if (CFile::fileExists(data->dest)) { - CFile::deleteFile(data.dest); + CFile::deleteFile(data->dest); } } } else { - nlwarning("DATA download failed '%s', error '%s'", data.url.c_str(), error.c_str()); + nlwarning("DATA download failed '%s', error '%s'", data->url.c_str(), error.c_str()); } finishCurlDownload(data); diff --git a/nel/src/gui/view_bitmap.cpp b/nel/src/gui/view_bitmap.cpp index 4186a4362..89c944a18 100644 --- a/nel/src/gui/view_bitmap.cpp +++ b/nel/src/gui/view_bitmap.cpp @@ -46,8 +46,8 @@ namespace NLGUI { CGroupHTML *groupHtml = dynamic_cast(CWidgetManager::getInstance()->getElementFromId("ui:interface:webig:content:html")); if (groupHtml) { - _HtmlDownload = false; - groupHtml->removeImageDownload(dynamic_cast(this)); + groupHtml->removeImageDownload(_HtmlDownload, dynamic_cast(this)); + _HtmlDownload = NULL; } } } @@ -476,12 +476,14 @@ namespace NLGUI if (!CFile::fileExists(localname)) localname = "web_del.tga"; _TextureId.setTexture (localname.c_str(), _TxtOffsetX, _TxtOffsetY, _TxtWidth, _TxtHeight, false); - _HtmlDownload = true; - groupHtml->addImageDownload(TxName, dynamic_cast(this)); + _HtmlDownload = groupHtml->addImageDownload(TxName, dynamic_cast(this)); } } else + { + _HtmlDownload = NULL; _TextureId.setTexture (TxName.c_str (), _TxtOffsetX, _TxtOffsetY, _TxtWidth, _TxtHeight, false); + } } // ---------------------------------------------------------------------------- From dff191a7c21faa0213f6d5af548595d8c2b542c8 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Sat, 17 Jul 2021 15:12:27 +0300 Subject: [PATCH 17/24] Move loading texture from file/memory into own functions --- nel/include/nel/gui/view_renderer.h | 3 + nel/src/gui/view_renderer.cpp | 125 +++++++++++++++++----------- 2 files changed, 79 insertions(+), 49 deletions(-) diff --git a/nel/include/nel/gui/view_renderer.h b/nel/include/nel/gui/view_renderer.h index 35d806aab..d1ad48d43 100644 --- a/nel/include/nel/gui/view_renderer.h +++ b/nel/include/nel/gui/view_renderer.h @@ -479,6 +479,9 @@ namespace NLGUI // \name Texture management // *************************************************************************** + bool loadTextureFromString(SGlobalTexture *gt, const std::string &data); + bool loadTextureFromFile(SGlobalTexture *gt, const std::string &filename); + // SImage accessors SImage *getSImage(sint32 textureId) { diff --git a/nel/src/gui/view_renderer.cpp b/nel/src/gui/view_renderer.cpp index 796479e82..0f0676cfc 100644 --- a/nel/src/gui/view_renderer.cpp +++ b/nel/src/gui/view_renderer.cpp @@ -999,6 +999,74 @@ namespace NLGUI ite->Texture = externalTexture; } + bool CViewRenderer::loadTextureFromString(CViewRenderer::SGlobalTexture *gt, const std::string &data) + { + size_t pos = data.find(";base64,"); + if (pos == std::string::npos) + { + nlwarning("Data does not have 'data:image/...;base64,...' format '%s'", data.c_str()); + return false; + } + + std::string decoded = base64::decode(data.substr(pos + 8)); + if (decoded.empty()) + { + nlwarning("base64 decoding failed '%s", data.substr(pos + 8).c_str()); + return false; + } + + CMemStream buf; + if (buf.isReading()) buf.invert(); + buf.serialBuffer((uint8 *)(decoded.data()), decoded.size()); + buf.invert(); + + CBitmap btm; + btm.load(buf); + + gt->Width = gt->DefaultWidth = btm.getWidth();; + gt->Height = gt->DefaultHeight = btm.getHeight(); + + if (gt->Width == 0 || gt->Height == 0) + { + nlwarning("Decoded image has width==0 || height==0, check image format. '%s'", data.c_str()); + return false; + } + + UTextureMem *texture = driver->createTextureMem(btm.getWidth(), btm.getHeight(), CBitmap::RGBA); + if (!texture) + { + nlwarning("Failed to create mem texture (%d,%d)", btm.getWidth(), btm.getHeight()); + return false; + } + + memcpy(texture->getPointer(), btm.getPixels().getPtr(), btm.getSize() * 4); + gt->Texture = texture; + gt->FromGlobaleTexture = false; + + return true; + } + + bool CViewRenderer::loadTextureFromFile(CViewRenderer::SGlobalTexture *gt, const std::string &filename) + { + // load new file + CIFile ifTmp; + if (ifTmp.open(filename)) + { + CBitmap::loadSize (ifTmp, gt->Width, gt->Height); + gt->DefaultWidth = gt->Width; + gt->DefaultHeight = gt->Height; + if (gt->Width == 0 || gt->Height == 0) + { + nlwarning("Failed to load the texture '%s', please check image format", filename.c_str()); + return false; + } + } + + gt->Texture = driver->createTextureFile(filename); + gt->FromGlobaleTexture = false; + + return true; + } /* * createTexture */ @@ -1030,27 +1098,20 @@ namespace NLGUI // If global texture not exists create it if (ite == _GlobalTextures.end()) { - SGlobalTexture gtTmp; - gtTmp.FromGlobaleTexture = false; string filename = CPath::lookup (sLwrGTName, false); if (filename.empty() ) return -1; - CIFile ifTmp; - if (ifTmp.open(filename)) - { - CBitmap::loadSize (ifTmp, gtTmp.Width, gtTmp.Height); - gtTmp.DefaultWidth = gtTmp.Width; - gtTmp.DefaultHeight = gtTmp.Height; - if (gtTmp.Width == 0 || gtTmp.Height == 0) - { - nlwarning("Failed to load the texture '%s', please check image format", filename.c_str()); - } - } - gtTmp.Texture = driver->createTextureFile (sLwrGTName); + + SGlobalTexture gtTmp; gtTmp.Name = sLwrGTName; + + if (!loadTextureFromFile(>Tmp, filename)) + return -1; + gtTmp.Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff); if(uploadDXTC) gtTmp.Texture->setUploadFormat(UTexture::DXTC5); gtTmp.Texture->setReleasable(bReleasable); + _GlobalTextures.push_back(gtTmp); ite = _GlobalTextures.end(); ite--; @@ -1105,44 +1166,10 @@ namespace NLGUI // If global texture not exists create it if (ite == _GlobalTextures.end()) { - std::string decoded = base64::decode(data.substr(pos + 8)); - if (decoded.empty()) - { - nlwarning("base64 decode failed '%s'", data.substr(pos + 8).c_str()); - return -1; - } - - // - CMemStream buf; - if (buf.isReading()) buf.invert(); - buf.serialBuffer((uint8 *)(decoded.data()), decoded.size()); - buf.invert(); - - CBitmap btm; - btm.load(buf); - SGlobalTexture gtTmp; - gtTmp.FromGlobaleTexture = false; - - gtTmp.Width = gtTmp.DefaultWidth = btm.getWidth();; - gtTmp.Height = gtTmp.DefaultHeight = btm.getHeight(); - - if (gtTmp.Width == 0 || gtTmp.Height == 0) - { - nlwarning("Failed to load the texture '%s', please check image format", data.c_str()); - return -1; - } - - UTextureMem *texture = driver->createTextureMem(btm.getWidth(), btm.getHeight(), CBitmap::RGBA); - if (!texture) - { - nlwarning("Failed to create mem texture (%d,%d)", btm.getWidth(), btm.getHeight()); + if (!loadTextureFromString(>Tmp, data)) return -1; - } - - memcpy(texture->getPointer(), btm.getPixels().getPtr(), btm.getSize() * 4); - gtTmp.Texture = texture; gtTmp.Name = md5hash; gtTmp.Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff); gtTmp.Texture->setReleasable(bReleasable); From a40ddf795577af54b1b7f8400d4ed44cf55f5e3a Mon Sep 17 00:00:00 2001 From: Nimetu Date: Sat, 17 Jul 2021 20:13:12 +0300 Subject: [PATCH 18/24] Methods to preallocate texture id and swap texture for existing id --- nel/include/nel/gui/view_renderer.h | 16 +++ nel/src/gui/view_renderer.cpp | 156 +++++++++++++++++++++++++++- 2 files changed, 169 insertions(+), 3 deletions(-) diff --git a/nel/include/nel/gui/view_renderer.h b/nel/include/nel/gui/view_renderer.h index d1ad48d43..d4349e83f 100644 --- a/nel/include/nel/gui/view_renderer.h +++ b/nel/include/nel/gui/view_renderer.h @@ -252,6 +252,19 @@ namespace NLGUI */ bool loadTextures (const std::string &textureFileName, const std::string &uvFileName, bool uploadDXTC); + /* + * newTextureId : Return new placeholder texture id. + * You should call deleteTexture when the texture is not used anymore. + */ + sint32 newTextureId (const std::string &name); + + /* + * reloadTexture : Replace existing global texture with new. + * If previous was texture atlas and still used by 2+ textures, + * then create new global texture. + */ + void reloadTexture (sint32 texId, const std::string &name, bool uploadDXTC=true, bool bReleasable=true); + /* * createTexture : create a texture for the interface, possibly from an externally created texture * If no external texture is given, then 'sGlobalTextureName' is the filename of the big texture @@ -312,6 +325,9 @@ namespace NLGUI /** * get a texture file pointer from a string name. O(logN) + * + * FIXME: only works with textures in atlas loaded with loadTextures() + * * \param id : the id of the texture * \return a texture file pointer. -1 if not found or if sName is empty() */ diff --git a/nel/src/gui/view_renderer.cpp b/nel/src/gui/view_renderer.cpp index 0f0676cfc..38c4a9bde 100644 --- a/nel/src/gui/view_renderer.cpp +++ b/nel/src/gui/view_renderer.cpp @@ -267,10 +267,18 @@ namespace NLGUI TGlobalTextureList::iterator ite = _GlobalTextures.begin(); while (ite != _GlobalTextures.end()) { - UTextureFile *tf = dynamic_cast(ite->Texture); - if (tf) + if (ite->Texture) { - driver->deleteTextureFile (tf); + UTextureFile *tf = dynamic_cast(ite->Texture); + if (tf) + { + driver->deleteTextureFile (tf); + } + else + { + UTextureMem *tf = dynamic_cast(ite->Texture); + if (tf) driver->deleteTextureMem(tf); + } } ite++; } @@ -1067,6 +1075,139 @@ namespace NLGUI return true; } + + sint32 CViewRenderer::newTextureId(const std::string &name) + { + SImage iTmp; + iTmp.Name = toLowerAscii(name); + iTmp.UVMin = CUV(0,0); + iTmp.UVMax = CUV(1,1); + + // lookup global texture with same name + TGlobalTextureList::iterator ite = _GlobalTextures.begin(); + while (ite != _GlobalTextures.end()) + { + std::string sText = toLowerAscii(ite->Name); + if (sText == iTmp.Name) + break; + ite++; + } + + if (ite == _GlobalTextures.end()) + { + SGlobalTexture gtTmp; + gtTmp.Name = iTmp.Name; + gtTmp.FromGlobaleTexture = false; + gtTmp.DefaultWidth = gtTmp.Width = 0; + gtTmp.DefaultHeight = gtTmp.Height = 0; + gtTmp.Texture = NULL; + _GlobalTextures.push_back(gtTmp); + ite = _GlobalTextures.end(); + ite--; + } + iTmp.GlobalTexturePtr = &(*ite); + + // allocate new texture id + return addSImage(iTmp); + } + + void CViewRenderer::reloadTexture(sint32 texId, const std::string &name, bool uploadDXTC, bool bReleasable) + { + if ((uint)texId >= _SImageIterators.size()) + { + nlwarning("Invalid texture id %d, maximum is %u", texId, _SImageIterators.size()); + return; + } + + SImage *sImage = getSImage(texId); + SGlobalTexture *gt = sImage->GlobalTexturePtr; + if (!gt) + { + nlwarning("Unknown texture id %d (file %s)", texId, name.c_str()); + return; + } + + // create new global texture if previous is atlas + if (gt->FromGlobaleTexture) + { + uint count = 0; + TSImageList::iterator ite = _SImages.begin(); + while (ite != _SImages.end() && count != 2) + { + // Same global texture ? + if (ite->GlobalTexturePtr == gt) + count++; + + ite++; + } + + // create new only when atlas is used by 2+ textures + if (count == 2) + { + SGlobalTexture gtTmp; + gtTmp.Name = toLowerAscii(name); + gtTmp.FromGlobaleTexture = false; + gtTmp.DefaultWidth = gtTmp.Width = 0; + gtTmp.DefaultHeight = gtTmp.Height = 0; + gtTmp.Texture = NULL; + _GlobalTextures.push_back(gtTmp); + + TGlobalTextureList::iterator ite = _GlobalTextures.end(); + ite--; + + sImage->GlobalTexturePtr = &(*ite); + gt = sImage->GlobalTexturePtr; + } + } + + NL3D::UTexture *oldTexture = gt->Texture; + + std::string sLwrGTName; + if (startsWith(name, "data:image/")) + { + if (!loadTextureFromString(gt, name)) + return; + + sLwrGTName = getMD5((uint8 *)name.c_str(), (uint32)name.size()).toString(); + } + else + { + sLwrGTName = toLowerAscii(name); + std::string filename = CPath::lookup(sLwrGTName, false); + if (filename.empty()) + { + nlwarning("Unable to find file '%s for texture %d", name.c_str(), texId); + return; + } + + if (!loadTextureFromFile(gt, filename)) + { + nlwarning("Unable to load texture from file '%s'", filename.c_str()); + return; + } + } + + gt->Name = sLwrGTName; + gt->Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff); + gt->Texture->setUploadFormat(uploadDXTC ? UTexture::DXTC5 : UTexture::Auto); + gt->Texture->setReleasable(bReleasable); + + // release previous only after successfully loading new one + if (oldTexture) + { + UTextureFile *tf = dynamic_cast(oldTexture); + if (tf) + { + driver->deleteTextureFile (tf); + } + else + { + UTextureMem *tf = dynamic_cast(oldTexture); + if (tf) driver->deleteTextureMem(tf); + } + } + } + /* * createTexture */ @@ -1285,6 +1426,8 @@ namespace NLGUI // This one ? if (&(*iteGT) == gt) { + if (iteGT->Texture == NULL) + return; // Remove this global texture UTextureFile *tf = dynamic_cast(iteGT->Texture); if (tf) @@ -1466,6 +1609,13 @@ namespace NLGUI TGlobalTextureList::iterator ite = _GlobalTextures.begin(); while (ite != _GlobalTextures.end()) { + // texture not loaded yet + if (ite->Texture == NULL) + { + ++ite; + continue; + } + // TMP TMP // volatile SGlobalTexture *sg = &(*ite); CLayer &layer= ite->Layers[layerId]; From 285cfb163ff32316ba727ecc7a1fc6ba38f062ee Mon Sep 17 00:00:00 2001 From: Nimetu Date: Tue, 7 Sep 2021 12:08:32 +0300 Subject: [PATCH 19/24] Fix possible deadlock in css content attribute --- nel/src/gui/group_html.cpp | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/nel/src/gui/group_html.cpp b/nel/src/gui/group_html.cpp index 0c48b92a8..7432cd9b7 100644 --- a/nel/src/gui/group_html.cpp +++ b/nel/src/gui/group_html.cpp @@ -1296,6 +1296,7 @@ namespace NLGUI } std::string::size_type pos = 0; + // TODO: tokenize by whitespace while(pos < content.size()) { std::string::size_type start; @@ -1328,6 +1329,9 @@ namespace NLGUI start = pos + 4; // fails if url contains ')' pos = content.find(")", start); + if (pos == std::string::npos) + break; + token = trim(content.substr(start, pos - start)); // skip ')' pos++; @@ -1390,14 +1394,22 @@ namespace NLGUI { // attr(title) start = pos + 5; - pos = content.find(")", start); - token = content.substr(start, pos - start); - // skip ')' - pos++; - - if (elm.hasAttribute(token)) + std::string::size_type end = 0; + end = content.find(")", start); + if (end != std::string::npos) { - addString(elm.getAttribute(token)); + token = content.substr(start, end - start); + // skip ')' + pos = end + 1; + if (elm.hasAttribute(token)) + { + addString(elm.getAttribute(token)); + } + } + else + { + // skip over 'a' + pos++; } } else From 12c515c2648a7f573cd28feabe9fed5dee4cb9a8 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Tue, 13 Jul 2021 14:44:22 +0300 Subject: [PATCH 20/24] Parse background style into own class --- nel/include/nel/gui/css_style.h | 7 +++-- nel/src/gui/css_style.cpp | 38 +++++++++++++++++++++---- nel/src/gui/group_html.cpp | 24 ++++++++-------- nel/tools/htmlcss_test/htmlcss_test.cpp | 2 +- 4 files changed, 49 insertions(+), 22 deletions(-) diff --git a/nel/include/nel/gui/css_style.h b/nel/include/nel/gui/css_style.h index 664d13c64..990b24e20 100644 --- a/nel/include/nel/gui/css_style.h +++ b/nel/include/nel/gui/css_style.h @@ -21,6 +21,8 @@ #include "nel/misc/rgba.h" #include "nel/gui/css_selector.h" #include "nel/gui/css_types.h" +#include "nel/gui/css_length.h" +#include "nel/gui/css_background.h" namespace NLGUI { @@ -70,7 +72,6 @@ namespace NLGUI BorderTopStyle = BorderRightStyle = BorderBottomStyle = BorderLeftStyle = CSS_LINE_STYLE_NONE; BorderTopColor = BorderRightColor = BorderBottomColor = BorderLeftColor = NLMISC::CRGBA::Transparent; // background - BackgroundColor=NLMISC::CRGBA::Black; BackgroundColorOver=NLMISC::CRGBA::Black; MarginTop = MarginRight = MarginBottom = MarginLeft = 0; PaddingTop = PaddingRight = PaddingBottom = PaddingLeft = 0; @@ -105,7 +106,7 @@ namespace NLGUI uint32 BorderTopWidth, BorderRightWidth, BorderBottomWidth, BorderLeftWidth; CSSLineStyle BorderTopStyle, BorderRightStyle, BorderBottomStyle, BorderLeftStyle; NLMISC::CRGBA BorderTopColor, BorderRightColor, BorderBottomColor, BorderLeftColor; - NLMISC::CRGBA BackgroundColor; + CSSBackground Background; NLMISC::CRGBA BackgroundColorOver; uint32 MarginTop, MarginRight, MarginBottom, MarginLeft; uint32 PaddingTop, PaddingRight, PaddingBottom, PaddingLeft; @@ -229,7 +230,7 @@ namespace NLGUI Current.BorderTopStyle = Current.BorderRightStyle = Current.BorderBottomStyle = Current.BorderLeftStyle = CSS_LINE_STYLE_NONE; Current.BorderTopColor = Current.BorderRightColor = Current.BorderBottomColor = Current.BorderLeftColor = Current.TextColor; - Current.BackgroundColor = NLMISC::CRGBA::Transparent; + Current.Background = CSSBackground(); Current.BackgroundColorOver = NLMISC::CRGBA::Transparent; Current.MarginTop = Current.MarginRight = Current.MarginBottom = Current.MarginLeft = 0; diff --git a/nel/src/gui/css_style.cpp b/nel/src/gui/css_style.cpp index c7dae35ad..7a9b725af 100644 --- a/nel/src/gui/css_style.cpp +++ b/nel/src/gui/css_style.cpp @@ -957,13 +957,13 @@ namespace NLGUI if (it->first == "background-color") { if (it->second == "inherit") - style.BackgroundColor = current.BackgroundColor; + style.Background.color = current.Background.color; else if (it->second == "transparent") - style.BackgroundColor = CRGBA(0, 0, 0, 0); + style.Background.color = CRGBA(0, 0, 0, 0); else if (it->second == "currentcolor") - style.BackgroundColorOver = style.TextColor; + style.Background.color = style.TextColor; else - scanHTMLColor(it->second.c_str(), style.BackgroundColor); + scanHTMLColor(it->second.c_str(), style.Background.color); } else if (it->first == "-ryzom-background-color-over") @@ -987,10 +987,13 @@ namespace NLGUI image = image.substr(4, image.size()-5); } style.StyleRules[it->first] = trimQuotes(image); + style.Background.setImage(style.StyleRules[it->first]); } else if (it->first == "background-repeat") { + style.Background.setRepeat(it->second); + // TODO: remove after removing old code that depends on this // normalize std::string val = toLowerAscii(trim(it->second)); std::vector parts; @@ -1004,6 +1007,8 @@ namespace NLGUI else if (it->first == "background-size") { + style.Background.setSize(it->second); + // TODO: remove after removing old code that depends on this // normalize std::string val = toLowerAscii(trim(it->second)); std::vector parts; @@ -1013,6 +1018,27 @@ namespace NLGUI style.StyleRules[it->first] = val; } + else + if (it->first == "background-position") + { + // TODO: background-position-x, background-position-y + style.Background.setPosition(it->second); + } + else + if (it->first == "background-origin") + { + style.Background.setOrigin(it->second); + } + else + if (it->first == "background-clip") + { + style.Background.setClip(it->second); + } + else + if (it->first == "background-attachment") + { + style.Background.setAttachment(it->second); + } } // if outer element has underline set, then inner element cannot remove it @@ -1348,10 +1374,10 @@ namespace NLGUI } else { - // fill in default if one is set + // fill in default if one is not set if (props[i] == "background-image") { - style[props[i]] = "none"; + style[props[i]] = ""; } else if (props[i] == "background-position") { diff --git a/nel/src/gui/group_html.cpp b/nel/src/gui/group_html.cpp index 7432cd9b7..8a56832c9 100644 --- a/nel/src/gui/group_html.cpp +++ b/nel/src/gui/group_html.cpp @@ -538,14 +538,14 @@ namespace NLGUI if (style.hasStyle("background-color")) { - ctrlButton->setColor(style.BackgroundColor); + ctrlButton->setColor(style.Background.color); if (style.hasStyle("-ryzom-background-color-over")) { ctrlButton->setColorOver(style.BackgroundColorOver); } else { - ctrlButton->setColorOver(style.BackgroundColor); + ctrlButton->setColorOver(style.Background.color); } ctrlButton->setTexture("", "blank.tga", "", false); ctrlButton->setTextureOver("", "blank.tga", ""); @@ -2706,7 +2706,7 @@ namespace NLGUI if (bg) { bg->setTexture("blank.tga"); - bg->setColor(style.BackgroundColor); + bg->setColor(style.Background.color); } } } @@ -4082,7 +4082,7 @@ namespace NLGUI clearContext(); // Reset default background color - setBackgroundColor (_BrowserStyle.Current.BackgroundColor); + setBackgroundColor (_BrowserStyle.Current.Background.color); setBackground ("blank.tga", true, false); paragraphChange (); @@ -4940,7 +4940,7 @@ namespace NLGUI style.pushStyle(); style.applyStyle(elm.getPseudo(":-webkit-meter-bar")); if(style.hasStyle("background-color")) - color = style.Current.BackgroundColor; + color = style.Current.Background.color; style.popStyle(); return color; @@ -4957,14 +4957,14 @@ namespace NLGUI { style.applyStyle(elm.getPseudo(":-webkit-meter-optimum-value")); if (style.hasStyle("background-color")) - color = style.Current.BackgroundColor; + color = style.Current.Background.color; break; } case VALUE_SUB_OPTIMAL: { style.applyStyle(elm.getPseudo(":-webkit-meter-suboptimum-value")); if (style.hasStyle("background-color")) - color = style.Current.BackgroundColor; + color = style.Current.Background.color; break; } case VALUE_EVEN_LESS_GOOD: // fall through @@ -4972,7 +4972,7 @@ namespace NLGUI { style.applyStyle(elm.getPseudo(":-webkit-meter-even-less-good-value")); if (style.hasStyle("background-color")) - color = style.Current.BackgroundColor; + color = style.Current.Background.color; break; } }//switch @@ -5009,7 +5009,7 @@ namespace NLGUI style.pushStyle(); style.applyStyle(elm.getPseudo(":-webkit-progress-bar")); if (style.hasStyle("background-color")) - color = style.Current.BackgroundColor; + color = style.Current.Background.color; style.popStyle(); return color; @@ -5023,7 +5023,7 @@ namespace NLGUI style.pushStyle(); style.applyStyle(elm.getPseudo(":-webkit-progress-value")); if (style.hasStyle("background-color")) - color = style.Current.BackgroundColor; + color = style.Current.Background.color; style.popStyle(); return color; @@ -5037,7 +5037,7 @@ namespace NLGUI cellParams = _CellParams.back(); if (_Style.hasStyle("background-color")) - cellParams.BgColor = _Style.Current.BackgroundColor; + cellParams.BgColor = _Style.Current.Background.color; else if (elm.hasNonEmptyAttribute("bgcolor")) scanHTMLColor(elm.getAttribute("bgcolor").c_str(), cellParams.BgColor); @@ -5120,7 +5120,7 @@ namespace NLGUI if (_Style.hasStyle("background-color")) { - CRGBA bgColor = _Style.Current.BackgroundColor; + CRGBA bgColor = _Style.Current.Background.color; scanHTMLColor(elm.getAttribute("bgcolor").c_str(), bgColor); if (root) { diff --git a/nel/tools/htmlcss_test/htmlcss_test.cpp b/nel/tools/htmlcss_test/htmlcss_test.cpp index 4e5b79843..563f5add9 100644 --- a/nel/tools/htmlcss_test/htmlcss_test.cpp +++ b/nel/tools/htmlcss_test/htmlcss_test.cpp @@ -46,7 +46,7 @@ void checkRuleset(CHtmlElement &elm, CCssStyle &style, TStyleVec testset, bool e } else if (it.first == "background-color") { - printf("[%s]: background-color: '%s'; expected '%s'\n", existsMessage.c_str(), style.Current.BackgroundColor.toString().c_str(), it.second.c_str()); + printf("[%s]: background-color: '%s'; expected '%s'\n", existsMessage.c_str(), style.Current.Background.color.toString().c_str(), it.second.c_str()); printf(" (%s)\n", elm.toString().c_str()); failed2 = false; } From 2046c4bf6e33e6b1064b5df46b743d7a47d4af36 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Tue, 13 Jul 2021 14:39:08 +0300 Subject: [PATCH 21/24] Draw browser background with CSSBackgroundRenderer --- nel/include/nel/gui/group_html.h | 32 ++++- nel/src/gui/group_html.cpp | 236 ++++++++++++++++++++----------- 2 files changed, 183 insertions(+), 85 deletions(-) diff --git a/nel/include/nel/gui/group_html.h b/nel/include/nel/gui/group_html.h index c94bec024..4315d8059 100644 --- a/nel/include/nel/gui/group_html.h +++ b/nel/include/nel/gui/group_html.h @@ -30,6 +30,7 @@ #include "nel/gui/html_element.h" #include "nel/gui/html_parser.h" #include "nel/gui/css_style.h" +#include "nel/gui/css_background_renderer.h" // forward declaration typedef void CURLM; @@ -148,6 +149,7 @@ namespace NLGUI // add image download (used by view_bitmap.cpp to load web images) ICurlDownloadCB *addImageDownload(const std::string &url, CViewBase *img, const CStyleParams &style = CStyleParams(), const TImageType type = NormalImage, const std::string &placeholder = "web_del.tga"); + ICurlDownloadCB *addTextureDownload(const std::string &url, sint32 &texId, CViewBase *view); void removeImageDownload(ICurlDownloadCB *handle, CViewBase *img); std::string localImageName(const std::string &url); @@ -178,6 +180,7 @@ namespace NLGUI std::string DefaultRadioButtonBitmapNormal; std::string DefaultRadioButtonBitmapPushed; std::string DefaultRadioButtonBitmapOver; + // TODO: remove from interface xml and code std::string DefaultBackgroundBitmapView; struct TFormField { @@ -342,6 +345,9 @@ namespace NLGUI const std::string &overBitmap, const char *actionHandler, const char *actionHandlerParams, const std::string &tooltip, const CStyleParams &style = CStyleParams()); + // Set the background color + void setupBackground(CSSBackgroundRenderer *bg); + // Set the background color void setBackgroundColor (const NLMISC::CRGBA &bgcolor); @@ -381,6 +387,10 @@ namespace NLGUI //