Merge remote-tracking branch 'origin/develop' into core4

# Conflicts:
#	ryzom/client/src/interface_v3/action_handler_help.cpp
merge/2021-11-19
kaetemi 3 years ago
commit ac9e8f4f5e
No known key found for this signature in database
GPG Key ID: 9873C4D40BB479BC

@ -0,0 +1,83 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// 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 <http://www.gnu.org/licenses/>.
#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<std::string> &parts);
void positionFromTwo(const std::vector<std::string> &parts);
void positionFromThree(const std::vector<std::string> &parts);
void positionFromFour(const std::vector<std::string> &parts);
};
}//namespace
#endif // CL_CSS_BACKGROUND_H

@ -0,0 +1,193 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// 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 <http://www.gnu.org/licenses/>.
#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, <html> 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<SDrawQueue> m_DrawQueue;
const sint8 m_RenderLayer;
bool m_ModulateGlobalColor;
// if true, painting area returns area at least the size of viewport (ie, <html> 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

@ -0,0 +1,72 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// 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 <http://www.gnu.org/licenses/>.
#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

@ -23,9 +23,12 @@
#include "nel/misc/rgba.h" #include "nel/misc/rgba.h"
#include "nel/misc/geom_ext.h" #include "nel/misc/geom_ext.h"
#include "nel/gui/css_types.h" #include "nel/gui/css_types.h"
#include "nel/gui/css_border.h"
namespace NLGUI namespace NLGUI
{ {
class CInterfaceElement;
/** /**
* \brief Border renderer for GUI classes * \brief Border renderer for GUI classes
* \date 2019-09-03 10:50 GMT * \date 2019-09-03 10:50 GMT
@ -34,29 +37,76 @@ namespace NLGUI
class CSSBorderRenderer class CSSBorderRenderer
{ {
private: 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 // parent element screen coordinates
sint32 _XReal, _YReal; sint32 m_XReal, m_YReal;
sint32 _WReal, _HReal; sint32 m_WReal, m_HReal;
NLMISC::CQuadUV _QuadT; struct SDrawBorder
NLMISC::CQuadUV _QuadR; {
NLMISC::CQuadUV _QuadB; NLMISC::CQuadUV Quad;
NLMISC::CQuadUV _QuadL; NLMISC::CRGBA Color;
};
std::vector<SDrawBorder> m_DrawBorders;
uint8 _RenderLayer; sint8 m_RenderLayer;
bool _ModulateGlobalColor; bool m_ModulateGlobalColor;
// if true, then updateCoords() is called from draw() // if true, then updateCoords() is called from draw()
bool _Dirty; bool m_Dirty;
// if true, then at least one border is set // UI scale, used to calculate number of segments to draw for circle
bool _Border; float m_Scale;
bool _BorderTop, _BorderRight, _BorderBottom, _BorderLeft;
public: CSSRect<CSSBorder> m_Border;
uint32 TopWidth, RightWidth, BottomWidth, LeftWidth; CSSRect<sint32> m_Computed;
NLMISC::CRGBA TopColor, RightColor, BottomColor, LeftColor;
CSSLineStyle TopStyle, RightStyle, BottomStyle, LeftStyle; // 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 // alpha value from parent
uint8 CurrentAlpha; uint8 CurrentAlpha;
@ -68,21 +118,67 @@ namespace NLGUI
void setRect(sint32 x, sint32 y, sint32 w, sint32 h); void setRect(sint32 x, sint32 y, sint32 w, sint32 h);
void setWidth(uint32 top, uint32 right, uint32 bottom, uint32 left); void setBorder(const CSSRect<CSSBorder> &b) { m_Dirty = true; m_Border = b; }
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 updateCoords(); void updateCoords();
void invalidateCoords() { _Dirty = _Border = true; } void invalidateCoords() { m_Dirty = true; }
void invalidateContent() { _Dirty = _Border = true; }; void invalidateContent() { m_Dirty = true; }
uint32 getTopWidth() const; bool isEmpty() const {
uint32 getRightWidth() const; return (m_Border.Top.Width.getFloat() +
uint32 getBottomWidth() const; m_Border.Right.Width.getFloat() +
uint32 getLeftWidth() const; 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; }
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(); void draw();
}; // CSSBorderRenderer }; // CSSBorderRenderer

@ -0,0 +1,76 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// 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 <http://www.gnu.org/licenses/>.
#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

@ -21,6 +21,9 @@
#include "nel/misc/rgba.h" #include "nel/misc/rgba.h"
#include "nel/gui/css_selector.h" #include "nel/gui/css_selector.h"
#include "nel/gui/css_types.h" #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 namespace NLGUI
{ {
@ -65,13 +68,9 @@ namespace NLGUI
Height=-1; Height=-1;
MaxWidth=-1; MaxWidth=-1;
MaxHeight=-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 // background
BackgroundColor=NLMISC::CRGBA::Black;
BackgroundColorOver=NLMISC::CRGBA::Black; BackgroundColorOver=NLMISC::CRGBA::Black;
MarginTop = MarginRight = MarginBottom = MarginLeft = 0;
PaddingTop = PaddingRight = PaddingBottom = PaddingLeft = 0; PaddingTop = PaddingRight = PaddingBottom = PaddingLeft = 0;
} }
@ -101,11 +100,10 @@ namespace NLGUI
sint32 Height; sint32 Height;
sint32 MaxWidth; sint32 MaxWidth;
sint32 MaxHeight; sint32 MaxHeight;
uint32 BorderTopWidth, BorderRightWidth, BorderBottomWidth, BorderLeftWidth; CSSRect<CSSBorder> Border;
CSSLineStyle BorderTopStyle, BorderRightStyle, BorderBottomStyle, BorderLeftStyle; CSSBackground Background;
NLMISC::CRGBA BorderTopColor, BorderRightColor, BorderBottomColor, BorderLeftColor;
NLMISC::CRGBA BackgroundColor;
NLMISC::CRGBA BackgroundColorOver; NLMISC::CRGBA BackgroundColorOver;
uint32 MarginTop, MarginRight, MarginBottom, MarginLeft;
uint32 PaddingTop, PaddingRight, PaddingBottom, PaddingLeft; uint32 PaddingTop, PaddingRight, PaddingBottom, PaddingLeft;
std::string WhiteSpace; std::string WhiteSpace;
@ -177,16 +175,18 @@ namespace NLGUI
// parse 'padding' into 'padding-top', 'padding-left', etc // parse 'padding' into 'padding-top', 'padding-left', etc
void expandPaddingShorthand(const std::string &value, TStyle &style) const; 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" // expand shorthand rule, ie "border", into longhand names, ie "border-top-width"
// if shorthand is present in style, then its removed // if shorthand is present in style, then its removed
void expandShorthand(const std::string &prop, const std::string &value, TStyle &style) const; void expandShorthand(const std::string &prop, const std::string &value, TStyle &style) const;
// parse string value into corresponding value // 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 &currentWidth) const;
void applyBorderColor(const std::string &value, NLMISC::CRGBA *dest, const NLMISC::CRGBA &currentColor, const NLMISC::CRGBA &textColor) const; void applyBorderColor(const std::string &value, NLMISC::CRGBA *dest, const NLMISC::CRGBA &currentColor, const NLMISC::CRGBA &textColor) const;
void applyLineStyle(const std::string &value, CSSLineStyle *dest, const CSSLineStyle &currentStyle) const; void applyLineStyle(const std::string &value, CSSLineStyle *dest, const CSSLineStyle &currentStyle) const;
void applyPaddingWidth(const std::string &value, uint32 *dest, const uint32 currentPadding, uint32 fontSize) const; 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 // parse and replace var(--name, fallback) function
// return false if property should be ignored // return false if property should be ignored
@ -221,9 +221,15 @@ namespace NLGUI
Current.MaxWidth=-1; Current.MaxWidth=-1;
Current.MaxHeight=-1; Current.MaxHeight=-1;
Current.BorderTopWidth = Current.BorderRightWidth = Current.BorderBottomWidth = Current.BorderLeftWidth = CSS_LINE_WIDTH_MEDIUM; Current.Border.Top.reset();
Current.BorderTopStyle = Current.BorderRightStyle = Current.BorderBottomStyle = Current.BorderLeftStyle = CSS_LINE_STYLE_NONE; Current.Border.Right.reset();
Current.BorderTopColor = Current.BorderRightColor = Current.BorderBottomColor = Current.BorderLeftColor = Current.TextColor; Current.Border.Bottom.reset();
Current.Border.Left.reset();
Current.Background = CSSBackground();
Current.BackgroundColorOver = NLMISC::CRGBA::Transparent;
Current.MarginTop = Current.MarginRight = Current.MarginBottom = Current.MarginLeft = 0;
Current.PaddingTop = Current.PaddingRight = Current.PaddingBottom = Current.PaddingLeft = 0; Current.PaddingTop = Current.PaddingRight = Current.PaddingBottom = Current.PaddingLeft = 0;
Current.StyleRules.clear(); Current.StyleRules.clear();

@ -30,7 +30,12 @@ namespace NLGUI
enum CSSLineStyle { enum CSSLineStyle {
CSS_LINE_STYLE_NONE = 0, CSS_LINE_STYLE_NONE = 0,
CSS_LINE_STYLE_HIDDEN, CSS_LINE_STYLE_HIDDEN,
CSS_LINE_STYLE_DOTTED,
CSS_LINE_STYLE_DASHED,
CSS_LINE_STYLE_SOLID, CSS_LINE_STYLE_SOLID,
CSS_LINE_STYLE_DOUBLE,
CSS_LINE_STYLE_GROOVE,
CSS_LINE_STYLE_RIDGE,
CSS_LINE_STYLE_INSET, CSS_LINE_STYLE_INSET,
CSS_LINE_STYLE_OUTSET CSS_LINE_STYLE_OUTSET
}; };
@ -42,6 +47,53 @@ namespace NLGUI
CSS_LINE_WIDTH_THICK = 5 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
};
template<typename T>
struct CSSRect
{
T Top;
T Right;
T Bottom;
T Left;
};
}//namespace }//namespace
#endif // CL_CSS_TYPES_H #endif // CL_CSS_TYPES_H

@ -30,6 +30,7 @@
#include "nel/gui/html_element.h" #include "nel/gui/html_element.h"
#include "nel/gui/html_parser.h" #include "nel/gui/html_parser.h"
#include "nel/gui/css_style.h" #include "nel/gui/css_style.h"
#include "nel/gui/css_background_renderer.h"
// forward declaration // forward declaration
typedef void CURLM; typedef void CURLM;
@ -47,6 +48,20 @@ namespace NLGUI
extern std::string CurrentCookie; 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 // HTML group
/** /**
* Widget to have a resizable scrolltext and its scrollbar * Widget to have a resizable scrolltext and its scrollbar
@ -136,9 +151,9 @@ namespace NLGUI
void endParagraph(); void endParagraph();
// add image download (used by view_bitmap.cpp to load web images) // 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"); ICurlDownloadCB *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 ICurlDownloadCB *addTextureDownload(const std::string &url, sint32 &texId, CViewBase *view);
void removeImageDownload(CViewBase *img); void removeImageDownload(ICurlDownloadCB *handle, CViewBase *img);
std::string localImageName(const std::string &url); std::string localImageName(const std::string &url);
// Timeout // Timeout
@ -168,6 +183,7 @@ namespace NLGUI
std::string DefaultRadioButtonBitmapNormal; std::string DefaultRadioButtonBitmapNormal;
std::string DefaultRadioButtonBitmapPushed; std::string DefaultRadioButtonBitmapPushed;
std::string DefaultRadioButtonBitmapOver; std::string DefaultRadioButtonBitmapOver;
// TODO: remove from interface xml and code
std::string DefaultBackgroundBitmapView; std::string DefaultBackgroundBitmapView;
struct TFormField { struct TFormField {
@ -332,6 +348,9 @@ namespace NLGUI
const std::string &overBitmap, const char *actionHandler, const char *actionHandlerParams, const std::string &tooltip, const std::string &overBitmap, const char *actionHandler, const char *actionHandlerParams, const std::string &tooltip,
const CStyleParams &style = CStyleParams()); const CStyleParams &style = CStyleParams());
// Set the background color
void setupBackground(CSSBackgroundRenderer *bg);
// Set the background color // Set the background color
void setBackgroundColor (const NLMISC::CRGBA &bgcolor); void setBackgroundColor (const NLMISC::CRGBA &bgcolor);
@ -371,6 +390,10 @@ namespace NLGUI
// <style> and downloaded <link rel=stylesheet> elements // <style> and downloaded <link rel=stylesheet> elements
std::vector<std::string> _HtmlStyles; std::vector<std::string> _HtmlStyles;
// background from <html> or <body> element
CSSBackgroundRenderer m_HtmlBackground;
CSSBackgroundRenderer m_BodyBackground;
// Valid base href was found // Valid base href was found
bool _IgnoreBaseUrlTag; bool _IgnoreBaseUrlTag;
// Fragment from loading url // Fragment from loading url
@ -820,48 +843,108 @@ namespace NLGUI
// decode all HTML entities // decode all HTML entities
static std::string decodeHTMLEntities(const std::string &str); static std::string decodeHTMLEntities(const std::string &str);
struct CDataImageDownload class CDataDownload : public ICurlDownloadCB
{
public:
CDataDownload(const std::string &u, const std::string &d)
: ICurlDownloadCB(u), data(NULL), fp(NULL), dest(d), redirects(0), ConnectionTimeout(60)
{}
virtual ~CDataDownload();
public:
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;
};
class ImageDownloadCB : public CDataDownload
{ {
public: public:
CDataImageDownload(CViewBase *img, CStyleParams style, TImageType type): Image(img), Style(style), Type(type) 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)
{ {
addImage(img, style, type);
} }
public:
CViewBase * Image; virtual void finish() NL_OVERRIDE;
void addImage(CViewBase *img, const CStyleParams &style, TImageType type);
void removeImage(CViewBase *img);
private:
std::vector<SImageInfo> Images;
CGroupHTML *Parent;
CStyleParams Style; CStyleParams Style;
TImageType Type; TImageType Type;
}; };
struct CDataDownload class TextureDownloadCB : public CDataDownload
{ {
public: 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) TextureDownloadCB(const std::string &url, const std::string &dest, sint32 texId, CViewBase *view)
: data(NULL), fp(NULL), url(u), dest(d), type(t), luaScript(s), md5sum(m), redirects(0), ConnectionTimeout(60) : CDataDownload(url, dest)
{ {
if (t == ImgType) imgs.push_back(CDataImageDownload(i, style, imagetype)); addTexture(texId, view);
}
virtual void finish() NL_OVERRIDE;
void addTexture(sint32 texId, CViewBase *view) {
TextureIds.push_back(std::make_pair(texId, view));
} }
~CDataDownload();
private:
std::vector<std::pair<sint32, CViewBase *> > TextureIds;
};
class BnpDownloadCB : public CDataDownload
{
public: public:
CCurlWWWData *data; BnpDownloadCB(const std::string &url, const std::string &dest, const std::string md5sum, const std::string lua, CGroupHTML *parent)
std::string url; : CDataDownload(url, dest), Parent(parent), m_md5sum(md5sum), m_lua(lua)
std::string dest; {}
std::string tmpdest;
std::string luaScript; virtual void finish() NL_OVERRIDE;
std::string md5sum;
TDataType type; private:
uint32 redirects; CGroupHTML *Parent;
FILE *fp; std::string m_md5sum;
std::vector<CDataImageDownload> imgs; std::string m_lua;
uint32 ConnectionTimeout;
}; };
std::list<CDataDownload> Curls; std::list<CDataDownload*> Curls;
CURLM *MultiCurl; CURLM *MultiCurl;
int RunningCurls; int RunningCurls;
bool startCurlDownload(CDataDownload &download); bool startCurlDownload(CDataDownload *download);
void finishCurlDownload(const CDataDownload &download); void finishCurlDownload(CDataDownload *download);
void pumpCurlQueue(); void pumpCurlQueue();
void initImageDownload(); void initImageDownload();
@ -891,7 +974,7 @@ namespace NLGUI
// _CurlWWW download finished // _CurlWWW download finished
void htmlDownloadFinished(bool success, const std::string &error); void htmlDownloadFinished(bool success, const std::string &error);
// images, stylesheets, etc finished downloading // 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 // HtmlType download finished
void htmlDownloadFinished(const std::string &content, const std::string &type, long code); void htmlDownloadFinished(const std::string &content, const std::string &type, long code);
@ -908,9 +991,6 @@ namespace NLGUI
// :before, :after rendering // :before, :after rendering
void renderPseudoElement(const std::string &pseudo, const CHtmlElement &elm); void renderPseudoElement(const std::string &pseudo, const CHtmlElement &elm);
// apply background from current style (for html, body)
void applyBackground(const CHtmlElement &elm);
void insertFormImageButton(const std::string &name, void insertFormImageButton(const std::string &name,
const std::string &tooltip, const std::string &tooltip,
const std::string &src, const std::string &src,

@ -31,6 +31,7 @@
namespace NLGUI namespace NLGUI
{ {
class CSSBorderRenderer; class CSSBorderRenderer;
class CSSBackgroundRenderer;
/** /**
* This group is used to simulate HTML cells. * This group is used to simulate HTML cells.
@ -94,22 +95,10 @@ namespace NLGUI
// Memorize max width // Memorize max width
sint32 WidthMax; sint32 WidthMax;
// The cell color
NLMISC::CRGBA BgColor;
CSSBorderRenderer* Border; CSSBorderRenderer* Border;
CSSBackgroundRenderer *Background;
uint32 PaddingTop, PaddingRight, PaddingBottom, PaddingLeft; uint32 PaddingTop, PaddingRight, PaddingBottom, PaddingLeft;
// Texture
CViewRenderer::CTextureId _TextureId;
bool _TextureTiled;
bool _TextureScaled;
// cached absolute coords for background texture
sint32 _TextureXReal;
sint32 _TextureYReal;
sint32 _TextureWReal;
sint32 _TextureHReal;
// Alignment // Alignment
TAlign Align; TAlign Align;
TVAlign VAlign; TVAlign VAlign;
@ -121,6 +110,7 @@ namespace NLGUI
// The cell is nowrap // The cell is nowrap
bool NoWrap; bool NoWrap;
// deprecated background image
void setTexture(const std::string & TxName); void setTexture(const std::string & TxName);
void setTextureTile(bool tiled); void setTextureTile(bool tiled);
void setTextureScale(bool scaled); void setTextureScale(bool scaled);
@ -134,8 +124,6 @@ namespace NLGUI
static bool getDebugUICell(){ return DebugUICell; } static bool getDebugUICell(){ return DebugUICell; }
private: private:
void updateTextureCoords();
void setEnclosedGroupDefaultParams(); void setEnclosedGroupDefaultParams();
static bool DebugUICell; static bool DebugUICell;
}; };
@ -164,18 +152,18 @@ namespace NLGUI
sint32 ForceWidthMin; sint32 ForceWidthMin;
CSSBorderRenderer* Border; CSSBorderRenderer* Border;
CSSBackgroundRenderer *Background;
// Cell has 1px solid border when <table> has 'border' attribute with width > 0 // Cell has 1px solid border when <table> has 'border' attribute with width > 0
bool CellBorder; bool CellBorder;
sint32 CellPadding; sint32 CellPadding;
sint32 CellSpacing; sint32 CellSpacing;
// The table color
NLMISC::CRGBA BgColor;
uint8 CurrentAlpha; uint8 CurrentAlpha;
bool ContinuousUpdate; bool ContinuousUpdate;
// deprecated background image
void setTexture(const std::string & TxName); void setTexture(const std::string & TxName);
void setTextureTile(bool tiled); void setTextureTile(bool tiled);
void setTextureScale(bool scaled); void setTextureScale(bool scaled);
@ -201,18 +189,6 @@ namespace NLGUI
virtual bool parse (xmlNodePtr cur, CInterfaceGroup * parentGroup); virtual bool parse (xmlNodePtr cur, CInterfaceGroup * parentGroup);
// Texture
CViewRenderer::CTextureId _TextureId;
bool _TextureTiled;
bool _TextureScaled;
// cached absolute coords for background texture
sint32 _TextureXReal;
sint32 _TextureYReal;
sint32 _TextureWReal;
sint32 _TextureHReal;
void updateTextureCoords();
// Content validated // Content validated
bool _ContentValidated; bool _ContentValidated;

@ -184,7 +184,7 @@ namespace NLGUI
// *************************************************************************** // ***************************************************************************
// Read a CSS length value, return true if one of supported units '%, rem, em, px, pt' // Read a CSS length value, return true if one of supported units '%, rem, em, px, pt'
// On failure: 'value' and 'unit' values are undefined // 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 // 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); bool getPercentage (sint32 &width, float &percent, const char *str);

@ -28,6 +28,7 @@
namespace NLGUI namespace NLGUI
{ {
class ICurlDownloadCB;
/** /**
* class implementing a bitmap view * class implementing a bitmap view
@ -61,7 +62,7 @@ namespace NLGUI
_TxtHeight = -1; _TxtHeight = -1;
// Support for https://.. textures // Support for https://.. textures
_HtmlDownload = false; _HtmlDownload = NULL;
} }
/// Destructor /// Destructor
@ -141,7 +142,9 @@ namespace NLGUI
bool _Flip : 1; bool _Flip : 1;
bool _Tile : 1; bool _Tile : 1;
bool _InheritGCAlpha : 1; bool _InheritGCAlpha : 1;
bool _HtmlDownload : 1;
// pointer to active curl download object
ICurlDownloadCB *_HtmlDownload;
// For single texture // For single texture

@ -252,6 +252,19 @@ namespace NLGUI
*/ */
bool loadTextures (const std::string &textureFileName, const std::string &uvFileName, bool uploadDXTC); 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 * 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 * 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) * 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 * \param id : the id of the texture
* \return a texture file pointer. -1 if not found or if sName is empty() * \return a texture file pointer. -1 if not found or if sName is empty()
*/ */
@ -479,6 +495,9 @@ namespace NLGUI
// \name Texture management // \name Texture management
// *************************************************************************** // ***************************************************************************
bool loadTextureFromString(SGlobalTexture *gt, const std::string &data);
bool loadTextureFromFile(SGlobalTexture *gt, const std::string &filename);
// SImage accessors // SImage accessors
SImage *getSImage(sint32 textureId) SImage *getSImage(sint32 textureId)
{ {

@ -283,18 +283,16 @@ void CDriverD3D::setupScissor (const class CScissor& scissor)
// Get viewport // Get viewport
_ScissorTouched = false; _ScissorTouched = false;
float x= scissor.X; float x= scissor.X;
float y= scissor.Y;
float width= scissor.Width; float width= scissor.Width;
float height= scissor.Height; 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); setRenderState (D3DRS_SCISSORTESTENABLE, FALSE);
} }
else else
{ {
float y= scissor.Y;
if (_HWnd) if (_HWnd)
{ {
// Get the render target size // Get the render target size

@ -0,0 +1,427 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// 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 <http://www.gnu.org/licenses/>.
#include "stdpch.h"
#include <string>
#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<std::string> 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<std::string> 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<std::string> 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<std::string> &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<std::string> &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<std::string> &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<std::string> &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

@ -0,0 +1,607 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// 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 <http://www.gnu.org/licenses/>.
#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

@ -29,290 +29,718 @@ using namespace NLMISC;
namespace NLGUI namespace NLGUI
{ {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
CSSBorderRenderer::CSSBorderRenderer() 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; CurrentAlpha = 255;
_RenderLayer = 0; m_Scale = 1.f;
_ModulateGlobalColor = false; m_RenderLayer = 0;
m_ModulateGlobalColor = false;
_Border = true;
_Dirty = true; m_Dirty = true;
_BorderTop = _BorderRight = _BorderBottom = _BorderLeft = false; m_MustComputeValues = true;
m_XReal = 0;
// m_YReal = 0;
_QuadT.Uv0.set(0.f, 0.f); m_WReal = 0;
_QuadT.Uv1.set(0.f, 0.f); m_HReal = 0;
_QuadT.Uv2.set(1.f, 1.f);
_QuadT.Uv3.set(0.f, 1.f); m_Viewport = NULL;
// m_FontSize = 16.f;
_QuadR.Uv0.set(0.f, 0.f); m_RootFontSize = 16.f;
_QuadR.Uv1.set(0.f, 0.f);
_QuadR.Uv2.set(1.f, 1.f); m_Computed.Top = m_Computed.Right = m_Computed.Bottom = m_Computed.Left = 0;
_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);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CSSBorderRenderer::setRenderLayer(sint layer) void CSSBorderRenderer::setRenderLayer(sint layer)
{ {
_RenderLayer = layer; m_RenderLayer = layer;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CSSBorderRenderer::setModulateGlobalColor(bool s) void CSSBorderRenderer::setModulateGlobalColor(bool s)
{ {
_ModulateGlobalColor = s; m_ModulateGlobalColor = s;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CSSBorderRenderer::setRect(sint32 x, sint32 y, sint32 w, sint32 h) void CSSBorderRenderer::setRect(sint32 x, sint32 y, sint32 w, sint32 h)
{ {
_XReal = x; m_XReal = x;
_YReal = y; m_YReal = y;
_WReal = w; m_WReal = w;
_HReal = h; m_HReal = h;
_Dirty = _Border = (w > 0 && h > 0); m_Dirty = (w > 0 && h > 0);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CSSBorderRenderer::setWidth(uint32 top, uint32 right, uint32 bottom, uint32 left) bool CSSBorderRenderer::hasInnerShape(CSSLineStyle style) const
{ {
TopWidth = top; return style == CSS_LINE_STYLE_DOUBLE ||
RightWidth = right; style == CSS_LINE_STYLE_GROOVE ||
BottomWidth = bottom; style == CSS_LINE_STYLE_RIDGE;
LeftWidth = left;
_Dirty = _Border = (top > 0 || right > 0 || bottom > 0 || left > 0);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CSSBorderRenderer::setStyle(CSSLineStyle top, CSSLineStyle right, CSSLineStyle bottom, CSSLineStyle left) void CSSBorderRenderer::computeValues()
{ {
TopStyle = top; m_MustComputeValues = false;
RightStyle = right;
BottomStyle = bottom;
LeftStyle = left;
_Dirty = _Border = true; // TODO :should save as computed value
sint32 vpW=0;
sint32 vpH=0;
if (m_Viewport)
{
vpW = m_Viewport->getWReal();
vpH = m_Viewport->getHReal();
}
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);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CSSBorderRenderer::setColor(const NLMISC::CRGBA &top, const NLMISC::CRGBA &right, const NLMISC::CRGBA &bottom, const NLMISC::CRGBA &left) void CSSBorderRenderer::getAdjacentBorders(EBorderSide side, EBorderSide &adjBorderL, EBorderSide &adjBorderR) const
{ {
TopColor = top; switch(side)
RightColor = right; {
BottomColor = bottom; case BORDER_TOP:
LeftColor = left; 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;
}
}
_Dirty = true; // ----------------------------------------------------------------------------
void CSSBorderRenderer::getAdjacentBorderWidth(EBorderSide side, sint32 &adjWidthL, sint32 &adjWidthR) const
{
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::getTopWidth() const void CSSBorderRenderer::buildDotCornerStart(EBorderSide side, SDrawBorder shape, float x1, float y1, float radius)
{ {
if (TopStyle == CSS_LINE_STYLE_NONE || TopStyle == CSS_LINE_STYLE_HIDDEN) NLMISC::CLine line;
return 0; 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;
}
}
return TopWidth; // ----------------------------------------------------------------------------
void CSSBorderRenderer::buildDotCornerEnd(EBorderSide side, SDrawBorder shape, float x1, float y1, float radius)
{
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;
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
uint32 CSSBorderRenderer::getRightWidth() const void CSSBorderRenderer::buildDashedBorder(EBorderSide side)
{ {
if (RightStyle == CSS_LINE_STYLE_NONE || RightStyle == CSS_LINE_STYLE_HIDDEN) CSSBorder border;
return 0;
return RightWidth; 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;
}
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);
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)
{
// 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;
sint32 count = std::floor((float)width / dot);
// 3n (dot, gap, dot) groups; count should be odd
if ((count % 2) == 0) count += 1;
if (count == 1)
{
// fallback to single dot
if (horizontal)
{
x += width / 2.f;
y += radius;
}
else
{
x += radius;
y += width / 2.f;
}
buildDotSegments(shape, x, y, radius);
return;
}
// 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
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));
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;
bool isDash = false;
while(count > 0)
{
if (isDash)
{
makeBorderQuad(side, shape, x, y, fullDash, thickness);
m_DrawBorders.push_back(shape);
}
isDash = !isDash;
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);
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
uint32 CSSBorderRenderer::getBottomWidth() const void CSSBorderRenderer::buildSolidBorder(EBorderSide side)
{ {
if (BottomStyle == CSS_LINE_STYLE_NONE || BottomStyle == CSS_LINE_STYLE_HIDDEN) float x, y;
return 0; sint width, thickness;
CSSBorder border;
switch(side)
{
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);
return BottomWidth; m_DrawBorders.push_back(shape);
}
if (hasInnerShape(border.Style))
{
if (side == BORDER_TOP || side == BORDER_LEFT)
{
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);
}
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;
}
// 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);
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
uint32 CSSBorderRenderer::getLeftWidth() const void CSSBorderRenderer::makeBorderQuad(EBorderSide side, SDrawBorder &shape, float x, float y, float width, float thickness) const
{ {
if (LeftStyle == CSS_LINE_STYLE_NONE || LeftStyle == CSS_LINE_STYLE_HIDDEN) float quadW, quadH;
return 0; switch(side)
{
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;
}
return LeftWidth; // ----------------------------------------------------------------------------
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;
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
uint32 CSSBorderRenderer::getLeftRightWidth() const static bool getCircleLineIntersection(float x, float y, float r, const NLMISC::CLine &line, NLMISC::CLine &result)
{ {
return getLeftWidth() + getRightWidth(); 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;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
uint32 CSSBorderRenderer::getTopBottomWidth() const void CSSBorderRenderer::buildDotCorner(SDrawBorder shape, float cX, float cY, float cR, const NLMISC::CLine &line)
{ {
return getTopWidth() + getBottomWidth(); 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::updateCoords() void CSSBorderRenderer::buildDotSegments(SDrawBorder shape, float x, float y, float radius, float fromAngle, float toAngle)
{ {
_Dirty = false; static const float pi = (float)NLMISC::Pi;
if (!_Border) return; 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;
}
// number of sectors for full circle
uint sectors = std::max(minSectors, (uint)std::ceil(radius * m_Scale));
sint dTop = getTopWidth(); _BorderTop = dTop > 0; float arcLength;
sint dRight = getRightWidth(); _BorderRight = dRight > 0; if (toAngle < fromAngle)
sint dBottom = getBottomWidth(); _BorderBottom = dBottom > 0; arcLength = twopi * (1 + toAngle - fromAngle);
sint dLeft = getLeftWidth(); _BorderLeft = dLeft > 0; else
arcLength = twopi * (toAngle - fromAngle);
_Border = _BorderTop || _BorderRight || _BorderBottom || _BorderLeft; // sectors to draw
if (!_Border) return; float arcSectors = ceil(arcLength * sectors / twopi );
float arcSectorLength = arcLength / arcSectors;
if (_BorderTop) if (arcSectors <= 1)
return;
if (arcLength < pi)
{ {
// top-left // only small segment is visible
_QuadT.V3.x = _XReal; float px1 = x + radius * cosf(twopi * fromAngle);
_QuadT.V3.y = _YReal + _HReal; float py1 = y + radius * sinf(twopi * fromAngle);
// top-right float px2 = x + radius * cosf(twopi * toAngle);
_QuadT.V2.x = _XReal + _WReal; float py2 = y + radius * sinf(twopi * toAngle);
_QuadT.V2.y = _YReal + _HReal; float cx = (px1 + px2) / 2.f;
// bottom-right float cy = (py1 + py2) / 2.f;
_QuadT.V1.x = _XReal + _WReal - dRight; shape.Quad.V0.x = cx; shape.Quad.V0.y = cy;
_QuadT.V1.y = _YReal + _HReal - dTop; shape.Quad.V1.x = cx; shape.Quad.V1.y = cy;
// bottom-left
_QuadT.V0.x = _XReal + dLeft;
_QuadT.V0.y = _YReal + _HReal - dTop;
} }
else
if (_BorderRight)
{ {
// top-left shape.Quad.V0.x = x; shape.Quad.V0.y = y;
_QuadR.V3.x = _XReal + _WReal - dRight; shape.Quad.V1.x = x; shape.Quad.V1.y = y;
_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 (_BorderBottom) float a1 = fromAngle * twopi;
uint step;
for(step = 0; step < (uint)arcSectors; step++)
{ {
// top-left float a2 = a1 + arcSectorLength;
_QuadB.V3.x = _XReal + dLeft;
_QuadB.V3.y = _YReal + dBottom; shape.Quad.V2.x = x + radius * cosf(a1); shape.Quad.V2.y = y + radius * sinf(a1);
// top-right shape.Quad.V3.x = x + radius * cosf(a2); shape.Quad.V3.y = y + radius * sinf(a2);
_QuadB.V2.x = _XReal + _WReal - dRight;
_QuadB.V2.y = _YReal + dBottom; m_DrawBorders.push_back(shape);
// bottom-right
_QuadB.V1.x = _XReal + _WReal; a1 = a2;
_QuadB.V1.y = _YReal;
// bottom-left
_QuadB.V0.x = _XReal;
_QuadB.V0.y = _YReal;
} }
if (_BorderLeft) // build last sector if requested range is over 180deg
if (arcLength > pi && arcLength < twopi)
{ {
// top-left float a2 = fromAngle * twopi;
_QuadL.V3.x = _XReal; shape.Quad.V2.x = x + radius * cosf(a1); shape.Quad.V2.y = y + radius * sinf(a1);
_QuadL.V3.y = _YReal + _HReal; shape.Quad.V3.x = x + radius * cosf(a2); shape.Quad.V3.y = y + radius * sinf(a2);
// top-right m_DrawBorders.push_back(shape);
_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;
} }
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CSSBorderRenderer::draw() { void CSSBorderRenderer::updateCoords()
if (_Dirty) updateCoords(); {
if (!_Border) return; m_Dirty = false;
m_DrawBorders.clear();
CViewRenderer &rVR = *CViewRenderer::getInstance();
// TODO: no need for widget manager, if global color is set from parent if (m_MustComputeValues)
CRGBA globalColor; computeValues();
if (_ModulateGlobalColor)
globalColor = CWidgetManager::getInstance()->getGlobalColor();
// TODO: monitor ModulateGlobalColor and CurrentAlpha in table checkCoords and recalculate colors on change only if (m_Computed.Top > 0 && m_Border.Top.Color.A > 0)
// OUTSET - bottom/right darker than normal (default table style)
// INSET - top/left darker than normal
if (_BorderTop)
{ {
CRGBA borderColorT = TopColor; if (m_Border.Top.Style == CSS_LINE_STYLE_DASHED || m_Border.Top.Style == CSS_LINE_STYLE_DOTTED)
if (TopStyle == CSS_LINE_STYLE_INSET) buildDashedBorder(BORDER_TOP);
borderColorT = blend(borderColorT, CRGBA::Black, 0.5f); else
buildSolidBorder(BORDER_TOP);
}
if (_ModulateGlobalColor) if (m_Computed.Bottom > 0 && m_Border.Bottom.Color.A > 0)
borderColorT.modulateFromColor (borderColorT, globalColor); {
if (m_Border.Bottom.Style == CSS_LINE_STYLE_DASHED || m_Border.Bottom.Style == CSS_LINE_STYLE_DOTTED)
buildDashedBorder(BORDER_BOTTOM);
else
buildSolidBorder(BORDER_BOTTOM);
}
borderColorT.A = (uint8) (((uint16) CurrentAlpha * (uint16) borderColorT.A) >> 8); if (m_Computed.Right > 0 && m_Border.Right.Color.A > 0)
rVR.drawQuad(_RenderLayer, _QuadT, rVR.getBlankTextureId(), borderColorT, false); {
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 (_BorderRight)
if (m_Computed.Left > 0 && m_Border.Left.Color.A > 0)
{ {
CRGBA borderColorR = RightColor; if (m_Border.Left.Style == CSS_LINE_STYLE_DASHED || m_Border.Left.Style == CSS_LINE_STYLE_DOTTED)
if (RightStyle == CSS_LINE_STYLE_OUTSET) buildDashedBorder(BORDER_LEFT);
borderColorR = blend(borderColorR, CRGBA::Black, 0.5f); else
buildSolidBorder(BORDER_LEFT);
}
if (_ModulateGlobalColor) }
borderColorR.modulateFromColor (borderColorR, globalColor);
borderColorR.A = (uint8) (((uint16) CurrentAlpha * (uint16) borderColorR.A) >> 8); // ----------------------------------------------------------------------------
rVR.drawQuad(_RenderLayer, _QuadR, rVR.getBlankTextureId(), borderColorR, false); void CSSBorderRenderer::draw() {
} if (m_Dirty) updateCoords();
if (_BorderBottom) if (m_DrawBorders.empty()) return;
{
CRGBA borderColorB = BottomColor;
if (BottomStyle == CSS_LINE_STYLE_OUTSET)
borderColorB = blend(borderColorB, CRGBA::Black, 0.5f);
if (_ModulateGlobalColor) CViewRenderer &rVR = *CViewRenderer::getInstance();
borderColorB.modulateFromColor (borderColorB, globalColor);
borderColorB.A = (uint8) (((uint16) CurrentAlpha * (uint16) borderColorB.A) >> 8); CRGBA globalColor;
rVR.drawQuad(_RenderLayer, _QuadB, rVR.getBlankTextureId(), borderColorB, false); if (m_ModulateGlobalColor)
} globalColor = CWidgetManager::getInstance()->getGlobalColor();
if (_BorderLeft)
{
CRGBA borderColorL = LeftColor;
if (LeftStyle == CSS_LINE_STYLE_INSET)
borderColorL = blend(borderColorL, CRGBA::Black, 0.5f);
if (_ModulateGlobalColor) sint32 texId = rVR.getBlankTextureId();
borderColorL.modulateFromColor (borderColorL, globalColor); for(uint i = 0; i < m_DrawBorders.size(); ++i)
{
CRGBA color = m_DrawBorders[i].Color;
if (m_ModulateGlobalColor)
color.modulateFromColor (color, globalColor);
borderColorL.A = (uint8) (((uint16) CurrentAlpha * (uint16) borderColorL.A) >> 8); color.A = (uint8) (((uint16) CurrentAlpha * (uint16) color.A) >> 8);
rVR.drawQuad(_RenderLayer, _QuadL, rVR.getBlankTextureId(), borderColorL, false); rVR.drawQuad(m_RenderLayer, m_DrawBorders[i].Quad, texId, color, false);
} }
} }

@ -0,0 +1,225 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// 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 <http://www.gnu.org/licenses/>.
#include "stdpch.h"
#include <string>
#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<std::string> 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
{
if (m_Kind == Auto)
return 0;
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

@ -41,25 +41,57 @@ namespace NLGUI
TStyleVec CCssParser::parseDecls(const std::string &styleString) TStyleVec CCssParser::parseDecls(const std::string &styleString)
{ {
TStyleVec styles; TStyleVec styles;
std::vector<std::string> elements; size_t pos = 0;
NLMISC::splitString(styleString, ";", elements); size_t end = styleString.size();
while(pos < end)
for(uint i = 0; i < elements.size(); ++i)
{ {
std::string::size_type pos; size_t sep = styleString.find(':', pos);
pos = elements[i].find_first_of(':'); if (sep == std::string::npos)
if (pos != 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 sep = styleString.find_first_of(";'\"(", sep);
// custom properties (--name; ...;) are case sensitive if (sep == std::string::npos || styleString[sep] == ';')
std::string key = trim(elements[i].substr(0, pos)); break;
if (key.size() < 2 || (key[0] != '-' && key[1] != '-'))
key = toLowerAscii(key); if (styleString[sep] == '\'' || styleString[sep] == '"')
std::string value = trim(elements[i].substr(pos+1)); {
styles.push_back(TStylePair(key, value)); 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; return styles;
} }

@ -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 &currentWidth) const
{ {
if (!dest) return; if (!dest) return;
if (value == "inherit") if (value == "inherit")
@ -513,37 +513,19 @@ namespace NLGUI
} }
else if (value == "thin") else if (value == "thin")
{ {
*dest = 1; dest->setFloatValue(1, "px");
} }
else if (value == "medium") else if (value == "medium")
{ {
*dest = 3; dest->setFloatValue(3, "px");
} }
else if (value == "thick") else if (value == "thick")
{ {
*dest = 5; dest->setFloatValue(5, "px");
}
else if (value == "0")
{
*dest = 0;
} }
else else
{ {
float tmpf; dest->parseValue(value, false, false);
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;
}
} }
} }
@ -571,12 +553,22 @@ namespace NLGUI
*dest = CSS_LINE_STYLE_NONE; *dest = CSS_LINE_STYLE_NONE;
else if (value == "hidden") else if (value == "hidden")
*dest = CSS_LINE_STYLE_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") else if (value == "inset")
*dest = CSS_LINE_STYLE_INSET; *dest = CSS_LINE_STYLE_INSET;
else if (value == "outset") else if (value == "outset")
*dest = CSS_LINE_STYLE_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 void CCssStyle::applyPaddingWidth(const std::string &value, uint32 *dest, const uint32 currentPadding, uint32 fontSize) const
@ -606,6 +598,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 // apply style rules
void CCssStyle::apply(CStyleParams &style, const CStyleParams &current) const void CCssStyle::apply(CStyleParams &style, const CStyleParams &current) const
{ {
@ -613,18 +638,22 @@ namespace NLGUI
TStyle::const_iterator it; TStyle::const_iterator it;
for (it=style.StyleRules.begin(); it != style.StyleRules.end(); ++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); 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.BorderTopColor, current.BorderTopColor, current.TextColor); 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.BorderTopStyle, current.BorderTopStyle); 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.BorderRightWidth, current.BorderRightWidth, current.FontSize); 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.BorderRightColor, current.BorderRightColor, current.TextColor); 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.BorderRightStyle, current.BorderRightStyle); 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.BorderBottomWidth, current.BorderBottomWidth, current.FontSize); 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.BorderBottomColor, current.BorderBottomColor, current.TextColor); 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.BorderBottomStyle, current.BorderBottomStyle); 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.BorderLeftWidth, current.BorderLeftWidth, current.FontSize); 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.BorderLeftColor, current.BorderLeftColor, current.TextColor); 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.BorderLeftStyle, current.BorderLeftStyle); 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);
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-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-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); else if (it->first == "padding-bottom") applyPaddingWidth(it->second, &style.PaddingBottom, current.PaddingBottom, current.FontSize);
@ -910,13 +939,13 @@ namespace NLGUI
if (it->first == "background-color") if (it->first == "background-color")
{ {
if (it->second == "inherit") if (it->second == "inherit")
style.BackgroundColor = current.BackgroundColor; style.Background.color = current.Background.color;
else if (it->second == "transparent") 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") else if (it->second == "currentcolor")
style.BackgroundColorOver = style.TextColor; style.Background.color = style.TextColor;
else else
scanHTMLColor(it->second.c_str(), style.BackgroundColor); scanHTMLColor(it->second.c_str(), style.Background.color);
} }
else else
if (it->first == "-ryzom-background-color-over") if (it->first == "-ryzom-background-color-over")
@ -940,10 +969,13 @@ namespace NLGUI
image = image.substr(4, image.size()-5); image = image.substr(4, image.size()-5);
} }
style.StyleRules[it->first] = trimQuotes(image); style.StyleRules[it->first] = trimQuotes(image);
style.Background.setImage(style.StyleRules[it->first]);
} }
else else
if (it->first == "background-repeat") if (it->first == "background-repeat")
{ {
style.Background.setRepeat(it->second);
// TODO: remove after removing old code that depends on this
// normalize // normalize
std::string val = toLowerAscii(trim(it->second)); std::string val = toLowerAscii(trim(it->second));
std::vector<std::string> parts; std::vector<std::string> parts;
@ -957,6 +989,8 @@ namespace NLGUI
else else
if (it->first == "background-size") if (it->first == "background-size")
{ {
style.Background.setSize(it->second);
// TODO: remove after removing old code that depends on this
// normalize // normalize
std::string val = toLowerAscii(trim(it->second)); std::string val = toLowerAscii(trim(it->second));
std::vector<std::string> parts; std::vector<std::string> parts;
@ -966,6 +1000,27 @@ namespace NLGUI
style.StyleRules[it->first] = val; 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 // if outer element has underline set, then inner element cannot remove it
@ -1038,7 +1093,6 @@ namespace NLGUI
// first loop -> true // first loop -> true
// second loop -> false && break // second loop -> false && break
loop = !loop; loop = !loop;
if (next < parts.size()) if (next < parts.size())
{ {
val = toLowerAscii(parts[next]); val = toLowerAscii(parts[next]);
@ -1049,30 +1103,57 @@ namespace NLGUI
// consume 'center' // consume 'center'
next++; next++;
} }
else if (val == "left" || val == "right") else if ((bgPositionX.empty() || bgPositionX == "center") && (val == "left" || val == "right"))
{ {
bgPositionX = val; bgPositionX = val;
// consume 'left|right' // consume 'left|right'
next++; next++;
if (next < parts.size() && getCssLength(fval, unit, parts[next])) if (next < parts.size() && getCssLength(fval, unit, parts[next]))
{ {
bgPositionX += " " + toString("%.0f%s", fval, unit.c_str()); bgPositionX += " " + parts[next];
// consume css length // consume css length
next++; next++;
} }
} }
else if (val == "top" || val == "bottom") else if ((bgPositionY.empty() || bgPositionY == "center") && (val == "top" || val == "bottom"))
{ {
bgPositionY = val; bgPositionY = val;
// consume top|bottom // consume top|bottom
next++; next++;
if (next < parts.size() && getCssLength(fval, unit, parts[next])) if (next < parts.size() && getCssLength(fval, unit, parts[next]))
{ {
bgPositionY += " " + toString("%.0f%s", fval, unit.c_str()); bgPositionY += " " + parts[next];
// consume css length // consume css length
next++; 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); } while (loop);
@ -1115,7 +1196,7 @@ namespace NLGUI
if (val == "auto") if (val == "auto")
h = v = "auto"; h = v = "auto";
else else
h = v = toString("%.0f%s", fval, unit.c_str()); h = v = val;
next++; next++;
if (next < parts.size()) if (next < parts.size())
@ -1124,7 +1205,7 @@ namespace NLGUI
if (val == "auto") if (val == "auto")
v = "auto"; v = "auto";
else if (getCssLength(fval, unit, val)) else if (getCssLength(fval, unit, val))
v = toString("%.0f%s", fval, unit.c_str()); v = val;
else else
next--; // not size token next--; // not size token
} }
@ -1207,7 +1288,10 @@ namespace NLGUI
// first time background-origin is set, also set background-clip // first time background-origin is set, also set background-clip
if (!bgClipFound) if (!bgClipFound)
{
bgClipValue = val; bgClipValue = val;
bgClipFound = true;
}
} }
} }
else if (props[i] == "background-clip") else if (props[i] == "background-clip")
@ -1257,6 +1341,7 @@ namespace NLGUI
{ {
if (props[i] == "background-position") if (props[i] == "background-position")
{ {
style["background-position"] = bgPositionX + " " + bgPositionY;
style["background-position-x"] = bgPositionX; style["background-position-x"] = bgPositionX;
style["background-position-y"] = bgPositionY; style["background-position-y"] = bgPositionY;
} }
@ -1271,10 +1356,10 @@ namespace NLGUI
} }
else else
{ {
// fill in default if one is set // fill in default if one is not set
if (props[i] == "background-image") if (props[i] == "background-image")
{ {
style[props[i]] = "none"; style[props[i]] = "";
} }
else if (props[i] == "background-position") else if (props[i] == "background-position")
{ {
@ -1556,6 +1641,22 @@ namespace NLGUI
style["padding-left"] = parts[l]; style["padding-left"] = parts[l];
} }
// ***************************************************************************
void CCssStyle::expandMarginShorthand(const std::string &value, TStyle &style) const
{
std::vector<std::string> 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 void CCssStyle::expandShorthand(const std::string &prop, const std::string &value, TStyle &style) const
{ {

File diff suppressed because it is too large Load Diff

@ -29,6 +29,7 @@
#include "nel/misc/i18n.h" #include "nel/misc/i18n.h"
#include "nel/misc/xml_auto_ptr.h" #include "nel/misc/xml_auto_ptr.h"
#include "nel/gui/css_border_renderer.h" #include "nel/gui/css_border_renderer.h"
#include "nel/gui/css_background_renderer.h"
using namespace std; using namespace std;
using namespace NLMISC; using namespace NLMISC;
@ -44,9 +45,7 @@ namespace NLGUI
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
CGroupCell::CGroupCell(const TCtorParam &param) CGroupCell::CGroupCell(const TCtorParam &param)
: CInterfaceGroup(param), : CInterfaceGroup(param)
BgColor (0,0,0,0)
{ {
NewLine = false; NewLine = false;
TableRatio = 0.f; TableRatio = 0.f;
@ -58,6 +57,7 @@ namespace NLGUI
Group = new CInterfaceGroup(CViewBase::TCtorParam()); Group = new CInterfaceGroup(CViewBase::TCtorParam());
// TODO: only initialize if border is set // TODO: only initialize if border is set
Border = new CSSBorderRenderer(); Border = new CSSBorderRenderer();
Background = new CSSBackgroundRenderer();
PaddingTop = PaddingRight = PaddingBottom = PaddingLeft = 0; PaddingTop = PaddingRight = PaddingBottom = PaddingLeft = 0;
Align = Left; Align = Left;
VAlign = Middle; VAlign = Middle;
@ -66,12 +66,6 @@ namespace NLGUI
IgnoreMaxWidth = false; IgnoreMaxWidth = false;
IgnoreMinWidth = false; IgnoreMinWidth = false;
AddChildW = false; AddChildW = false;
_TextureTiled = false;
_TextureScaled = false;
_TextureXReal = 0;
_TextureYReal = 0;
_TextureWReal = 0;
_TextureHReal = 0;
setEnclosedGroupDefaultParams(); setEnclosedGroupDefaultParams();
addGroup (Group); addGroup (Group);
} }
@ -84,6 +78,11 @@ namespace NLGUI
delete Border; delete Border;
Border = NULL; Border = NULL;
} }
if (Background)
{
delete Background;
Background = NULL;
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -151,7 +150,9 @@ namespace NLGUI
else else
if( name == "bgcolor" ) if( name == "bgcolor" )
{ {
return toString( BgColor ); if (Background)
return toString( Background->getColor() );
return toString(CRGBA::Transparent);
} }
else else
if( name == "width" ) if( name == "width" )
@ -233,9 +234,12 @@ namespace NLGUI
else else
if( name == "bgcolor" ) if( name == "bgcolor" )
{ {
if (!Background)
Background = new CSSBackgroundRenderer();
CRGBA c; CRGBA c;
if( fromString( value, c ) ) if( fromString( value, c ) )
BgColor = c; Background->setColor(c);
return; return;
} }
else else
@ -342,7 +346,8 @@ namespace NLGUI
xmlSetProp( node, BAD_CAST "valign", BAD_CAST "" ); xmlSetProp( node, BAD_CAST "valign", BAD_CAST "" );
xmlSetProp( node, BAD_CAST "left_margin", BAD_CAST toString( LeftMargin ).c_str() ); xmlSetProp( node, BAD_CAST "left_margin", BAD_CAST toString( LeftMargin ).c_str() );
xmlSetProp( node, BAD_CAST "nowrap", BAD_CAST toString( NoWrap ).c_str() ); xmlSetProp( node, BAD_CAST "nowrap", BAD_CAST toString( NoWrap ).c_str() );
xmlSetProp( node, BAD_CAST "bgcolor", BAD_CAST toString( BgColor ).c_str() ); if (Background)
xmlSetProp( node, BAD_CAST "bgcolor", BAD_CAST toString( Background->getColor() ).c_str() );
if( WidthWanted != 0 ) if( WidthWanted != 0 )
xmlSetProp( node, BAD_CAST "width", BAD_CAST toString( WidthWanted ).c_str() ); xmlSetProp( node, BAD_CAST "width", BAD_CAST toString( WidthWanted ).c_str() );
@ -435,7 +440,9 @@ namespace NLGUI
ptr = (char*) xmlGetProp( cur, (xmlChar*)"bgcolor" ); ptr = (char*) xmlGetProp( cur, (xmlChar*)"bgcolor" );
if (ptr) if (ptr)
{ {
BgColor = convertColor(ptr); if (!Background)
Background = new CSSBackgroundRenderer();
Background->setColor(convertColor(ptr));
} }
// //
ptr = (char*) xmlGetProp( cur, (xmlChar*)"width" ); ptr = (char*) xmlGetProp( cur, (xmlChar*)"width" );
@ -501,72 +508,39 @@ namespace NLGUI
rVR.drawRotFlipBitmap (_RenderLayer, _XReal+_WReal-1, _YReal, 1, _HReal, 0, false, rVR.getBlankTextureId(), CRGBA(0,255,255,255) ); rVR.drawRotFlipBitmap (_RenderLayer, _XReal+_WReal-1, _YReal, 1, _HReal, 0, false, rVR.getBlankTextureId(), CRGBA(0,255,255,255) );
} }
uint8 CurrentAlpha = 255;
CGroupTable *table = NULL;
if (getParent ())
{
table = static_cast<CGroupTable*> (getParent ());
CurrentAlpha = table->CurrentAlpha;
}
// Draw the background
if (BgColor.A > 0 || !_TextureId.empty())
{ {
CViewRenderer &rVR = *CViewRenderer::getInstance(); CViewRenderer &rVR = *CViewRenderer::getInstance();
// flush draw queue to force correct draw order
rVR.flush();
bool flush = false; if (Background)
if (CurrentAlpha > 0 && !_TextureId.empty())
{ {
CRGBA col = CRGBA::White; uint8 CurrentAlpha = 255;
col.A = CurrentAlpha; CGroupTable *table = NULL;
if (getParent ())
sint32 oldSciX, oldSciY, oldSciW, oldSciH;
makeNewClip (oldSciX, oldSciY, oldSciW, oldSciH);
if (_TextureScaled && !_TextureTiled)
{ {
rVR.drawRotFlipBitmap(_RenderLayer, _TextureXReal, _TextureYReal, _WReal, _HReal, 0, false, _TextureId, col); table = static_cast<CGroupTable*> (getParent ());
CurrentAlpha = table->CurrentAlpha;
} }
else if (CurrentAlpha > 0)
{ {
if (!_TextureTiled) Background->CurrentAlpha = CurrentAlpha;
rVR.drawRotFlipBitmap(_RenderLayer, _TextureXReal, _TextureYReal, _TextureWReal, _TextureHReal, 0, false, _TextureId, col); Background->setModulateGlobalColor(_ModulateGlobalColor);
else Background->draw();
rVR.drawRotFlipBitmapTiled(_RenderLayer, _TextureXReal, _TextureYReal, _WReal, _TextureHReal, 0, false, _TextureId, 0, col); rVR.flush();
} }
restoreClip (oldSciX, oldSciY, oldSciW, oldSciH);
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) if (Border)
rVR.flush();
}
if (Border)
{
// TODO: monitor these in checkCoords and update when changed
uint8 contentAlpha = CWidgetManager::getInstance()->getGlobalColorForContent().A;
if (contentAlpha > 0)
{ {
Border->CurrentAlpha = contentAlpha; Border->CurrentAlpha = CWidgetManager::getInstance()->getGlobalColorForContent().A;
Border->setRenderLayer(_RenderLayer); if (Border->CurrentAlpha > 0)
Border->setModulateGlobalColor(_ModulateGlobalColor); {
Border->draw(); Border->setRenderLayer(_RenderLayer);
Border->setModulateGlobalColor(_ModulateGlobalColor);
Border->draw();
rVR.flush();
}
} }
} }
@ -595,62 +569,35 @@ namespace NLGUI
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CGroupCell::setTexture(const std::string & TxName) void CGroupCell::setTexture(const std::string & TxName)
{ {
if (TxName.empty() || TxName == "none") if (Background)
{ Background->setImage(TxName);
_TextureId.clear();
}
else
{
_TextureId.setTexture (TxName.c_str (), 0, 0, -1, -1, false);
updateTextureCoords();
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CGroupCell::setTextureTile(bool tiled) void CGroupCell::setTextureTile(bool tiled)
{ {
_TextureTiled = tiled; if (Background)
Background->setImageRepeat(tiled);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CGroupCell::setTextureScale(bool scaled) void CGroupCell::setTextureScale(bool scaled)
{ {
_TextureScaled = scaled; if (Background)
} Background->setImageCover(scaled);
// ----------------------------------------------------------------------------
void CGroupCell::updateTextureCoords()
{
if (_TextureId.empty()) return;
CViewRenderer &rVR = *CViewRenderer::getInstance();
rVR.getTextureSizeFromId (_TextureId, _TextureWReal, _TextureHReal);
_TextureXReal = _XReal;
_TextureYReal = _YReal + _HReal - _TextureHReal;
if (_TextureTiled && _TextureHReal > 0)
{
sint diff = (_HReal / _TextureHReal) * _TextureHReal;
_TextureYReal -= diff;
_TextureHReal += diff;
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CGroupCell::updateCoords() void CGroupCell::updateCoords()
{ {
CInterfaceGroup::updateCoords(); CInterfaceGroup::updateCoords();
updateTextureCoords();
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
NLMISC_REGISTER_OBJECT(CViewBase, CGroupTable, std::string, "table"); NLMISC_REGISTER_OBJECT(CViewBase, CGroupTable, std::string, "table");
CGroupTable::CGroupTable(const TCtorParam &param) CGroupTable::CGroupTable(const TCtorParam &param)
: CInterfaceGroup(param), : CInterfaceGroup(param)
BgColor(0,0,0,255)
{ {
_ContentValidated = false; _ContentValidated = false;
TableRatio = 0.f; TableRatio = 0.f;
@ -658,18 +605,12 @@ namespace NLGUI
// TODO: only initialize when needed // TODO: only initialize when needed
Border = new CSSBorderRenderer(); Border = new CSSBorderRenderer();
Background = new CSSBackgroundRenderer();
CellBorder = false; CellBorder = false;
CellPadding=1; CellPadding=1;
CellSpacing=2; CellSpacing=2;
ContinuousUpdate = false; ContinuousUpdate = false;
_TextureTiled = false;
_TextureScaled = false;
_TextureXReal = 0;
_TextureYReal = 0;
_TextureWReal = 0;
_TextureHReal = 0;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -737,6 +678,12 @@ namespace NLGUI
Border = NULL; Border = NULL;
} }
if (Background)
{
delete Background;
Background = NULL;
}
/* uint i; /* uint i;
for (i=0; i<_Cells.size(); i++) for (i=0; i<_Cells.size(); i++)
delete _Cells[i]; delete _Cells[i];
@ -746,46 +693,22 @@ namespace NLGUI
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CGroupTable::setTexture(const std::string & TxName) void CGroupTable::setTexture(const std::string & TxName)
{ {
if (TxName.empty() || TxName == "none") if (Background)
{ Background->setImage(TxName);
_TextureId.clear();
}
else
{
_TextureId.setTexture (TxName.c_str (), 0, 0, -1, -1, false);
updateTextureCoords();
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CGroupTable::setTextureTile(bool tiled) void CGroupTable::setTextureTile(bool tiled)
{ {
_TextureTiled = tiled; if (Background)
Background->setImageRepeat(tiled);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void CGroupTable::setTextureScale(bool scaled) void CGroupTable::setTextureScale(bool scaled)
{ {
_TextureScaled = scaled; if (Background)
} Background->setImageCover(scaled);
// ----------------------------------------------------------------------------
void CGroupTable::updateTextureCoords()
{
if (_TextureId.empty()) return;
CViewRenderer &rVR = *CViewRenderer::getInstance();
rVR.getTextureSizeFromId (_TextureId, _TextureWReal, _TextureHReal);
_TextureXReal = _XReal;
_TextureYReal = _YReal + _HReal - _TextureHReal;
if (_TextureTiled && _TextureHReal > 0)
{
sint diff = (_HReal / _TextureHReal) * _TextureHReal;
_TextureYReal -= diff;
_TextureHReal += diff;
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -1299,14 +1222,35 @@ namespace NLGUI
CInterfaceGroup::updateCoords(); CInterfaceGroup::updateCoords();
updateTextureCoords();
// update borders if present // update borders if present
if (Border) if (Border)
{ {
Border->setRect(_XReal + _MarginLeft, _YReal, _WReal, _HReal); Border->setRect(_XReal + _MarginLeft, _YReal, _WReal, _HReal);
} }
if (Background)
{
sint32 l = _XReal + _MarginLeft;
sint32 b = _YReal;
sint32 w = _WReal;
sint32 h = _HReal;
Background->setBorderArea(l, b, w, h);
if (Border) {
l += Border->getLeftWidth();
b += Border->getBottomWidth();
w -= Border->getLeftRightWidth();
h -= Border->getTopBottomWidth();
}
Background->setPaddingArea(l, b, w, h);
// TODO: padding
//CSSRect<sint32> Padding;
//l += Padding.Left;
//b += Padding.Bottom;
//w -= Padding.Left - Padding.Right;
//h -= Padding.Top - Padding.Bottom;
Background->setContentArea(l, b, w, h);
}
// update cell borders if present // update cell borders if present
for (uint32 i=0; i<_Cells.size(); i++) for (uint32 i=0; i<_Cells.size(); i++)
{ {
@ -1314,6 +1258,28 @@ namespace NLGUI
{ {
_Cells[i]->Border->setRect(_Cells[i]->_XReal, _Cells[i]->_YReal, _Cells[i]->_WReal, _Cells[i]->_HReal); _Cells[i]->Border->setRect(_Cells[i]->_XReal, _Cells[i]->_YReal, _Cells[i]->_WReal, _Cells[i]->_HReal);
} }
if (_Cells[i]->Background)
{
sint32 l = _Cells[i]->_XReal;
sint32 b = _Cells[i]->_YReal;
sint32 w = _Cells[i]->_WReal;
sint32 h = _Cells[i]->_HReal;
_Cells[i]->Background->setBorderArea(l, b, w, h);
if (_Cells[i]->Border) {
l += Border->getLeftWidth();
b += Border->getBottomWidth();
w -= Border->getLeftRightWidth();
h -= Border->getTopBottomWidth();
}
_Cells[i]->Background->setPaddingArea(l, b, w, h);
// TODO: padding
//CSSRect<sint32> Padding;
//l += Padding.Left;
//b += Padding.Bottom;
//w -= Padding.Left - Padding.Right;
//h -= Padding.Top - Padding.Bottom;
_Cells[i]->Background->setContentArea(l, b, w, h);
}
} }
// Validated // Validated
@ -1502,57 +1468,24 @@ namespace NLGUI
bool flush = false; bool flush = false;
CViewRenderer &rVR = *CViewRenderer::getInstance(); CViewRenderer &rVR = *CViewRenderer::getInstance();
if (BgColor.A > 0) // TODO: monitor these in checkCoords and update when changed
{ uint8 contentAlpha = CWidgetManager::getInstance()->getGlobalColorForContent().A;
CRGBA finalColor = BgColor; if (contentAlpha > 0)
if (_ModulateGlobalColor)
finalColor.modulateFromColor (finalColor, CWidgetManager::getInstance()->getGlobalColor());
finalColor.A = (uint8) (((uint16) CurrentAlpha * (uint16) finalColor.A) >> 8);
rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal, _WReal, _HReal, 0, false, rVR.getBlankTextureId(), finalColor);
flush = true;
}
// Draw the background
if (CurrentAlpha > 0 && !_TextureId.empty())
{ {
sint32 oldSciX, oldSciY, oldSciW, oldSciH; if (Background)
makeNewClip (oldSciX, oldSciY, oldSciW, oldSciH);
CRGBA col = CRGBA::White;
col.A = CurrentAlpha;
if (_TextureScaled && !_TextureTiled)
{
rVR.drawRotFlipBitmap(_RenderLayer, _TextureXReal, _TextureYReal, _WReal, _HReal, 0, false, _TextureId, col);
}
else
{ {
if (!_TextureTiled) Background->CurrentAlpha = CurrentAlpha;
rVR.drawRotFlipBitmap(_RenderLayer, _TextureXReal, _TextureYReal, _TextureWReal, _TextureHReal, 0, false, _TextureId, col); Background->setModulateGlobalColor(_ModulateGlobalColor);
else Background->draw();
rVR.drawRotFlipBitmapTiled(_RenderLayer, _TextureXReal, _TextureYReal, _WReal, _TextureHReal, 0, false, _TextureId, 0, col); rVR.flush();
} }
if (Border)
restoreClip (oldSciX, oldSciY, oldSciW, oldSciH);
flush = true;
}
// flush background color and image
if (flush)
rVR.flush();
if (Border)
{
// TODO: monitor these in checkCoords and update when changed
uint8 contentAlpha = CWidgetManager::getInstance()->getGlobalColorForContent().A;
if (contentAlpha > 0)
{ {
Border->CurrentAlpha = CurrentAlpha; Border->CurrentAlpha = CurrentAlpha;
Border->setRenderLayer(_RenderLayer); Border->setRenderLayer(_RenderLayer);
Border->setModulateGlobalColor(_ModulateGlobalColor); Border->setModulateGlobalColor(_ModulateGlobalColor);
Border->draw(); Border->draw();
rVR.flush();
} }
} }
} }
@ -1568,14 +1501,14 @@ namespace NLGUI
if( name == "border" ) if( name == "border" )
{ {
if (Border) if (Border)
return toString( Border->TopWidth ); return toString( Border->getTopWidth() );
return "0"; return "0";
} }
else else
if( name == "bordercolor" ) if( name == "bordercolor" )
{ {
if (Border) if (Border)
return toString( Border->TopColor ); return toString( Border->getTopColor() );
return toString(CRGBA::Transparent); return toString(CRGBA::Transparent);
} }
else else
@ -1591,7 +1524,9 @@ namespace NLGUI
else else
if( name == "bgcolor" ) if( name == "bgcolor" )
{ {
return toString( BgColor ); if (Background)
return toString( Background->getColor() );
return toString( CRGBA::Transparent );
} }
else else
if( name == "width" ) if( name == "width" )
@ -1614,10 +1549,7 @@ namespace NLGUI
{ {
if (!Border) if (!Border)
Border = new CSSBorderRenderer(); Border = new CSSBorderRenderer();
Border->TopWidth = i; Border->setWidth(i);
Border->RightWidth = i;
Border->BottomWidth = i;
Border->LeftWidth = i;
} }
return; return;
} }
@ -1629,10 +1561,7 @@ namespace NLGUI
{ {
if (!Border) if (!Border)
Border = new CSSBorderRenderer(); Border = new CSSBorderRenderer();
Border->TopColor = c; Border->setColor(c);
Border->RightColor = c;
Border->BottomColor = c;
Border->LeftColor = c;
} }
return; return;
} }
@ -1655,9 +1584,12 @@ namespace NLGUI
else else
if( name == "bgcolor" ) if( name == "bgcolor" )
{ {
if (!Background)
Background = new CSSBackgroundRenderer();
CRGBA c; CRGBA c;
if( fromString( value, c ) ) if( fromString( value, c ) )
BgColor = c; Background->setColor(c);
return; return;
} }
else else
@ -1680,12 +1612,16 @@ namespace NLGUI
xmlSetProp( node, BAD_CAST "type", BAD_CAST "table" ); xmlSetProp( node, BAD_CAST "type", BAD_CAST "table" );
if (Border) if (Border)
{ {
xmlSetProp( node, BAD_CAST "border", BAD_CAST toString( Border->TopWidth ).c_str() ); xmlSetProp( node, BAD_CAST "border", BAD_CAST toString( Border->getTopWidth() ).c_str() );
xmlSetProp( node, BAD_CAST "bordercolor", BAD_CAST toString( Border->TopColor ).c_str() ); xmlSetProp( node, BAD_CAST "bordercolor", BAD_CAST toString( Border->getTopColor() ).c_str() );
} }
xmlSetProp( node, BAD_CAST "cellpadding", BAD_CAST toString( CellPadding ).c_str() ); xmlSetProp( node, BAD_CAST "cellpadding", BAD_CAST toString( CellPadding ).c_str() );
xmlSetProp( node, BAD_CAST "cellspacing", BAD_CAST toString( CellSpacing ).c_str() ); xmlSetProp( node, BAD_CAST "cellspacing", BAD_CAST toString( CellSpacing ).c_str() );
xmlSetProp( node, BAD_CAST "bgcolor", BAD_CAST toString( BgColor ).c_str() ); if (Background)
{
xmlSetProp( node, BAD_CAST "bgcolor", BAD_CAST toString( Background->getColor() ).c_str() );
// TODO: Texture
}
if( ForceWidthMin != 0 ) if( ForceWidthMin != 0 )
xmlSetProp( node, BAD_CAST "width", BAD_CAST toString( ForceWidthMin ).c_str() ); xmlSetProp( node, BAD_CAST "width", BAD_CAST toString( ForceWidthMin ).c_str() );
@ -1708,7 +1644,7 @@ namespace NLGUI
fromString((const char*)ptr, w); fromString((const char*)ptr, w);
if (!Border) if (!Border)
Border = new CSSBorderRenderer(); Border = new CSSBorderRenderer();
Border->setWidth(w, w, w, w); Border->setWidth(w);
} }
// //
ptr = (char*) xmlGetProp( cur, (xmlChar*)"bordercolor" ); ptr = (char*) xmlGetProp( cur, (xmlChar*)"bordercolor" );
@ -1717,7 +1653,7 @@ namespace NLGUI
CRGBA c = convertColor((const char*)ptr); CRGBA c = convertColor((const char*)ptr);
if (!Border) if (!Border)
Border = new CSSBorderRenderer(); Border = new CSSBorderRenderer();
Border->setColor(c, c, c, c); Border->setColor(c);
} }
// //
ptr = (char*) xmlGetProp( cur, (xmlChar*)"cellpadding" ); ptr = (char*) xmlGetProp( cur, (xmlChar*)"cellpadding" );
@ -1735,7 +1671,10 @@ namespace NLGUI
ptr = (char*) xmlGetProp( cur, (xmlChar*)"bgcolor" ); ptr = (char*) xmlGetProp( cur, (xmlChar*)"bgcolor" );
if (ptr) if (ptr)
{ {
BgColor = convertColor((const char *) ptr); if (!Background)
Background = new CSSBackgroundRenderer();
Background->setColor(convertColor((const char *) ptr));
} }
// //
ptr = (char*) xmlGetProp( cur, (xmlChar*)"width" ); ptr = (char*) xmlGetProp( cur, (xmlChar*)"width" );

@ -206,15 +206,32 @@ 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<std::string> knownUnits = {
"%", "rem", "em", "px", "pt", "vw", "vh", "vi", "vb", "vmin", "vmax"
};
std::string::size_type pos = 0; std::string::size_type pos = 0;
std::string::size_type len = str.size(); std::string::size_type len = str.size();
if (len == 1 && str[0] == '.') if (len == 0)
{ {
return false; return false;
} }
if (len == 1 && str[0] == '0')
{
value = 0;
unit.clear();
return true;
}
// +100px; -100px
if (str[0] == '+')
pos++;
else if (neg && str[0] == '-')
pos++;
while(pos < len) while(pos < len)
{ {
bool isNumeric = (str[pos] >= '0' && str[pos] <= '9') bool isNumeric = (str[pos] >= '0' && str[pos] <= '9')
@ -229,7 +246,7 @@ namespace NLGUI
} }
unit = toLowerAscii(str.substr(pos)); 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); std::string tmpstr = str.substr(0, pos);
return fromString(tmpstr, value); return fromString(tmpstr, value);
@ -611,6 +628,12 @@ namespace NLGUI
return false; return false;
} }
if (nlstricmp(src, "transparent") == 0)
{
dest = CRGBA::Transparent;
return true;
}
{ {
// slow but should suffice for now // slow but should suffice for now
for(uint k = 0; k < sizeofarray(htmlColorNameToRGBA); ++k) for(uint k = 0; k < sizeofarray(htmlColorNameToRGBA); ++k)

@ -46,8 +46,8 @@ namespace NLGUI
{ {
CGroupHTML *groupHtml = dynamic_cast<CGroupHTML*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:webig:content:html")); CGroupHTML *groupHtml = dynamic_cast<CGroupHTML*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:webig:content:html"));
if (groupHtml) { if (groupHtml) {
_HtmlDownload = false; groupHtml->removeImageDownload(_HtmlDownload, dynamic_cast<CViewBase*>(this));
groupHtml->removeImageDownload(dynamic_cast<CViewBase*>(this)); _HtmlDownload = NULL;
} }
} }
} }
@ -476,12 +476,14 @@ namespace NLGUI
if (!CFile::fileExists(localname)) if (!CFile::fileExists(localname))
localname = "web_del.tga"; localname = "web_del.tga";
_TextureId.setTexture (localname.c_str(), _TxtOffsetX, _TxtOffsetY, _TxtWidth, _TxtHeight, false); _TextureId.setTexture (localname.c_str(), _TxtOffsetX, _TxtOffsetY, _TxtWidth, _TxtHeight, false);
_HtmlDownload = true; _HtmlDownload = groupHtml->addImageDownload(TxName, dynamic_cast<CViewBase*>(this));
groupHtml->addImageDownload(TxName, dynamic_cast<CViewBase*>(this));
} }
} }
else else
{
_HtmlDownload = NULL;
_TextureId.setTexture (TxName.c_str (), _TxtOffsetX, _TxtOffsetY, _TxtWidth, _TxtHeight, false); _TextureId.setTexture (TxName.c_str (), _TxtOffsetX, _TxtOffsetY, _TxtWidth, _TxtHeight, false);
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

@ -267,10 +267,18 @@ namespace NLGUI
TGlobalTextureList::iterator ite = _GlobalTextures.begin(); TGlobalTextureList::iterator ite = _GlobalTextures.begin();
while (ite != _GlobalTextures.end()) while (ite != _GlobalTextures.end())
{ {
UTextureFile *tf = dynamic_cast<NL3D::UTextureFile *>(ite->Texture); if (ite->Texture)
if (tf)
{ {
driver->deleteTextureFile (tf); UTextureFile *tf = dynamic_cast<NL3D::UTextureFile *>(ite->Texture);
if (tf)
{
driver->deleteTextureFile (tf);
}
else
{
UTextureMem *tf = dynamic_cast<NL3D::UTextureMem *>(ite->Texture);
if (tf) driver->deleteTextureMem(tf);
}
} }
ite++; ite++;
} }
@ -999,6 +1007,207 @@ namespace NLGUI
ite->Texture = externalTexture; 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;
}
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<NL3D::UTextureFile *>(oldTexture);
if (tf)
{
driver->deleteTextureFile (tf);
}
else
{
UTextureMem *tf = dynamic_cast<NL3D::UTextureMem *>(oldTexture);
if (tf) driver->deleteTextureMem(tf);
}
}
}
/* /*
* createTexture * createTexture
*/ */
@ -1030,27 +1239,20 @@ namespace NLGUI
// If global texture not exists create it // If global texture not exists create it
if (ite == _GlobalTextures.end()) if (ite == _GlobalTextures.end())
{ {
SGlobalTexture gtTmp;
gtTmp.FromGlobaleTexture = false;
string filename = CPath::lookup (sLwrGTName, false); string filename = CPath::lookup (sLwrGTName, false);
if (filename.empty() ) return -1; if (filename.empty() ) return -1;
CIFile ifTmp;
if (ifTmp.open(filename)) SGlobalTexture gtTmp;
{
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);
gtTmp.Name = sLwrGTName; gtTmp.Name = sLwrGTName;
if (!loadTextureFromFile(&gtTmp, filename))
return -1;
gtTmp.Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff); gtTmp.Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff);
if(uploadDXTC) if(uploadDXTC)
gtTmp.Texture->setUploadFormat(UTexture::DXTC5); gtTmp.Texture->setUploadFormat(UTexture::DXTC5);
gtTmp.Texture->setReleasable(bReleasable); gtTmp.Texture->setReleasable(bReleasable);
_GlobalTextures.push_back(gtTmp); _GlobalTextures.push_back(gtTmp);
ite = _GlobalTextures.end(); ite = _GlobalTextures.end();
ite--; ite--;
@ -1105,44 +1307,10 @@ namespace NLGUI
// If global texture not exists create it // If global texture not exists create it
if (ite == _GlobalTextures.end()) 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; SGlobalTexture gtTmp;
gtTmp.FromGlobaleTexture = false; if (!loadTextureFromString(&gtTmp, data))
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; 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());
return -1;
}
memcpy(texture->getPointer(), btm.getPixels().getPtr(), btm.getSize() * 4);
gtTmp.Texture = texture;
gtTmp.Name = md5hash; gtTmp.Name = md5hash;
gtTmp.Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff); gtTmp.Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff);
gtTmp.Texture->setReleasable(bReleasable); gtTmp.Texture->setReleasable(bReleasable);
@ -1258,6 +1426,8 @@ namespace NLGUI
// This one ? // This one ?
if (&(*iteGT) == gt) if (&(*iteGT) == gt)
{ {
if (iteGT->Texture == NULL)
return;
// Remove this global texture // Remove this global texture
UTextureFile *tf = dynamic_cast<NL3D::UTextureFile *>(iteGT->Texture); UTextureFile *tf = dynamic_cast<NL3D::UTextureFile *>(iteGT->Texture);
if (tf) if (tf)
@ -1439,6 +1609,13 @@ namespace NLGUI
TGlobalTextureList::iterator ite = _GlobalTextures.begin(); TGlobalTextureList::iterator ite = _GlobalTextures.begin();
while (ite != _GlobalTextures.end()) while (ite != _GlobalTextures.end())
{ {
// texture not loaded yet
if (ite->Texture == NULL)
{
++ite;
continue;
}
// TMP TMP // TMP TMP
// volatile SGlobalTexture *sg = &(*ite); // volatile SGlobalTexture *sg = &(*ite);
CLayer &layer= ite->Layers[layerId]; CLayer &layer= ite->Layers[layerId];

@ -46,7 +46,7 @@ void checkRuleset(CHtmlElement &elm, CCssStyle &style, TStyleVec testset, bool e
} }
else if (it.first == "background-color") 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()); printf(" (%s)\n", elm.toString().c_str());
failed2 = false; failed2 = false;
} }

@ -156,7 +156,7 @@ void CRingAccess::init()
CXMLAutoPtr sheetClientPtr( (const char*) xmlGetProp(entityAccess, (xmlChar*) "sheetClient") ); CXMLAutoPtr sheetClientPtr( (const char*) xmlGetProp(entityAccess, (xmlChar*) "sheetClient") );
CXMLAutoPtr sheetPtr( (const char*) xmlGetProp(entityAccess, (xmlChar*) "sheetServer") ); 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()); nlerror( "Syntax error in %s", pathFileName.c_str());
return; return;

Loading…
Cancel
Save