From d7825bca1b3e0ed21c766da3b86c69f1da98917f Mon Sep 17 00:00:00 2001 From: DS Date: Tue, 31 Mar 2020 19:34:42 +0200 Subject: Fix GUI element click-through by changing visibility (#9534) This adds a vector that holds pointers to elements that should only be visible while being drawn. In the guifsmenu's draw func, all elements in this vector are made visible and invisible again. Apart from there, they are always invisible. (Well they are still visible before the first drawn, does this matter? If yes, it could be fixed easily with some lines of code everywhere.) --- src/gui/guiFormSpecMenu.cpp | 30 +++++++++++++++++++++++++++++- src/gui/guiFormSpecMenu.h | 1 + 2 files changed, 30 insertions(+), 1 deletion(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 012ac953f..59cd130ef 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -141,6 +141,8 @@ GUIFormSpecMenu::~GUIFormSpecMenu() background_it->drop(); for (auto &tooltip_rect_it : m_tooltip_rects) tooltip_rect_it.first->drop(); + for (auto &clickthrough_it : m_clickthrough_elements) + clickthrough_it->drop(); delete m_selected_item; delete m_form_src; @@ -742,6 +744,9 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); m_fields.push_back(spec); + // images should let events through + e->grab(); + m_clickthrough_elements.push_back(e); return; } @@ -775,6 +780,9 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); m_fields.push_back(spec); + // images should let events through + e->grab(); + m_clickthrough_elements.push_back(e); return; } errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'" << std::endl; @@ -882,7 +890,9 @@ void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &elemen core::rect(pos, pos + geom), name, m_font, m_client); auto style = getStyleForElement("item_image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - e->drop(); + + // item images should let events through + m_clickthrough_elements.push_back(e); m_fields.push_back(spec); return; @@ -1747,6 +1757,10 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); m_fields.push_back(spec); + + // labels should let events through + e->grab(); + m_clickthrough_elements.push_back(e); } return; @@ -1823,6 +1837,10 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); m_fields.push_back(spec); + + // vertlabels should let events through + e->grab(); + m_clickthrough_elements.push_back(e); return; } errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'" << std::endl; @@ -2745,6 +2763,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) background_it->drop(); for (auto &tooltip_rect_it : m_tooltip_rects) tooltip_rect_it.first->drop(); + for (auto &clickthrough_it : m_clickthrough_elements) + clickthrough_it->drop(); mydata.size= v2s32(100,100); mydata.screensize = screensize; @@ -2767,6 +2787,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_dropdowns.clear(); theme_by_name.clear(); theme_by_type.clear(); + m_clickthrough_elements.clear(); m_bgnonfullscreen = true; m_bgfullscreen = false; @@ -3248,12 +3269,19 @@ void GUIFormSpecMenu::drawMenu() e->setVisible(false); } + // Some elements are only visible while being drawn + for (gui::IGUIElement *e : m_clickthrough_elements) + e->setVisible(true); + /* Call base class (This is where all the drawing happens.) */ gui::IGUIElement::draw(); + for (gui::IGUIElement *e : m_clickthrough_elements) + e->setVisible(false); + // Draw hovered item tooltips for (const std::string &tooltip : m_hovered_item_tooltips) { showTooltip(utf8_to_wide(tooltip), m_default_tooltip_color, diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 184b26f3c..17bfef205 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -307,6 +307,7 @@ protected: std::vector> m_tooltip_rects; std::vector> m_scrollbars; std::vector>> m_dropdowns; + std::vector m_clickthrough_elements; GUIInventoryList::ItemSpec *m_selected_item = nullptr; u16 m_selected_amount = 0; -- cgit v1.2.3 From 86a0e991efff2fc616372f566a301352be28dae9 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Sat, 4 Apr 2020 16:17:15 +0200 Subject: Fix cursor still visible after closing formspec while on HyperText (#9583) --- src/gui/guiHyperText.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/gui') diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index 482b74f04..85e562dad 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -1054,12 +1054,14 @@ void GUIHyperText::checkHover(s32 X, s32 Y) } } +#ifndef HAVE_TOUCHSCREENGUI if (m_drawer.m_hovertag) RenderingEngine::get_raw_device()->getCursorControl()->setActiveIcon( gui::ECI_HAND); else RenderingEngine::get_raw_device()->getCursorControl()->setActiveIcon( gui::ECI_NORMAL); +#endif } bool GUIHyperText::OnEvent(const SEvent &event) @@ -1075,8 +1077,12 @@ bool GUIHyperText::OnEvent(const SEvent &event) if (event.EventType == EET_GUI_EVENT && event.GUIEvent.EventType == EGET_ELEMENT_LEFT) { m_drawer.m_hovertag = nullptr; - RenderingEngine::get_raw_device()->getCursorControl()->setActiveIcon( - gui::ECI_NORMAL); +#ifndef HAVE_TOUCHSCREENGUI + gui::ICursorControl *cursor_control = + RenderingEngine::get_raw_device()->getCursorControl(); + if (cursor_control->isVisible()) + cursor_control->setActiveIcon(gui::ECI_NORMAL); +#endif } if (event.EventType == EET_MOUSE_INPUT_EVENT) { -- cgit v1.2.3 From 0eb3072819e500ea6cc70c7031079472ec2ea569 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Sun, 5 Apr 2020 15:04:41 +0200 Subject: Hypertext: Fix hovercolor not working in global tag (#9582) --- src/gui/guiHyperText.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/gui') diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index 85e562dad..e107b5a3e 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -107,7 +107,7 @@ ParsedText::ParsedText(const wchar_t *text) m_root_tag.style["underline"] = "false"; m_root_tag.style["halign"] = "left"; m_root_tag.style["color"] = "#EEEEEE"; - m_root_tag.style["hovercolor"] = m_root_tag.style["color"]; + m_root_tag.style["hovercolor"] = "#FF0000"; m_active_tags.push_front(&m_root_tag); m_style = m_root_tag.style; @@ -115,7 +115,6 @@ ParsedText::ParsedText(const wchar_t *text) // Default simple tags definitions StyleList style; - style["hovercolor"] = "#FF0000"; style["color"] = "#0000FF"; style["underline"] = "true"; m_elementtags["action"] = style; -- cgit v1.2.3 From 01b3f26c7bc62305b205136bb95a2a4550761c3c Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sat, 11 Apr 2020 15:31:02 +0200 Subject: Formspec: No spec ID for static text labels Fixes #9634 --- src/gui/guiFormSpecMenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 59cd130ef..acb153569 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -1424,7 +1424,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); + this, 0); return; } -- cgit v1.2.3 From f780bae05cc2fdd23a6d7326c770783da8d94ea3 Mon Sep 17 00:00:00 2001 From: Hugues Ross Date: Sat, 11 Apr 2020 16:39:30 -0400 Subject: Formspecs: Add state-selection to style elements (#9378) --- doc/lua_api.txt | 61 +++++++++--- games/minimal/mods/test/formspec.lua | 5 +- src/client/game.cpp | 9 +- src/gui/StyleSpec.h | 65 ++++++++++-- src/gui/guiButton.cpp | 163 +++++++++++++++--------------- src/gui/guiButton.h | 34 ++++--- src/gui/guiButtonImage.cpp | 102 ++++--------------- src/gui/guiButtonImage.h | 26 ++--- src/gui/guiButtonItemImage.cpp | 15 +-- src/gui/guiButtonItemImage.h | 9 +- src/gui/guiConfirmRegistration.cpp | 8 +- src/gui/guiConfirmRegistration.h | 4 +- src/gui/guiFormSpecMenu.cpp | 187 ++++++++++++++++++++++++----------- src/gui/guiFormSpecMenu.h | 8 +- src/gui/guiKeyChangeMenu.cpp | 12 ++- src/gui/guiKeyChangeMenu.h | 5 +- src/gui/guiPasswordChange.cpp | 10 +- src/gui/guiPasswordChange.h | 5 +- src/gui/guiVolumeChange.cpp | 7 +- src/gui/guiVolumeChange.h | 7 +- src/script/lua_api/l_mainmenu.cpp | 19 ++-- src/util/numeric.h | 10 ++ 22 files changed, 454 insertions(+), 317 deletions(-) (limited to 'src/gui') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 6ba87d619..b083b2907 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2188,12 +2188,12 @@ Elements * 9-sliced background. See https://en.wikipedia.org/wiki/9-slice_scaling * Middle is a rect which defines the middle of the 9-slice. - * `x` - The middle will be x pixels from all sides. - * `x,y` - The middle will be x pixels from the horizontal and y from the vertical. - * `x,y,x2,y2` - The middle will start at x,y, and end at x2, y2. Negative x2 and y2 values - will be added to the width and height of the texture, allowing it to be used as the - distance from the far end. - * All numbers in middle are integers. + * `x` - The middle will be x pixels from all sides. + * `x,y` - The middle will be x pixels from the horizontal and y from the vertical. + * `x,y,x2,y2` - The middle will start at x,y, and end at x2, y2. Negative x2 and y2 values + will be added to the width and height of the texture, allowing it to be used as the + distance from the far end. + * All numbers in middle are integers. * Example for formspec 8x4 in 16x resolution: image shall be sized 8 times 16px times 4 times 16px * If `auto_clip` is `true`, the background is clipped to the formspec size @@ -2508,16 +2508,28 @@ Elements * `span=`: number of following columns to affect (default: infinite). -### `style[,,...;;;...]` +### `style[,;;;...]` -* Set the style for the named element(s) `name`. +* Set the style for the element(s) matching `selector` by name. +* `selector` can be one of: + * `` - An element name. + * `:` - An element name, a colon, and one or more states. +* `state` is a list of states separated by the `+` character. + * If a state is provided, the style will only take effect when the element is in that state. + * All provided states must be active for the style to apply. * Note: this **must** be before the element is defined. * See [Styling Formspecs]. -### `style_type[,,...;;;...]` +### `style_type[,;;;...]` -* Sets the style for all elements of type(s) `type` which appear after this element. +* Set the style for the element(s) matching `selector` by type. +* `selector` can be one of: + * `` - An element type. + * `:` - An element type, a colon, and one or more states. +* `state` is a list of states separated by the `+` character. + * If a state is provided, the style will only take effect when the element is in that state. + * All provided states must be active for the style to apply. * See [Styling Formspecs]. Migrating to Real Coordinates @@ -2560,23 +2572,32 @@ Styling Formspecs Formspec elements can be themed using the style elements: - style[,,...;;;...] - style_type[,,...;;;...] + style[,;;;...] + style[:,:;;;...] + style_type[,;;;...] + style_type[:,:;;;...] Where a prop is: property_name=property_value +For example: + + style_type[button;bgcolor=#006699] + style[world_delete;bgcolor=red;textcolor=yellow] + button[4,3.95;2.6,1;world_delete;Delete] + A name/type can optionally be a comma separated list of names/types, like so: world_delete,world_create,world_configure button,image_button -For example: +Any name/type in the list can also be accompanied by a `+`-separated list of states, like so: - style_type[button;bgcolor=#006699] - style[world_delete;bgcolor=red;textcolor=yellow] - button[4,3.95;2.6,1;world_delete;Delete] + world_delete:hovered+pressed + button:pressed + +States allow you to apply styles in response to changes in the element, instead of applying at all times. Setting a property to nothing will reset it to the default value. For example: @@ -2654,6 +2675,14 @@ Some types may inherit styles from parent types. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * textcolor - color. Default white. +### Valid States + +* *all elements* + * default - Equivalent to providing no states +* button, button_exit, image_button, item_image_button + * hovered - Active when the mouse is hovering over the element + * pressed - Active when the button is pressed + Markup Language --------------- diff --git a/games/minimal/mods/test/formspec.lua b/games/minimal/mods/test/formspec.lua index d2123b4af..14886aad2 100644 --- a/games/minimal/mods/test/formspec.lua +++ b/games/minimal/mods/test/formspec.lua @@ -65,7 +65,10 @@ local style_fs = [[ style[one_btn13;border=false] item_image_button[1.25,8.35;1,1;default:sword_steel;one_btn13;NoBor] - style[one_btn14;border=false;bgimg=test_bg.png;bgimg_hovered=test_bg_hovered.png;bgimg_pressed=test_bg_pressed.png;fgimg=bubble.png;fgimg_hovered=default_apple.png;fgimg_pressed=heart.png] + style[one_btn14;border=false;bgimg=test_bg.png;fgimg=bubble.png] + style[one_btn14:hovered;bgimg=test_bg_hovered.png;fgimg=default_apple.png;textcolor=red] + style[one_btn14:pressed;bgimg=test_bg_pressed.png;fgimg=heart.png;textcolor=green] + style[one_btn14:hovered+pressed;textcolor=blue] image_button[0,9.6;1,1;bubble.png;one_btn14;Bg] style[one_btn15;border=false;bgimg=test_bg.png;bgimg_hovered=test_bg_hovered.png;bgimg_pressed=test_bg_pressed.png] diff --git a/src/client/game.cpp b/src/client/game.cpp index 505108caf..06e76d170 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1556,7 +1556,8 @@ bool Game::connectToServer(const std::string &playername, } else { registration_confirmation_shown = true; (new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1, - &g_menumgr, client, playername, password, connection_aborted))->drop(); + &g_menumgr, client, playername, password, + connection_aborted, texture_src))->drop(); } } else { wait_time += dtime; @@ -1711,19 +1712,19 @@ inline bool Game::handleCallbacks() if (g_gamecallback->changepassword_requested) { (new GUIPasswordChange(guienv, guiroot, -1, - &g_menumgr, client))->drop(); + &g_menumgr, client, texture_src))->drop(); g_gamecallback->changepassword_requested = false; } if (g_gamecallback->changevolume_requested) { (new GUIVolumeChange(guienv, guiroot, -1, - &g_menumgr))->drop(); + &g_menumgr, texture_src))->drop(); g_gamecallback->changevolume_requested = false; } if (g_gamecallback->keyconfig_requested) { (new GUIKeyChangeMenu(guienv, guiroot, -1, - &g_menumgr))->drop(); + &g_menumgr, texture_src))->drop(); g_gamecallback->keyconfig_requested = false; } diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index 999c1d237..799fbf46d 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 @@ -31,25 +32,34 @@ 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, 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 property_set{}; std::array properties; + State state_map = STATE_DEFAULT; public: static Property GetPropertyByName(const std::string &name) @@ -99,6 +109,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_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 &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]; diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp index 4c16ee237..9dfe36bc4 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 rectangle, bool noclip) + s32 id, core::rect 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(Colors[i].getRed() * COLOR_HOVERED_MOD, 0, 255), - core::clamp(Colors[i].getGreen() * COLOR_HOVERED_MOD, 0, 255), - core::clamp(Colors[i].getBlue() * COLOR_HOVERED_MOD, 0, 255)); - PressedColors[i] = irr::video::SColor(Colors[i].getAlpha(), - core::clamp(Colors[i].getRed() * COLOR_PRESSED_MOD, 0, 255), - core::clamp(Colors[i].getGreen() * COLOR_PRESSED_MOD, 0, 255), - core::clamp(Colors[i].getBlue() * COLOR_PRESSED_MOD, 0, 255)); } StaticText = gui::StaticText::add(Environment, Text.c_str(), core::rect(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(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 pos(buttonCenter); @@ -548,18 +552,6 @@ void GUIButton::setPressedImage(video::ITexture* image, const core::rect& 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& 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) { @@ -618,6 +610,8 @@ void GUIButton::setPressed(bool pressed) skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y))); } } + + setFromState(); } } @@ -729,10 +723,12 @@ void GUIButton::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWri } // PATCH -GUIButton* GUIButton::addButton(IGUIEnvironment *environment, const core::rect& rectangle, - IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext) +GUIButton* GUIButton::addButton(IGUIEnvironment *environment, + const core::rect& 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 +745,87 @@ 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(Colors[i].getRed() * COLOR_HOVERED_MOD, 0, 255), - core::clamp(Colors[i].getGreen() * COLOR_HOVERED_MOD, 0, 255), - core::clamp(Colors[i].getBlue() * COLOR_HOVERED_MOD, 0, 255)); - PressedColors[i] = irr::video::SColor(Colors[i].getAlpha(), - core::clamp(Colors[i].getRed() * COLOR_PRESSED_MOD, 0, 255), - core::clamp(Colors[i].getGreen() * COLOR_PRESSED_MOD, 0, 255), - core::clamp(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(state | StyleSpec::STATE_PRESSED); + + if (isHovered()) + state = static_cast(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 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)); - setScaleImage(true); - } - 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); - - setPressedImage(guiScalingImageButton( - Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y)); + Environment->getVideoDriver(), texture, geom.X, geom.Y)); setScaleImage(true); + } else { + setImage(nullptr); } BgMiddle = style.getRect(StyleSpec::BGIMG_MIDDLE, BgMiddle); } + +//! Set the styles used for each state +void GUIButton::setStyles(const std::array& styles) +{ + Styles = styles; + setFromState(); +} // END PATCH diff --git a/src/gui/guiButton.h b/src/gui/guiButton.h index 3d1f98c32..ef10f926e 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 rectangle, bool noclip=false); + s32 id, core::rect 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& 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& 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& styles); // END PATCH //! Do not drop returned handle - static GUIButton* addButton(gui::IGUIEnvironment *environment, const core::rect& rectangle, - IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext=L""); + static GUIButton* addButton(gui::IGUIEnvironment *environment, + const core::rect& 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(0,0,0,0)) @@ -308,6 +310,8 @@ private: ButtonImage ButtonImages[gui::EGBIS_COUNT]; + std::array Styles; + gui::IGUIFont* OverrideFont; bool OverrideColorEnabled; @@ -326,8 +330,8 @@ private: video::SColor Colors[4]; // PATCH - video::SColor HoveredColors[4]; - video::SColor PressedColors[4]; + bool WasHovered = false; + ISimpleTextureSource *TSrc; gui::IGUIStaticText *StaticText; diff --git a/src/gui/guiButtonImage.cpp b/src/gui/guiButtonImage.cpp index 02d920277..2658ad967 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 rectangle, bool noclip) - : GUIButton (environment, parent, id, rectangle, noclip) + gui::IGUIElement *parent, s32 id, core::rect rectangle, + ISimpleTextureSource *tsrc, bool noclip) + : GUIButton (environment, parent, id, rectangle, tsrc, noclip) { m_image = Environment->addImage( core::rect(0,0,rectangle.getWidth(),rectangle.getHeight()), this); @@ -39,100 +40,38 @@ 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 &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 &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 &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 &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 geom(buttonCenter); if (style.isNotDefault(StyleSpec::FGIMG)) { - video::ITexture *texture = style.getTexture(StyleSpec::FGIMG, tsrc); + video::ITexture *texture = style.getTexture(StyleSpec::FGIMG, + getTextureSource()); 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); - - setPressedForegroundImage(guiScalingImageButton(driver, pressed_texture, geom.X, geom.Y)); - setScaleImage(true); + } else { + setForegroundImage(nullptr); } } @@ -143,11 +82,12 @@ void GUIButtonImage::setScaleImage(bool scaleImage) } GUIButtonImage *GUIButtonImage::addButton(IGUIEnvironment *environment, - const core::rect &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, const wchar_t *tooltiptext) + const core::rect &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..a948d772b 100644 --- a/src/gui/guiButtonImage.h +++ b/src/gui/guiButtonImage.h @@ -27,33 +27,23 @@ class GUIButtonImage : public GUIButton public: //! constructor GUIButtonImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent, - s32 id, core::rect 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 &sourceRect = core::rect(0, 0, 0, 0)); + s32 id, core::rect rectangle, ISimpleTextureSource *tsrc, + bool noclip = false); void setForegroundImage(video::ITexture *image = nullptr); - void setForegroundImage(video::ITexture *image, const core::rect &pos); - - void setPressedForegroundImage(video::ITexture *image = nullptr); - void setPressedForegroundImage(video::ITexture *image, const core::rect &pos); - - void setHoveredForegroundImage(video::ITexture *image = nullptr); - void setHoveredForegroundImage(video::ITexture *image, const core::rect &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 &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, const wchar_t *tooltiptext = L""); + const core::rect &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 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 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(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 &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, std::string item, Client *client) + const core::rect &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..9cd0f6188 100644 --- a/src/gui/guiButtonItemImage.h +++ b/src/gui/guiButtonItemImage.h @@ -30,13 +30,14 @@ class GUIButtonItemImage : public GUIButton public: //! constructor GUIButtonItemImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent, - s32 id, core::rect rectangle, std::string item, - Client *client, bool noclip = false); + s32 id, core::rect 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 &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, std::string item, Client *client); + const core::rect &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/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 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 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 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/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index acb153569..6a383a791 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -553,7 +553,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element gui::IGUICheckBox *e = Environment->addCheckBox(fselected, rect, this, 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) { @@ -613,7 +613,7 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen GUIScrollBar *e = new GUIScrollBar(Environment, this, 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); @@ -740,7 +740,7 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) gui::IGUIImage *e = Environment->addImage(rect, this, 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); @@ -776,7 +776,7 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) ); gui::IGUIImage *e = Environment->addImage(texture, pos, true, this, spec.fid, 0); - 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); @@ -841,7 +841,7 @@ 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(); @@ -888,7 +888,7 @@ void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &elemen GUIItemImage *e = new GUIItemImage(Environment, this, spec.fid, core::rect(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 +949,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, this, + 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); @@ -1155,7 +1156,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); @@ -1231,7 +1232,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); @@ -1306,7 +1307,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); @@ -1394,7 +1395,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element 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))); @@ -1454,7 +1455,7 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, } } - 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) @@ -1752,7 +1753,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) spec.flabel.c_str(), rect, false, false, this, 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))); @@ -1832,7 +1833,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen rect, false, false, this, 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 +1864,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 +1903,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, + this, 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; } @@ -2033,7 +2020,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) { @@ -2118,10 +2105,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, this, 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); @@ -2177,7 +2166,7 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) GUIBox *e = new GUIBox(Environment, this, 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(); @@ -2469,6 +2458,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) { @@ -2500,16 +2490,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 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); + } } } @@ -3080,7 +3146,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; } } @@ -4432,25 +4498,34 @@ 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 GUIFormSpecMenu::getStyleForElement(const std::string &type, + const std::string &name, const std::string &parent_type) +{ + std::array ret; if (!parent_type.empty()) { auto 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); 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..b3bf67110 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -274,11 +274,13 @@ protected: v2s32 getRealCoordinateBasePos(const std::vector &v_pos); v2s32 getRealCoordinateGeometry(const std::vector &v_geom); - std::unordered_map theme_by_type; - std::unordered_map theme_by_name; + std::unordered_map> theme_by_type; + std::unordered_map> theme_by_name; std::unordered_set 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 getStyleForElement(const std::string &type, const std::string &name="", const std::string &parent_type=""); v2s32 padding; 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 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 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 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 #include +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_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 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 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 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/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 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 +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/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 76db7ed13..867e84e13 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -571,9 +571,10 @@ int ModApiMainMenu::l_show_keys_menu(lua_State *L) sanity_check(engine != NULL); GUIKeyChangeMenu *kmenu = new GUIKeyChangeMenu(RenderingEngine::get_gui_env(), - engine->m_parent, - -1, - engine->m_menumanager); + engine->m_parent, + -1, + engine->m_menumanager, + engine->m_texture_source); kmenu->drop(); return 0; } @@ -904,12 +905,12 @@ int ModApiMainMenu::l_show_path_select_dialog(lua_State *L) GUIFileSelectMenu* fileOpenMenu = new GUIFileSelectMenu(RenderingEngine::get_gui_env(), - engine->m_parent, - -1, - engine->m_menumanager, - title, - formname, - is_file_select); + engine->m_parent, + -1, + engine->m_menumanager, + title, + formname, + is_file_select); fileOpenMenu->setTextDest(engine->m_buttonhandler); fileOpenMenu->drop(); return 0; diff --git a/src/util/numeric.h b/src/util/numeric.h index 6f82a18c1..864ab7543 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_v2d.h" #include "irr_v3d.h" #include "irr_aabb3d.h" +#include "SColor.h" #include #define rangelim(d, min, max) ((d) < (min) ? (min) : ((d) > (max) ? (max) : (d))) @@ -432,3 +433,12 @@ inline v3f getPitchYawRoll(const core::matrix4 &m) { return getPitchYawRollRad(m) * core::RADTODEG64; } + +// Muliply the RGB value of a color linearly, and clamp to black/white +inline irr::video::SColor multiplyColorValue(const irr::video::SColor &color, float mod) +{ + return irr::video::SColor(color.getAlpha(), + core::clamp(color.getRed() * mod, 0, 255), + core::clamp(color.getGreen() * mod, 0, 255), + core::clamp(color.getBlue() * mod, 0, 255)); +} -- cgit v1.2.3 From 6cf15cf872cc9ce76990d83d380ca3c4b7485eb1 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sun, 12 Apr 2020 12:02:32 +0200 Subject: GUIFormSpecMenu: Add basic element highlighing debug feature (#9423) Activated using F5 --- src/gui/guiFormSpecMenu.cpp | 10 +++++++++- src/gui/guiFormSpecMenu.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 6a383a791..f3bf04275 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3380,8 +3380,12 @@ void GUIFormSpecMenu::drawMenu() bool hovered_element_found = false; if (hovered != NULL) { - s32 id = hovered->getID(); + if (m_show_debug) { + core::rect rect = hovered->getAbsoluteClippingRect(); + driver->draw2DRectangle(0x22FFFF00, rect, &rect); + } + s32 id = hovered->getID(); u64 delta = 0; if (id == -1) { m_old_tooltip_id = id; @@ -3903,6 +3907,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 || diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index b3bf67110..dc22e8b54 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -343,6 +343,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; -- cgit v1.2.3 From 0ac999ded725f8efcd26db284161683e37efeecf Mon Sep 17 00:00:00 2001 From: DS Date: Mon, 13 Apr 2020 10:50:07 +0200 Subject: Add scroll_container formspec element (redo) (#9101) New formspec elements: - `scroll_container[,;,;;;]` - `scroll_container_end[]` Other elements can be embedded in this element. Scrollbar must be placed manually. --- build/android/jni/Android.mk | 1 + doc/lua_api.txt | 20 ++++ games/minimal/mods/test/formspec.lua | 69 ++++++++++- src/gui/CMakeLists.txt | 1 + src/gui/guiFormSpecMenu.cpp | 216 +++++++++++++++++++++++++++++------ src/gui/guiFormSpecMenu.h | 5 + src/gui/guiHyperText.cpp | 20 ++-- src/gui/guiHyperText.h | 2 +- src/gui/guiScrollContainer.cpp | 70 ++++++++++++ src/gui/guiScrollContainer.h | 56 +++++++++ src/network/networkprotocol.h | 1 + 11 files changed, 411 insertions(+), 50 deletions(-) create mode 100644 src/gui/guiScrollContainer.cpp create mode 100644 src/gui/guiScrollContainer.h (limited to 'src/gui') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index fdbfba9bc..b67322d79 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -194,6 +194,7 @@ LOCAL_SRC_FILES := \ jni/src/gui/guiPasswordChange.cpp \ jni/src/gui/guiPathSelectMenu.cpp \ jni/src/gui/guiScrollBar.cpp \ + jni/src/gui/guiScrollContainer.cpp \ jni/src/gui/guiSkin.cpp \ jni/src/gui/guiTable.cpp \ jni/src/gui/guiVolumeChange.cpp \ diff --git a/doc/lua_api.txt b/doc/lua_api.txt index dff6c728e..f43987cd8 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2102,6 +2102,26 @@ Elements * End of a container, following elements are no longer relative to this container. +### `scroll_container[,;,;;;]` + +* Start of a scroll_container block. All contained elements will ... + * take the scroll_container coordinate as position origin, + * be additionally moved by the current value of the scrollbar with the name + `scrollbar name` times `scroll factor` along the orientation `orientation` and + * be clipped to the rectangle defined by `X`, `Y`, `W` and `H`. +* `orientation`: possible values are `vertical` and `horizontal`. +* `scroll factor`: optional, defaults to `0.1`. +* Nesting is possible. +* Some elements might work a little different if they are in a scroll_container. +* Note: If you want the scroll_container to actually work, you also need to add a + scrollbar element with the specified name. Furthermore, it is highly recommended + to use a scrollbaroptions element on this scrollbar. + +### `scroll_container_end[]` + +* End of a scroll_container, following elements are no longer bound to this + container. + ### `list[;;,;,;]` * Show an inventory list if it has been sent to the client. Nothing will diff --git a/games/minimal/mods/test/formspec.lua b/games/minimal/mods/test/formspec.lua index 14886aad2..53e92b243 100644 --- a/games/minimal/mods/test/formspec.lua +++ b/games/minimal/mods/test/formspec.lua @@ -1,5 +1,35 @@ local color = minetest.colorize +local hypertext = minetest.formspec_escape([[ + +
Furnace
+ are crafted and used by the player for the purpose of cooking food and smelting various items. + + Solid block + Itself + No + Inactive:No Active:Yes (8) + No + No + Yes + Yes (99) + default:furnace default:furnace_active + Usage + The furnace menu can be accessed by using the furnace. + The furnace has 3 inventories: An input slot, a fuel slot and 4 output slots. The fire in the furnace will automatically start when there is a smeltable item in the input slot and a fuel in the fuel slot. + As long as the fire is on, the furnace will smelt any smeltable item in the input slot, one by one, until it is empty. When the fire goes off, it will smelt the next item until there are no smeltable items and no fuel items left. + The current stage of cooking can be seen by pointing the furnace or by viewing the furnace menu. In the furnace menu, the flame symbol roughly shows the remaining burning time. The arrow symbol shows the progress of the current smelting process. + Renewing + Furnaces can be crafted from e.g. cobblestone, a renewable resource. + Crafting + Sorry no way to display crafting yet in formspec pages. + Fuel + See Smelting for a list of furnace fuels. + Recipes + See the Smelting page. +]]) + + local clip_fs = [[ style_type[label,button,image_button,item_image_button, tabheader,scrollbar,table,animated_image @@ -188,13 +218,48 @@ Number] animated_image[3,4.25;1,1;;test_animation.png;4;0;3] animated_image[5.5,0.5;5,2;;test_animation.png;4;100] animated_image[5.5,2.75;5,2;;test_animation.jpg;4;100] - ]] + ]], + + "formspec_version[3]".. + "size[12,12]".. + "button[8.5,1;1,1;bla;Bla]".. + "box[1,1;8,6;#00aa]".. + "scroll_container[1,1;8,6;scrbar;vertical]".. + "button[0,1;1,1;lorem;Lorem]".. + "button[0,10;1,1;ipsum;Ipsum]".. + "pwdfield[2,2;1,1;lorem2;Lorem]".. + "list[current_player;main;4,4;1,5;]".. + "box[2,5;3,2;#ffff00]".. + "image[1,10;3,2;bubble.png]".. + "image[3,1;bubble.png]".. + "item_image[2,6;3,2;default:mese]".. + "label[2,15;bla Bli\nfoo bar]".. + "item_image_button[2,3;1,1;default:dirt_with_grass;itemimagebutton;ItemImageButton]".. + "tooltip[0,11;3,2;Buz;#f00;#000]".. + "box[0,11;3,2;#00ff00]".. + "hypertext[3,13;3,3;;" .. hypertext .. "]" .. + "container[0,18]".. + "box[1,2;3,2;#0a0a]".. + "scroll_container[1,2;3,2;scrbar2;horizontal;0.06]".. + "button[0,0;6,1;butnest;Nest]".. + "label[10,0.5;nest]".. + "scroll_container_end[]".. + "scrollbar[1,0;3.5,0.3;horizontal;scrbar2;0]".. + "container_end[]".. + "dropdown[0,6;2;hmdrpdwn;apple,bulb;1]".. + "image_button[0,4;2,2;bubble.png;bubblebutton;bbbbtt;false;true;heart.png]".. + "box[1,22.5;4,1;#a00a]".. + "scroll_container_end[]".. + "scrollbaroptions[max=170]".. -- lowest seen pos is: 0.1*170+6=23 (factor*max+height) + "scrollbar[7.5,0;0.3,4;vertical;scrbar;0]".. + "scrollbar[8,0;0.3,4;vertical;scrbarhmmm;0]".. + "dropdown[0,6;2;hmdrpdwnnn;apple,bulb;1]", } local function show_test_formspec(pname, page_id) page_id = page_id or 2 - local fs = pages[page_id] .. "tabheader[0,0;6,0.65;maintabs;Real Coord,Styles,Noclip,MiscEle;" .. page_id .. ";false;false]" + local fs = pages[page_id] .. "tabheader[0,0;8,0.65;maintabs;Real Coord,Styles,Noclip,MiscEle,Scroll Container;" .. page_id .. ";false;false]" minetest.show_formspec(pname, "test:formspec", fs) end 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/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index f3bf04275..aac039ad6 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 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 v_pos = split(parts[0], ','); + std::vector 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 rect_clipper = core::rect(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 rect_mover = core::rect(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) { @@ -443,9 +542,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,8 +649,8 @@ 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 = getDefaultStyleForElement("checkbox", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); @@ -610,8 +709,8 @@ 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 = getDefaultStyleForElement("scrollbar", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); @@ -737,7 +836,8 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) 1 ); core::rect 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 = getDefaultStyleForElement("image", spec.fname); @@ -774,8 +874,8 @@ 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); + 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); @@ -886,7 +986,7 @@ 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(pos, pos + geom), name, m_font, m_client); auto style = getDefaultStyleForElement("item_image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); @@ -949,8 +1049,8 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, if(type == "button_exit") spec.is_exit = true; - GUIButton *e = GUIButton::addButton(Environment, rect, m_tsrc, 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->setStyles(style); @@ -1141,7 +1241,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); @@ -1217,7 +1318,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); @@ -1293,7 +1395,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); @@ -1379,7 +1482,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); @@ -1390,7 +1494,7 @@ 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'*'); @@ -1425,7 +1529,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, 0); + data->current_parent, 0); return; } @@ -1443,14 +1547,14 @@ 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(); } } @@ -1491,7 +1595,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)); @@ -1671,8 +1775,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); @@ -1750,7 +1854,8 @@ 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 = getDefaultStyleForElement("label", spec.fname); @@ -1830,7 +1935,7 @@ 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 = getDefaultStyleForElement("vertlabel", spec.fname, "label"); @@ -1904,7 +2009,7 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem spec.is_exit = true; GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, m_tsrc, - this, spec.fid, spec.flabel.c_str()); + data->current_parent, spec.fid, spec.flabel.c_str()); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -2010,8 +2115,8 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen core::rect rect = core::rect(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); @@ -2046,7 +2151,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; @@ -2106,7 +2210,7 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & ); GUIButtonItemImage *e_btn = GUIButtonItemImage::addButton(Environment, - rect, m_tsrc, this, spec_btn.fid, spec_btn.flabel.c_str(), + 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"); @@ -2164,7 +2268,8 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) core::rect 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 = getDefaultStyleForElement("box", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); @@ -2316,7 +2421,7 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element) core::rect 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); @@ -2775,6 +2880,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; @@ -2831,8 +2946,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); @@ -2841,6 +2958,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(); @@ -2851,6 +2971,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(); @@ -3117,11 +3238,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 &c : m_scroll_containers) { + for (const std::pair &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) { @@ -4418,6 +4552,12 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } } + if (event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED) { + // move scroll_containers + for (const std::pair &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; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index dc22e8b54..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, @@ -310,6 +311,7 @@ protected: std::vector> m_scrollbars; std::vector>> m_dropdowns; std::vector m_clickthrough_elements; + std::vector> m_scroll_containers; GUIInventoryList::ItemSpec *m_selected_item = nullptr; u16 m_selected_amount = 0; @@ -359,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; @@ -391,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 &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 &dest_rect, +void TextDrawer::draw(const core::rect &clip_rect, const core::position2d &dest_offset) { irr::video::IVideoDriver *driver = m_environment->getVideoDriver(); - core::position2d offset = dest_rect.UpperLeftCorner + dest_offset; + core::position2d 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 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 &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 &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 &dest_rect, irr::core::rect( core::position2d(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 &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 &dest_rect); inline s32 getHeight() { return m_height; }; - void draw(const core::rect &dest_rect, + void draw(const core::rect &clip_rect, const core::position2d &dest_offset); ParsedText::Element *getElementAt(core::position2d pos); ParsedText::Tag *m_hovertag; 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 &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( + 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 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..9eaa880bf --- /dev/null +++ b/src/gui/guiScrollContainer.h @@ -0,0 +1,56 @@ +/* +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 &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; } + +private: + enum OrientationEnum + { + VERTICAL, + HORIZONTAL, + UNDEFINED + }; + + GUIScrollBar *m_scrollbar; + OrientationEnum m_orientation; + f32 m_scrollfactor; + + void updateScrolling(); +}; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 7223ce05c..4b7345b15 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -237,6 +237,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Formspec elements are drawn in the order of definition bgcolor[]: use 3 parameters (bgcolor, formspec (now an enum), fbgcolor) box[] and image[] elements enable clipping by default + new element: scroll_container[] */ #define FORMSPEC_API_VERSION 3 -- cgit v1.2.3 From 57038b3cb4b48d242af8e7dc9d598edf2e1018c4 Mon Sep 17 00:00:00 2001 From: Maksim Date: Thu, 16 Apr 2020 19:19:47 +0200 Subject: Android: fix formspec input for AArch64 devices (#9685) --- src/gui/guiFormSpecMenu.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index aac039ad6..b0255cf1b 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3346,28 +3346,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::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; } -- cgit v1.2.3 From 241bf442608d27df0ce3c74889c357fb5589ab20 Mon Sep 17 00:00:00 2001 From: DS Date: Sat, 18 Apr 2020 17:20:20 +0200 Subject: Apply a scrollbar's default value to scroll containers (#9699) Fixes #9691. --- games/minimal/mods/test/formspec.lua | 2 +- src/gui/guiScrollContainer.h | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/gui') diff --git a/games/minimal/mods/test/formspec.lua b/games/minimal/mods/test/formspec.lua index 53e92b243..4ab4f2717 100644 --- a/games/minimal/mods/test/formspec.lua +++ b/games/minimal/mods/test/formspec.lua @@ -251,7 +251,7 @@ Number] "box[1,22.5;4,1;#a00a]".. "scroll_container_end[]".. "scrollbaroptions[max=170]".. -- lowest seen pos is: 0.1*170+6=23 (factor*max+height) - "scrollbar[7.5,0;0.3,4;vertical;scrbar;0]".. + "scrollbar[7.5,0;0.3,4;vertical;scrbar;20]".. "scrollbar[8,0;0.3,4;vertical;scrbarhmmm;0]".. "dropdown[0,6;2;hmdrpdwnnn;apple,bulb;1]", } diff --git a/src/gui/guiScrollContainer.h b/src/gui/guiScrollContainer.h index 9eaa880bf..a0306291e 100644 --- a/src/gui/guiScrollContainer.h +++ b/src/gui/guiScrollContainer.h @@ -38,7 +38,11 @@ public: updateScrolling(); } - inline void setScrollBar(GUIScrollBar *scrollbar) { m_scrollbar = scrollbar; } + inline void setScrollBar(GUIScrollBar *scrollbar) + { + m_scrollbar = scrollbar; + updateScrolling(); + } private: enum OrientationEnum -- cgit v1.2.3 From 4fb6b6afa7329676166bbbccb897bd625155d038 Mon Sep 17 00:00:00 2001 From: DS Date: Sat, 18 Apr 2020 17:21:10 +0200 Subject: Formspec: allow lists to change size and existence while the formspec is open (#9700) Fixes #9640. --- src/gui/guiFormSpecMenu.cpp | 30 +----------------------------- src/gui/guiInventoryList.cpp | 42 +++++++++++++++++++++++++++++++----------- src/gui/guiInventoryList.h | 5 ++++- 3 files changed, 36 insertions(+), 41 deletions(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index b0255cf1b..85ab2eb50 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -489,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; 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 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 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; }; -- cgit v1.2.3 From 6cc5c7cbb489a429009e769a958f07da33a9d2ca Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Sat, 25 Apr 2020 07:48:04 +0200 Subject: Simplify how parseElement splits element string (#9726) --- src/gui/guiFormSpecMenu.cpp | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 85ab2eb50..0ed525f97 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -2668,24 +2668,12 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) if (parseVersionDirect(element)) return; - std::vector 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); -- cgit v1.2.3 From bc60e44d80a02bd48163440da2fd3165c0910376 Mon Sep 17 00:00:00 2001 From: v-rob Date: Sat, 25 Apr 2020 07:55:21 -0700 Subject: Add `animated_image` to clickthrough elements (#9724) --- src/gui/guiFormSpecMenu.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 0ed525f97..567f0ca7e 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -915,7 +915,9 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el 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); } -- cgit v1.2.3 From cb9a44ef8998ff66c00187efc508bf8bd6cc2d67 Mon Sep 17 00:00:00 2001 From: Hugues Ross Date: Sat, 2 May 2020 07:32:02 -0400 Subject: Add 'content_offset' and 'padding' style properties for buttons (#9661) * Add padding and content_offset style properties to buttons --- doc/lua_api.txt | 6 +++- games/minimal/mods/test/formspec.lua | 3 +- src/gui/StyleSpec.h | 54 ++++++++++++++++++++++++++++++++++++ src/gui/guiButton.cpp | 44 ++++++++++++++++------------- src/gui/guiButton.h | 2 ++ 5 files changed, 88 insertions(+), 21 deletions(-) (limited to 'src/gui') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 988acde89..1ffb5c39b 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2697,10 +2697,14 @@ Some types may inherit styles from parent types. * bgimg - standard background image. Defaults to none. * bgimg_hovered - background image when hovered. Defaults to bgimg when not provided. * bgimg_middle - Makes the bgimg textures render in 9-sliced mode and defines the middle rect. - See background9[] documentation for more details + See background9[] documentation for more details. This property also pads the + button's content when set. * bgimg_pressed - background image when pressed. Defaults to bgimg when not provided. * border - boolean, draw border. Set to false to hide the bevelled button pane. Default true. + * content_offset - 2d vector, shifts the position of the button's content without resizing it. * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * padding - rect, adds space between the edges of the button and the content. This value is + relative to bgimg_middle. * textcolor - color, default white. * checkbox * noclip - boolean, set to true to allow the element to exceed formspec bounds. diff --git a/games/minimal/mods/test/formspec.lua b/games/minimal/mods/test/formspec.lua index 4ab4f2717..a5d3074cd 100644 --- a/games/minimal/mods/test/formspec.lua +++ b/games/minimal/mods/test/formspec.lua @@ -95,7 +95,7 @@ local style_fs = [[ style[one_btn13;border=false] item_image_button[1.25,8.35;1,1;default:sword_steel;one_btn13;NoBor] - style[one_btn14;border=false;bgimg=test_bg.png;fgimg=bubble.png] + style[one_btn14;border=false;bgimg=test_bg.png;fgimg=bubble.png;padding=8] style[one_btn14:hovered;bgimg=test_bg_hovered.png;fgimg=default_apple.png;textcolor=red] style[one_btn14:pressed;bgimg=test_bg_pressed.png;fgimg=heart.png;textcolor=green] style[one_btn14:hovered+pressed;textcolor=blue] @@ -105,6 +105,7 @@ local style_fs = [[ item_image_button[1.25,9.6;1,1;default:sword_steel;one_btn15;Bg] style[one_btn16;border=false;bgimg=test_bg_9slice.png;bgimg_hovered=test_bg_9slice_hovered.png;bgimg_pressed=test_bg_9slice_pressed.png;bgimg_middle=4,6] + style[one_btn16:pressed;content_offset=0,1] button[2.5,9.6;2,1;one_btn16;9-Slice Bg] diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index 799fbf46d..3e842e826 100644 --- a/src/gui/StyleSpec.h +++ b/src/gui/StyleSpec.h @@ -44,6 +44,8 @@ public: FGIMG_HOVERED, // Note: Deprecated property FGIMG_PRESSED, // Note: Deprecated property ALPHA, + CONTENT_OFFSET, + PADDING, NUM_PROPERTIES, NONE }; @@ -92,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; } @@ -196,6 +202,29 @@ public: return rect; } + irr::core::vector2d getVector2i(Property prop, irr::core::vector2d def) const + { + const auto &val = properties[prop]; + if (val.empty()) + return def; + + irr::core::vector2d vec; + if (!parseVector2i(val, &vec)) + return def; + + return vec; + } + + irr::core::vector2d getVector2i(Property prop) const + { + const auto &val = properties[prop]; + FATAL_ERROR_IF(val.empty(), "Unexpected missing property"); + + irr::core::vector2d vec; + parseVector2i(val, &vec); + return vec; + } + video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc, video::ITexture *def) const { @@ -286,4 +315,29 @@ private: return true; } + + bool parseVector2i(const std::string &value, irr::core::vector2d *parsed_vec) const + { + irr::core::vector2d vec; + std::vector 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 9dfe36bc4..ff35958fd 100644 --- a/src/gui/guiButton.cpp +++ b/src/gui/guiButton.cpp @@ -592,25 +592,6 @@ void GUIButton::setPressed(bool pressed) { ClickTime = porting::getTimeMs(); Pressed = pressed; - - GUISkin* skin = dynamic_cast(Environment->getSkin()); - - for(IGUIElement *child : getChildren()) - { - core::rect originalRect = child->getRelativePosition(); - if (Pressed) { - child->setRelativePosition(originalRect + - core::dimension2d( - 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( - skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X), - skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y))); - } - } - setFromState(); } } @@ -819,7 +800,32 @@ void GUIButton::setFromStyle(const StyleSpec& style) } else { setImage(nullptr); } + BgMiddle = style.getRect(StyleSpec::BGIMG_MIDDLE, BgMiddle); + + // Child padding and offset + Padding = style.getRect(StyleSpec::PADDING, core::rect()); + Padding = core::rect( + Padding.UpperLeftCorner + BgMiddle.UpperLeftCorner, + Padding.LowerRightCorner + BgMiddle.LowerRightCorner); + + GUISkin* skin = dynamic_cast(Environment->getSkin()); + core::vector2d 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(0)); + + core::rect 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); + } } //! Set the styles used for each state diff --git a/src/gui/guiButton.h b/src/gui/guiButton.h index ef10f926e..95fa1a2a1 100644 --- a/src/gui/guiButton.h +++ b/src/gui/guiButton.h @@ -336,5 +336,7 @@ private: gui::IGUIStaticText *StaticText; core::rect BgMiddle; + core::rect Padding; + core::vector2d ContentOffset; // END PATCH }; -- cgit v1.2.3 From f1a05d0f71d69641fd954daf68d13acad91114f1 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Tue, 5 May 2020 08:38:18 +0200 Subject: Fix broken client if openal cannot be opened (#9804) --- src/client/clientlauncher.cpp | 2 +- src/client/game.cpp | 2 +- src/client/sound_openal.cpp | 33 +++++++++++++++++++++++++-------- src/gui/guiEngine.cpp | 4 ++-- 4 files changed, 29 insertions(+), 12 deletions(-) (limited to 'src/gui') diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 2a9d6097f..f18915a55 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -105,7 +105,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) } RenderingEngine::get_instance()->setupTopLevelWindow(PROJECT_NAME_C); - + /* This changes the minimum allowed number of vertices in a VBO. Default is 500. diff --git a/src/client/game.cpp b/src/client/game.cpp index d1eb3bba2..1577a37db 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1249,7 +1249,7 @@ bool Game::init( bool Game::initSound() { #if USE_SOUND - if (g_settings->getBool("enable_sound")) { + if (g_settings->getBool("enable_sound") && g_sound_manager_singleton.get()) { infostream << "Attempting to use OpenAL audio" << std::endl; sound = createOpenALSoundManager(g_sound_manager_singleton.get(), &soundfetcher); if (!sound) diff --git a/src/client/sound_openal.cpp b/src/client/sound_openal.cpp index d0f935a7a..20a651c1d 100644 --- a/src/client/sound_openal.cpp +++ b/src/client/sound_openal.cpp @@ -275,25 +275,38 @@ public: m_device(nullptr, delete_alcdevice), m_context(nullptr, delete_alccontext) { - if (!(m_device = unique_ptr_alcdevice(alcOpenDevice(nullptr), delete_alcdevice))) - throw std::runtime_error("Audio: Global Initialization: Device Open"); + } + + bool init() + { + if (!(m_device = unique_ptr_alcdevice(alcOpenDevice(nullptr), delete_alcdevice))) { + errorstream << "Audio: Global Initialization: Failed to open device" << std::endl; + return false; + } if (!(m_context = unique_ptr_alccontext( alcCreateContext(m_device.get(), nullptr), delete_alccontext))) { - throw std::runtime_error("Audio: Global Initialization: Context Create"); + errorstream << "Audio: Global Initialization: Failed to create context" << std::endl; + return false; } - if (!alcMakeContextCurrent(m_context.get())) - throw std::runtime_error("Audio: Global Initialization: Context Current"); + if (!alcMakeContextCurrent(m_context.get())) { + errorstream << "Audio: Global Initialization: Failed to make current context" << std::endl; + return false; + } alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); - if (alGetError() != AL_NO_ERROR) - throw std::runtime_error("Audio: Global Initialization: OpenAL Error"); + if (alGetError() != AL_NO_ERROR) { + errorstream << "Audio: Global Initialization: OpenAL Error " << alGetError() << std::endl; + return false; + } infostream << "Audio: Global Initialized: OpenAL " << alGetString(AL_VERSION) << ", using " << alcGetString(m_device.get(), ALC_DEVICE_SPECIFIER) << std::endl; + + return true; } ~SoundManagerSingleton() @@ -682,7 +695,11 @@ public: std::shared_ptr createSoundManagerSingleton() { - return std::shared_ptr(new SoundManagerSingleton()); + auto smg = std::make_shared(); + if (!smg->init()) { + smg.reset(); + } + return smg; } ISoundManager *createOpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher) 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 -- cgit v1.2.3 From 664800b2adda44039a85c3566b4ed958abff8b95 Mon Sep 17 00:00:00 2001 From: v-rob Date: Wed, 6 May 2020 10:36:02 -0700 Subject: FormSpec: Add universal style selector `*` (#9718) --- doc/lua_api.txt | 6 ++++-- src/gui/guiFormSpecMenu.cpp | 20 ++++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) (limited to 'src/gui') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 948e0f89e..961e1ff37 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2567,7 +2567,7 @@ Elements * Set the style for the element(s) matching `selector` by name. * `selector` can be one of: - * `` - An element name. + * `` - An element name. Includes `*`, which represents every element. * `:` - An element name, a colon, and one or more states. * `state` is a list of states separated by the `+` character. * If a state is provided, the style will only take effect when the element is in that state. @@ -2580,7 +2580,7 @@ Elements * Set the style for the element(s) matching `selector` by type. * `selector` can be one of: - * `` - An element type. + * `` - An element type. Includes `*`, which represents every element. * `:` - An element type, a colon, and one or more states. * `state` is a list of states separated by the `+` character. * If a state is provided, the style will only take effect when the element is in that state. @@ -2647,6 +2647,8 @@ A name/type can optionally be a comma separated list of names/types, like so: world_delete,world_create,world_configure button,image_button +A `*` type can be used to select every element in the formspec. + Any name/type in the list can also be accompanied by a `+`-separated list of states, like so: world_delete:hovered+pressed diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 567f0ca7e..72095a86e 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -4609,20 +4609,32 @@ StyleSpec GUIFormSpecMenu::getDefaultStyleForElement(const std::string &type, return getStyleForElement(type, name, parent_type)[StyleSpec::STATE_DEFAULT]; } -std::array GUIFormSpecMenu::getStyleForElement(const std::string &type, - const std::string &name, const std::string &parent_type) +std::array GUIFormSpecMenu::getStyleForElement( + const std::string &type, const std::string &name, const std::string &parent_type) { std::array 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()) { 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()) { for (const StyleSpec &spec : it->second) ret[(u32)spec.getState()] |= spec; -- cgit v1.2.3 From b6242498aad793e579960f871f01ba5bd5456658 Mon Sep 17 00:00:00 2001 From: Paul Ouellette Date: Sat, 9 May 2020 11:14:29 -0400 Subject: Always use same default tabheader height (#9319) Previously the default tabheader height was different when using real coordinates. This resulted in the height of tabs changing when switching tabs in sfinv if some tabs used real coordinates. --- src/gui/guiFormSpecMenu.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 72095a86e..49133f1cb 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -2027,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 v_geom = {"1", "0.75"}; // Dummy width and default height + std::vector v_geom = {"1", "1"}; // Dummy width and height bool auto_width = true; if (parts.size() == 7) { i++; @@ -2071,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 -- cgit v1.2.3 From 037422fdba9a47bd538480988fbf8aad67d66c85 Mon Sep 17 00:00:00 2001 From: Awkor Date: Sat, 23 May 2020 13:23:05 +0200 Subject: Modernize include guards and add missing ones (#9898) --- src/client/hud.h | 4 +--- src/client/render/factory.h | 2 ++ src/gui/guiButtonImage.h | 2 ++ src/gui/guiButtonItemImage.h | 2 ++ src/hud.h | 4 +--- src/script/lua_api/l_particles_local.h | 2 ++ src/util/md32_common.h | 2 ++ 7 files changed, 12 insertions(+), 6 deletions(-) (limited to 'src/gui') diff --git a/src/client/hud.h b/src/client/hud.h index 6274b1a83..e2abf326c 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -18,8 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef CLIENT_HUD_HEADER -#define CLIENT_HUD_HEADER +#pragma once #include #include @@ -147,4 +146,3 @@ void drawItemStack( const v3s16 &angle, const v3s16 &rotation_speed); -#endif diff --git a/src/client/render/factory.h b/src/client/render/factory.h index 22738404a..e3339a836 100644 --- a/src/client/render/factory.h +++ b/src/client/render/factory.h @@ -18,6 +18,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 #include "core.h" diff --git a/src/gui/guiButtonImage.h b/src/gui/guiButtonImage.h index a948d772b..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" diff --git a/src/gui/guiButtonItemImage.h b/src/gui/guiButtonItemImage.h index 9cd0f6188..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" diff --git a/src/hud.h b/src/hud.h index bab420ed2..e015baec1 100644 --- a/src/hud.h +++ b/src/hud.h @@ -18,8 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef HUD_HEADER -#define HUD_HEADER +#pragma once #include "irrlichttypes_extrabloated.h" #include @@ -101,4 +100,3 @@ extern const EnumString es_HudElementType[]; extern const EnumString es_HudElementStat[]; extern const EnumString es_HudBuiltinElement[]; -#endif diff --git a/src/script/lua_api/l_particles_local.h b/src/script/lua_api/l_particles_local.h index 5dff153b3..d8bb2b1c6 100644 --- a/src/script/lua_api/l_particles_local.h +++ b/src/script/lua_api/l_particles_local.h @@ -18,6 +18,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 "lua_api/l_base.h" class ModApiParticlesLocal : public ModApiBase diff --git a/src/util/md32_common.h b/src/util/md32_common.h index 085d1d7a5..a4c2099c9 100644 --- a/src/util/md32_common.h +++ b/src/util/md32_common.h @@ -109,6 +109,8 @@ * */ +#pragma once + #if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN) # error "DATA_ORDER must be defined!" #endif -- cgit v1.2.3 From a9b74f4c3966ad38c2f9a97364d3fdda0e514c93 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sun, 24 May 2020 14:24:13 +0200 Subject: Add chat_font_size setting (#9736) Default font sizes are used when the setting value is 0 or below (clamped by Settings). --- builtin/settingtypes.txt | 4 ++++ src/client/gameui.cpp | 23 +++++++++++++---------- src/defaultsettings.cpp | 4 ++++ src/gui/guiChatConsole.cpp | 4 +++- 4 files changed, 24 insertions(+), 11 deletions(-) (limited to 'src/gui') diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index e18de3382..c787aea2c 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -903,6 +903,10 @@ fallback_font_shadow_alpha (Fallback font shadow alpha) int 128 0 255 # This font will be used for certain languages or if the default font is unavailable. fallback_font_path (Fallback font path) filepath fonts/DroidSansFallbackFull.ttf +# Font size of the recent chat text and chat prompt in point (pt). +# Value 0 will use the default font size. +chat_font_size (Chat font size) int 0 + # Path to save screenshots at. Can be an absolute or relative path. # The folder will be created if it doesn't already exist. screenshot_path (Screenshot folder) path screenshots diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index bbe7caeb1..c216f405d 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -76,6 +76,11 @@ void GameUI::init() m_guitext_chat = gui::StaticText::add(guienv, L"", core::rect(0, 0, 0, 0), //false, false); // Disable word wrap as of now false, true, guiroot); + u16 chat_font_size = g_settings->getU16("chat_font_size"); + if (chat_font_size != 0) { + m_guitext_chat->setOverrideFont(g_fontengine->getFont( + chat_font_size, FM_Unspecified)); + } // Profiler text (size is updated when text is updated) m_guitext_profiler = gui::StaticText::add(guienv, L"", @@ -213,7 +218,6 @@ void GameUI::showTranslatedStatusText(const char *str) void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count) { - setStaticText(m_guitext_chat, chat_text); // Update gui element size and position s32 chat_y = 5; @@ -221,16 +225,15 @@ void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count) if (m_flags.show_debug) chat_y += 2 * g_fontengine->getLineHeight(); - // first pass to calculate height of text to be set const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize(); - s32 width = std::min(g_fontengine->getTextWidth(chat_text.c_str()) + 10, - window_size.X - 20); - m_guitext_chat->setRelativePosition(core::rect(10, chat_y, width, - chat_y + window_size.Y)); - - // now use real height of text and adjust rect according to this size - m_guitext_chat->setRelativePosition(core::rect(10, chat_y, width, - chat_y + m_guitext_chat->getTextHeight())); + + core::rect chat_size(10, chat_y, + window_size.X - 20, 0); + chat_size.LowerRightCorner.Y = std::min((s32)window_size.Y, + m_guitext_chat->getTextHeight() + chat_y); + + m_guitext_chat->setRelativePosition(chat_size); + setStaticText(m_guitext_chat, chat_text); m_recent_chat_count = recent_chat_count; } diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 1d0610c0f..5d1795003 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -321,8 +321,12 @@ void set_default_settings(Settings *settings) std::string font_size_str = std::to_string(DEFAULT_FONT_SIZE); #endif + // General font settings settings->setDefault("font_size", font_size_str); settings->setDefault("mono_font_size", font_size_str); + settings->setDefault("chat_font_size", "0"); // Default "font_size" + + // ContentDB settings->setDefault("contentdb_url", "https://content.minetest.net"); #ifdef __ANDROID__ settings->setDefault("contentdb_flag_blacklist", "nonfree, android_default"); 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; -- cgit v1.2.3 From 3cee55f2b4df39fcbf870c2467b51c3d82075874 Mon Sep 17 00:00:00 2001 From: MoNTE48 Date: Thu, 16 Apr 2020 18:49:30 +0200 Subject: Hide tooltip after ETIE_LEFT_UP (for touch control) --- src/gui/guiFormSpecMenu.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 49133f1cb..0bb401251 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3560,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 -- cgit v1.2.3 From 756dc89ede7daa640fe6391ee2c51e78864c0d5c Mon Sep 17 00:00:00 2001 From: Maksim Date: Sun, 31 May 2020 22:27:50 +0200 Subject: Fix real keyboard movements on touchscreen devices --- src/gui/touchscreengui.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/gui') diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index 94e331f72..4df5979e1 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -1211,7 +1211,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) && -- cgit v1.2.3 From 0d8e5bf587ab497b3c84a6e0c6bf15c01ae03f0a Mon Sep 17 00:00:00 2001 From: MoNTE48 Date: Thu, 16 Apr 2020 18:49:07 +0200 Subject: TouchScreen Control: fix some bugs, cleanup --- src/gui/modalMenu.cpp | 59 +++++++++++++++++---------------------------------- 1 file changed, 19 insertions(+), 40 deletions(-) (limited to 'src/gui') 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(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(event.TouchInput.X, event.TouchInput.Y)); + gui::IGUIElement *hovered = + root->getElementFromPoint(core::position2d( + 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) { -- cgit v1.2.3 From b9f618746c0f673ea1f851bb2473fb3ca9410093 Mon Sep 17 00:00:00 2001 From: MoNTE48 Date: Thu, 16 Apr 2020 17:37:40 +0200 Subject: TouchScreenGUI: fix some bugs, cleanup --- src/client/game.cpp | 4 + src/gui/touchscreengui.cpp | 414 +++++++++++++++++---------------------------- src/gui/touchscreengui.h | 55 +++--- 3 files changed, 183 insertions(+), 290 deletions(-) (limited to 'src/gui') diff --git a/src/client/game.cpp b/src/client/game.cpp index cdf4da21e..139742cec 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1173,6 +1173,10 @@ void Game::shutdown() if (formspec) formspec->quitMenu(); +#ifdef HAVE_TOUCHSCREENGUI + g_touchscreengui->hide(); +#endif + showOverlayMessage(N_("Shutting down..."), 0, 0, false); if (clouds) diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index 4df5979e1..3ee9e4a64 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 -// 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 button_rect, ISimpleTextureSource *tsrc, video::IVideoDriver *driver) + const rect &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 current_button = rect(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 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(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(m_upper_left.X, y_start, m_lower_right.Y, - y_end); + current_button = rect(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(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::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::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::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::iterator iter = m_buttons.begin(); - - while (iter != m_buttons.end()) { - std::vector::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::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::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::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 button_rect, - std::wstring caption, bool immediate_release, float repeat_delay) +void TouchScreenGUI::initButton(touch_gui_button_id id, const rect &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 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 button_rect, - int texture_id, bool visible) +button_info *TouchScreenGUI::initJoystickButton(touch_gui_button_id id, + const rect &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(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::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 >::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::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::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::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,18 +723,19 @@ 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; @@ -826,7 +744,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) 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::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,21 +1030,20 @@ 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; - SEvent translated{}; + SEvent translated; translated.EventType = irr::EET_KEY_INPUT_EVENT; translated.KeyInput.Key = id2keycode(m_joystick_names[i]); translated.KeyInput.PressedDown = false; @@ -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); @@ -1264,22 +1162,18 @@ void TouchScreenGUI::registerHudItem(int index, const rect &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 ids; + std::vector 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> m_hud_rects; - std::map m_hud_ids; + std::map 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 button_rect, - std::wstring caption, bool immediate_release, + void initButton(touch_gui_button_id id, const rect &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 button_rect, - int texture_id, bool visible = true); + button_info *initJoystickButton(touch_gui_button_id id, + const rect &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 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 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; -- cgit v1.2.3 From 8ebeed53ad01986eea0608cb8b1f583aace9528b Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Thu, 18 Jun 2020 01:43:43 +0100 Subject: Android: Fix only right strafe working (#10046) --- src/gui/touchscreengui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/gui') diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index 3ee9e4a64..0d64aa618 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -738,7 +738,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) * 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; @@ -1043,7 +1043,7 @@ void TouchScreenGUI::applyJoystickStatus() if (i == 4 && !m_joystick_triggers_special1) continue; - SEvent translated; + SEvent translated{}; translated.EventType = irr::EET_KEY_INPUT_EVENT; translated.KeyInput.Key = id2keycode(m_joystick_names[i]); translated.KeyInput.PressedDown = false; -- cgit v1.2.3 From ee6c499ff8b9abf0d22ebc817a26f33322ffdd5b Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Wed, 24 Jun 2020 11:35:06 +0200 Subject: Fix wrong use of guiScalingImageButton in formspecs buttons (#10094) --- src/gui/guiButton.cpp | 5 ++--- src/gui/guiButtonImage.cpp | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src/gui') diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp index ff35958fd..6732a9233 100644 --- a/src/gui/guiButton.cpp +++ b/src/gui/guiButton.cpp @@ -789,13 +789,12 @@ void GUIButton::setFromStyle(const StyleSpec& style) setDrawBorder(style.getBool(StyleSpec::BORDER, true)); setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true)); - const core::position2di buttonCenter(AbsoluteRect.getCenter()); - core::position2d geom(buttonCenter); if (style.isNotDefault(StyleSpec::BGIMG)) { 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); diff --git a/src/gui/guiButtonImage.cpp b/src/gui/guiButtonImage.cpp index 2658ad967..b507ffece 100644 --- a/src/gui/guiButtonImage.cpp +++ b/src/gui/guiButtonImage.cpp @@ -62,13 +62,12 @@ void GUIButtonImage::setFromStyle(const StyleSpec& style) video::IVideoDriver *driver = Environment->getVideoDriver(); - const core::position2di buttonCenter(AbsoluteRect.getCenter()); - core::position2d geom(buttonCenter); if (style.isNotDefault(StyleSpec::FGIMG)) { video::ITexture *texture = style.getTexture(StyleSpec::FGIMG, getTextureSource()); - setForegroundImage(guiScalingImageButton(driver, texture, geom.X, geom.Y)); + setForegroundImage(guiScalingImageButton(driver, texture, + AbsoluteRect.getWidth(), AbsoluteRect.getHeight())); setScaleImage(true); } else { setForegroundImage(nullptr); -- cgit v1.2.3 From da71313e1df3326c2b9ddb410b963ec25682319e Mon Sep 17 00:00:00 2001 From: v-rob Date: Fri, 3 Jul 2020 09:33:23 -0700 Subject: Don't stop style parsing on unknown property (#10143) --- src/gui/guiFormSpecMenu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 0bb401251..37edf3c4b 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -2562,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); @@ -2603,7 +2603,7 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b } } - if(!state_valid) { + if (!state_valid) { // Skip this selector continue; } -- cgit v1.2.3 From b1ff04e06da531dc52f6ee91e2967b39743033e6 Mon Sep 17 00:00:00 2001 From: v-rob Date: Fri, 10 Jul 2020 03:11:26 -0700 Subject: Formspec: Make dropdowns optionally return event based on index, not value (#9496) --- doc/lua_api.txt | 15 ++++++++++++--- src/gui/guiFormSpecMenu.cpp | 39 +++++++++++++++++++++++---------------- src/gui/guiFormSpecMenu.h | 1 + src/network/networkprotocol.h | 4 +++- 4 files changed, 39 insertions(+), 20 deletions(-) (limited to 'src/gui') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index f2a83eca5..2ac4d6766 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2443,7 +2443,7 @@ Elements * `color` is color specified as a `ColorString`. If the alpha component is left blank, the box will be semitransparent. -### `dropdown[,;;;,, ...,;]` +### `dropdown[,;;;,, ...,;;]` * Show a dropdown field * **Important note**: There are two different operation modes: @@ -2454,8 +2454,12 @@ Elements * Fieldname data is transferred to Lua * Items to be shown in dropdown * Index of currently selected dropdown item +* `index event` (optional, allowed parameter since formspec version 4): Specifies the + event field value for selected items. + * `true`: Selected item index + * `false` (default): Selected item value -### `dropdown[,;,;;,, ...,;]` +### `dropdown[,;,;;,, ...,;;]` * Show a dropdown field * **Important note**: This syntax for dropdowns can only be used with the @@ -2468,6 +2472,10 @@ Elements * Fieldname data is transferred to Lua * Items to be shown in dropdown * Index of currently selected dropdown item +* `index event` (optional, allowed parameter since formspec version 4): Specifies the + event field value for selected items. + * `true`: Selected item index + * `false` (default): Selected item value ### `checkbox[,;;