diff options
author | Vincent Robinson <robinsonvincent89@gmail.com> | 2022-07-03 05:52:26 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-03 08:52:26 -0400 |
commit | f7bcf7fa46a634653e50f9e580faf2a53ab50e88 (patch) | |
tree | fc951aac79d80b0a1408d97dd51e8505317527e1 /src/gui | |
parent | 5a562a597cb8d1b71c5e0c1247836fe21ebccc56 (diff) | |
download | minetest-f7bcf7fa46a634653e50f9e580faf2a53ab50e88.tar.gz minetest-f7bcf7fa46a634653e50f9e580faf2a53ab50e88.tar.bz2 minetest-f7bcf7fa46a634653e50f9e580faf2a53ab50e88.zip |
FormSpec: 9-slice images, animated_images, and fgimg_middle (#12453)
* FormSpec: 9-slice images and animated_images
* Add fgimg_middle; clean up code
* Address issues, add tests
* Fix stupid error; bump formspec version
* Re-add image[] elements without a size
Diffstat (limited to 'src/gui')
-rw-r--r-- | src/gui/StyleSpec.h | 3 | ||||
-rw-r--r-- | src/gui/guiAnimatedImage.cpp | 59 | ||||
-rw-r--r-- | src/gui/guiAnimatedImage.h | 25 | ||||
-rw-r--r-- | src/gui/guiBackgroundImage.cpp | 16 | ||||
-rw-r--r-- | src/gui/guiButton.cpp | 8 | ||||
-rw-r--r-- | src/gui/guiButtonImage.cpp | 27 | ||||
-rw-r--r-- | src/gui/guiButtonImage.h | 10 | ||||
-rw-r--r-- | src/gui/guiFormSpecMenu.cpp | 182 | ||||
-rw-r--r-- | src/gui/guiFormSpecMenu.h | 2 |
9 files changed, 163 insertions, 169 deletions
diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index fc92a861b..7a45e07d1 100644 --- a/src/gui/StyleSpec.h +++ b/src/gui/StyleSpec.h @@ -45,6 +45,7 @@ public: BGIMG_PRESSED, // Note: Deprecated property FGIMG, FGIMG_HOVERED, // Note: Deprecated property + FGIMG_MIDDLE, FGIMG_PRESSED, // Note: Deprecated property ALPHA, CONTENT_OFFSET, @@ -101,6 +102,8 @@ public: return FGIMG; } else if (name == "fgimg_hovered") { return FGIMG_HOVERED; + } else if (name == "fgimg_middle") { + return FGIMG_MIDDLE; } else if (name == "fgimg_pressed") { return FGIMG_PRESSED; } else if (name == "alpha") { diff --git a/src/gui/guiAnimatedImage.cpp b/src/gui/guiAnimatedImage.cpp index b1447c45f..890763e71 100644 --- a/src/gui/guiAnimatedImage.cpp +++ b/src/gui/guiAnimatedImage.cpp @@ -9,40 +9,37 @@ #include <vector> GUIAnimatedImage::GUIAnimatedImage(gui::IGUIEnvironment *env, gui::IGUIElement *parent, - s32 id, const core::rect<s32> &rectangle, const std::string &texture_name, - s32 frame_count, s32 frame_duration, ISimpleTextureSource *tsrc) : - gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle), m_tsrc(tsrc) + s32 id, const core::rect<s32> &rectangle) : + gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle) { - m_texture = m_tsrc->getTexture(texture_name); - - m_frame_count = std::max(frame_count, 1); - m_frame_duration = std::max(frame_duration, 0); - - if (m_texture != nullptr) { - core::dimension2d<u32> size = m_texture->getOriginalSize(); - if (size.Height < (u64)m_frame_count) - m_frame_count = size.Height; - } else { - // No need to step an animation if we have nothing to draw - m_frame_count = 1; - } } void GUIAnimatedImage::draw() { - // Render the current frame - if (m_texture != nullptr) { - video::IVideoDriver *driver = Environment->getVideoDriver(); + if (m_texture == nullptr) + return; - const video::SColor color(255, 255, 255, 255); - const video::SColor colors[] = {color, color, color, color}; + video::IVideoDriver *driver = Environment->getVideoDriver(); + + core::dimension2d<u32> size = m_texture->getOriginalSize(); + + if ((u32)m_frame_count > size.Height) + m_frame_count = size.Height; + if (m_frame_idx >= m_frame_count) + m_frame_idx = m_frame_count - 1; - core::dimension2d<u32> size = m_texture->getOriginalSize(); - size.Height /= m_frame_count; + size.Height /= m_frame_count; - draw2DImageFilterScaled(driver, m_texture, AbsoluteRect, - core::rect<s32>(core::position2d<s32>(0, size.Height * m_frame_idx), size), - NoClip ? nullptr : &AbsoluteClippingRect, colors, true); + core::rect<s32> rect(core::position2d<s32>(0, size.Height * m_frame_idx), size); + core::rect<s32> *cliprect = NoClip ? nullptr : &AbsoluteClippingRect; + + if (m_middle.getArea() == 0) { + const video::SColor color(255, 255, 255, 255); + const video::SColor colors[] = {color, color, color, color}; + draw2DImageFilterScaled(driver, m_texture, AbsoluteRect, rect, cliprect, + colors, true); + } else { + draw2DImage9Slice(driver, m_texture, AbsoluteRect, rect, m_middle, cliprect); } // Step the animation @@ -55,7 +52,7 @@ void GUIAnimatedImage::draw() m_global_time = new_global_time; // Advance by the number of elapsed frames, looping if necessary - m_frame_idx += u32(m_frame_time / m_frame_duration); + m_frame_idx += (u32)(m_frame_time / m_frame_duration); m_frame_idx %= m_frame_count; // If 1 or more frames have elapsed, reset the frame time counter with @@ -63,11 +60,3 @@ void GUIAnimatedImage::draw() m_frame_time %= m_frame_duration; } } - - -void GUIAnimatedImage::setFrameIndex(s32 frame) -{ - s32 idx = std::max(frame, 0); - if (idx > 0 && idx < m_frame_count) - m_frame_idx = idx; -} diff --git a/src/gui/guiAnimatedImage.h b/src/gui/guiAnimatedImage.h index f8e6a506e..885aedece 100644 --- a/src/gui/guiAnimatedImage.h +++ b/src/gui/guiAnimatedImage.h @@ -1,6 +1,7 @@ #pragma once #include "irrlichttypes_extrabloated.h" +#include <algorithm> #include <string> class ISimpleTextureSource; @@ -8,21 +9,33 @@ class ISimpleTextureSource; class GUIAnimatedImage : public gui::IGUIElement { public: GUIAnimatedImage(gui::IGUIEnvironment *env, gui::IGUIElement *parent, - s32 id, const core::rect<s32> &rectangle, const std::string &texture_name, - s32 frame_count, s32 frame_duration, ISimpleTextureSource *tsrc); + s32 id, const core::rect<s32> &rectangle); virtual void draw() override; - void setFrameIndex(s32 frame); + void setTexture(video::ITexture *texture) { m_texture = texture; }; + video::ITexture *getTexture() const { return m_texture; }; + + void setMiddleRect(const core::rect<s32> &middle) { m_middle = middle; }; + core::rect<s32> getMiddleRect() const { return m_middle; }; + + void setFrameDuration(u64 duration) { m_frame_duration = duration; }; + u64 getFrameDuration() const { return m_frame_duration; }; + + void setFrameCount(s32 count) { m_frame_count = std::max(count, 1); }; + s32 getFrameCount() const { return m_frame_count; }; + + void setFrameIndex(s32 frame) { m_frame_idx = std::max(frame, 0); }; s32 getFrameIndex() const { return m_frame_idx; }; private: - ISimpleTextureSource *m_tsrc; - video::ITexture *m_texture = nullptr; + u64 m_global_time = 0; s32 m_frame_idx = 0; s32 m_frame_count = 1; - u64 m_frame_duration = 1; + u64 m_frame_duration = 0; u64 m_frame_time = 0; + + core::rect<s32> m_middle; }; diff --git a/src/gui/guiBackgroundImage.cpp b/src/gui/guiBackgroundImage.cpp index 85e870771..8d0d1c010 100644 --- a/src/gui/guiBackgroundImage.cpp +++ b/src/gui/guiBackgroundImage.cpp @@ -48,21 +48,15 @@ void GUIBackgroundImage::draw() video::IVideoDriver *driver = Environment->getVideoDriver(); + core::rect<s32> srcrect(core::position2d<s32>(0, 0), + core::dimension2di(texture->getOriginalSize())); + if (m_middle.getArea() == 0) { const video::SColor color(255, 255, 255, 255); const video::SColor colors[] = {color, color, color, color}; - draw2DImageFilterScaled(driver, texture, rect, - core::rect<s32>(core::position2d<s32>(0, 0), - core::dimension2di(texture->getOriginalSize())), - nullptr, colors, true); + draw2DImageFilterScaled(driver, texture, rect, srcrect, nullptr, colors, true); } else { - core::rect<s32> middle = m_middle; - // `-x` is interpreted as `w - x` - if (middle.LowerRightCorner.X < 0) - middle.LowerRightCorner.X += texture->getOriginalSize().Width; - if (middle.LowerRightCorner.Y < 0) - middle.LowerRightCorner.Y += texture->getOriginalSize().Height; - draw2DImage9Slice(driver, texture, rect, middle); + draw2DImage9Slice(driver, texture, rect, srcrect, m_middle); } IGUIElement::draw(); diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp index ba95b81c3..c38d901c4 100644 --- a/src/gui/guiButton.cpp +++ b/src/gui/guiButton.cpp @@ -320,15 +320,9 @@ void GUIButton::draw() sourceRect, &AbsoluteClippingRect,
image_colors, UseAlphaChannel);
} else {
- core::rect<s32> middle = BgMiddle;
- // `-x` is interpreted as `w - x`
- if (middle.LowerRightCorner.X < 0)
- middle.LowerRightCorner.X += texture->getOriginalSize().Width;
- if (middle.LowerRightCorner.Y < 0)
- middle.LowerRightCorner.Y += texture->getOriginalSize().Height;
draw2DImage9Slice(driver, texture,
ScaleImage ? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
- middle, &AbsoluteClippingRect, image_colors);
+ sourceRect, BgMiddle, &AbsoluteClippingRect, image_colors);
}
// END PATCH
}
diff --git a/src/gui/guiButtonImage.cpp b/src/gui/guiButtonImage.cpp index b507ffece..4ab770a99 100644 --- a/src/gui/guiButtonImage.cpp +++ b/src/gui/guiButtonImage.cpp @@ -32,15 +32,15 @@ using namespace gui; GUIButtonImage::GUIButtonImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent, s32 id, core::rect<s32> rectangle, ISimpleTextureSource *tsrc, bool noclip) - : GUIButton (environment, parent, id, rectangle, tsrc, noclip) + : GUIButton(environment, parent, id, rectangle, tsrc, noclip) { - m_image = Environment->addImage( - core::rect<s32>(0,0,rectangle.getWidth(),rectangle.getHeight()), this); - m_image->setScaleImage(isScalingImage()); + GUIButton::setScaleImage(true); + m_image = new GUIAnimatedImage(environment, this, id, rectangle); sendToBack(m_image); } -void GUIButtonImage::setForegroundImage(video::ITexture *image) +void GUIButtonImage::setForegroundImage(video::ITexture *image, + const core::rect<s32> &middle) { if (image == m_foreground_image) return; @@ -52,11 +52,12 @@ void GUIButtonImage::setForegroundImage(video::ITexture *image) m_foreground_image->drop(); m_foreground_image = image; - m_image->setImage(image); + m_image->setTexture(image); + m_image->setMiddleRect(middle); } //! Set element properties from a StyleSpec -void GUIButtonImage::setFromStyle(const StyleSpec& style) +void GUIButtonImage::setFromStyle(const StyleSpec &style) { GUIButton::setFromStyle(style); @@ -67,19 +68,13 @@ void GUIButtonImage::setFromStyle(const StyleSpec& style) getTextureSource()); setForegroundImage(guiScalingImageButton(driver, texture, - AbsoluteRect.getWidth(), AbsoluteRect.getHeight())); - setScaleImage(true); + AbsoluteRect.getWidth(), AbsoluteRect.getHeight()), + style.getRect(StyleSpec::FGIMG_MIDDLE, m_image->getMiddleRect())); } else { - setForegroundImage(nullptr); + setForegroundImage(); } } -void GUIButtonImage::setScaleImage(bool scaleImage) -{ - GUIButton::setScaleImage(scaleImage); - m_image->setScaleImage(scaleImage); -} - GUIButtonImage *GUIButtonImage::addButton(IGUIEnvironment *environment, const core::rect<s32> &rectangle, ISimpleTextureSource *tsrc, IGUIElement *parent, s32 id, const wchar_t *text, diff --git a/src/gui/guiButtonImage.h b/src/gui/guiButtonImage.h index 59a25b4f0..554934518 100644 --- a/src/gui/guiButtonImage.h +++ b/src/gui/guiButtonImage.h @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiButton.h" #include "IGUIButton.h" +#include "guiAnimatedImage.h" using namespace irr; @@ -32,12 +33,11 @@ public: s32 id, core::rect<s32> rectangle, ISimpleTextureSource *tsrc, bool noclip = false); - void setForegroundImage(video::ITexture *image = nullptr); + void setForegroundImage(video::ITexture *image = nullptr, + const core::rect<s32> &middle = core::rect<s32>()); //! Set element properties from a StyleSpec - virtual void setFromStyle(const StyleSpec& style) override; - - virtual void setScaleImage(bool scaleImage=true) override; + virtual void setFromStyle(const StyleSpec &style) override; //! Do not drop returned handle static GUIButtonImage *addButton(gui::IGUIEnvironment *environment, @@ -47,5 +47,5 @@ public: private: video::ITexture *m_foreground_image = nullptr; - gui::IGUIImage *m_image; + GUIAnimatedImage *m_image; }; diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 422d6da16..e01a5347a 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -767,101 +767,84 @@ void GUIFormSpecMenu::parseScrollBarOptions(parserData* data, const std::string void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) { std::vector<std::string> parts; - if (!precheckElement("image", element, 2, 3, parts)) + if (!precheckElement("image", element, 2, 4, parts)) return; - if (parts.size() >= 3) { - 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]); + size_t offset = parts.size() >= 3; + + std::vector<std::string> v_pos = split(parts[0],','); + MY_CHECKPOS("image", 0); - MY_CHECKPOS("image", 0); + std::vector<std::string> v_geom; + if (parts.size() >= 3) { + v_geom = split(parts[1],','); MY_CHECKGEOM("image", 1); + } - v2s32 pos; - v2s32 geom; + std::string name = unescape_string(parts[1 + offset]); + video::ITexture *texture = m_tsrc->getTexture(name); - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); + v2s32 pos; + v2s32 geom; + + if (parts.size() < 3) { + if (texture != nullptr) { + core::dimension2du dim = texture->getOriginalSize(); + geom.X = dim.Width; + geom.Y = dim.Height; } else { - pos = getElementBasePos(&v_pos); - geom.X = stof(v_geom[0]) * (float)imgsize.X; - geom.Y = stof(v_geom[1]) * (float)imgsize.Y; + geom = v2s32(0); } + } - if (!data->explicit_size) - warningstream<<"invalid use of image without a size[] element"<<std::endl; - - video::ITexture *texture = m_tsrc->getTexture(name); - if (!texture) { - errorstream << "GUIFormSpecMenu::parseImage() Unable to load texture:" - << std::endl << "\t" << name << std::endl; - return; + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + if (parts.size() >= 3) + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + if (parts.size() >= 3) { + geom.X = stof(v_geom[0]) * (float)imgsize.X; + geom.Y = stof(v_geom[1]) * (float)imgsize.Y; } - - FieldSpec spec( - name, - L"", - L"", - 258 + m_fields.size(), - 1 - ); - core::rect<s32> rect(pos, pos + geom); - gui::IGUIImage *e = Environment->addImage(rect, data->current_parent, - spec.fid, 0, true); - e->setImage(texture); - e->setScaleImage(true); - auto style = getDefaultStyleForElement("image", spec.fname); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); - m_fields.push_back(spec); - - // images should let events through - e->grab(); - m_clickthrough_elements.push_back(e); - return; } - // Else: 2 arguments in "parts" - - std::vector<std::string> v_pos = split(parts[0],','); - std::string name = unescape_string(parts[1]); - - MY_CHECKPOS("image", 0); - - v2s32 pos = getElementBasePos(&v_pos); - if (!data->explicit_size) - warningstream<<"invalid use of image without a size[] element"<<std::endl; - - video::ITexture *texture = m_tsrc->getTexture(name); - if (!texture) { - errorstream << "GUIFormSpecMenu::parseImage() Unable to load texture:" - << std::endl << "\t" << name << std::endl; - return; - } + warningstream << "Invalid use of image without a size[] element" << std::endl; FieldSpec spec( name, L"", L"", - 258 + m_fields.size() + 258 + m_fields.size(), + 1 ); - gui::IGUIImage *e = Environment->addImage(texture, pos, true, - data->current_parent, spec.fid, 0); + + core::rect<s32> rect = core::rect<s32>(pos, pos + geom); + + core::rect<s32> middle; + if (parts.size() >= 4) + parseMiddleRect(parts[3], &middle); + + GUIAnimatedImage *e = new GUIAnimatedImage(Environment, data->current_parent, + spec.fid, rect); + + e->setTexture(texture); + e->setMiddleRect(middle); + auto style = getDefaultStyleForElement("image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); - m_fields.push_back(spec); - // images should let events through - e->grab(); + // Animated images should let events through m_clickthrough_elements.push_back(e); + + m_fields.push_back(spec); } void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &element) { std::vector<std::string> parts; - if (!precheckElement("animated_image", element, 6, 7, parts)) + if (!precheckElement("animated_image", element, 6, 8, parts)) return; std::vector<std::string> v_pos = split(parts[0], ','); @@ -887,7 +870,8 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el } if (!data->explicit_size) - warningstream << "Invalid use of animated_image without a size[] element" << std::endl; + warningstream << "Invalid use of animated_image without a size[] element" + << std::endl; FieldSpec spec( name, @@ -900,9 +884,17 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el core::rect<s32> rect = core::rect<s32>(pos, pos + geom); - GUIAnimatedImage *e = new GUIAnimatedImage(Environment, data->current_parent, spec.fid, - rect, texture_name, frame_count, frame_duration, m_tsrc); + core::rect<s32> middle; + if (parts.size() >= 8) + parseMiddleRect(parts[7], &middle); + + GUIAnimatedImage *e = new GUIAnimatedImage(Environment, data->current_parent, + spec.fid, rect); + e->setTexture(m_tsrc->getTexture(texture_name)); + e->setMiddleRect(middle); + e->setFrameDuration(frame_duration); + e->setFrameCount(frame_count); if (parts.size() >= 7) e->setFrameIndex(stoi(parts[6]) - 1); @@ -1027,6 +1019,35 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, m_fields.push_back(spec); } +bool GUIFormSpecMenu::parseMiddleRect(const std::string &value, core::rect<s32> *parsed_rect) +{ + core::rect<s32> rect; + std::vector<std::string> v_rect = split(value, ','); + + if (v_rect.size() == 1) { + s32 x = stoi(v_rect[0]); + rect.UpperLeftCorner = core::vector2di(x, x); + rect.LowerRightCorner = core::vector2di(-x, -x); + } else if (v_rect.size() == 2) { + s32 x = stoi(v_rect[0]); + s32 y = stoi(v_rect[1]); + rect.UpperLeftCorner = core::vector2di(x, y); + rect.LowerRightCorner = core::vector2di(-x, -y); + // `-x` is interpreted as `w - x` + } else if (v_rect.size() == 4) { + rect.UpperLeftCorner = core::vector2di(stoi(v_rect[0]), stoi(v_rect[1])); + rect.LowerRightCorner = core::vector2di(stoi(v_rect[2]), stoi(v_rect[3])); + } else { + warningstream << "Invalid rectangle string format: \"" << value + << "\"" << std::endl; + return false; + } + + *parsed_rect = rect; + + return true; +} + void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &element) { std::vector<std::string> parts; @@ -1068,25 +1089,8 @@ void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &eleme } core::rect<s32> middle; - if (parts.size() >= 5) { - std::vector<std::string> v_middle = split(parts[4], ','); - if (v_middle.size() == 1) { - s32 x = stoi(v_middle[0]); - middle.UpperLeftCorner = core::vector2di(x, x); - middle.LowerRightCorner = core::vector2di(-x, -x); - } else if (v_middle.size() == 2) { - s32 x = stoi(v_middle[0]); - s32 y = stoi(v_middle[1]); - middle.UpperLeftCorner = core::vector2di(x, y); - middle.LowerRightCorner = core::vector2di(-x, -y); - // `-x` is interpreted as `w - x` - } else if (v_middle.size() == 4) { - middle.UpperLeftCorner = core::vector2di(stoi(v_middle[0]), stoi(v_middle[1])); - middle.LowerRightCorner = core::vector2di(stoi(v_middle[2]), stoi(v_middle[3])); - } else { - warningstream << "Invalid rectangle given to middle param of background[] element" << std::endl; - } - } + if (parts.size() >= 5) + parseMiddleRect(parts[4], &middle); if (!data->explicit_size && !clip) warningstream << "invalid use of unclipped background without a size[] element" << std::endl; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index a584456db..c01ff817b 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -457,6 +457,8 @@ private: void parseSetFocus(const std::string &element); void parseModel(parserData *data, const std::string &element); + bool parseMiddleRect(const std::string &value, core::rect<s32> *parsed_rect); + void tryClose(); void showTooltip(const std::wstring &text, const irr::video::SColor &color, |