Add CSSLength, CSSBackground, CSSBackgroundRenderer classes
parent
65edd9f95d
commit
dfe45029ab
@ -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,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
|
||||
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,222 @@
|
||||
// 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
|
||||
{
|
||||
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
|
||||
|
Loading…
Reference in New Issue