diff --git a/nel/include/nel/gui/css_border.h b/nel/include/nel/gui/css_border.h new file mode 100644 index 000000000..787b71710 --- /dev/null +++ b/nel/include/nel/gui/css_border.h @@ -0,0 +1,72 @@ +// 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_BORDER_H +#define CL_CSS_BORDER_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 border info + * \date 2021-07-23 09:51 GMT + * \author Meelis Mägi (Nimetu) + */ + class CSSBorder + { + public: + CSSBorder() + { + reset(); + } + + CSSBorder(uint width, CSSLineStyle style, NLMISC::CRGBA color) + { + set(width, style, color); + } + + void reset() + { + set(CSS_LINE_WIDTH_MEDIUM, CSS_LINE_STYLE_NONE, NLMISC::CRGBA::Transparent); + } + + void set(uint width, CSSLineStyle style, NLMISC::CRGBA color) + { + Width.setFloatValue(width, "px"); + Style = style; + Color = color; + } + + bool empty() const + { + return Style == CSS_LINE_STYLE_NONE || Style == CSS_LINE_STYLE_HIDDEN + || Width.getFloat() == 0; + } + + CSSLength Width; + CSSLineStyle Style; + NLMISC::CRGBA Color; + }; + +}//namespace + +#endif // CL_CSS_BORDER_H + + diff --git a/nel/include/nel/gui/css_border_renderer.h b/nel/include/nel/gui/css_border_renderer.h index b70f1c169..54505a686 100644 --- a/nel/include/nel/gui/css_border_renderer.h +++ b/nel/include/nel/gui/css_border_renderer.h @@ -23,9 +23,12 @@ #include "nel/misc/rgba.h" #include "nel/misc/geom_ext.h" #include "nel/gui/css_types.h" +#include "nel/gui/css_border.h" namespace NLGUI { + class CInterfaceElement; + /** * \brief Border renderer for GUI classes * \date 2019-09-03 10:50 GMT @@ -34,6 +37,22 @@ namespace NLGUI class CSSBorderRenderer { private: + enum EBorderSide + { + BORDER_TOP_LEFT = 0, + BORDER_TOP_RIGHT, + BORDER_BOTTOM_LEFT, + BORDER_BOTTOM_RIGHT, + BORDER_LEFT_TOP, + BORDER_RIGHT_TOP, + BORDER_LEFT_BOTTOM, + BORDER_RIGHT_BOTTOM, + BORDER_TOP, + BORDER_RIGHT, + BORDER_BOTTOM, + BORDER_LEFT, + BORDER_INVALID + }; // parent element screen coordinates sint32 m_XReal, m_YReal; sint32 m_WReal, m_HReal; @@ -50,15 +69,44 @@ namespace NLGUI // if true, then updateCoords() is called from draw() bool m_Dirty; - // if true, then at least one border is set - bool m_Border; - bool m_BorderTop, m_BorderRight, m_BorderBottom, m_BorderLeft; + // UI scale, used to calculate number of segments to draw for circle + float m_Scale; - public: - uint32 TopWidth, RightWidth, BottomWidth, LeftWidth; - NLMISC::CRGBA TopColor, RightColor, BottomColor, LeftColor; - CSSLineStyle TopStyle, RightStyle, BottomStyle, LeftStyle; + CSSRect m_Border; + CSSRect m_Computed; + + // font size for 'rem' + float m_RootFontSize; + + // font size for 'em' + float m_FontSize; + + // if true, then CSSLength values are recomputed + bool m_MustComputeValues; + + // viewport element for vw,wh,vmin,vmax + CInterfaceElement* m_Viewport; + + // update CSSLength values + void computeValues(); + + void getAdjacentBorders(EBorderSide side, EBorderSide &adjBorderL, EBorderSide &adjBorderR) const; + void getAdjacentBorderWidth(EBorderSide side, sint32 &adjWidthL, sint32 &adjWidthR) const; + // dot + void buildDotCornerStart(EBorderSide side, SDrawBorder shape, float x1, float y1, float radius); + void buildDotCornerEnd(EBorderSide side, SDrawBorder shape, float x1, float y1, float radius); + void buildDotCorner(SDrawBorder shape, float x, float y, float r, const NLMISC::CLine &line); + // draw circle, angle is CCW between 0..1 (3'o'clock being 0deg). + void buildDotSegments(SDrawBorder shape, float x, float y, float radius, float fromAngle=0.f, float toAngle=1.f); + // dash + void makeBorderQuad(EBorderSide side, SDrawBorder &shape, float x, float y, float width, float thickness) const; + void makeCornerQuad(EBorderSide side, SDrawBorder &shape) const; + void buildDashedBorder(EBorderSide side); + void buildSolidBorder(EBorderSide side); + + bool hasInnerShape(CSSLineStyle style) const; + public: // alpha value from parent uint8 CurrentAlpha; @@ -70,23 +118,67 @@ namespace NLGUI void setRect(sint32 x, sint32 y, sint32 w, sint32 h); - void setWidth(uint32 top, uint32 right, uint32 bottom, uint32 left); - void setStyle(CSSLineStyle top, CSSLineStyle right, CSSLineStyle bottom, CSSLineStyle left); - void setColor(const NLMISC::CRGBA &top, const NLMISC::CRGBA &right, const NLMISC::CRGBA &bottom, const NLMISC::CRGBA &left); + void setBorder(const CSSRect &b) { m_Dirty = true; m_Border = b; } void updateCoords(); - void invalidateCoords() { m_Dirty = m_Border = true; } - void invalidateContent() { m_Dirty = m_Border = true; }; + void invalidateCoords() { m_Dirty = true; } + void invalidateContent() { m_Dirty = true; } - uint32 getTopWidth() const; - uint32 getRightWidth() const; - uint32 getBottomWidth() const; - uint32 getLeftWidth() const; + bool isEmpty() const { + return (m_Border.Top.Width.getFloat() + + m_Border.Right.Width.getFloat() + + m_Border.Bottom.Width.getFloat() + + m_Border.Left.Width.getFloat()) == 0; + } - uint32 getLeftRightWidth() const; - uint32 getTopBottomWidth() const; + uint32 getTopWidth() { if (m_MustComputeValues) computeValues(); return m_Computed.Top; } + uint32 getRightWidth() { if (m_MustComputeValues) computeValues(); return m_Computed.Right; } + uint32 getBottomWidth() { if (m_MustComputeValues) computeValues(); return m_Computed.Bottom; } + uint32 getLeftWidth() { if (m_MustComputeValues) computeValues(); return m_Computed.Left; } - bool hasInnerShape(CSSLineStyle style) const; + uint32 getLeftRightWidth() { if (m_MustComputeValues) computeValues(); return m_Computed.Left + m_Computed.Right; } + uint32 getTopBottomWidth() { if (m_MustComputeValues) computeValues(); return m_Computed.Top + m_Computed.Bottom; } + + NLMISC::CRGBA getTopColor() const { return m_Border.Top.Color; } + NLMISC::CRGBA getRightColor() const { return m_Border.Right.Color; } + NLMISC::CRGBA getBottomColor() const { return m_Border.Bottom.Color; } + NLMISC::CRGBA getLeftColor() const { return m_Border.Left.Color; } + + + void setWidth(uint width) + { + m_Dirty = true; + m_MustComputeValues = true; + m_Border.Top.Width.setFloatValue(width, "px"); + m_Border.Right.Width.setFloatValue(width, "px"); + m_Border.Bottom.Width.setFloatValue(width, "px"); + m_Border.Left.Width.setFloatValue(width, "px"); + } + + void setColor(const NLMISC::CRGBA &color) + { + m_Dirty = true; + m_Border.Top.Color = color; + m_Border.Right.Color = color; + m_Border.Bottom.Color = color; + m_Border.Left.Color = color; + } + + // sizes for em, rem units + void setFontSize(float rootFontSize, float fontSize) + { + m_Dirty = true; + m_MustComputeValues = true; + m_RootFontSize = rootFontSize; + m_FontSize = fontSize; + } + + void setViewport(CInterfaceElement *root) + { + m_Dirty = true; + m_MustComputeValues = true; + m_Viewport = root; + } void draw(); }; // CSSBorderRenderer diff --git a/nel/include/nel/gui/css_style.h b/nel/include/nel/gui/css_style.h index 990b24e20..042486d17 100644 --- a/nel/include/nel/gui/css_style.h +++ b/nel/include/nel/gui/css_style.h @@ -23,6 +23,7 @@ #include "nel/gui/css_types.h" #include "nel/gui/css_length.h" #include "nel/gui/css_background.h" +#include "nel/gui/css_border.h" namespace NLGUI { @@ -67,10 +68,6 @@ namespace NLGUI Height=-1; MaxWidth=-1; MaxHeight=-1; - // border style - BorderTopWidth = BorderRightWidth = BorderBottomWidth = BorderLeftWidth = CSS_LINE_WIDTH_MEDIUM; - BorderTopStyle = BorderRightStyle = BorderBottomStyle = BorderLeftStyle = CSS_LINE_STYLE_NONE; - BorderTopColor = BorderRightColor = BorderBottomColor = BorderLeftColor = NLMISC::CRGBA::Transparent; // background BackgroundColorOver=NLMISC::CRGBA::Black; MarginTop = MarginRight = MarginBottom = MarginLeft = 0; @@ -103,9 +100,7 @@ namespace NLGUI sint32 Height; sint32 MaxWidth; sint32 MaxHeight; - uint32 BorderTopWidth, BorderRightWidth, BorderBottomWidth, BorderLeftWidth; - CSSLineStyle BorderTopStyle, BorderRightStyle, BorderBottomStyle, BorderLeftStyle; - NLMISC::CRGBA BorderTopColor, BorderRightColor, BorderBottomColor, BorderLeftColor; + CSSRect Border; CSSBackground Background; NLMISC::CRGBA BackgroundColorOver; uint32 MarginTop, MarginRight, MarginBottom, MarginLeft; @@ -187,7 +182,7 @@ namespace NLGUI void expandShorthand(const std::string &prop, const std::string &value, TStyle &style) const; // parse string value into corresponding value - void applyBorderWidth(const std::string &value, uint32 *dest, const uint32 currentWidth, const uint32 fontSize) const; + void applyBorderWidth(const std::string &value, CSSLength *dest, const CSSLength ¤tWidth) const; 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; @@ -226,9 +221,10 @@ namespace NLGUI Current.MaxWidth=-1; Current.MaxHeight=-1; - 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.Border.Top.reset(); + Current.Border.Right.reset(); + Current.Border.Bottom.reset(); + Current.Border.Left.reset(); Current.Background = CSSBackground(); Current.BackgroundColorOver = NLMISC::CRGBA::Transparent; diff --git a/nel/include/nel/gui/css_types.h b/nel/include/nel/gui/css_types.h index ac59af422..172c21d18 100644 --- a/nel/include/nel/gui/css_types.h +++ b/nel/include/nel/gui/css_types.h @@ -85,6 +85,15 @@ namespace NLGUI CSS_VALUE_CONTAIN }; + template + struct CSSRect + { + T Top; + T Right; + T Bottom; + T Left; + }; + }//namespace #endif // CL_CSS_TYPES_H diff --git a/nel/src/gui/css_border_renderer.cpp b/nel/src/gui/css_border_renderer.cpp index aa44073f4..cb2ef9709 100644 --- a/nel/src/gui/css_border_renderer.cpp +++ b/nel/src/gui/css_border_renderer.cpp @@ -32,21 +32,24 @@ namespace NLGUI // ---------------------------------------------------------------------------- CSSBorderRenderer::CSSBorderRenderer() { - TopWidth = RightWidth = BottomWidth = LeftWidth = 0; - TopColor = RightColor = BottomColor = LeftColor = CRGBA(128, 128, 128, 255); - TopStyle = RightStyle = BottomStyle = LeftStyle = CSS_LINE_STYLE_SOLID; CurrentAlpha = 255; + m_Scale = 1.f; m_RenderLayer = 0; m_ModulateGlobalColor = false; - m_Border = true; m_Dirty = true; - m_BorderTop = m_BorderRight = m_BorderBottom = m_BorderLeft = false; + m_MustComputeValues = true; m_XReal = 0; m_YReal = 0; m_WReal = 0; m_HReal = 0; + + m_Viewport = NULL; + m_FontSize = 16.f; + m_RootFontSize = 16.f; + + m_Computed.Top = m_Computed.Right = m_Computed.Bottom = m_Computed.Left = 0; } // ---------------------------------------------------------------------------- @@ -69,304 +72,662 @@ namespace NLGUI m_WReal = w; m_HReal = h; - m_Dirty = m_Border = (w > 0 && h > 0); - } - - // ---------------------------------------------------------------------------- - void CSSBorderRenderer::setWidth(uint32 top, uint32 right, uint32 bottom, uint32 left) - { - TopWidth = top; - RightWidth = right; - BottomWidth = bottom; - LeftWidth = left; - - m_Dirty = m_Border = (top > 0 || right > 0 || bottom > 0 || left > 0); - } - - // ---------------------------------------------------------------------------- - void CSSBorderRenderer::setStyle(CSSLineStyle top, CSSLineStyle right, CSSLineStyle bottom, CSSLineStyle left) - { - TopStyle = top; - RightStyle = right; - BottomStyle = bottom; - LeftStyle = left; - - m_Dirty = m_Border = true; + m_Dirty = (w > 0 && h > 0); } // ---------------------------------------------------------------------------- - void CSSBorderRenderer::setColor(const NLMISC::CRGBA &top, const NLMISC::CRGBA &right, const NLMISC::CRGBA &bottom, const NLMISC::CRGBA &left) - { - TopColor = top; - RightColor = right; - BottomColor = bottom; - LeftColor = left; - - m_Dirty = true; - } - - // ---------------------------------------------------------------------------- - uint32 CSSBorderRenderer::getTopWidth() const + bool CSSBorderRenderer::hasInnerShape(CSSLineStyle style) const { - if (TopStyle == CSS_LINE_STYLE_NONE || TopStyle == CSS_LINE_STYLE_HIDDEN) - return 0; - - return TopWidth; + return style == CSS_LINE_STYLE_DOUBLE || + style == CSS_LINE_STYLE_GROOVE || + style == CSS_LINE_STYLE_RIDGE; } // ---------------------------------------------------------------------------- - uint32 CSSBorderRenderer::getRightWidth() const + void CSSBorderRenderer::computeValues() { - if (RightStyle == CSS_LINE_STYLE_NONE || RightStyle == CSS_LINE_STYLE_HIDDEN) - return 0; - - return RightWidth; - } + m_MustComputeValues = false; - // ---------------------------------------------------------------------------- - uint32 CSSBorderRenderer::getBottomWidth() const - { - if (BottomStyle == CSS_LINE_STYLE_NONE || BottomStyle == CSS_LINE_STYLE_HIDDEN) - return 0; + // TODO :should save as computed value + sint32 vpW=0; + sint32 vpH=0; + if (m_Viewport) + { + vpW = m_Viewport->getWReal(); + vpH = m_Viewport->getHReal(); + } - return BottomWidth; + m_Computed.Top = m_Border.Top.empty() ? 0 : m_Border.Top.Width.calculate(0, m_FontSize, m_RootFontSize, vpW, vpH); + m_Computed.Right = m_Border.Right.empty() ? 0 : m_Border.Right.Width.calculate(0, m_FontSize, m_RootFontSize, vpW, vpH); + m_Computed.Bottom = m_Border.Bottom.empty() ? 0 : m_Border.Bottom.Width.calculate(0, m_FontSize, m_RootFontSize, vpW, vpH); + m_Computed.Left = m_Border.Left.empty() ? 0 : m_Border.Left.Width.calculate(0, m_FontSize, m_RootFontSize, vpW, vpH); } // ---------------------------------------------------------------------------- - uint32 CSSBorderRenderer::getLeftWidth() const + void CSSBorderRenderer::getAdjacentBorders(EBorderSide side, EBorderSide &adjBorderL, EBorderSide &adjBorderR) const { - if (LeftStyle == CSS_LINE_STYLE_NONE || LeftStyle == CSS_LINE_STYLE_HIDDEN) - return 0; - - return LeftWidth; + switch(side) + { + case BORDER_TOP: + adjBorderL = BORDER_TOP_LEFT; + adjBorderR = BORDER_TOP_RIGHT; + break; + case BORDER_RIGHT: + adjBorderL = BORDER_RIGHT_BOTTOM; + adjBorderR = BORDER_RIGHT_TOP; + break; + case BORDER_BOTTOM: + adjBorderL = BORDER_BOTTOM_LEFT; + adjBorderR = BORDER_BOTTOM_RIGHT; + break; + case BORDER_LEFT: + adjBorderL = BORDER_LEFT_BOTTOM; + adjBorderR = BORDER_LEFT_TOP; + break; + default: + adjBorderL = adjBorderR = BORDER_INVALID; + break; + } } // ---------------------------------------------------------------------------- - uint32 CSSBorderRenderer::getLeftRightWidth() const + void CSSBorderRenderer::getAdjacentBorderWidth(EBorderSide side, sint32 &adjWidthL, sint32 &adjWidthR) const { - return getLeftWidth() + getRightWidth(); + switch(side) + { + case BORDER_TOP: + case BORDER_BOTTOM: + adjWidthL = m_Computed.Left; + adjWidthR = m_Computed.Right; + break; + case BORDER_LEFT: + case BORDER_RIGHT: + adjWidthL = m_Computed.Bottom; + adjWidthR = m_Computed.Top; + break; + default: + adjWidthL = adjWidthR = 0; + break; + } } // ---------------------------------------------------------------------------- - uint32 CSSBorderRenderer::getTopBottomWidth() const + void CSSBorderRenderer::buildDotCornerStart(EBorderSide side, SDrawBorder shape, float x1, float y1, float radius) { - return getTopWidth() + getBottomWidth(); + NLMISC::CLine line; + switch(side) + { + case BORDER_TOP: + line.V0.set(m_XReal + m_Computed.Left, m_YReal + m_HReal - m_Computed.Top, 0); + line.V1.set(m_XReal, m_YReal + m_HReal, 0); + buildDotCorner(shape, x1, y1, radius, line); + break; + case BORDER_RIGHT: + line.V0.set(m_XReal + m_WReal, m_YReal, 0); + line.V1.set(m_XReal + m_WReal - m_Computed.Right, m_YReal + m_Computed.Bottom, 0); + buildDotCorner(shape, x1, y1, radius, line); + break; + case BORDER_BOTTOM: + line.V0.set(m_XReal, m_YReal, 0); + line.V1.set(m_XReal + m_Computed.Left, m_YReal + m_Computed.Bottom, 0); + buildDotCorner(shape, x1, y1, radius, line); + break; + case BORDER_LEFT: + line.V0.set(m_XReal + m_Computed.Left, m_YReal + m_Computed.Bottom, 0); + line.V1.set(m_XReal, m_YReal, 0); + buildDotCorner(shape, x1, y1, radius, line); + break; + } } // ---------------------------------------------------------------------------- - bool CSSBorderRenderer::hasInnerShape(CSSLineStyle style) const + void CSSBorderRenderer::buildDotCornerEnd(EBorderSide side, SDrawBorder shape, float x1, float y1, float radius) { - return style == CSS_LINE_STYLE_DOUBLE || - style == CSS_LINE_STYLE_GROOVE || - style == CSS_LINE_STYLE_RIDGE; + NLMISC::CLine line; + switch(side) + { + case BORDER_TOP: + line.V0.set(m_XReal + m_WReal, m_YReal + m_HReal, 0); + line.V1.set(m_XReal + m_WReal - m_Computed.Right, m_YReal + m_HReal - m_Computed.Top, 0); + buildDotCorner(shape, x1, y1, radius, line); + break; + case BORDER_RIGHT: + line.V0.set(m_XReal + m_WReal - m_Computed.Right, m_YReal + m_HReal - m_Computed.Top, 0); + line.V1.set(m_XReal + m_WReal, m_YReal + m_HReal, 0); + buildDotCorner(shape, x1, y1, radius, line); + break; + case BORDER_BOTTOM: + line.V0.set(m_XReal + m_WReal - m_Computed.Right, m_YReal + m_Computed.Bottom, 0); + line.V1.set(m_XReal + m_WReal, m_YReal, 0); + buildDotCorner(shape, x1, y1, radius, line); + break; + case BORDER_LEFT: + line.V0.set(m_XReal, m_YReal + m_HReal, 0); + line.V1.set(m_XReal + m_Computed.Left, m_YReal + m_HReal - m_Computed.Top, 0); + buildDotCorner(shape, x1, y1, radius, line); + break; + } } // ---------------------------------------------------------------------------- - void CSSBorderRenderer::updateCoords() + void CSSBorderRenderer::buildDashedBorder(EBorderSide side) { - m_Dirty = false; - m_DrawBorders.clear(); - if (!m_Border) return; + CSSBorder border; - 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; + float x, y; + bool horizontal; + sint32 width, thickness; + switch(side) + { + case BORDER_TOP: + horizontal = true; + border = m_Border.Top; + width = m_WReal; + thickness = m_Computed.Top; + x = m_XReal; + y = m_YReal + m_HReal - thickness; + break; + case BORDER_RIGHT: + horizontal = false; + border = m_Border.Right; + width = m_HReal; + thickness = m_Computed.Right; + x = m_XReal + m_WReal - thickness; + y = m_YReal; + break; + case BORDER_BOTTOM: + horizontal = true; + border = m_Border.Bottom; + width = m_WReal; + thickness = m_Computed.Bottom; + x = m_XReal; + y = m_YReal; + break; + case BORDER_LEFT: + horizontal = false; + border = m_Border.Left; + width = m_HReal; + thickness = m_Computed.Left; + x = m_XReal; + y = m_YReal; + break; + default: + return; + } - sint bLeft = m_XReal + dLeft; - sint bRight = xRight - dRight; - sint bTop = xTop - dTop; - sint bBottom = m_YReal + dBottom; + if (width < 1) return; + if (thickness < 1) return; SDrawBorder shape; + shape.Color = border.Color; 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) + EBorderSide adjBorderL, adjBorderR; + getAdjacentBorders(side, adjBorderL, adjBorderR); + + sint32 adjWidthL, adjWidthR; + getAdjacentBorderWidth(side, adjWidthL, adjWidthR); + + // get alias to x/y + float &xy = horizontal ? x : y; + + if (border.Style == CSS_LINE_STYLE_DOTTED) { - if (TopStyle == CSS_LINE_STYLE_INSET || TopStyle == CSS_LINE_STYLE_GROOVE) - shape.Color = blend(TopColor, CRGBA::Black, 0.5f); - else - shape.Color = TopColor; + // thick border with little or no content might try to draw larger dot that fits + float radius = std::min(thickness / 2.f, width / 2.f); + float dot = thickness; - 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); + sint32 count = std::floor((float)width / dot); + // 3n (dot, gap, dot) groups; count should be odd + if ((count % 2) == 0) count += 1; - if (hasInnerShape(TopStyle)) + if (count == 1) { - float iLeft, iTop, iRight; - if (TopStyle == CSS_LINE_STYLE_DOUBLE) + // fallback to single dot + if (horizontal) { - 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; + x += width / 2.f; + y += radius; } - - 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); + { + x += radius; + y += width / 2.f; + } + + buildDotSegments(shape, x, y, radius); + return; } - } - if (m_BorderBottom) - { - if (BottomStyle == CSS_LINE_STYLE_OUTSET || BottomStyle == CSS_LINE_STYLE_RIDGE) - shape.Color = blend(BottomColor, CRGBA::Black, 0.5f); + // center-to-center spacing for dots + float spacing = dot + (width - dot * count) / (count-1); + + x += radius; + y += radius; + + if (adjWidthL > 0) + buildDotCornerStart(side, shape, x, y, radius); else - shape.Color = BottomColor; + buildDotSegments(shape, x, y, radius); + + xy += spacing; + count--; + + if (adjWidthR > 0) + count--; + + bool isDot = false; + while(count > 0) + { + if (isDot) + buildDotSegments(shape, x, y, radius); + + isDot = !isDot; + xy += spacing; + count--; + } + + if (adjWidthR > 0) + buildDotCornerEnd(side, shape, x, y, radius); + } + else + { + sint32 innerWidth = width; + if (adjWidthL > 0) innerWidth -= adjWidthL; + if (adjWidthR > 0) innerWidth -= adjWidthR; + + sint32 count = std::floor((float)innerWidth * 2.f / (thickness * 3)); - 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; + if ((float)innerWidth < 2.f * thickness) + { + buildSolidBorder(side); + return; + } + + // 4n groups (halfDash, halfGap, halfGap, halfDash) + if ((count % 4) == 1) count += 3; + else if ((count % 4) == 2) count += 2; + else if ((count % 4) == 3) count += 1; + + float halfDash = (float)innerWidth / (float)count; + float fullDash = halfDash * 2.f; + + // draw half dash or full corner + makeBorderQuad(side, shape, x, y, adjWidthL + halfDash, thickness); + if (adjWidthL > 0) + makeCornerQuad(adjBorderL, shape); m_DrawBorders.push_back(shape); + xy += adjWidthL + halfDash; + + // start/end half dash that are merged with corners + count -= 2; - if (hasInnerShape(BottomStyle)) + bool isDash = false; + while(count > 0) { - float iLeft, iBottom, iRight; - if (BottomStyle == CSS_LINE_STYLE_DOUBLE) + if (isDash) { - 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; + makeBorderQuad(side, shape, x, y, fullDash, thickness); + m_DrawBorders.push_back(shape); } + isDash = !isDash; - 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); + xy += fullDash; + count -= 2; } + + // draw half dash or full corner + makeBorderQuad(side, shape, x, y, adjWidthR + halfDash, thickness); + if (adjWidthR > 0) + makeCornerQuad(adjBorderR, shape); + + m_DrawBorders.push_back(shape); } + } - if (m_BorderRight) + // ---------------------------------------------------------------------------- + void CSSBorderRenderer::buildSolidBorder(EBorderSide side) + { + float x, y; + sint width, thickness; + CSSBorder border; + switch(side) { - if (RightStyle == CSS_LINE_STYLE_OUTSET || RightStyle == CSS_LINE_STYLE_RIDGE) - shape.Color = blend(RightColor, CRGBA::Black, 0.5f); - else - shape.Color = RightColor; + case BORDER_TOP: + border = m_Border.Top; + width = m_WReal; + thickness = m_Computed.Top; + x = m_XReal; + y = m_YReal + m_HReal - thickness; + break; + case BORDER_RIGHT: + border = m_Border.Right; + width = m_HReal; + thickness = m_Computed.Right; + x = m_XReal + m_WReal - thickness; + y = m_YReal; + break; + case BORDER_BOTTOM: + border = m_Border.Bottom; + width = m_WReal; + thickness = m_Computed.Bottom; + x = m_XReal; + y = m_YReal; + break; + case BORDER_LEFT: + border = m_Border.Left; + width = m_HReal; + thickness = m_Computed.Left; + x = m_XReal; + y = m_YReal; + break; + default: + return; + } + + SDrawBorder shape; + shape.Color = border.Color; + 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); + + if (border.Style == CSS_LINE_STYLE_INSET && (side == BORDER_TOP || side == BORDER_LEFT)) + shape.Color = blend(border.Color, CRGBA::Black, 0.5f); + else if (border.Style == CSS_LINE_STYLE_OUTSET && (side == BORDER_BOTTOM || side == BORDER_RIGHT)) + shape.Color = blend(border.Color, CRGBA::Black, 0.5f); + + // solid border + { + EBorderSide adjBorderL, adjBorderR; + getAdjacentBorders(side, adjBorderL, adjBorderR); + + makeBorderQuad(side, shape, x, y, width, thickness); + makeCornerQuad(adjBorderL, shape); + makeCornerQuad(adjBorderR, shape); - 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)) + if (hasInnerShape(border.Style)) + { + if (side == BORDER_TOP || side == BORDER_LEFT) { - 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 (border.Style == CSS_LINE_STYLE_GROOVE) + m_DrawBorders.back().Color = blend(border.Color, CRGBA::Black, 0.5f); + else if (border.Style == CSS_LINE_STYLE_RIDGE) + shape.Color = blend(border.Color, CRGBA::Black, 0.5f); + } + else if (side == BORDER_BOTTOM || side == BORDER_RIGHT) + { + if (border.Style == CSS_LINE_STYLE_GROOVE) + shape.Color = blend(border.Color, CRGBA::Black, 0.5f); + else if (border.Style == CSS_LINE_STYLE_RIDGE) + m_DrawBorders.back().Color = blend(border.Color, CRGBA::Black, 0.5f); + } - if (RightStyle == CSS_LINE_STYLE_GROOVE) - shape.Color = blend(shape.Color, CRGBA::Black, 0.5f); - else - shape.Color = RightColor; + sint32 adjWidthL, adjWidthR; + getAdjacentBorderWidth(side, adjWidthL, adjWidthR); + + float iStart, iMiddle, iEnd; + if (border.Style == CSS_LINE_STYLE_DOUBLE) + { + iStart = 2 * adjWidthL / 3.f; + iMiddle = 2 * thickness / 3.f; + iEnd = 2 * adjWidthR / 3.f; + } else { + iStart = adjWidthL / 2.f; + iMiddle = thickness / 2.f; + iEnd = adjWidthR / 2.f; + } - 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); + // create inner shape and remove overlapping from outer shape + switch(side) + { + case BORDER_TOP: + m_DrawBorders.back().Quad.V0.x -= iStart; m_DrawBorders.back().Quad.V0.y += iMiddle; + m_DrawBorders.back().Quad.V1.x += iEnd; m_DrawBorders.back().Quad.V1.y += iMiddle; + shape.Quad.V3.x += iStart; shape.Quad.V3.y -= iMiddle; + shape.Quad.V2.x -= iEnd; shape.Quad.V2.y -= iMiddle; + break; + case BORDER_BOTTOM: + m_DrawBorders.back().Quad.V2.x += iEnd; m_DrawBorders.back().Quad.V2.y -= iMiddle; + m_DrawBorders.back().Quad.V3.x -= iStart; m_DrawBorders.back().Quad.V3.y -= iMiddle; + shape.Quad.V1.x -= iEnd; shape.Quad.V1.y += iMiddle; + shape.Quad.V0.x += iStart; shape.Quad.V0.y += iMiddle; + break; + case BORDER_RIGHT: + m_DrawBorders.back().Quad.V3.x += iMiddle; m_DrawBorders.back().Quad.V3.y += iEnd; + m_DrawBorders.back().Quad.V0.x += iMiddle; m_DrawBorders.back().Quad.V0.y -= iStart; + shape.Quad.V2.x -= iMiddle; shape.Quad.V2.y -= iEnd; + shape.Quad.V1.x -= iMiddle; shape.Quad.V1.y += iStart; + break; + case BORDER_LEFT: + m_DrawBorders.back().Quad.V2.x -= iMiddle; m_DrawBorders.back().Quad.V2.y += iEnd; + m_DrawBorders.back().Quad.V1.x -= iMiddle; m_DrawBorders.back().Quad.V1.y -= iStart; + shape.Quad.V3.x += iMiddle; shape.Quad.V3.y -= iEnd; + shape.Quad.V0.x += iMiddle; shape.Quad.V0.y += iStart; + break; } + m_DrawBorders.push_back(shape); } + } - if (m_BorderLeft) + // ---------------------------------------------------------------------------- + void CSSBorderRenderer::makeBorderQuad(EBorderSide side, SDrawBorder &shape, float x, float y, float width, float thickness) const + { + float quadW, quadH; + switch(side) { - if (LeftStyle == CSS_LINE_STYLE_INSET || LeftStyle == CSS_LINE_STYLE_GROOVE) - shape.Color = blend(LeftColor, CRGBA::Black, 0.5f); - else - shape.Color = LeftColor; + case BORDER_TOP: quadW = width; quadH = thickness; break; + case BORDER_BOTTOM:quadW = width; quadH = thickness; break; + case BORDER_LEFT: quadW = thickness; quadH = width; break; + case BORDER_RIGHT: quadW = thickness; quadH = width; break; + default: return; + } + shape.Quad.V3.x = x; shape.Quad.V3.y = y + quadH; + shape.Quad.V2.x = x + quadW; shape.Quad.V2.y = y + quadH; + shape.Quad.V1.x = x + quadW; shape.Quad.V1.y = y; + shape.Quad.V0.x = x; shape.Quad.V0.y = y; + } - 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; + // ---------------------------------------------------------------------------- + void CSSBorderRenderer::makeCornerQuad(EBorderSide side, SDrawBorder &shape) const + { + switch(side) + { + case BORDER_TOP_LEFT: shape.Quad.V0.x += m_Computed.Left; break; + case BORDER_TOP_RIGHT: shape.Quad.V1.x -= m_Computed.Right; break; + case BORDER_RIGHT_TOP: shape.Quad.V3.y -= m_Computed.Top; break; + case BORDER_RIGHT_BOTTOM: shape.Quad.V0.y += m_Computed.Bottom; break; + case BORDER_BOTTOM_LEFT: shape.Quad.V3.x += m_Computed.Left; break; + case BORDER_BOTTOM_RIGHT: shape.Quad.V2.x -= m_Computed.Right; break; + case BORDER_LEFT_TOP: shape.Quad.V2.y -= m_Computed.Top; break; + case BORDER_LEFT_BOTTOM: shape.Quad.V1.y += m_Computed.Bottom; break; + } + } + + // ---------------------------------------------------------------------------- + static bool getCircleLineIntersection(float x, float y, float r, const NLMISC::CLine &line, NLMISC::CLine &result) + { + float dx = line.V0.x - line.V1.x; + float dy = line.V0.y - line.V1.y; + float rx = line.V0.x-x; + float ry = line.V0.y-y; + float a = dx*dx + dy*dy; + float b = 2*(dx * rx + dy * ry); + float c = rx * rx + ry * ry - r*r; + float d = b*b - 4 * a * c; + + if (d < 0) + return false; + + if (d == 0) + { + // single intersection + //float t = -b / (2*a); + //result.V0.x = result.V1.x = line.V0.x + t * dx; + //result.V0.y = result.V1.y = line.V0.y + t * dy; + return false; + } + + float t; + t = (-b + sqrt(d)) / (2 * a); + result.V0.x = line.V0.x + t * dx; + result.V0.y = line.V0.y + t * dy; + + t = (-b - sqrt(d)) / (2 * a); + result.V1.x = line.V0.x + t * dx; + result.V1.y = line.V0.y + t * dy; + return true; + } + + // ---------------------------------------------------------------------------- + void CSSBorderRenderer::buildDotCorner(SDrawBorder shape, float cX, float cY, float cR, const NLMISC::CLine &line) + { + static const float twopi = 2 * NLMISC::Pi; + NLMISC::CLine out; + if (getCircleLineIntersection(cX, cY, cR, line, out)) + { + float fromAngle = std::atan2(out.V0.y - cY, out.V0.x - cX); + float toAngle = std::atan2(out.V1.y - cY, out.V1.x - cX); + if (fromAngle < 0) fromAngle += twopi; + if (toAngle < 0) toAngle += twopi; + fromAngle /= twopi; + toAngle /= twopi; + buildDotSegments(shape, cX, cY, cR, fromAngle, toAngle); + } else { + buildDotSegments(shape, cX, cY, cR); + } + } + + // ---------------------------------------------------------------------------- + void CSSBorderRenderer::buildDotSegments(SDrawBorder shape, float x, float y, float radius, float fromAngle, float toAngle) + { + static const float pi = (float)NLMISC::Pi; + static const float twopi = (float)(NLMISC::Pi * 2); + static const uint minSectors = 12; + + // use single quad if dot is small + if (2 * radius * m_Scale < 4) + { + makeBorderQuad(BORDER_TOP, shape, x - radius, y - radius, radius * 2, radius * 2); m_DrawBorders.push_back(shape); + return; + } - if (hasInnerShape(LeftStyle)) - { - if (LeftStyle == CSS_LINE_STYLE_RIDGE) - shape.Color = blend(LeftColor, CRGBA::Black, 0.5f); - else - shape.Color = LeftColor; + // number of sectors for full circle + uint sectors = std::max(minSectors, (uint)std::ceil(radius * m_Scale)); - 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; - } + float arcLength; + if (toAngle < fromAngle) + arcLength = twopi * (1 + toAngle - fromAngle); + else + arcLength = twopi * (toAngle - fromAngle); - 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); - } + // sectors to draw + float arcSectors = ceil(arcLength * sectors / twopi ); + float arcSectorLength = arcLength / arcSectors; + + if (arcSectors <= 1) + return; + + if (arcLength < pi) + { + // only small segment is visible + float px1 = x + radius * cosf(twopi * fromAngle); + float py1 = y + radius * sinf(twopi * fromAngle); + float px2 = x + radius * cosf(twopi * toAngle); + float py2 = y + radius * sinf(twopi * toAngle); + float cx = (px1 + px2) / 2.f; + float cy = (py1 + py2) / 2.f; + shape.Quad.V0.x = cx; shape.Quad.V0.y = cy; + shape.Quad.V1.x = cx; shape.Quad.V1.y = cy; + } + else + { + shape.Quad.V0.x = x; shape.Quad.V0.y = y; + shape.Quad.V1.x = x; shape.Quad.V1.y = y; + } + + float a1 = fromAngle * twopi; + uint step; + for(step = 0; step < (uint)arcSectors; step++) + { + float a2 = a1 + arcSectorLength; + + shape.Quad.V2.x = x + radius * cosf(a1); shape.Quad.V2.y = y + radius * sinf(a1); + shape.Quad.V3.x = x + radius * cosf(a2); shape.Quad.V3.y = y + radius * sinf(a2); + + m_DrawBorders.push_back(shape); + + a1 = a2; } + + // build last sector if requested range is over 180deg + if (arcLength > pi && arcLength < twopi) + { + float a2 = fromAngle * twopi; + shape.Quad.V2.x = x + radius * cosf(a1); shape.Quad.V2.y = y + radius * sinf(a1); + shape.Quad.V3.x = x + radius * cosf(a2); shape.Quad.V3.y = y + radius * sinf(a2); + m_DrawBorders.push_back(shape); + } + } + + // ---------------------------------------------------------------------------- + void CSSBorderRenderer::updateCoords() + { + m_Dirty = false; + m_DrawBorders.clear(); + + if (m_MustComputeValues) + computeValues(); + + if (m_Computed.Top > 0 && m_Border.Top.Color.A > 0) + { + if (m_Border.Top.Style == CSS_LINE_STYLE_DASHED || m_Border.Top.Style == CSS_LINE_STYLE_DOTTED) + buildDashedBorder(BORDER_TOP); + else + buildSolidBorder(BORDER_TOP); + } + + if (m_Computed.Bottom > 0 && m_Border.Bottom.Color.A > 0) + { + if (m_Border.Bottom.Style == CSS_LINE_STYLE_DASHED || m_Border.Bottom.Style == CSS_LINE_STYLE_DOTTED) + buildDashedBorder(BORDER_BOTTOM); + else + buildSolidBorder(BORDER_BOTTOM); + } + + if (m_Computed.Right > 0 && m_Border.Right.Color.A > 0) + { + if (m_Border.Right.Style == CSS_LINE_STYLE_DASHED || m_Border.Right.Style == CSS_LINE_STYLE_DOTTED) + buildDashedBorder(BORDER_RIGHT); + else + buildSolidBorder(BORDER_RIGHT); + } + + if (m_Computed.Left > 0 && m_Border.Left.Color.A > 0) + { + if (m_Border.Left.Style == CSS_LINE_STYLE_DASHED || m_Border.Left.Style == CSS_LINE_STYLE_DOTTED) + buildDashedBorder(BORDER_LEFT); + else + buildSolidBorder(BORDER_LEFT); + } + } // ---------------------------------------------------------------------------- void CSSBorderRenderer::draw() { if (m_Dirty) updateCoords(); - if (!m_Border) return; + if (m_DrawBorders.empty()) return; CViewRenderer &rVR = *CViewRenderer::getInstance(); - // TODO: no need for widget manager, if global color is set from parent CRGBA globalColor; if (m_ModulateGlobalColor) globalColor = CWidgetManager::getInstance()->getGlobalColor(); diff --git a/nel/src/gui/css_style.cpp b/nel/src/gui/css_style.cpp index 7a9b725af..912ff04d4 100644 --- a/nel/src/gui/css_style.cpp +++ b/nel/src/gui/css_style.cpp @@ -504,7 +504,7 @@ namespace NLGUI } } - void CCssStyle::applyBorderWidth(const std::string &value, uint32 *dest, const uint32 currentWidth, const uint32 fontSize) const + void CCssStyle::applyBorderWidth(const std::string &value, CSSLength *dest, const CSSLength ¤tWidth) const { if (!dest) return; if (value == "inherit") @@ -513,37 +513,19 @@ namespace NLGUI } else if (value == "thin") { - *dest = 1; + dest->setFloatValue(1, "px"); } else if (value == "medium") { - *dest = 3; + dest->setFloatValue(3, "px"); } else if (value == "thick") { - *dest = 5; - } - else if (value == "0") - { - *dest = 0; + dest->setFloatValue(5, "px"); } else { - 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; // no support for % in border width - else - *dest = tmpf; - } + dest->parseValue(value, false, false); } } @@ -656,18 +638,18 @@ namespace NLGUI TStyle::const_iterator it; for (it=style.StyleRules.begin(); it != style.StyleRules.end(); ++it) { - if (it->first == "border-top-width") applyBorderWidth(it->second, &style.BorderTopWidth, current.BorderTopWidth, current.FontSize); - else if (it->first == "border-top-color") applyBorderColor(it->second, &style.BorderTopColor, current.BorderTopColor, current.TextColor); - else if (it->first == "border-top-style") applyLineStyle(it->second, &style.BorderTopStyle, current.BorderTopStyle); - else if (it->first == "border-right-width") applyBorderWidth(it->second, &style.BorderRightWidth, current.BorderRightWidth, current.FontSize); - else if (it->first == "border-right-color") applyBorderColor(it->second, &style.BorderRightColor, current.BorderRightColor, current.TextColor); - else if (it->first == "border-right-style") applyLineStyle(it->second, &style.BorderRightStyle, current.BorderRightStyle); - else if (it->first == "border-bottom-width") applyBorderWidth(it->second, &style.BorderBottomWidth, current.BorderBottomWidth, current.FontSize); - else if (it->first == "border-bottom-color") applyBorderColor(it->second, &style.BorderBottomColor, current.BorderBottomColor, current.TextColor); - else if (it->first == "border-bottom-style") applyLineStyle(it->second, &style.BorderBottomStyle, current.BorderBottomStyle); - 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); + if (it->first == "border-top-width") applyBorderWidth(it->second, &style.Border.Top.Width, current.Border.Top.Width); + else if (it->first == "border-top-color") applyBorderColor(it->second, &style.Border.Top.Color, current.Border.Top.Color, current.TextColor); + else if (it->first == "border-top-style") applyLineStyle(it->second, &style.Border.Top.Style, current.Border.Top.Style); + else if (it->first == "border-right-width") applyBorderWidth(it->second, &style.Border.Right.Width, current.Border.Right.Width); + else if (it->first == "border-right-color") applyBorderColor(it->second, &style.Border.Right.Color, current.Border.Right.Color, current.TextColor); + else if (it->first == "border-right-style") applyLineStyle(it->second, &style.Border.Right.Style, current.Border.Right.Style); + else if (it->first == "border-bottom-width") applyBorderWidth(it->second, &style.Border.Bottom.Width, current.Border.Bottom.Width); + else if (it->first == "border-bottom-color") applyBorderColor(it->second, &style.Border.Bottom.Color, current.Border.Bottom.Color, current.TextColor); + else if (it->first == "border-bottom-style") applyLineStyle(it->second, &style.Border.Bottom.Style, current.Border.Bottom.Style); + else if (it->first == "border-left-width") applyBorderWidth(it->second, &style.Border.Left.Width, current.Border.Left.Width); + else if (it->first == "border-left-color") applyBorderColor(it->second, &style.Border.Left.Color, current.Border.Left.Color, current.TextColor); + else if (it->first == "border-left-style") applyLineStyle(it->second, &style.Border.Left.Style, current.Border.Left.Style); 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);