diff options
Diffstat (limited to 'src/gui')
31 files changed, 1572 insertions, 2337 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index fdd36914a..513b13e8e 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -1,3 +1,8 @@ +set(extra_gui_SRCS "") +if(ENABLE_TOUCH) + set(extra_gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/touchscreengui.cpp) +endif() + set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/guiAnimatedImage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiBackgroundImage.cpp @@ -23,8 +28,8 @@ set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiHyperText.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/intlGUIEditBox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/modalMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/profilergraph.cpp + ${extra_gui_SRCS} PARENT_SCOPE ) diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp index b98e5de82..ba95b81c3 100644 --- a/src/gui/guiButton.cpp +++ b/src/gui/guiButton.cpp @@ -506,6 +506,13 @@ video::SColor GUIButton::getOverrideColor() const return OverrideColor;
}
+#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8
+video::SColor GUIButton::getActiveColor() const
+{
+ return video::SColor(0,0,0,0); // unused?
+}
+#endif
+
void GUIButton::enableOverrideColor(bool enable)
{
OverrideColorEnabled = enable;
@@ -625,85 +632,6 @@ bool GUIButton::isDrawingBorder() const }
-//! Writes attributes of the element.
-void GUIButton::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
-{
- IGUIButton::serializeAttributes(out,options);
-
- out->addBool ("PushButton", IsPushButton );
- if (IsPushButton)
- out->addBool("Pressed", Pressed);
-
- for ( u32 i=0; i<(u32)EGBIS_COUNT; ++i )
- {
- if ( ButtonImages[i].Texture )
- {
- core::stringc name( GUIButtonImageStateNames[i] );
- out->addTexture(name.c_str(), ButtonImages[i].Texture);
- name += "Rect";
- out->addRect(name.c_str(), ButtonImages[i].SourceRect);
- }
- }
-
- out->addBool ("UseAlphaChannel", UseAlphaChannel);
- out->addBool ("Border", DrawBorder);
- out->addBool ("ScaleImage", ScaleImage);
-
- for ( u32 i=0; i<(u32)EGBS_COUNT; ++i )
- {
- if ( ButtonSprites[i].Index >= 0 )
- {
- core::stringc nameIndex( GUIButtonStateNames[i] );
- nameIndex += "Index";
- out->addInt(nameIndex.c_str(), ButtonSprites[i].Index );
-
- core::stringc nameColor( GUIButtonStateNames[i] );
- nameColor += "Color";
- out->addColor(nameColor.c_str(), ButtonSprites[i].Color );
-
- core::stringc nameLoop( GUIButtonStateNames[i] );
- nameLoop += "Loop";
- out->addBool(nameLoop.c_str(), ButtonSprites[i].Loop );
-
- core::stringc nameScale( GUIButtonStateNames[i] );
- nameScale += "Scale";
- out->addBool(nameScale.c_str(), ButtonSprites[i].Scale );
- }
- }
-
- // out->addString ("OverrideFont", OverrideFont);
-}
-
-
-//! Reads attributes of the element
-void GUIButton::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
-{
- IGUIButton::deserializeAttributes(in,options);
-
- IsPushButton = in->getAttributeAsBool("PushButton");
- Pressed = IsPushButton ? in->getAttributeAsBool("Pressed") : false;
-
- core::rect<s32> rec = in->getAttributeAsRect("ImageRect");
- if (rec.isValid())
- setImage( in->getAttributeAsTexture("Image"), rec);
- else
- setImage( in->getAttributeAsTexture("Image") );
-
- rec = in->getAttributeAsRect("PressedImageRect");
- if (rec.isValid())
- setPressedImage( in->getAttributeAsTexture("PressedImage"), rec);
- else
- setPressedImage( in->getAttributeAsTexture("PressedImage") );
-
- setDrawBorder(in->getAttributeAsBool("Border"));
- setUseAlphaChannel(in->getAttributeAsBool("UseAlphaChannel"));
- setScaleImage(in->getAttributeAsBool("ScaleImage"));
-
- // setOverrideFont(in->getAttributeAsString("OverrideFont"));
-
- updateAbsolutePosition();
-}
-
// PATCH
GUIButton* GUIButton::addButton(IGUIEnvironment *environment,
const core::rect<s32>& rectangle, ISimpleTextureSource *tsrc,
diff --git a/src/gui/guiButton.h b/src/gui/guiButton.h index 4e1b04aac..ee9bb6f21 100644 --- a/src/gui/guiButton.h +++ b/src/gui/guiButton.h @@ -69,6 +69,12 @@ using namespace irr; class ISimpleTextureSource;
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 8)
+#define OVERRIDE_19
+#else
+#define OVERRIDE_19 override
+#endif
+
class GUIButton : public gui::IGUIButton
{
public:
@@ -97,22 +103,27 @@ public: virtual gui::IGUIFont* getActiveFont() const override;
//! Sets another color for the button text.
- virtual void setOverrideColor(video::SColor color);
+ virtual void setOverrideColor(video::SColor color) OVERRIDE_19;
//! Gets the override color
- virtual video::SColor getOverrideColor(void) const;
+ virtual video::SColor getOverrideColor(void) const OVERRIDE_19;
+
+ #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8
+ //! Gets the currently used text color
+ virtual video::SColor getActiveColor() const override;
+ #endif
//! Sets if the button text should use the override color or the color in the gui skin.
- virtual void enableOverrideColor(bool enable);
+ virtual void enableOverrideColor(bool enable) OVERRIDE_19;
//! Checks if an override color is enabled
- virtual bool isOverrideColorEnabled(void) const;
+ virtual bool isOverrideColorEnabled(void) const OVERRIDE_19;
// PATCH
//! Sets an image which should be displayed on the button when it is in the given state.
virtual void setImage(gui::EGUI_BUTTON_IMAGE_STATE state,
video::ITexture* image=nullptr,
- const core::rect<s32>& sourceRect=core::rect<s32>(0,0,0,0));
+ const core::rect<s32>& sourceRect=core::rect<s32>(0,0,0,0)) OVERRIDE_19;
//! Sets an image which should be displayed on the button when it is in normal state.
virtual void setImage(video::ITexture* image=nullptr) override;
@@ -141,7 +152,7 @@ public: */
virtual void setSprite(gui::EGUI_BUTTON_STATE state, s32 index,
video::SColor color=video::SColor(255,255,255,255),
- bool loop=false, bool scale=false);
+ bool loop=false, bool scale=false) OVERRIDE_19;
#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 8)
void setSprite(gui::EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop) override {
@@ -150,16 +161,16 @@ public: #endif
//! Get the sprite-index for the given state or -1 when no sprite is set
- virtual s32 getSpriteIndex(gui::EGUI_BUTTON_STATE state) const;
+ virtual s32 getSpriteIndex(gui::EGUI_BUTTON_STATE state) const OVERRIDE_19;
//! Get the sprite color for the given state. Color is only used when a sprite is set.
- virtual video::SColor getSpriteColor(gui::EGUI_BUTTON_STATE state) const;
+ virtual video::SColor getSpriteColor(gui::EGUI_BUTTON_STATE state) const OVERRIDE_19;
//! Returns if the sprite in the given state does loop
- virtual bool getSpriteLoop(gui::EGUI_BUTTON_STATE state) const;
+ virtual bool getSpriteLoop(gui::EGUI_BUTTON_STATE state) const OVERRIDE_19;
//! Returns if the sprite in the given state is scaled
- virtual bool getSpriteScale(gui::EGUI_BUTTON_STATE state) const;
+ virtual bool getSpriteScale(gui::EGUI_BUTTON_STATE state) const OVERRIDE_19;
//! Sets if the button should behave like a push button. Which means it
//! can be in two states: Normal or Pressed. With a click on the button,
@@ -199,25 +210,17 @@ public: virtual bool isScalingImage() const override;
//! Get if the shift key was pressed in last EGET_BUTTON_CLICKED event
- virtual bool getClickShiftState() const
+ virtual bool getClickShiftState() const OVERRIDE_19
{
return ClickShiftState;
}
//! Get if the control key was pressed in last EGET_BUTTON_CLICKED event
- virtual bool getClickControlState() const
+ virtual bool getClickControlState() const OVERRIDE_19
{
return ClickControlState;
}
- //! Writes attributes of the element.
- virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const override;
-
- //! Reads attributes of the element
- virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) override;
-
-
-
void setColor(video::SColor color);
// PATCH
//! Set element properties from a StyleSpec corresponding to the button state
diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp index ef471106d..01e10ea2e 100644 --- a/src/gui/guiChatConsole.cpp +++ b/src/gui/guiChatConsole.cpp @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "IrrCompileConfig.h" #include "guiChatConsole.h" #include "chat.h" #include "client/client.h" @@ -29,17 +30,18 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/fontengine.h" #include "log.h" #include "gettext.h" +#include "irrlicht_changes/CGUITTFont.h" #include <string> -#if USE_FREETYPE - #include "irrlicht_changes/CGUITTFont.h" -#endif - inline u32 clamp_u8(s32 value) { return (u32) MYMIN(MYMAX(value, 0), 255); } +inline bool isInCtrlKeys(const irr::EKEY_CODE& kc) +{ + return kc == KEY_LCONTROL || kc == KEY_RCONTROL || kc == KEY_CONTROL; +} GUIChatConsole::GUIChatConsole( gui::IGUIEnvironment* env, @@ -90,6 +92,10 @@ GUIChatConsole::GUIChatConsole( // set default cursor options setCursor(true, true, 2.0, 0.1); + + // track ctrl keys for mouse event + m_is_ctrl_down = false; + m_cache_clickable_chat_weblinks = g_settings->getBool("clickable_chat_weblinks"); } GUIChatConsole::~GUIChatConsole() @@ -319,20 +325,16 @@ void GUIChatConsole::drawText() core::rect<s32> destrect( x, y, x + m_fontsize.X * fragment.text.size(), y + m_fontsize.Y); -#if USE_FREETYPE if (m_font->getType() == irr::gui::EGFT_CUSTOM) { - // Draw colored text if FreeType is enabled - irr::gui::CGUITTFont *tmp = dynamic_cast<irr::gui::CGUITTFont *>(m_font); + // Draw colored text if possible + gui::CGUITTFont *tmp = static_cast<gui::CGUITTFont*>(m_font); tmp->draw( fragment.text, destrect, - video::SColor(255, 255, 255, 255), false, false, &AbsoluteClippingRect); - } else -#endif - { + } else { // Otherwise use standard text m_font->draw( fragment.text.c_str(), @@ -405,8 +407,21 @@ bool GUIChatConsole::OnEvent(const SEvent& event) ChatPrompt &prompt = m_chat_backend->getPrompt(); - if(event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown) + if (event.EventType == EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown) + { + // CTRL up + if (isInCtrlKeys(event.KeyInput.Key)) + { + m_is_ctrl_down = false; + } + } + else if(event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown) { + // CTRL down + if (isInCtrlKeys(event.KeyInput.Key)) { + m_is_ctrl_down = true; + } + // Key input if (KeyPress(event.KeyInput) == getKeySetting("keymap_console")) { closeConsole(); @@ -559,8 +574,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event) const c8 *text = os_operator->getTextFromClipboard(); if (!text) return true; - std::basic_string<unsigned char> str((const unsigned char*)text); - prompt.input(std::wstring(str.begin(), str.end())); + prompt.input(utf8_to_wide(text)); return true; } else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control) @@ -607,24 +621,38 @@ bool GUIChatConsole::OnEvent(const SEvent& event) prompt.nickCompletion(names, backwards); return true; } else if (!iswcntrl(event.KeyInput.Char) && !event.KeyInput.Control) { - #if defined(__linux__) && (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9) - wchar_t wc = L'_'; - mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) ); - prompt.input(wc); - #else - prompt.input(event.KeyInput.Char); - #endif + prompt.input(event.KeyInput.Char); return true; } } else if(event.EventType == EET_MOUSE_INPUT_EVENT) { - if(event.MouseInput.Event == EMIE_MOUSE_WHEEL) + if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) { s32 rows = myround(-3.0 * event.MouseInput.Wheel); m_chat_backend->scroll(rows); } + // Middle click or ctrl-click opens weblink, if enabled in config + else if(m_cache_clickable_chat_weblinks && ( + event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN || + (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN && m_is_ctrl_down) + )) + { + // If clicked within console output region + if (event.MouseInput.Y / m_fontsize.Y < (m_height / m_fontsize.Y) - 1 ) + { + // Translate pixel position to font position + middleClick(event.MouseInput.X / m_fontsize.X, event.MouseInput.Y / m_fontsize.Y); + } + } } +#if (IRRLICHT_VERSION_MT_REVISION >= 2) + else if(event.EventType == EET_STRING_INPUT_EVENT) + { + prompt.input(std::wstring(event.StringInput.Str->c_str())); + return true; + } +#endif return Parent ? Parent->OnEvent(event) : false; } @@ -639,3 +667,62 @@ void GUIChatConsole::setVisible(bool visible) } } +void GUIChatConsole::middleClick(s32 col, s32 row) +{ + // Prevent accidental rapid clicking + static u64 s_oldtime = 0; + u64 newtime = porting::getTimeMs(); + + // 0.6 seconds should suffice + if (newtime - s_oldtime < 600) + return; + s_oldtime = newtime; + + const std::vector<ChatFormattedFragment> & + frags = m_chat_backend->getConsoleBuffer().getFormattedLine(row).fragments; + std::string weblink = ""; // from frag meta + + // Identify targetted fragment, if exists + int indx = frags.size() - 1; + if (indx < 0) { + // Invalid row, frags is empty + return; + } + // Scan from right to left, offset by 1 font space because left margin + while (indx > -1 && (u32)col < frags[indx].column + 1) { + --indx; + } + if (indx > -1) { + weblink = frags[indx].weblink; + // Note if(indx < 0) then a frag somehow had a corrupt column field + } + + /* + // Debug help. Please keep this in case adjustments are made later. + std::string ws; + ws = "Middleclick: (" + std::to_string(col) + ',' + std::to_string(row) + ')' + " frags:"; + // show all frags <position>(<length>) for the clicked row + for (u32 i=0;i<frags.size();++i) { + if (indx == int(i)) + // tag the actual clicked frag + ws += '*'; + ws += std::to_string(frags.at(i).column) + '(' + + std::to_string(frags.at(i).text.size()) + "),"; + } + actionstream << ws << std::endl; + */ + + // User notification + if (weblink.size() != 0) { + std::ostringstream msg; + msg << " * "; + if (porting::open_url(weblink)) { + msg << gettext("Opening webpage"); + } + else { + msg << gettext("Failed to open webpage"); + } + msg << " '" << weblink << "'"; + m_chat_backend->addUnparsedMessage(utf8_to_wide(msg.str())); + } +} diff --git a/src/gui/guiChatConsole.h b/src/gui/guiChatConsole.h index 896342ab0..32628f0d8 100644 --- a/src/gui/guiChatConsole.h +++ b/src/gui/guiChatConsole.h @@ -72,6 +72,8 @@ public: virtual void setVisible(bool visible); + virtual bool acceptsIME() { return true; } + private: void reformatConsole(); void recalculateConsolePosition(); @@ -82,6 +84,9 @@ private: void drawText(); void drawPrompt(); + // If clicked fragment has a web url, send it to the system default web browser + void middleClick(s32 col, s32 row); + private: ChatBackend* m_chat_backend; Client* m_client; @@ -124,4 +129,9 @@ private: // font gui::IGUIFont *m_font = nullptr; v2u32 m_fontsize; + + // Enable clickable chat weblinks + bool m_cache_clickable_chat_weblinks; + // Track if a ctrl key is currently held down + bool m_is_ctrl_down; }; diff --git a/src/gui/guiConfirmRegistration.cpp b/src/gui/guiConfirmRegistration.cpp index 4a798c39b..b8887a4af 100644 --- a/src/gui/guiConfirmRegistration.cpp +++ b/src/gui/guiConfirmRegistration.cpp @@ -25,9 +25,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <IGUIButton.h> #include <IGUIStaticText.h> #include <IGUIFont.h> -#include "intlGUIEditBox.h" +#include "guiEditBoxWithScrollbar.h" #include "porting.h" +#ifdef HAVE_TOUCHSCREENGUI + #include "client/renderingengine.h" +#endif + #include "gettext.h" // Continuing from guiPasswordChange.cpp @@ -45,7 +49,7 @@ GUIConfirmRegistration::GUIConfirmRegistration(gui::IGUIEnvironment *env, m_client(client), m_playername(playername), m_password(password), m_aborted(aborted), m_tsrc(tsrc) { -#ifdef __ANDROID__ +#ifdef HAVE_TOUCHSCREENGUI m_touchscreen_visible = false; #endif } @@ -73,8 +77,8 @@ void GUIConfirmRegistration::regenerateGui(v2u32 screensize) /* Calculate new sizes and positions */ -#ifdef __ANDROID__ - const float s = m_gui_scale * porting::getDisplayDensity() / 2; +#ifdef HAVE_TOUCHSCREENGUI + const float s = m_gui_scale * RenderingEngine::getDisplayDensity() / 2; #else const float s = m_gui_scale; #endif @@ -109,10 +113,9 @@ void GUIConfirmRegistration::regenerateGui(v2u32 screensize) porting::mt_snprintf(info_text_buf, sizeof(info_text_buf), info_text_template.c_str(), m_playername.c_str()); - wchar_t *info_text_buf_wide = utf8_to_wide_c(info_text_buf); - gui::IGUIEditBox *e = new gui::intlGUIEditBox(info_text_buf_wide, true, - Environment, this, ID_intotext, rect2, false, true); - delete[] info_text_buf_wide; + std::wstring info_text_w = utf8_to_wide(info_text_buf); + gui::IGUIEditBox *e = new GUIEditBoxWithScrollBar(info_text_w.c_str(), + true, Environment, this, ID_intotext, rect2, false, true); e->drop(); e->setMultiLine(true); e->setWordWrap(true); diff --git a/src/gui/guiEditBox.cpp b/src/gui/guiEditBox.cpp index 79979dbc3..4a0f5013d 100644 --- a/src/gui/guiEditBox.cpp +++ b/src/gui/guiEditBox.cpp @@ -19,11 +19,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiEditBox.h" +#include "IrrCompileConfig.h" #include "IGUISkin.h" #include "IGUIEnvironment.h" #include "IGUIFont.h" #include "porting.h" +#include "util/string.h" GUIEditBox::~GUIEditBox() { @@ -208,35 +210,19 @@ bool GUIEditBox::OnEvent(const SEvent &event) } } break; - case EET_KEY_INPUT_EVENT: { -#if (defined(__linux__) || defined(__FreeBSD__)) || defined(__DragonFly__) - // ################################################################ - // ValkaTR: - // This part is the difference from the original intlGUIEditBox - // It converts UTF-8 character into a UCS-2 (wchar_t) - wchar_t wc = L'_'; - mbtowc(&wc, (char *)&event.KeyInput.Char, - sizeof(event.KeyInput.Char)); - - // printf( "char: %lc (%u) \r\n", wc, wc ); - - SEvent irrevent(event); - irrevent.KeyInput.Char = wc; - // ################################################################ - - if (processKey(irrevent)) - return true; -#else + case EET_KEY_INPUT_EVENT: if (processKey(event)) return true; -#endif // defined(linux) - break; - } case EET_MOUSE_INPUT_EVENT: if (processMouse(event)) return true; break; +#if (IRRLICHT_VERSION_MT_REVISION >= 2) + case EET_STRING_INPUT_EVENT: + inputString(*event.StringInput.Str); + return true; +#endif default: break; } @@ -247,10 +233,6 @@ bool GUIEditBox::OnEvent(const SEvent &event) bool GUIEditBox::processKey(const SEvent &event) { - if (!m_writable) { - return false; - } - if (!event.KeyInput.PressedDown) return false; @@ -536,8 +518,7 @@ void GUIEditBox::onKeyControlC(const SEvent &event) const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end; const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin; - core::stringc s; - s = Text.subString(realmbgn, realmend - realmbgn).c_str(); + std::string s = stringw_to_utf8(Text.subString(realmbgn, realmend - realmbgn)); m_operator->copyToClipboard(s.c_str()); } @@ -546,6 +527,9 @@ bool GUIEditBox::onKeyControlX(const SEvent &event, s32 &mark_begin, s32 &mark_e // First copy to clipboard onKeyControlC(event); + if (!m_writable) + return false; + if (m_passwordbox || !m_operator || m_mark_begin == m_mark_end) return false; @@ -571,7 +555,7 @@ bool GUIEditBox::onKeyControlX(const SEvent &event, s32 &mark_begin, s32 &mark_e bool GUIEditBox::onKeyControlV(const SEvent &event, s32 &mark_begin, s32 &mark_end) { - if (!isEnabled()) + if (!isEnabled() || !m_writable) return false; // paste from the clipboard @@ -583,29 +567,28 @@ bool GUIEditBox::onKeyControlV(const SEvent &event, s32 &mark_begin, s32 &mark_e // add new character if (const c8 *p = m_operator->getTextFromClipboard()) { + core::stringw inserted_text = utf8_to_stringw(p); if (m_mark_begin == m_mark_end) { // insert text core::stringw s = Text.subString(0, m_cursor_pos); - s.append(p); + s.append(inserted_text); s.append(Text.subString( m_cursor_pos, Text.size() - m_cursor_pos)); if (!m_max || s.size() <= m_max) { Text = s; - s = p; - m_cursor_pos += s.size(); + m_cursor_pos += inserted_text.size(); } } else { // replace text core::stringw s = Text.subString(0, realmbgn); - s.append(p); + s.append(inserted_text); s.append(Text.subString(realmend, Text.size() - realmend)); if (!m_max || s.size() <= m_max) { Text = s; - s = p; - m_cursor_pos = realmbgn + s.size(); + m_cursor_pos = realmbgn + inserted_text.size(); } } } @@ -617,7 +600,7 @@ bool GUIEditBox::onKeyControlV(const SEvent &event, s32 &mark_begin, s32 &mark_e bool GUIEditBox::onKeyBack(const SEvent &event, s32 &mark_begin, s32 &mark_end) { - if (!isEnabled() || Text.empty()) + if (!isEnabled() || Text.empty() || !m_writable) return false; core::stringw s; @@ -655,7 +638,7 @@ bool GUIEditBox::onKeyBack(const SEvent &event, s32 &mark_begin, s32 &mark_end) bool GUIEditBox::onKeyDelete(const SEvent &event, s32 &mark_begin, s32 &mark_end) { - if (!isEnabled() || Text.empty()) + if (!isEnabled() || Text.empty() || !m_writable) return false; core::stringw s; @@ -691,39 +674,44 @@ bool GUIEditBox::onKeyDelete(const SEvent &event, s32 &mark_begin, s32 &mark_end void GUIEditBox::inputChar(wchar_t c) { - if (!isEnabled() || !m_writable) + if (c == 0) return; + core::stringw s(&c, 1); + inputString(s); +} - if (c != 0) { - if (Text.size() < m_max || m_max == 0) { - core::stringw s; - - if (m_mark_begin != m_mark_end) { - // clang-format off - // replace marked text - s32 real_begin = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end; - s32 real_end = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin; - - s = Text.subString(0, real_begin); - s.append(c); - s.append(Text.subString(real_end, Text.size() - real_end)); - Text = s; - m_cursor_pos = real_begin + 1; - // clang-format on - } else { - // add new character - s = Text.subString(0, m_cursor_pos); - s.append(c); - s.append(Text.subString(m_cursor_pos, - Text.size() - m_cursor_pos)); - Text = s; - ++m_cursor_pos; - } +void GUIEditBox::inputString(const core::stringw &str) +{ + if (!isEnabled() || !m_writable) + return; - m_blink_start_time = porting::getTimeMs(); - setTextMarkers(0, 0); + u32 len = str.size(); + if (Text.size()+len <= m_max || m_max == 0) { + core::stringw s; + if (m_mark_begin != m_mark_end) { + // replace marked text + s32 real_begin = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end; + s32 real_end = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin; + + s = Text.subString(0, real_begin); + s.append(str); + s.append(Text.subString(real_end, Text.size() - real_end)); + Text = s; + m_cursor_pos = real_begin + len; + } else { + // append string + s = Text.subString(0, m_cursor_pos); + s.append(str); + s.append(Text.subString(m_cursor_pos, + Text.size() - m_cursor_pos)); + Text = s; + m_cursor_pos += len; } + + m_blink_start_time = porting::getTimeMs(); + setTextMarkers(0, 0); } + breakText(); sendGuiEvent(EGET_EDITBOX_CHANGED); calculateScrollPos(); @@ -858,54 +846,3 @@ void GUIEditBox::updateVScrollBar() } } } - -void GUIEditBox::deserializeAttributes( - io::IAttributes *in, io::SAttributeReadWriteOptions *options = 0) -{ - IGUIEditBox::deserializeAttributes(in, options); - - setOverrideColor(in->getAttributeAsColor("OverrideColor")); - enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled")); - setMax(in->getAttributeAsInt("MaxChars")); - setWordWrap(in->getAttributeAsBool("WordWrap")); - setMultiLine(in->getAttributeAsBool("MultiLine")); - setAutoScroll(in->getAttributeAsBool("AutoScroll")); - core::stringw ch = in->getAttributeAsStringW("PasswordChar"); - - if (ch.empty()) - setPasswordBox(in->getAttributeAsBool("PasswordBox")); - else - setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]); - - setTextAlignment((EGUI_ALIGNMENT)in->getAttributeAsEnumeration( - "HTextAlign", GUIAlignmentNames), - (EGUI_ALIGNMENT)in->getAttributeAsEnumeration( - "VTextAlign", GUIAlignmentNames)); - - setWritable(in->getAttributeAsBool("Writable")); - // setOverrideFont(in->getAttributeAsFont("OverrideFont")); -} - -//! Writes attributes of the element. -void GUIEditBox::serializeAttributes( - io::IAttributes *out, io::SAttributeReadWriteOptions *options = 0) const -{ - // IGUIEditBox::serializeAttributes(out,options); - - out->addBool("OverrideColorEnabled", m_override_color_enabled); - out->addColor("OverrideColor", m_override_color); - // out->addFont("OverrideFont",m_override_font); - out->addInt("MaxChars", m_max); - out->addBool("WordWrap", m_word_wrap); - out->addBool("MultiLine", m_multiline); - out->addBool("AutoScroll", m_autoscroll); - out->addBool("PasswordBox", m_passwordbox); - core::stringw ch = L" "; - ch[0] = m_passwordchar; - out->addString("PasswordChar", ch.c_str()); - out->addEnum("HTextAlign", m_halign, GUIAlignmentNames); - out->addEnum("VTextAlign", m_valign, GUIAlignmentNames); - out->addBool("Writable", m_writable); - - IGUIEditBox::serializeAttributes(out, options); -} diff --git a/src/gui/guiEditBox.h b/src/gui/guiEditBox.h index c616d75d1..4c7413f54 100644 --- a/src/gui/guiEditBox.h +++ b/src/gui/guiEditBox.h @@ -130,13 +130,7 @@ public: //! called if an event happened. virtual bool OnEvent(const SEvent &event); - //! Writes attributes of the element. - virtual void serializeAttributes(io::IAttributes *out, - io::SAttributeReadWriteOptions *options) const; - - //! Reads attributes of the element - virtual void deserializeAttributes( - io::IAttributes *in, io::SAttributeReadWriteOptions *options); + virtual bool acceptsIME() { return isEnabled() && m_writable; }; protected: virtual void breakText() = 0; @@ -156,6 +150,7 @@ protected: virtual s32 getCursorPos(s32 x, s32 y) = 0; bool processKey(const SEvent &event); + virtual void inputString(const core::stringw &str); virtual void inputChar(wchar_t c); //! returns the line number that the cursor is on diff --git a/src/gui/guiEditBoxWithScrollbar.cpp b/src/gui/guiEditBoxWithScrollbar.cpp index c72070787..1b7f7832a 100644 --- a/src/gui/guiEditBoxWithScrollbar.cpp +++ b/src/gui/guiEditBoxWithScrollbar.cpp @@ -620,6 +620,17 @@ void GUIEditBoxWithScrollBar::createVScrollBar() if (Environment) skin = Environment->getSkin(); + s32 fontHeight = 1; + + if (m_override_font) { + fontHeight = m_override_font->getDimension(L"Ay").Height; + } else { + IGUIFont *font; + if (skin && (font = skin->getFont())) { + fontHeight = font->getDimension(L"Ay").Height; + } + } + m_scrollbar_width = skin ? skin->getSize(gui::EGDS_SCROLLBAR_SIZE) : 16; irr::core::rect<s32> scrollbarrect = m_frame_rect; @@ -628,8 +639,8 @@ void GUIEditBoxWithScrollBar::createVScrollBar() scrollbarrect, false, true); m_vscrollbar->setVisible(false); - m_vscrollbar->setSmallStep(1); - m_vscrollbar->setLargeStep(1); + m_vscrollbar->setSmallStep(3 * fontHeight); + m_vscrollbar->setLargeStep(10 * fontHeight); } @@ -641,26 +652,6 @@ void GUIEditBoxWithScrollBar::setBackgroundColor(const video::SColor &bg_color) m_bg_color_used = true; } -//! Writes attributes of the element. -void GUIEditBoxWithScrollBar::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options = 0) const -{ - out->addBool("Border", m_border); - out->addBool("Background", m_background); - // out->addFont("OverrideFont", OverrideFont); - - GUIEditBox::serializeAttributes(out, options); -} - - -//! Reads attributes of the element -void GUIEditBoxWithScrollBar::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options = 0) -{ - GUIEditBox::deserializeAttributes(in, options); - - setDrawBorder(in->getAttributeAsBool("Border")); - setDrawBackground(in->getAttributeAsBool("Background")); -} - bool GUIEditBoxWithScrollBar::isDrawBackgroundEnabled() const { return false; } bool GUIEditBoxWithScrollBar::isDrawBorderEnabled() const { return false; } void GUIEditBoxWithScrollBar::setCursorChar(const wchar_t cursorChar) { } diff --git a/src/gui/guiEditBoxWithScrollbar.h b/src/gui/guiEditBoxWithScrollbar.h index 3f7450dcb..cea482fc2 100644 --- a/src/gui/guiEditBoxWithScrollbar.h +++ b/src/gui/guiEditBoxWithScrollbar.h @@ -31,12 +31,6 @@ public: //! Change the background color virtual void setBackgroundColor(const video::SColor &bg_color); - //! Writes attributes of the element. - virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const; - - //! Reads attributes of the element - virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options); - virtual bool isDrawBackgroundEnabled() const; virtual bool isDrawBorderEnabled() const; virtual void setCursorChar(const wchar_t cursorChar); diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index 93463ad70..b65b31304 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -104,16 +104,22 @@ void MenuMusicFetcher::fetchSounds(const std::string &name, if(m_fetched.count(name)) return; m_fetched.insert(name); - std::string base; - base = porting::path_share + DIR_DELIM + "sounds"; - dst_paths.insert(base + DIR_DELIM + name + ".ogg"); - int i; - for(i=0; i<10; i++) - dst_paths.insert(base + DIR_DELIM + name + "."+itos(i)+".ogg"); - base = porting::path_user + DIR_DELIM + "sounds"; - dst_paths.insert(base + DIR_DELIM + name + ".ogg"); - for(i=0; i<10; i++) - dst_paths.insert(base + DIR_DELIM + name + "."+itos(i)+".ogg"); + std::vector<fs::DirListNode> list; + // Reusable local function + auto add_paths = [&dst_paths](const std::string name, const std::string base = "") { + dst_paths.insert(base + name + ".ogg"); + for (int i = 0; i < 10; i++) + dst_paths.insert(base + name + "." + itos(i) + ".ogg"); + }; + // Allow full paths + if (name.find(DIR_DELIM_CHAR) != std::string::npos) { + add_paths(name); + } else { + std::string share_prefix = porting::path_share + DIR_DELIM; + add_paths(name, share_prefix + "sounds" + DIR_DELIM); + std::string user_prefix = porting::path_user + DIR_DELIM; + add_paths(name, user_prefix + "sounds" + DIR_DELIM); + } } /******************************************************************************/ @@ -121,12 +127,14 @@ void MenuMusicFetcher::fetchSounds(const std::string &name, /******************************************************************************/ GUIEngine::GUIEngine(JoystickController *joystick, gui::IGUIElement *parent, + RenderingEngine *rendering_engine, IMenuManager *menumgr, MainMenuData *data, bool &kill) : + m_rendering_engine(rendering_engine), m_parent(parent), m_menumanager(menumgr), - m_smgr(RenderingEngine::get_scene_manager()), + m_smgr(rendering_engine->get_scene_manager()), m_data(data), m_kill(kill) { @@ -138,7 +146,7 @@ GUIEngine::GUIEngine(JoystickController *joystick, m_buttonhandler = new TextDestGuiEngine(this); //create texture source - m_texture_source = new MenuTextureSource(RenderingEngine::get_video_driver()); + m_texture_source = new MenuTextureSource(rendering_engine->get_video_driver()); //create soundmanager MenuMusicFetcher soundfetcher; @@ -156,7 +164,7 @@ GUIEngine::GUIEngine(JoystickController *joystick, g_fontengine->getTextHeight()); rect += v2s32(4, 0); - m_irr_toplefttext = gui::StaticText::add(RenderingEngine::get_gui_env(), + m_irr_toplefttext = gui::StaticText::add(rendering_engine->get_gui_env(), m_toplefttext, rect, false, true, 0, -1); //create formspecsource @@ -168,6 +176,7 @@ GUIEngine::GUIEngine(JoystickController *joystick, -1, m_menumanager, NULL /* &client */, + m_rendering_engine->get_gui_env(), m_texture_source, m_sound_manager, m_formspecgui, @@ -232,7 +241,7 @@ void GUIEngine::run() { // Always create clouds because they may or may not be // needed based on the game selected - video::IVideoDriver *driver = RenderingEngine::get_video_driver(); + video::IVideoDriver *driver = m_rendering_engine->get_video_driver(); cloudInit(); @@ -259,10 +268,10 @@ void GUIEngine::run() fog_pixelfog, fog_rangefog); } - while (RenderingEngine::run() && (!m_startgame) && (!m_kill)) { + while (m_rendering_engine->run() && (!m_startgame) && (!m_kill)) { const irr::core::dimension2d<u32> ¤t_screen_size = - RenderingEngine::get_video_driver()->getScreenSize(); + m_rendering_engine->get_video_driver()->getScreenSize(); // Verify if window size has changed and save it if it's the case // Ensure evaluating settings->getBool after verifying screensize // First condition is cheaper @@ -293,11 +302,11 @@ void GUIEngine::run() drawHeader(driver); drawFooter(driver); - RenderingEngine::get_gui_env()->drawAll(); + m_rendering_engine->get_gui_env()->drawAll(); driver->endScene(); - IrrlichtDevice *device = RenderingEngine::get_raw_device(); + IrrlichtDevice *device = m_rendering_engine->get_raw_device(); u32 frametime_min = 1000 / (device->isWindowFocused() ? g_settings->getFloat("fps_max") : g_settings->getFloat("fps_max_unfocused")); @@ -330,7 +339,7 @@ GUIEngine::~GUIEngine() //clean up texture pointers for (image_definition &texture : m_textures) { if (texture.texture) - RenderingEngine::get_video_driver()->removeTexture(texture.texture); + m_rendering_engine->get_video_driver()->removeTexture(texture.texture); } delete m_texture_source; @@ -350,13 +359,13 @@ void GUIEngine::cloudInit() v3f(0,0,0), v3f(0, 60, 100)); m_cloud.camera->setFarValue(10000); - m_cloud.lasttime = RenderingEngine::get_timer_time(); + m_cloud.lasttime = m_rendering_engine->get_timer_time(); } /******************************************************************************/ void GUIEngine::cloudPreProcess() { - u32 time = RenderingEngine::get_timer_time(); + u32 time = m_rendering_engine->get_timer_time(); if(time > m_cloud.lasttime) m_cloud.dtime = (time - m_cloud.lasttime) / 1000.0; @@ -377,7 +386,7 @@ void GUIEngine::cloudPostProcess(u32 frametime_min, IrrlichtDevice *device) u32 busytime_u32; // not using getRealTime is necessary for wine - u32 time = RenderingEngine::get_timer_time(); + u32 time = m_rendering_engine->get_timer_time(); if(time > m_cloud.lasttime) busytime_u32 = time - m_cloud.lasttime; else @@ -434,9 +443,22 @@ void GUIEngine::drawBackground(video::IVideoDriver *driver) return; } + // Chop background image to the smaller screen dimension + v2u32 bg_size = screensize; + v2f32 scale( + (f32) bg_size.X / sourcesize.X, + (f32) bg_size.Y / sourcesize.Y); + if (scale.X < scale.Y) + bg_size.X = (int) (scale.Y * sourcesize.X); + else + bg_size.Y = (int) (scale.X * sourcesize.Y); + v2s32 offset = v2s32( + (s32) screensize.X - (s32) bg_size.X, + (s32) screensize.Y - (s32) bg_size.Y + ) / 2; /* Draw background texture */ draw2DImageFilterScaled(driver, texture, - core::rect<s32>(0, 0, screensize.X, screensize.Y), + core::rect<s32>(offset.X, offset.Y, bg_size.X + offset.X, bg_size.Y + offset.Y), core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y), NULL, NULL, true); } @@ -528,7 +550,7 @@ void GUIEngine::drawFooter(video::IVideoDriver *driver) bool GUIEngine::setTexture(texture_layer layer, const std::string &texturepath, bool tile_image, unsigned int minsize) { - video::IVideoDriver *driver = RenderingEngine::get_video_driver(); + video::IVideoDriver *driver = m_rendering_engine->get_video_driver(); if (m_textures[layer].texture) { driver->removeTexture(m_textures[layer].texture); @@ -595,7 +617,7 @@ void GUIEngine::updateTopLeftTextSize() rect += v2s32(4, 0); m_irr_toplefttext->remove(); - m_irr_toplefttext = gui::StaticText::add(RenderingEngine::get_gui_env(), + m_irr_toplefttext = gui::StaticText::add(m_rendering_engine->get_gui_env(), m_toplefttext, rect, false, true, 0, -1); } @@ -611,10 +633,3 @@ void GUIEngine::stopSound(s32 handle) { m_sound_manager->stopSound(handle); } - -/******************************************************************************/ -unsigned int GUIEngine::queueAsync(const std::string &serialized_func, - const std::string &serialized_params) -{ - return m_script->queueAsync(serialized_func, serialized_params); -} diff --git a/src/gui/guiEngine.h b/src/gui/guiEngine.h index eef1ad8aa..d7e6485ef 100644 --- a/src/gui/guiEngine.h +++ b/src/gui/guiEngine.h @@ -50,6 +50,7 @@ struct image_definition { /* forward declarations */ /******************************************************************************/ class GUIEngine; +class RenderingEngine; class MainMenuScripting; class Clouds; struct MainMenuData; @@ -150,6 +151,7 @@ public: */ GUIEngine(JoystickController *joystick, gui::IGUIElement *parent, + RenderingEngine *rendering_engine, IMenuManager *menumgr, MainMenuData *data, bool &kill); @@ -173,10 +175,6 @@ public: return m_scriptdir; } - /** pass async callback to scriptengine **/ - unsigned int queueAsync(const std::string &serialized_fct, - const std::string &serialized_params); - private: /** find and run the main menu script */ @@ -188,6 +186,7 @@ private: /** update size of topleftext element */ void updateTopLeftTextSize(); + RenderingEngine *m_rendering_engine = nullptr; /** parent gui element */ gui::IGUIElement *m_parent = nullptr; /** manager to add menus to */ diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 5aa6dc9ae..85bd04900 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -65,7 +65,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiInventoryList.h" #include "guiItemImage.h" #include "guiScrollContainer.h" -#include "intlGUIEditBox.h" #include "guiHyperText.h" #include "guiScene.h" @@ -82,6 +81,13 @@ with this program; if not, write to the Free Software Foundation, Inc., " specified: \"" << parts[b] << "\"" << std::endl; \ return; \ } + +#define MY_CHECKCLIENT(a) \ + if (!m_client) { \ + errorstream << "Attempted to use element " << a << " with m_client == nullptr." << std::endl; \ + return; \ + } + /* GUIFormSpecMenu */ @@ -97,10 +103,10 @@ inline u32 clamp_u8(s32 value) GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick, gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, - Client *client, ISimpleTextureSource *tsrc, ISoundManager *sound_manager, - IFormSource *fsrc, TextDest *tdst, + Client *client, gui::IGUIEnvironment *guienv, ISimpleTextureSource *tsrc, + ISoundManager *sound_manager, IFormSource *fsrc, TextDest *tdst, const std::string &formspecPrepend, bool remap_dbl_click): - GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr, remap_dbl_click), + GUIModalMenu(guienv, parent, id, menumgr, remap_dbl_click), m_invmgr(client), m_tsrc(tsrc), m_sound_manager(sound_manager), @@ -146,12 +152,12 @@ GUIFormSpecMenu::~GUIFormSpecMenu() } void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client, - JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest, - const std::string &formspecPrepend, ISoundManager *sound_manager) + gui::IGUIEnvironment *guienv, JoystickController *joystick, IFormSource *fs_src, + TextDest *txt_dest, const std::string &formspecPrepend, ISoundManager *sound_manager) { if (cur_formspec == nullptr) { cur_formspec = new GUIFormSpecMenu(joystick, guiroot, -1, &g_menumgr, - client, client->getTextureSource(), sound_manager, fs_src, + client, guienv, client->getTextureSource(), sound_manager, fs_src, txt_dest, formspecPrepend); cur_formspec->doPause = false; @@ -295,8 +301,20 @@ v2s32 GUIFormSpecMenu::getRealCoordinateGeometry(const std::vector<std::string> return v2s32(stof(v_geom[0]) * imgsize.X, stof(v_geom[1]) * imgsize.Y); } +bool GUIFormSpecMenu::precheckElement(const std::string &name, const std::string &element, + size_t args_min, size_t args_max, std::vector<std::string> &parts) +{ + parts = split(element, ';'); + if (parts.size() >= args_min && (parts.size() <= args_max || m_formspec_version > FORMSPEC_API_VERSION)) + return true; + + errorstream << "Invalid " << name << " element(" << parts.size() << "): '" << element << "'" << std::endl; + return false; +} + void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element) { + // Note: do not use precheckElement due to "," separator. std::vector<std::string> parts = split(element,','); if (((parts.size() == 2) || parts.size() == 3) || @@ -309,7 +327,7 @@ void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element) data->invsize.Y = MYMAX(0, stof(parts[1])); lockSize(false); -#ifndef __ANDROID__ +#ifndef HAVE_TOUCHSCREENGUI if (parts.size() == 3) { if (parts[2] == "true") { lockSize(true,v2u32(800,600)); @@ -350,14 +368,9 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data) void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string &element) { - std::vector<std::string> parts = split(element, ';'); - - if (parts.size() < 4 || - (parts.size() > 5 && m_formspec_version <= FORMSPEC_API_VERSION)) { - errorstream << "Invalid scroll_container start element (" << parts.size() - << "): '" << element << "'" << std::endl; + std::vector<std::string> parts; + if (!precheckElement("scroll_container start", element, 4, 5, parts)) return; - } std::vector<std::string> v_pos = split(parts[0], ','); std::vector<std::string> v_geom = split(parts[1], ','); @@ -446,105 +459,95 @@ void GUIFormSpecMenu::parseScrollContainerEnd(parserData *data) void GUIFormSpecMenu::parseList(parserData *data, const std::string &element) { - if (m_client == 0) { - warningstream<<"invalid use of 'list' with m_client==0"<<std::endl; - return; - } + MY_CHECKCLIENT("list"); - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts; + if (!precheckElement("list", element, 4, 5, parts)) + return; - if (((parts.size() == 4) || (parts.size() == 5)) || - ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::string location = parts[0]; - std::string listname = parts[1]; - std::vector<std::string> v_pos = split(parts[2],','); - std::vector<std::string> v_geom = split(parts[3],','); - std::string startindex; - if (parts.size() == 5) - startindex = parts[4]; + std::string location = parts[0]; + std::string listname = parts[1]; + std::vector<std::string> v_pos = split(parts[2],','); + std::vector<std::string> v_geom = split(parts[3],','); + std::string startindex; + if (parts.size() == 5) + startindex = parts[4]; - MY_CHECKPOS("list",2); - MY_CHECKGEOM("list",3); + MY_CHECKPOS("list",2); + MY_CHECKGEOM("list",3); - InventoryLocation loc; + InventoryLocation loc; - if (location == "context" || location == "current_name") - loc = m_current_inventory_location; - else - loc.deSerialize(location); + if (location == "context" || location == "current_name") + loc = m_current_inventory_location; + else + loc.deSerialize(location); - v2s32 geom; - geom.X = stoi(v_geom[0]); - geom.Y = stoi(v_geom[1]); + v2s32 geom; + geom.X = stoi(v_geom[0]); + geom.Y = stoi(v_geom[1]); - s32 start_i = 0; - if (!startindex.empty()) - start_i = stoi(startindex); + s32 start_i = 0; + if (!startindex.empty()) + start_i = stoi(startindex); - if (geom.X < 0 || geom.Y < 0 || start_i < 0) { - errorstream << "Invalid list element: '" << element << "'" << std::endl; - return; - } + if (geom.X < 0 || geom.Y < 0 || start_i < 0) { + errorstream << "Invalid list element: '" << element << "'" << std::endl; + return; + } - if (!data->explicit_size) - warningstream << "invalid use of list without a size[] element" << std::endl; + if (!data->explicit_size) + warningstream << "invalid use of list without a size[] element" << std::endl; - FieldSpec spec( - "", - L"", - L"", - 258 + m_fields.size(), - 3 - ); + FieldSpec spec( + "", + L"", + L"", + 258 + m_fields.size(), + 3 + ); - auto style = getDefaultStyleForElement("list", spec.fname); + auto style = getDefaultStyleForElement("list", spec.fname); - v2f32 slot_scale = style.getVector2f(StyleSpec::SIZE, v2f32(0, 0)); - v2f32 slot_size( - slot_scale.X <= 0 ? imgsize.X : std::max<f32>(slot_scale.X * imgsize.X, 1), - slot_scale.Y <= 0 ? imgsize.Y : std::max<f32>(slot_scale.Y * imgsize.Y, 1) - ); + v2f32 slot_scale = style.getVector2f(StyleSpec::SIZE, v2f32(0, 0)); + v2f32 slot_size( + slot_scale.X <= 0 ? imgsize.X : std::max<f32>(slot_scale.X * imgsize.X, 1), + slot_scale.Y <= 0 ? imgsize.Y : std::max<f32>(slot_scale.Y * imgsize.Y, 1) + ); - v2f32 slot_spacing = style.getVector2f(StyleSpec::SPACING, v2f32(-1, -1)); - v2f32 default_spacing = data->real_coordinates ? - v2f32(imgsize.X * 0.25f, imgsize.Y * 0.25f) : - v2f32(spacing.X - imgsize.X, spacing.Y - imgsize.Y); + v2f32 slot_spacing = style.getVector2f(StyleSpec::SPACING, v2f32(-1, -1)); + v2f32 default_spacing = data->real_coordinates ? + v2f32(imgsize.X * 0.25f, imgsize.Y * 0.25f) : + v2f32(spacing.X - imgsize.X, spacing.Y - imgsize.Y); - slot_spacing.X = slot_spacing.X < 0 ? default_spacing.X : - imgsize.X * slot_spacing.X; - slot_spacing.Y = slot_spacing.Y < 0 ? default_spacing.Y : - imgsize.Y * slot_spacing.Y; + slot_spacing.X = slot_spacing.X < 0 ? default_spacing.X : + imgsize.X * slot_spacing.X; + slot_spacing.Y = slot_spacing.Y < 0 ? default_spacing.Y : + imgsize.Y * slot_spacing.Y; - slot_spacing += slot_size; + slot_spacing += slot_size; - v2s32 pos = data->real_coordinates ? getRealCoordinateBasePos(v_pos) : - getElementBasePos(&v_pos); + v2s32 pos = data->real_coordinates ? getRealCoordinateBasePos(v_pos) : + getElementBasePos(&v_pos); - core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, - pos.X + (geom.X - 1) * slot_spacing.X + slot_size.X, - pos.Y + (geom.Y - 1) * slot_spacing.Y + slot_size.Y); + core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, + pos.X + (geom.X - 1) * slot_spacing.X + slot_size.X, + pos.Y + (geom.Y - 1) * slot_spacing.Y + slot_size.Y); - GUIInventoryList *e = new GUIInventoryList(Environment, data->current_parent, - spec.fid, rect, m_invmgr, loc, listname, geom, start_i, - v2s32(slot_size.X, slot_size.Y), 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, + v2s32(slot_size.X, slot_size.Y), slot_spacing, this, + data->inventorylist_options, m_font); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - m_inventorylists.push_back(e); - m_fields.push_back(spec); - return; - } - errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl; + m_inventorylists.push_back(e); + m_fields.push_back(spec); } void GUIFormSpecMenu::parseListRing(parserData *data, const std::string &element) { - if (m_client == 0) { - errorstream << "WARNING: invalid use of 'listring' with m_client==0" << std::endl; - return; - } + MY_CHECKCLIENT("listring"); std::vector<std::string> parts = split(element, ';'); @@ -579,157 +582,150 @@ void GUIFormSpecMenu::parseListRing(parserData *data, const std::string &element void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); - - if (((parts.size() >= 3) && (parts.size() <= 4)) || - ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector<std::string> v_pos = split(parts[0],','); - std::string name = parts[1]; - std::string label = parts[2]; - std::string selected; + std::vector<std::string> parts; + if (!precheckElement("checkbox", element, 3, 4, parts)) + return; - if (parts.size() >= 4) - selected = parts[3]; + std::vector<std::string> v_pos = split(parts[0],','); + std::string name = parts[1]; + std::string label = parts[2]; + std::string selected; - MY_CHECKPOS("checkbox",0); + if (parts.size() >= 4) + selected = parts[3]; - bool fselected = false; + MY_CHECKPOS("checkbox",0); - if (selected == "true") - fselected = true; + bool fselected = false; - std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); - const core::dimension2d<u32> label_size = m_font->getDimension(wlabel.c_str()); - s32 cb_size = Environment->getSkin()->getSize(gui::EGDS_CHECK_BOX_WIDTH); - s32 y_center = (std::max(label_size.Height, (u32)cb_size) + 1) / 2; + if (selected == "true") + fselected = true; - v2s32 pos; - core::rect<s32> rect; + std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); + const core::dimension2d<u32> label_size = m_font->getDimension(wlabel.c_str()); + s32 cb_size = Environment->getSkin()->getSize(gui::EGDS_CHECK_BOX_WIDTH); + s32 y_center = (std::max(label_size.Height, (u32)cb_size) + 1) / 2; - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); + v2s32 pos; + core::rect<s32> rect; - rect = core::rect<s32>( - pos.X, - pos.Y - y_center, - pos.X + label_size.Width + cb_size + 7, - pos.Y + y_center - ); - } else { - pos = getElementBasePos(&v_pos); - rect = core::rect<s32>( - pos.X, - pos.Y + imgsize.Y / 2 - y_center, - pos.X + label_size.Width + cb_size + 7, - pos.Y + imgsize.Y / 2 + y_center - ); - } + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); - FieldSpec spec( - name, - wlabel, //Needed for displaying text on MSVC - wlabel, - 258+m_fields.size() + rect = core::rect<s32>( + pos.X, + pos.Y - y_center, + pos.X + label_size.Width + cb_size + 7, + pos.Y + y_center + ); + } else { + pos = getElementBasePos(&v_pos); + rect = core::rect<s32>( + pos.X, + pos.Y + imgsize.Y / 2 - y_center, + pos.X + label_size.Width + cb_size + 7, + pos.Y + imgsize.Y / 2 + y_center ); + } - spec.ftype = f_CheckBox; + FieldSpec spec( + name, + wlabel, //Needed for displaying text on MSVC + wlabel, + 258+m_fields.size() + ); - gui::IGUICheckBox *e = Environment->addCheckBox(fselected, rect, - data->current_parent, spec.fid, spec.flabel.c_str()); + spec.ftype = f_CheckBox; - auto style = getDefaultStyleForElement("checkbox", name); + gui::IGUICheckBox *e = Environment->addCheckBox(fselected, rect, + data->current_parent, spec.fid, spec.flabel.c_str()); - spec.sound = style.get(StyleSpec::Property::SOUND, ""); + auto style = getDefaultStyleForElement("checkbox", name); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + spec.sound = style.get(StyleSpec::Property::SOUND, ""); - if (spec.fname == m_focused_element) { - Environment->setFocus(e); - } + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - e->grab(); - m_checkboxes.emplace_back(spec, e); - m_fields.push_back(spec); - return; + if (spec.fname == m_focused_element) { + Environment->setFocus(e); } - errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'" << std::endl; + + e->grab(); + m_checkboxes.emplace_back(spec, e); + m_fields.push_back(spec); } void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts; + if (!precheckElement("scrollbar", element, 5, 5, parts)) + return; - if (parts.size() >= 5) { - std::vector<std::string> v_pos = split(parts[0],','); - std::vector<std::string> v_geom = split(parts[1],','); - std::string name = parts[3]; - std::string value = parts[4]; + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> v_geom = split(parts[1],','); + std::string name = parts[3]; + std::string value = parts[4]; - MY_CHECKPOS("scrollbar",0); - MY_CHECKGEOM("scrollbar",1); + MY_CHECKPOS("scrollbar",0); + MY_CHECKGEOM("scrollbar",1); - v2s32 pos; - v2s32 dim; + v2s32 pos; + v2s32 dim; - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - dim = getRealCoordinateGeometry(v_geom); - } else { - pos = getElementBasePos(&v_pos); - dim.X = stof(v_geom[0]) * spacing.X; - dim.Y = stof(v_geom[1]) * spacing.Y; - } + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + dim = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + dim.X = stof(v_geom[0]) * spacing.X; + dim.Y = stof(v_geom[1]) * spacing.Y; + } - core::rect<s32> rect = - core::rect<s32>(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y); + core::rect<s32> rect = + core::rect<s32>(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y); - FieldSpec spec( - name, - L"", - L"", - 258+m_fields.size() - ); + FieldSpec spec( + name, + L"", + L"", + 258+m_fields.size() + ); - bool is_horizontal = true; + bool is_horizontal = true; - if (parts[2] == "vertical") - is_horizontal = false; + if (parts[2] == "vertical") + is_horizontal = false; - spec.ftype = f_ScrollBar; - spec.send = true; - GUIScrollBar *e = new GUIScrollBar(Environment, data->current_parent, - spec.fid, rect, is_horizontal, true); + spec.ftype = f_ScrollBar; + spec.send = 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)); - e->setArrowsVisible(data->scrollbar_options.arrow_visiblity); + auto style = getDefaultStyleForElement("scrollbar", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setArrowsVisible(data->scrollbar_options.arrow_visiblity); - s32 max = data->scrollbar_options.max; - s32 min = data->scrollbar_options.min; + s32 max = data->scrollbar_options.max; + s32 min = data->scrollbar_options.min; - e->setMax(max); - e->setMin(min); + e->setMax(max); + e->setMin(min); - e->setPos(stoi(parts[4])); + e->setPos(stoi(parts[4])); - e->setSmallStep(data->scrollbar_options.small_step); - e->setLargeStep(data->scrollbar_options.large_step); + e->setSmallStep(data->scrollbar_options.small_step); + e->setLargeStep(data->scrollbar_options.large_step); - s32 scrollbar_size = is_horizontal ? dim.X : dim.Y; + s32 scrollbar_size = is_horizontal ? dim.X : dim.Y; - e->setPageSize(scrollbar_size * (max - min + 1) / data->scrollbar_options.thumb_size); + e->setPageSize(scrollbar_size * (max - min + 1) / data->scrollbar_options.thumb_size); - if (spec.fname == m_focused_element) { - Environment->setFocus(e); - } - - m_scrollbars.emplace_back(spec,e); - m_fields.push_back(spec); - return; + if (spec.fname == m_focused_element) { + Environment->setFocus(e); } - errorstream << "Invalid scrollbar element(" << parts.size() << "): '" << element - << "'" << std::endl; + + m_scrollbars.emplace_back(spec,e); + m_fields.push_back(spec); } void GUIFormSpecMenu::parseScrollBarOptions(parserData* data, const std::string &element) @@ -787,11 +783,11 @@ void GUIFormSpecMenu::parseScrollBarOptions(parserData* data, const std::string void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts; + if (!precheckElement("image", element, 2, 3, parts)) + return; - if ((parts.size() == 3) || - ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION))) - { + if (parts.size() >= 3) { std::vector<std::string> v_pos = split(parts[0],','); std::vector<std::string> v_geom = split(parts[1],','); std::string name = unescape_string(parts[2]); @@ -843,54 +839,47 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) return; } - if (parts.size() == 2) { - std::vector<std::string> v_pos = split(parts[0],','); - std::string name = unescape_string(parts[1]); - - MY_CHECKPOS("image", 0); + // Else: 2 arguments in "parts" - v2s32 pos = getElementBasePos(&v_pos); + std::vector<std::string> v_pos = split(parts[0],','); + std::string name = unescape_string(parts[1]); - if (!data->explicit_size) - warningstream<<"invalid use of image without a size[] element"<<std::endl; + MY_CHECKPOS("image", 0); - video::ITexture *texture = m_tsrc->getTexture(name); - if (!texture) { - errorstream << "GUIFormSpecMenu::parseImage() Unable to load texture:" - << std::endl << "\t" << name << std::endl; - return; - } + v2s32 pos = getElementBasePos(&v_pos); - FieldSpec spec( - name, - L"", - L"", - 258 + m_fields.size() - ); - 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); + if (!data->explicit_size) + warningstream<<"invalid use of image without a size[] element"<<std::endl; - // images should let events through - e->grab(); - m_clickthrough_elements.push_back(e); + video::ITexture *texture = m_tsrc->getTexture(name); + if (!texture) { + errorstream << "GUIFormSpecMenu::parseImage() Unable to load texture:" + << std::endl << "\t" << name << std::endl; return; } - errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'" << std::endl; + + FieldSpec spec( + name, + L"", + L"", + 258 + m_fields.size() + ); + 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); + + // images should let events through + e->grab(); + m_clickthrough_elements.push_back(e); } void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &element) { - std::vector<std::string> parts = split(element, ';'); - - if (parts.size() != 6 && parts.size() != 7 && - !(parts.size() > 7 && m_formspec_version > FORMSPEC_API_VERSION)) { - errorstream << "Invalid animated_image element(" << parts.size() - << "): '" << element << "'" << std::endl; + std::vector<std::string> parts; + if (!precheckElement("animated_image", element, 6, 7, parts)) return; - } std::vector<std::string> v_pos = split(parts[0], ','); std::vector<std::string> v_geom = split(parts[1], ','); @@ -945,218 +934,207 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts; + if (!precheckElement("item_image", element, 3, 3, parts)) + return; - if ((parts.size() == 3) || - ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector<std::string> v_pos = split(parts[0],','); - std::vector<std::string> v_geom = split(parts[1],','); - std::string name = parts[2]; + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> v_geom = split(parts[1],','); + std::string name = parts[2]; - MY_CHECKPOS("itemimage",0); - MY_CHECKGEOM("itemimage",1); + MY_CHECKPOS("item_image",0); + MY_CHECKGEOM("item_image",1); - v2s32 pos; - v2s32 geom; + v2s32 pos; + v2s32 geom; - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); - } else { - pos = getElementBasePos(&v_pos); - geom.X = stof(v_geom[0]) * (float)imgsize.X; - geom.Y = stof(v_geom[1]) * (float)imgsize.Y; - } + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + geom.X = stof(v_geom[0]) * (float)imgsize.X; + geom.Y = stof(v_geom[1]) * (float)imgsize.Y; + } - if(!data->explicit_size) - warningstream<<"invalid use of item_image without a size[] element"<<std::endl; + if(!data->explicit_size) + warningstream<<"invalid use of item_image without a size[] element"<<std::endl; - FieldSpec spec( - "", - L"", - L"", - 258 + m_fields.size(), - 2 - ); - spec.ftype = f_ItemImage; + FieldSpec spec( + "", + L"", + L"", + 258 + m_fields.size(), + 2 + ); + spec.ftype = f_ItemImage; - GUIItemImage *e = new GUIItemImage(Environment, data->current_parent, spec.fid, - core::rect<s32>(pos, pos + geom), name, m_font, m_client); - auto style = getDefaultStyleForElement("item_image", spec.fname); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + GUIItemImage *e = new GUIItemImage(Environment, data->current_parent, spec.fid, + core::rect<s32>(pos, pos + geom), name, m_font, m_client); + auto style = getDefaultStyleForElement("item_image", spec.fname); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - // item images should let events through - m_clickthrough_elements.push_back(e); + // item images should let events through + m_clickthrough_elements.push_back(e); - m_fields.push_back(spec); - return; - } - errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'" << std::endl; + m_fields.push_back(spec); } void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, const std::string &type) { - std::vector<std::string> parts = split(element,';'); - - if ((parts.size() == 4) || - ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector<std::string> v_pos = split(parts[0],','); - std::vector<std::string> v_geom = split(parts[1],','); - std::string name = parts[2]; - std::string label = parts[3]; + std::vector<std::string> parts; + if (!precheckElement("button", element, 4, 4, parts)) + return; - MY_CHECKPOS("button",0); - MY_CHECKGEOM("button",1); + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> v_geom = split(parts[1],','); + std::string name = parts[2]; + std::string label = parts[3]; - v2s32 pos; - v2s32 geom; - core::rect<s32> rect; + MY_CHECKPOS("button",0); + MY_CHECKGEOM("button",1); - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); - rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, - pos.Y+geom.Y); - } else { - pos = getElementBasePos(&v_pos); - geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); - pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2; + v2s32 pos; + v2s32 geom; + core::rect<s32> rect; - rect = core::rect<s32>(pos.X, pos.Y - m_btn_height, - pos.X + geom.X, pos.Y + m_btn_height); - } + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, + pos.Y+geom.Y); + } else { + pos = getElementBasePos(&v_pos); + geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); + pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2; - if(!data->explicit_size) - warningstream<<"invalid use of button without a size[] element"<<std::endl; + rect = core::rect<s32>(pos.X, pos.Y - m_btn_height, + pos.X + geom.X, pos.Y + m_btn_height); + } - std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); + if(!data->explicit_size) + warningstream<<"invalid use of button without a size[] element"<<std::endl; - FieldSpec spec( - name, - wlabel, - L"", - 258 + m_fields.size() - ); - spec.ftype = f_Button; - if(type == "button_exit") - spec.is_exit = true; + std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); - GUIButton *e = GUIButton::addButton(Environment, rect, m_tsrc, - data->current_parent, spec.fid, spec.flabel.c_str()); + FieldSpec spec( + name, + wlabel, + L"", + 258 + m_fields.size() + ); + spec.ftype = f_Button; + if(type == "button_exit") + spec.is_exit = true; - auto style = getStyleForElement(type, name, (type != "button") ? "button" : ""); + GUIButton *e = GUIButton::addButton(Environment, rect, m_tsrc, + data->current_parent, spec.fid, spec.flabel.c_str()); - spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); + auto style = getStyleForElement(type, name, (type != "button") ? "button" : ""); - e->setStyles(style); + spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); - if (spec.fname == m_focused_element) { - Environment->setFocus(e); - } + e->setStyles(style); - m_fields.push_back(spec); - return; + if (spec.fname == m_focused_element) { + Environment->setFocus(e); } - errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'" << std::endl; + + m_fields.push_back(spec); } void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts; + if (!precheckElement("background", element, 3, 5, parts)) + return; - if ((parts.size() >= 3 && parts.size() <= 5) || - (parts.size() > 5 && m_formspec_version > FORMSPEC_API_VERSION)) { - std::vector<std::string> v_pos = split(parts[0],','); - std::vector<std::string> v_geom = split(parts[1],','); - std::string name = unescape_string(parts[2]); + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> v_geom = split(parts[1],','); + std::string name = unescape_string(parts[2]); - MY_CHECKPOS("background",0); - MY_CHECKGEOM("background",1); + MY_CHECKPOS("background",0); + MY_CHECKGEOM("background",1); - v2s32 pos; - v2s32 geom; + v2s32 pos; + v2s32 geom; - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); - } else { - pos = getElementBasePos(&v_pos); - pos.X -= (spacing.X - (float)imgsize.X) / 2; - pos.Y -= (spacing.Y - (float)imgsize.Y) / 2; + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + pos.X -= (spacing.X - (float)imgsize.X) / 2; + pos.Y -= (spacing.Y - (float)imgsize.Y) / 2; - geom.X = stof(v_geom[0]) * spacing.X; - geom.Y = stof(v_geom[1]) * spacing.Y; - } + geom.X = stof(v_geom[0]) * spacing.X; + geom.Y = stof(v_geom[1]) * spacing.Y; + } - bool clip = false; - if (parts.size() >= 4 && is_yes(parts[3])) { - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos) * -1; - geom = v2s32(0, 0); - } else { - pos.X = stoi(v_pos[0]); //acts as offset - pos.Y = stoi(v_pos[1]); - } - clip = true; + bool clip = false; + if (parts.size() >= 4 && is_yes(parts[3])) { + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos) * -1; + geom = v2s32(0, 0); + } else { + pos.X = stoi(v_pos[0]); //acts as offset + pos.Y = stoi(v_pos[1]); } + clip = true; + } - core::rect<s32> middle; - if (parts.size() >= 5) { - std::vector<std::string> v_middle = split(parts[4], ','); - if (v_middle.size() == 1) { - s32 x = stoi(v_middle[0]); - middle.UpperLeftCorner = core::vector2di(x, x); - middle.LowerRightCorner = core::vector2di(-x, -x); - } else if (v_middle.size() == 2) { - s32 x = stoi(v_middle[0]); - s32 y = stoi(v_middle[1]); - middle.UpperLeftCorner = core::vector2di(x, y); - middle.LowerRightCorner = core::vector2di(-x, -y); - // `-x` is interpreted as `w - x` - } else if (v_middle.size() == 4) { - middle.UpperLeftCorner = core::vector2di(stoi(v_middle[0]), stoi(v_middle[1])); - middle.LowerRightCorner = core::vector2di(stoi(v_middle[2]), stoi(v_middle[3])); - } else { - warningstream << "Invalid rectangle given to middle param of background[] element" << std::endl; - } + core::rect<s32> middle; + if (parts.size() >= 5) { + std::vector<std::string> v_middle = split(parts[4], ','); + if (v_middle.size() == 1) { + s32 x = stoi(v_middle[0]); + middle.UpperLeftCorner = core::vector2di(x, x); + middle.LowerRightCorner = core::vector2di(-x, -x); + } else if (v_middle.size() == 2) { + s32 x = stoi(v_middle[0]); + s32 y = stoi(v_middle[1]); + middle.UpperLeftCorner = core::vector2di(x, y); + middle.LowerRightCorner = core::vector2di(-x, -y); + // `-x` is interpreted as `w - x` + } else if (v_middle.size() == 4) { + middle.UpperLeftCorner = core::vector2di(stoi(v_middle[0]), stoi(v_middle[1])); + middle.LowerRightCorner = core::vector2di(stoi(v_middle[2]), stoi(v_middle[3])); + } else { + warningstream << "Invalid rectangle given to middle param of background[] element" << std::endl; } + } - if (!data->explicit_size && !clip) - warningstream << "invalid use of unclipped background without a size[] element" << std::endl; + if (!data->explicit_size && !clip) + warningstream << "invalid use of unclipped background without a size[] element" << std::endl; - FieldSpec spec( - name, - L"", - L"", - 258 + m_fields.size() - ); + FieldSpec spec( + name, + L"", + L"", + 258 + m_fields.size() + ); - core::rect<s32> rect; - if (!clip) { - // no auto_clip => position like normal image - rect = core::rect<s32>(pos, pos + geom); - } else { - // it will be auto-clipped when drawing - rect = core::rect<s32>(-pos, pos); - } + core::rect<s32> rect; + if (!clip) { + // no auto_clip => position like normal image + rect = core::rect<s32>(pos, pos + geom); + } else { + // it will be auto-clipped when drawing + rect = core::rect<s32>(-pos, pos); + } - GUIBackgroundImage *e = new GUIBackgroundImage(Environment, this, spec.fid, - rect, name, middle, m_tsrc, clip); + GUIBackgroundImage *e = new GUIBackgroundImage(Environment, this, spec.fid, + rect, name, middle, m_tsrc, clip); - FATAL_ERROR_IF(!e, "Failed to create background formspec element"); + FATAL_ERROR_IF(!e, "Failed to create background formspec element"); - e->setNotClipped(true); + e->setNotClipped(true); - e->setVisible(false); // the element is drawn manually before all others + e->setVisible(false); // the element is drawn manually before all others - m_backgrounds.push_back(e); - m_fields.push_back(spec); - return; - } - errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'" << std::endl; + m_backgrounds.push_back(e); + m_fields.push_back(spec); } void GUIFormSpecMenu::parseTableOptions(parserData* data, const std::string &element) @@ -1193,338 +1171,320 @@ void GUIFormSpecMenu::parseTableColumns(parserData* data, const std::string &ele void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts; + if (!precheckElement("table", element, 4, 5, parts)) + return; - if (((parts.size() == 4) || (parts.size() == 5)) || - ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector<std::string> v_pos = split(parts[0],','); - std::vector<std::string> v_geom = split(parts[1],','); - std::string name = parts[2]; - std::vector<std::string> items = split(parts[3],','); - std::string str_initial_selection; - std::string str_transparent = "false"; + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> v_geom = split(parts[1],','); + std::string name = parts[2]; + std::vector<std::string> items = split(parts[3],','); + std::string str_initial_selection; + std::string str_transparent = "false"; - if (parts.size() >= 5) - str_initial_selection = parts[4]; + if (parts.size() >= 5) + str_initial_selection = parts[4]; - MY_CHECKPOS("table",0); - MY_CHECKGEOM("table",1); + MY_CHECKPOS("table",0); + MY_CHECKGEOM("table",1); - v2s32 pos; - v2s32 geom; + v2s32 pos; + v2s32 geom; - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); - } else { - pos = getElementBasePos(&v_pos); - geom.X = stof(v_geom[0]) * spacing.X; - geom.Y = stof(v_geom[1]) * spacing.Y; - } + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + geom.X = stof(v_geom[0]) * spacing.X; + geom.Y = stof(v_geom[1]) * spacing.Y; + } - core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); + core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - FieldSpec spec( - name, - L"", - L"", - 258 + m_fields.size() - ); + FieldSpec spec( + name, + L"", + L"", + 258 + m_fields.size() + ); - spec.ftype = f_Table; + spec.ftype = f_Table; - for (std::string &item : items) { - item = wide_to_utf8(unescape_translate(utf8_to_wide(unescape_string(item)))); - } + for (std::string &item : items) { + item = wide_to_utf8(unescape_translate(utf8_to_wide(unescape_string(item)))); + } - //now really show table - GUITable *e = new GUITable(Environment, data->current_parent, spec.fid, - rect, m_tsrc); + //now really show table + GUITable *e = new GUITable(Environment, data->current_parent, spec.fid, + rect, m_tsrc); - if (spec.fname == m_focused_element) { - Environment->setFocus(e); - } + if (spec.fname == m_focused_element) { + Environment->setFocus(e); + } - e->setTable(data->table_options, data->table_columns, items); + e->setTable(data->table_options, data->table_columns, items); - if (data->table_dyndata.find(name) != data->table_dyndata.end()) { - e->setDynamicData(data->table_dyndata[name]); - } + if (data->table_dyndata.find(name) != data->table_dyndata.end()) { + e->setDynamicData(data->table_dyndata[name]); + } - if (!str_initial_selection.empty() && str_initial_selection != "0") - e->setSelected(stoi(str_initial_selection)); + if (!str_initial_selection.empty() && str_initial_selection != "0") + e->setSelected(stoi(str_initial_selection)); - auto style = getDefaultStyleForElement("table", name); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - e->setOverrideFont(style.getFont()); + auto style = getDefaultStyleForElement("table", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setOverrideFont(style.getFont()); - m_tables.emplace_back(spec, e); - m_fields.push_back(spec); - return; - } - errorstream<< "Invalid table element(" << parts.size() << "): '" << element << "'" << std::endl; + m_tables.emplace_back(spec, e); + m_fields.push_back(spec); } void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts; + if (!precheckElement("textlist", element, 4, 6, parts)) + return; - if (((parts.size() == 4) || (parts.size() == 5) || (parts.size() == 6)) || - ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector<std::string> v_pos = split(parts[0],','); - std::vector<std::string> v_geom = split(parts[1],','); - std::string name = parts[2]; - std::vector<std::string> items = split(parts[3],','); - std::string str_initial_selection; - std::string str_transparent = "false"; + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> v_geom = split(parts[1],','); + std::string name = parts[2]; + std::vector<std::string> items = split(parts[3],','); + std::string str_initial_selection; + std::string str_transparent = "false"; - if (parts.size() >= 5) - str_initial_selection = parts[4]; + if (parts.size() >= 5) + str_initial_selection = parts[4]; - if (parts.size() >= 6) - str_transparent = parts[5]; + if (parts.size() >= 6) + str_transparent = parts[5]; - MY_CHECKPOS("textlist",0); - MY_CHECKGEOM("textlist",1); + MY_CHECKPOS("textlist",0); + MY_CHECKGEOM("textlist",1); - v2s32 pos; - v2s32 geom; + v2s32 pos; + v2s32 geom; - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); - } else { - pos = getElementBasePos(&v_pos); - geom.X = stof(v_geom[0]) * spacing.X; - geom.Y = stof(v_geom[1]) * spacing.Y; - } + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + geom.X = stof(v_geom[0]) * spacing.X; + geom.Y = stof(v_geom[1]) * spacing.Y; + } - core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); + core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - FieldSpec spec( - name, - L"", - L"", - 258 + m_fields.size() - ); + FieldSpec spec( + name, + L"", + L"", + 258 + m_fields.size() + ); - spec.ftype = f_Table; + spec.ftype = f_Table; - for (std::string &item : items) { - item = wide_to_utf8(unescape_translate(utf8_to_wide(unescape_string(item)))); - } + for (std::string &item : items) { + item = wide_to_utf8(unescape_translate(utf8_to_wide(unescape_string(item)))); + } - //now really show list - GUITable *e = new GUITable(Environment, data->current_parent, spec.fid, - rect, m_tsrc); + //now really show list + GUITable *e = new GUITable(Environment, data->current_parent, spec.fid, + rect, m_tsrc); - if (spec.fname == m_focused_element) { - Environment->setFocus(e); - } + if (spec.fname == m_focused_element) { + Environment->setFocus(e); + } - e->setTextList(items, is_yes(str_transparent)); + e->setTextList(items, is_yes(str_transparent)); - if (data->table_dyndata.find(name) != data->table_dyndata.end()) { - e->setDynamicData(data->table_dyndata[name]); - } + if (data->table_dyndata.find(name) != data->table_dyndata.end()) { + e->setDynamicData(data->table_dyndata[name]); + } - if (!str_initial_selection.empty() && str_initial_selection != "0") - e->setSelected(stoi(str_initial_selection)); + if (!str_initial_selection.empty() && str_initial_selection != "0") + e->setSelected(stoi(str_initial_selection)); - auto style = getDefaultStyleForElement("textlist", name); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - e->setOverrideFont(style.getFont()); + auto style = getDefaultStyleForElement("textlist", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setOverrideFont(style.getFont()); - m_tables.emplace_back(spec, e); - m_fields.push_back(spec); - return; - } - errorstream<< "Invalid textlist element(" << parts.size() << "): '" << element << "'" << std::endl; + m_tables.emplace_back(spec, e); + m_fields.push_back(spec); } void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element, ';'); - - if (parts.size() == 5 || parts.size() == 6 || - (parts.size() > 6 && m_formspec_version > FORMSPEC_API_VERSION)) - { - std::vector<std::string> v_pos = split(parts[0], ','); - std::string name = parts[2]; - std::vector<std::string> items = split(parts[3], ','); - std::string str_initial_selection = parts[4]; + std::vector<std::string> parts; + if (!precheckElement("dropdown", element, 5, 6, parts)) + return; - if (parts.size() >= 6 && is_yes(parts[5])) - m_dropdown_index_event[name] = true; + std::vector<std::string> v_pos = split(parts[0], ','); + std::string name = parts[2]; + std::vector<std::string> items = split(parts[3], ','); + std::string str_initial_selection = parts[4]; - MY_CHECKPOS("dropdown",0); + if (parts.size() >= 6 && is_yes(parts[5])) + m_dropdown_index_event[name] = true; - v2s32 pos; - v2s32 geom; - core::rect<s32> rect; + MY_CHECKPOS("dropdown",0); - if (data->real_coordinates) { - std::vector<std::string> v_geom = split(parts[1],','); + v2s32 pos; + v2s32 geom; + core::rect<s32> rect; - if (v_geom.size() == 1) - v_geom.emplace_back("1"); + if (data->real_coordinates) { + std::vector<std::string> v_geom = split(parts[1],','); - MY_CHECKGEOM("dropdown",1); + if (v_geom.size() == 1) + v_geom.emplace_back("1"); - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); - rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - } else { - pos = getElementBasePos(&v_pos); + MY_CHECKGEOM("dropdown",1); - s32 width = stof(parts[1]) * spacing.Y; + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); + } else { + pos = getElementBasePos(&v_pos); - rect = core::rect<s32>(pos.X, pos.Y, - pos.X + width, pos.Y + (m_btn_height * 2)); - } + s32 width = stof(parts[1]) * spacing.Y; - FieldSpec spec( - name, - L"", - L"", - 258 + m_fields.size() - ); + rect = core::rect<s32>(pos.X, pos.Y, + pos.X + width, pos.Y + (m_btn_height * 2)); + } - spec.ftype = f_DropDown; - spec.send = true; + FieldSpec spec( + name, + L"", + L"", + 258 + m_fields.size() + ); - //now really show list - gui::IGUIComboBox *e = Environment->addComboBox(rect, data->current_parent, - spec.fid); + spec.ftype = f_DropDown; + spec.send = true; - if (spec.fname == m_focused_element) { - Environment->setFocus(e); - } + //now really show list + gui::IGUIComboBox *e = Environment->addComboBox(rect, data->current_parent, + spec.fid); - for (const std::string &item : items) { - e->addItem(unescape_translate(unescape_string( - utf8_to_wide(item))).c_str()); - } + if (spec.fname == m_focused_element) { + Environment->setFocus(e); + } - if (!str_initial_selection.empty()) - e->setSelected(stoi(str_initial_selection)-1); + for (const std::string &item : items) { + e->addItem(unescape_translate(unescape_string( + utf8_to_wide(item))).c_str()); + } - auto style = getDefaultStyleForElement("dropdown", name); + if (!str_initial_selection.empty()) + e->setSelected(stoi(str_initial_selection)-1); - spec.sound = style.get(StyleSpec::Property::SOUND, ""); + auto style = getDefaultStyleForElement("dropdown", name); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + spec.sound = style.get(StyleSpec::Property::SOUND, ""); - m_fields.push_back(spec); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - m_dropdowns.emplace_back(spec, std::vector<std::string>()); - std::vector<std::string> &values = m_dropdowns.back().second; - for (const std::string &item : items) { - values.push_back(unescape_string(item)); - } + m_fields.push_back(spec); - return; + m_dropdowns.emplace_back(spec, std::vector<std::string>()); + std::vector<std::string> &values = m_dropdowns.back().second; + for (const std::string &item : items) { + values.push_back(unescape_string(item)); } - errorstream << "Invalid dropdown element(" << parts.size() << "): '" << element - << "'" << std::endl; } void GUIFormSpecMenu::parseFieldCloseOnEnter(parserData *data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); - if (parts.size() == 2 || - (parts.size() > 2 && m_formspec_version > FORMSPEC_API_VERSION)) { - field_close_on_enter[parts[0]] = is_yes(parts[1]); - } + std::vector<std::string> parts; + if (!precheckElement("field_close_on_enter", element, 2, 2, parts)) + return; + + field_close_on_enter[parts[0]] = is_yes(parts[1]); } void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); - - if (parts.size() == 4 || - (parts.size() > 4 && m_formspec_version > FORMSPEC_API_VERSION)) - { - std::vector<std::string> v_pos = split(parts[0],','); - std::vector<std::string> v_geom = split(parts[1],','); - std::string name = parts[2]; - std::string label = parts[3]; - - MY_CHECKPOS("pwdfield",0); - MY_CHECKGEOM("pwdfield",1); + std::vector<std::string> parts; + if (!precheckElement("pwdfield", element, 4, 4, parts)) + return; - v2s32 pos; - v2s32 geom; + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> v_geom = split(parts[1],','); + std::string name = parts[2]; + std::string label = parts[3]; - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); - } else { - pos = getElementBasePos(&v_pos); - pos -= padding; + MY_CHECKPOS("pwdfield",0); + MY_CHECKGEOM("pwdfield",1); - geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); + v2s32 pos; + v2s32 geom; - pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2; - pos.Y -= m_btn_height; - geom.Y = m_btn_height*2; - } + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + pos -= padding; - core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); + geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); - std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); + pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2; + pos.Y -= m_btn_height; + geom.Y = m_btn_height*2; + } - FieldSpec spec( - name, - wlabel, - L"", - 258 + m_fields.size(), - 0, - ECI_IBEAM - ); + core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - spec.send = true; - gui::IGUIEditBox *e = Environment->addEditBox(0, rect, true, - data->current_parent, spec.fid); + std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); - if (spec.fname == m_focused_element) { - Environment->setFocus(e); - } + FieldSpec spec( + name, + wlabel, + L"", + 258 + m_fields.size(), + 0, + ECI_IBEAM + ); - if (label.length() >= 1) { - int font_height = g_fontengine->getTextHeight(); - rect.UpperLeftCorner.Y -= font_height; - rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; - gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true, - data->current_parent, 0); - } + spec.send = true; + gui::IGUIEditBox *e = Environment->addEditBox(0, rect, true, + data->current_parent, spec.fid); - e->setPasswordBox(true,L'*'); + if (spec.fname == m_focused_element) { + Environment->setFocus(e); + } - 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))); - e->setOverrideFont(style.getFont()); + if (label.length() >= 1) { + int font_height = g_fontengine->getTextHeight(); + rect.UpperLeftCorner.Y -= font_height; + rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; + gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true, + data->current_parent, 0); + } - irr::SEvent evt; - evt.EventType = EET_KEY_INPUT_EVENT; - evt.KeyInput.Key = KEY_END; - evt.KeyInput.Char = 0; - evt.KeyInput.Control = false; - evt.KeyInput.Shift = false; - evt.KeyInput.PressedDown = true; - e->OnEvent(evt); + e->setPasswordBox(true,L'*'); - // Note: Before 5.2.0 "parts.size() >= 5" resulted in a - // warning referring to field_close_on_enter[]! + 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))); + e->setOverrideFont(style.getFont()); + + irr::SEvent evt; + evt.EventType = EET_KEY_INPUT_EVENT; + evt.KeyInput.Key = KEY_END; + evt.KeyInput.Char = 0; + evt.KeyInput.Control = false; + evt.KeyInput.Shift = false; + evt.KeyInput.PressedDown = true; + e->OnEvent(evt); + + // Note: Before 5.2.0 "parts.size() >= 5" resulted in a + // warning referring to field_close_on_enter[]! - m_fields.push_back(spec); - return; - } - errorstream<< "Invalid pwdfield element(" << parts.size() << "): '" << element << "'" << std::endl; + m_fields.push_back(spec); } void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, @@ -1547,21 +1507,13 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, } gui::IGUIEditBox *e = nullptr; - static constexpr bool use_intl_edit_box = USE_FREETYPE && - IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9; - - if (use_intl_edit_box && g_settings->getBool("freetype")) { - e = new gui::intlGUIEditBox(spec.fdefault.c_str(), true, Environment, - data->current_parent, spec.fid, rect, is_editable, is_multiline); - } else { - if (is_multiline) { - 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, - data->current_parent, spec.fid); - e->grab(); - } + if (is_multiline) { + 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, + data->current_parent, spec.fid); + e->grab(); } auto style = getDefaultStyleForElement(is_multiline ? "textarea" : "field", spec.fname); @@ -1586,11 +1538,10 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, } e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); - if (style.get(StyleSpec::BGCOLOR, "") == "transparent") { - e->setDrawBackground(false); - } + bool border = style.getBool(StyleSpec::BORDER, true); + e->setDrawBorder(border); + e->setDrawBackground(border); e->setOverrideFont(style.getFont()); e->drop(); @@ -1720,30 +1671,26 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector<std::string>& void GUIFormSpecMenu::parseField(parserData* data, const std::string &element, const std::string &type) { - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts; + if (!precheckElement(type, element, 3, 5, parts)) + return; if (parts.size() == 3 || parts.size() == 4) { - parseSimpleField(data,parts); + parseSimpleField(data, parts); return; } - if ((parts.size() == 5) || - ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - parseTextArea(data,parts,type); - return; - } - errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'" << std::endl; + // Else: >= 5 arguments in "parts" + parseTextArea(data, parts, type); } void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &element) { - std::vector<std::string> parts = split(element, ';'); + MY_CHECKCLIENT("list"); - if (parts.size() != 4 && m_formspec_version < FORMSPEC_API_VERSION) { - errorstream << "Invalid text element(" << parts.size() << "): '" << element << "'" << std::endl; + std::vector<std::string> parts; + if (!precheckElement("hypertext", element, 4, 4, parts)) return; - } std::vector<std::string> v_pos = split(parts[0], ','); std::vector<std::string> v_geom = split(parts[1], ','); @@ -1794,539 +1741,521 @@ void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &elemen void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts; + if (!precheckElement("label", element, 2, 2, parts)) + return; - if ((parts.size() == 2) || - ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector<std::string> v_pos = split(parts[0],','); - std::string text = parts[1]; + std::vector<std::string> v_pos = split(parts[0],','); + std::string text = parts[1]; - MY_CHECKPOS("label",0); + MY_CHECKPOS("label",0); - if(!data->explicit_size) - warningstream<<"invalid use of label without a size[] element"<<std::endl; + if(!data->explicit_size) + warningstream<<"invalid use of label without a size[] element"<<std::endl; - std::vector<std::string> lines = split(text, '\n'); + std::vector<std::string> lines = split(text, '\n'); - auto style = getDefaultStyleForElement("label", ""); - gui::IGUIFont *font = style.getFont(); - if (!font) - font = m_font; + auto style = getDefaultStyleForElement("label", ""); + gui::IGUIFont *font = style.getFont(); + if (!font) + font = m_font; - for (unsigned int i = 0; i != lines.size(); i++) { - std::wstring wlabel_colors = translate_string( - utf8_to_wide(unescape_string(lines[i]))); - // Without color escapes to get the font dimensions - std::wstring wlabel_plain = unescape_enriched(wlabel_colors); + for (unsigned int i = 0; i != lines.size(); i++) { + std::wstring wlabel_colors = translate_string( + utf8_to_wide(unescape_string(lines[i]))); + // Without color escapes to get the font dimensions + std::wstring wlabel_plain = unescape_enriched(wlabel_colors); - core::rect<s32> rect; + core::rect<s32> rect; - if (data->real_coordinates) { - // Lines are spaced at the distance of 1/2 imgsize. - // This alows lines that line up with the new elements - // easily without sacrificing good line distance. If - // it was one whole imgsize, it would have too much - // spacing. - v2s32 pos = getRealCoordinateBasePos(v_pos); + if (data->real_coordinates) { + // Lines are spaced at the distance of 1/2 imgsize. + // This alows lines that line up with the new elements + // easily without sacrificing good line distance. If + // it was one whole imgsize, it would have too much + // spacing. + v2s32 pos = getRealCoordinateBasePos(v_pos); - // Labels are positioned by their center, not their top. - pos.Y += (((float) imgsize.Y) / -2) + (((float) imgsize.Y) * i / 2); + // Labels are positioned by their center, not their top. + pos.Y += (((float) imgsize.Y) / -2) + (((float) imgsize.Y) * i / 2); - rect = core::rect<s32>( - pos.X, pos.Y, - pos.X + font->getDimension(wlabel_plain.c_str()).Width, - pos.Y + imgsize.Y); + rect = core::rect<s32>( + pos.X, pos.Y, + pos.X + font->getDimension(wlabel_plain.c_str()).Width, + pos.Y + imgsize.Y); - } else { - // Lines are spaced at the nominal distance of - // 2/5 inventory slot, even if the font doesn't - // quite match that. This provides consistent - // form layout, at the expense of sometimes - // having sub-optimal spacing for the font. - // We multiply by 2 and then divide by 5, rather - // than multiply by 0.4, to get exact results - // in the integer cases: 0.4 is not exactly - // representable in binary floating point. - - v2s32 pos = getElementBasePos(nullptr); - pos.X += stof(v_pos[0]) * spacing.X; - pos.Y += (stof(v_pos[1]) + 7.0f / 30.0f) * spacing.Y; - - pos.Y += ((float) i) * spacing.Y * 2.0 / 5.0; - - rect = core::rect<s32>( - pos.X, pos.Y - m_btn_height, - pos.X + font->getDimension(wlabel_plain.c_str()).Width, - pos.Y + m_btn_height); - } + } else { + // Lines are spaced at the nominal distance of + // 2/5 inventory slot, even if the font doesn't + // quite match that. This provides consistent + // form layout, at the expense of sometimes + // having sub-optimal spacing for the font. + // We multiply by 2 and then divide by 5, rather + // than multiply by 0.4, to get exact results + // in the integer cases: 0.4 is not exactly + // representable in binary floating point. + + v2s32 pos = getElementBasePos(nullptr); + pos.X += stof(v_pos[0]) * spacing.X; + pos.Y += (stof(v_pos[1]) + 7.0f / 30.0f) * spacing.Y; + + pos.Y += ((float) i) * spacing.Y * 2.0 / 5.0; - FieldSpec spec( - "", - wlabel_colors, - L"", - 258 + m_fields.size(), - 4 - ); - gui::IGUIStaticText *e = gui::StaticText::add(Environment, - spec.flabel.c_str(), rect, false, false, data->current_parent, - spec.fid); - e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); + rect = core::rect<s32>( + pos.X, pos.Y - m_btn_height, + pos.X + font->getDimension(wlabel_plain.c_str()).Width, + pos.Y + m_btn_height); + } - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); - e->setOverrideFont(font); + FieldSpec spec( + "", + wlabel_colors, + L"", + 258 + m_fields.size(), + 4 + ); + gui::IGUIStaticText *e = gui::StaticText::add(Environment, + spec.flabel.c_str(), rect, false, false, data->current_parent, + spec.fid); + e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); - m_fields.push_back(spec); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); + e->setOverrideFont(font); - // labels should let events through - e->grab(); - m_clickthrough_elements.push_back(e); - } + m_fields.push_back(spec); - return; + // labels should let events through + e->grab(); + m_clickthrough_elements.push_back(e); } - errorstream << "Invalid label element(" << parts.size() << "): '" << element - << "'" << std::endl; } void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts; + if (!precheckElement("vertlabel", element, 2, 2, parts)) + return; - if ((parts.size() == 2) || - ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector<std::string> v_pos = split(parts[0],','); - std::wstring text = unescape_translate( - unescape_string(utf8_to_wide(parts[1]))); + std::vector<std::string> v_pos = split(parts[0],','); + std::wstring text = unescape_translate( + unescape_string(utf8_to_wide(parts[1]))); - MY_CHECKPOS("vertlabel",1); + MY_CHECKPOS("vertlabel",1); - auto style = getDefaultStyleForElement("vertlabel", "", "label"); - gui::IGUIFont *font = style.getFont(); - if (!font) - font = m_font; + auto style = getDefaultStyleForElement("vertlabel", "", "label"); + gui::IGUIFont *font = style.getFont(); + if (!font) + font = m_font; - v2s32 pos; - core::rect<s32> rect; + v2s32 pos; + core::rect<s32> rect; - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); - // Vertlabels are positioned by center, not left. - pos.X -= imgsize.X / 2; + // Vertlabels are positioned by center, not left. + pos.X -= imgsize.X / 2; - // We use text.length + 1 because without it, the rect - // isn't quite tall enough and cuts off the text. - rect = core::rect<s32>(pos.X, pos.Y, - pos.X + imgsize.X, - pos.Y + font_line_height(font) * - (text.length() + 1)); + // We use text.length + 1 because without it, the rect + // isn't quite tall enough and cuts off the text. + rect = core::rect<s32>(pos.X, pos.Y, + pos.X + imgsize.X, + pos.Y + font_line_height(font) * + (text.length() + 1)); - } else { - pos = getElementBasePos(&v_pos); + } else { + pos = getElementBasePos(&v_pos); - // As above, the length must be one longer. The width of - // the rect (15 pixels) seems rather arbitrary, but - // changing it might break something. - rect = core::rect<s32>( - pos.X, pos.Y+((imgsize.Y/2) - m_btn_height), - pos.X+15, pos.Y + - font_line_height(font) * - (text.length() + 1) + - ((imgsize.Y/2) - m_btn_height)); - } + // As above, the length must be one longer. The width of + // the rect (15 pixels) seems rather arbitrary, but + // changing it might break something. + rect = core::rect<s32>( + pos.X, pos.Y+((imgsize.Y/2) - m_btn_height), + pos.X+15, pos.Y + + font_line_height(font) * + (text.length() + 1) + + ((imgsize.Y/2) - m_btn_height)); + } - if(!data->explicit_size) - warningstream<<"invalid use of label without a size[] element"<<std::endl; + if(!data->explicit_size) + warningstream<<"invalid use of label without a size[] element"<<std::endl; - std::wstring label; + std::wstring label; - for (wchar_t i : text) { - label += i; - label += L"\n"; - } + for (wchar_t i : text) { + label += i; + label += L"\n"; + } - FieldSpec spec( - "", - label, - L"", - 258 + m_fields.size() - ); - gui::IGUIStaticText *e = gui::StaticText::add(Environment, spec.flabel.c_str(), - rect, false, false, data->current_parent, spec.fid); - e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); + FieldSpec spec( + "", + label, + L"", + 258 + m_fields.size() + ); + gui::IGUIStaticText *e = gui::StaticText::add(Environment, spec.flabel.c_str(), + rect, false, false, data->current_parent, spec.fid); + e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); - e->setOverrideFont(font); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); + e->setOverrideFont(font); - m_fields.push_back(spec); + 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; + // vertlabels should let events through + e->grab(); + m_clickthrough_elements.push_back(e); } void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &element, const std::string &type) { - std::vector<std::string> parts = split(element,';'); - - if ((((parts.size() >= 5) && (parts.size() <= 8)) && (parts.size() != 6)) || - ((parts.size() > 8) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector<std::string> v_pos = split(parts[0],','); - std::vector<std::string> v_geom = split(parts[1],','); - std::string image_name = parts[2]; - std::string name = parts[3]; - std::string label = parts[4]; + std::vector<std::string> parts; + if (!precheckElement("image_button", element, 5, 8, parts)) + return; - MY_CHECKPOS("imagebutton",0); - MY_CHECKGEOM("imagebutton",1); + if (parts.size() == 6) { + // Invalid argument count. + errorstream << "Invalid image_button element(" << parts.size() << "): '" << element << "'" << std::endl; + return; + } - std::string pressed_image_name; + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> v_geom = split(parts[1],','); + std::string image_name = parts[2]; + std::string name = parts[3]; + std::string label = parts[4]; - if (parts.size() >= 8) { - pressed_image_name = parts[7]; - } + MY_CHECKPOS("image_button",0); + MY_CHECKGEOM("image_button",1); - v2s32 pos; - v2s32 geom; + std::string pressed_image_name; - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); - } else { - pos = getElementBasePos(&v_pos); - geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); - geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y); - } + if (parts.size() >= 8) { + pressed_image_name = parts[7]; + } - core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, - pos.Y+geom.Y); + v2s32 pos; + v2s32 geom; - if (!data->explicit_size) - warningstream<<"invalid use of image_button without a size[] element"<<std::endl; + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); + geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y); + } - image_name = unescape_string(image_name); - pressed_image_name = unescape_string(pressed_image_name); + core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, + pos.Y+geom.Y); - std::wstring wlabel = utf8_to_wide(unescape_string(label)); + if (!data->explicit_size) + warningstream<<"invalid use of image_button without a size[] element"<<std::endl; - FieldSpec spec( - name, - wlabel, - utf8_to_wide(image_name), - 258 + m_fields.size() - ); - spec.ftype = f_Button; - if (type == "image_button_exit") - spec.is_exit = true; + image_name = unescape_string(image_name); + pressed_image_name = unescape_string(pressed_image_name); - GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, m_tsrc, - data->current_parent, spec.fid, spec.flabel.c_str()); + std::wstring wlabel = utf8_to_wide(unescape_string(label)); - if (spec.fname == m_focused_element) { - Environment->setFocus(e); - } + FieldSpec spec( + name, + wlabel, + utf8_to_wide(image_name), + 258 + m_fields.size() + ); + spec.ftype = f_Button; + if (type == "image_button_exit") + spec.is_exit = true; - auto style = getStyleForElement("image_button", spec.fname); + GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, m_tsrc, + data->current_parent, spec.fid, spec.flabel.c_str()); - spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); + if (spec.fname == m_focused_element) { + Environment->setFocus(e); + } - // Override style properties with values specified directly in the element - if (!image_name.empty()) - style[StyleSpec::STATE_DEFAULT].set(StyleSpec::FGIMG, image_name); + auto style = getStyleForElement("image_button", spec.fname); - if (!pressed_image_name.empty()) - style[StyleSpec::STATE_PRESSED].set(StyleSpec::FGIMG, pressed_image_name); + spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); - if (parts.size() >= 7) { - style[StyleSpec::STATE_DEFAULT].set(StyleSpec::NOCLIP, parts[5]); - style[StyleSpec::STATE_DEFAULT].set(StyleSpec::BORDER, parts[6]); - } + // Override style properties with values specified directly in the element + if (!image_name.empty()) + style[StyleSpec::STATE_DEFAULT].set(StyleSpec::FGIMG, image_name); - e->setStyles(style); - e->setScaleImage(true); + if (!pressed_image_name.empty()) + style[StyleSpec::STATE_PRESSED].set(StyleSpec::FGIMG, pressed_image_name); - m_fields.push_back(spec); - return; + if (parts.size() >= 7) { + style[StyleSpec::STATE_DEFAULT].set(StyleSpec::NOCLIP, parts[5]); + style[StyleSpec::STATE_DEFAULT].set(StyleSpec::BORDER, parts[6]); } - errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'" << std::endl; + e->setStyles(style); + e->setScaleImage(true); + + m_fields.push_back(spec); } void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element, ';'); - - if (((parts.size() == 4) || (parts.size() == 6)) || (parts.size() == 7 && - data->real_coordinates) || ((parts.size() > 6) && - (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> parts; + if (!precheckElement("tabheader", element, 4, 7, parts)) + return; - // If we're using real coordinates, add an extra field for height. - // Width is not here because tabs are the width of the text, and - // there's no reason to change that. - unsigned int i = 0; - std::vector<std::string> v_geom = {"1", "1"}; // Dummy width and height - bool auto_width = true; - if (parts.size() == 7) { - i++; + // Length 7: Additional "height" parameter after "pos". Only valid with real_coordinates. + // Note: New arguments for the "height" syntax cannot be added without breaking older clients. + if (parts.size() == 5 || (parts.size() == 7 && !data->real_coordinates)) { + errorstream << "Invalid tabheader element(" << parts.size() << "): '" + << element << "'" << std::endl; + return; + } - v_geom = split(parts[1], ','); - if (v_geom.size() == 1) - v_geom.insert(v_geom.begin(), "1"); // Dummy value - else - auto_width = false; - } + std::vector<std::string> v_pos = split(parts[0],','); - std::string name = parts[i+1]; - std::vector<std::string> buttons = split(parts[i+2], ','); - std::string str_index = parts[i+3]; - bool show_background = true; - bool show_border = true; - int tab_index = stoi(str_index) - 1; + // If we're using real coordinates, add an extra field for height. + // Width is not here because tabs are the width of the text, and + // there's no reason to change that. + unsigned int i = 0; + std::vector<std::string> v_geom = {"1", "1"}; // Dummy width and height + bool auto_width = true; + if (parts.size() == 7) { + i++; + + v_geom = split(parts[1], ','); + if (v_geom.size() == 1) + v_geom.insert(v_geom.begin(), "1"); // Dummy value + else + auto_width = false; + } - MY_CHECKPOS("tabheader", 0); + std::string name = parts[i+1]; + std::vector<std::string> buttons = split(parts[i+2], ','); + std::string str_index = parts[i+3]; + bool show_background = true; + bool show_border = true; + int tab_index = stoi(str_index) - 1; - if (parts.size() == 6 + i) { - if (parts[4+i] == "true") - show_background = false; - if (parts[5+i] == "false") - show_border = false; - } + MY_CHECKPOS("tabheader", 0); - FieldSpec spec( - name, - L"", - L"", - 258 + m_fields.size() - ); + if (parts.size() == 6 + i) { + if (parts[4+i] == "true") + show_background = false; + if (parts[5+i] == "false") + show_border = false; + } - spec.ftype = f_TabHeader; + FieldSpec spec( + name, + L"", + L"", + 258 + m_fields.size() + ); - v2s32 pos; - v2s32 geom; + spec.ftype = f_TabHeader; - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); + v2s32 pos; + v2s32 geom; - 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 - - MY_CHECKGEOM("tabheader", 1); - } else { - v2f32 pos_f = pos_offset * spacing; - pos_f.X += stof(v_pos[0]) * spacing.X; - pos_f.Y += stof(v_pos[1]) * spacing.Y - m_btn_height * 2; - pos = v2s32(pos_f.X, pos_f.Y); + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + // Set default height + if (parts.size() <= 6) geom.Y = m_btn_height * 2; - geom.X = DesiredRect.getWidth(); - } + pos.Y -= geom.Y; // TabHeader base pos is the bottom, not the top. + if (auto_width) + geom.X = DesiredRect.getWidth(); // Set automatic width - core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, - pos.Y+geom.Y); + MY_CHECKGEOM("tabheader", 1); + } else { + v2f32 pos_f = pos_offset * spacing; + pos_f.X += stof(v_pos[0]) * spacing.X; + pos_f.Y += stof(v_pos[1]) * spacing.Y - m_btn_height * 2; + pos = v2s32(pos_f.X, pos_f.Y); - 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); + geom.Y = m_btn_height * 2; + geom.X = DesiredRect.getWidth(); + } - auto style = getDefaultStyleForElement("tabheader", name); + core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, + pos.Y+geom.Y); - spec.sound = style.get(StyleSpec::Property::SOUND, ""); + 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); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true)); + auto style = getDefaultStyleForElement("tabheader", name); - for (const std::string &button : buttons) { - auto tab = e->addTab(unescape_translate(unescape_string( - utf8_to_wide(button))).c_str(), -1); - if (style.isNotDefault(StyleSpec::BGCOLOR)) - tab->setBackgroundColor(style.getColor(StyleSpec::BGCOLOR)); + spec.sound = style.get(StyleSpec::Property::SOUND, ""); - tab->setTextColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); - } + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true)); - if ((tab_index >= 0) && - (buttons.size() < INT_MAX) && - (tab_index < (int) buttons.size())) - e->setActiveTab(tab_index); + for (const std::string &button : buttons) { + auto tab = e->addTab(unescape_translate(unescape_string( + utf8_to_wide(button))).c_str(), -1); + if (style.isNotDefault(StyleSpec::BGCOLOR)) + tab->setBackgroundColor(style.getColor(StyleSpec::BGCOLOR)); - m_fields.push_back(spec); - return; + tab->setTextColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); } - errorstream << "Invalid TabHeader element(" << parts.size() << "): '" - << element << "'" << std::endl; + + if ((tab_index >= 0) && + (buttons.size() < INT_MAX) && + (tab_index < (int) buttons.size())) + e->setActiveTab(tab_index); + + m_fields.push_back(spec); } 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; - return; - } + MY_CHECKCLIENT("item_image_button"); - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts; + if (!precheckElement("item_image_button", element, 5, 5, parts)) + return; - if ((parts.size() == 5) || - ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector<std::string> v_pos = split(parts[0],','); - std::vector<std::string> v_geom = split(parts[1],','); - std::string item_name = parts[2]; - std::string name = parts[3]; - std::string label = parts[4]; + std::vector<std::string> v_pos = split(parts[0],','); + std::vector<std::string> v_geom = split(parts[1],','); + std::string item_name = parts[2]; + std::string name = parts[3]; + std::string label = parts[4]; - label = unescape_string(label); - item_name = unescape_string(item_name); + label = unescape_string(label); + item_name = unescape_string(item_name); - MY_CHECKPOS("itemimagebutton",0); - MY_CHECKGEOM("itemimagebutton",1); + MY_CHECKPOS("item_image_button",0); + MY_CHECKGEOM("item_image_button",1); - v2s32 pos; - v2s32 geom; - - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); - } else { - pos = getElementBasePos(&v_pos); - geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); - geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y); - } + v2s32 pos; + v2s32 geom; - core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); + geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y); + } - if(!data->explicit_size) - warningstream<<"invalid use of item_image_button without a size[] element"<<std::endl; + core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - IItemDefManager *idef = m_client->idef(); - ItemStack item; - item.deSerialize(item_name, idef); + if(!data->explicit_size) + warningstream<<"invalid use of item_image_button without a size[] element"<<std::endl; - m_tooltips[name] = - TooltipSpec(utf8_to_wide(item.getDefinition(idef).description), - m_default_tooltip_bgcolor, - m_default_tooltip_color); + IItemDefManager *idef = m_client->idef(); + ItemStack item; + item.deSerialize(item_name, idef); - // the spec for the button - FieldSpec spec_btn( - name, - utf8_to_wide(label), - utf8_to_wide(item_name), - 258 + m_fields.size(), - 2 - ); + m_tooltips[name] = + TooltipSpec(utf8_to_wide(item.getDefinition(idef).description), + m_default_tooltip_bgcolor, + m_default_tooltip_color); - GUIButtonItemImage *e_btn = GUIButtonItemImage::addButton(Environment, - rect, m_tsrc, data->current_parent, spec_btn.fid, spec_btn.flabel.c_str(), - item_name, m_client); + // the spec for the button + FieldSpec spec_btn( + name, + utf8_to_wide(label), + utf8_to_wide(item_name), + 258 + m_fields.size(), + 2 + ); - auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button"); + GUIButtonItemImage *e_btn = GUIButtonItemImage::addButton(Environment, + rect, m_tsrc, data->current_parent, spec_btn.fid, spec_btn.flabel.c_str(), + item_name, m_client); - spec_btn.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); + auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button"); - e_btn->setStyles(style); + spec_btn.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); - if (spec_btn.fname == m_focused_element) { - Environment->setFocus(e_btn); - } + e_btn->setStyles(style); - spec_btn.ftype = f_Button; - rect += data->basepos-padding; - spec_btn.rect = rect; - m_fields.push_back(spec_btn); - return; + if (spec_btn.fname == m_focused_element) { + Environment->setFocus(e_btn); } - errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'" << std::endl; + + spec_btn.ftype = f_Button; + rect += data->basepos-padding; + spec_btn.rect = rect; + m_fields.push_back(spec_btn); } void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element, ';'); + std::vector<std::string> parts; + if (!precheckElement("box", element, 3, 3, parts)) + return; - if ((parts.size() == 3) || - ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector<std::string> v_pos = split(parts[0], ','); - std::vector<std::string> v_geom = split(parts[1], ','); + std::vector<std::string> v_pos = split(parts[0], ','); + std::vector<std::string> v_geom = split(parts[1], ','); - MY_CHECKPOS("box", 0); - MY_CHECKGEOM("box", 1); + MY_CHECKPOS("box", 0); + MY_CHECKGEOM("box", 1); - v2s32 pos; - v2s32 geom; + v2s32 pos; + v2s32 geom; - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); - } else { - pos = getElementBasePos(&v_pos); - geom.X = stof(v_geom[0]) * spacing.X; - geom.Y = stof(v_geom[1]) * spacing.Y; - } + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + geom.X = stof(v_geom[0]) * spacing.X; + geom.Y = stof(v_geom[1]) * spacing.Y; + } - FieldSpec spec( - "", - L"", - L"", - 258 + m_fields.size(), - -2 - ); - spec.ftype = f_Box; + FieldSpec spec( + "", + L"", + L"", + 258 + m_fields.size(), + -2 + ); + spec.ftype = f_Box; - auto style = getDefaultStyleForElement("box", spec.fname); + auto style = getDefaultStyleForElement("box", spec.fname); - video::SColor tmp_color; - std::array<video::SColor, 4> colors; - std::array<video::SColor, 4> bordercolors = {0x0, 0x0, 0x0, 0x0}; - std::array<s32, 4> borderwidths = {0, 0, 0, 0}; + video::SColor tmp_color; + std::array<video::SColor, 4> colors; + std::array<video::SColor, 4> bordercolors = {0x0, 0x0, 0x0, 0x0}; + std::array<s32, 4> borderwidths = {0, 0, 0, 0}; - if (parseColorString(parts[2], tmp_color, true, 0x8C)) { - colors = {tmp_color, tmp_color, tmp_color, tmp_color}; - } else { - colors = style.getColorArray(StyleSpec::COLORS, {0x0, 0x0, 0x0, 0x0}); - bordercolors = style.getColorArray(StyleSpec::BORDERCOLORS, - {0x0, 0x0, 0x0, 0x0}); - borderwidths = style.getIntArray(StyleSpec::BORDERWIDTHS, {0, 0, 0, 0}); - } + if (parseColorString(parts[2], tmp_color, true, 0x8C)) { + colors = {tmp_color, tmp_color, tmp_color, tmp_color}; + } else { + colors = style.getColorArray(StyleSpec::COLORS, {0x0, 0x0, 0x0, 0x0}); + bordercolors = style.getColorArray(StyleSpec::BORDERCOLORS, + {0x0, 0x0, 0x0, 0x0}); + borderwidths = style.getIntArray(StyleSpec::BORDERWIDTHS, {0, 0, 0, 0}); + } - core::rect<s32> rect(pos, pos + geom); + core::rect<s32> rect(pos, pos + geom); - GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, rect, - colors, bordercolors, borderwidths); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); - e->drop(); + GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, rect, + colors, bordercolors, borderwidths); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); + e->drop(); - m_fields.push_back(spec); - return; - } - errorstream << "Invalid Box element(" << parts.size() << "): '" << element - << "'" << std::endl; + m_fields.push_back(spec); } void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts; + if (!precheckElement("bgcolor", element, 1, 3, parts)) + return; + const u32 parameter_count = parts.size(); - if ((parameter_count > 2 && m_formspec_version < 3) || - (parameter_count > 3 && m_formspec_version <= FORMSPEC_API_VERSION)) { + if (parameter_count > 2 && m_formspec_version < 3) { errorstream << "Invalid bgcolor element(" << parameter_count << "): '" << element << "'" << std::endl; return; @@ -2357,49 +2286,51 @@ void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string & void GUIFormSpecMenu::parseListColors(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts; + // Legacy Note: If clients older than 5.5.0-dev are supplied with additional arguments, + // the tooltip colors will be ignored. + if (!precheckElement("listcolors", element, 2, 5, parts)) + return; - if (((parts.size() == 2) || (parts.size() == 3) || (parts.size() == 5)) || - ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - parseColorString(parts[0], data->inventorylist_options.slotbg_n, false); - parseColorString(parts[1], data->inventorylist_options.slotbg_h, false); + if (parts.size() == 4) { + // Invalid argument combination + errorstream << "Invalid listcolors element(" << parts.size() << "): '" + << element << "'" << std::endl; + return; + } - if (parts.size() >= 3) { - if (parseColorString(parts[2], data->inventorylist_options.slotbordercolor, - false)) { - data->inventorylist_options.slotborder = true; - } - } - if (parts.size() == 5) { - video::SColor tmp_color; + parseColorString(parts[0], data->inventorylist_options.slotbg_n, false); + parseColorString(parts[1], data->inventorylist_options.slotbg_h, false); - if (parseColorString(parts[3], tmp_color, false)) - m_default_tooltip_bgcolor = tmp_color; - if (parseColorString(parts[4], tmp_color, false)) - m_default_tooltip_color = tmp_color; + if (parts.size() >= 3) { + if (parseColorString(parts[2], data->inventorylist_options.slotbordercolor, + false)) { + data->inventorylist_options.slotborder = true; } + } + if (parts.size() >= 5) { + video::SColor tmp_color; - // update all already parsed inventorylists - for (GUIInventoryList *e : m_inventorylists) { - e->setSlotBGColors(data->inventorylist_options.slotbg_n, - data->inventorylist_options.slotbg_h); - e->setSlotBorders(data->inventorylist_options.slotborder, - data->inventorylist_options.slotbordercolor); - } - return; + if (parseColorString(parts[3], tmp_color, false)) + m_default_tooltip_bgcolor = tmp_color; + if (parseColorString(parts[4], tmp_color, false)) + m_default_tooltip_color = tmp_color; + } + + // update all already parsed inventorylists + for (GUIInventoryList *e : m_inventorylists) { + e->setSlotBGColors(data->inventorylist_options.slotbg_n, + data->inventorylist_options.slotbg_h); + e->setSlotBorders(data->inventorylist_options.slotborder, + data->inventorylist_options.slotbordercolor); } - errorstream<< "Invalid listcolors element(" << parts.size() << "): '" << element << "'" << std::endl; } void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); - if (parts.size() < 2) { - errorstream << "Invalid tooltip element(" << parts.size() << "): '" - << element << "'" << std::endl; + std::vector<std::string> parts; + if (!precheckElement("tooltip", element, 2, 5, parts)) return; - } // Get mode and check size bool rect_mode = parts[0].find(',') != std::string::npos; @@ -2539,11 +2470,16 @@ bool GUIFormSpecMenu::parsePositionDirect(parserData *data, const std::string &e void GUIFormSpecMenu::parsePosition(parserData *data, const std::string &element) { - std::vector<std::string> parts = split(element, ','); + std::vector<std::string> parts = split(element, ';'); - if (parts.size() == 2) { - data->offset.X = stof(parts[0]); - data->offset.Y = stof(parts[1]); + if (parts.size() == 1 || + (parts.size() > 1 && m_formspec_version > FORMSPEC_API_VERSION)) { + std::vector<std::string> v_geom = split(parts[0], ','); + + MY_CHECKGEOM("position", 0); + + data->offset.X = stof(v_geom[0]); + data->offset.Y = stof(v_geom[1]); return; } @@ -2573,11 +2509,16 @@ bool GUIFormSpecMenu::parseAnchorDirect(parserData *data, const std::string &ele void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element) { - std::vector<std::string> parts = split(element, ','); + std::vector<std::string> parts = split(element, ';'); - if (parts.size() == 2) { - data->anchor.X = stof(parts[0]); - data->anchor.Y = stof(parts[1]); + if (parts.size() == 1 || + (parts.size() > 1 && m_formspec_version > FORMSPEC_API_VERSION)) { + std::vector<std::string> v_geom = split(parts[0], ','); + + MY_CHECKGEOM("anchor", 0); + + data->anchor.X = stof(v_geom[0]); + data->anchor.Y = stof(v_geom[1]); return; } @@ -2585,6 +2526,46 @@ void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element) << "'" << std::endl; } +bool GUIFormSpecMenu::parsePaddingDirect(parserData *data, const std::string &element) +{ + if (element.empty()) + return false; + + std::vector<std::string> parts = split(element, '['); + + if (parts.size() != 2) + return false; + + std::string type = trim(parts[0]); + std::string description = trim(parts[1]); + + if (type != "padding") + return false; + + parsePadding(data, description); + + return true; +} + +void GUIFormSpecMenu::parsePadding(parserData *data, const std::string &element) +{ + std::vector<std::string> parts = split(element, ';'); + + if (parts.size() == 1 || + (parts.size() > 1 && m_formspec_version > FORMSPEC_API_VERSION)) { + std::vector<std::string> v_geom = split(parts[0], ','); + + MY_CHECKGEOM("padding", 0); + + data->padding.X = stof(v_geom[0]); + data->padding.Y = stof(v_geom[1]); + return; + } + + errorstream << "Invalid padding element (" << parts.size() << "): '" << element + << "'" << std::endl; +} + bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, bool style_type) { std::vector<std::string> parts = split(element, ';'); @@ -2723,39 +2704,29 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b void GUIFormSpecMenu::parseSetFocus(const std::string &element) { - std::vector<std::string> parts = split(element, ';'); - - if (parts.size() <= 2 || - (parts.size() > 2 && m_formspec_version > FORMSPEC_API_VERSION)) - { - if (m_is_form_regenerated) - return; // Never focus on resizing - - bool force_focus = parts.size() >= 2 && is_yes(parts[1]); - if (force_focus || m_text_dst->m_formname != m_last_formname) - setFocus(parts[0]); - + std::vector<std::string> parts; + if (!precheckElement("set_focus", element, 1, 2, parts)) return; - } - errorstream << "Invalid set_focus element (" << parts.size() << "): '" << element - << "'" << std::endl; + if (m_is_form_regenerated) + return; // Never focus on resizing + + bool force_focus = parts.size() >= 2 && is_yes(parts[1]); + if (force_focus || m_text_dst->m_formname != m_last_formname) + setFocus(parts[0]); } void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) { - std::vector<std::string> parts = split(element, ';'); + MY_CHECKCLIENT("model"); - if (parts.size() < 5 || (parts.size() > 9 && - m_formspec_version <= FORMSPEC_API_VERSION)) { - errorstream << "Invalid model element (" << parts.size() << "): '" << element - << "'" << std::endl; + std::vector<std::string> parts; + if (!precheckElement("model", element, 5, 10, parts)) return; - } // Avoid length checks by resizing - if (parts.size() < 9) - parts.resize(9); + if (parts.size() < 10) + parts.resize(10); std::vector<std::string> v_pos = split(parts[0], ','); std::vector<std::string> v_geom = split(parts[1], ','); @@ -2766,6 +2737,7 @@ void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) bool inf_rotation = is_yes(parts[6]); bool mousectrl = is_yes(parts[7]) || parts[7].empty(); // default true std::vector<std::string> frame_loop = split(parts[8], ','); + std::string speed = unescape_string(parts[9]); MY_CHECKPOS("model", 0); MY_CHECKGEOM("model", 1); @@ -2802,7 +2774,7 @@ void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) core::rect<s32> rect(pos, pos + geom); - GUIScene *e = new GUIScene(Environment, RenderingEngine::get_scene_manager(), + GUIScene *e = new GUIScene(Environment, m_client->getSceneManager(), data->current_parent, rect, spec.fid); auto meshnode = e->setMesh(mesh); @@ -2825,6 +2797,7 @@ void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) } e->setFrameLoop(frame_loop_begin, frame_loop_end); + e->setAnimationSpeed(stof(speed)); auto style = getStyleForElement("model", spec.fname); e->setStyles(style); @@ -3099,6 +3072,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) mydata.screensize = screensize; mydata.offset = v2f32(0.5f, 0.5f); mydata.anchor = v2f32(0.5f, 0.5f); + mydata.padding = v2f32(0.05f, 0.05f); mydata.simple_field_count = 0; // Base position of contents of form @@ -3201,7 +3175,14 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) } } - /* "no_prepend" element is always after "position" (or "size" element) if it used */ + /* "padding" element is always after "anchor" and previous if it is used */ + for (; i < elements.size(); i++) { + if (!parsePaddingDirect(&mydata, elements[i])) { + break; + } + } + + /* "no_prepend" element is always after "padding" and previous if it used */ bool enable_prepends = true; for (; i < elements.size(); i++) { if (elements[i].empty()) @@ -3266,11 +3247,9 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) double fitx_imgsize; double fity_imgsize; - // Pad the screensize with 5% of the screensize on all sides to ensure - // that even the largest formspecs don't touch the screen borders. v2f padded_screensize( - mydata.screensize.X * 0.9f, - mydata.screensize.Y * 0.9f + mydata.screensize.X * (1.0f - mydata.padding.X * 2.0f), + mydata.screensize.Y * (1.0f - mydata.padding.Y * 2.0f) ); if (mydata.real_coordinates) { @@ -3286,13 +3265,15 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) ((15.0 / 13.0) * (0.85 + mydata.invsize.Y)); } -#ifdef __ANDROID__ + s32 min_screen_dim = std::min(padded_screensize.X, padded_screensize.Y); + +#ifdef HAVE_TOUCHSCREENGUI // In Android, the preferred imgsize should be larger to accommodate the // smaller screensize. - double prefer_imgsize = padded_screensize.Y / 10 * gui_scaling; + double prefer_imgsize = min_screen_dim / 10 * gui_scaling; #else // Desktop computers have more space, so try to fit 15 coordinates. - double prefer_imgsize = padded_screensize.Y / 15 * gui_scaling; + double prefer_imgsize = min_screen_dim / 15 * gui_scaling; #endif // Try to use the preferred imgsize, but if that's bigger than the maximum // size, use the maximum size. @@ -3749,7 +3730,7 @@ void GUIFormSpecMenu::showTooltip(const std::wstring &text, v2u32 screenSize = Environment->getVideoDriver()->getScreenSize(); int tooltip_offset_x = m_btn_height; int tooltip_offset_y = m_btn_height; -#ifdef __ANDROID__ +#ifdef HAVE_TOUCHSCREENGUI tooltip_offset_x *= 3; tooltip_offset_y = 0; if (m_pointer.X > (s32)screenSize.X / 2) @@ -3941,9 +3922,7 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode) } if (e != 0) { - std::stringstream ss; - ss << (e->getActiveTab() +1); - fields[name] = ss.str(); + fields[name] = itos(e->getActiveTab() + 1); } } else if (s.ftype == f_CheckBox) { // No dynamic cast possible due to some distributions shipped @@ -3969,12 +3948,10 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode) e = static_cast<GUIScrollBar *>(element); if (e) { - std::stringstream os; - os << e->getPos(); if (s.fdefault == L"Changed") - fields[name] = "CHG:" + os.str(); + fields[name] = "CHG:" + itos(e->getPos()); else - fields[name] = "VAL:" + os.str(); + fields[name] = "VAL:" + itos(e->getPos()); } } else if (s.ftype == f_AnimatedImage) { // No dynamic cast possible due to some distributions shipped diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index d658aba7b..0b4d3879d 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -152,6 +152,7 @@ public: gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, Client *client, + gui::IGUIEnvironment *guienv, ISimpleTextureSource *tsrc, ISoundManager *sound_manager, IFormSource* fs_src, @@ -162,8 +163,9 @@ public: ~GUIFormSpecMenu(); static void create(GUIFormSpecMenu *&cur_formspec, Client *client, - JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest, - const std::string &formspecPrepend, ISoundManager *sound_manager); + gui::IGUIEnvironment *guienv, JoystickController *joystick, IFormSource *fs_src, + TextDest *txt_dest, const std::string &formspecPrepend, + ISoundManager *sound_manager); void setFormSpec(const std::string &formspec_string, const InventoryLocation ¤t_inventory_location) @@ -227,7 +229,7 @@ public: return m_selected_item; } - const u16 getSelectedAmount() const + u16 getSelectedAmount() const { return m_selected_amount; } @@ -277,6 +279,8 @@ protected: v2s32 getElementBasePos(const std::vector<std::string> *v_pos); v2s32 getRealCoordinateBasePos(const std::vector<std::string> &v_pos); v2s32 getRealCoordinateGeometry(const std::vector<std::string> &v_geom); + bool precheckElement(const std::string &name, const std::string &element, + size_t args_min, size_t args_max, std::vector<std::string> &parts); std::unordered_map<std::string, std::vector<StyleSpec>> theme_by_type; std::unordered_map<std::string, std::vector<StyleSpec>> theme_by_name; @@ -364,6 +368,7 @@ private: v2s32 size; v2f32 offset; v2f32 anchor; + v2f32 padding; core::rect<s32> rect; v2s32 basepos; v2u32 screensize; @@ -445,6 +450,8 @@ private: void parsePosition(parserData *data, const std::string &element); bool parseAnchorDirect(parserData *data, const std::string &element); void parseAnchor(parserData *data, const std::string &element); + bool parsePaddingDirect(parserData *data, const std::string &element); + void parsePadding(parserData *data, const std::string &element); bool parseStyle(parserData *data, const std::string &element, bool style_type); void parseSetFocus(const std::string &element); void parseModel(parserData *data, const std::string &element); diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index ccfdcb81d..40450ce5f 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -17,31 +17,26 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "IGUIEnvironment.h" -#include "IGUIElement.h" +#include "guiHyperText.h" #include "guiScrollBar.h" -#include "IGUIFont.h" -#include <vector> -#include <list> -#include <unordered_map> -using namespace irr::gui; #include "client/fontengine.h" -#include <SColor.h> #include "client/tile.h" #include "IVideoDriver.h" #include "client/client.h" #include "client/renderingengine.h" #include "hud.h" -#include "guiHyperText.h" #include "util/string.h" +#include "irrlicht_changes/CGUITTFont.h" -bool check_color(const std::string &str) +using namespace irr::gui; + +static bool check_color(const std::string &str) { irr::video::SColor color; return parseColorString(str, color, false); } -bool check_integer(const std::string &str) +static bool check_integer(const std::string &str) { if (str.empty()) return false; @@ -616,12 +611,10 @@ TextDrawer::TextDrawer(const wchar_t *text, Client *client, if (e.font) { e.dim.Width = e.font->getDimension(e.text.c_str()).Width; e.dim.Height = e.font->getDimension(L"Yy").Height; -#if USE_FREETYPE if (e.font->getType() == irr::gui::EGFT_CUSTOM) { - e.baseline = e.dim.Height - 1 - - ((irr::gui::CGUITTFont *)e.font)->getAscender() / 64; + CGUITTFont *tmp = static_cast<CGUITTFont*>(e.font); + e.baseline = e.dim.Height - 1 - tmp->getAscender() / 64; } -#endif } else { e.dim = {0, 0}; } diff --git a/src/gui/guiHyperText.h b/src/gui/guiHyperText.h index 5b936262e..04c664df5 100644 --- a/src/gui/guiHyperText.h +++ b/src/gui/guiHyperText.h @@ -19,16 +19,17 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "config.h" // for USE_FREETYPE +#include <vector> +#include <list> +#include <unordered_map> +#include <string> +#include "irrlichttypes_extrabloated.h" using namespace irr; class ISimpleTextureSource; class Client; - -#if USE_FREETYPE -#include "irrlicht_changes/CGUITTFont.h" -#endif +class GUIScrollBar; class ParsedText { diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp index 4dcb47779..29d5138f0 100644 --- a/src/gui/guiKeyChangeMenu.cpp +++ b/src/gui/guiKeyChangeMenu.cpp @@ -46,7 +46,7 @@ enum GUI_ID_KEY_BACKWARD_BUTTON, GUI_ID_KEY_LEFT_BUTTON, GUI_ID_KEY_RIGHT_BUTTON, - GUI_ID_KEY_USE_BUTTON, + GUI_ID_KEY_AUX1_BUTTON, GUI_ID_KEY_FLY_BUTTON, GUI_ID_KEY_FAST_BUTTON, GUI_ID_KEY_JUMP_BUTTON, @@ -70,6 +70,7 @@ enum GUI_ID_KEY_MINIMAP_BUTTON, GUI_ID_KEY_SCREENSHOT_BUTTON, GUI_ID_KEY_CHATLOG_BUTTON, + GUI_ID_KEY_BLOCK_BOUNDS_BUTTON, GUI_ID_KEY_HUD_BUTTON, GUI_ID_KEY_FOG_BUTTON, GUI_ID_KEY_DEC_RANGE_BUTTON, @@ -177,7 +178,7 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) { core::rect<s32> rect(0, 0, option_w, 30 * s); rect += topleft + v2s32(option_x, option_y); - const wchar_t *text = wgettext("\"Special\" = climb down"); + const wchar_t *text = wgettext("\"Aux1\" = climb down"); Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this, GUI_ID_CB_AUX1_DESCENDS, text); delete[] text; @@ -412,37 +413,38 @@ void GUIKeyChangeMenu::add_key(int id, const wchar_t *button_name, const std::st void GUIKeyChangeMenu::init_keys() { - this->add_key(GUI_ID_KEY_FORWARD_BUTTON, wgettext("Forward"), "keymap_forward"); - this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, wgettext("Backward"), "keymap_backward"); - this->add_key(GUI_ID_KEY_LEFT_BUTTON, wgettext("Left"), "keymap_left"); - this->add_key(GUI_ID_KEY_RIGHT_BUTTON, wgettext("Right"), "keymap_right"); - this->add_key(GUI_ID_KEY_USE_BUTTON, wgettext("Special"), "keymap_special1"); - this->add_key(GUI_ID_KEY_JUMP_BUTTON, wgettext("Jump"), "keymap_jump"); - this->add_key(GUI_ID_KEY_SNEAK_BUTTON, wgettext("Sneak"), "keymap_sneak"); - this->add_key(GUI_ID_KEY_DROP_BUTTON, wgettext("Drop"), "keymap_drop"); - this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"), "keymap_inventory"); - this->add_key(GUI_ID_KEY_HOTBAR_PREV_BUTTON,wgettext("Prev. item"), "keymap_hotbar_previous"); - this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON,wgettext("Next item"), "keymap_hotbar_next"); - this->add_key(GUI_ID_KEY_ZOOM_BUTTON, wgettext("Zoom"), "keymap_zoom"); - this->add_key(GUI_ID_KEY_CAMERA_BUTTON, wgettext("Change camera"), "keymap_camera_mode"); - this->add_key(GUI_ID_KEY_MINIMAP_BUTTON, wgettext("Toggle minimap"), "keymap_minimap"); - this->add_key(GUI_ID_KEY_FLY_BUTTON, wgettext("Toggle fly"), "keymap_freemove"); - this->add_key(GUI_ID_KEY_PITCH_MOVE, wgettext("Toggle pitchmove"), "keymap_pitchmove"); - this->add_key(GUI_ID_KEY_FAST_BUTTON, wgettext("Toggle fast"), "keymap_fastmove"); - this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, wgettext("Toggle noclip"), "keymap_noclip"); - this->add_key(GUI_ID_KEY_MUTE_BUTTON, wgettext("Mute"), "keymap_mute"); - this->add_key(GUI_ID_KEY_DEC_VOLUME_BUTTON,wgettext("Dec. volume"), "keymap_decrease_volume"); - this->add_key(GUI_ID_KEY_INC_VOLUME_BUTTON,wgettext("Inc. volume"), "keymap_increase_volume"); - this->add_key(GUI_ID_KEY_AUTOFWD_BUTTON, wgettext("Autoforward"), "keymap_autoforward"); - this->add_key(GUI_ID_KEY_CHAT_BUTTON, wgettext("Chat"), "keymap_chat"); - this->add_key(GUI_ID_KEY_SCREENSHOT_BUTTON,wgettext("Screenshot"), "keymap_screenshot"); - this->add_key(GUI_ID_KEY_RANGE_BUTTON, wgettext("Range select"), "keymap_rangeselect"); - this->add_key(GUI_ID_KEY_DEC_RANGE_BUTTON, wgettext("Dec. range"), "keymap_decrease_viewing_range_min"); - this->add_key(GUI_ID_KEY_INC_RANGE_BUTTON, wgettext("Inc. range"), "keymap_increase_viewing_range_min"); - this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, wgettext("Console"), "keymap_console"); - this->add_key(GUI_ID_KEY_CMD_BUTTON, wgettext("Command"), "keymap_cmd"); - this->add_key(GUI_ID_KEY_CMD_LOCAL_BUTTON, wgettext("Local command"), "keymap_cmd_local"); - this->add_key(GUI_ID_KEY_HUD_BUTTON, wgettext("Toggle HUD"), "keymap_toggle_hud"); - this->add_key(GUI_ID_KEY_CHATLOG_BUTTON, wgettext("Toggle chat log"), "keymap_toggle_chat"); - this->add_key(GUI_ID_KEY_FOG_BUTTON, wgettext("Toggle fog"), "keymap_toggle_fog"); + this->add_key(GUI_ID_KEY_FORWARD_BUTTON, wgettext("Forward"), "keymap_forward"); + this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, wgettext("Backward"), "keymap_backward"); + this->add_key(GUI_ID_KEY_LEFT_BUTTON, wgettext("Left"), "keymap_left"); + this->add_key(GUI_ID_KEY_RIGHT_BUTTON, wgettext("Right"), "keymap_right"); + this->add_key(GUI_ID_KEY_AUX1_BUTTON, wgettext("Aux1"), "keymap_aux1"); + this->add_key(GUI_ID_KEY_JUMP_BUTTON, wgettext("Jump"), "keymap_jump"); + this->add_key(GUI_ID_KEY_SNEAK_BUTTON, wgettext("Sneak"), "keymap_sneak"); + this->add_key(GUI_ID_KEY_DROP_BUTTON, wgettext("Drop"), "keymap_drop"); + this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"), "keymap_inventory"); + this->add_key(GUI_ID_KEY_HOTBAR_PREV_BUTTON, wgettext("Prev. item"), "keymap_hotbar_previous"); + this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON, wgettext("Next item"), "keymap_hotbar_next"); + this->add_key(GUI_ID_KEY_ZOOM_BUTTON, wgettext("Zoom"), "keymap_zoom"); + this->add_key(GUI_ID_KEY_CAMERA_BUTTON, wgettext("Change camera"), "keymap_camera_mode"); + this->add_key(GUI_ID_KEY_MINIMAP_BUTTON, wgettext("Toggle minimap"), "keymap_minimap"); + this->add_key(GUI_ID_KEY_FLY_BUTTON, wgettext("Toggle fly"), "keymap_freemove"); + this->add_key(GUI_ID_KEY_PITCH_MOVE, wgettext("Toggle pitchmove"), "keymap_pitchmove"); + this->add_key(GUI_ID_KEY_FAST_BUTTON, wgettext("Toggle fast"), "keymap_fastmove"); + this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, wgettext("Toggle noclip"), "keymap_noclip"); + this->add_key(GUI_ID_KEY_MUTE_BUTTON, wgettext("Mute"), "keymap_mute"); + this->add_key(GUI_ID_KEY_DEC_VOLUME_BUTTON, wgettext("Dec. volume"), "keymap_decrease_volume"); + this->add_key(GUI_ID_KEY_INC_VOLUME_BUTTON, wgettext("Inc. volume"), "keymap_increase_volume"); + this->add_key(GUI_ID_KEY_AUTOFWD_BUTTON, wgettext("Autoforward"), "keymap_autoforward"); + this->add_key(GUI_ID_KEY_CHAT_BUTTON, wgettext("Chat"), "keymap_chat"); + this->add_key(GUI_ID_KEY_SCREENSHOT_BUTTON, wgettext("Screenshot"), "keymap_screenshot"); + this->add_key(GUI_ID_KEY_RANGE_BUTTON, wgettext("Range select"), "keymap_rangeselect"); + this->add_key(GUI_ID_KEY_DEC_RANGE_BUTTON, wgettext("Dec. range"), "keymap_decrease_viewing_range_min"); + this->add_key(GUI_ID_KEY_INC_RANGE_BUTTON, wgettext("Inc. range"), "keymap_increase_viewing_range_min"); + this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, wgettext("Console"), "keymap_console"); + this->add_key(GUI_ID_KEY_CMD_BUTTON, wgettext("Command"), "keymap_cmd"); + this->add_key(GUI_ID_KEY_CMD_LOCAL_BUTTON, wgettext("Local command"), "keymap_cmd_local"); + this->add_key(GUI_ID_KEY_BLOCK_BOUNDS_BUTTON, wgettext("Block bounds"), "keymap_toggle_block_bounds"); + this->add_key(GUI_ID_KEY_HUD_BUTTON, wgettext("Toggle HUD"), "keymap_toggle_hud"); + this->add_key(GUI_ID_KEY_CHATLOG_BUTTON, wgettext("Toggle chat log"), "keymap_toggle_chat"); + this->add_key(GUI_ID_KEY_FOG_BUTTON, wgettext("Toggle fog"), "keymap_toggle_fog"); } diff --git a/src/gui/guiPasswordChange.cpp b/src/gui/guiPasswordChange.cpp index 74cd62f5b..c983260f6 100644 --- a/src/gui/guiPasswordChange.cpp +++ b/src/gui/guiPasswordChange.cpp @@ -25,6 +25,10 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include <IGUIStaticText.h> #include <IGUIFont.h> +#ifdef HAVE_TOUCHSCREENGUI + #include "client/renderingengine.h" +#endif + #include "porting.h" #include "gettext.h" @@ -79,8 +83,8 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize) /* Calculate new sizes and positions */ -#ifdef __ANDROID__ - const float s = m_gui_scale * porting::getDisplayDensity() / 2; +#ifdef HAVE_TOUCHSCREENGUI + const float s = m_gui_scale * RenderingEngine::getDisplayDensity() / 2; #else const float s = m_gui_scale; #endif diff --git a/src/gui/guiScene.cpp b/src/gui/guiScene.cpp index 5f4c50b91..ee2556b03 100644 --- a/src/gui/guiScene.cpp +++ b/src/gui/guiScene.cpp @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <SViewFrustum.h> #include <IAnimatedMeshSceneNode.h> -#include <ILightSceneNode.h> #include "porting.h" GUIScene::GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr, @@ -34,9 +33,6 @@ GUIScene::GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr, m_cam = m_smgr->addCameraSceneNode(0, v3f(0.f, 0.f, -100.f), v3f(0.f)); m_cam->setFOV(30.f * core::DEGTORAD); - scene::ILightSceneNode *light = m_smgr->addLightSceneNode(m_cam); - light->setRadius(1000.f); - m_smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true); } @@ -60,6 +56,7 @@ scene::IAnimatedMeshSceneNode *GUIScene::setMesh(scene::IAnimatedMesh *mesh) m_mesh = m_smgr->addAnimatedMeshSceneNode(mesh); m_mesh->setPosition(-m_mesh->getBoundingBox().getCenter()); m_mesh->animateJoints(); + return m_mesh; } @@ -73,10 +70,13 @@ void GUIScene::setTexture(u32 idx, video::ITexture *texture) material.setFlag(video::EMF_FOG_ENABLE, true); material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_BACK_FACE_CULLING, false); + material.setFlag(video::EMF_ZWRITE_ENABLE, true); } void GUIScene::draw() { + m_driver->clearBuffers(video::ECBF_DEPTH); + // Control rotation speed based on time u64 new_time = porting::getTimeMs(); u64 dtime_ms = 0; @@ -161,6 +161,14 @@ void GUIScene::setFrameLoop(s32 begin, s32 end) m_mesh->setFrameLoop(begin, end); } +/** + * Sets the animation speed (FPS) for the mesh + */ +void GUIScene::setAnimationSpeed(f32 speed) +{ + m_mesh->setAnimationSpeed(speed); +} + /* Camera control functions */ inline void GUIScene::calcOptimalDistance() diff --git a/src/gui/guiScene.h b/src/gui/guiScene.h index 08eb7f350..0f5f3a891 100644 --- a/src/gui/guiScene.h +++ b/src/gui/guiScene.h @@ -37,6 +37,7 @@ public: void setTexture(u32 idx, video::ITexture *texture); void setBackgroundColor(const video::SColor &color) noexcept { m_bgcolor = color; }; void setFrameLoop(s32 begin, s32 end); + void setAnimationSpeed(f32 speed); void enableMouseControl(bool enable) noexcept { m_mouse_ctrl = enable; }; void setRotation(v2f rot) noexcept { m_custom_rot = rot; }; void enableContinuousRotation(bool enable) noexcept { m_inf_rot = enable; }; diff --git a/src/gui/guiSkin.cpp b/src/gui/guiSkin.cpp index e09209bd9..ca692f6cb 100644 --- a/src/gui/guiSkin.cpp +++ b/src/gui/guiSkin.cpp @@ -1024,48 +1024,6 @@ void GUISkin::draw2DRectangle(IGUIElement* element, }
-//! Writes attributes of the object.
-//! Implement this to expose the attributes of your scene node animator for
-//! scripting languages, editors, debuggers or xml serialization purposes.
-void GUISkin::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
-{
- u32 i;
- for (i=0; i<EGDC_COUNT; ++i)
- out->addColor(GUISkinColorNames[i], Colors[i]);
-
- for (i=0; i<EGDS_COUNT; ++i)
- out->addInt(GUISkinSizeNames[i], Sizes[i]);
-
- for (i=0; i<EGDT_COUNT; ++i)
- out->addString(GUISkinTextNames[i], Texts[i].c_str());
-
- for (i=0; i<EGDI_COUNT; ++i)
- out->addInt(GUISkinIconNames[i], Icons[i]);
-}
-
-
-//! Reads attributes of the object.
-//! Implement this to set the attributes of your scene node animator for
-//! scripting languages, editors, debuggers or xml deserialization purposes.
-void GUISkin::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
-{
- // TODO: This is not nice code for downward compatibility, whenever new values are added and users
- // load an old skin the corresponding values will be set to 0.
- u32 i;
- for (i=0; i<EGDC_COUNT; ++i)
- Colors[i] = in->getAttributeAsColor(GUISkinColorNames[i]);
-
- for (i=0; i<EGDS_COUNT; ++i)
- Sizes[i] = in->getAttributeAsInt(GUISkinSizeNames[i]);
-
- for (i=0; i<EGDT_COUNT; ++i)
- Texts[i] = in->getAttributeAsStringW(GUISkinTextNames[i]);
-
- for (i=0; i<EGDI_COUNT; ++i)
- Icons[i] = in->getAttributeAsInt(GUISkinIconNames[i]);
-}
-
-
//! gets the colors
// PATCH
void GUISkin::getColors(video::SColor* colors)
diff --git a/src/gui/guiSkin.h b/src/gui/guiSkin.h index bbb900f9f..fa9b27bdd 100644 --- a/src/gui/guiSkin.h +++ b/src/gui/guiSkin.h @@ -290,16 +290,6 @@ namespace gui //! get the type of this skin
virtual EGUI_SKIN_TYPE getType() const;
- //! Writes attributes of the object.
- //! Implement this to expose the attributes of your scene node animator for
- //! scripting languages, editors, debuggers or xml serialization purposes.
- virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const;
-
- //! Reads attributes of the object.
- //! Implement this to set the attributes of your scene node animator for
- //! scripting languages, editors, debuggers or xml deserialization purposes.
- virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0);
-
//! gets the colors
virtual void getColors(video::SColor* colors); // ::PATCH:
diff --git a/src/gui/guiTable.cpp b/src/gui/guiTable.cpp index cab2e19fd..79ae1aea3 100644 --- a/src/gui/guiTable.cpp +++ b/src/gui/guiTable.cpp @@ -77,9 +77,10 @@ GUITable::GUITable(gui::IGUIEnvironment *env, setTabStop(true); setTabOrder(-1); updateAbsolutePosition(); +#ifdef HAVE_TOUCHSCREENGUI + float density = 1; // dp scaling is applied by the skin +#else float density = RenderingEngine::getDisplayDensity(); -#ifdef __ANDROID__ - density = 1; // dp scaling is applied by the skin #endif core::rect<s32> relative_rect = m_scrollbar->getRelativePosition(); s32 width = (relative_rect.getWidth() / (2.0 / 3.0)) * density * diff --git a/src/gui/guiVolumeChange.cpp b/src/gui/guiVolumeChange.cpp index f17cfa986..61ab758a1 100644 --- a/src/gui/guiVolumeChange.cpp +++ b/src/gui/guiVolumeChange.cpp @@ -93,11 +93,12 @@ void GUIVolumeChange::regenerateGui(v2u32 screensize) core::rect<s32> rect(0, 0, 160 * s, 20 * s); rect = rect + v2s32(size.X / 2 - 80 * s, size.Y / 2 - 70 * s); - const wchar_t *text = wgettext("Sound Volume: "); + wchar_t text[100]; + const wchar_t *str = wgettext("Sound Volume: %d%%"); + swprintf(text, sizeof(text) / sizeof(wchar_t), str, volume); + delete[] str; core::stringw volume_text = text; - delete [] text; - volume_text += core::stringw(volume) + core::stringw("%"); Environment->addStaticText(volume_text.c_str(), rect, false, true, this, ID_soundText); } @@ -183,11 +184,13 @@ bool GUIVolumeChange::OnEvent(const SEvent& event) g_settings->setFloat("sound_volume", (float) pos / 100); gui::IGUIElement *e = getElementFromId(ID_soundText); - const wchar_t *text = wgettext("Sound Volume: "); + wchar_t text[100]; + const wchar_t *str = wgettext("Sound Volume: %d%%"); + swprintf(text, sizeof(text) / sizeof(wchar_t), str, pos); + delete[] str; + core::stringw volume_text = text; - delete [] text; - volume_text += core::stringw(pos) + core::stringw("%"); e->setText(volume_text.c_str()); return true; } diff --git a/src/gui/intlGUIEditBox.cpp b/src/gui/intlGUIEditBox.cpp deleted file mode 100644 index 0f09ea746..000000000 --- a/src/gui/intlGUIEditBox.cpp +++ /dev/null @@ -1,626 +0,0 @@ -// 11.11.2011 11:11 ValkaTR -// -// This is a copy of intlGUIEditBox from the irrlicht, but with a -// fix in the OnEvent function, which doesn't allowed input of -// other keyboard layouts than latin-1 -// -// Characters like: ä ö ü õ ы й ю я ъ № € ° ... -// -// This fix is only needed for linux, because of a bug -// in the CIrrDeviceLinux.cpp:1014-1015 of the irrlicht -// -// Also locale in the programm should not be changed to -// a "C", "POSIX" or whatever, it should be set to "", -// or XLookupString will return nothing for the international -// characters. -// -// From the "man setlocale": -// -// On startup of the main program, the portable "C" locale -// is selected as default. A program may be made -// portable to all locales by calling: -// -// setlocale(LC_ALL, ""); -// -// after program initialization.... -// - -// Copyright (C) 2002-2013 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#include <util/numeric.h> -#include "intlGUIEditBox.h" - -#include "IGUISkin.h" -#include "IGUIEnvironment.h" -#include "IGUIFont.h" -#include "IVideoDriver.h" -//#include "irrlicht/os.cpp" -#include "porting.h" -//#include "Keycodes.h" -#include "log.h" - -/* - todo: - optional scrollbars - ctrl+left/right to select word - double click/ctrl click: word select + drag to select whole words, triple click to select line - optional? dragging selected text - numerical -*/ - -namespace irr -{ -namespace gui -{ - -//! constructor -intlGUIEditBox::intlGUIEditBox(const wchar_t* text, bool border, - IGUIEnvironment* environment, IGUIElement* parent, s32 id, - const core::rect<s32>& rectangle, bool writable, bool has_vscrollbar) - : GUIEditBox(environment, parent, id, rectangle, border, writable) -{ - #ifdef _DEBUG - setDebugName("intlintlGUIEditBox"); - #endif - - Text = text; - - if (Environment) - m_operator = Environment->getOSOperator(); - - if (m_operator) - m_operator->grab(); - - // this element can be tabbed to - setTabStop(true); - setTabOrder(-1); - - IGUISkin *skin = 0; - if (Environment) - skin = Environment->getSkin(); - if (m_border && skin) - { - m_frame_rect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1; - m_frame_rect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; - m_frame_rect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1; - m_frame_rect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; - } - - if (skin && has_vscrollbar) { - m_scrollbar_width = skin->getSize(gui::EGDS_SCROLLBAR_SIZE); - - if (m_scrollbar_width > 0) { - createVScrollBar(); - } - } - - breakText(); - - calculateScrollPos(); - setWritable(writable); -} - -//! Sets whether to draw the background -void intlGUIEditBox::setDrawBackground(bool draw) -{ -} - -void intlGUIEditBox::updateAbsolutePosition() -{ - core::rect<s32> oldAbsoluteRect(AbsoluteRect); - IGUIElement::updateAbsolutePosition(); - if ( oldAbsoluteRect != AbsoluteRect ) - { - breakText(); - } -} - - -//! draws the element and its children -void intlGUIEditBox::draw() -{ - if (!IsVisible) - return; - - const bool focus = Environment->hasFocus(this); - - IGUISkin* skin = Environment->getSkin(); - if (!skin) - return; - - m_frame_rect = AbsoluteRect; - - // draw the border - - if (m_border) - { - if (m_writable) { - skin->draw3DSunkenPane(this, skin->getColor(EGDC_WINDOW), - false, true, m_frame_rect, &AbsoluteClippingRect); - } - - m_frame_rect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1; - m_frame_rect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; - m_frame_rect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1; - m_frame_rect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; - } - - updateVScrollBar(); - core::rect<s32> localClipRect = m_frame_rect; - localClipRect.clipAgainst(AbsoluteClippingRect); - - // draw the text - - IGUIFont* font = m_override_font; - if (!m_override_font) - font = skin->getFont(); - - s32 cursorLine = 0; - s32 charcursorpos = 0; - - if (font) - { - if (m_last_break_font != font) - { - breakText(); - } - - // calculate cursor pos - - core::stringw *txtLine = &Text; - s32 startPos = 0; - - core::stringw s, s2; - - // get mark position - const bool ml = (!m_passwordbox && (m_word_wrap || m_multiline)); - const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end; - const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin; - const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0; - const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1; - const s32 lineCount = ml ? m_broken_text.size() : 1; - - // Save the override color information. - // Then, alter it if the edit box is disabled. - const bool prevOver = m_override_color_enabled; - const video::SColor prevColor = m_override_color; - - if (!Text.empty()) { - if (!IsEnabled && !m_override_color_enabled) - { - m_override_color_enabled = true; - m_override_color = skin->getColor(EGDC_GRAY_TEXT); - } - - for (s32 i=0; i < lineCount; ++i) - { - setTextRect(i); - - // clipping test - don't draw anything outside the visible area - core::rect<s32> c = localClipRect; - c.clipAgainst(m_current_text_rect); - if (!c.isValid()) - continue; - - // get current line - if (m_passwordbox) - { - if (m_broken_text.size() != 1) - { - m_broken_text.clear(); - m_broken_text.emplace_back(); - } - if (m_broken_text[0].size() != Text.size()) - { - m_broken_text[0] = Text; - for (u32 q = 0; q < Text.size(); ++q) - { - m_broken_text[0] [q] = m_passwordchar; - } - } - txtLine = &m_broken_text[0]; - startPos = 0; - } - else - { - txtLine = ml ? &m_broken_text[i] : &Text; - startPos = ml ? m_broken_text_positions[i] : 0; - } - - - // draw normal text - font->draw(txtLine->c_str(), m_current_text_rect, - m_override_color_enabled ? m_override_color : skin->getColor(EGDC_BUTTON_TEXT), - false, true, &localClipRect); - - // draw mark and marked text - if (focus && m_mark_begin != m_mark_end && i >= hlineStart && i < hlineStart + hlineCount) - { - - s32 mbegin = 0, mend = 0; - s32 lineStartPos = 0, lineEndPos = txtLine->size(); - - if (i == hlineStart) - { - // highlight start is on this line - s = txtLine->subString(0, realmbgn - startPos); - mbegin = font->getDimension(s.c_str()).Width; - - // deal with kerning - mbegin += font->getKerningWidth( - &((*txtLine)[realmbgn - startPos]), - realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0); - - lineStartPos = realmbgn - startPos; - } - if (i == hlineStart + hlineCount - 1) - { - // highlight end is on this line - s2 = txtLine->subString(0, realmend - startPos); - mend = font->getDimension(s2.c_str()).Width; - lineEndPos = (s32)s2.size(); - } - else - mend = font->getDimension(txtLine->c_str()).Width; - - m_current_text_rect.UpperLeftCorner.X += mbegin; - m_current_text_rect.LowerRightCorner.X = m_current_text_rect.UpperLeftCorner.X + mend - mbegin; - - // draw mark - skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), m_current_text_rect, &localClipRect); - - // draw marked text - s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos); - - if (!s.empty()) - font->draw(s.c_str(), m_current_text_rect, - m_override_color_enabled ? m_override_color : skin->getColor(EGDC_HIGH_LIGHT_TEXT), - false, true, &localClipRect); - - } - } - - // Return the override color information to its previous settings. - m_override_color_enabled = prevOver; - m_override_color = prevColor; - } - - // draw cursor - - if (m_word_wrap || m_multiline) - { - cursorLine = getLineFromPos(m_cursor_pos); - txtLine = &m_broken_text[cursorLine]; - startPos = m_broken_text_positions[cursorLine]; - } - s = txtLine->subString(0,m_cursor_pos-startPos); - charcursorpos = font->getDimension(s.c_str()).Width + - font->getKerningWidth(L"_", m_cursor_pos-startPos > 0 ? &((*txtLine)[m_cursor_pos-startPos-1]) : 0); - - if (m_writable) { - if (focus && (porting::getTimeMs() - m_blink_start_time) % 700 < 350) { - setTextRect(cursorLine); - m_current_text_rect.UpperLeftCorner.X += charcursorpos; - - font->draw(L"_", m_current_text_rect, - m_override_color_enabled ? m_override_color : skin->getColor(EGDC_BUTTON_TEXT), - false, true, &localClipRect); - } - } - } - - // draw children - IGUIElement::draw(); -} - - -s32 intlGUIEditBox::getCursorPos(s32 x, s32 y) -{ - IGUIFont* font = getActiveFont(); - - const u32 lineCount = (m_word_wrap || m_multiline) ? m_broken_text.size() : 1; - - core::stringw *txtLine = NULL; - s32 startPos = 0; - u32 curr_line_idx = 0; - x += 3; - - for (; curr_line_idx < lineCount; ++curr_line_idx) { - setTextRect(curr_line_idx); - if (curr_line_idx == 0 && y < m_current_text_rect.UpperLeftCorner.Y) - y = m_current_text_rect.UpperLeftCorner.Y; - if (curr_line_idx == lineCount - 1 && y > m_current_text_rect.LowerRightCorner.Y) - y = m_current_text_rect.LowerRightCorner.Y; - - // is it inside this region? - if (y >= m_current_text_rect.UpperLeftCorner.Y && y <= m_current_text_rect.LowerRightCorner.Y) { - // we've found the clicked line - txtLine = (m_word_wrap || m_multiline) ? &m_broken_text[curr_line_idx] : &Text; - startPos = (m_word_wrap || m_multiline) ? m_broken_text_positions[curr_line_idx] : 0; - break; - } - } - - if (x < m_current_text_rect.UpperLeftCorner.X) - x = m_current_text_rect.UpperLeftCorner.X; - else if (x > m_current_text_rect.LowerRightCorner.X) - x = m_current_text_rect.LowerRightCorner.X; - - s32 idx = font->getCharacterFromPos(txtLine->c_str(), x - m_current_text_rect.UpperLeftCorner.X); - // Special handling for last line, if we are on limits, add 1 extra shift because idx - // will be the last char, not null char of the wstring - if (curr_line_idx == lineCount - 1 && x == m_current_text_rect.LowerRightCorner.X) - idx++; - - return rangelim(idx + startPos, 0, S32_MAX); -} - - -//! Breaks the single text line. -void intlGUIEditBox::breakText() -{ - IGUISkin* skin = Environment->getSkin(); - - if ((!m_word_wrap && !m_multiline) || !skin) - return; - - m_broken_text.clear(); // need to reallocate :/ - m_broken_text_positions.clear(); - - IGUIFont* font = m_override_font; - if (!m_override_font) - font = skin->getFont(); - - if (!font) - return; - - m_last_break_font = font; - - core::stringw line; - core::stringw word; - core::stringw whitespace; - s32 lastLineStart = 0; - s32 size = Text.size(); - s32 length = 0; - s32 elWidth = RelativeRect.getWidth() - m_scrollbar_width - 10; - wchar_t c; - - for (s32 i=0; i<size; ++i) - { - c = Text[i]; - bool lineBreak = false; - - if (c == L'\r') // Mac or Windows breaks - { - lineBreak = true; - c = ' '; - if (Text[i+1] == L'\n') // Windows breaks - { - Text.erase(i+1); - --size; - } - } - else if (c == L'\n') // Unix breaks - { - lineBreak = true; - c = ' '; - } - - // don't break if we're not a multi-line edit box - if (!m_multiline) - lineBreak = false; - - if (c == L' ' || c == 0 || i == (size-1)) - { - if (!word.empty()) { - // here comes the next whitespace, look if - // we can break the last word to the next line. - s32 whitelgth = font->getDimension(whitespace.c_str()).Width; - s32 worldlgth = font->getDimension(word.c_str()).Width; - - if (m_word_wrap && length + worldlgth + whitelgth > elWidth) - { - // break to next line - length = worldlgth; - m_broken_text.push_back(line); - m_broken_text_positions.push_back(lastLineStart); - lastLineStart = i - (s32)word.size(); - line = word; - } - else - { - // add word to line - line += whitespace; - line += word; - length += whitelgth + worldlgth; - } - - word = L""; - whitespace = L""; - } - - whitespace += c; - - // compute line break - if (lineBreak) - { - line += whitespace; - line += word; - m_broken_text.push_back(line); - m_broken_text_positions.push_back(lastLineStart); - lastLineStart = i+1; - line = L""; - word = L""; - whitespace = L""; - length = 0; - } - } - else - { - // yippee this is a word.. - word += c; - } - } - - line += whitespace; - line += word; - m_broken_text.push_back(line); - m_broken_text_positions.push_back(lastLineStart); -} - - -void intlGUIEditBox::setTextRect(s32 line) -{ - core::dimension2du d; - - IGUISkin* skin = Environment->getSkin(); - if (!skin) - return; - - IGUIFont* font = m_override_font ? m_override_font : skin->getFont(); - - if (!font) - return; - - // get text dimension - const u32 lineCount = (m_word_wrap || m_multiline) ? m_broken_text.size() : 1; - if (m_word_wrap || m_multiline) - { - d = font->getDimension(m_broken_text[line].c_str()); - } - else - { - d = font->getDimension(Text.c_str()); - d.Height = AbsoluteRect.getHeight(); - } - d.Height += font->getKerningHeight(); - - // justification - switch (m_halign) - { - case EGUIA_CENTER: - // align to h centre - m_current_text_rect.UpperLeftCorner.X = (m_frame_rect.getWidth()/2) - (d.Width/2); - m_current_text_rect.LowerRightCorner.X = (m_frame_rect.getWidth()/2) + (d.Width/2); - break; - case EGUIA_LOWERRIGHT: - // align to right edge - m_current_text_rect.UpperLeftCorner.X = m_frame_rect.getWidth() - d.Width; - m_current_text_rect.LowerRightCorner.X = m_frame_rect.getWidth(); - break; - default: - // align to left edge - m_current_text_rect.UpperLeftCorner.X = 0; - m_current_text_rect.LowerRightCorner.X = d.Width; - - } - - switch (m_valign) - { - case EGUIA_CENTER: - // align to v centre - m_current_text_rect.UpperLeftCorner.Y = - (m_frame_rect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line; - break; - case EGUIA_LOWERRIGHT: - // align to bottom edge - m_current_text_rect.UpperLeftCorner.Y = - m_frame_rect.getHeight() - lineCount*d.Height + d.Height*line; - break; - default: - // align to top edge - m_current_text_rect.UpperLeftCorner.Y = d.Height*line; - break; - } - - m_current_text_rect.UpperLeftCorner.X -= m_hscroll_pos; - m_current_text_rect.LowerRightCorner.X -= m_hscroll_pos; - m_current_text_rect.UpperLeftCorner.Y -= m_vscroll_pos; - m_current_text_rect.LowerRightCorner.Y = m_current_text_rect.UpperLeftCorner.Y + d.Height; - - m_current_text_rect += m_frame_rect.UpperLeftCorner; - -} - -void intlGUIEditBox::calculateScrollPos() -{ - if (!m_autoscroll) - return; - - // calculate horizontal scroll position - s32 cursLine = getLineFromPos(m_cursor_pos); - setTextRect(cursLine); - - // don't do horizontal scrolling when wordwrap is enabled. - if (!m_word_wrap) - { - // get cursor position - IGUISkin* skin = Environment->getSkin(); - if (!skin) - return; - IGUIFont* font = m_override_font ? m_override_font : skin->getFont(); - if (!font) - return; - - core::stringw *txtLine = m_multiline ? &m_broken_text[cursLine] : &Text; - s32 cPos = m_multiline ? m_cursor_pos - m_broken_text_positions[cursLine] : m_cursor_pos; - - s32 cStart = m_current_text_rect.UpperLeftCorner.X + m_hscroll_pos + - font->getDimension(txtLine->subString(0, cPos).c_str()).Width; - - s32 cEnd = cStart + font->getDimension(L"_ ").Width; - - if (m_frame_rect.LowerRightCorner.X < cEnd) - m_hscroll_pos = cEnd - m_frame_rect.LowerRightCorner.X; - else if (m_frame_rect.UpperLeftCorner.X > cStart) - m_hscroll_pos = cStart - m_frame_rect.UpperLeftCorner.X; - else - m_hscroll_pos = 0; - - // todo: adjust scrollbar - } - - if (!m_word_wrap && !m_multiline) - return; - - // vertical scroll position - if (m_frame_rect.LowerRightCorner.Y < m_current_text_rect.LowerRightCorner.Y) - m_vscroll_pos += m_current_text_rect.LowerRightCorner.Y - m_frame_rect.LowerRightCorner.Y; // scrolling downwards - else if (m_frame_rect.UpperLeftCorner.Y > m_current_text_rect.UpperLeftCorner.Y) - m_vscroll_pos += m_current_text_rect.UpperLeftCorner.Y - m_frame_rect.UpperLeftCorner.Y; // scrolling upwards - - // todo: adjust scrollbar - if (m_vscrollbar) - m_vscrollbar->setPos(m_vscroll_pos); -} - - -//! Create a vertical scrollbar -void intlGUIEditBox::createVScrollBar() -{ - s32 fontHeight = 1; - - if (m_override_font) { - fontHeight = m_override_font->getDimension(L"").Height; - } else { - if (IGUISkin* skin = Environment->getSkin()) { - if (IGUIFont* font = skin->getFont()) { - fontHeight = font->getDimension(L"").Height; - } - } - } - - irr::core::rect<s32> scrollbarrect = m_frame_rect; - scrollbarrect.UpperLeftCorner.X += m_frame_rect.getWidth() - m_scrollbar_width; - m_vscrollbar = new GUIScrollBar(Environment, getParent(), -1, - scrollbarrect, false, true); - - m_vscrollbar->setVisible(false); - m_vscrollbar->setSmallStep(3 * fontHeight); - m_vscrollbar->setLargeStep(10 * fontHeight); -} - -} // end namespace gui -} // end namespace irr diff --git a/src/gui/intlGUIEditBox.h b/src/gui/intlGUIEditBox.h deleted file mode 100644 index 007fe1c93..000000000 --- a/src/gui/intlGUIEditBox.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2002-2013 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#pragma once - -#include "IrrCompileConfig.h" -//#ifdef _IRR_COMPILE_WITH_GUI_ - -#include "guiEditBox.h" -#include "irrArray.h" -#include "IOSOperator.h" - -namespace irr -{ -namespace gui -{ - class intlGUIEditBox : public GUIEditBox - { - public: - - //! constructor - intlGUIEditBox(const wchar_t* text, bool border, IGUIEnvironment* environment, - IGUIElement* parent, s32 id, const core::rect<s32>& rectangle, - bool writable = true, bool has_vscrollbar = false); - - //! destructor - virtual ~intlGUIEditBox() {} - - //! Sets whether to draw the background - virtual void setDrawBackground(bool draw); - - virtual bool isDrawBackgroundEnabled() const { return true; } - - //! draws the element and its children - virtual void draw(); - - //! Updates the absolute position, splits text if required - virtual void updateAbsolutePosition(); - - virtual void setCursorChar(const wchar_t cursorChar) {} - - virtual wchar_t getCursorChar() const { return L'|'; } - - virtual void setCursorBlinkTime(u32 timeMs) {} - - virtual u32 getCursorBlinkTime() const { return 500; } - - protected: - //! Breaks the single text line. - virtual void breakText(); - //! sets the area of the given line - virtual void setTextRect(s32 line); - - //! calculates the current scroll position - void calculateScrollPos(); - - s32 getCursorPos(s32 x, s32 y); - - //! Create a vertical scrollbar - void createVScrollBar(); - }; - - -} // end namespace gui -} // end namespace irr - -//#endif // _IRR_COMPILE_WITH_GUI_ diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index 0d3fb55f0..56a5d2cb9 100644 --- a/src/gui/modalMenu.cpp +++ b/src/gui/modalMenu.cpp @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifdef HAVE_TOUCHSCREENGUI #include "touchscreengui.h" +#include "client/renderingengine.h" #endif // clang-format off @@ -40,8 +41,8 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, m_remap_dbl_click(remap_dbl_click) { m_gui_scale = g_settings->getFloat("gui_scaling"); -#ifdef __ANDROID__ - float d = porting::getDisplayDensity(); +#ifdef HAVE_TOUCHSCREENGUI + float d = RenderingEngine::getDisplayDensity(); m_gui_scale *= 1.1 - 0.3 * d + 0.2 * d * d; #endif setVisible(true); @@ -183,7 +184,7 @@ static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent) return false; } -#ifdef __ANDROID__ +#ifdef HAVE_TOUCHSCREENGUI bool GUIModalMenu::simulateMouseEvent( gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event) @@ -217,6 +218,8 @@ bool GUIModalMenu::simulateMouseEvent( void GUIModalMenu::enter(gui::IGUIElement *hovered) { + if (!hovered) + return; sanity_check(!m_hovered); m_hovered.grab(hovered); SEvent gui_event{}; @@ -268,7 +271,7 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) std::string label = wide_to_utf8(getLabelByID(hovered->getID())); if (label.empty()) label = "text"; - message += gettext(label) + ":"; + message += strgettext(label) + ":"; // single line text input int type = 2; @@ -286,7 +289,9 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) return retval; } } +#endif +#ifdef HAVE_TOUCHSCREENGUI if (event.EventType == EET_TOUCH_INPUT_EVENT) { irr_ptr<GUIModalMenu> holder; holder.grab(this); // keep this alive until return (it might be dropped downstream [?]) diff --git a/src/gui/modalMenu.h b/src/gui/modalMenu.h index ed0da3205..06e78f06b 100644 --- a/src/gui/modalMenu.h +++ b/src/gui/modalMenu.h @@ -75,10 +75,10 @@ protected: v2u32 m_screensize_old; float m_gui_scale; #ifdef __ANDROID__ - v2s32 m_down_pos; std::string m_jni_field_name; #endif #ifdef HAVE_TOUCHSCREENGUI + v2s32 m_down_pos; bool m_touchscreen_visible = true; #endif @@ -102,7 +102,7 @@ private: // wants to launch other menus bool m_allow_focus_removal = false; -#ifdef __ANDROID__ +#ifdef HAVE_TOUCHSCREENGUI irr_ptr<gui::IGUIElement> m_hovered; bool simulateMouseEvent(gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event); diff --git a/src/gui/profilergraph.cpp b/src/gui/profilergraph.cpp index b29285e2f..f71ef3799 100644 --- a/src/gui/profilergraph.cpp +++ b/src/gui/profilergraph.cpp @@ -94,20 +94,29 @@ void ProfilerGraph::draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver, show_min = 0; } - s32 texth = 15; + const s32 texth = 15; char buf[10]; - porting::mt_snprintf(buf, sizeof(buf), "%.3g", show_max); + if (floorf(show_max) == show_max) + porting::mt_snprintf(buf, sizeof(buf), "%.5g", show_max); + else + porting::mt_snprintf(buf, sizeof(buf), "%.3g", show_max); font->draw(utf8_to_wide(buf).c_str(), core::rect<s32>(textx, y - graphh, textx2, y - graphh + texth), meta.color); - porting::mt_snprintf(buf, sizeof(buf), "%.3g", show_min); + + if (floorf(show_min) == show_min) + porting::mt_snprintf(buf, sizeof(buf), "%.5g", show_min); + else + porting::mt_snprintf(buf, sizeof(buf), "%.3g", show_min); font->draw(utf8_to_wide(buf).c_str(), core::rect<s32>(textx, y - texth, textx2, y), meta.color); + font->draw(utf8_to_wide(id).c_str(), core::rect<s32>(textx, y - graphh / 2 - texth / 2, textx2, y - graphh / 2 + texth / 2), meta.color); + s32 graph1y = y; s32 graph1h = graphh; bool relativegraph = (show_min != 0 && show_min != show_max); diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index e1a971462..ebe1a6325 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -28,19 +28,18 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include "porting.h" #include "client/guiscalingfilter.h" +#include "client/renderingengine.h" #include <iostream> #include <algorithm> -#include <ISceneCollisionManager.h> - using namespace irr::core; const char **button_imagenames = (const char *[]) { "jump_btn.png", "down.png", "zoom.png", - "aux_btn.png" + "aux1_btn.png" }; const char **joystick_imagenames = (const char *[]) { @@ -80,8 +79,8 @@ static irr::EKEY_CODE id2keycode(touch_gui_button_id id) case zoom_id: key = "zoom"; break; - case special1_id: - key = "special1"; + case aux1_id: + key = "aux1"; break; case fly_id: key = "freemove"; @@ -425,10 +424,10 @@ TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver *receiver) 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_joystick_triggers_aux1 = g_settings->getBool("virtual_joystick_triggers_aux1"); m_screensize = m_device->getVideoDriver()->getScreenSize(); button_size = MYMIN(m_screensize.Y / 4.5f, - porting::getDisplayDensity() * + RenderingEngine::getDisplayDensity() * g_settings->getFloat("hud_scaling") * 65.0f); } @@ -521,9 +520,9 @@ void TouchScreenGUI::init(ISimpleTextureSource *tsrc) m_screensize.Y - (3 * button_size)), L"z", false); - // init special1/aux button - if (!m_joystick_triggers_special1) - initButton(special1_id, + // init aux1 button + if (!m_joystick_triggers_aux1) + initButton(aux1_id, rect<s32>(m_screensize.X - (1.25 * button_size), m_screensize.Y - (2.5 * button_size), m_screensize.X - (0.25 * button_size), @@ -670,9 +669,9 @@ void TouchScreenGUI::handleReleaseEvent(size_t evt_id) if (button != after_last_element_id) { // handle button events handleButtonEvent(button, evt_id, false); - } else if (evt_id == m_move_id) { + } else if (m_has_move_id && evt_id == m_move_id) { // handle the point used for moving view - m_move_id = -1; + m_has_move_id = false; // if this pointer issued a mouse event issue symmetric release here if (m_move_sent_as_mouse_event) { @@ -694,8 +693,8 @@ void TouchScreenGUI::handleReleaseEvent(size_t evt_id) } // handle joystick - else if (evt_id == m_joystick_id) { - m_joystick_id = -1; + else if (m_has_joystick_id && evt_id == m_joystick_id) { + m_has_joystick_id = false; // reset joystick for (unsigned int i = 0; i < 4; i++) @@ -778,7 +777,8 @@ void TouchScreenGUI::translateEvent(const SEvent &event) 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.0f)) { // If we don't already have a starting point for joystick make this the one. - if (m_joystick_id == -1) { + if (!m_has_joystick_id) { + m_has_joystick_id = true; m_joystick_id = event.TouchInput.ID; m_joystick_has_really_moved = false; @@ -798,7 +798,8 @@ void TouchScreenGUI::translateEvent(const SEvent &event) } } else { // If we don't already have a moving point make this the moving one. - if (m_move_id == -1) { + if (!m_has_move_id) { + m_has_move_id = true; m_move_id = event.TouchInput.ID; m_move_has_really_moved = false; m_move_downtime = porting::getTimeMs(); @@ -821,7 +822,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) v2s32(event.TouchInput.X, event.TouchInput.Y)) return; - if (m_move_id != -1) { + if (m_has_move_id) { if ((event.TouchInput.ID == m_move_id) && (!m_move_sent_as_mouse_event)) { @@ -864,7 +865,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) } } - if (m_joystick_id != -1 && event.TouchInput.ID == m_joystick_id) { + if (m_has_joystick_id && event.TouchInput.ID == m_joystick_id) { s32 X = event.TouchInput.X; s32 Y = event.TouchInput.Y; @@ -923,7 +924,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) } if (distance > button_size) { - m_joystick_status[j_special1] = true; + m_joystick_status[j_aux1] = true; // move joystick "button" s32 ndx = button_size * dx / distance - button_size / 2.0f; s32 ndy = button_size * dy / distance - button_size / 2.0f; @@ -943,7 +944,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) } } - if (m_move_id == -1 && m_joystick_id == -1) + if (!m_has_move_id && !m_has_joystick_id) handleChangedButton(event); } } @@ -1039,7 +1040,7 @@ bool TouchScreenGUI::doubleTapDetection() void TouchScreenGUI::applyJoystickStatus() { for (unsigned int i = 0; i < 5; i++) { - if (i == 4 && !m_joystick_triggers_special1) + if (i == 4 && !m_joystick_triggers_aux1) continue; SEvent translated{}; @@ -1088,7 +1089,7 @@ void TouchScreenGUI::step(float dtime) button.repeatcounter += dtime; // in case we're moving around digging does not happen - if (m_move_id != -1) + if (m_has_move_id) m_move_has_really_moved = true; if (button.repeatcounter < button.repeatdelay) @@ -1116,7 +1117,7 @@ void TouchScreenGUI::step(float dtime) } // if a new placed pointer isn't moved for some time start digging - if ((m_move_id != -1) && + if (m_has_move_id && (!m_move_has_really_moved) && (!m_move_sent_as_mouse_event)) { diff --git a/src/gui/touchscreengui.h b/src/gui/touchscreengui.h index 0349624fa..6b36c0d59 100644 --- a/src/gui/touchscreengui.h +++ b/src/gui/touchscreengui.h @@ -39,7 +39,7 @@ typedef enum jump_id = 0, crunch_id, zoom_id, - special1_id, + aux1_id, after_last_element_id, settings_starter_id, rare_controls_starter_id, @@ -69,7 +69,7 @@ typedef enum j_backward, j_left, j_right, - j_special1 + j_aux1 } touch_gui_joystick_move_id; typedef enum @@ -217,7 +217,7 @@ private: // forward, backward, left, right touch_gui_button_id m_joystick_names[5] = { - forward_id, backward_id, left_id, right_id, special1_id}; + forward_id, backward_id, left_id, right_id, aux1_id}; bool m_joystick_status[5] = {false, false, false, false, false}; /* @@ -228,16 +228,18 @@ private: */ line3d<f32> m_shootline; - int m_move_id = -1; + bool m_has_move_id = false; + size_t m_move_id; bool m_move_has_really_moved = false; u64 m_move_downtime = 0; bool m_move_sent_as_mouse_event = false; v2s32 m_move_downlocation = v2s32(-10000, -10000); - int m_joystick_id = -1; + bool m_has_joystick_id = false; + size_t m_joystick_id; bool m_joystick_has_really_moved = false; bool m_fixed_joystick = false; - bool m_joystick_triggers_special1 = false; + bool m_joystick_triggers_aux1 = false; button_info *m_joystick_btn_off = nullptr; button_info *m_joystick_btn_bg = nullptr; button_info *m_joystick_btn_center = nullptr; |