aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorv-rob <robinsonvincent89@gmail.com>2020-08-19 18:14:47 -0700
committerGitHub <noreply@github.com>2020-08-19 18:14:47 -0700
commit83d0c360cc648e02650aa4d341dbf5205c1e708f (patch)
tree4defb9776d28281059da0b145df9cfeca921f489
parent471497fa9198695becae26a83097ec80643b4cc5 (diff)
downloadminetest-83d0c360cc648e02650aa4d341dbf5205c1e708f.tar.gz
minetest-83d0c360cc648e02650aa4d341dbf5205c1e708f.tar.bz2
minetest-83d0c360cc648e02650aa4d341dbf5205c1e708f.zip
Add gradients and borders to FormSpec boxes (#8676)
-rw-r--r--doc/lua_api.txt43
-rw-r--r--src/gui/StyleSpec.h64
-rw-r--r--src/gui/guiBox.cpp86
-rw-r--r--src/gui/guiBox.h11
-rw-r--r--src/gui/guiFormSpecMenu.cpp61
5 files changed, 218 insertions, 47 deletions
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index ba55033dd..49fbe0d94 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -2442,6 +2442,8 @@ Elements
* Simple colored box
* `color` is color specified as a `ColorString`.
If the alpha component is left blank, the box will be semitransparent.
+ If the color is not specified, the box will use the options specified by
+ its style. If the color is specified, all styling options will be ignored.
### `dropdown[<X>,<Y>;<W>;<name>;<item 1>,<item 2>, ...,<item n>;<selected idx>;<index event>]`
@@ -2708,21 +2710,23 @@ Setting a property to nothing will reset it to the default value. For example:
Some types may inherit styles from parent types.
* animated_image, inherits from image
+* box
* button
* button_exit, inherits from button
* checkbox
-* scrollbar
-* table
-* textlist
* dropdown
* field
-* pwdfield, inherits from field
-* textarea
-* label
-* vertlabel, inherits from field
+* image
* image_button
* item_image_button
+* label
+* pwdfield, inherits from field
+* scrollbar
* tabheader
+* table
+* textarea
+* textlist
+* vertlabel, inherits from label
### Valid Properties
@@ -2731,7 +2735,18 @@ Some types may inherit styles from parent types.
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* box
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
- * Default to false in formspec_version version 3 or higher
+ * Defaults to false in formspec_version version 3 or higher
+ * **Note**: `colors`, `bordercolors`, and `borderwidths` accept multiple input types:
+ * Single value (e.g. `#FF0`): All corners/borders.
+ * Two values (e.g. `red,#FFAAFF`): top-left and bottom-right,top-right and bottom-left/
+ top and bottom,left and right.
+ * Four values (e.g. `blue,#A0F,green,#FFFA`): top-left/top and rotates clockwise.
+ * These work similarly to CSS borders.
+ * colors - `ColorString`. Sets the color(s) of the box corners. Default `black`.
+ * bordercolors - `ColorString`. Sets the color(s) of the borders. Default `black`.
+ * borderwidths - Integer. Sets the width(s) of the borders in pixels. If the width is
+ negative, the border will extend inside the box, whereas positive extends outside
+ the box. A width of zero results in no border; this is default.
* button, button_exit, image_button, item_image_button
* alpha - boolean, whether to draw alpha in bgimg. Default true.
* bgcolor - color, sets button tint.
@@ -2767,12 +2782,6 @@ Some types may inherit styles from parent types.
* textcolor - color, default white.
* checkbox
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
-* scrollbar
- * noclip - boolean, set to true to allow the element to exceed formspec bounds.
-* table, textlist
- * font - Sets font type. See button `font` property for more information.
- * font_size - Sets font size. See button `font_size` property for more information.
- * noclip - boolean, set to true to allow the element to exceed formspec bounds.
* dropdown
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* field, pwdfield, textarea
@@ -2797,9 +2806,15 @@ Some types may inherit styles from parent types.
* fgimg_pressed - image when pressed. Defaults to fgimg when not provided.
* This is deprecated, use states instead.
* NOTE: The parameters of any given image_button will take precedence over fgimg/fgimg_pressed
+* scrollbar
+ * noclip - boolean, set to true to allow the element to exceed formspec bounds.
* tabheader
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* textcolor - color. Default white.
+* table, textlist
+ * font - Sets font type. See button `font` property for more information.
+ * font_size - Sets font size. See button `font_size` property for more information.
+ * noclip - boolean, set to true to allow the element to exceed formspec bounds.
### Valid States
diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h
index 67caf4f7b..36ad51a89 100644
--- a/src/gui/StyleSpec.h
+++ b/src/gui/StyleSpec.h
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/string.h"
#include <algorithm>
#include <array>
+#include <vector>
#pragma once
@@ -50,6 +51,9 @@ public:
PADDING,
FONT,
FONT_SIZE,
+ COLORS,
+ BORDERCOLORS,
+ BORDERWIDTHS,
NUM_PROPERTIES,
NONE
};
@@ -106,6 +110,12 @@ public:
return FONT;
} else if (name == "font_size") {
return FONT_SIZE;
+ } else if (name == "colors") {
+ return COLORS;
+ } else if (name == "bordercolors") {
+ return BORDERCOLORS;
+ } else if (name == "borderwidths") {
+ return BORDERWIDTHS;
} else {
return NONE;
}
@@ -187,6 +197,42 @@ public:
return color;
}
+ std::array<video::SColor, 4> getColorArray(Property prop,
+ std::array<video::SColor, 4> def) const
+ {
+ const auto &val = properties[prop];
+ if (val.empty())
+ return def;
+
+ std::vector<std::string> strs;
+ if (!parseArray(val, strs))
+ return def;
+
+ for (size_t i = 0; i <= 3; i++) {
+ video::SColor color;
+ if (parseColorString(strs[i], color, false, 0xff))
+ def[i] = color;
+ }
+
+ return def;
+ }
+
+ std::array<s32, 4> getIntArray(Property prop, std::array<s32, 4> def) const
+ {
+ const auto &val = properties[prop];
+ if (val.empty())
+ return def;
+
+ std::vector<std::string> strs;
+ if (!parseArray(val, strs))
+ return def;
+
+ for (size_t i = 0; i <= 3; i++)
+ def[i] = stoi(strs[i]);
+
+ return def;
+ }
+
irr::core::rect<s32> getRect(Property prop, irr::core::rect<s32> def) const
{
const auto &val = properties[prop];
@@ -334,6 +380,24 @@ public:
}
private:
+ bool parseArray(const std::string &value, std::vector<std::string> &arr) const
+ {
+ std::vector<std::string> strs = split(value, ',');
+
+ if (strs.size() == 1) {
+ arr = {strs[0], strs[0], strs[0], strs[0]};
+ } else if (strs.size() == 2) {
+ arr = {strs[0], strs[1], strs[0], strs[1]};
+ } else if (strs.size() == 4) {
+ arr = strs;
+ } else {
+ warningstream << "Invalid array size (" << strs.size()
+ << " arguments): \"" << value << "\"" << std::endl;
+ return false;
+ }
+ return true;
+ }
+
bool parseRect(const std::string &value, irr::core::rect<s32> *parsed_rect) const
{
irr::core::rect<s32> rect;
diff --git a/src/gui/guiBox.cpp b/src/gui/guiBox.cpp
index 7f329cc32..99a115daf 100644
--- a/src/gui/guiBox.cpp
+++ b/src/gui/guiBox.cpp
@@ -20,9 +20,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiBox.h"
GUIBox::GUIBox(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
- const core::rect<s32> &rectangle, const video::SColor &color) :
+ const core::rect<s32> &rectangle,
+ const std::array<video::SColor, 4> &colors,
+ const std::array<video::SColor, 4> &bordercolors,
+ const std::array<s32, 4> &borderwidths) :
gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle),
- m_color(color)
+ m_colors(colors),
+ m_bordercolors(bordercolors),
+ m_borderwidths(borderwidths)
{
}
@@ -31,8 +36,81 @@ void GUIBox::draw()
if (!IsVisible)
return;
- Environment->getVideoDriver()->draw2DRectangle(m_color, AbsoluteRect,
- &AbsoluteClippingRect);
+ std::array<s32, 4> negative_borders = {0, 0, 0, 0};
+ std::array<s32, 4> positive_borders = {0, 0, 0, 0};
+
+ for (size_t i = 0; i <= 3; i++) {
+ if (m_borderwidths[i] > 0)
+ positive_borders[i] = m_borderwidths[i];
+ else
+ negative_borders[i] = m_borderwidths[i];
+ }
+
+ v2s32 upperleft = AbsoluteRect.UpperLeftCorner;
+ v2s32 lowerright = AbsoluteRect.LowerRightCorner;
+
+ v2s32 topleft_border = {
+ upperleft.X - positive_borders[3],
+ upperleft.Y - positive_borders[0]
+ };
+ v2s32 topleft_rect = {
+ upperleft.X - negative_borders[3],
+ upperleft.Y - negative_borders[0]
+ };
+
+ v2s32 lowerright_border = {
+ lowerright.X + positive_borders[1],
+ lowerright.Y + positive_borders[2]
+ };
+ v2s32 lowerright_rect = {
+ lowerright.X + negative_borders[1],
+ lowerright.Y + negative_borders[2]
+ };
+
+ core::rect<s32> main_rect(
+ topleft_rect.X,
+ topleft_rect.Y,
+ lowerright_rect.X,
+ lowerright_rect.Y
+ );
+
+ std::array<core::rect<s32>, 4> border_rects;
+
+ border_rects[0] = core::rect<s32>(
+ topleft_border.X,
+ topleft_border.Y,
+ lowerright_border.X,
+ topleft_rect.Y
+ );
+
+ border_rects[1] = core::rect<s32>(
+ lowerright_rect.X,
+ topleft_rect.Y,
+ lowerright_border.X,
+ lowerright_rect.Y
+ );
+
+ border_rects[2] = core::rect<s32>(
+ topleft_border.X,
+ lowerright_rect.Y,
+ lowerright_border.X,
+ lowerright_border.Y
+ );
+
+ border_rects[3] = core::rect<s32>(
+ topleft_border.X,
+ topleft_rect.Y,
+ topleft_rect.X,
+ lowerright_rect.Y
+ );
+
+ video::IVideoDriver *driver = Environment->getVideoDriver();
+
+ driver->draw2DRectangle(main_rect, m_colors[0], m_colors[1], m_colors[3],
+ m_colors[2], nullptr);
+
+ for (size_t i = 0; i <= 3; i++)
+ driver->draw2DRectangle(m_bordercolors[i], border_rects[i], nullptr);
IGUIElement::draw();
}
diff --git a/src/gui/guiBox.h b/src/gui/guiBox.h
index 5306fdf65..ca8f83771 100644
--- a/src/gui/guiBox.h
+++ b/src/gui/guiBox.h
@@ -19,16 +19,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
+#include <vector>
+#include <array>
#include "irrlichttypes_extrabloated.h"
class GUIBox : public gui::IGUIElement
{
public:
GUIBox(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
- const core::rect<s32> &rectangle, const video::SColor &color);
+ const core::rect<s32> &rectangle,
+ const std::array<video::SColor, 4> &colors,
+ const std::array<video::SColor, 4> &bordercolors,
+ const std::array<s32, 4> &borderwidths);
virtual void draw() override;
private:
- video::SColor m_color;
+ std::array<video::SColor, 4> m_colors;
+ std::array<video::SColor, 4> m_bordercolors;
+ std::array<s32, 4> m_borderwidths;
};
diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp
index f702b40bf..9bd5f27fd 100644
--- a/src/gui/guiFormSpecMenu.cpp
+++ b/src/gui/guiFormSpecMenu.cpp
@@ -2211,16 +2211,16 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
{
- std::vector<std::string> parts = split(element,';');
+ std::vector<std::string> parts = split(element, ';');
if ((parts.size() == 3) ||
((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
{
- std::vector<std::string> v_pos = split(parts[0],',');
- std::vector<std::string> v_geom = split(parts[1],',');
+ std::vector<std::string> v_pos = split(parts[0], ',');
+ std::vector<std::string> v_geom = split(parts[1], ',');
- MY_CHECKPOS("box",0);
- MY_CHECKGEOM("box",1);
+ MY_CHECKPOS("box", 0);
+ MY_CHECKGEOM("box", 1);
v2s32 pos;
v2s32 geom;
@@ -2234,36 +2234,43 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
geom.Y = stof(v_geom[1]) * spacing.Y;
}
- video::SColor tmp_color;
-
- if (parseColorString(parts[2], tmp_color, false, 0x8C)) {
- FieldSpec spec(
- "",
- L"",
- L"",
- 258 + m_fields.size(),
- -2
- );
- spec.ftype = f_Box;
+ FieldSpec spec(
+ "",
+ L"",
+ L"",
+ 258 + m_fields.size(),
+ -2
+ );
+ spec.ftype = f_Box;
- core::rect<s32> rect(pos, pos + geom);
+ auto style = getDefaultStyleForElement("box", spec.fname);
- GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid,
- rect, tmp_color);
+ video::SColor tmp_color;
+ std::array<video::SColor, 4> colors;
+ std::array<video::SColor, 4> bordercolors = {0x0, 0x0, 0x0, 0x0};
+ std::array<s32, 4> borderwidths = {0, 0, 0, 0};
- auto style = getDefaultStyleForElement("box", spec.fname);
- e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3));
+ if (parseColorString(parts[2], tmp_color, true, 0x8C)) {
+ colors = {tmp_color, tmp_color, tmp_color, tmp_color};
+ } else {
+ colors = style.getColorArray(StyleSpec::COLORS, {0x0, 0x0, 0x0, 0x0});
+ bordercolors = style.getColorArray(StyleSpec::BORDERCOLORS,
+ {0x0, 0x0, 0x0, 0x0});
+ borderwidths = style.getIntArray(StyleSpec::BORDERWIDTHS, {0, 0, 0, 0});
+ }
- e->drop();
+ core::rect<s32> rect(pos, pos + geom);
- m_fields.push_back(spec);
+ GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, rect,
+ colors, bordercolors, borderwidths);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3));
+ e->drop();
- } else {
- errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "' INVALID COLOR" << std::endl;
- }
+ m_fields.push_back(spec);
return;
}
- errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl;
+ errorstream << "Invalid Box element(" << parts.size() << "): '" << element
+ << "'" << std::endl;
}
void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element)