Skip to content

Commit

Permalink
Add gradients and borders to FormSpec boxes (#8676)
Browse files Browse the repository at this point in the history
  • Loading branch information
v-rob committed Aug 20, 2020
1 parent 471497f commit 83d0c36
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 47 deletions.
43 changes: 29 additions & 14 deletions doc/lua_api.txt
Expand Up @@ -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>]`

Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down
64 changes: 64 additions & 0 deletions src/gui/StyleSpec.h
Expand Up @@ -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

Expand All @@ -50,6 +51,9 @@ class StyleSpec
PADDING,
FONT,
FONT_SIZE,
COLORS,
BORDERCOLORS,
BORDERWIDTHS,
NUM_PROPERTIES,
NONE
};
Expand Down Expand Up @@ -106,6 +110,12 @@ class StyleSpec
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;
}
Expand Down Expand Up @@ -187,6 +197,42 @@ class StyleSpec
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];
Expand Down Expand Up @@ -334,6 +380,24 @@ class StyleSpec
}

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;
Expand Down
86 changes: 82 additions & 4 deletions src/gui/guiBox.cpp
Expand Up @@ -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)
{
}

Expand All @@ -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();
}
11 changes: 9 additions & 2 deletions src/gui/guiBox.h
Expand Up @@ -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;
};

0 comments on commit 83d0c36

Please sign in to comment.