diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 994a30981..24de70a3b 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2310,6 +2310,8 @@ Version History * Allow dropdown indexing events * Formspec version 5 (5.5.0): * Added padding[] element +* Formspec version 6 (5.6.0): + * Add nine-slice images, animated_images, and fgimg_middle Elements -------- @@ -2474,20 +2476,25 @@ Elements * `bgcolor` tooltip background color as `ColorString` (optional) * `fontcolor` tooltip font color as `ColorString` (optional) -### `image[,;,;]` +### `image[,;,;;]` -* Show an image +* Show an image. +* `middle` (optional): Makes the image render in 9-sliced mode and defines the middle rect. + * Requires formspec version >= 6. + * See `background9[]` documentation for more information. -### `animated_image[,;,;;;;;]` +### `animated_image[,;,;;;;;;]` * Show an animated image. The image is drawn like a "vertical_frames" tile - animation (See [Tile animation definition]), but uses a frame count/duration - for simplicity + animation (See [Tile animation definition]), but uses a frame count/duration for simplicity * `name`: Element name to send when an event occurs. The event value is the index of the current frame. * `texture name`: The image to use. * `frame count`: The number of frames animating the image. * `frame duration`: Milliseconds between each frame. `0` means the frames don't advance. -* `frame start` (Optional): The index of the frame to start on. Default `1`. +* `frame start` (optional): The index of the frame to start on. Default `1`. +* `middle` (optional): Makes the image render in 9-sliced mode and defines the middle rect. + * Requires formspec version >= 6. + * See `background9[]` documentation for more information. ### `model[,;,;;;;;;;;]` @@ -3101,6 +3108,8 @@ Some types may inherit styles from parent types. * This is deprecated, use states instead. * fgimg_pressed - image when pressed. Defaults to fgimg when not provided. * This is deprecated, use states instead. + * fgimg_middle - Makes the fgimg textures render in 9-sliced mode and defines the middle rect. + See background9[] documentation for more details. * NOTE: The parameters of any given image_button will take precedence over fgimg/fgimg_pressed * sound - a sound to be played when triggered. * scrollbar diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 9f867631f..5f1f8970e 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -195,8 +195,10 @@ local style_fs = [[ style[one_btn15;border=false;bgcolor=#1cc;bgimg=testformspec_bg.png;bgimg_hovered=testformspec_bg_hovered.png;bgimg_pressed=testformspec_bg_pressed.png] item_image_button[1.25,9.6;1,1;testformspec:item;one_btn15;Bg] - style[one_btn16;border=false;bgimg=testformspec_bg_9slice.png;bgimg_hovered=testformspec_bg_9slice_hovered.png;bgimg_pressed=testformspec_bg_9slice_pressed.png;bgimg_middle=4,6] - button[2.5,9.6;2,1;one_btn16;9-Slice Bg] + style[one_btn16;border=false;bgimg=testformspec_bg_9slice.png;bgimg_middle=4,6;padding=5,7;fgimg=testformspec_bg.png;fgimg_middle=1] + style[one_btn16:hovered;bgimg=testformspec_bg_9slice_hovered.png;fgimg=testformspec_bg_hovered.png] + style[one_btn16:pressed;bgimg=testformspec_bg_9slice_pressed.png;fgimg=testformspec_bg_pressed.png] + image_button[2.5,9.6;2,1;;one_btn16;9-Slice Bg] @@ -375,12 +377,16 @@ local pages = { -- Animation [[ - formspec_version[3] + formspec_version[6] size[12,13] animated_image[0.5,0.5;1,1;;testformspec_animation.png;4;100] animated_image[0.5,1.75;1,1;;testformspec_animation.jpg;4;100] animated_image[1.75,0.5;1,1;;testformspec_animation.png;100;100] animated_image[3,0.5;1,1;ani_img_1;testformspec_animation.png;4;1000] + image[0.5,3;1,1;testformspec_bg.png;1] + animated_image[0.5,4.25;1,1;;[combine:16x48:0,0=testformspec_bg.png:0,16=testformspec_bg_hovered.png:0,32=testformspec_bg_pressed.png;3;250;1;1] + image[0.5,5.5;2,1;testformspec_9slice.png;16,0,-16,-16] + animated_image[2.75,5.5;1.5,0.5;;[combine:300x140:0,0=testformspec_9slice.png:0,70=(testformspec_9slice.png^[transformFX);2;500;1;16,0,-16,-16] button[4.25,0.5;1,1;ani_btn_1;Current Number] animated_image[3,1.75;1,1;ani_img_2;testformspec_animation.png;4;1000;2] diff --git a/games/devtest/mods/testformspec/textures/testformspec_9slice.png b/games/devtest/mods/testformspec/textures/testformspec_9slice.png new file mode 100644 index 000000000..70b6412a4 Binary files /dev/null and b/games/devtest/mods/testformspec/textures/testformspec_9slice.png differ diff --git a/src/client/guiscalingfilter.cpp b/src/client/guiscalingfilter.cpp index de122becf..42508259f 100644 --- a/src/client/guiscalingfilter.cpp +++ b/src/client/guiscalingfilter.cpp @@ -176,52 +176,61 @@ void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr, } void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture, - const core::rect &rect, const core::rect &middle, - const core::rect *cliprect, const video::SColor *const colors) + const core::rect &destrect, const core::rect &srcrect, + const core::rect &middlerect, const core::rect *cliprect, + const video::SColor *const colors) { - auto originalSize = texture->getOriginalSize(); - core::vector2di lowerRightOffset = core::vector2di(originalSize.Width, originalSize.Height) - middle.LowerRightCorner; + // `-x` is interpreted as `w - x` + core::rect middle = middlerect; + + if (middlerect.LowerRightCorner.X < 0) + middle.LowerRightCorner.X += srcrect.getWidth(); + if (middlerect.LowerRightCorner.Y < 0) + middle.LowerRightCorner.Y += srcrect.getHeight(); + + core::vector2di lower_right_offset = core::vector2di(srcrect.getWidth(), + srcrect.getHeight()) - middle.LowerRightCorner; for (int y = 0; y < 3; ++y) { for (int x = 0; x < 3; ++x) { - core::rect src({0, 0}, originalSize); - core::rect dest = rect; + core::rect src = srcrect; + core::rect dest = destrect; switch (x) { case 0: - dest.LowerRightCorner.X = rect.UpperLeftCorner.X + middle.UpperLeftCorner.X; - src.LowerRightCorner.X = middle.UpperLeftCorner.X; + dest.LowerRightCorner.X = destrect.UpperLeftCorner.X + middle.UpperLeftCorner.X; + src.LowerRightCorner.X = srcrect.UpperLeftCorner.X + middle.UpperLeftCorner.X; break; case 1: dest.UpperLeftCorner.X += middle.UpperLeftCorner.X; - dest.LowerRightCorner.X -= lowerRightOffset.X; - src.UpperLeftCorner.X = middle.UpperLeftCorner.X; - src.LowerRightCorner.X = middle.LowerRightCorner.X; + dest.LowerRightCorner.X -= lower_right_offset.X; + src.UpperLeftCorner.X += middle.UpperLeftCorner.X; + src.LowerRightCorner.X -= lower_right_offset.X; break; case 2: - dest.UpperLeftCorner.X = rect.LowerRightCorner.X - lowerRightOffset.X; - src.UpperLeftCorner.X = middle.LowerRightCorner.X; + dest.UpperLeftCorner.X = destrect.LowerRightCorner.X - lower_right_offset.X; + src.UpperLeftCorner.X = srcrect.LowerRightCorner.X - lower_right_offset.X; break; } switch (y) { case 0: - dest.LowerRightCorner.Y = rect.UpperLeftCorner.Y + middle.UpperLeftCorner.Y; - src.LowerRightCorner.Y = middle.UpperLeftCorner.Y; + dest.LowerRightCorner.Y = destrect.UpperLeftCorner.Y + middle.UpperLeftCorner.Y; + src.LowerRightCorner.Y = srcrect.UpperLeftCorner.Y + middle.UpperLeftCorner.Y; break; case 1: dest.UpperLeftCorner.Y += middle.UpperLeftCorner.Y; - dest.LowerRightCorner.Y -= lowerRightOffset.Y; - src.UpperLeftCorner.Y = middle.UpperLeftCorner.Y; - src.LowerRightCorner.Y = middle.LowerRightCorner.Y; + dest.LowerRightCorner.Y -= lower_right_offset.Y; + src.UpperLeftCorner.Y += middle.UpperLeftCorner.Y; + src.LowerRightCorner.Y -= lower_right_offset.Y; break; case 2: - dest.UpperLeftCorner.Y = rect.LowerRightCorner.Y - lowerRightOffset.Y; - src.UpperLeftCorner.Y = middle.LowerRightCorner.Y; + dest.UpperLeftCorner.Y = destrect.LowerRightCorner.Y - lower_right_offset.Y; + src.UpperLeftCorner.Y = srcrect.LowerRightCorner.Y - lower_right_offset.Y; break; } diff --git a/src/client/guiscalingfilter.h b/src/client/guiscalingfilter.h index 379a4bdb0..f2d2fce10 100644 --- a/src/client/guiscalingfilter.h +++ b/src/client/guiscalingfilter.h @@ -46,13 +46,13 @@ video::ITexture *guiScalingImageButton(video::IVideoDriver *driver, video::IText */ void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr, const core::rect &destrect, const core::rect &srcrect, - const core::rect *cliprect = 0, const video::SColor *const colors = 0, - bool usealpha = false); + const core::rect *cliprect = nullptr, + const video::SColor *const colors = nullptr, bool usealpha = false); /* * 9-slice / segment drawing */ void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture, - const core::rect &rect, const core::rect &middle, - const core::rect *cliprect = nullptr, + const core::rect &destrect, const core::rect &srcrect, + const core::rect &middlerect, const core::rect *cliprect = nullptr, const video::SColor *const colors = nullptr); 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 GUIAnimatedImage::GUIAnimatedImage(gui::IGUIEnvironment *env, gui::IGUIElement *parent, - s32 id, const core::rect &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 &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 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; + video::IVideoDriver *driver = Environment->getVideoDriver(); + + core::dimension2d 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; + + size.Height /= m_frame_count; + + core::rect rect(core::position2d(0, size.Height * m_frame_idx), size); + core::rect *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}; - - core::dimension2d size = m_texture->getOriginalSize(); - size.Height /= m_frame_count; - - draw2DImageFilterScaled(driver, m_texture, AbsoluteRect, - core::rect(core::position2d(0, size.Height * m_frame_idx), size), - NoClip ? nullptr : &AbsoluteClippingRect, colors, true); + 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 #include 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 &rectangle, const std::string &texture_name, - s32 frame_count, s32 frame_duration, ISimpleTextureSource *tsrc); + s32 id, const core::rect &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 &middle) { m_middle = middle; }; + core::rect 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 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 srcrect(core::position2d(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(core::position2d(0, 0), - core::dimension2di(texture->getOriginalSize())), - nullptr, colors, true); + draw2DImageFilterScaled(driver, texture, rect, srcrect, nullptr, colors, true); } else { - core::rect 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 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(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 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(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 &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 &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 rectangle, ISimpleTextureSource *tsrc, bool noclip = false); - void setForegroundImage(video::ITexture *image = nullptr); + void setForegroundImage(video::ITexture *image = nullptr, + const core::rect &middle = core::rect()); //! 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 parts; - if (!precheckElement("image", element, 2, 3, parts)) + if (!precheckElement("image", element, 2, 4, parts)) return; + size_t offset = parts.size() >= 3; + + std::vector v_pos = split(parts[0],','); + MY_CHECKPOS("image", 0); + + std::vector v_geom; if (parts.size() >= 3) { - std::vector v_pos = split(parts[0],','); - std::vector v_geom = split(parts[1],','); - std::string name = unescape_string(parts[2]); - - MY_CHECKPOS("image", 0); + 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 = v2s32(0); + } + } + + 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; } - - if (!data->explicit_size) - warningstream<<"invalid use of image without a size[] element"<getTexture(name); - if (!texture) { - errorstream << "GUIFormSpecMenu::parseImage() Unable to load texture:" - << std::endl << "\t" << name << std::endl; - return; - } - - FieldSpec spec( - name, - L"", - L"", - 258 + m_fields.size(), - 1 - ); - core::rect 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 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"<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 rect = core::rect(pos, pos + geom); + + core::rect 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 parts; - if (!precheckElement("animated_image", element, 6, 7, parts)) + if (!precheckElement("animated_image", element, 6, 8, parts)) return; std::vector 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 rect = core::rect(pos, pos + geom); - GUIAnimatedImage *e = new GUIAnimatedImage(Environment, data->current_parent, spec.fid, - rect, texture_name, frame_count, frame_duration, m_tsrc); + core::rect 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 *parsed_rect) +{ + core::rect rect; + std::vector 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 parts; @@ -1068,25 +1089,8 @@ void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &eleme } core::rect middle; - if (parts.size() >= 5) { - std::vector 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 *parsed_rect); + void tryClose(); void showTooltip(const std::wstring &text, const irr::video::SColor &color, diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 7c5b2e3fe..33e49afa4 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -228,8 +228,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PASSWORD_SIZE 28 // Maximum password length. Allows for // base64-encoded SHA-1 (27+\0). -// See also: Formspec Version History in doc/lua_api.txt -#define FORMSPEC_API_VERSION 5 +// See also formspec [Version History] in doc/lua_api.txt +#define FORMSPEC_API_VERSION 6 #define TEXTURENAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.-"