diff options
Diffstat (limited to 'src/gui')
29 files changed, 1081 insertions, 762 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 110a00595..147f445f4 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -16,6 +16,7 @@ set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiPathSelectMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiHyperText.cpp diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index 999c1d237..3e842e826 100644 --- a/src/gui/StyleSpec.h +++ b/src/gui/StyleSpec.h @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "client/tile.h" // ITextureSource +#include "debug.h" #include "irrlichttypes_extrabloated.h" #include "util/string.h" #include <array> @@ -31,25 +32,36 @@ public: { TEXTCOLOR, BGCOLOR, - BGCOLOR_HOVERED, - BGCOLOR_PRESSED, + BGCOLOR_HOVERED, // Note: Deprecated property + BGCOLOR_PRESSED, // Note: Deprecated property NOCLIP, BORDER, BGIMG, - BGIMG_HOVERED, + BGIMG_HOVERED, // Note: Deprecated property BGIMG_MIDDLE, - BGIMG_PRESSED, + BGIMG_PRESSED, // Note: Deprecated property FGIMG, - FGIMG_HOVERED, - FGIMG_PRESSED, + FGIMG_HOVERED, // Note: Deprecated property + FGIMG_PRESSED, // Note: Deprecated property ALPHA, + CONTENT_OFFSET, + PADDING, NUM_PROPERTIES, NONE }; + enum State + { + STATE_DEFAULT = 0, + STATE_HOVERED = 1 << 0, + STATE_PRESSED = 1 << 1, + NUM_STATES = 1 << 2, + STATE_INVALID = 1 << 3, + }; private: std::array<bool, NUM_PROPERTIES> property_set{}; std::array<std::string, NUM_PROPERTIES> properties; + State state_map = STATE_DEFAULT; public: static Property GetPropertyByName(const std::string &name) @@ -82,6 +94,10 @@ public: return FGIMG_PRESSED; } else if (name == "alpha") { return ALPHA; + } else if (name == "content_offset") { + return CONTENT_OFFSET; + } else if (name == "padding") { + return PADDING; } else { return NONE; } @@ -99,6 +115,49 @@ public: property_set[prop] = true; } + //! Parses a name and returns the corresponding state enum + static State getStateByName(const std::string &name) + { + if (name == "default") { + return STATE_DEFAULT; + } else if (name == "hovered") { + return STATE_HOVERED; + } else if (name == "pressed") { + return STATE_PRESSED; + } else { + return STATE_INVALID; + } + } + + //! Gets the state that this style is intended for + State getState() const + { + return state_map; + } + + //! Set the given state on this style + void addState(State state) + { + FATAL_ERROR_IF(state >= NUM_STATES, "Out-of-bounds state received"); + + state_map = static_cast<State>(state_map | state); + } + + //! Using a list of styles mapped to state values, calculate the final + // combined style for a state by propagating values in its component states + static StyleSpec getStyleFromStatePropagation(const std::array<StyleSpec, NUM_STATES> &styles, State state) + { + StyleSpec temp = styles[StyleSpec::STATE_DEFAULT]; + temp.state_map = state; + for (int i = StyleSpec::STATE_DEFAULT + 1; i <= state; i++) { + if ((state & i) != 0) { + temp = temp | styles[i]; + } + } + + return temp; + } + video::SColor getColor(Property prop, video::SColor def) const { const auto &val = properties[prop]; @@ -143,6 +202,29 @@ public: return rect; } + irr::core::vector2d<s32> getVector2i(Property prop, irr::core::vector2d<s32> def) const + { + const auto &val = properties[prop]; + if (val.empty()) + return def; + + irr::core::vector2d<s32> vec; + if (!parseVector2i(val, &vec)) + return def; + + return vec; + } + + irr::core::vector2d<s32> getVector2i(Property prop) const + { + const auto &val = properties[prop]; + FATAL_ERROR_IF(val.empty(), "Unexpected missing property"); + + irr::core::vector2d<s32> vec; + parseVector2i(val, &vec); + return vec; + } + video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc, video::ITexture *def) const { @@ -233,4 +315,29 @@ private: return true; } + + bool parseVector2i(const std::string &value, irr::core::vector2d<s32> *parsed_vec) const + { + irr::core::vector2d<s32> vec; + std::vector<std::string> v_vector = split(value, ','); + + if (v_vector.size() == 1) { + s32 x = stoi(v_vector[0]); + vec.X = x; + vec.Y = x; + } else if (v_vector.size() == 2) { + s32 x = stoi(v_vector[0]); + s32 y = stoi(v_vector[1]); + vec.X = x; + vec.Y = y; + } else { + warningstream << "Invalid vector2d string format: \"" << value + << "\"" << std::endl; + return false; + } + + *parsed_vec = vec; + + return true; + } }; diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp index 4c16ee237..6732a9233 100644 --- a/src/gui/guiButton.cpp +++ b/src/gui/guiButton.cpp @@ -14,6 +14,7 @@ #include "irrlicht_changes/static_text.h"
#include "porting.h"
#include "StyleSpec.h"
+#include "util/numeric.h"
using namespace irr;
using namespace gui;
@@ -26,14 +27,15 @@ using namespace gui; //! constructor
GUIButton::GUIButton(IGUIEnvironment* environment, IGUIElement* parent,
- s32 id, core::rect<s32> rectangle, bool noclip)
+ s32 id, core::rect<s32> rectangle, ISimpleTextureSource *tsrc,
+ bool noclip)
: IGUIButton(environment, parent, id, rectangle),
SpriteBank(0), OverrideFont(0),
OverrideColorEnabled(false), OverrideColor(video::SColor(101,255,255,255)),
ClickTime(0), HoverTime(0), FocusTime(0),
ClickShiftState(false), ClickControlState(false),
IsPushButton(false), Pressed(false),
- UseAlphaChannel(false), DrawBorder(true), ScaleImage(false)
+ UseAlphaChannel(false), DrawBorder(true), ScaleImage(false), TSrc(tsrc)
{
setNotClipped(noclip);
@@ -44,14 +46,6 @@ GUIButton::GUIButton(IGUIEnvironment* environment, IGUIElement* parent, // PATCH
for (size_t i = 0; i < 4; i++) {
Colors[i] = Environment->getSkin()->getColor((EGUI_DEFAULT_COLOR)i);
- HoveredColors[i] = irr::video::SColor(Colors[i].getAlpha(),
- core::clamp<u32>(Colors[i].getRed() * COLOR_HOVERED_MOD, 0, 255),
- core::clamp<u32>(Colors[i].getGreen() * COLOR_HOVERED_MOD, 0, 255),
- core::clamp<u32>(Colors[i].getBlue() * COLOR_HOVERED_MOD, 0, 255));
- PressedColors[i] = irr::video::SColor(Colors[i].getAlpha(),
- core::clamp<u32>(Colors[i].getRed() * COLOR_PRESSED_MOD, 0, 255),
- core::clamp<u32>(Colors[i].getGreen() * COLOR_PRESSED_MOD, 0, 255),
- core::clamp<u32>(Colors[i].getBlue() * COLOR_PRESSED_MOD, 0, 255));
}
StaticText = gui::StaticText::add(Environment, Text.c_str(), core::rect<s32>(0,0,rectangle.getWidth(),rectangle.getHeight()), false, false, this, id);
StaticText->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
@@ -262,6 +256,13 @@ void GUIButton::draw() return;
// PATCH
+ // Track hovered state, if it has changed then we need to update the style.
+ bool hovered = isHovered();
+ if (hovered != WasHovered) {
+ WasHovered = hovered;
+ setFromState();
+ }
+
GUISkin* skin = dynamic_cast<GUISkin*>(Environment->getSkin());
video::IVideoDriver* driver = Environment->getVideoDriver();
// END PATCH
@@ -271,21 +272,24 @@ void GUIButton::draw() if (!Pressed)
{
// PATCH
- skin->drawColored3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect,
- isHovered() ? HoveredColors : Colors);
+ skin->drawColored3DButtonPaneStandard(this, AbsoluteRect,
+ &AbsoluteClippingRect, Colors);
// END PATCH
}
else
{
// PATCH
- skin->drawColored3DButtonPanePressed(this,
- AbsoluteRect, &AbsoluteClippingRect, PressedColors);
+ skin->drawColored3DButtonPanePressed(this, AbsoluteRect,
+ &AbsoluteClippingRect, Colors);
// END PATCH
}
}
const core::position2di buttonCenter(AbsoluteRect.getCenter());
- EGUI_BUTTON_IMAGE_STATE imageState = getImageState(Pressed);
+ // PATCH
+ // The image changes based on the state, so we use the default every time.
+ EGUI_BUTTON_IMAGE_STATE imageState = EGBIS_IMAGE_UP;
+ // END PATCH
if ( ButtonImages[(u32)imageState].Texture )
{
core::position2d<s32> pos(buttonCenter);
@@ -548,18 +552,6 @@ void GUIButton::setPressedImage(video::ITexture* image, const core::rect<s32>& p setImage(gui::EGBIS_IMAGE_DOWN, image, pos);
}
-void GUIButton::setHoveredImage(video::ITexture* image)
-{
- setImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image);
- setImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image);
-}
-
-void GUIButton::setHoveredImage(video::ITexture* image, const core::rect<s32>& pos)
-{
- setImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image, pos);
- setImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image, pos);
-}
-
//! Sets the text displayed by the button
void GUIButton::setText(const wchar_t* text)
{
@@ -600,24 +592,7 @@ void GUIButton::setPressed(bool pressed) {
ClickTime = porting::getTimeMs();
Pressed = pressed;
-
- GUISkin* skin = dynamic_cast<GUISkin*>(Environment->getSkin());
-
- for(IGUIElement *child : getChildren())
- {
- core::rect<s32> originalRect = child->getRelativePosition();
- if (Pressed) {
- child->setRelativePosition(originalRect +
- core::dimension2d<s32>(
- skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
- skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y)));
- } else {
- child->setRelativePosition(originalRect -
- core::dimension2d<s32>(
- skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
- skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y)));
- }
- }
+ setFromState();
}
}
@@ -729,10 +704,12 @@ void GUIButton::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWri }
// PATCH
-GUIButton* GUIButton::addButton(IGUIEnvironment *environment, const core::rect<s32>& rectangle,
- IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext)
+GUIButton* GUIButton::addButton(IGUIEnvironment *environment,
+ const core::rect<s32>& rectangle, ISimpleTextureSource *tsrc,
+ IGUIElement* parent, s32 id, const wchar_t* text,
+ const wchar_t *tooltiptext)
{
- GUIButton* button = new GUIButton(environment, parent ? parent : environment->getRootGUIElement(), id, rectangle);
+ GUIButton* button = new GUIButton(environment, parent ? parent : environment->getRootGUIElement(), id, rectangle, tsrc);
if (text)
button->setText(text);
@@ -749,76 +726,111 @@ void GUIButton::setColor(video::SColor color) for (size_t i = 0; i < 4; i++) {
video::SColor base = Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
Colors[i] = base.getInterpolated(color, d);
- HoveredColors[i] = irr::video::SColor(Colors[i].getAlpha(),
- core::clamp<u32>(Colors[i].getRed() * COLOR_HOVERED_MOD, 0, 255),
- core::clamp<u32>(Colors[i].getGreen() * COLOR_HOVERED_MOD, 0, 255),
- core::clamp<u32>(Colors[i].getBlue() * COLOR_HOVERED_MOD, 0, 255));
- PressedColors[i] = irr::video::SColor(Colors[i].getAlpha(),
- core::clamp<u32>(Colors[i].getRed() * COLOR_PRESSED_MOD, 0, 255),
- core::clamp<u32>(Colors[i].getGreen() * COLOR_PRESSED_MOD, 0, 255),
- core::clamp<u32>(Colors[i].getBlue() * COLOR_PRESSED_MOD, 0, 255));
}
}
-void GUIButton::setHoveredColor(video::SColor color)
-{
- float d = 0.65f;
- for (size_t i = 0; i < 4; i++) {
- video::SColor base = Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
- HoveredColors[i] = base.getInterpolated(color, d);
- }
-}
-void GUIButton::setPressedColor(video::SColor color)
+
+//! Set element properties from a StyleSpec corresponding to the button state
+void GUIButton::setFromState()
{
- float d = 0.65f;
- for (size_t i = 0; i < 4; i++) {
- video::SColor base = Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
- PressedColors[i] = base.getInterpolated(color, d);
- }
+ StyleSpec::State state = StyleSpec::STATE_DEFAULT;
+
+ if (isPressed())
+ state = static_cast<StyleSpec::State>(state | StyleSpec::STATE_PRESSED);
+
+ if (isHovered())
+ state = static_cast<StyleSpec::State>(state | StyleSpec::STATE_HOVERED);
+
+ setFromStyle(StyleSpec::getStyleFromStatePropagation(Styles, state));
}
//! Set element properties from a StyleSpec
-void GUIButton::setFromStyle(const StyleSpec& style, ISimpleTextureSource *tsrc)
+void GUIButton::setFromStyle(const StyleSpec& style)
{
+ bool hovered = (style.getState() & StyleSpec::STATE_HOVERED) != 0;
+ bool pressed = (style.getState() & StyleSpec::STATE_PRESSED) != 0;
+
if (style.isNotDefault(StyleSpec::BGCOLOR)) {
+
setColor(style.getColor(StyleSpec::BGCOLOR));
- }
- if (style.isNotDefault(StyleSpec::BGCOLOR_HOVERED)) {
- setHoveredColor(style.getColor(StyleSpec::BGCOLOR_HOVERED));
- }
- if (style.isNotDefault(StyleSpec::BGCOLOR_PRESSED)) {
- setPressedColor(style.getColor(StyleSpec::BGCOLOR_PRESSED));
+
+ // If we have a propagated hover/press color, we need to automatically
+ // lighten/darken it
+ if (!Styles[style.getState()].isNotDefault(StyleSpec::BGCOLOR)) {
+ for (size_t i = 0; i < 4; i++) {
+ if (pressed) {
+ Colors[i] = multiplyColorValue(Colors[i], COLOR_PRESSED_MOD);
+ } else if (hovered) {
+ Colors[i] = multiplyColorValue(Colors[i], COLOR_HOVERED_MOD);
+ }
+ }
+ }
+
+ } else {
+ for (size_t i = 0; i < 4; i++) {
+ video::SColor base =
+ Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
+ if (pressed) {
+ Colors[i] = multiplyColorValue(base, COLOR_PRESSED_MOD);
+ } else if (hovered) {
+ Colors[i] = multiplyColorValue(base, COLOR_HOVERED_MOD);
+ } else {
+ Colors[i] = base;
+ }
+ }
}
if (style.isNotDefault(StyleSpec::TEXTCOLOR)) {
setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR));
+ } else {
+ setOverrideColor(video::SColor(255,255,255,255));
+ OverrideColorEnabled = false;
}
- setNotClipped(style.getBool(StyleSpec::NOCLIP, isNotClipped()));
- setDrawBorder(style.getBool(StyleSpec::BORDER, DrawBorder));
+ setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ setDrawBorder(style.getBool(StyleSpec::BORDER, true));
setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
- const core::position2di buttonCenter(AbsoluteRect.getCenter());
- core::position2d<s32> geom(buttonCenter);
if (style.isNotDefault(StyleSpec::BGIMG)) {
- video::ITexture *texture = style.getTexture(StyleSpec::BGIMG, tsrc);
-
+ video::ITexture *texture = style.getTexture(StyleSpec::BGIMG,
+ getTextureSource());
setImage(guiScalingImageButton(
- Environment->getVideoDriver(), texture, geom.X, geom.Y));
+ Environment->getVideoDriver(), texture,
+ AbsoluteRect.getWidth(), AbsoluteRect.getHeight()));
setScaleImage(true);
+ } else {
+ setImage(nullptr);
}
- if (style.isNotDefault(StyleSpec::BGIMG_HOVERED)) {
- video::ITexture *hovered_texture = style.getTexture(StyleSpec::BGIMG_HOVERED, tsrc);
- setHoveredImage(guiScalingImageButton(
- Environment->getVideoDriver(), hovered_texture, geom.X, geom.Y));
- setScaleImage(true);
- }
- if (style.isNotDefault(StyleSpec::BGIMG_PRESSED)) {
- video::ITexture *pressed_texture = style.getTexture(StyleSpec::BGIMG_PRESSED, tsrc);
+ BgMiddle = style.getRect(StyleSpec::BGIMG_MIDDLE, BgMiddle);
- setPressedImage(guiScalingImageButton(
- Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
- setScaleImage(true);
+ // Child padding and offset
+ Padding = style.getRect(StyleSpec::PADDING, core::rect<s32>());
+ Padding = core::rect<s32>(
+ Padding.UpperLeftCorner + BgMiddle.UpperLeftCorner,
+ Padding.LowerRightCorner + BgMiddle.LowerRightCorner);
+
+ GUISkin* skin = dynamic_cast<GUISkin*>(Environment->getSkin());
+ core::vector2d<s32> defaultPressOffset(
+ skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
+ skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y));
+ ContentOffset = style.getVector2i(StyleSpec::CONTENT_OFFSET, isPressed()
+ ? defaultPressOffset
+ : core::vector2d<s32>(0));
+
+ core::rect<s32> childBounds(
+ Padding.UpperLeftCorner.X + ContentOffset.X,
+ Padding.UpperLeftCorner.Y + ContentOffset.Y,
+ AbsoluteRect.getWidth() + Padding.LowerRightCorner.X + ContentOffset.X,
+ AbsoluteRect.getHeight() + Padding.LowerRightCorner.Y + ContentOffset.Y);
+
+ for (IGUIElement *child : getChildren()) {
+ child->setRelativePosition(childBounds);
}
- BgMiddle = style.getRect(StyleSpec::BGIMG_MIDDLE, BgMiddle);
+}
+
+//! Set the styles used for each state
+void GUIButton::setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES>& styles)
+{
+ Styles = styles;
+ setFromState();
}
// END PATCH
diff --git a/src/gui/guiButton.h b/src/gui/guiButton.h index 3d1f98c32..95fa1a2a1 100644 --- a/src/gui/guiButton.h +++ b/src/gui/guiButton.h @@ -13,6 +13,7 @@ #include "ITexture.h"
#include "SColor.h"
#include "guiSkin.h"
+#include "StyleSpec.h"
using namespace irr;
@@ -67,7 +68,6 @@ using namespace irr; #endif
class ISimpleTextureSource;
-class StyleSpec;
class GUIButton : public gui::IGUIButton
{
@@ -75,7 +75,8 @@ public: //! constructor
GUIButton(gui::IGUIEnvironment* environment, gui::IGUIElement* parent,
- s32 id, core::rect<s32> rectangle, bool noclip=false);
+ s32 id, core::rect<s32> rectangle, ISimpleTextureSource *tsrc,
+ bool noclip=false);
//! destructor
virtual ~GUIButton();
@@ -125,16 +126,10 @@ public: //! Sets an image which should be displayed on the button when it is in pressed state.
virtual void setPressedImage(video::ITexture* image, const core::rect<s32>& pos) override;
- //! Sets an image which should be displayed on the button when it is in hovered state.
- virtual void setHoveredImage(video::ITexture* image=nullptr);
-
//! Sets the text displayed by the button
virtual void setText(const wchar_t* text) override;
// END PATCH
- //! Sets an image which should be displayed on the button when it is in hovered state.
- virtual void setHoveredImage(video::ITexture* image, const core::rect<s32>& pos);
-
//! Sets the sprite bank used by the button
virtual void setSpriteBank(gui::IGUISpriteBank* bank=0) override;
@@ -225,22 +220,29 @@ public: void setColor(video::SColor color);
// PATCH
- void setHoveredColor(video::SColor color);
- void setPressedColor(video::SColor color);
+ //! Set element properties from a StyleSpec corresponding to the button state
+ void setFromState();
//! Set element properties from a StyleSpec
- virtual void setFromStyle(const StyleSpec& style, ISimpleTextureSource *tsrc);
+ virtual void setFromStyle(const StyleSpec& style);
+
+ //! Set the styles used for each state
+ void setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES>& styles);
// END PATCH
//! Do not drop returned handle
- static GUIButton* addButton(gui::IGUIEnvironment *environment, const core::rect<s32>& rectangle,
- IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext=L"");
+ static GUIButton* addButton(gui::IGUIEnvironment *environment,
+ const core::rect<s32>& rectangle, ISimpleTextureSource *tsrc,
+ IGUIElement* parent, s32 id, const wchar_t* text,
+ const wchar_t *tooltiptext=L"");
protected:
void drawSprite(gui::EGUI_BUTTON_STATE state, u32 startTime, const core::position2di& center);
gui::EGUI_BUTTON_IMAGE_STATE getImageState(bool pressed) const;
+ ISimpleTextureSource *getTextureSource() { return TSrc; }
+
struct ButtonImage
{
ButtonImage() : Texture(0), SourceRect(core::rect<s32>(0,0,0,0))
@@ -308,6 +310,8 @@ private: ButtonImage ButtonImages[gui::EGBIS_COUNT];
+ std::array<StyleSpec, StyleSpec::NUM_STATES> Styles;
+
gui::IGUIFont* OverrideFont;
bool OverrideColorEnabled;
@@ -326,11 +330,13 @@ private: video::SColor Colors[4];
// PATCH
- video::SColor HoveredColors[4];
- video::SColor PressedColors[4];
+ bool WasHovered = false;
+ ISimpleTextureSource *TSrc;
gui::IGUIStaticText *StaticText;
core::rect<s32> BgMiddle;
+ core::rect<s32> Padding;
+ core::vector2d<s32> ContentOffset;
// END PATCH
};
diff --git a/src/gui/guiButtonImage.cpp b/src/gui/guiButtonImage.cpp index 02d920277..b507ffece 100644 --- a/src/gui/guiButtonImage.cpp +++ b/src/gui/guiButtonImage.cpp @@ -30,8 +30,9 @@ using namespace irr; using namespace gui; GUIButtonImage::GUIButtonImage(gui::IGUIEnvironment *environment, - gui::IGUIElement *parent, s32 id, core::rect<s32> rectangle, bool noclip) - : GUIButton (environment, parent, id, rectangle, noclip) + gui::IGUIElement *parent, s32 id, core::rect<s32> rectangle, + ISimpleTextureSource *tsrc, bool noclip) + : GUIButton (environment, parent, id, rectangle, tsrc, noclip) { m_image = Environment->addImage( core::rect<s32>(0,0,rectangle.getWidth(),rectangle.getHeight()), this); @@ -39,100 +40,37 @@ GUIButtonImage::GUIButtonImage(gui::IGUIEnvironment *environment, sendToBack(m_image); } -bool GUIButtonImage::OnEvent(const SEvent& event) -{ - bool result = GUIButton::OnEvent(event); - - EGUI_BUTTON_IMAGE_STATE imageState = getImageState(isPressed(), m_foreground_images); - video::ITexture *texture = m_foreground_images[(u32)imageState].Texture; - if (texture != nullptr) - { - m_image->setImage(texture); - } - - m_image->setVisible(texture != nullptr); - - return result; -} - -void GUIButtonImage::setForegroundImage(EGUI_BUTTON_IMAGE_STATE state, - video::ITexture *image, const core::rect<s32> &sourceRect) +void GUIButtonImage::setForegroundImage(video::ITexture *image) { - if (state >= EGBIS_COUNT) + if (image == m_foreground_image) return; - if (image) + if (image != nullptr) image->grab(); - u32 stateIdx = (u32)state; - if (m_foreground_images[stateIdx].Texture) - m_foreground_images[stateIdx].Texture->drop(); - - m_foreground_images[stateIdx].Texture = image; - m_foreground_images[stateIdx].SourceRect = sourceRect; - - EGUI_BUTTON_IMAGE_STATE imageState = getImageState(isPressed(), m_foreground_images); - if (imageState == stateIdx) - m_image->setImage(image); -} - -void GUIButtonImage::setForegroundImage(video::ITexture *image) -{ - setForegroundImage(gui::EGBIS_IMAGE_UP, image); -} - -void GUIButtonImage::setForegroundImage(video::ITexture *image, const core::rect<s32> &pos) -{ - setForegroundImage(gui::EGBIS_IMAGE_UP, image, pos); -} - -void GUIButtonImage::setPressedForegroundImage(video::ITexture *image) -{ - setForegroundImage(gui::EGBIS_IMAGE_DOWN, image); -} + if (m_foreground_image != nullptr) + m_foreground_image->drop(); -void GUIButtonImage::setPressedForegroundImage(video::ITexture *image, const core::rect<s32> &pos) -{ - setForegroundImage(gui::EGBIS_IMAGE_DOWN, image, pos); + m_foreground_image = image; + m_image->setImage(image); } -void GUIButtonImage::setHoveredForegroundImage(video::ITexture *image) +//! Set element properties from a StyleSpec +void GUIButtonImage::setFromStyle(const StyleSpec& style) { - setForegroundImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image); - setForegroundImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image); -} - -void GUIButtonImage::setHoveredForegroundImage(video::ITexture *image, const core::rect<s32> &pos) -{ - setForegroundImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image, pos); - setForegroundImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image, pos); -} - -void GUIButtonImage::setFromStyle(const StyleSpec &style, ISimpleTextureSource *tsrc) -{ - GUIButton::setFromStyle(style, tsrc); + GUIButton::setFromStyle(style); video::IVideoDriver *driver = Environment->getVideoDriver(); - const core::position2di buttonCenter(AbsoluteRect.getCenter()); - core::position2d<s32> geom(buttonCenter); if (style.isNotDefault(StyleSpec::FGIMG)) { - video::ITexture *texture = style.getTexture(StyleSpec::FGIMG, tsrc); - - setForegroundImage(guiScalingImageButton(driver, texture, geom.X, geom.Y)); - setScaleImage(true); - } - if (style.isNotDefault(StyleSpec::FGIMG_HOVERED)) { - video::ITexture *hovered_texture = style.getTexture(StyleSpec::FGIMG_HOVERED, tsrc); - - setHoveredForegroundImage(guiScalingImageButton(driver, hovered_texture, geom.X, geom.Y)); - setScaleImage(true); - } - if (style.isNotDefault(StyleSpec::FGIMG_PRESSED)) { - video::ITexture *pressed_texture = style.getTexture(StyleSpec::FGIMG_PRESSED, tsrc); + video::ITexture *texture = style.getTexture(StyleSpec::FGIMG, + getTextureSource()); - setPressedForegroundImage(guiScalingImageButton(driver, pressed_texture, geom.X, geom.Y)); + setForegroundImage(guiScalingImageButton(driver, texture, + AbsoluteRect.getWidth(), AbsoluteRect.getHeight())); setScaleImage(true); + } else { + setForegroundImage(nullptr); } } @@ -143,11 +81,12 @@ void GUIButtonImage::setScaleImage(bool scaleImage) } GUIButtonImage *GUIButtonImage::addButton(IGUIEnvironment *environment, - const core::rect<s32> &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, const wchar_t *tooltiptext) + const core::rect<s32> &rectangle, ISimpleTextureSource *tsrc, + IGUIElement *parent, s32 id, const wchar_t *text, + const wchar_t *tooltiptext) { GUIButtonImage *button = new GUIButtonImage(environment, - parent ? parent : environment->getRootGUIElement(), id, rectangle); + parent ? parent : environment->getRootGUIElement(), id, rectangle, tsrc); if (text) button->setText(text); diff --git a/src/gui/guiButtonImage.h b/src/gui/guiButtonImage.h index 15901ee5d..59a25b4f0 100644 --- a/src/gui/guiButtonImage.h +++ b/src/gui/guiButtonImage.h @@ -17,6 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#pragma once + #include "guiButton.h" #include "IGUIButton.h" @@ -27,33 +29,23 @@ class GUIButtonImage : public GUIButton public: //! constructor GUIButtonImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent, - s32 id, core::rect<s32> rectangle, bool noclip = false); - - virtual bool OnEvent(const SEvent& event) override; - - void setForegroundImage(gui::EGUI_BUTTON_IMAGE_STATE state, - video::ITexture *image = nullptr, - const core::rect<s32> &sourceRect = core::rect<s32>(0, 0, 0, 0)); + s32 id, core::rect<s32> rectangle, ISimpleTextureSource *tsrc, + bool noclip = false); void setForegroundImage(video::ITexture *image = nullptr); - void setForegroundImage(video::ITexture *image, const core::rect<s32> &pos); - - void setPressedForegroundImage(video::ITexture *image = nullptr); - void setPressedForegroundImage(video::ITexture *image, const core::rect<s32> &pos); - - void setHoveredForegroundImage(video::ITexture *image = nullptr); - void setHoveredForegroundImage(video::ITexture *image, const core::rect<s32> &pos); - virtual void setFromStyle(const StyleSpec &style, ISimpleTextureSource *tsrc) override; + //! Set element properties from a StyleSpec + virtual void setFromStyle(const StyleSpec& style) override; virtual void setScaleImage(bool scaleImage=true) override; //! Do not drop returned handle static GUIButtonImage *addButton(gui::IGUIEnvironment *environment, - const core::rect<s32> &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, const wchar_t *tooltiptext = L""); + const core::rect<s32> &rectangle, ISimpleTextureSource *tsrc, + IGUIElement *parent, s32 id, const wchar_t *text, + const wchar_t *tooltiptext = L""); private: - ButtonImage m_foreground_images[gui::EGBIS_COUNT]; + video::ITexture *m_foreground_image = nullptr; gui::IGUIImage *m_image; }; diff --git a/src/gui/guiButtonItemImage.cpp b/src/gui/guiButtonItemImage.cpp index 5c48b2acd..d8b9042ac 100644 --- a/src/gui/guiButtonItemImage.cpp +++ b/src/gui/guiButtonItemImage.cpp @@ -28,9 +28,11 @@ with this program; if not, write to the Free Software Foundation, Inc., using namespace irr; using namespace gui; -GUIButtonItemImage::GUIButtonItemImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent, - s32 id, core::rect<s32> rectangle, std::string item, Client *client, bool noclip) - : GUIButton (environment, parent, id, rectangle, noclip) +GUIButtonItemImage::GUIButtonItemImage(gui::IGUIEnvironment *environment, + gui::IGUIElement *parent, s32 id, core::rect<s32> rectangle, + ISimpleTextureSource *tsrc, std::string item, Client *client, + bool noclip) + : GUIButton (environment, parent, id, rectangle, tsrc, noclip) { m_image = new GUIItemImage(environment, this, id, core::rect<s32>(0,0,rectangle.getWidth(),rectangle.getHeight()), @@ -42,12 +44,13 @@ GUIButtonItemImage::GUIButtonItemImage(gui::IGUIEnvironment *environment, gui::I } GUIButtonItemImage *GUIButtonItemImage::addButton(IGUIEnvironment *environment, - const core::rect<s32> &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, std::string item, Client *client) + const core::rect<s32> &rectangle, ISimpleTextureSource *tsrc, + IGUIElement *parent, s32 id, const wchar_t *text, std::string item, + Client *client) { GUIButtonItemImage *button = new GUIButtonItemImage(environment, parent ? parent : environment->getRootGUIElement(), - id, rectangle, item, client); + id, rectangle, tsrc, item, client); if (text) button->setText(text); diff --git a/src/gui/guiButtonItemImage.h b/src/gui/guiButtonItemImage.h index 0a61874da..aad923bda 100644 --- a/src/gui/guiButtonItemImage.h +++ b/src/gui/guiButtonItemImage.h @@ -17,6 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#pragma once + #include "guiButton.h" #include "IGUIButton.h" @@ -30,13 +32,14 @@ class GUIButtonItemImage : public GUIButton public: //! constructor GUIButtonItemImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent, - s32 id, core::rect<s32> rectangle, std::string item, - Client *client, bool noclip = false); + s32 id, core::rect<s32> rectangle, ISimpleTextureSource *tsrc, + std::string item, Client *client, bool noclip = false); //! Do not drop returned handle static GUIButtonItemImage *addButton(gui::IGUIEnvironment *environment, - const core::rect<s32> &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, std::string item, Client *client); + const core::rect<s32> &rectangle, ISimpleTextureSource *tsrc, + IGUIElement *parent, s32 id, const wchar_t *text, std::string item, + Client *client); private: std::string m_item_name; diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp index e67fae3c6..8de00c12f 100644 --- a/src/gui/guiChatConsole.cpp +++ b/src/gui/guiChatConsole.cpp @@ -74,7 +74,9 @@ GUIChatConsole::GUIChatConsole( m_background_color.setBlue(clamp_u8(myround(console_color.Z))); } - m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, FM_Mono); + u16 chat_font_size = g_settings->getU16("chat_font_size"); + m_font = g_fontengine->getFont(chat_font_size != 0 ? + chat_font_size : FONT_SIZE_UNSPECIFIED, FM_Mono); if (!m_font) { errorstream << "GUIChatConsole: Unable to load mono font" << std::endl; diff --git a/src/gui/guiConfirmRegistration.cpp b/src/gui/guiConfirmRegistration.cpp index 0d8bdf54e..58ac42740 100644 --- a/src/gui/guiConfirmRegistration.cpp +++ b/src/gui/guiConfirmRegistration.cpp @@ -40,10 +40,10 @@ const int ID_message = 266; GUIConfirmRegistration::GUIConfirmRegistration(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, Client *client, const std::string &playername, const std::string &password, - bool *aborted) : + bool *aborted, ISimpleTextureSource *tsrc) : GUIModalMenu(env, parent, id, menumgr), m_client(client), m_playername(playername), m_password(password), - m_aborted(aborted) + m_aborted(aborted), m_tsrc(tsrc) { #ifdef __ANDROID__ m_touchscreen_visible = false; @@ -130,14 +130,14 @@ void GUIConfirmRegistration::regenerateGui(v2u32 screensize) core::rect<s32> rect2(0, 0, 230 * s, 35 * s); rect2 = rect2 + v2s32(size.X / 2 - 220 * s, ypos); text = wgettext("Register and Join"); - GUIButton::addButton(Environment, rect2, this, ID_confirm, text); + GUIButton::addButton(Environment, rect2, m_tsrc, this, ID_confirm, text); delete[] text; } { core::rect<s32> rect2(0, 0, 120 * s, 35 * s); rect2 = rect2 + v2s32(size.X / 2 + 70 * s, ypos); text = wgettext("Cancel"); - GUIButton::addButton(Environment, rect2, this, ID_cancel, text); + GUIButton::addButton(Environment, rect2, m_tsrc, this, ID_cancel, text); delete[] text; } { diff --git a/src/gui/guiConfirmRegistration.h b/src/gui/guiConfirmRegistration.h index 42c07e4ed..d8387201d 100644 --- a/src/gui/guiConfirmRegistration.h +++ b/src/gui/guiConfirmRegistration.h @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <string> class Client; +class ISimpleTextureSource; class GUIConfirmRegistration : public GUIModalMenu { @@ -32,7 +33,7 @@ public: GUIConfirmRegistration(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, Client *client, const std::string &playername, const std::string &password, - bool *aborted); + bool *aborted, ISimpleTextureSource *tsrc); ~GUIConfirmRegistration(); void removeChildren(); @@ -63,4 +64,5 @@ private: const std::string &m_password; bool *m_aborted = nullptr; std::wstring m_pass_confirm = L""; + ISimpleTextureSource *m_tsrc; }; diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index 3107d64cd..b40707d01 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -144,10 +144,10 @@ GUIEngine::GUIEngine(JoystickController *joystick, //create soundmanager MenuMusicFetcher soundfetcher; #if USE_SOUND - if (g_settings->getBool("enable_sound")) + if (g_settings->getBool("enable_sound") && g_sound_manager_singleton.get()) m_sound_manager = createOpenALSoundManager(g_sound_manager_singleton.get(), &soundfetcher); #endif - if(!m_sound_manager) + if (!m_sound_manager) m_sound_manager = &dummySoundManager; //create topleft header diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 59cd130ef..37edf3c4b 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -65,6 +65,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiInventoryList.h" #include "guiItemImage.h" #include "guiScrollBar.h" +#include "guiScrollContainer.h" #include "guiTable.h" #include "intlGUIEditBox.h" #include "guiHyperText.h" @@ -143,6 +144,8 @@ GUIFormSpecMenu::~GUIFormSpecMenu() tooltip_rect_it.first->drop(); for (auto &clickthrough_it : m_clickthrough_elements) clickthrough_it->drop(); + for (auto &scroll_container_it : m_scroll_containers) + scroll_container_it.second->drop(); delete m_selected_item; delete m_form_src; @@ -351,6 +354,102 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data) } } +void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string &element) +{ + std::vector<std::string> parts = split(element, ';'); + + if (parts.size() < 4 || + (parts.size() > 5 && m_formspec_version <= FORMSPEC_API_VERSION)) { + errorstream << "Invalid scroll_container start element (" << parts.size() + << "): '" << element << "'" << std::endl; + return; + } + + std::vector<std::string> v_pos = split(parts[0], ','); + std::vector<std::string> v_geom = split(parts[1], ','); + std::string scrollbar_name = parts[2]; + std::string orientation = parts[3]; + f32 scroll_factor = 0.1f; + if (parts.size() >= 5 && !parts[4].empty()) + scroll_factor = stof(parts[4]); + + MY_CHECKPOS("scroll_container", 0); + MY_CHECKGEOM("scroll_container", 1); + + v2s32 pos = getRealCoordinateBasePos(v_pos); + v2s32 geom = getRealCoordinateGeometry(v_geom); + + if (orientation == "vertical") + scroll_factor *= -imgsize.Y; + else if (orientation == "horizontal") + scroll_factor *= -imgsize.X; + else + warningstream << "GUIFormSpecMenu::parseScrollContainer(): " + << "Invalid scroll_container orientation: " << orientation + << std::endl; + + // old parent (at first: this) + // ^ is parent of clipper + // ^ is parent of mover + // ^ is parent of other elements + + // make clipper + core::rect<s32> rect_clipper = core::rect<s32>(pos, pos + geom); + + gui::IGUIElement *clipper = new gui::IGUIElement(EGUIET_ELEMENT, Environment, + data->current_parent, 0, rect_clipper); + + // make mover + FieldSpec spec_mover( + "", + L"", + L"", + 258 + m_fields.size() + ); + + core::rect<s32> rect_mover = core::rect<s32>(0, 0, geom.X, geom.Y); + + GUIScrollContainer *mover = new GUIScrollContainer(Environment, + clipper, spec_mover.fid, rect_mover, orientation, scroll_factor); + + data->current_parent = mover; + + m_scroll_containers.emplace_back(scrollbar_name, mover); + + m_fields.push_back(spec_mover); + + clipper->drop(); + + // remove interferring offset of normal containers + container_stack.push(pos_offset); + pos_offset.X = 0.0f; + pos_offset.Y = 0.0f; +} + +void GUIFormSpecMenu::parseScrollContainerEnd(parserData *data) +{ + if (data->current_parent == this || data->current_parent->getParent() == this || + container_stack.empty()) { + errorstream << "Invalid scroll_container end element, " + << "no matching scroll_container start element" << std::endl; + return; + } + + if (pos_offset.getLengthSQ() != 0.0f) { + // pos_offset is only set by containers and scroll_containers. + // scroll_containers always set it to 0,0 which means that if it is + // not 0,0, it is a normal container that was opened last, not a + // scroll_container + errorstream << "Invalid scroll_container end element, " + << "an inner container was left open" << std::endl; + return; + } + + data->current_parent = data->current_parent->getParent()->getParent(); + pos_offset = container_stack.top(); + container_stack.pop(); +} + void GUIFormSpecMenu::parseList(parserData *data, const std::string &element) { if (m_client == 0) { @@ -390,38 +489,10 @@ void GUIFormSpecMenu::parseList(parserData *data, const std::string &element) start_i = stoi(startindex); if (geom.X < 0 || geom.Y < 0 || start_i < 0) { - errorstream<< "Invalid list element: '" << element << "'" << std::endl; + errorstream << "Invalid list element: '" << element << "'" << std::endl; return; } - // check for the existence of inventory and list - Inventory *inv = m_invmgr->getInventory(loc); - if (!inv) { - warningstream << "GUIFormSpecMenu::parseList(): " - << "The inventory location " - << "\"" << loc.dump() << "\" doesn't exist" - << std::endl; - return; - } - InventoryList *ilist = inv->getList(listname); - if (!ilist) { - warningstream << "GUIFormSpecMenu::parseList(): " - << "The inventory list \"" << listname << "\" " - << "@ \"" << loc.dump() << "\" doesn't exist" - << std::endl; - return; - } - - // trim geom if it is larger than the actual inventory size - s32 list_size = (s32)ilist->getSize(); - if (list_size < geom.X * geom.Y + start_i) { - list_size -= MYMAX(start_i, 0); - geom.Y = list_size / geom.X; - geom.Y += list_size % geom.X > 0 ? 1 : 0; - if (geom.Y <= 1) - geom.X = list_size; - } - if (!data->explicit_size) warningstream << "invalid use of list without a size[] element" << std::endl; @@ -443,9 +514,9 @@ void GUIFormSpecMenu::parseList(parserData *data, const std::string &element) pos.X + (geom.X - 1) * slot_spacing.X + imgsize.X, pos.Y + (geom.Y - 1) * slot_spacing.Y + imgsize.Y); - GUIInventoryList *e = new GUIInventoryList(Environment, this, spec.fid, - rect, m_invmgr, loc, listname, geom, start_i, imgsize, slot_spacing, - this, data->inventorylist_options, m_font); + GUIInventoryList *e = new GUIInventoryList(Environment, data->current_parent, + spec.fid, rect, m_invmgr, loc, listname, geom, start_i, imgsize, + slot_spacing, this, data->inventorylist_options, m_font); m_inventorylists.push_back(e); m_fields.push_back(spec); @@ -550,10 +621,10 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element spec.ftype = f_CheckBox; - gui::IGUICheckBox *e = Environment->addCheckBox(fselected, rect, this, - spec.fid, spec.flabel.c_str()); + gui::IGUICheckBox *e = Environment->addCheckBox(fselected, rect, + data->current_parent, spec.fid, spec.flabel.c_str()); - auto style = getStyleForElement("checkbox", name); + auto style = getDefaultStyleForElement("checkbox", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); if (spec.fname == data->focused_fieldname) { @@ -610,10 +681,10 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen spec.ftype = f_ScrollBar; spec.send = true; - GUIScrollBar *e = new GUIScrollBar(Environment, this, spec.fid, rect, - is_horizontal, true); + GUIScrollBar *e = new GUIScrollBar(Environment, data->current_parent, + spec.fid, rect, is_horizontal, true); - auto style = getStyleForElement("scrollbar", name); + auto style = getDefaultStyleForElement("scrollbar", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setArrowsVisible(data->scrollbar_options.arrow_visiblity); @@ -737,10 +808,11 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) 1 ); core::rect<s32> rect(pos, pos + geom); - gui::IGUIImage *e = Environment->addImage(rect, this, spec.fid, 0, true); + gui::IGUIImage *e = Environment->addImage(rect, data->current_parent, + spec.fid, 0, true); e->setImage(texture); e->setScaleImage(true); - auto style = getStyleForElement("image", spec.fname); + auto style = getDefaultStyleForElement("image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); m_fields.push_back(spec); @@ -774,9 +846,9 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) L"", 258 + m_fields.size() ); - gui::IGUIImage *e = Environment->addImage(texture, pos, true, this, - spec.fid, 0); - auto style = getStyleForElement("image", spec.fname); + gui::IGUIImage *e = Environment->addImage(texture, pos, true, + data->current_parent, spec.fid, 0); + auto style = getDefaultStyleForElement("image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); m_fields.push_back(spec); @@ -841,9 +913,11 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el if (parts.size() >= 7) e->setFrameIndex(stoi(parts[6]) - 1); - auto style = getStyleForElement("animated_image", spec.fname, "image"); + auto style = getDefaultStyleForElement("animated_image", spec.fname, "image"); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - e->drop(); + + // Animated images should let events through + m_clickthrough_elements.push_back(e); m_fields.push_back(spec); } @@ -886,9 +960,9 @@ void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &elemen ); spec.ftype = f_ItemImage; - GUIItemImage *e = new GUIItemImage(Environment, this, spec.fid, + GUIItemImage *e = new GUIItemImage(Environment, data->current_parent, spec.fid, core::rect<s32>(pos, pos + geom), name, m_font, m_client); - auto style = getStyleForElement("item_image", spec.fname); + auto style = getDefaultStyleForElement("item_image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); // item images should let events through @@ -949,10 +1023,11 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, if(type == "button_exit") spec.is_exit = true; - GUIButton *e = GUIButton::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str()); + GUIButton *e = GUIButton::addButton(Environment, rect, m_tsrc, + data->current_parent, spec.fid, spec.flabel.c_str()); auto style = getStyleForElement(type, name, (type != "button") ? "button" : ""); - e->setFromStyle(style, m_tsrc); + e->setStyles(style); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1140,7 +1215,8 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) } //now really show table - GUITable *e = new GUITable(Environment, this, spec.fid, rect, m_tsrc); + GUITable *e = new GUITable(Environment, data->current_parent, spec.fid, + rect, m_tsrc); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1155,7 +1231,7 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) if (!str_initial_selection.empty() && str_initial_selection != "0") e->setSelected(stoi(str_initial_selection)); - auto style = getStyleForElement("table", name); + auto style = getDefaultStyleForElement("table", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); m_tables.emplace_back(spec, e); @@ -1216,7 +1292,8 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element } //now really show list - GUITable *e = new GUITable(Environment, this, spec.fid, rect, m_tsrc); + GUITable *e = new GUITable(Environment, data->current_parent, spec.fid, + rect, m_tsrc); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1231,7 +1308,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element if (!str_initial_selection.empty() && str_initial_selection != "0") e->setSelected(stoi(str_initial_selection)); - auto style = getStyleForElement("textlist", name); + auto style = getDefaultStyleForElement("textlist", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); m_tables.emplace_back(spec, e); @@ -1292,7 +1369,8 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element spec.send = true; //now really show list - gui::IGUIComboBox *e = Environment->addComboBox(rect, this, spec.fid); + gui::IGUIComboBox *e = Environment->addComboBox(rect, data->current_parent, + spec.fid); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1306,7 +1384,7 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element if (!str_initial_selection.empty()) e->setSelected(stoi(str_initial_selection)-1); - auto style = getStyleForElement("dropdown", name); + auto style = getDefaultStyleForElement("dropdown", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); m_fields.push_back(spec); @@ -1378,7 +1456,8 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element ); spec.send = true; - gui::IGUIEditBox * e = Environment->addEditBox(0, rect, true, this, spec.fid); + gui::IGUIEditBox *e = Environment->addEditBox(0, rect, true, + data->current_parent, spec.fid); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1389,12 +1468,12 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element rect.UpperLeftCorner.Y -= font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true, - this, 0); + data->current_parent, 0); } e->setPasswordBox(true,L'*'); - auto style = getStyleForElement("pwdfield", name, "field"); + auto style = getDefaultStyleForElement("pwdfield", name, "field"); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); @@ -1424,7 +1503,7 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, if (!is_editable && !is_multiline) { // spec field id to 0, this stops submit searching for a value that isn't there gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true, - this, spec.fid); + data->current_parent, 0); return; } @@ -1442,19 +1521,19 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, if (use_intl_edit_box && g_settings->getBool("freetype")) { e = new gui::intlGUIEditBox(spec.fdefault.c_str(), true, Environment, - this, spec.fid, rect, is_editable, is_multiline); + data->current_parent, spec.fid, rect, is_editable, is_multiline); } else { if (is_multiline) { - e = new GUIEditBoxWithScrollBar(spec.fdefault.c_str(), true, - Environment, this, spec.fid, rect, is_editable, true); + e = new GUIEditBoxWithScrollBar(spec.fdefault.c_str(), true, Environment, + data->current_parent, spec.fid, rect, is_editable, true); } else if (is_editable) { - e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, - spec.fid); + e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, + data->current_parent, spec.fid); e->grab(); } } - auto style = getStyleForElement(is_multiline ? "textarea" : "field", spec.fname); + auto style = getDefaultStyleForElement(is_multiline ? "textarea" : "field", spec.fname); if (e) { if (is_editable && spec.fname == data->focused_fieldname) @@ -1490,7 +1569,7 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, rect.UpperLeftCorner.Y -= font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; IGUIElement *t = gui::StaticText::add(Environment, spec.flabel.c_str(), - rect, false, true, this, 0); + rect, false, true, data->current_parent, 0); if (t) t->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); @@ -1670,8 +1749,8 @@ void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &elemen ); spec.ftype = f_HyperText; - GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment, this, - spec.fid, rect, m_client, m_tsrc); + GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment, + data->current_parent, spec.fid, rect, m_client, m_tsrc); e->drop(); m_fields.push_back(spec); @@ -1749,10 +1828,11 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) 4 ); gui::IGUIStaticText *e = gui::StaticText::add(Environment, - spec.flabel.c_str(), rect, false, false, this, spec.fid); + spec.flabel.c_str(), rect, false, false, data->current_parent, + spec.fid); e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); - auto style = getStyleForElement("label", spec.fname); + auto style = getDefaultStyleForElement("label", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); @@ -1829,10 +1909,10 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen 258 + m_fields.size() ); gui::IGUIStaticText *e = gui::StaticText::add(Environment, spec.flabel.c_str(), - rect, false, false, this, spec.fid); + rect, false, false, data->current_parent, spec.fid); e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); - auto style = getStyleForElement("vertlabel", spec.fname, "label"); + auto style = getDefaultStyleForElement("vertlabel", spec.fname, "label"); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); @@ -1863,17 +1943,8 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem MY_CHECKPOS("imagebutton",0); MY_CHECKGEOM("imagebutton",1); - bool noclip = false; - bool drawborder = true; std::string pressed_image_name; - if (parts.size() >= 7) { - if (parts[5] == "true") - noclip = true; - if (parts[6] == "false") - drawborder = false; - } - if (parts.size() >= 8) { pressed_image_name = parts[7]; } @@ -1911,35 +1982,30 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem if (type == "image_button_exit") spec.is_exit = true; - GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str()); + GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, m_tsrc, + data->current_parent, spec.fid, spec.flabel.c_str()); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); } auto style = getStyleForElement("image_button", spec.fname); - e->setFromStyle(style, m_tsrc); - // We explicitly handle these arguments *after* the style properties in - // order to override them if they are provided + // Override style properties with values specified directly in the element if (!image_name.empty()) - { - video::ITexture *texture = m_tsrc->getTexture(image_name); - e->setForegroundImage(guiScalingImageButton( - Environment->getVideoDriver(), texture, geom.X, geom.Y)); - } - if (!pressed_image_name.empty()) { - video::ITexture *pressed_texture = m_tsrc->getTexture(pressed_image_name); - e->setPressedForegroundImage(guiScalingImageButton( - Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y)); - } - e->setScaleImage(true); + style[StyleSpec::STATE_DEFAULT].set(StyleSpec::FGIMG, image_name); + + if (!pressed_image_name.empty()) + style[StyleSpec::STATE_PRESSED].set(StyleSpec::FGIMG, pressed_image_name); if (parts.size() >= 7) { - e->setNotClipped(noclip); - e->setDrawBorder(drawborder); + style[StyleSpec::STATE_DEFAULT].set(StyleSpec::NOCLIP, parts[5]); + style[StyleSpec::STATE_DEFAULT].set(StyleSpec::BORDER, parts[6]); } + e->setStyles(style); + e->setScaleImage(true); + m_fields.push_back(spec); return; } @@ -1961,7 +2027,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen // Width is not here because tabs are the width of the text, and // there's no reason to change that. unsigned int i = 0; - std::vector<std::string> v_geom = {"1", "0.75"}; // Dummy width and default height + std::vector<std::string> v_geom = {"1", "1"}; // Dummy width and height bool auto_width = true; if (parts.size() == 7) { i++; @@ -2005,6 +2071,9 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen pos = getRealCoordinateBasePos(v_pos); geom = getRealCoordinateGeometry(v_geom); + // Set default height + if (parts.size() <= 6) + geom.Y = m_btn_height * 2; pos.Y -= geom.Y; // TabHeader base pos is the bottom, not the top. if (auto_width) geom.X = DesiredRect.getWidth(); // Set automatic width @@ -2023,8 +2092,8 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - gui::IGUITabControl *e = Environment->addTabControl(rect, this, - show_background, show_border, spec.fid); + gui::IGUITabControl *e = Environment->addTabControl(rect, + data->current_parent, show_background, show_border, spec.fid); e->setAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT); e->setTabHeight(geom.Y); @@ -2033,7 +2102,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen Environment->setFocus(e); } - auto style = getStyleForElement("tabheader", name); + auto style = getDefaultStyleForElement("tabheader", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true)); for (const std::string &button : buttons) { @@ -2059,7 +2128,6 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &element) { - if (m_client == 0) { warningstream << "invalid use of item_image_button with m_client==0" << std::endl; @@ -2118,10 +2186,12 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & 2 ); - GUIButtonItemImage *e_btn = GUIButtonItemImage::addButton(Environment, rect, this, spec_btn.fid, spec_btn.flabel.c_str(), item_name, m_client); + GUIButtonItemImage *e_btn = GUIButtonItemImage::addButton(Environment, + rect, m_tsrc, data->current_parent, spec_btn.fid, spec_btn.flabel.c_str(), + item_name, m_client); auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button"); - e_btn->setFromStyle(style, m_tsrc); + e_btn->setStyles(style); if (spec_btn.fname == data->focused_fieldname) { Environment->setFocus(e_btn); @@ -2175,9 +2245,10 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) core::rect<s32> rect(pos, pos + geom); - GUIBox *e = new GUIBox(Environment, this, spec.fid, rect, tmp_color); + GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, + rect, tmp_color); - auto style = getStyleForElement("box", spec.fname); + auto style = getDefaultStyleForElement("box", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); e->drop(); @@ -2327,7 +2398,7 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element) core::rect<s32> rect(pos, pos + geom); gui::IGUIElement *e = new gui::IGUIElement(EGUIET_ELEMENT, Environment, - this, fieldspec.fid, rect); + data->current_parent, fieldspec.fid, rect); // the element the rect tooltip is bound to should not block mouse-clicks e->setVisible(false); @@ -2469,6 +2540,7 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b StyleSpec spec; + // Parse properties for (size_t i = 1; i < parts.size(); i++) { size_t equal_pos = parts[i].find('='); if (equal_pos == std::string::npos) { @@ -2490,7 +2562,7 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b << "'" << std::endl; property_warned.insert(propname); } - return false; + continue; } spec.set(prop, value); @@ -2500,16 +2572,92 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b for (size_t sel = 0; sel < selectors.size(); sel++) { std::string selector = trim(selectors[sel]); - if (selector.empty()) { - errorstream << "Invalid style element (Empty selector): '" << element - << "'" << std::endl; + // Copy the style properties to a new StyleSpec + // This allows a separate state mask per-selector + StyleSpec selector_spec = spec; + + // Parse state information, if it exists + bool state_valid = true; + size_t state_pos = selector.find(':'); + if (state_pos != std::string::npos) { + std::string state_str = selector.substr(state_pos + 1); + selector = selector.substr(0, state_pos); + + if (state_str.empty()) { + errorstream << "Invalid style element (Invalid state): '" << element + << "'" << std::endl; + state_valid = false; + } else { + std::vector<std::string> states = split(state_str, '+'); + for (std::string &state : states) { + StyleSpec::State converted = StyleSpec::getStateByName(state); + if (converted == StyleSpec::STATE_INVALID) { + infostream << "Unknown style state " << state << + " in element '" << element << "'" << std::endl; + state_valid = false; + break; + } + + selector_spec.addState(converted); + } + } + } + + if (!state_valid) { + // Skip this selector continue; } if (style_type) { - theme_by_type[selector] |= spec; + theme_by_type[selector].push_back(selector_spec); } else { - theme_by_name[selector] |= spec; + theme_by_name[selector].push_back(selector_spec); + } + + // Backwards-compatibility for existing _hovered/_pressed properties + if (selector_spec.hasProperty(StyleSpec::BGCOLOR_HOVERED) + || selector_spec.hasProperty(StyleSpec::BGIMG_HOVERED) + || selector_spec.hasProperty(StyleSpec::FGIMG_HOVERED)) { + StyleSpec hover_spec; + hover_spec.addState(StyleSpec::STATE_HOVERED); + + if (selector_spec.hasProperty(StyleSpec::BGCOLOR_HOVERED)) { + hover_spec.set(StyleSpec::BGCOLOR, selector_spec.get(StyleSpec::BGCOLOR_HOVERED, "")); + } + if (selector_spec.hasProperty(StyleSpec::BGIMG_HOVERED)) { + hover_spec.set(StyleSpec::BGIMG, selector_spec.get(StyleSpec::BGIMG_HOVERED, "")); + } + if (selector_spec.hasProperty(StyleSpec::FGIMG_HOVERED)) { + hover_spec.set(StyleSpec::FGIMG, selector_spec.get(StyleSpec::FGIMG_HOVERED, "")); + } + + if (style_type) { + theme_by_type[selector].push_back(hover_spec); + } else { + theme_by_name[selector].push_back(hover_spec); + } + } + if (selector_spec.hasProperty(StyleSpec::BGCOLOR_PRESSED) + || selector_spec.hasProperty(StyleSpec::BGIMG_PRESSED) + || selector_spec.hasProperty(StyleSpec::FGIMG_PRESSED)) { + StyleSpec press_spec; + press_spec.addState(StyleSpec::STATE_PRESSED); + + if (selector_spec.hasProperty(StyleSpec::BGCOLOR_PRESSED)) { + press_spec.set(StyleSpec::BGCOLOR, selector_spec.get(StyleSpec::BGCOLOR_PRESSED, "")); + } + if (selector_spec.hasProperty(StyleSpec::BGIMG_PRESSED)) { + press_spec.set(StyleSpec::BGIMG, selector_spec.get(StyleSpec::BGIMG_PRESSED, "")); + } + if (selector_spec.hasProperty(StyleSpec::FGIMG_PRESSED)) { + press_spec.set(StyleSpec::FGIMG, selector_spec.get(StyleSpec::FGIMG_PRESSED, "")); + } + + if (style_type) { + theme_by_type[selector].push_back(press_spec); + } else { + theme_by_name[selector].push_back(press_spec); + } } } @@ -2525,24 +2673,12 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) if (parseVersionDirect(element)) return; - std::vector<std::string> parts = split(element,'['); - - // ugly workaround to keep compatibility - if (parts.size() > 2) { - if (trim(parts[0]) == "image") { - for (unsigned int i=2;i< parts.size(); i++) { - parts[1] += "[" + parts[i]; - } - } - else { return; } - } - - if (parts.size() < 2) { + size_t pos = element.find('['); + if (pos == std::string::npos) return; - } - std::string type = trim(parts[0]); - std::string description = trim(parts[1]); + std::string type = trim(element.substr(0, pos)); + std::string description = element.substr(pos+1); if (type == "container") { parseContainer(data, description); @@ -2709,6 +2845,16 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) return; } + if (type == "scroll_container") { + parseScrollContainer(data, description); + return; + } + + if (type == "scroll_container_end") { + parseScrollContainerEnd(data); + return; + } + // Ignore others infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\"" << std::endl; @@ -2765,8 +2911,10 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) tooltip_rect_it.first->drop(); for (auto &clickthrough_it : m_clickthrough_elements) clickthrough_it->drop(); + for (auto &scroll_container_it : m_scroll_containers) + scroll_container_it.second->drop(); - mydata.size= v2s32(100,100); + mydata.size = v2s32(100, 100); mydata.screensize = screensize; mydata.offset = v2f32(0.5f, 0.5f); mydata.anchor = v2f32(0.5f, 0.5f); @@ -2775,6 +2923,9 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) // Base position of contents of form mydata.basepos = getBasePos(); + // the parent for the parsed elements + mydata.current_parent = this; + m_inventorylists.clear(); m_backgrounds.clear(); m_tables.clear(); @@ -2785,6 +2936,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_tooltip_rects.clear(); m_inventory_rings.clear(); m_dropdowns.clear(); + m_scroll_containers.clear(); theme_by_name.clear(); theme_by_type.clear(); m_clickthrough_elements.clear(); @@ -3051,11 +3203,24 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) parseElement(&mydata, elements[i]); } - if (!container_stack.empty()) { + if (mydata.current_parent != this) { + errorstream << "Invalid formspec string: scroll_container was never closed!" + << std::endl; + } else if (!container_stack.empty()) { errorstream << "Invalid formspec string: container was never closed!" << std::endl; } + // get the scrollbar elements for scroll_containers + for (const std::pair<std::string, GUIScrollContainer *> &c : m_scroll_containers) { + for (const std::pair<FieldSpec, GUIScrollBar *> &b : m_scrollbars) { + if (c.first == b.first.fname) { + c.second->setScrollBar(b.second); + break; + } + } + } + // If there are fields without explicit size[], add a "Proceed" // button and adjust size to fit all the fields. if (mydata.simple_field_count > 0 && !mydata.explicit_size) { @@ -3080,7 +3245,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) size.X / 2 - 70 + 140, pos.Y + m_btn_height * 2 ); const wchar_t *text = wgettext("Proceed"); - GUIButton::addButton(Environment, mydata.rect, this, 257, text); + GUIButton::addButton(Environment, mydata.rect, m_tsrc, this, 257, text); delete[] text; } } @@ -3146,28 +3311,24 @@ bool GUIFormSpecMenu::getAndroidUIInput() if (!hasAndroidUIInput()) return false; + // still waiting + if (porting::getInputDialogState() == -1) + return true; + std::string fieldname = m_jni_field_name; m_jni_field_name.clear(); - for (std::vector<FieldSpec>::iterator iter = m_fields.begin(); - iter != m_fields.end(); ++iter) { - - if (iter->fname != fieldname) { + for (const FieldSpec &field : m_fields) { + if (field.fname != fieldname) continue; - } - IGUIElement *tochange = getElementFromId(iter->fid, true); - if (tochange == 0) { - return false; - } + IGUIElement *element = getElementFromId(field.fid, true); - if (tochange->getType() != irr::gui::EGUIET_EDIT_BOX) { + if (!element || element->getType() != irr::gui::EGUIET_EDIT_BOX) return false; - } std::string text = porting::getInputDialogValue(); - - ((gui::IGUIEditBox *)tochange)->setText(utf8_to_wide(text).c_str()); + ((gui::IGUIEditBox *)element)->setText(utf8_to_wide(text).c_str()); } return false; } @@ -3314,8 +3475,12 @@ void GUIFormSpecMenu::drawMenu() bool hovered_element_found = false; if (hovered != NULL) { - s32 id = hovered->getID(); + if (m_show_debug) { + core::rect<s32> rect = hovered->getAbsoluteClippingRect(); + driver->draw2DRectangle(0x22FFFF00, rect, &rect); + } + s32 id = hovered->getID(); u64 delta = 0; if (id == -1) { m_old_tooltip_id = id; @@ -3395,6 +3560,10 @@ void GUIFormSpecMenu::showTooltip(const std::wstring &text, tooltip_offset_y = 0; if (m_pointer.X > (s32)screenSize.X / 2) tooltip_offset_x = -(tooltip_offset_x + tooltip_width); + + // Hide tooltip after ETIE_LEFT_UP + if (m_pointer.X == 0) + return; #endif // Calculate and set the tooltip position @@ -3837,6 +4006,10 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) (kp == getKeySetting("keymap_screenshot"))) { m_client->makeScreenshot(); } + + if (event.KeyInput.PressedDown && kp == getKeySetting("keymap_toggle_debug")) + m_show_debug = !m_show_debug; + if (event.KeyInput.PressedDown && (event.KeyInput.Key==KEY_RETURN || event.KeyInput.Key==KEY_UP || @@ -4344,6 +4517,12 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } } + if (event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED) { + // move scroll_containers + for (const std::pair<std::string, GUIScrollContainer *> &c : m_scroll_containers) + c.second->onScrollEvent(event.GUIEvent.Caller); + } + if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) { if (event.GUIEvent.Caller->getID() > 257) { bool close_on_enter = true; @@ -4432,25 +4611,46 @@ std::wstring GUIFormSpecMenu::getLabelByID(s32 id) return L""; } -StyleSpec GUIFormSpecMenu::getStyleForElement(const std::string &type, +StyleSpec GUIFormSpecMenu::getDefaultStyleForElement(const std::string &type, const std::string &name, const std::string &parent_type) { - StyleSpec ret; + return getStyleForElement(type, name, parent_type)[StyleSpec::STATE_DEFAULT]; +} + +std::array<StyleSpec, StyleSpec::NUM_STATES> GUIFormSpecMenu::getStyleForElement( + const std::string &type, const std::string &name, const std::string &parent_type) +{ + std::array<StyleSpec, StyleSpec::NUM_STATES> ret; + + auto it = theme_by_type.find("*"); + if (it != theme_by_type.end()) { + for (const StyleSpec &spec : it->second) + ret[(u32)spec.getState()] |= spec; + } + + it = theme_by_name.find("*"); + if (it != theme_by_name.end()) { + for (const StyleSpec &spec : it->second) + ret[(u32)spec.getState()] |= spec; + } if (!parent_type.empty()) { - auto it = theme_by_type.find(parent_type); + it = theme_by_type.find(parent_type); if (it != theme_by_type.end()) { - ret |= it->second; + for (const StyleSpec &spec : it->second) + ret[(u32)spec.getState()] |= spec; } } - auto it = theme_by_type.find(type); + it = theme_by_type.find(type); if (it != theme_by_type.end()) { - ret |= it->second; + for (const StyleSpec &spec : it->second) + ret[(u32)spec.getState()] |= spec; } it = theme_by_name.find(name); if (it != theme_by_name.end()) { - ret |= it->second; + for (const StyleSpec &spec : it->second) + ret[(u32)spec.getState()] |= spec; } return ret; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 17bfef205..28088416d 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -39,6 +39,7 @@ class ISimpleTextureSource; class Client; class GUIScrollBar; class TexturePool; +class GUIScrollContainer; typedef enum { f_Button, @@ -274,11 +275,13 @@ protected: v2s32 getRealCoordinateBasePos(const std::vector<std::string> &v_pos); v2s32 getRealCoordinateGeometry(const std::vector<std::string> &v_geom); - std::unordered_map<std::string, StyleSpec> theme_by_type; - std::unordered_map<std::string, StyleSpec> theme_by_name; + std::unordered_map<std::string, std::vector<StyleSpec>> theme_by_type; + std::unordered_map<std::string, std::vector<StyleSpec>> theme_by_name; std::unordered_set<std::string> property_warned; - StyleSpec getStyleForElement(const std::string &type, + StyleSpec getDefaultStyleForElement(const std::string &type, + const std::string &name="", const std::string &parent_type=""); + std::array<StyleSpec, StyleSpec::NUM_STATES> getStyleForElement(const std::string &type, const std::string &name="", const std::string &parent_type=""); v2s32 padding; @@ -308,6 +311,7 @@ protected: std::vector<std::pair<FieldSpec, GUIScrollBar *>> m_scrollbars; std::vector<std::pair<FieldSpec, std::vector<std::string>>> m_dropdowns; std::vector<gui::IGUIElement *> m_clickthrough_elements; + std::vector<std::pair<std::string, GUIScrollContainer *>> m_scroll_containers; GUIInventoryList::ItemSpec *m_selected_item = nullptr; u16 m_selected_amount = 0; @@ -341,6 +345,7 @@ private: u16 m_formspec_version = 1; std::string m_focused_element = ""; JoystickController *m_joystick; + bool m_show_debug = false; typedef struct { bool explicit_size; @@ -356,6 +361,7 @@ private: std::string focused_fieldname; GUITable::TableOptions table_options; GUITable::TableColumns table_columns; + gui::IGUIElement *current_parent = nullptr; GUIInventoryList::Options inventorylist_options; @@ -388,6 +394,8 @@ private: void parseSize(parserData* data, const std::string &element); void parseContainer(parserData* data, const std::string &element); void parseContainerEnd(parserData* data); + void parseScrollContainer(parserData *data, const std::string &element); + void parseScrollContainerEnd(parserData *data); void parseList(parserData* data, const std::string &element); void parseListRing(parserData* data, const std::string &element); void parseCheckbox(parserData* data, const std::string &element); diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index e107b5a3e..88931cdf9 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -917,20 +917,20 @@ void TextDrawer::place(const core::rect<s32> &dest_rect) // Draw text in a rectangle with a given offset. Items are actually placed in // relative (to upper left corner) coordinates. -void TextDrawer::draw(const core::rect<s32> &dest_rect, +void TextDrawer::draw(const core::rect<s32> &clip_rect, const core::position2d<s32> &dest_offset) { irr::video::IVideoDriver *driver = m_environment->getVideoDriver(); - core::position2d<s32> offset = dest_rect.UpperLeftCorner + dest_offset; + core::position2d<s32> offset = dest_offset; offset.Y += m_voffset; if (m_text.background_type == ParsedText::BACKGROUND_COLOR) - driver->draw2DRectangle(m_text.background_color, dest_rect); + driver->draw2DRectangle(m_text.background_color, clip_rect); for (auto &p : m_text.m_paragraphs) { for (auto &el : p.elements) { core::rect<s32> rect(el.pos + offset, el.dim); - if (!rect.isRectCollided(dest_rect)) + if (!rect.isRectCollided(clip_rect)) continue; switch (el.type) { @@ -947,7 +947,7 @@ void TextDrawer::draw(const core::rect<s32> &dest_rect, if (el.type == ParsedText::ELEMENT_TEXT) el.font->draw(el.text, rect, color, false, true, - &dest_rect); + &clip_rect); if (el.underline && el.drawwidth) { s32 linepos = el.pos.Y + offset.Y + @@ -958,7 +958,7 @@ void TextDrawer::draw(const core::rect<s32> &dest_rect, el.pos.X + offset.X + el.drawwidth, linepos + (el.baseline >> 3)); - driver->draw2DRectangle(color, linerect, &dest_rect); + driver->draw2DRectangle(color, linerect, &clip_rect); } } break; @@ -972,7 +972,7 @@ void TextDrawer::draw(const core::rect<s32> &dest_rect, irr::core::rect<s32>( core::position2d<s32>(0, 0), texture->getOriginalSize()), - &dest_rect, 0, true); + &clip_rect, 0, true); } break; case ParsedText::ELEMENT_ITEM: { @@ -982,7 +982,7 @@ void TextDrawer::draw(const core::rect<s32> &dest_rect, drawItemStack( m_environment->getVideoDriver(), - g_fontengine->getFont(), item, rect, &dest_rect, + g_fontengine->getFont(), item, rect, &clip_rect, m_client, IT_ROT_OTHER, el.angle, el.rotation ); } break; @@ -1094,6 +1094,7 @@ bool GUIHyperText::OnEvent(const SEvent &event) m_text_scrollpos.Y = -m_vscrollbar->getPos(); m_drawer.draw(m_display_text_rect, m_text_scrollpos); checkHover(event.MouseInput.X, event.MouseInput.Y); + return true; } else if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { ParsedText::Element *element = getElementAt( @@ -1151,7 +1152,8 @@ void GUIHyperText::draw() m_vscrollbar->setPos(0); m_vscrollbar->setVisible(false); } - m_drawer.draw(m_display_text_rect, m_text_scrollpos); + m_drawer.draw(AbsoluteClippingRect, + m_display_text_rect.UpperLeftCorner + m_text_scrollpos); // draw children IGUIElement::draw(); diff --git a/src/gui/guiHyperText.h b/src/gui/guiHyperText.h index c55f8a705..5b936262e 100644 --- a/src/gui/guiHyperText.h +++ b/src/gui/guiHyperText.h @@ -174,7 +174,7 @@ public: void place(const core::rect<s32> &dest_rect); inline s32 getHeight() { return m_height; }; - void draw(const core::rect<s32> &dest_rect, + void draw(const core::rect<s32> &clip_rect, const core::position2d<s32> &dest_offset); ParsedText::Element *getElementAt(core::position2d<s32> pos); ParsedText::Tag *m_hovertag; diff --git a/src/gui/guiInventoryList.cpp b/src/gui/guiInventoryList.cpp index 536471229..dfdb60448 100644 --- a/src/gui/guiInventoryList.cpp +++ b/src/gui/guiInventoryList.cpp @@ -47,7 +47,8 @@ GUIInventoryList::GUIInventoryList(gui::IGUIEnvironment *env, m_fs_menu(fs_menu), m_options(options), m_font(font), - m_hovered_i(-1) + m_hovered_i(-1), + m_already_warned(false) { } @@ -58,20 +59,27 @@ void GUIInventoryList::draw() Inventory *inv = m_invmgr->getInventory(m_inventoryloc); if (!inv) { - warningstream << "GUIInventoryList::draw(): " - << "The inventory location " - << "\"" << m_inventoryloc.dump() << "\" doesn't exist anymore" - << std::endl; + if (!m_already_warned) { + warningstream << "GUIInventoryList::draw(): " + << "The inventory location " + << "\"" << m_inventoryloc.dump() << "\" doesn't exist" + << std::endl; + m_already_warned = true; + } return; } InventoryList *ilist = inv->getList(m_listname); if (!ilist) { - warningstream << "GUIInventoryList::draw(): " - << "The inventory list \"" << m_listname << "\" @ \"" - << m_inventoryloc.dump() << "\" doesn't exist anymore" - << std::endl; + if (!m_already_warned) { + warningstream << "GUIInventoryList::draw(): " + << "The inventory list \"" << m_listname << "\" @ \"" + << m_inventoryloc.dump() << "\" doesn't exist" + << std::endl; + m_already_warned = true; + } return; } + m_already_warned = false; video::IVideoDriver *driver = Environment->getVideoDriver(); Client *client = m_fs_menu->getClient(); @@ -80,9 +88,11 @@ void GUIInventoryList::draw() core::rect<s32> imgrect(0, 0, m_slot_size.X, m_slot_size.Y); v2s32 base_pos = AbsoluteRect.UpperLeftCorner; + const s32 list_size = (s32)ilist->getSize(); + for (s32 i = 0; i < m_geom.X * m_geom.Y; i++) { s32 item_i = i + m_start_item_i; - if (item_i >= (s32)ilist->getSize()) + if (item_i >= list_size) break; v2s32 p((i % m_geom.X) * m_slot_spacing.X, @@ -192,10 +202,19 @@ bool GUIInventoryList::OnEvent(const SEvent &event) s32 GUIInventoryList::getItemIndexAtPos(v2s32 p) const { + // no item if no gui element at pointer if (!IsVisible || AbsoluteClippingRect.getArea() <= 0 || !AbsoluteClippingRect.isPointInside(p)) return -1; + // there can not be an item if the inventory or the inventorylist does not exist + Inventory *inv = m_invmgr->getInventory(m_inventoryloc); + if (!inv) + return -1; + InventoryList *ilist = inv->getList(m_listname); + if (!ilist) + return -1; + core::rect<s32> imgrect(0, 0, m_slot_size.X, m_slot_size.Y); v2s32 base_pos = AbsoluteRect.UpperLeftCorner; @@ -210,7 +229,8 @@ s32 GUIInventoryList::getItemIndexAtPos(v2s32 p) const rect.clipAgainst(AbsoluteClippingRect); - if (rect.getArea() > 0 && rect.isPointInside(p)) + if (rect.getArea() > 0 && rect.isPointInside(p) && + i + m_start_item_i < (s32)ilist->getSize()) return i + m_start_item_i; return -1; diff --git a/src/gui/guiInventoryList.h b/src/gui/guiInventoryList.h index fd2c3601b..28e95fbbf 100644 --- a/src/gui/guiInventoryList.h +++ b/src/gui/guiInventoryList.h @@ -107,7 +107,7 @@ private: const InventoryLocation m_inventoryloc; const std::string m_listname; - // specifies the width and height of the inventorylist in itemslots + // the specified width and height of the shown inventorylist in itemslots const v2s32 m_geom; // the first item's index in inventory const s32 m_start_item_i; @@ -127,4 +127,7 @@ private: // the index of the hovered item; -1 if no item is hovered s32 m_hovered_i; + + // we do not want to write a warning on every draw + bool m_already_warned; }; diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp index 3f270fc7a..da0e25c23 100644 --- a/src/gui/guiKeyChangeMenu.cpp +++ b/src/gui/guiKeyChangeMenu.cpp @@ -82,8 +82,10 @@ enum }; GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env, - gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) : -GUIModalMenu(env, parent, id, menumgr) + gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, + ISimpleTextureSource *tsrc) : + GUIModalMenu(env, parent, id, menumgr), + m_tsrc(tsrc) { init_keys(); } @@ -157,7 +159,7 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) core::rect<s32> rect(0, 0, 100 * s, 30 * s); rect += topleft + v2s32(offset.X + 150 * s, offset.Y - 5 * s); const wchar_t *text = wgettext(k->key.name()); - k->button = GUIButton::addButton(Environment, rect, this, k->id, text); + k->button = GUIButton::addButton(Environment, rect, m_tsrc, this, k->id, text); delete[] text; } if ((i + 1) % KMaxButtonPerColumns == 0) { @@ -217,14 +219,14 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) core::rect<s32> rect(0, 0, 100 * s, 30 * s); rect += topleft + v2s32(size.X / 2 - 105 * s, size.Y - 40 * s); const wchar_t *text = wgettext("Save"); - GUIButton::addButton(Environment, rect, this, GUI_ID_BACK_BUTTON, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, GUI_ID_BACK_BUTTON, text); delete[] text; } { core::rect<s32> rect(0, 0, 100 * s, 30 * s); rect += topleft + v2s32(size.X / 2 + 5 * s, size.Y - 40 * s); const wchar_t *text = wgettext("Cancel"); - GUIButton::addButton(Environment, rect, this, GUI_ID_ABORT_BUTTON, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, GUI_ID_ABORT_BUTTON, text); delete[] text; } } diff --git a/src/gui/guiKeyChangeMenu.h b/src/gui/guiKeyChangeMenu.h index 528827fd9..1c0f40247 100644 --- a/src/gui/guiKeyChangeMenu.h +++ b/src/gui/guiKeyChangeMenu.h @@ -28,6 +28,8 @@ #include <string> #include <vector> +class ISimpleTextureSource; + struct key_setting { int id; @@ -41,7 +43,7 @@ class GUIKeyChangeMenu : public GUIModalMenu { public: GUIKeyChangeMenu(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, - IMenuManager *menumgr); + IMenuManager *menumgr, ISimpleTextureSource *tsrc); ~GUIKeyChangeMenu(); void removeChildren(); @@ -74,4 +76,5 @@ private: key_setting *active_key = nullptr; gui::IGUIStaticText *key_used_text = nullptr; std::vector<key_setting *> key_settings; + ISimpleTextureSource *m_tsrc; }; diff --git a/src/gui/guiPasswordChange.cpp b/src/gui/guiPasswordChange.cpp index af91ce84c..965a2d6f7 100644 --- a/src/gui/guiPasswordChange.cpp +++ b/src/gui/guiPasswordChange.cpp @@ -38,10 +38,12 @@ const int ID_cancel = 261; GUIPasswordChange::GUIPasswordChange(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, - Client* client + Client* client, + ISimpleTextureSource *tsrc ): GUIModalMenu(env, parent, id, menumgr), - m_client(client) + m_client(client), + m_tsrc(tsrc) { } @@ -146,14 +148,14 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize) core::rect<s32> rect(0, 0, 100 * s, 30 * s); rect = rect + v2s32(size.X / 4 + 56 * s, ypos); text = wgettext("Change"); - GUIButton::addButton(Environment, rect, this, ID_change, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, ID_change, text); delete[] text; } { core::rect<s32> rect(0, 0, 100 * s, 30 * s); rect = rect + v2s32(size.X / 4 + 185 * s, ypos); text = wgettext("Cancel"); - GUIButton::addButton(Environment, rect, this, ID_cancel, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, ID_cancel, text); delete[] text; } diff --git a/src/gui/guiPasswordChange.h b/src/gui/guiPasswordChange.h index 58541a399..7141100c0 100644 --- a/src/gui/guiPasswordChange.h +++ b/src/gui/guiPasswordChange.h @@ -23,12 +23,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include <string> class Client; +class ISimpleTextureSource; class GUIPasswordChange : public GUIModalMenu { public: GUIPasswordChange(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, - IMenuManager *menumgr, Client *client); + IMenuManager *menumgr, Client *client, + ISimpleTextureSource *tsrc); ~GUIPasswordChange(); void removeChildren(); @@ -57,4 +59,5 @@ private: std::wstring m_oldpass = L""; std::wstring m_newpass = L""; std::wstring m_newpass_confirm = L""; + ISimpleTextureSource *m_tsrc; }; diff --git a/src/gui/guiScrollContainer.cpp b/src/gui/guiScrollContainer.cpp new file mode 100644 index 000000000..88cdc7057 --- /dev/null +++ b/src/gui/guiScrollContainer.cpp @@ -0,0 +1,70 @@ +/* +Minetest +Copyright (C) 2020 DS + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "guiScrollContainer.h" + +GUIScrollContainer::GUIScrollContainer(gui::IGUIEnvironment *env, + gui::IGUIElement *parent, s32 id, const core::rect<s32> &rectangle, + const std::string &orientation, f32 scrollfactor) : + gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle), + m_scrollbar(nullptr), m_scrollfactor(scrollfactor) +{ + if (orientation == "vertical") + m_orientation = VERTICAL; + else if (orientation == "horizontal") + m_orientation = HORIZONTAL; + else + m_orientation = UNDEFINED; +} + +bool GUIScrollContainer::OnEvent(const SEvent &event) +{ + if (event.EventType == EET_MOUSE_INPUT_EVENT && + event.MouseInput.Event == EMIE_MOUSE_WHEEL && + !event.MouseInput.isLeftPressed() && m_scrollbar) { + Environment->setFocus(m_scrollbar); + bool retval = m_scrollbar->OnEvent(event); + + // a hacky fix for updating the hovering and co. + IGUIElement *hovered_elem = getElementFromPoint(core::position2d<s32>( + event.MouseInput.X, event.MouseInput.Y)); + SEvent mov_event = event; + mov_event.MouseInput.Event = EMIE_MOUSE_MOVED; + Environment->postEventFromUser(mov_event); + if (hovered_elem) + hovered_elem->OnEvent(mov_event); + + return retval; + } + + return IGUIElement::OnEvent(event); +} + +void GUIScrollContainer::updateScrolling() +{ + s32 pos = m_scrollbar->getPos(); + core::rect<s32> rect = getRelativePosition(); + + if (m_orientation == VERTICAL) + rect.UpperLeftCorner.Y = pos * m_scrollfactor; + else if (m_orientation == HORIZONTAL) + rect.UpperLeftCorner.X = pos * m_scrollfactor; + + setRelativePosition(rect); +} diff --git a/src/gui/guiScrollContainer.h b/src/gui/guiScrollContainer.h new file mode 100644 index 000000000..a0306291e --- /dev/null +++ b/src/gui/guiScrollContainer.h @@ -0,0 +1,60 @@ +/* +Minetest +Copyright (C) 2020 DS + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "irrlichttypes_extrabloated.h" +#include "util/string.h" +#include "guiScrollBar.h" + +class GUIScrollContainer : public gui::IGUIElement +{ +public: + GUIScrollContainer(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, + const core::rect<s32> &rectangle, const std::string &orientation, + f32 scrollfactor); + + virtual bool OnEvent(const SEvent &event) override; + + inline void onScrollEvent(gui::IGUIElement *caller) + { + if (caller == m_scrollbar) + updateScrolling(); + } + + inline void setScrollBar(GUIScrollBar *scrollbar) + { + m_scrollbar = scrollbar; + updateScrolling(); + } + +private: + enum OrientationEnum + { + VERTICAL, + HORIZONTAL, + UNDEFINED + }; + + GUIScrollBar *m_scrollbar; + OrientationEnum m_orientation; + f32 m_scrollfactor; + + void updateScrolling(); +}; diff --git a/src/gui/guiVolumeChange.cpp b/src/gui/guiVolumeChange.cpp index 9428cde83..07b11248c 100644 --- a/src/gui/guiVolumeChange.cpp +++ b/src/gui/guiVolumeChange.cpp @@ -38,9 +38,10 @@ const int ID_soundMuteButton = 266; GUIVolumeChange::GUIVolumeChange(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, - IMenuManager *menumgr + IMenuManager *menumgr, ISimpleTextureSource *tsrc ): - GUIModalMenu(env, parent, id, menumgr) + GUIModalMenu(env, parent, id, menumgr), + m_tsrc(tsrc) { } @@ -104,7 +105,7 @@ void GUIVolumeChange::regenerateGui(v2u32 screensize) core::rect<s32> rect(0, 0, 80 * s, 30 * s); rect = rect + v2s32(size.X / 2 - 80 * s / 2, size.Y / 2 + 55 * s); const wchar_t *text = wgettext("Exit"); - GUIButton::addButton(Environment, rect, this, ID_soundExitButton, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, ID_soundExitButton, text); delete[] text; } { diff --git a/src/gui/guiVolumeChange.h b/src/gui/guiVolumeChange.h index f4f4e9eea..466e17f9d 100644 --- a/src/gui/guiVolumeChange.h +++ b/src/gui/guiVolumeChange.h @@ -23,12 +23,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "modalMenu.h" #include <string> +class ISimpleTextureSource; + class GUIVolumeChange : public GUIModalMenu { public: GUIVolumeChange(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, - IMenuManager *menumgr); + IMenuManager *menumgr, ISimpleTextureSource *tsrc); ~GUIVolumeChange(); void removeChildren(); @@ -46,4 +48,7 @@ public: protected: std::wstring getLabelByID(s32 id) { return L""; } std::string getNameByID(s32 id) { return ""; } + +private: + ISimpleTextureSource *m_tsrc; }; diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index 8fb6c6f0f..a6fe7ebaf 100644 --- a/src/gui/modalMenu.cpp +++ b/src/gui/modalMenu.cpp @@ -28,8 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "touchscreengui.h" #endif -// clang-format off -GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, +GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, IMenuManager *menumgr) : IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, core::rect<s32>(0, 0, 100, 100)), @@ -153,8 +152,8 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) if (((gui::IGUIEditBox *)hovered)->isPasswordBox()) type = 3; - porting::showInputDialog(gettext("ok"), "", - wide_to_utf8(((gui::IGUIEditBox *)hovered)->getText()), type); + porting::showInputDialog(gettext("OK"), "", + wide_to_utf8(((gui::IGUIEditBox *)hovered)->getText()), type); return retval; } } @@ -167,18 +166,17 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) if (!root) { errorstream << "GUIModalMenu::preprocessEvent" - << " unable to get root element" << std::endl; + << " unable to get root element" << std::endl; return false; } - gui::IGUIElement *hovered = root->getElementFromPoint( - core::position2d<s32>(event.TouchInput.X, event.TouchInput.Y)); + gui::IGUIElement *hovered = + root->getElementFromPoint(core::position2d<s32>( + event.TouchInput.X, event.TouchInput.Y)); translated.MouseInput.X = event.TouchInput.X; translated.MouseInput.Y = event.TouchInput.Y; translated.MouseInput.Control = false; - bool dont_send_event = false; - if (event.TouchInput.touchedCount == 1) { switch (event.TouchInput.Event) { case ETIE_PRESSED_DOWN: @@ -205,11 +203,7 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) m_down_pos = v2s32(0, 0); break; default: - dont_send_event = true; - // this is not supposed to happen - errorstream << "GUIModalMenu::preprocessEvent" - << " unexpected usecase Event=" - << event.TouchInput.Event << std::endl; + break; } } else if ((event.TouchInput.touchedCount == 2) && (event.TouchInput.Event == ETIE_PRESSED_DOWN)) { @@ -219,50 +213,37 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) translated.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT; translated.MouseInput.X = m_pointer.X; translated.MouseInput.Y = m_pointer.Y; - if (hovered) { + if (hovered) hovered->OnEvent(translated); - } translated.MouseInput.Event = EMIE_RMOUSE_LEFT_UP; translated.MouseInput.ButtonStates = EMBSM_LEFT; - if (hovered) { + if (hovered) hovered->OnEvent(translated); - } - dont_send_event = true; - } - // ignore unhandled 2 touch events ... accidental moving for example - else if (event.TouchInput.touchedCount == 2) { - dont_send_event = true; - } - else if (event.TouchInput.touchedCount > 2) { - errorstream << "GUIModalMenu::preprocessEvent" - << " to many multitouch events " - << event.TouchInput.touchedCount << " ignoring them" - << std::endl; - } - if (dont_send_event) { + return true; + } else { + // ignore unhandled 2 touch events (accidental moving for example) return true; } // check if translated event needs to be preprocessed again - if (preprocessEvent(translated)) { + if (preprocessEvent(translated)) return true; - } + if (hovered) { grab(); bool retval = hovered->OnEvent(translated); - if (event.TouchInput.Event == ETIE_LEFT_UP) { + if (event.TouchInput.Event == ETIE_LEFT_UP) // reset pointer m_pointer = v2s32(0, 0); - } + drop(); return retval; } } - // clang-format on #endif return false; } @@ -271,14 +252,12 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) bool GUIModalMenu::hasAndroidUIInput() { // no dialog shown - if (m_jni_field_name.empty()) { + if (m_jni_field_name.empty()) return false; - } // still waiting - if (porting::getInputDialogState() == -1) { + if (porting::getInputDialogState() == -1) return true; - } // no value abort dialog processing if (porting::getInputDialogState() != 0) { diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index 94e331f72..0d64aa618 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -34,19 +34,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <ISceneCollisionManager.h> -// Very slow button repeat frequency (in seconds) -#define SLOW_BUTTON_REPEAT (1.0f) - using namespace irr::core; -const char **touchgui_button_imagenames = (const char *[]) { +const char **button_imagenames = (const char *[]) { "jump_btn.png", "down.png", "zoom.png", "aux_btn.png" }; -const char **touchgui_joystick_imagenames = (const char *[]) { +const char **joystick_imagenames = (const char *[]) { "joystick_off.png", "joystick_bg.png", "joystick_center.png" @@ -113,15 +110,17 @@ static irr::EKEY_CODE id2keycode(touch_gui_button_id id) case range_id: key = "rangeselect"; break; + default: + break; } - assert(key != ""); + assert(!key.empty()); return keyname_to_keycode(g_settings->get("keymap_" + key).c_str()); } TouchScreenGUI *g_touchscreengui; static void load_button_texture(button_info *btn, const char *path, - rect<s32> button_rect, ISimpleTextureSource *tsrc, video::IVideoDriver *driver) + const rect<s32> &button_rect, ISimpleTextureSource *tsrc, video::IVideoDriver *driver) { unsigned int tid; video::ITexture *texture = guiScalingImageButton(driver, @@ -141,7 +140,7 @@ static void load_button_texture(button_info *btn, const char *path, } btn->guibutton->setDrawBorder(false); btn->guibutton->setText(L""); - } + } } AutoHideButtonBar::AutoHideButtonBar(IrrlichtDevice *device, @@ -153,8 +152,8 @@ AutoHideButtonBar::AutoHideButtonBar(IrrlichtDevice *device, } void AutoHideButtonBar::init(ISimpleTextureSource *tsrc, - const char *starter_img, int button_id, v2s32 UpperLeft, - v2s32 LowerRight, autohide_button_bar_dir dir, float timeout) + const char *starter_img, int button_id, const v2s32 &UpperLeft, + const v2s32 &LowerRight, autohide_button_bar_dir dir, float timeout) { m_texturesource = tsrc; @@ -166,7 +165,7 @@ void AutoHideButtonBar::init(ISimpleTextureSource *tsrc, irr::core::rect<int> current_button = rect<s32>(UpperLeft.X, UpperLeft.Y, LowerRight.X, LowerRight.Y); - m_starter.guibutton = m_guienv->addButton(current_button, 0, button_id, L"", 0); + m_starter.guibutton = m_guienv->addButton(current_button, nullptr, button_id, L"", nullptr); m_starter.guibutton->grab(); m_starter.repeatcounter = -1; m_starter.keycode = KEY_OEM_8; // use invalid keycode as it's not relevant @@ -201,16 +200,14 @@ void AutoHideButtonBar::addButton(touch_gui_button_id button_id, } int button_size = 0; - if ((m_dir == AHBB_Dir_Top_Bottom) || (m_dir == AHBB_Dir_Bottom_Top)) { + if ((m_dir == AHBB_Dir_Top_Bottom) || (m_dir == AHBB_Dir_Bottom_Top)) button_size = m_lower_right.X - m_upper_left.X; - } else { + else button_size = m_lower_right.Y - m_upper_left.Y; - } irr::core::rect<int> current_button; if ((m_dir == AHBB_Dir_Right_Left) || (m_dir == AHBB_Dir_Left_Right)) { - int x_start = 0; int x_end = 0; @@ -227,8 +224,8 @@ void AutoHideButtonBar::addButton(touch_gui_button_id button_id, current_button = rect<s32>(x_start, m_upper_left.Y, x_end, m_lower_right.Y); } else { - int y_start = 0; - int y_end = 0; + double y_start = 0; + double y_end = 0; if (m_dir == AHBB_Dir_Top_Bottom) { y_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size()) @@ -240,12 +237,13 @@ void AutoHideButtonBar::addButton(touch_gui_button_id button_id, y_start = y_end - button_size; } - current_button = rect<s32>(m_upper_left.X, y_start, m_lower_right.Y, - y_end); + current_button = rect<s32>(m_upper_left.X, y_start, + m_lower_right.Y, y_end); } - button_info *btn = new button_info(); - btn->guibutton = m_guienv->addButton(current_button, 0, button_id, caption, 0); + auto *btn = new button_info(); + btn->guibutton = m_guienv->addButton(current_button, + nullptr, button_id, caption, nullptr); btn->guibutton->grab(); btn->guibutton->setVisible(false); btn->guibutton->setEnabled(false); @@ -275,26 +273,23 @@ bool AutoHideButtonBar::isButton(const SEvent &event) { IGUIElement *rootguielement = m_guienv->getRootGUIElement(); - if (rootguielement == NULL) { + if (rootguielement == nullptr) return false; - } gui::IGUIElement *element = rootguielement->getElementFromPoint( core::position2d<s32>(event.TouchInput.X, event.TouchInput.Y)); - if (element == NULL) { + if (element == nullptr) return false; - } if (m_active) { // check for all buttons in vector - - std::vector<button_info *>::iterator iter = m_buttons.begin(); + auto iter = m_buttons.begin(); while (iter != m_buttons.end()) { if ((*iter)->guibutton == element) { - SEvent *translated = new SEvent(); + auto *translated = new SEvent(); memset(translated, 0, sizeof(SEvent)); translated->EventType = irr::EET_KEY_INPUT_EVENT; translated->KeyInput.Key = (*iter)->keycode; @@ -341,7 +336,7 @@ bool AutoHideButtonBar::isButton(const SEvent &event) m_active = true; m_timeout = 0; - std::vector<button_info*>::iterator iter = m_buttons.begin(); + auto iter = m_buttons.begin(); while (iter != m_buttons.end()) { (*iter)->guibutton->setVisible(true); @@ -355,41 +350,13 @@ bool AutoHideButtonBar::isButton(const SEvent &event) return false; } -bool AutoHideButtonBar::isReleaseButton(int eventID) -{ - std::vector<int>::iterator id = std::find(m_starter.ids.begin(), - m_starter.ids.end(), eventID); - - if (id != m_starter.ids.end()) { - m_starter.ids.erase(id); - return true; - } - - std::vector<button_info *>::iterator iter = m_buttons.begin(); - - while (iter != m_buttons.end()) { - std::vector<int>::iterator id = std::find((*iter)->ids.begin(), - (*iter)->ids.end(), eventID); - - if (id != (*iter)->ids.end()) { - (*iter)->ids.erase(id); - // TODO handle settings button release - return true; - } - ++iter; - } - - return false; -} - void AutoHideButtonBar::step(float dtime) { if (m_active) { m_timeout += dtime; - if (m_timeout > m_timeout_value) { + if (m_timeout > m_timeout_value) deactivate(); - } } } @@ -401,11 +368,11 @@ void AutoHideButtonBar::deactivate() } m_active = false; - std::vector<button_info *>::iterator iter = m_buttons.begin(); + auto iter = m_buttons.begin(); while (iter != m_buttons.end()) { - (*iter)->guibutton->setVisible(false); - (*iter)->guibutton->setEnabled(false); + (*iter)->guibutton->setVisible(false); + (*iter)->guibutton->setEnabled(false); ++iter; } } @@ -416,7 +383,7 @@ void AutoHideButtonBar::hide() m_starter.guibutton->setVisible(false); m_starter.guibutton->setEnabled(false); - std::vector<button_info *>::iterator iter = m_buttons.begin(); + auto iter = m_buttons.begin(); while (iter != m_buttons.end()) { (*iter)->guibutton->setVisible(false); @@ -430,7 +397,7 @@ void AutoHideButtonBar::show() m_visible = true; if (m_active) { - std::vector<button_info *>::iterator iter = m_buttons.begin(); + auto iter = m_buttons.begin(); while (iter != m_buttons.end()) { (*iter)->guibutton->setVisible(true); @@ -450,24 +417,26 @@ TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver *receiver) m_settingsbar(device, receiver), m_rarecontrolsbar(device, receiver) { - for (unsigned int i=0; i < after_last_element_id; i++) { - m_buttons[i].guibutton = 0; - m_buttons[i].repeatcounter = -1; - m_buttons[i].repeatdelay = BUTTON_REPEAT_DELAY; + for (auto &button : m_buttons) { + button.guibutton = nullptr; + button.repeatcounter = -1; + button.repeatdelay = BUTTON_REPEAT_DELAY; } m_touchscreen_threshold = g_settings->getU16("touchscreen_threshold"); m_fixed_joystick = g_settings->getBool("fixed_virtual_joystick"); m_joystick_triggers_special1 = g_settings->getBool("virtual_joystick_triggers_aux"); m_screensize = m_device->getVideoDriver()->getScreenSize(); + button_size = MYMIN(m_screensize.Y / 4.5f, + porting::getDisplayDensity() * + g_settings->getFloat("hud_scaling") * 65.0f); } -void TouchScreenGUI::initButton(touch_gui_button_id id, rect<s32> button_rect, - std::wstring caption, bool immediate_release, float repeat_delay) +void TouchScreenGUI::initButton(touch_gui_button_id id, const rect<s32> &button_rect, + const std::wstring &caption, bool immediate_release, float repeat_delay) { - button_info *btn = &m_buttons[id]; - btn->guibutton = m_guienv->addButton(button_rect, 0, id, caption.c_str()); + btn->guibutton = m_guienv->addButton(button_rect, nullptr, id, caption.c_str()); btn->guibutton->grab(); btn->repeatcounter = -1; btn->repeatdelay = repeat_delay; @@ -475,42 +444,29 @@ void TouchScreenGUI::initButton(touch_gui_button_id id, rect<s32> button_rect, btn->immediate_release = immediate_release; btn->ids.clear(); - load_button_texture(btn, touchgui_button_imagenames[id], button_rect, + load_button_texture(btn, button_imagenames[id], button_rect, m_texturesource, m_device->getVideoDriver()); } -button_info *TouchScreenGUI::initJoystickButton(touch_gui_button_id id, rect<s32> button_rect, - int texture_id, bool visible) +button_info *TouchScreenGUI::initJoystickButton(touch_gui_button_id id, + const rect<s32> &button_rect, int texture_id, bool visible) { - button_info *btn = new button_info(); - btn->guibutton = m_guienv->addButton(button_rect, 0, id, L"O"); + auto *btn = new button_info(); + btn->guibutton = m_guienv->addButton(button_rect, nullptr, id, L"O"); btn->guibutton->setVisible(visible); btn->guibutton->grab(); btn->ids.clear(); - load_button_texture(btn, touchgui_joystick_imagenames[texture_id], button_rect, - m_texturesource, m_device->getVideoDriver()); + load_button_texture(btn, joystick_imagenames[texture_id], + button_rect, m_texturesource, m_device->getVideoDriver()); return btn; } -static int getMaxControlPadSize(float density) { - return 200 * density * g_settings->getFloat("hud_scaling"); -} - -int TouchScreenGUI::getGuiButtonSize() -{ - u32 control_pad_size = MYMIN((2 * m_screensize.Y) / 3, - getMaxControlPadSize(porting::getDisplayDensity())); - - return control_pad_size / 3; -} - void TouchScreenGUI::init(ISimpleTextureSource *tsrc) { assert(tsrc); - u32 button_size = getGuiButtonSize(); m_visible = true; m_texturesource = tsrc; @@ -608,35 +564,31 @@ void TouchScreenGUI::init(ISimpleTextureSource *tsrc) m_rarecontrolsbar.addButton(chat_id, L"Chat", "chat_btn.png"); m_rarecontrolsbar.addButton(inventory_id, L"inv", "inventory_btn.png"); m_rarecontrolsbar.addButton(drop_id, L"drop", "drop_btn.png"); - } touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y) { IGUIElement *rootguielement = m_guienv->getRootGUIElement(); - if (rootguielement != NULL) { + if (rootguielement != nullptr) { gui::IGUIElement *element = rootguielement->getElementFromPoint(core::position2d<s32>(x, y)); - if (element) { - for (unsigned int i=0; i < after_last_element_id; i++) { - if (element == m_buttons[i].guibutton) { + if (element) + for (unsigned int i = 0; i < after_last_element_id; i++) + if (element == m_buttons[i].guibutton) return (touch_gui_button_id) i; - } - } - } } + return after_last_element_id; } -touch_gui_button_id TouchScreenGUI::getButtonID(int eventID) +touch_gui_button_id TouchScreenGUI::getButtonID(size_t eventID) { - for (unsigned int i=0; i < after_last_element_id; i++) { + for (unsigned int i = 0; i < after_last_element_id; i++) { button_info *btn = &m_buttons[i]; - std::vector<int>::iterator id = - std::find(btn->ids.begin(), btn->ids.end(), eventID); + auto id = std::find(btn->ids.begin(), btn->ids.end(), eventID); if (id != btn->ids.end()) return (touch_gui_button_id) i; @@ -648,55 +600,30 @@ touch_gui_button_id TouchScreenGUI::getButtonID(int eventID) bool TouchScreenGUI::isHUDButton(const SEvent &event) { // check if hud item is pressed - for (std::map<int, rect<s32> >::iterator iter = m_hud_rects.begin(); - iter != m_hud_rects.end(); ++iter) { - if (iter->second.isPointInside( - v2s32(event.TouchInput.X, - event.TouchInput.Y) - )) { - if ( iter->first < 8) { - SEvent *translated = new SEvent(); - memset(translated, 0, sizeof(SEvent)); - translated->EventType = irr::EET_KEY_INPUT_EVENT; - translated->KeyInput.Key = (irr::EKEY_CODE) (KEY_KEY_1 + iter->first); - translated->KeyInput.Control = false; - translated->KeyInput.Shift = false; - translated->KeyInput.PressedDown = true; - m_receiver->OnEvent(*translated); - m_hud_ids[event.TouchInput.ID] = translated->KeyInput.Key; - delete translated; - return true; - } + for (auto &hud_rect : m_hud_rects) { + if (hud_rect.second.isPointInside(v2s32(event.TouchInput.X, + event.TouchInput.Y))) { + auto *translated = new SEvent(); + memset(translated, 0, sizeof(SEvent)); + translated->EventType = irr::EET_KEY_INPUT_EVENT; + translated->KeyInput.Key = (irr::EKEY_CODE) (KEY_KEY_1 + hud_rect.first); + translated->KeyInput.Control = false; + translated->KeyInput.Shift = false; + translated->KeyInput.PressedDown = true; + m_receiver->OnEvent(*translated); + m_hud_ids[event.TouchInput.ID] = translated->KeyInput.Key; + delete translated; + return true; } } return false; } -bool TouchScreenGUI::isReleaseHUDButton(int eventID) -{ - std::map<int, irr::EKEY_CODE>::iterator iter = m_hud_ids.find(eventID); - - if (iter != m_hud_ids.end()) { - SEvent *translated = new SEvent(); - memset(translated, 0, sizeof(SEvent)); - translated->EventType = irr::EET_KEY_INPUT_EVENT; - translated->KeyInput.Key = iter->second; - translated->KeyInput.PressedDown = false; - translated->KeyInput.Control = false; - translated->KeyInput.Shift = false; - m_receiver->OnEvent(*translated); - m_hud_ids.erase(iter); - delete translated; - return true; - } - return false; -} - void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button, - int eventID, bool action) + size_t eventID, bool action) { button_info *btn = &m_buttons[button]; - SEvent *translated = new SEvent(); + auto *translated = new SEvent(); memset(translated, 0, sizeof(SEvent)); translated->EventType = irr::EET_KEY_INPUT_EVENT; translated->KeyInput.Key = btn->keycode; @@ -717,16 +644,16 @@ void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button, translated->KeyInput.Key = btn->keycode; m_receiver->OnEvent(*translated); } + // remove event if ((!action) || (btn->immediate_release)) { - - std::vector<int>::iterator pos = - std::find(btn->ids.begin(), btn->ids.end(), eventID); + auto pos = std::find(btn->ids.begin(), btn->ids.end(), eventID); // has to be in touch list assert(pos != btn->ids.end()); btn->ids.erase(pos); - if (btn->ids.size() > 0) { return; } + if (!btn->ids.empty()) + return; translated->KeyInput.PressedDown = false; btn->repeatcounter = -1; @@ -735,30 +662,21 @@ void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button, delete translated; } - -void TouchScreenGUI::handleReleaseEvent(int evt_id) +void TouchScreenGUI::handleReleaseEvent(size_t evt_id) { touch_gui_button_id button = getButtonID(evt_id); - // handle button events + if (button != after_last_element_id) { + // handle button events handleButtonEvent(button, evt_id, false); - } - // handle hud button events - else if (isReleaseHUDButton(evt_id)) { - // nothing to do here - } else if (m_settingsbar.isReleaseButton(evt_id)) { - // nothing to do here - } else if (m_rarecontrolsbar.isReleaseButton(evt_id)) { - // nothing to do here - } - // handle the point used for moving view - else if (evt_id == m_move_id) { + } else if (evt_id == m_move_id) { + // handle the point used for moving view m_move_id = -1; // if this pointer issued a mouse event issue symmetric release here if (m_move_sent_as_mouse_event) { - SEvent *translated = new SEvent; + auto *translated = new SEvent; memset(translated, 0, sizeof(SEvent)); translated->EventType = EET_MOUSE_INPUT_EVENT; translated->MouseInput.X = m_move_downlocation.X; @@ -769,32 +687,31 @@ void TouchScreenGUI::handleReleaseEvent(int evt_id) translated->MouseInput.Event = EMIE_LMOUSE_LEFT_UP; m_receiver->OnEvent(*translated); delete translated; - } - else { + } else { // do double tap detection doubleTapDetection(); } } + // handle joystick else if (evt_id == m_joystick_id) { m_joystick_id = -1; // reset joystick - for (unsigned int i = 0; i < 4; i ++) + for (unsigned int i = 0; i < 4; i++) m_joystick_status[i] = false; applyJoystickStatus(); m_joystick_btn_off->guibutton->setVisible(true); m_joystick_btn_bg->guibutton->setVisible(false); m_joystick_btn_center->guibutton->setVisible(false); - } - else { + } else { infostream << "TouchScreenGUI::translateEvent released unknown button: " << evt_id << std::endl; } - for (std::vector<id_status>::iterator iter = m_known_ids.begin(); + for (auto iter = m_known_ids.begin(); iter != m_known_ids.end(); ++iter) { if (iter->id == evt_id) { m_known_ids.erase(iter); @@ -806,27 +723,28 @@ void TouchScreenGUI::handleReleaseEvent(int evt_id) void TouchScreenGUI::translateEvent(const SEvent &event) { if (!m_visible) { - infostream << "TouchScreenGUI::translateEvent got event but not visible?!" << std::endl; + infostream + << "TouchScreenGUI::translateEvent got event but not visible!" + << std::endl; return; } - if (event.EventType != EET_TOUCH_INPUT_EVENT) { + if (event.EventType != EET_TOUCH_INPUT_EVENT) return; - } if (event.TouchInput.Event == ETIE_PRESSED_DOWN) { - - /* add to own copy of eventlist ... - * android would provide this information but irrlicht guys don't + /* + * Add to own copy of event list... + * android would provide this information but Irrlicht guys don't * wanna design a efficient interface */ - id_status toadd; + id_status toadd{}; toadd.id = event.TouchInput.ID; toadd.X = event.TouchInput.X; toadd.Y = event.TouchInput.Y; m_known_ids.push_back(toadd); - int eventID = event.TouchInput.ID; + size_t eventID = event.TouchInput.ID; touch_gui_button_id button = getButtonID(event.TouchInput.X, event.TouchInput.Y); @@ -846,21 +764,19 @@ void TouchScreenGUI::translateEvent(const SEvent &event) } else if (m_rarecontrolsbar.isButton(event)) { m_settingsbar.deactivate(); // already handled in isSettingsBarButton() - } - // handle non button events - else { + } else { + // handle non button events m_settingsbar.deactivate(); m_rarecontrolsbar.deactivate(); - u32 button_size = getGuiButtonSize(); - s32 dxj = event.TouchInput.X - button_size * 5 / 2; - s32 dyj = event.TouchInput.Y - m_screensize.Y + button_size * 5 / 2; + s32 dxj = event.TouchInput.X - button_size * 5.0f / 2.0f; + s32 dyj = event.TouchInput.Y - m_screensize.Y + button_size * 5.0f / 2.0f; /* Select joystick when left 1/3 of screen dragged or * when joystick tapped (fixed joystick position) */ if ((m_fixed_joystick && dxj * dxj + dyj * dyj <= button_size * button_size * 1.5 * 1.5) || - (!m_fixed_joystick && event.TouchInput.X < m_screensize.X / 3)) { + (!m_fixed_joystick && event.TouchInput.X < m_screensize.X / 3.0f)) { // If we don't already have a starting point for joystick make this the one. if (m_joystick_id == -1) { m_joystick_id = event.TouchInput.ID; @@ -871,14 +787,14 @@ void TouchScreenGUI::translateEvent(const SEvent &event) m_joystick_btn_center->guibutton->setVisible(true); // If it's a fixed joystick, don't move the joystick "button". - if (!m_fixed_joystick) { + if (!m_fixed_joystick) m_joystick_btn_bg->guibutton->setRelativePosition(v2s32( - event.TouchInput.X - button_size * 3 / 2, - event.TouchInput.Y - button_size * 3 / 2)); - } + event.TouchInput.X - button_size * 3.0f / 2.0f, + event.TouchInput.Y - button_size * 3.0f / 2.0f)); + m_joystick_btn_center->guibutton->setRelativePosition(v2s32( - event.TouchInput.X - button_size / 2, - event.TouchInput.Y - button_size / 2)); + event.TouchInput.X - button_size / 2.0f, + event.TouchInput.Y - button_size / 2.0f)); } } else { // If we don't already have a moving point make this the moving one. @@ -895,17 +811,15 @@ void TouchScreenGUI::translateEvent(const SEvent &event) m_pointerpos[event.TouchInput.ID] = v2s32(event.TouchInput.X, event.TouchInput.Y); } else if (event.TouchInput.Event == ETIE_LEFT_UP) { - verbosestream << "Up event for pointerid: " << event.TouchInput.ID << std::endl; + verbosestream + << "Up event for pointerid: " << event.TouchInput.ID << std::endl; handleReleaseEvent(event.TouchInput.ID); - } - else { + } else { assert(event.TouchInput.Event == ETIE_MOVED); - int move_idx = event.TouchInput.ID; if (m_pointerpos[event.TouchInput.ID] == - v2s32(event.TouchInput.X, event.TouchInput.Y)) { + v2s32(event.TouchInput.X, event.TouchInput.Y)) return; - } if (m_move_id != -1) { if ((event.TouchInput.ID == m_move_id) && @@ -928,9 +842,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) s32 dy = Y - m_pointerpos[event.TouchInput.ID].Y; // adapt to similar behaviour as pc screen - double d = g_settings->getFloat("mouse_sensitivity") * 4; - double old_yaw = m_camera_yaw_change; - double old_pitch = m_camera_pitch; + double d = g_settings->getFloat("mouse_sensitivity") * 3.0f; m_camera_yaw_change -= dx * d; m_camera_pitch = MYMIN(MYMAX(m_camera_pitch + (dy * d), -180), 180); @@ -942,8 +854,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) ->getRayFromScreenCoordinates(v2s32(X, Y)); m_pointerpos[event.TouchInput.ID] = v2s32(X, Y); } - } - else if ((event.TouchInput.ID == m_move_id) && + } else if ((event.TouchInput.ID == m_move_id) && (m_move_sent_as_mouse_event)) { m_shootline = m_device ->getSceneManager() @@ -954,7 +865,6 @@ void TouchScreenGUI::translateEvent(const SEvent &event) } if (m_joystick_id != -1 && event.TouchInput.ID == m_joystick_id) { - u32 button_size = getGuiButtonSize(); s32 X = event.TouchInput.X; s32 Y = event.TouchInput.Y; @@ -967,8 +877,8 @@ void TouchScreenGUI::translateEvent(const SEvent &event) double distance_sq = dx * dx + dy * dy; - s32 dxj = event.TouchInput.X - button_size * 5 / 2; - s32 dyj = event.TouchInput.Y - m_screensize.Y + button_size * 5 / 2; + s32 dxj = event.TouchInput.X - button_size * 5.0f / 2.0f; + s32 dyj = event.TouchInput.Y - m_screensize.Y + button_size * 5.0f / 2.0f; bool inside_joystick = (dxj * dxj + dyj * dyj <= button_size * button_size * 1.5 * 1.5); if (m_joystick_has_really_moved || @@ -986,8 +896,8 @@ void TouchScreenGUI::translateEvent(const SEvent &event) angle = fmod(angle + 180 + 22.5, 360); // reset state before applying - for (unsigned int i = 0; i < 5; i ++) - m_joystick_status[i] = false; + for (bool & joystick_status : m_joystick_status) + joystick_status = false; if (distance <= m_touchscreen_threshold) { // do nothing @@ -1016,8 +926,8 @@ void TouchScreenGUI::translateEvent(const SEvent &event) if (distance > button_size) { m_joystick_status[j_special1] = true; // move joystick "button" - s32 ndx = (s32) button_size * dx / distance - (s32) button_size / 2; - s32 ndy = (s32) button_size * dy / distance - (s32) button_size / 2; + s32 ndx = button_size * dx / distance - button_size / 2.0f; + s32 ndy = button_size * dy / distance - button_size / 2.0f; if (m_fixed_joystick) { m_joystick_btn_center->guibutton->setRelativePosition(v2s32( button_size * 5 / 2 + ndx, @@ -1028,64 +938,54 @@ void TouchScreenGUI::translateEvent(const SEvent &event) m_pointerpos[event.TouchInput.ID].Y + ndy)); } } else { - m_joystick_btn_center->guibutton->setRelativePosition(v2s32( - X - button_size / 2, Y - button_size / 2)); + m_joystick_btn_center->guibutton->setRelativePosition( + v2s32(X - button_size / 2, Y - button_size / 2)); } } } - if (m_move_id == -1 && m_joystick_id == -1) { + if (m_move_id == -1 && m_joystick_id == -1) handleChangedButton(event); - } } } void TouchScreenGUI::handleChangedButton(const SEvent &event) { for (unsigned int i = 0; i < after_last_element_id; i++) { - - if (m_buttons[i].ids.empty()) { + if (m_buttons[i].ids.empty()) continue; - } - for (std::vector<int>::iterator iter = m_buttons[i].ids.begin(); - iter != m_buttons[i].ids.end(); ++iter) { + for (auto iter = m_buttons[i].ids.begin(); + iter != m_buttons[i].ids.end(); ++iter) { if (event.TouchInput.ID == *iter) { - int current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y); - if (current_button_id == i) { + if (current_button_id == i) continue; - } // remove old button handleButtonEvent((touch_gui_button_id) i, *iter, false); - if (current_button_id == after_last_element_id) { + if (current_button_id == after_last_element_id) return; - } + handleButtonEvent((touch_gui_button_id) current_button_id, *iter, true); return; - } } } int current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y); - if (current_button_id == after_last_element_id) { + if (current_button_id == after_last_element_id) return; - } button_info *btn = &m_buttons[current_button_id]; if (std::find(btn->ids.begin(), btn->ids.end(), event.TouchInput.ID) == btn->ids.end()) - { handleButtonEvent((touch_gui_button_id) current_button_id, event.TouchInput.ID, true); - } - } bool TouchScreenGUI::doubleTapDetection() @@ -1102,14 +1002,15 @@ bool TouchScreenGUI::doubleTapDetection() return false; double distance = sqrt( - (m_key_events[0].x - m_key_events[1].x) * (m_key_events[0].x - m_key_events[1].x) + - (m_key_events[0].y - m_key_events[1].y) * (m_key_events[0].y - m_key_events[1].y)); - + (m_key_events[0].x - m_key_events[1].x) * + (m_key_events[0].x - m_key_events[1].x) + + (m_key_events[0].y - m_key_events[1].y) * + (m_key_events[0].y - m_key_events[1].y)); if (distance > (20 + m_touchscreen_threshold)) return false; - SEvent *translated = new SEvent(); + auto *translated = new SEvent(); memset(translated, 0, sizeof(SEvent)); translated->EventType = EET_MOUSE_INPUT_EVENT; translated->MouseInput.X = m_key_events[0].x; @@ -1129,17 +1030,16 @@ bool TouchScreenGUI::doubleTapDetection() m_receiver->OnEvent(*translated); translated->MouseInput.ButtonStates = 0; - translated->MouseInput.Event = EMIE_RMOUSE_LEFT_UP; + translated->MouseInput.Event = EMIE_RMOUSE_LEFT_UP; verbosestream << "TouchScreenGUI::translateEvent right click release" << std::endl; m_receiver->OnEvent(*translated); delete translated; return true; - } void TouchScreenGUI::applyJoystickStatus() { - for (unsigned int i = 0; i < 5; i ++) { + for (unsigned int i = 0; i < 5; i++) { if (i == 4 && !m_joystick_triggers_special1) continue; @@ -1158,50 +1058,48 @@ void TouchScreenGUI::applyJoystickStatus() TouchScreenGUI::~TouchScreenGUI() { - for (unsigned int i = 0; i < after_last_element_id; i++) { - button_info *btn = &m_buttons[i]; - if (btn->guibutton) { - btn->guibutton->drop(); - btn->guibutton = NULL; + for (auto &button : m_buttons) { + if (button.guibutton) { + button.guibutton->drop(); + button.guibutton = nullptr; } } if (m_joystick_btn_off->guibutton) { m_joystick_btn_off->guibutton->drop(); - m_joystick_btn_off->guibutton = NULL; + m_joystick_btn_off->guibutton = nullptr; } if (m_joystick_btn_bg->guibutton) { m_joystick_btn_bg->guibutton->drop(); - m_joystick_btn_bg->guibutton = NULL; + m_joystick_btn_bg->guibutton = nullptr; } if (m_joystick_btn_center->guibutton) { m_joystick_btn_center->guibutton->drop(); - m_joystick_btn_center->guibutton = NULL; + m_joystick_btn_center->guibutton = nullptr; } } void TouchScreenGUI::step(float dtime) { // simulate keyboard repeats - for (unsigned int i = 0; i < after_last_element_id; i++) { - button_info *btn = &m_buttons[i]; - - if (btn->ids.size() > 0) { - btn->repeatcounter += dtime; + for (auto &button : m_buttons) { + if (!button.ids.empty()) { + button.repeatcounter += dtime; // in case we're moving around digging does not happen if (m_move_id != -1) m_move_has_really_moved = true; - if (btn->repeatcounter < btn->repeatdelay) continue; + if (button.repeatcounter < button.repeatdelay) + continue; - btn->repeatcounter = 0; + button.repeatcounter = 0; SEvent translated; memset(&translated, 0, sizeof(SEvent)); translated.EventType = irr::EET_KEY_INPUT_EVENT; - translated.KeyInput.Key = btn->keycode; + translated.KeyInput.Key = button.keycode; translated.KeyInput.PressedDown = false; m_receiver->OnEvent(translated); @@ -1211,7 +1109,12 @@ void TouchScreenGUI::step(float dtime) } // joystick - applyJoystickStatus(); + for (unsigned int i = 0; i < 4; i++) { + if (m_joystick_status[i]) { + applyJoystickStatus(); + break; + } + } // if a new placed pointer isn't moved for some time start digging if ((m_move_id != -1) && @@ -1259,22 +1162,18 @@ void TouchScreenGUI::registerHudItem(int index, const rect<s32> &rect) void TouchScreenGUI::Toggle(bool visible) { m_visible = visible; - for (unsigned int i = 0; i < after_last_element_id; i++) { - button_info *btn = &m_buttons[i]; - if (btn->guibutton) { - btn->guibutton->setVisible(visible); - } + for (auto &button : m_buttons) { + if (button.guibutton) + button.guibutton->setVisible(visible); } - if (m_joystick_btn_off->guibutton) { + if (m_joystick_btn_off->guibutton) m_joystick_btn_off->guibutton->setVisible(visible); - } // clear all active buttons if (!visible) { - while (m_known_ids.size() > 0) { + while (!m_known_ids.empty()) handleReleaseEvent(m_known_ids.begin()->id); - } m_settingsbar.hide(); m_rarecontrolsbar.hide(); diff --git a/src/gui/touchscreengui.h b/src/gui/touchscreengui.h index 2a3f24a3f..761d33207 100644 --- a/src/gui/touchscreengui.h +++ b/src/gui/touchscreengui.h @@ -80,21 +80,22 @@ typedef enum } autohide_button_bar_dir; #define MIN_DIG_TIME_MS 500 -#define MAX_TOUCH_COUNT 64 #define BUTTON_REPEAT_DELAY 0.2f - #define SETTINGS_BAR_Y_OFFSET 5 #define RARE_CONTROLS_BAR_Y_OFFSET 5 -extern const char **touchgui_button_imagenames; -extern const char **touchgui_joystick_imagenames; +// Very slow button repeat frequency +#define SLOW_BUTTON_REPEAT 1.0f + +extern const char **button_imagenames; +extern const char **joystick_imagenames; struct button_info { float repeatcounter; float repeatdelay; irr::EKEY_CODE keycode; - std::vector<int> ids; + std::vector<size_t> ids; IGUIButton *guibutton = nullptr; bool immediate_release; @@ -109,8 +110,8 @@ public: AutoHideButtonBar(IrrlichtDevice *device, IEventReceiver *receiver); void init(ISimpleTextureSource *tsrc, const char *starter_img, int button_id, - v2s32 UpperLeft, v2s32 LowerRight, autohide_button_bar_dir dir, - float timeout); + const v2s32 &UpperLeft, const v2s32 &LowerRight, + autohide_button_bar_dir dir, float timeout); ~AutoHideButtonBar(); @@ -125,9 +126,6 @@ public: // detect settings bar button events bool isButton(const SEvent &event); - // handle released hud buttons - bool isReleaseButton(int eventID); - // step handler void step(float dtime); @@ -182,7 +180,7 @@ public: double getPitch() { return m_camera_pitch; } - /*! + /* * Returns a line which describes what the player is pointing at. * The starting point and looking direction are significant, * the line should be scaled to match its length to the actual distance @@ -206,9 +204,10 @@ private: IEventReceiver *m_receiver; ISimpleTextureSource *m_texturesource; v2u32 m_screensize; + s32 button_size; double m_touchscreen_threshold; std::map<int, rect<s32>> m_hud_rects; - std::map<int, irr::EKEY_CODE> m_hud_ids; + std::map<size_t, irr::EKEY_CODE> m_hud_ids; bool m_visible; // is the gui visible // value in degree @@ -220,7 +219,7 @@ private: forward_id, backward_id, left_id, right_id, special1_id}; bool m_joystick_status[5] = {false, false, false, false, false}; - /*! + /* * A line starting at the camera and pointing towards the * selected object. * The line ends on the camera's far plane. @@ -248,23 +247,24 @@ private: touch_gui_button_id getButtonID(s32 x, s32 y); // gui button by eventID - touch_gui_button_id getButtonID(int eventID); + touch_gui_button_id getButtonID(size_t eventID); // check if a button has changed void handleChangedButton(const SEvent &event); // initialize a button - void initButton(touch_gui_button_id id, rect<s32> button_rect, - std::wstring caption, bool immediate_release, + void initButton(touch_gui_button_id id, const rect<s32> &button_rect, + const std::wstring &caption, bool immediate_release, float repeat_delay = BUTTON_REPEAT_DELAY); // initialize a joystick button - button_info *initJoystickButton(touch_gui_button_id id, rect<s32> button_rect, - int texture_id, bool visible = true); + button_info *initJoystickButton(touch_gui_button_id id, + const rect<s32> &button_rect, int texture_id, + bool visible = true); struct id_status { - int id; + size_t id; int X; int Y; }; @@ -273,27 +273,21 @@ private: std::vector<id_status> m_known_ids; // handle a button event - void handleButtonEvent(touch_gui_button_id bID, int eventID, bool action); + void handleButtonEvent(touch_gui_button_id bID, size_t eventID, bool action); // handle pressed hud buttons bool isHUDButton(const SEvent &event); - // handle released hud buttons - bool isReleaseHUDButton(int eventID); - // handle double taps bool doubleTapDetection(); // handle release event - void handleReleaseEvent(int evt_id); + void handleReleaseEvent(size_t evt_id); // apply joystick status void applyJoystickStatus(); - // get size of regular gui control button - int getGuiButtonSize(); - - // doubleclick detection variables + // double-click detection variables struct key_event { u64 down_time; @@ -302,9 +296,9 @@ private: }; // array for saving last known position of a pointer - v2s32 m_pointerpos[MAX_TOUCH_COUNT]; + std::map<size_t, v2s32> m_pointerpos; - // array for doubletap detection + // array for double tap detection key_event m_key_events[2]; // settings bar @@ -313,4 +307,5 @@ private: // rare controls bar AutoHideButtonBar m_rarecontrolsbar; }; + extern TouchScreenGUI *g_touchscreengui; |