1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-07-02 16:38:41 +00:00

Reworked formspecs and kahrl's hexcolor parser

This commit is contained in:
BlockMen 2013-11-02 04:26:44 +01:00
parent 0b78889289
commit 25edae00ea
5 changed files with 260 additions and 70 deletions

View file

@ -604,7 +604,7 @@ void GUIFormSpecMenu::parseButton(parserData* data,std::string element,std::stri
void GUIFormSpecMenu::parseBackground(parserData* data,std::string element) {
std::vector<std::string> parts = split(element,';');
if (parts.size() == 3) {
if ((parts.size() == 3) || (parts.size() == 4)) {
std::vector<std::string> v_pos = split(parts[0],',');
std::vector<std::string> v_geom = split(parts[1],',');
std::string name = unescape_string(parts[2]);
@ -620,6 +620,14 @@ void GUIFormSpecMenu::parseBackground(parserData* data,std::string element) {
geom.X = stof(v_geom[0]) * (float)spacing.X;
geom.Y = stof(v_geom[1]) * (float)spacing.Y;
if (parts.size() == 4) {
m_clipbackground = is_yes(parts[3]);
if (m_clipbackground) {
pos.X = stoi(v_pos[0]); //acts as offset
pos.Y = stoi(v_pos[1]); //acts as offset
}
}
if(data->bp_set != 2)
errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
@ -686,16 +694,16 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) {
e->addItem(narrow_to_wide(unescape_string(items[i])).c_str() +1);
}
else {
std::string color = items[i].substr(1,6);
std::string color = items[i].substr(0,7);
std::wstring toadd =
narrow_to_wide(unescape_string(items[i]).c_str() + 7);
e->addItem(toadd.c_str());
irr::video::SColor toset;
video::SColor tmp_color;
if (parseColor(color, toset))
e->setItemOverrideColor(i,toset);
if (parseColor(color, tmp_color, false))
e->setItemOverrideColor(i,tmp_color);
}
}
else {
@ -1329,7 +1337,6 @@ void GUIFormSpecMenu::parseBox(parserData* data,std::string element) {
if (parts.size() == 3) {
std::vector<std::string> v_pos = split(parts[0],',');
std::vector<std::string> v_geom = split(parts[1],',');
std::string color_str = parts[2];
MY_CHECKPOS("box",0);
MY_CHECKGEOM("box",1);
@ -1342,10 +1349,10 @@ void GUIFormSpecMenu::parseBox(parserData* data,std::string element) {
geom.X = stof(v_geom[0]) * (float)spacing.X;
geom.Y = stof(v_geom[1]) * (float)spacing.Y;
irr::video::SColor color;
video::SColor tmp_color;
if (parseColor(color_str, color)) {
BoxDrawSpec spec(pos,geom,color);
if (parseColor(parts[2], tmp_color, false)) {
BoxDrawSpec spec(pos, geom, tmp_color);
m_boxes.push_back(spec);
}
@ -1357,6 +1364,46 @@ void GUIFormSpecMenu::parseBox(parserData* data,std::string element) {
errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl;
}
void GUIFormSpecMenu::parseBackgroundColor(parserData* data,std::string element) {
std::vector<std::string> parts = split(element,';');
if ((parts.size() == 1) || (parts.size() == 2)) {
parseColor(parts[0],m_bgcolor,false);
if (parts.size() == 2) {
std::string fullscreen = parts[1];
m_bgfullscreen = is_yes(fullscreen);
}
return;
}
errorstream<< "Invalid bgcolor element(" << parts.size() << "): '" << element << "'" << std::endl;
}
void GUIFormSpecMenu::parseListColors(parserData* data,std::string element) {
std::vector<std::string> parts = split(element,';');
if (parts.size() == 2) || (parts.size() == 3) || (parts.size() == 5)) {
parseColor(parts[0], m_slotbg_n, false);
parseColor(parts[1], m_slotbg_h, false);
if (parts.size() >= 3) {
if (parseColor(parts[2], m_slotbordercolor, false)) {
m_slotborder = true;
}
}
if (parts.size() == 5) {
video::SColor tmp_color;
if (parseColor(parts[3], tmp_color, false))
m_tooltip_element->setBackgroundColor(tmp_color);
if (parseColor(parts[4], tmp_color, false))
m_tooltip_element->setOverrideColor(tmp_color);
}
return;
}
errorstream<< "Invalid listcolors element(" << parts.size() << "): '" << element << "'" << std::endl;
}
void GUIFormSpecMenu::parseElement(parserData* data,std::string element) {
//some prechecks
@ -1467,6 +1514,16 @@ void GUIFormSpecMenu::parseElement(parserData* data,std::string element) {
return;
}
if (type == "bgcolor") {
parseBackgroundColor(data,description);
return;
}
if (type == "listcolors") {
parseListColors(data,description);
return;
}
// Ignore others
infostream
<< "Unknown DrawSpec: type="<<type<<", data=\""<<description<<"\""
@ -1537,6 +1594,32 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
m_fields.clear();
m_boxes.clear();
// Set default values (fits old formspec values)
m_bgcolor = video::SColor(140,0,0,0);
m_bgfullscreen = false;
m_slotbg_n = video::SColor(255,128,128,128);
m_slotbg_h = video::SColor(255,192,192,192);
m_slotbordercolor = video::SColor(200,0,0,0);
m_slotborder = false;
m_clipbackground = false;
// Add tooltip
{
// Note: parent != this so that the tooltip isn't clipped by the menu rectangle
m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
m_tooltip_element->enableOverrideColor(true);
m_tooltip_element->setBackgroundColor(video::SColor(255,110,130,60));
m_tooltip_element->setDrawBackground(true);
m_tooltip_element->setDrawBorder(true);
m_tooltip_element->setOverrideColor(video::SColor(255,255,255,255));
m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
m_tooltip_element->setWordWrap(false);
//we're not parent so no autograb for this one!
m_tooltip_element->grab();
}
std::vector<std::string> elements = split(m_formspec_string,']');
@ -1544,18 +1627,6 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
parseElement(&mydata,elements[i]);
}
// If there's inventory, put the usage string at the bottom
if (m_inventorylists.size())
{
changeCtype("");
core::rect<s32> rect(0, 0, mydata.size.X-padding.X*2, mydata.helptext_h);
rect = rect + v2s32((mydata.size.X/2 - mydata.rect.getWidth()/2) +5,
mydata.size.Y-5-mydata.helptext_h);
const wchar_t *text = wgettext("Left click: Move all items, Right click: Move single item");
Environment->addStaticText(text, rect, false, true, this, 256);
delete[] text;
changeCtype("C");
}
// If there's fields, add a Proceed button
if (m_fields.size() && mydata.bp_set != 2)
{
@ -1583,20 +1654,6 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
}
changeCtype("C");
}
// Add tooltip
{
// Note: parent != this so that the tooltip isn't clipped by the menu rectangle
m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
m_tooltip_element->enableOverrideColor(true);
m_tooltip_element->setBackgroundColor(video::SColor(255,110,130,60));
m_tooltip_element->setDrawBackground(true);
m_tooltip_element->setDrawBorder(true);
m_tooltip_element->setOverrideColor(video::SColor(255,255,255,255));
m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
m_tooltip_element->setWordWrap(false);
//we're not parent so no autograb for this one!
m_tooltip_element->grab();
}
//set initial focus if parser didn't set it
focused_element = Environment->getFocus();
@ -1681,16 +1738,31 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
if(phase == 0)
{
if(hovering && m_selected_item)
{
video::SColor bgcolor(255,192,192,192);
driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
}
if(hovering)
driver->draw2DRectangle(m_slotbg_h, rect, &AbsoluteClippingRect);
else
{
video::SColor bgcolor(255,128,128,128);
driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
}
driver->draw2DRectangle(m_slotbg_n, rect, &AbsoluteClippingRect);
}
//Draw inv slot borders
if (m_slotborder) {
s32 x1 = rect.UpperLeftCorner.X;
s32 y1 = rect.UpperLeftCorner.Y;
s32 x2 = rect.LowerRightCorner.X;
s32 y2 = rect.LowerRightCorner.Y;
s32 border = 1;
driver->draw2DRectangle(m_slotbordercolor,
core::rect<s32>(v2s32(x1 - border, y1 - border),
v2s32(x2 + border, y1)), NULL);
driver->draw2DRectangle(m_slotbordercolor,
core::rect<s32>(v2s32(x1 - border, y2),
v2s32(x2 + border, y2 + border)), NULL);
driver->draw2DRectangle(m_slotbordercolor,
core::rect<s32>(v2s32(x1 - border, y1),
v2s32(x1, y2)), NULL);
driver->draw2DRectangle(m_slotbordercolor,
core::rect<s32>(v2s32(x2, y1),
v2s32(x2 + border, y2)), NULL);
}
if(phase == 1)
@ -1771,8 +1843,12 @@ void GUIFormSpecMenu::drawMenu()
return;
video::IVideoDriver* driver = Environment->getVideoDriver();
video::SColor bgcolor(140,0,0,0);
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
v2u32 screenSize = driver->getScreenSize();
core::rect<s32> allbg(0, 0, screenSize.X , screenSize.Y);
if (m_bgfullscreen)
driver->draw2DRectangle(m_bgcolor, allbg, &allbg);
else
driver->draw2DRectangle(m_bgcolor, AbsoluteRect, &AbsoluteClippingRect);
m_tooltip_element->setVisible(false);
@ -1789,6 +1865,15 @@ void GUIFormSpecMenu::drawMenu()
core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
// Image rectangle on screen
core::rect<s32> rect = imgrect + spec.pos;
if (m_clipbackground) {
core::dimension2d<s32> absrec_size = AbsoluteRect.getSize();
rect = core::rect<s32>(AbsoluteRect.UpperLeftCorner.X - spec.pos.X,
AbsoluteRect.UpperLeftCorner.Y - spec.pos.Y,
AbsoluteRect.UpperLeftCorner.X + absrec_size.Width + spec.pos.X*2,
AbsoluteRect.UpperLeftCorner.Y + absrec_size.Height + spec.pos.Y*2);
}
const video::SColor color(255,255,255,255);
const video::SColor colors[] = {color,color,color,color};
driver->draw2DImage(texture, rect,
@ -1880,10 +1965,8 @@ void GUIFormSpecMenu::drawMenu()
Draw items
Phase 0: Item slot rectangles
Phase 1: Item images; prepare tooltip
If backgrounds used, do not draw Item slot rectangles
*/
int start_phase=0;
if (m_backgrounds.size() > 0) start_phase=1;
for(int phase=start_phase; phase<=1; phase++)
for(u32 i=0; i<m_inventorylists.size(); i++)
{
@ -2643,18 +2726,78 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
return Parent ? Parent->OnEvent(event) : false;
}
bool GUIFormSpecMenu::parseColor(std::string color, irr::video::SColor& outcolor) {
outcolor = irr::video::SColor(0,0,0,0);
if (!string_allowed(color, "0123456789abcdefABCDEF"))
static inline bool hex_digit_decode(char hexdigit, unsigned char &value)
{
if(hexdigit >= '0' && hexdigit <= '9')
value = hexdigit - '0';
else if(hexdigit >= 'A' && hexdigit <= 'F')
value = hexdigit - 'A' + 10;
else if(hexdigit >= 'a' && hexdigit <= 'f')
value = hexdigit - 'a' + 10;
else
return false;
u32 color_value;
std::istringstream iss(color);
iss >> std::hex >> color_value;
outcolor = irr::video::SColor(color_value);
outcolor.setAlpha(255);
return true;
}
bool GUIFormSpecMenu::parseColor(std::string &value, video::SColor &color, bool quiet)
{
const char *hexpattern = NULL;
if (value[0] == '#') {
if (value.size() == 9)
hexpattern = "#RRGGBBAA";
else if (value.size() == 7)
hexpattern = "#RRGGBB";
else if (value.size() == 5)
hexpattern = "#RGBA";
else if (value.size() == 4)
hexpattern = "#RGB";
}
if (hexpattern) {
assert(strlen(hexpattern) == value.size());
video::SColor outcolor(255, 255, 255, 255);
for (size_t pos = 0; pos < value.size(); ++pos) {
// '#' in the pattern means skip that character
if (hexpattern[pos] == '#')
continue;
// Else assume hexpattern[pos] is one of 'R' 'G' 'B' 'A'
// Read one or two digits, depending on hexpattern
unsigned char c1, c2;
if (hexpattern[pos+1] == hexpattern[pos]) {
// Two digits, e.g. hexpattern == "#RRGGBB"
if (!hex_digit_decode(value[pos], c1) ||
!hex_digit_decode(value[pos+1], c2))
goto fail;
++pos;
}
else {
// One digit, e.g. hexpattern == "#RGB"
if (!hex_digit_decode(value[pos], c1))
goto fail;
c2 = c1;
}
u32 colorpart = ((c1 & 0x0f) << 4) | (c2 & 0x0f);
// Update outcolor with newly read color part
if (hexpattern[pos] == 'R')
outcolor.setRed(colorpart);
else if (hexpattern[pos] == 'G')
outcolor.setGreen(colorpart);
else if (hexpattern[pos] == 'B')
outcolor.setBlue(colorpart);
else if (hexpattern[pos] == 'A')
outcolor.setAlpha(colorpart);
}
color = outcolor;
return true;
}
// Optionally, named colors could be implemented here
fail:
if (!quiet)
errorstream<<"Invalid color: \""<<value<<"\""<<std::endl;
return false;
}

View file

@ -285,6 +285,14 @@ protected:
bool m_allowclose;
bool m_lock;
v2u32 m_lockscreensize;
bool m_bgfullscreen;
bool m_slotborder;
bool m_clipbackground;
video::SColor m_bgcolor;
video::SColor m_slotbg_n;
video::SColor m_slotbg_h;
video::SColor m_slotbordercolor;
private:
typedef struct {
v2s32 size;
@ -334,8 +342,10 @@ private:
void parseItemImageButton(parserData* data,std::string element);
void parseTabHeader(parserData* data,std::string element);
void parseBox(parserData* data,std::string element);
void parseBackgroundColor(parserData* data,std::string element);
void parseListColors(parserData* data,std::string element);
bool parseColor(std::string color, irr::video::SColor& outcolor);
bool parseColor(std::string &value, video::SColor &color, bool quiet);
};
class FormspecFormSource: public IFormSource