summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Robinson <robinsonvincent89@gmail.com>2022-07-03 05:52:26 -0700
committerGitHub <noreply@github.com>2022-07-03 08:52:26 -0400
commitf7bcf7fa46a634653e50f9e580faf2a53ab50e88 (patch)
treefc951aac79d80b0a1408d97dd51e8505317527e1
parent5a562a597cb8d1b71c5e0c1247836fe21ebccc56 (diff)
downloadminetest-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
-rw-r--r--doc/lua_api.txt21
-rw-r--r--games/devtest/mods/testformspec/formspec.lua12
-rw-r--r--games/devtest/mods/testformspec/textures/testformspec_9slice.pngbin0 -> 5935 bytes
-rw-r--r--src/client/guiscalingfilter.cpp49
-rw-r--r--src/client/guiscalingfilter.h8
-rw-r--r--src/gui/StyleSpec.h3
-rw-r--r--src/gui/guiAnimatedImage.cpp59
-rw-r--r--src/gui/guiAnimatedImage.h25
-rw-r--r--src/gui/guiBackgroundImage.cpp16
-rw-r--r--src/gui/guiButton.cpp8
-rw-r--r--src/gui/guiButtonImage.cpp27
-rw-r--r--src/gui/guiButtonImage.h10
-rw-r--r--src/gui/guiFormSpecMenu.cpp182
-rw-r--r--src/gui/guiFormSpecMenu.h2
-rw-r--r--src/network/networkprotocol.h4
15 files changed, 222 insertions, 204 deletions
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[<X>,<Y>;<W>,<H>;<texture name>]`
+### `image[<X>,<Y>;<W>,<H>;<texture name>;<middle>]`
-* 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[<X>,<Y>;<W>,<H>;<name>;<texture name>;<frame count>;<frame duration>;<frame start>]`
+### `animated_image[<X>,<Y>;<W>,<H>;<name>;<texture name>;<frame count>;<frame duration>;<frame start>;<middle>]`
* 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[<X>,<Y>;<W>,<H>;<name>;<mesh>;<textures>;<rotation X,Y>;<continuous>;<mouse control>;<frame loop range>;<animation speed>]`
@@ -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
--- /dev/null
+++ b/games/devtest/mods/testformspec/textures/testformspec_9slice.png
Binary files 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<s32> &rect, const core::rect<s32> &middle,
- const core::rect<s32> *cliprect, const video::SColor *const colors)
+ const core::rect<s32> &destrect, const core::rect<s32> &srcrect,
+ const core::rect<s32> &middlerect, const core::rect<s32> *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<s32> 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<s32> src({0, 0}, originalSize);
- core::rect<s32> dest = rect;
+ core::rect<s32> src = srcrect;
+ core::rect<s32> 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<s32> &destrect, const core::rect<s32> &srcrect,
- const core::rect<s32> *cliprect = 0, const video::SColor *const colors = 0,
- bool usealpha = false);
+ const core::rect<s32> *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<s32> &rect, const core::rect<s32> &middle,
- const core::rect<s32> *cliprect = nullptr,
+ const core::rect<s32> &destrect, const core::rect<s32> &srcrect,
+ const core::rect<s32> &middlerect, const core::rect<s32> *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 <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,
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_.-"