diff options
-rw-r--r-- | builtin/mm_menubar.lua | 4 | ||||
-rw-r--r-- | builtin/modstore.lua | 6 | ||||
-rw-r--r-- | doc/lua_api.txt | 45 | ||||
-rw-r--r-- | src/guiFormSpecMenu.cpp | 261 | ||||
-rw-r--r-- | src/guiFormSpecMenu.h | 12 |
5 files changed, 259 insertions, 69 deletions
diff --git a/builtin/mm_menubar.lua b/builtin/mm_menubar.lua index c3ddbb289..a60ea7ae5 100644 --- a/builtin/mm_menubar.lua +++ b/builtin/mm_menubar.lua @@ -30,8 +30,8 @@ end -------------------------------------------------------------------------------- function menubar.refresh() - menubar.formspec = "box[-0.3,5.625;12.4,1.3;000000]" .. - "box[-0.3,5.6;12.4,0.05;FFFFFF]" + menubar.formspec = "box[-0.3,5.625;12.4,1.3;#000000]" .. + "box[-0.3,5.6;12.4,0.05;#FFFFFF]" menubar.buttons = {} local button_base = -0.25 diff --git a/builtin/modstore.lua b/builtin/modstore.lua index 43f7759ad..b364ce6bd 100644 --- a/builtin/modstore.lua +++ b/builtin/modstore.lua @@ -183,9 +183,9 @@ function modstore.getmodlist(list) retval = retval .. "label[10,-0.4;" .. fgettext("Page $1 of $2", list.page+1, list.pagecount) .. "]" retval = retval .. "button[11.6,-0.1;0.5,0.5;btn_modstore_page_up;^]" - retval = retval .. "box[11.6,0.35;0.28,8.6;000000]" + retval = retval .. "box[11.6,0.35;0.28,8.6;#000000]" local scrollbarpos = 0.35 + (8.1/(list.pagecount-1)) * list.page - retval = retval .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;32CD32]" + retval = retval .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;#32CD32]" retval = retval .. "button[11.6,9.0;0.5,0.5;btn_modstore_page_down;v]" @@ -206,7 +206,7 @@ function modstore.getmodlist(list) if details ~= nil then local screenshot_ypos = (i-1 - (list.page * modstore.modsperpage))*1.9 +0.2 - retval = retval .. "box[0," .. screenshot_ypos .. ";11.4,1.75;FFFFFF]" + retval = retval .. "box[0," .. screenshot_ypos .. ";11.4,1.75;#FFFFFF]" --screenshot if details.screenshot_url ~= nil and diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 933050224..689496e22 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -873,6 +873,22 @@ list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;] list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>] ^ Show an inventory list +listcolors[<slot_bg_normal>;<slot_bg_hover>] +^ Sets background color of slots in HEX-Color format +^ Sets background color of slots on mouse hovering + +listcolors[<slot_bg_normal>;<slot_bg_hover>;<slot_border>] +^ Sets background color of slots in HEX-Color format +^ Sets background color of slots on mouse hovering +^ Sets color of slots border + +listcolors[<slot_bg_normal>;<slot_bg_hover>;<slot_border>;<tooltip_bgcolor>;<tooltip_fontcolor>] +^ Sets background color of slots in HEX-Color format +^ Sets background color of slots on mouse hovering +^ Sets color of slots border +^ Sets background color of tooltips +^ Sets font color of tooltips + image[<X>,<Y>;<W>,<H>;<texture name>] ^ Show an image ^ Position and size units are inventory slots @@ -881,11 +897,21 @@ item_image[<X>,<Y>;<W>,<H>;<item name>] ^ Show an inventory image of registered item/node ^ Position and size units are inventory slots +bgcolor[<color>;<fullscreen>] +^ Sets background color of formspec in HEX-Color format +^ If true the background color is drawn fullscreen (does not effect the size of the formspec) + background[<X>,<Y>;<W>,<H>;<texture name>] ^ Use a background. Inventory rectangles are not drawn then. ^ Position and size units are inventory slots ^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px +background[<X>,<Y>;<W>,<H>;<texture name>;<auto_clip>] +^ Use a background. Inventory rectangles are not drawn then. +^ Position and size units are inventory slots +^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px +^ If true the background is clipped to formspec size (x and y are used as offset values, w and h are ignored) + pwdfield[<X>,<Y>;<W>,<H>;<name>;<label>] ^ Textual password style field; will be sent to server when a button is clicked ^ x and y position the field relative to the top left of the menu @@ -972,7 +998,7 @@ textlist[<X>,<Y>;<W>,<H>;<name>;<listelem 1>,<listelem 2>,...,<listelem n>] ^ x and y position the itemlist relative to the top left of the menu ^ w and h are the size of the itemlist ^ name fieldname sent to server on doubleclick value is current selected element -^ listelements can be prepended by #color in hexadecimal format RRGGBB, +^ listelements can be prepended by #color in hexadecimal format RRGGBB (only), ^ if you want a listelement to start with # write ## textlist[<X>,<Y>;<W>,<H>;<name>;<listelem 1>,<listelem 2>,...,<listelem n>;<selected idx>;<transparent>] @@ -980,7 +1006,7 @@ textlist[<X>,<Y>;<W>,<H>;<name>;<listelem 1>,<listelem 2>,...,<listelem n>;<sele ^ x and y position the itemlist relative to the top left of the menu ^ w and h are the size of the itemlist ^ name fieldname sent to server on doubleclick value is current selected element -^ listelements can be prepended by #RRGGBB in hexadecimal format +^ listelements can be prepended by #RRGGBB (only) in hexadecimal format ^ if you want a listelement to start with # write ## ^ index to be selected within textlist ^ true/false draw transparent background @@ -998,7 +1024,7 @@ box[<X>,<Y>;<W>,<H>;<color>] ^ simple colored semitransparent box ^ x and y position the box relative to the top left of the menu ^ w and h are the size of box -^ color in hexadecimal format RRGGBB +^ color in HEX-Color format dropdown[<X>,<Y>;<W>;<name>;<item 1>,<item 2>, ...,<item n>;<selected idx>] ^ show a dropdown field @@ -1007,7 +1033,7 @@ dropdown[<X>,<Y>;<W>;<name>;<item 1>,<item 2>, ...,<item n>;<selected idx>] ^ fieldname data is transfered to lua ^ items to be shown in dropdown ^ index of currently selected dropdown item -^ color in hexadecimal format RRGGBB +^ color in hexadecimal format RRGGBB (only) checkbox[<X>,<Y>;<name>;<label>;<selected>] ^ show a checkbox @@ -1027,6 +1053,17 @@ Inventory location: - "nodemeta:<X>,<Y>,<Z>": Any node metadata - "detached:<name>": A detached inventory +HEX-Color +--------- +#RGB +^ defines a color in hexadecimal format +#RGBA +^ defines a color in hexadecimal format and alpha channel +#RRGGBB +^ defines a color in hexadecimal format +#RRGGBBAA +^ defines a color in hexadecimal format and alpha channel + Vector helpers --------------- vector.new([x[, y, z]]) -> vector diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index f3d4568ef..111e17d6c 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -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; + return true; +} - u32 color_value; - std::istringstream iss(color); - iss >> std::hex >> color_value; - - outcolor = irr::video::SColor(color_value); +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; - outcolor.setAlpha(255); - return true; + // 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; } diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 20ab52e8b..8b0e50379 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -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 |