From 3dcf9e963e3d3c5d209cd3676c2f979a58c6c1ab Mon Sep 17 00:00:00 2001 From: TheBrokenRail <17478432+TheBrokenRail@users.noreply.github.com> Date: Sun, 26 Sep 2021 12:04:09 -0400 Subject: Touch UI support for desktop builds (#10729) --- src/gui/CMakeLists.txt | 6 ++++++ src/gui/guiConfirmRegistration.cpp | 10 +++++++--- src/gui/guiFormSpecMenu.cpp | 6 +++--- src/gui/guiPasswordChange.cpp | 8 ++++++-- src/gui/guiTable.cpp | 5 +++-- src/gui/modalMenu.cpp | 11 ++++++++--- src/gui/modalMenu.h | 4 ++-- src/gui/touchscreengui.cpp | 27 +++++++++++++++------------ src/gui/touchscreengui.h | 6 ++++-- 9 files changed, 54 insertions(+), 29 deletions(-) (limited to 'src/gui') diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 5552cebea..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 @@ -25,5 +30,6 @@ set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/modalMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/profilergraph.cpp + ${extra_gui_SRCS} PARENT_SCOPE ) diff --git a/src/gui/guiConfirmRegistration.cpp b/src/gui/guiConfirmRegistration.cpp index 4ca9a64ed..b8887a4af 100644 --- a/src/gui/guiConfirmRegistration.cpp +++ b/src/gui/guiConfirmRegistration.cpp @@ -28,6 +28,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #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 diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 797fd3ff6..938481fa2 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -308,7 +308,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)); @@ -3278,7 +3278,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) ((15.0 / 13.0) * (0.85 + mydata.invsize.Y)); } -#ifdef __ANDROID__ +#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; @@ -3741,7 +3741,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) 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 #include +#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/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 relative_rect = m_scrollbar->getRelativePosition(); s32 width = (relative_rect.getWidth() / (2.0 / 3.0)) * density * diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index 1016de389..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{}; @@ -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 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 m_hovered; bool simulateMouseEvent(gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event); diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index eb20b7e70..ebe1a6325 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -28,6 +28,7 @@ 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 #include @@ -426,7 +427,7 @@ TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver *receiver) 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); } @@ -668,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) { @@ -692,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++) @@ -776,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; @@ -796,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(); @@ -819,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)) { @@ -862,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; @@ -941,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); } } @@ -1086,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) @@ -1114,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 ad5abae87..6b36c0d59 100644 --- a/src/gui/touchscreengui.h +++ b/src/gui/touchscreengui.h @@ -228,13 +228,15 @@ private: */ line3d 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_aux1 = false; -- cgit v1.2.3 From d51d0f3a5a60679436bf7d4e1980f3a82f229848 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 27 Sep 2021 17:45:44 +0200 Subject: Various code improvements * Camera: Fix division by 0 after view bobbing * Remove ignored constness * Connection: Improve window size range limits --- src/client/camera.cpp | 3 ++- src/client/client.h | 8 ++++---- src/client/gameui.h | 2 +- src/gui/guiFormSpecMenu.h | 2 +- src/inventory.cpp | 2 +- src/inventory.h | 2 +- src/network/connection.cpp | 32 ++++++++++---------------------- src/network/connection.h | 30 +++++++++++++++++------------- src/network/networkpacket.h | 2 +- src/remoteplayer.cpp | 2 +- src/remoteplayer.h | 2 +- 11 files changed, 40 insertions(+), 47 deletions(-) (limited to 'src/gui') diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 2629a6359..48e60c433 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -378,7 +378,8 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r // Smoothen and invert the above fall_bobbing = sin(fall_bobbing * 0.5 * M_PI) * -1; // Amplify according to the intensity of the impact - fall_bobbing *= (1 - rangelim(50 / player->camera_impact, 0, 1)) * 5; + if (player->camera_impact > 0.0f) + fall_bobbing *= (1 - rangelim(50 / player->camera_impact, 0, 1)) * 5; fall_bobbing *= m_cache_fall_bobbing_amount; } diff --git a/src/client/client.h b/src/client/client.h index b0324ee90..b92b456f4 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -334,13 +334,13 @@ public: // disconnect client when CSM failed. const std::string &accessDeniedReason() const { return m_access_denied_reason; } - const bool itemdefReceived() const + bool itemdefReceived() const { return m_itemdef_received; } - const bool nodedefReceived() const + bool nodedefReceived() const { return m_nodedef_received; } - const bool mediaReceived() const + bool mediaReceived() const { return !m_media_downloader; } - const bool activeObjectsReceived() const + bool activeObjectsReceived() const { return m_activeobjects_received; } u16 getProtoVersion() diff --git a/src/client/gameui.h b/src/client/gameui.h index 2eb2488e6..3f31f1b57 100644 --- a/src/client/gameui.h +++ b/src/client/gameui.h @@ -84,7 +84,7 @@ public: void showTranslatedStatusText(const char *str); inline void clearStatusText() { m_statustext.clear(); } - const bool isChatVisible() + bool isChatVisible() { return m_flags.show_chat && m_recent_chat_count != 0 && m_profiler_current_page == 0; } diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 926de66d5..eee84eff6 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -229,7 +229,7 @@ public: return m_selected_item; } - const u16 getSelectedAmount() const + u16 getSelectedAmount() const { return m_selected_amount; } diff --git a/src/inventory.cpp b/src/inventory.cpp index da6517e62..029fcbf4f 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -995,7 +995,7 @@ const InventoryList *Inventory::getList(const std::string &name) const return m_lists[i]; } -const s32 Inventory::getListIndex(const std::string &name) const +s32 Inventory::getListIndex(const std::string &name) const { for(u32 i=0; i m_lists; IItemDefManager *m_itemdef; diff --git a/src/network/connection.cpp b/src/network/connection.cpp index a4970954f..548b2e3a0 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -578,7 +578,7 @@ u16 Channel::getOutgoingSequenceNumber(bool& successful) // ugly cast but this one is required in order to tell compiler we // know about difference of two unsigned may be negative in general // but we already made sure it won't happen in this case - if (((u16)(next_outgoing_seqnum - lowest_unacked_seqnumber)) > window_size) { + if (((u16)(next_outgoing_seqnum - lowest_unacked_seqnumber)) > m_window_size) { successful = false; return 0; } @@ -588,7 +588,7 @@ u16 Channel::getOutgoingSequenceNumber(bool& successful) // know about difference of two unsigned may be negative in general // but we already made sure it won't happen in this case if ((next_outgoing_seqnum + (u16)(SEQNUM_MAX - lowest_unacked_seqnumber)) > - window_size) { + m_window_size) { successful = false; return 0; } @@ -666,7 +666,7 @@ void Channel::UpdateTimers(float dtime) //packet_too_late = current_packet_too_late; packets_successful = current_packet_successful; - if (current_bytes_transfered > (unsigned int) (window_size*512/2)) { + if (current_bytes_transfered > (unsigned int) (m_window_size*512/2)) { reasonable_amount_of_data_transmitted = true; } current_packet_loss = 0; @@ -681,37 +681,25 @@ void Channel::UpdateTimers(float dtime) if (packets_successful > 0) { successful_to_lost_ratio = packet_loss/packets_successful; } else if (packet_loss > 0) { - window_size = std::max( - (window_size - 10), - MIN_RELIABLE_WINDOW_SIZE); + setWindowSize(m_window_size - 10); done = true; } if (!done) { - if ((successful_to_lost_ratio < 0.01f) && - (window_size < MAX_RELIABLE_WINDOW_SIZE)) { + if (successful_to_lost_ratio < 0.01f) { /* don't even think about increasing if we didn't even * use major parts of our window */ if (reasonable_amount_of_data_transmitted) - window_size = std::min( - (window_size + 100), - MAX_RELIABLE_WINDOW_SIZE); - } else if ((successful_to_lost_ratio < 0.05f) && - (window_size < MAX_RELIABLE_WINDOW_SIZE)) { + setWindowSize(m_window_size + 100); + } else if (successful_to_lost_ratio < 0.05f) { /* don't even think about increasing if we didn't even * use major parts of our window */ if (reasonable_amount_of_data_transmitted) - window_size = std::min( - (window_size + 50), - MAX_RELIABLE_WINDOW_SIZE); + setWindowSize(m_window_size + 50); } else if (successful_to_lost_ratio > 0.15f) { - window_size = std::max( - (window_size - 100), - MIN_RELIABLE_WINDOW_SIZE); + setWindowSize(m_window_size - 100); } else if (successful_to_lost_ratio > 0.1f) { - window_size = std::max( - (window_size - 50), - MIN_RELIABLE_WINDOW_SIZE); + setWindowSize(m_window_size - 50); } } } diff --git a/src/network/connection.h b/src/network/connection.h index 49bb65c3e..ea74ffb1c 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -420,34 +420,38 @@ public: void UpdateTimers(float dtime); - const float getCurrentDownloadRateKB() + float getCurrentDownloadRateKB() { MutexAutoLock lock(m_internal_mutex); return cur_kbps; }; - const float getMaxDownloadRateKB() + float getMaxDownloadRateKB() { MutexAutoLock lock(m_internal_mutex); return max_kbps; }; - const float getCurrentLossRateKB() + float getCurrentLossRateKB() { MutexAutoLock lock(m_internal_mutex); return cur_kbps_lost; }; - const float getMaxLossRateKB() + float getMaxLossRateKB() { MutexAutoLock lock(m_internal_mutex); return max_kbps_lost; }; - const float getCurrentIncomingRateKB() + float getCurrentIncomingRateKB() { MutexAutoLock lock(m_internal_mutex); return cur_incoming_kbps; }; - const float getMaxIncomingRateKB() + float getMaxIncomingRateKB() { MutexAutoLock lock(m_internal_mutex); return max_incoming_kbps; }; - const float getAvgDownloadRateKB() + float getAvgDownloadRateKB() { MutexAutoLock lock(m_internal_mutex); return avg_kbps; }; - const float getAvgLossRateKB() + float getAvgLossRateKB() { MutexAutoLock lock(m_internal_mutex); return avg_kbps_lost; }; - const float getAvgIncomingRateKB() + float getAvgIncomingRateKB() { MutexAutoLock lock(m_internal_mutex); return avg_incoming_kbps; }; - const unsigned int getWindowSize() const { return window_size; }; + u16 getWindowSize() const { return m_window_size; }; + + void setWindowSize(long size) + { + m_window_size = (u16)rangelim(size, MIN_RELIABLE_WINDOW_SIZE, MAX_RELIABLE_WINDOW_SIZE); + } - void setWindowSize(unsigned int size) { window_size = size; }; private: std::mutex m_internal_mutex; - int window_size = MIN_RELIABLE_WINDOW_SIZE; + u16 m_window_size = MIN_RELIABLE_WINDOW_SIZE; u16 next_incoming_seqnum = SEQNUM_INITIAL; @@ -765,7 +769,7 @@ public: Address GetPeerAddress(session_t peer_id); float getPeerStat(session_t peer_id, rtt_stat_type type); float getLocalStat(rate_stat_type type); - const u32 GetProtocolID() const { return m_protocol_id; }; + u32 GetProtocolID() const { return m_protocol_id; }; const std::string getDesc(); void DisconnectPeer(session_t peer_id); diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h index b1c44f055..b9c39f332 100644 --- a/src/network/networkpacket.h +++ b/src/network/networkpacket.h @@ -41,7 +41,7 @@ public: u32 getSize() const { return m_datasize; } session_t getPeerId() const { return m_peer_id; } u16 getCommand() { return m_command; } - const u32 getRemainingBytes() const { return m_datasize - m_read_offset; } + u32 getRemainingBytes() const { return m_datasize - m_read_offset; } const char *getRemainingString() { return getString(m_read_offset); } // Returns a c-string without copying. diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 925ad001b..d537965a2 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -84,7 +84,7 @@ RemotePlayer::RemotePlayer(const char *name, IItemDefManager *idef): } -const RemotePlayerChatResult RemotePlayer::canSendChatMessage() +RemotePlayerChatResult RemotePlayer::canSendChatMessage() { // Rate limit messages u32 now = time(NULL); diff --git a/src/remoteplayer.h b/src/remoteplayer.h index 8d086fc5a..bd39b68ba 100644 --- a/src/remoteplayer.h +++ b/src/remoteplayer.h @@ -47,7 +47,7 @@ public: PlayerSAO *getPlayerSAO() { return m_sao; } void setPlayerSAO(PlayerSAO *sao) { m_sao = sao; } - const RemotePlayerChatResult canSendChatMessage(); + RemotePlayerChatResult canSendChatMessage(); void setHotbarItemcount(s32 hotbar_itemcount) { -- cgit v1.2.3 From 8dfeba02b9f084ddd52090ccd906534200f468b3 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Mon, 25 Oct 2021 21:36:28 +0100 Subject: Fix crash on hypertext[] with not enough parts The length check used < rather than <=, disabling the check when the formspec version matches the client's FORMSPEC_API_VERSION. Additionally, it was possible to have fewer parts than required if the formspec version was greater than the client's FORMSPEC_API_VERSION. --- src/gui/guiFormSpecMenu.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 938481fa2..1ce55673d 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -1730,8 +1730,9 @@ void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &elemen { std::vector parts = split(element, ';'); - if (parts.size() != 4 && m_formspec_version < FORMSPEC_API_VERSION) { - errorstream << "Invalid text element(" << parts.size() << "): '" << element << "'" << std::endl; + if (parts.size() != 4 && + (parts.size() < 4 || m_formspec_version <= FORMSPEC_API_VERSION)) { + errorstream << "Invalid hypertext element(" << parts.size() << "): '" << element << "'" << std::endl; return; } -- cgit v1.2.3 From 52bfbf6ed02e16d11f353c4066a0f4129d045e15 Mon Sep 17 00:00:00 2001 From: ExeVirus <44562154+ExeVirus@users.noreply.github.com> Date: Mon, 22 Nov 2021 12:26:46 -0500 Subject: Allow for Game-Specific Menu Music (#11241) --- builtin/mainmenu/dlg_create_world.lua | 2 +- builtin/mainmenu/game_theme.lua | 203 ++++++++++++++++++++++++++++++++++ builtin/mainmenu/init.lua | 8 +- builtin/mainmenu/tab_local.lua | 12 +- builtin/mainmenu/tab_settings.lua | 2 +- builtin/mainmenu/textures.lua | 185 ------------------------------- doc/lua_api.txt | 10 ++ src/gui/guiEngine.cpp | 26 +++-- 8 files changed, 240 insertions(+), 208 deletions(-) create mode 100644 builtin/mainmenu/game_theme.lua delete mode 100644 builtin/mainmenu/textures.lua (limited to 'src/gui') diff --git a/builtin/mainmenu/dlg_create_world.lua b/builtin/mainmenu/dlg_create_world.lua index 1938747fe..5456eb3eb 100644 --- a/builtin/mainmenu/dlg_create_world.lua +++ b/builtin/mainmenu/dlg_create_world.lua @@ -393,7 +393,7 @@ local function create_world_buttonhandler(this, fields) core.settings:set("menu_last_game",pkgmgr.games[gameindex].id) if this.data.update_worldlist_filter then menudata.worldlist:set_filtercriteria(pkgmgr.games[gameindex].id) - mm_texture.update("singleplayer", pkgmgr.games[gameindex].id) + mm_game_theme.update("singleplayer", pkgmgr.games[gameindex].id) end menudata.worldlist:refresh() core.settings:set("mainmenu_last_selected_world", diff --git a/builtin/mainmenu/game_theme.lua b/builtin/mainmenu/game_theme.lua new file mode 100644 index 000000000..f8deb29a1 --- /dev/null +++ b/builtin/mainmenu/game_theme.lua @@ -0,0 +1,203 @@ +--Minetest +--Copyright (C) 2013 sapier +-- +--This program is free software; you can redistribute it and/or modify +--it under the terms of the GNU Lesser General Public License as published by +--the Free Software Foundation; either version 2.1 of the License, or +--(at your option) any later version. +-- +--This program is distributed in the hope that it will be useful, +--but WITHOUT ANY WARRANTY; without even the implied warranty of +--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +--GNU Lesser General Public License for more details. +-- +--You should have received a copy of the GNU Lesser General Public License along +--with this program; if not, write to the Free Software Foundation, Inc., +--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +mm_game_theme = {} + +-------------------------------------------------------------------------------- +function mm_game_theme.init() + mm_game_theme.defaulttexturedir = core.get_texturepath() .. DIR_DELIM .. "base" .. + DIR_DELIM .. "pack" .. DIR_DELIM + mm_game_theme.basetexturedir = mm_game_theme.defaulttexturedir + + mm_game_theme.texturepack = core.settings:get("texture_path") + + mm_game_theme.gameid = nil + + mm_game_theme.music_handle = nil +end + +-------------------------------------------------------------------------------- +function mm_game_theme.update(tab,gamedetails) + if tab ~= "singleplayer" then + mm_game_theme.reset() + return + end + + if gamedetails == nil then + return + end + + mm_game_theme.update_game(gamedetails) +end + +-------------------------------------------------------------------------------- +function mm_game_theme.reset() + mm_game_theme.gameid = nil + local have_bg = false + local have_overlay = mm_game_theme.set_generic("overlay") + + if not have_overlay then + have_bg = mm_game_theme.set_generic("background") + end + + mm_game_theme.clear("header") + mm_game_theme.clear("footer") + core.set_clouds(false) + + mm_game_theme.set_generic("footer") + mm_game_theme.set_generic("header") + + if not have_bg then + if core.settings:get_bool("menu_clouds") then + core.set_clouds(true) + else + mm_game_theme.set_dirt_bg() + end + end + + if mm_game_theme.music_handle ~= nil then + core.sound_stop(mm_game_theme.music_handle) + end +end + +-------------------------------------------------------------------------------- +function mm_game_theme.update_game(gamedetails) + if mm_game_theme.gameid == gamedetails.id then + return + end + + local have_bg = false + local have_overlay = mm_game_theme.set_game("overlay",gamedetails) + + if not have_overlay then + have_bg = mm_game_theme.set_game("background",gamedetails) + end + + mm_game_theme.clear("header") + mm_game_theme.clear("footer") + core.set_clouds(false) + + if not have_bg then + + if core.settings:get_bool("menu_clouds") then + core.set_clouds(true) + else + mm_game_theme.set_dirt_bg() + end + end + + mm_game_theme.set_game("footer",gamedetails) + mm_game_theme.set_game("header",gamedetails) + + mm_game_theme.gameid = gamedetails.id +end + +-------------------------------------------------------------------------------- +function mm_game_theme.clear(identifier) + core.set_background(identifier,"") +end + +-------------------------------------------------------------------------------- +function mm_game_theme.set_generic(identifier) + --try texture pack first + if mm_game_theme.texturepack ~= nil then + local path = mm_game_theme.texturepack .. DIR_DELIM .."menu_" .. + identifier .. ".png" + if core.set_background(identifier,path) then + return true + end + end + + if mm_game_theme.defaulttexturedir ~= nil then + local path = mm_game_theme.defaulttexturedir .. DIR_DELIM .."menu_" .. + identifier .. ".png" + if core.set_background(identifier,path) then + return true + end + end + + return false +end + +-------------------------------------------------------------------------------- +function mm_game_theme.set_game(identifier, gamedetails) + + if gamedetails == nil then + return false + end + + mm_game_theme.set_music(gamedetails) + + if mm_game_theme.texturepack ~= nil then + local path = mm_game_theme.texturepack .. DIR_DELIM .. + gamedetails.id .. "_menu_" .. identifier .. ".png" + if core.set_background(identifier, path) then + return true + end + end + + -- Find out how many randomized textures the game provides + local n = 0 + local filename + local menu_files = core.get_dir_list(gamedetails.path .. DIR_DELIM .. "menu", false) + for i = 1, #menu_files do + filename = identifier .. "." .. i .. ".png" + if table.indexof(menu_files, filename) == -1 then + n = i - 1 + break + end + end + -- Select random texture, 0 means standard texture + n = math.random(0, n) + if n == 0 then + filename = identifier .. ".png" + else + filename = identifier .. "." .. n .. ".png" + end + + local path = gamedetails.path .. DIR_DELIM .. "menu" .. + DIR_DELIM .. filename + if core.set_background(identifier, path) then + return true + end + + return false +end + +-------------------------------------------------------------------------------- +function mm_game_theme.set_dirt_bg() + if mm_game_theme.texturepack ~= nil then + local path = mm_game_theme.texturepack .. DIR_DELIM .."default_dirt.png" + if core.set_background("background", path, true, 128) then + return true + end + end + + -- Use universal fallback texture in textures/base/pack + local minimalpath = defaulttexturedir .. "menu_bg.png" + core.set_background("background", minimalpath, true, 128) +end + +-------------------------------------------------------------------------------- +function mm_game_theme.set_music(gamedetails) + if mm_game_theme.music_handle ~= nil then + core.sound_stop(mm_game_theme.music_handle) + end + local music_path = gamedetails.path .. DIR_DELIM .. "menu" .. DIR_DELIM .. "theme" + mm_game_theme.music_handle = core.sound_play(music_path, true) +end diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 0c8578cd6..8e716c2eb 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -35,7 +35,7 @@ dofile(menupath .. DIR_DELIM .. "async_event.lua") dofile(menupath .. DIR_DELIM .. "common.lua") dofile(menupath .. DIR_DELIM .. "pkgmgr.lua") dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua") -dofile(menupath .. DIR_DELIM .. "textures.lua") +dofile(menupath .. DIR_DELIM .. "game_theme.lua") dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua") dofile(menupath .. DIR_DELIM .. "dlg_settings_advanced.lua") @@ -87,7 +87,7 @@ local function init_globals() core.settings:set("menu_last_game", default_game) end - mm_texture.init() + mm_game_theme.init() -- Create main tabview local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0}) @@ -113,7 +113,7 @@ local function init_globals() if tv_main.current_tab == "local" then local game = pkgmgr.find_by_gameid(core.settings:get("menu_last_game")) if game == nil then - mm_texture.reset() + mm_game_theme.reset() end end @@ -121,8 +121,6 @@ local function init_globals() tv_main:show() ui.update() - - core.sound_play("main_menu", true) end init_globals() diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua index be5f905ac..2d1a616a8 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/tab_local.lua @@ -54,7 +54,7 @@ if enable_gamebar then for key,value in pairs(fields) do for j=1,#pkgmgr.games,1 do if ("game_btnbar_" .. pkgmgr.games[j].id == key) then - mm_texture.update("singleplayer", pkgmgr.games[j]) + mm_game_theme.update("singleplayer", pkgmgr.games[j]) core.set_topleft_text(pkgmgr.games[j].name) core.settings:set("menu_last_game",pkgmgr.games[j].id) menudata.worldlist:set_filtercriteria(pkgmgr.games[j].id) @@ -323,7 +323,7 @@ local function main_button_handler(this, fields, name, tabdata) create_world_dlg:set_parent(this) this:hide() create_world_dlg:show() - mm_texture.update("singleplayer", current_game()) + mm_game_theme.update("singleplayer", current_game()) return true end @@ -340,7 +340,7 @@ local function main_button_handler(this, fields, name, tabdata) delete_world_dlg:set_parent(this) this:hide() delete_world_dlg:show() - mm_texture.update("singleplayer",current_game()) + mm_game_theme.update("singleplayer",current_game()) end end @@ -358,7 +358,7 @@ local function main_button_handler(this, fields, name, tabdata) configdialog:set_parent(this) this:hide() configdialog:show() - mm_texture.update("singleplayer",current_game()) + mm_game_theme.update("singleplayer",current_game()) end end @@ -375,7 +375,7 @@ if enable_gamebar then if game then menudata.worldlist:set_filtercriteria(game.id) core.set_topleft_text(game.name) - mm_texture.update("singleplayer",game) + mm_game_theme.update("singleplayer",game) end singleplayer_refresh_gamebar() @@ -387,7 +387,7 @@ if enable_gamebar then gamebar:hide() end core.set_topleft_text("") - mm_texture.update(new_tab,nil) + mm_game_theme.update(new_tab,nil) end end end diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua index f06e35872..29f3c9b62 100644 --- a/builtin/mainmenu/tab_settings.lua +++ b/builtin/mainmenu/tab_settings.lua @@ -247,7 +247,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) adv_settings_dlg:set_parent(this) this:hide() adv_settings_dlg:show() - --mm_texture.update("singleplayer", current_game()) + --mm_game_theme.update("singleplayer", current_game()) return true end if fields["cb_smooth_lighting"] then diff --git a/builtin/mainmenu/textures.lua b/builtin/mainmenu/textures.lua deleted file mode 100644 index a3acbbdec..000000000 --- a/builtin/mainmenu/textures.lua +++ /dev/null @@ -1,185 +0,0 @@ ---Minetest ---Copyright (C) 2013 sapier --- ---This program is free software; you can redistribute it and/or modify ---it under the terms of the GNU Lesser General Public License as published by ---the Free Software Foundation; either version 2.1 of the License, or ---(at your option) any later version. --- ---This program is distributed in the hope that it will be useful, ---but WITHOUT ANY WARRANTY; without even the implied warranty of ---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ---GNU Lesser General Public License for more details. --- ---You should have received a copy of the GNU Lesser General Public License along ---with this program; if not, write to the Free Software Foundation, Inc., ---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - -mm_texture = {} - --------------------------------------------------------------------------------- -function mm_texture.init() - mm_texture.defaulttexturedir = core.get_texturepath() .. DIR_DELIM .. "base" .. - DIR_DELIM .. "pack" .. DIR_DELIM - mm_texture.basetexturedir = mm_texture.defaulttexturedir - - mm_texture.texturepack = core.settings:get("texture_path") - - mm_texture.gameid = nil -end - --------------------------------------------------------------------------------- -function mm_texture.update(tab,gamedetails) - if tab ~= "singleplayer" then - mm_texture.reset() - return - end - - if gamedetails == nil then - return - end - - mm_texture.update_game(gamedetails) -end - --------------------------------------------------------------------------------- -function mm_texture.reset() - mm_texture.gameid = nil - local have_bg = false - local have_overlay = mm_texture.set_generic("overlay") - - if not have_overlay then - have_bg = mm_texture.set_generic("background") - end - - mm_texture.clear("header") - mm_texture.clear("footer") - core.set_clouds(false) - - mm_texture.set_generic("footer") - mm_texture.set_generic("header") - - if not have_bg then - if core.settings:get_bool("menu_clouds") then - core.set_clouds(true) - else - mm_texture.set_dirt_bg() - end - end -end - --------------------------------------------------------------------------------- -function mm_texture.update_game(gamedetails) - if mm_texture.gameid == gamedetails.id then - return - end - - local have_bg = false - local have_overlay = mm_texture.set_game("overlay",gamedetails) - - if not have_overlay then - have_bg = mm_texture.set_game("background",gamedetails) - end - - mm_texture.clear("header") - mm_texture.clear("footer") - core.set_clouds(false) - - if not have_bg then - - if core.settings:get_bool("menu_clouds") then - core.set_clouds(true) - else - mm_texture.set_dirt_bg() - end - end - - mm_texture.set_game("footer",gamedetails) - mm_texture.set_game("header",gamedetails) - - mm_texture.gameid = gamedetails.id -end - --------------------------------------------------------------------------------- -function mm_texture.clear(identifier) - core.set_background(identifier,"") -end - --------------------------------------------------------------------------------- -function mm_texture.set_generic(identifier) - --try texture pack first - if mm_texture.texturepack ~= nil then - local path = mm_texture.texturepack .. DIR_DELIM .."menu_" .. - identifier .. ".png" - if core.set_background(identifier,path) then - return true - end - end - - if mm_texture.defaulttexturedir ~= nil then - local path = mm_texture.defaulttexturedir .. DIR_DELIM .."menu_" .. - identifier .. ".png" - if core.set_background(identifier,path) then - return true - end - end - - return false -end - --------------------------------------------------------------------------------- -function mm_texture.set_game(identifier, gamedetails) - - if gamedetails == nil then - return false - end - - if mm_texture.texturepack ~= nil then - local path = mm_texture.texturepack .. DIR_DELIM .. - gamedetails.id .. "_menu_" .. identifier .. ".png" - if core.set_background(identifier, path) then - return true - end - end - - -- Find out how many randomized textures the game provides - local n = 0 - local filename - local menu_files = core.get_dir_list(gamedetails.path .. DIR_DELIM .. "menu", false) - for i = 1, #menu_files do - filename = identifier .. "." .. i .. ".png" - if table.indexof(menu_files, filename) == -1 then - n = i - 1 - break - end - end - -- Select random texture, 0 means standard texture - n = math.random(0, n) - if n == 0 then - filename = identifier .. ".png" - else - filename = identifier .. "." .. n .. ".png" - end - - local path = gamedetails.path .. DIR_DELIM .. "menu" .. - DIR_DELIM .. filename - if core.set_background(identifier, path) then - return true - end - - return false -end - -function mm_texture.set_dirt_bg() - if mm_texture.texturepack ~= nil then - local path = mm_texture.texturepack .. DIR_DELIM .."default_dirt.png" - if core.set_background("background", path, true, 128) then - return true - end - end - - -- Use universal fallback texture in textures/base/pack - local minimalpath = defaulttexturedir .. "menu_bg.png" - core.set_background("background", minimalpath, true, 128) -end diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 3b9f4c339..efc9585e4 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -113,8 +113,16 @@ If you want to specify multiple images for one identifier, add additional images named like `$identifier.$n.png`, with an ascending number $n starting with 1, and a random image will be chosen from the provided ones. +Menu music +----------- +Games can provide custom main menu music. They are put inside a `menu` +directory inside the game directory. +The music files are named `theme.ogg`. +If you want to specify multiple music files for one game, add additional +images named like `theme.$n.ogg`, with an ascending number $n starting +with 1 (max 10), and a random music file will be chosen from the provided ones. Mods ==== @@ -5642,6 +5650,8 @@ Sounds player actions (e.g. door closing). * `minetest.sound_stop(handle)` * `handle` is a handle returned by `minetest.sound_play` +* `minetest.sound_stop_all()` + Stops all currently playing non-ephermeral sounds. * `minetest.sound_fade(handle, step, gain)` * `handle` is a handle returned by `minetest.sound_play` * `step` determines how fast a sound will fade. diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index c39c3ee0d..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 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); + } } /******************************************************************************/ -- cgit v1.2.3 From 05573d6d8d9e5a756ab1b03b159b127144f8e775 Mon Sep 17 00:00:00 2001 From: ROllerozxa Date: Wed, 29 Dec 2021 23:00:16 +0100 Subject: Remove unused (de)serializeAttributes() methods --- src/gui/guiButton.cpp | 79 ------------------------------------ src/gui/guiButton.h | 8 ---- src/gui/guiEditBox.cpp | 51 ----------------------- src/gui/guiEditBox.h | 8 ---- src/gui/guiEditBoxWithScrollbar.cpp | 20 --------- src/gui/guiEditBoxWithScrollbar.h | 6 --- src/gui/guiSkin.cpp | 42 ------------------- src/gui/guiSkin.h | 10 ----- src/irrlicht_changes/static_text.cpp | 44 -------------------- src/irrlicht_changes/static_text.h | 6 --- 10 files changed, 274 deletions(-) (limited to 'src/gui') diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp index d6dbddf54..ba95b81c3 100644 --- a/src/gui/guiButton.cpp +++ b/src/gui/guiButton.cpp @@ -632,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 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& rectangle, ISimpleTextureSource *tsrc, diff --git a/src/gui/guiButton.h b/src/gui/guiButton.h index 834405f51..ee9bb6f21 100644 --- a/src/gui/guiButton.h +++ b/src/gui/guiButton.h @@ -221,14 +221,6 @@ public: 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/guiEditBox.cpp b/src/gui/guiEditBox.cpp index 8459107cd..4a0f5013d 100644 --- a/src/gui/guiEditBox.cpp +++ b/src/gui/guiEditBox.cpp @@ -846,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 2a5c911bc..4c7413f54 100644 --- a/src/gui/guiEditBox.h +++ b/src/gui/guiEditBox.h @@ -130,14 +130,6 @@ 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: diff --git a/src/gui/guiEditBoxWithScrollbar.cpp b/src/gui/guiEditBoxWithScrollbar.cpp index fb4bc2a0b..1b7f7832a 100644 --- a/src/gui/guiEditBoxWithScrollbar.cpp +++ b/src/gui/guiEditBoxWithScrollbar.cpp @@ -652,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/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; iaddColor(GUISkinColorNames[i], Colors[i]); - - for (i=0; iaddInt(GUISkinSizeNames[i], Sizes[i]); - - for (i=0; iaddString(GUISkinTextNames[i], Texts[i].c_str()); - - for (i=0; iaddInt(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; igetAttributeAsColor(GUISkinColorNames[i]); - - for (i=0; igetAttributeAsInt(GUISkinSizeNames[i]); - - for (i=0; igetAttributeAsStringW(GUISkinTextNames[i]); - - for (i=0; igetAttributeAsInt(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/irrlicht_changes/static_text.cpp b/src/irrlicht_changes/static_text.cpp index 8908a91f7..f548c3f71 100644 --- a/src/irrlicht_changes/static_text.cpp +++ b/src/irrlicht_changes/static_text.cpp @@ -588,50 +588,6 @@ s32 StaticText::getTextWidth() const } -//! Writes attributes of the element. -//! Implement this to expose the attributes of your element for -//! scripting languages, editors, debuggers or xml serialization purposes. -void StaticText::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const -{ - IGUIStaticText::serializeAttributes(out,options); - - out->addBool ("Border", Border); - out->addBool ("OverrideColorEnabled",true); - out->addBool ("OverrideBGColorEnabled",ColoredText.hasBackground()); - out->addBool ("WordWrap", WordWrap); - out->addBool ("Background", Background); - out->addBool ("RightToLeft", RightToLeft); - out->addBool ("RestrainTextInside", RestrainTextInside); - out->addColor ("OverrideColor", ColoredText.getDefaultColor()); - out->addColor ("BGColor", ColoredText.getBackground()); - out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames); - out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames); - - // out->addFont ("OverrideFont", OverrideFont); -} - - -//! Reads attributes of the element -void StaticText::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) -{ - IGUIStaticText::deserializeAttributes(in,options); - - Border = in->getAttributeAsBool("Border"); - setWordWrap(in->getAttributeAsBool("WordWrap")); - Background = in->getAttributeAsBool("Background"); - RightToLeft = in->getAttributeAsBool("RightToLeft"); - RestrainTextInside = in->getAttributeAsBool("RestrainTextInside"); - if (in->getAttributeAsBool("OverrideColorEnabled")) - ColoredText.setDefaultColor(in->getAttributeAsColor("OverrideColor")); - if (in->getAttributeAsBool("OverrideBGColorEnabled")) - ColoredText.setBackground(in->getAttributeAsColor("BGColor")); - - setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames), - (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames)); - - // OverrideFont = in->getAttributeAsFont("OverrideFont"); -} - } // end namespace gui #endif // USE_FREETYPE diff --git a/src/irrlicht_changes/static_text.h b/src/irrlicht_changes/static_text.h index 83bbf4c3d..17a3bf753 100644 --- a/src/irrlicht_changes/static_text.h +++ b/src/irrlicht_changes/static_text.h @@ -184,12 +184,6 @@ namespace gui //! Checks if the text should be interpreted as right-to-left text virtual bool isRightToLeft() const; - //! 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 hasType(EGUI_ELEMENT_TYPE t) const { return (t == EGUIET_ENRICHED_STATIC_TEXT) || (t == EGUIET_STATIC_TEXT); }; -- cgit v1.2.3 From 14c7fae378fc40f88d3c430dea2cb726afc005b1 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Wed, 29 Dec 2021 23:58:26 +0100 Subject: Formspec: Unify argument checks (#11851) --- src/gui/guiFormSpecMenu.cpp | 2152 +++++++++++++++++++++---------------------- src/gui/guiFormSpecMenu.h | 2 + 2 files changed, 1043 insertions(+), 1111 deletions(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 1ce55673d..dfeea12db 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -81,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 */ @@ -294,8 +301,20 @@ v2s32 GUIFormSpecMenu::getRealCoordinateGeometry(const std::vector 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 &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 parts = split(element,','); if (((parts.size() == 2) || parts.size() == 3) || @@ -349,14 +368,9 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data) void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string &element) { - std::vector parts = split(element, ';'); - - if (parts.size() < 4 || - (parts.size() > 5 && m_formspec_version <= FORMSPEC_API_VERSION)) { - errorstream << "Invalid scroll_container start element (" << parts.size() - << "): '" << element << "'" << std::endl; + std::vector parts; + if (!precheckElement("scroll_container start", element, 4, 5, parts)) return; - } std::vector v_pos = split(parts[0], ','); std::vector v_geom = split(parts[1], ','); @@ -445,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"< parts = split(element,';'); + std::vector 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 v_pos = split(parts[2],','); - std::vector 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 v_pos = split(parts[2],','); + std::vector 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(slot_scale.X * imgsize.X, 1), - slot_scale.Y <= 0 ? imgsize.Y : std::max(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(slot_scale.X * imgsize.X, 1), + slot_scale.Y <= 0 ? imgsize.Y : std::max(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 rect = core::rect(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 rect = core::rect(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 parts = split(element, ';'); @@ -578,157 +582,150 @@ void GUIFormSpecMenu::parseListRing(parserData *data, const std::string &element void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element) { - std::vector parts = split(element,';'); - - if (((parts.size() >= 3) && (parts.size() <= 4)) || - ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector v_pos = split(parts[0],','); - std::string name = parts[1]; - std::string label = parts[2]; - std::string selected; + std::vector parts; + if (!precheckElement("checkbox", element, 3, 4, parts)) + return; - if (parts.size() >= 4) - selected = parts[3]; + std::vector 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 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 rect; + std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); + const core::dimension2d 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 rect; - rect = core::rect( - 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( - 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( + 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( + 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 parts = split(element,';'); - - if (parts.size() >= 5) { - std::vector v_pos = split(parts[0],','); - std::vector v_geom = split(parts[1],','); - std::string name = parts[3]; - std::string value = parts[4]; + std::vector parts; + if (!precheckElement("scrollbar", element, 5, 5, parts)) + return; - MY_CHECKPOS("scrollbar",0); - MY_CHECKGEOM("scrollbar",1); + std::vector v_pos = split(parts[0],','); + std::vector v_geom = split(parts[1],','); + std::string name = parts[3]; + std::string value = parts[4]; - v2s32 pos; - v2s32 dim; + MY_CHECKPOS("scrollbar",0); + MY_CHECKGEOM("scrollbar",1); - 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; - } + v2s32 pos; + v2s32 dim; - core::rect rect = - core::rect(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.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; + } - FieldSpec spec( - name, - L"", - L"", - 258+m_fields.size() - ); + core::rect rect = + core::rect(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y); - bool is_horizontal = true; + FieldSpec spec( + name, + L"", + L"", + 258+m_fields.size() + ); - if (parts[2] == "vertical") - is_horizontal = false; + bool is_horizontal = true; - spec.ftype = f_ScrollBar; - spec.send = true; - GUIScrollBar *e = new GUIScrollBar(Environment, data->current_parent, - spec.fid, rect, is_horizontal, true); + if (parts[2] == "vertical") + is_horizontal = false; - auto style = getDefaultStyleForElement("scrollbar", name); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - e->setArrowsVisible(data->scrollbar_options.arrow_visiblity); + spec.ftype = f_ScrollBar; + spec.send = true; + GUIScrollBar *e = new GUIScrollBar(Environment, data->current_parent, + spec.fid, rect, is_horizontal, true); - s32 max = data->scrollbar_options.max; - s32 min = data->scrollbar_options.min; + auto style = getDefaultStyleForElement("scrollbar", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setArrowsVisible(data->scrollbar_options.arrow_visiblity); - e->setMax(max); - e->setMin(min); + s32 max = data->scrollbar_options.max; + s32 min = data->scrollbar_options.min; - e->setPos(stoi(parts[4])); + e->setMax(max); + e->setMin(min); - e->setSmallStep(data->scrollbar_options.small_step); - e->setLargeStep(data->scrollbar_options.large_step); + e->setPos(stoi(parts[4])); - s32 scrollbar_size = is_horizontal ? dim.X : dim.Y; + e->setSmallStep(data->scrollbar_options.small_step); + e->setLargeStep(data->scrollbar_options.large_step); - e->setPageSize(scrollbar_size * (max - min + 1) / data->scrollbar_options.thumb_size); + s32 scrollbar_size = is_horizontal ? dim.X : dim.Y; - if (spec.fname == m_focused_element) { - Environment->setFocus(e); - } + e->setPageSize(scrollbar_size * (max - min + 1) / data->scrollbar_options.thumb_size); - 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) @@ -786,11 +783,11 @@ void GUIFormSpecMenu::parseScrollBarOptions(parserData* data, const std::string void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) { - std::vector parts = split(element,';'); + std::vector 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 v_pos = split(parts[0],','); std::vector v_geom = split(parts[1],','); std::string name = unescape_string(parts[2]); @@ -842,54 +839,47 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) return; } - if (parts.size() == 2) { - std::vector 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 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"<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"<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 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 parts; + if (!precheckElement("animated_image", element, 6, 7, parts)) return; - } std::vector v_pos = split(parts[0], ','); std::vector v_geom = split(parts[1], ','); @@ -944,218 +934,207 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &element) { - std::vector parts = split(element,';'); + std::vector 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 v_pos = split(parts[0],','); - std::vector v_geom = split(parts[1],','); - std::string name = parts[2]; + std::vector v_pos = split(parts[0],','); + std::vector 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"<explicit_size) + warningstream<<"invalid use of item_image without a size[] element"<current_parent, spec.fid, - core::rect(pos, pos + geom), name, m_font, m_client); - auto style = getDefaultStyleForElement("item_image", spec.fname); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + GUIItemImage *e = new GUIItemImage(Environment, data->current_parent, spec.fid, + core::rect(pos, pos + geom), name, m_font, m_client); + auto style = getDefaultStyleForElement("item_image", spec.fname); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - // 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 parts = split(element,';'); + std::vector parts; + if (!precheckElement("button", element, 4, 4, parts)) + return; - if ((parts.size() == 4) || - ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector v_pos = split(parts[0],','); - std::vector v_geom = split(parts[1],','); - std::string name = parts[2]; - std::string label = parts[3]; + std::vector v_pos = split(parts[0],','); + std::vector v_geom = split(parts[1],','); + std::string name = parts[2]; + std::string label = parts[3]; - MY_CHECKPOS("button",0); - MY_CHECKGEOM("button",1); + MY_CHECKPOS("button",0); + MY_CHECKGEOM("button",1); - v2s32 pos; - v2s32 geom; - core::rect rect; + v2s32 pos; + v2s32 geom; + core::rect rect; - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); - rect = core::rect(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->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + rect = core::rect(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; - rect = core::rect(pos.X, pos.Y - m_btn_height, - pos.X + geom.X, pos.Y + m_btn_height); - } + rect = core::rect(pos.X, pos.Y - m_btn_height, + pos.X + geom.X, pos.Y + m_btn_height); + } - if(!data->explicit_size) - warningstream<<"invalid use of button without a size[] element"<explicit_size) + warningstream<<"invalid use of button without a size[] element"<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 parts = split(element,';'); + std::vector 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 v_pos = split(parts[0],','); - std::vector v_geom = split(parts[1],','); - std::string name = unescape_string(parts[2]); + std::vector v_pos = split(parts[0],','); + std::vector 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 middle; - if (parts.size() >= 5) { - std::vector v_middle = split(parts[4], ','); - if (v_middle.size() == 1) { - s32 x = stoi(v_middle[0]); - middle.UpperLeftCorner = core::vector2di(x, x); - middle.LowerRightCorner = core::vector2di(-x, -x); - } else if (v_middle.size() == 2) { - s32 x = stoi(v_middle[0]); - s32 y = stoi(v_middle[1]); - middle.UpperLeftCorner = core::vector2di(x, y); - middle.LowerRightCorner = core::vector2di(-x, -y); - // `-x` is interpreted as `w - x` - } else if (v_middle.size() == 4) { - middle.UpperLeftCorner = core::vector2di(stoi(v_middle[0]), stoi(v_middle[1])); - middle.LowerRightCorner = core::vector2di(stoi(v_middle[2]), stoi(v_middle[3])); - } else { - warningstream << "Invalid rectangle given to middle param of background[] element" << std::endl; - } + core::rect middle; + if (parts.size() >= 5) { + std::vector v_middle = split(parts[4], ','); + if (v_middle.size() == 1) { + s32 x = stoi(v_middle[0]); + middle.UpperLeftCorner = core::vector2di(x, x); + middle.LowerRightCorner = core::vector2di(-x, -x); + } else if (v_middle.size() == 2) { + s32 x = stoi(v_middle[0]); + s32 y = stoi(v_middle[1]); + middle.UpperLeftCorner = core::vector2di(x, y); + middle.LowerRightCorner = core::vector2di(-x, -y); + // `-x` is interpreted as `w - x` + } else if (v_middle.size() == 4) { + middle.UpperLeftCorner = core::vector2di(stoi(v_middle[0]), stoi(v_middle[1])); + middle.LowerRightCorner = core::vector2di(stoi(v_middle[2]), stoi(v_middle[3])); + } else { + warningstream << "Invalid rectangle given to middle param of background[] element" << std::endl; } + } - if (!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 rect; - if (!clip) { - // no auto_clip => position like normal image - rect = core::rect(pos, pos + geom); - } else { - // it will be auto-clipped when drawing - rect = core::rect(-pos, pos); - } + core::rect rect; + if (!clip) { + // no auto_clip => position like normal image + rect = core::rect(pos, pos + geom); + } else { + // it will be auto-clipped when drawing + rect = core::rect(-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) @@ -1192,338 +1171,320 @@ void GUIFormSpecMenu::parseTableColumns(parserData* data, const std::string &ele void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) { - std::vector parts = split(element,';'); + std::vector 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 v_pos = split(parts[0],','); - std::vector v_geom = split(parts[1],','); - std::string name = parts[2]; - std::vector items = split(parts[3],','); - std::string str_initial_selection; - std::string str_transparent = "false"; + std::vector v_pos = split(parts[0],','); + std::vector v_geom = split(parts[1],','); + std::string name = parts[2]; + std::vector 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 rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); + core::rect rect = core::rect(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 parts = split(element,';'); + std::vector 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 v_pos = split(parts[0],','); - std::vector v_geom = split(parts[1],','); - std::string name = parts[2]; - std::vector items = split(parts[3],','); - std::string str_initial_selection; - std::string str_transparent = "false"; + std::vector v_pos = split(parts[0],','); + std::vector v_geom = split(parts[1],','); + std::string name = parts[2]; + std::vector 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 rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); + core::rect rect = core::rect(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 parts = split(element, ';'); - - if (parts.size() == 5 || parts.size() == 6 || - (parts.size() > 6 && m_formspec_version > FORMSPEC_API_VERSION)) - { - std::vector v_pos = split(parts[0], ','); - std::string name = parts[2]; - std::vector items = split(parts[3], ','); - std::string str_initial_selection = parts[4]; + std::vector 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 v_pos = split(parts[0], ','); + std::string name = parts[2]; + std::vector 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 rect; + MY_CHECKPOS("dropdown",0); - if (data->real_coordinates) { - std::vector v_geom = split(parts[1],','); + v2s32 pos; + v2s32 geom; + core::rect rect; - if (v_geom.size() == 1) - v_geom.emplace_back("1"); + if (data->real_coordinates) { + std::vector 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(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(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); + } else { + pos = getElementBasePos(&v_pos); - rect = core::rect(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(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::vector &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::vector &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 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 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 parts = split(element,';'); - - if (parts.size() == 4 || - (parts.size() > 4 && m_formspec_version > FORMSPEC_API_VERSION)) - { - std::vector v_pos = split(parts[0],','); - std::vector 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 parts; + if (!precheckElement("pwdfield", element, 4, 4, parts)) + return; - v2s32 pos; - v2s32 geom; + std::vector v_pos = split(parts[0],','); + std::vector 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 rect = core::rect(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 rect = core::rect(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, @@ -1710,31 +1671,26 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector& void GUIFormSpecMenu::parseField(parserData* data, const std::string &element, const std::string &type) { - std::vector parts = split(element,';'); + std::vector 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 parts = split(element, ';'); + MY_CHECKCLIENT("list"); - if (parts.size() != 4 && - (parts.size() < 4 || m_formspec_version <= FORMSPEC_API_VERSION)) { - errorstream << "Invalid hypertext element(" << parts.size() << "): '" << element << "'" << std::endl; + std::vector parts; + if (!precheckElement("hypertext", element, 4, 4, parts)) return; - } std::vector v_pos = split(parts[0], ','); std::vector v_geom = split(parts[1], ','); @@ -1785,539 +1741,521 @@ void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &elemen void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) { - std::vector parts = split(element,';'); + std::vector parts; + if (!precheckElement("label", element, 2, 2, parts)) + return; - if ((parts.size() == 2) || - ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector v_pos = split(parts[0],','); - std::string text = parts[1]; + std::vector 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"<explicit_size) + warningstream<<"invalid use of label without a size[] element"< lines = split(text, '\n'); + std::vector 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 rect; + core::rect 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( - pos.X, pos.Y, - pos.X + font->getDimension(wlabel_plain.c_str()).Width, - pos.Y + imgsize.Y); + rect = core::rect( + 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( - 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( + 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 parts = split(element,';'); + std::vector parts; + if (!precheckElement("vertlabel", element, 2, 2, parts)) + return; - if ((parts.size() == 2) || - ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector v_pos = split(parts[0],','); - std::wstring text = unescape_translate( - unescape_string(utf8_to_wide(parts[1]))); + std::vector 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 rect; + v2s32 pos; + core::rect 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(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(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( - 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( + 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"<explicit_size) + warningstream<<"invalid use of label without a size[] element"<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 parts = split(element,';'); - - if ((((parts.size() >= 5) && (parts.size() <= 8)) && (parts.size() != 6)) || - ((parts.size() > 8) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector v_pos = split(parts[0],','); - std::vector v_geom = split(parts[1],','); - std::string image_name = parts[2]; - std::string name = parts[3]; - std::string label = parts[4]; + std::vector 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 v_pos = split(parts[0],','); + std::vector 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 rect = core::rect(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"<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 rect = core::rect(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"<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 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 v_pos = split(parts[0],','); + std::vector 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 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 v_pos = split(parts[0],','); - std::string name = parts[i+1]; - std::vector 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 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 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 rect = core::rect(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 rect = core::rect(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 parts = split(element,';'); - - if ((parts.size() == 5) || - ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector v_pos = split(parts[0],','); - std::vector v_geom = split(parts[1],','); - std::string item_name = parts[2]; - std::string name = parts[3]; - std::string label = parts[4]; + std::vector parts; + if (!precheckElement("item_image_button", element, 5, 5, parts)) + return; - label = unescape_string(label); - item_name = unescape_string(item_name); + std::vector v_pos = split(parts[0],','); + std::vector v_geom = split(parts[1],','); + std::string item_name = parts[2]; + std::string name = parts[3]; + std::string label = parts[4]; - MY_CHECKPOS("itemimagebutton",0); - MY_CHECKGEOM("itemimagebutton",1); + label = unescape_string(label); + item_name = unescape_string(item_name); - v2s32 pos; - v2s32 geom; + MY_CHECKPOS("item_image_button",0); + MY_CHECKGEOM("item_image_button",1); - 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 rect = core::rect(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"< rect = core::rect(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"<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 parts = split(element, ';'); + std::vector parts; + if (!precheckElement("box", element, 3, 3, parts)) + return; - if ((parts.size() == 3) || - ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - std::vector v_pos = split(parts[0], ','); - std::vector v_geom = split(parts[1], ','); + std::vector v_pos = split(parts[0], ','); + std::vector 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 colors; - std::array bordercolors = {0x0, 0x0, 0x0, 0x0}; - std::array borderwidths = {0, 0, 0, 0}; + video::SColor tmp_color; + std::array colors; + std::array bordercolors = {0x0, 0x0, 0x0, 0x0}; + std::array 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 rect(pos, pos + geom); + core::rect 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 parts = split(element,';'); + std::vector parts; + if (!precheckElement("bgcolor", element, 2, 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; @@ -2348,49 +2286,51 @@ void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string & void GUIFormSpecMenu::parseListColors(parserData* data, const std::string &element) { - std::vector parts = split(element,';'); + std::vector 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 parts = split(element,';'); - if (parts.size() < 2) { - errorstream << "Invalid tooltip element(" << parts.size() << "): '" - << element << "'" << std::endl; + std::vector parts; + if (!precheckElement("tooltip", element, 2, 5, parts)) return; - } // Get mode and check size bool rect_mode = parts[0].find(',') != std::string::npos; @@ -2714,35 +2654,25 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b void GUIFormSpecMenu::parseSetFocus(const std::string &element) { - std::vector 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 parts; + if (!precheckElement("set_focus", element, 2, 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 parts = split(element, ';'); + MY_CHECKCLIENT("model"); - if (parts.size() < 5 || (parts.size() > 10 && - m_formspec_version <= FORMSPEC_API_VERSION)) { - errorstream << "Invalid model element (" << parts.size() << "): '" << element - << "'" << std::endl; + std::vector parts; + if (!precheckElement("model", element, 5, 10, parts)) return; - } // Avoid length checks by resizing if (parts.size() < 10) diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index eee84eff6..4ba9f3959 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -279,6 +279,8 @@ protected: v2s32 getElementBasePos(const std::vector *v_pos); v2s32 getRealCoordinateBasePos(const std::vector &v_pos); v2s32 getRealCoordinateGeometry(const std::vector &v_geom); + bool precheckElement(const std::string &name, const std::string &element, + size_t args_min, size_t args_max, std::vector &parts); std::unordered_map> theme_by_type; std::unordered_map> theme_by_name; -- cgit v1.2.3 From 544b9d5c72f690d6a729053616d26e023f7e0e28 Mon Sep 17 00:00:00 2001 From: Vincent Robinson Date: Thu, 30 Dec 2021 12:54:47 -0800 Subject: Add padding[] element to formspecs (#11821) --- doc/lua_api.txt | 17 +++++- games/devtest/mods/testformspec/formspec.lua | 65 ++++++++++++++++++-- src/gui/guiFormSpecMenu.cpp | 88 +++++++++++++++++++++++----- src/gui/guiFormSpecMenu.h | 3 + 4 files changed, 149 insertions(+), 24 deletions(-) (limited to 'src/gui') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 1950659ab..0879dcfb5 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -117,7 +117,7 @@ Menu music ----------- Games can provide custom main menu music. They are put inside a `menu` -directory inside the game directory. +directory inside the game directory. The music files are named `theme.ogg`. If you want to specify multiple music files for one game, add additional @@ -2326,9 +2326,20 @@ Elements * `position` and `anchor` elements need suitable values to avoid a formspec extending off the game window due to particular game window sizes. -### `no_prepend[]` +### `padding[,]` * Must be used after the `size`, `position`, and `anchor` elements (if present). +* Defines how much space is padded around the formspec if the formspec tries to + increase past the size of the screen and coordinates have to be shrunk. +* For X and Y, 0.0 represents no padding (the formspec can touch the edge of the + screen), and 0.5 represents half the screen (which forces the coordinate size + to 0). If negative, the formspec can extend off the edge of the screen. +* Defaults to [0.05, 0.05]. + +### `no_prepend[]` + +* Must be used after the `size`, `position`, `anchor`, and `padding` elements + (if present). * Disables player:set_formspec_prepend() from applying to this formspec. ### `real_coordinates[]` @@ -7915,7 +7926,7 @@ Used by `minetest.register_node`. items = {"default:sand", "default:desert_sand"}, }, { - -- Only drop if using an item in the "magicwand" group, or + -- Only drop if using an item in the "magicwand" group, or -- an item that is in both the "pickaxe" and the "lucky" -- groups. tool_groups = { diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 501b5e354..c0db695b7 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -270,6 +270,16 @@ local scroll_fs = --style_type[label;border=;bgcolor=] --label[0.75,2;Reset] +local window = { + sizex = 12, + sizey = 13, + positionx = 0.5, + positiony = 0.5, + anchorx = 0.5, + anchory = 0.5, + paddingx = 0.05, + paddingy = 0.05 +} local pages = { -- Real Coordinates @@ -341,9 +351,28 @@ local pages = { "size[12,13]real_coordinates[true]" .. "container[0.5,1.5]" .. tabheaders_fs .. "container_end[]", - -- Inv + -- Inv "size[12,13]real_coordinates[true]" .. inv_style_fs, + -- Window + function() + return "formspec_version[3]" .. + string.format("size[%s,%s]position[%s,%s]anchor[%s,%s]padding[%s,%s]", + window.sizex, window.sizey, window.positionx, window.positiony, + window.anchorx, window.anchory, window.paddingx, window.paddingy) .. + string.format("field[0.5,0.5;2.5,0.5;sizex;X Size;%s]field[3.5,0.5;2.5,0.5;sizey;Y Size;%s]" .. + "field[0.5,1.5;2.5,0.5;positionx;X Position;%s]field[3.5,1.5;2.5,0.5;positiony;Y Position;%s]" .. + "field[0.5,2.5;2.5,0.5;anchorx;X Anchor;%s]field[3.5,2.5;2.5,0.5;anchory;Y Anchor;%s]" .. + "field[0.5,3.5;2.5,0.5;paddingx;X Padding;%s]field[3.5,3.5;2.5,0.5;paddingy;Y Padding;%s]" .. + "button[2,4.5;2.5,0.5;submit_window;Submit]", + window.sizex, window.sizey, window.positionx, window.positiony, + window.anchorx, window.anchory, window.paddingx, window.paddingy) .. + "field_close_on_enter[sizex;false]field_close_on_enter[sizey;false]" .. + "field_close_on_enter[positionx;false]field_close_on_enter[positiony;false]" .. + "field_close_on_enter[anchorx;false]field_close_on_enter[anchory;false]" .. + "field_close_on_enter[paddingx;false]field_close_on_enter[paddingy;false]" + end, + -- Animation [[ formspec_version[3] @@ -403,10 +432,14 @@ mouse control = true] ]], } -local function show_test_formspec(pname, page_id) - page_id = page_id or 2 +local page_id = 2 +local function show_test_formspec(pname) + local page = pages[page_id] + if type(page) == "function" then + page = page() + end - local fs = pages[page_id] .. "tabheader[0,0;8,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Invs,Anim,Model,ScrollC,Sound;" .. page_id .. ";false;false]" + local fs = page .. "tabheader[0,0;8,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Invs,Window,Anim,Model,ScrollC,Sound;" .. page_id .. ";false;false]" minetest.show_formspec(pname, "testformspec:formspec", fs) end @@ -416,9 +449,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) return false end - if fields.maintabs then - show_test_formspec(player:get_player_name(), tonumber(fields.maintabs)) + page_id = tonumber(fields.maintabs) + show_test_formspec(player:get_player_name()) return true end @@ -434,6 +467,26 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) minetest.chat_send_player(player:get_player_name(), "Hypertext action received: " .. tostring(fields.hypertext)) return true end + + for name, value in pairs(fields) do + if window[name] then + print(name, window[name]) + local num_val = tonumber(value) or 0 + + if name == "sizex" and num_val < 4 then + num_val = 6.5 + elseif name == "sizey" and num_val < 5 then + num_val = 5.5 + end + + window[name] = num_val + print(name, window[name]) + end + end + + if fields.submit_window then + show_test_formspec(player:get_player_name()) + end end) minetest.register_chatcommand("test_formspec", { diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index dfeea12db..2cf9d9942 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -2470,11 +2470,16 @@ bool GUIFormSpecMenu::parsePositionDirect(parserData *data, const std::string &e void GUIFormSpecMenu::parsePosition(parserData *data, const std::string &element) { - std::vector parts = split(element, ','); + std::vector 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 v_geom = split(parts[0], ','); + + MY_CHECKGEOM("position", 0); + + data->offset.X = stof(v_geom[0]); + data->offset.Y = stof(v_geom[1]); return; } @@ -2504,11 +2509,16 @@ bool GUIFormSpecMenu::parseAnchorDirect(parserData *data, const std::string &ele void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element) { - std::vector parts = split(element, ','); + std::vector 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 v_geom = split(parts[0], ','); + + MY_CHECKGEOM("anchor", 0); + + data->anchor.X = stof(v_geom[0]); + data->anchor.Y = stof(v_geom[1]); return; } @@ -2516,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 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 parts = split(element, ';'); + + if (parts.size() == 1 || + (parts.size() > 1 && m_formspec_version > FORMSPEC_API_VERSION)) { + std::vector 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 parts = split(element, ';'); @@ -3022,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 @@ -3124,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()) @@ -3189,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) { @@ -3209,13 +3265,15 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) ((15.0 / 13.0) * (0.85 + mydata.invsize.Y)); } + s32 min_screen_dim = std::min(mydata.screensize.X, mydata.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. diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 4ba9f3959..0b4d3879d 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -368,6 +368,7 @@ private: v2s32 size; v2f32 offset; v2f32 anchor; + v2f32 padding; core::rect rect; v2s32 basepos; v2u32 screensize; @@ -449,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); -- cgit v1.2.3 From e39b159845871fbb1634570bd4af999c1c72e6fa Mon Sep 17 00:00:00 2001 From: Vincent Robinson Date: Tue, 4 Jan 2022 17:47:32 -0800 Subject: Base formspec coordinate size on padded screensize --- src/gui/guiFormSpecMenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 2cf9d9942..770a50bd9 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3265,7 +3265,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) ((15.0 / 13.0) * (0.85 + mydata.invsize.Y)); } - s32 min_screen_dim = std::min(mydata.screensize.X, mydata.screensize.Y); + 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 -- cgit v1.2.3 From 76dbd0d2d04712dcad4f7c6afecb97fa8d662d6d Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 8 Jan 2022 14:53:25 +0100 Subject: Fully remove bitmap font support (#11863) Freetype is now a build requirement. --- .github/workflows/build.yml | 23 ------ README.md | 18 ++--- android/native/jni/Android.mk | 1 - builtin/settingtypes.txt | 16 +--- doc/Doxyfile.in | 1 - fonts/mono_dejavu_sans_10.xml | Bin 257014 -> 0 bytes fonts/mono_dejavu_sans_100.png | Bin 56121 -> 0 bytes fonts/mono_dejavu_sans_11.xml | Bin 263644 -> 0 bytes fonts/mono_dejavu_sans_110.png | Bin 67613 -> 0 bytes fonts/mono_dejavu_sans_12.xml | Bin 268932 -> 0 bytes fonts/mono_dejavu_sans_120.png | Bin 73938 -> 0 bytes fonts/mono_dejavu_sans_14.xml | Bin 269188 -> 0 bytes fonts/mono_dejavu_sans_140.png | Bin 89073 -> 0 bytes fonts/mono_dejavu_sans_16.xml | Bin 275642 -> 0 bytes fonts/mono_dejavu_sans_160.png | Bin 101939 -> 0 bytes fonts/mono_dejavu_sans_18.xml | Bin 279962 -> 0 bytes fonts/mono_dejavu_sans_180.png | Bin 122274 -> 0 bytes fonts/mono_dejavu_sans_20.xml | Bin 282588 -> 0 bytes fonts/mono_dejavu_sans_200.png | Bin 138662 -> 0 bytes fonts/mono_dejavu_sans_22.xml | Bin 283950 -> 0 bytes fonts/mono_dejavu_sans_220.png | Bin 152844 -> 0 bytes fonts/mono_dejavu_sans_24.xml | Bin 286626 -> 0 bytes fonts/mono_dejavu_sans_240.png | Bin 170247 -> 0 bytes fonts/mono_dejavu_sans_26.xml | Bin 289710 -> 0 bytes fonts/mono_dejavu_sans_260.png | Bin 190156 -> 0 bytes fonts/mono_dejavu_sans_28.xml | Bin 292596 -> 0 bytes fonts/mono_dejavu_sans_280.png | Bin 200848 -> 0 bytes fonts/mono_dejavu_sans_4.xml | Bin 237740 -> 0 bytes fonts/mono_dejavu_sans_40.png | Bin 15668 -> 0 bytes fonts/mono_dejavu_sans_6.xml | Bin 245472 -> 0 bytes fonts/mono_dejavu_sans_60.png | Bin 29291 -> 0 bytes fonts/mono_dejavu_sans_8.xml | Bin 251876 -> 0 bytes fonts/mono_dejavu_sans_80.png | Bin 45552 -> 0 bytes fonts/mono_dejavu_sans_9.xml | Bin 254016 -> 0 bytes fonts/mono_dejavu_sans_90.png | Bin 50995 -> 0 bytes src/CMakeLists.txt | 49 ++++------- src/client/fontengine.cpp | 153 +++++------------------------------ src/client/fontengine.h | 9 +-- src/cmake_config.h.in | 1 - src/constants.h | 1 - src/defaultsettings.cpp | 11 +-- src/gui/guiChatConsole.cpp | 14 +--- src/gui/guiHyperText.cpp | 23 ++---- src/gui/guiHyperText.h | 11 +-- src/irrlicht_changes/CMakeLists.txt | 7 +- src/irrlicht_changes/static_text.cpp | 13 +-- src/irrlicht_changes/static_text.h | 36 --------- src/version.cpp | 1 - util/buildbot/buildwin32.sh | 1 - util/buildbot/buildwin64.sh | 1 - 50 files changed, 71 insertions(+), 319 deletions(-) delete mode 100644 fonts/mono_dejavu_sans_10.xml delete mode 100644 fonts/mono_dejavu_sans_100.png delete mode 100644 fonts/mono_dejavu_sans_11.xml delete mode 100644 fonts/mono_dejavu_sans_110.png delete mode 100644 fonts/mono_dejavu_sans_12.xml delete mode 100644 fonts/mono_dejavu_sans_120.png delete mode 100644 fonts/mono_dejavu_sans_14.xml delete mode 100644 fonts/mono_dejavu_sans_140.png delete mode 100644 fonts/mono_dejavu_sans_16.xml delete mode 100644 fonts/mono_dejavu_sans_160.png delete mode 100644 fonts/mono_dejavu_sans_18.xml delete mode 100644 fonts/mono_dejavu_sans_180.png delete mode 100644 fonts/mono_dejavu_sans_20.xml delete mode 100644 fonts/mono_dejavu_sans_200.png delete mode 100644 fonts/mono_dejavu_sans_22.xml delete mode 100644 fonts/mono_dejavu_sans_220.png delete mode 100644 fonts/mono_dejavu_sans_24.xml delete mode 100644 fonts/mono_dejavu_sans_240.png delete mode 100644 fonts/mono_dejavu_sans_26.xml delete mode 100644 fonts/mono_dejavu_sans_260.png delete mode 100644 fonts/mono_dejavu_sans_28.xml delete mode 100644 fonts/mono_dejavu_sans_280.png delete mode 100644 fonts/mono_dejavu_sans_4.xml delete mode 100644 fonts/mono_dejavu_sans_40.png delete mode 100644 fonts/mono_dejavu_sans_6.xml delete mode 100644 fonts/mono_dejavu_sans_60.png delete mode 100644 fonts/mono_dejavu_sans_8.xml delete mode 100644 fonts/mono_dejavu_sans_80.png delete mode 100644 fonts/mono_dejavu_sans_9.xml delete mode 100644 fonts/mono_dejavu_sans_90.png (limited to 'src/gui') diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index af1de15ec..b1ba78ed0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -150,29 +150,6 @@ jobs: run: | ./bin/minetestserver --run-unittests - # Build without freetype (client-only) - clang_9_no_freetype: - name: "clang_9 (FREETYPE=0)" - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - name: Install deps - run: | - source ./util/ci/common.sh - install_linux_deps clang-9 - - - name: Build - run: | - ./util/ci/build.sh - env: - CC: clang-9 - CXX: clang++-9 - CMAKE_FLAGS: "-DENABLE_FREETYPE=0 -DBUILD_SERVER=0" - - - name: Test - run: | - ./bin/minetest --run-unittests - docker: name: "Docker image" runs-on: ubuntu-18.04 diff --git a/README.md b/README.md index 009ae8d38..03a161c9a 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,8 @@ Compiling | GCC | 4.9+ | Can be replaced with Clang 3.4+ | | CMake | 3.5+ | | | IrrlichtMt | - | Custom version of Irrlicht, see https://github.com/minetest/irrlicht | -| SQLite3 | 3.0+ | | +| Freetype | 2.0+ | | +| SQLite3 | 3+ | | | Zstd | 1.0+ | | | LuaJIT | 2.0+ | Bundled Lua 5.1 is used if not present | | GMP | 5.0.0+ | Bundled mini-GMP is used if not present | @@ -143,7 +144,7 @@ Compiling For Debian/Ubuntu users: - sudo apt install g++ make libc6-dev cmake libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev + sudo apt install g++ make libc6-dev cmake libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev libluajit-5.1-dev For Fedora users: @@ -247,7 +248,6 @@ General options and their default values: MinSizeRel - Release build with -Os passed to compiler to make executable as small as possible ENABLE_CURL=ON - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http ENABLE_CURSES=ON - Build with (n)curses; Enables a server side terminal (command line option: --terminal) - ENABLE_FREETYPE=ON - Build with FreeType2; Allows using TTF fonts ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations ENABLE_GLES=OFF - Build for OpenGL ES instead of OpenGL (requires support by IrrlichtMt) ENABLE_LEVELDB=ON - Build with LevelDB; Enables use of LevelDB map backend @@ -273,10 +273,10 @@ Library specific options: EGL_INCLUDE_DIR - Only if building with GLES; directory that contains egl.h EGL_LIBRARY - Only if building with GLES; path to libEGL.a/libEGL.so EXTRA_DLL - Only on Windows; optional paths to additional DLLs that should be packaged - FREETYPE_INCLUDE_DIR_freetype2 - Only if building with FreeType 2; directory that contains an freetype directory with files such as ftimage.h in it - FREETYPE_INCLUDE_DIR_ft2build - Only if building with FreeType 2; directory that contains ft2build.h - FREETYPE_LIBRARY - Only if building with FreeType 2; path to libfreetype.a/libfreetype.so/freetype.lib - FREETYPE_DLL - Only if building with FreeType 2 on Windows; path to libfreetype.dll + FREETYPE_INCLUDE_DIR_freetype2 - Directory that contains files such as ftimage.h + FREETYPE_INCLUDE_DIR_ft2build - Directory that contains ft2build.h + FREETYPE_LIBRARY - Path to libfreetype.a/libfreetype.so/freetype.lib + FREETYPE_DLL - Only on Windows; path to libfreetype-6.dll GETTEXT_DLL - Only when building with gettext on Windows; paths to libintl + libiconv DLLs GETTEXT_INCLUDE_DIR - Only when building with gettext; directory that contains iconv.h GETTEXT_LIBRARY - Only when building with gettext on Windows; path to libintl.dll.a @@ -337,7 +337,6 @@ vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo - **Don't forget about IrrlichtMt.** The easiest way is to clone it to `lib/irrlichtmt` as described in the Linux section. - `curl` is optional, but required to read the serverlist, `curl[winssl]` is required to use the content store. - `openal-soft`, `libvorbis` and `libogg` are optional, but required to use sound. -- `freetype` is optional, it allows true-type font rendering. - `luajit` is optional, it replaces the integrated Lua interpreter with a faster just-in-time interpreter. - `gmp` and `jsoncpp` are optional, otherwise the bundled versions will be compiled @@ -429,8 +428,7 @@ cmake .. \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 \ -DCMAKE_FIND_FRAMEWORK=LAST \ -DCMAKE_INSTALL_PREFIX=../build/macos/ \ - -DRUN_IN_PLACE=FALSE \ - -DENABLE_FREETYPE=TRUE -DENABLE_GETTEXT=TRUE + -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE make -j$(nproc) make install diff --git a/android/native/jni/Android.mk b/android/native/jni/Android.mk index 85c13bfb3..f8ca74d3c 100644 --- a/android/native/jni/Android.mk +++ b/android/native/jni/Android.mk @@ -91,7 +91,6 @@ LOCAL_CFLAGS += \ -DENABLE_GLES=1 \ -DUSE_CURL=1 \ -DUSE_SOUND=1 \ - -DUSE_FREETYPE=1 \ -DUSE_LEVELDB=0 \ -DUSE_LUAJIT=1 \ -DUSE_GETTEXT=1 \ diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 22e69e30a..c25a941de 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -886,10 +886,6 @@ tooltip_show_delay (Tooltip delay) int 400 # Append item name to tooltip. tooltip_append_itemname (Append item name) bool false -# Whether FreeType fonts are used, requires FreeType support to be compiled in. -# If disabled, bitmap and XML vectors fonts are used instead. -freetype (FreeType fonts) bool true - font_bold (Font bold by default) bool false font_italic (Font italic by default) bool false @@ -909,9 +905,7 @@ font_size (Font size) int 16 1 # sized 16, 32, 48, etc., so a mod requesting a size of 25 will get 32. font_size_divisible_by (Font size divisible by) int 1 1 -# Path to the default font. -# If “freetype” setting is enabled: Must be a TrueType font. -# If “freetype” setting is disabled: Must be a bitmap or XML vectors font. +# Path to the default font. Must be a TrueType font. # The fallback font will be used if the font cannot be loaded. font_path (Regular font path) filepath fonts/Arimo-Regular.ttf @@ -928,9 +922,7 @@ mono_font_size (Monospace font size) int 16 1 # sized 16, 32, 48, etc., so a mod requesting a size of 25 will get 32. mono_font_size_divisible_by (Monospace font size divisible by) int 1 1 -# Path to the monospace font. -# If “freetype” setting is enabled: Must be a TrueType font. -# If “freetype” setting is disabled: Must be a bitmap or XML vectors font. +# Path to the monospace font. Must be a TrueType font. # This font is used for e.g. the console and profiler screen. mono_font_path (Monospace font path) filepath fonts/Cousine-Regular.ttf @@ -938,9 +930,7 @@ mono_font_path_bold (Bold monospace font path) filepath fonts/Cousine-Bold.ttf mono_font_path_italic (Italic monospace font path) filepath fonts/Cousine-Italic.ttf mono_font_path_bold_italic (Bold and italic monospace font path) filepath fonts/Cousine-BoldItalic.ttf -# Path of the fallback font. -# If “freetype” setting is enabled: Must be a TrueType font. -# If “freetype” setting is disabled: Must be a bitmap or XML vectors font. +# Path of the fallback font. Must be a TrueType font. # This font will be used for certain languages or if the default font is unavailable. fallback_font_path (Fallback font path) filepath fonts/DroidSansFallbackFull.ttf diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index d7816f0e4..ae36fd6bf 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -16,7 +16,6 @@ PREDEFINED = "USE_SPATIAL=1" \ "USE_REDIS=1" \ "USE_SOUND=1" \ "USE_CURL=1" \ - "USE_FREETYPE=1" \ "USE_GETTEXT=1" # Input diff --git a/fonts/mono_dejavu_sans_10.xml b/fonts/mono_dejavu_sans_10.xml deleted file mode 100644 index 0276cedb6..000000000 Binary files a/fonts/mono_dejavu_sans_10.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_100.png b/fonts/mono_dejavu_sans_100.png deleted file mode 100644 index 45a312542..000000000 Binary files a/fonts/mono_dejavu_sans_100.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_11.xml b/fonts/mono_dejavu_sans_11.xml deleted file mode 100644 index f727ed2bb..000000000 Binary files a/fonts/mono_dejavu_sans_11.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_110.png b/fonts/mono_dejavu_sans_110.png deleted file mode 100644 index c90c0e242..000000000 Binary files a/fonts/mono_dejavu_sans_110.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_12.xml b/fonts/mono_dejavu_sans_12.xml deleted file mode 100644 index 38f6427be..000000000 Binary files a/fonts/mono_dejavu_sans_12.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_120.png b/fonts/mono_dejavu_sans_120.png deleted file mode 100644 index 0cebd70e8..000000000 Binary files a/fonts/mono_dejavu_sans_120.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_14.xml b/fonts/mono_dejavu_sans_14.xml deleted file mode 100644 index b90a34960..000000000 Binary files a/fonts/mono_dejavu_sans_14.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_140.png b/fonts/mono_dejavu_sans_140.png deleted file mode 100644 index a413759ea..000000000 Binary files a/fonts/mono_dejavu_sans_140.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_16.xml b/fonts/mono_dejavu_sans_16.xml deleted file mode 100644 index 3f7d2c2a2..000000000 Binary files a/fonts/mono_dejavu_sans_16.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_160.png b/fonts/mono_dejavu_sans_160.png deleted file mode 100644 index bd8a2f40a..000000000 Binary files a/fonts/mono_dejavu_sans_160.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_18.xml b/fonts/mono_dejavu_sans_18.xml deleted file mode 100644 index 92865cbfc..000000000 Binary files a/fonts/mono_dejavu_sans_18.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_180.png b/fonts/mono_dejavu_sans_180.png deleted file mode 100644 index a299afcbe..000000000 Binary files a/fonts/mono_dejavu_sans_180.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_20.xml b/fonts/mono_dejavu_sans_20.xml deleted file mode 100644 index acd8c77d0..000000000 Binary files a/fonts/mono_dejavu_sans_20.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_200.png b/fonts/mono_dejavu_sans_200.png deleted file mode 100644 index 68ee62681..000000000 Binary files a/fonts/mono_dejavu_sans_200.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_22.xml b/fonts/mono_dejavu_sans_22.xml deleted file mode 100644 index eafb4def6..000000000 Binary files a/fonts/mono_dejavu_sans_22.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_220.png b/fonts/mono_dejavu_sans_220.png deleted file mode 100644 index 042d7e094..000000000 Binary files a/fonts/mono_dejavu_sans_220.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_24.xml b/fonts/mono_dejavu_sans_24.xml deleted file mode 100644 index fc8b6232e..000000000 Binary files a/fonts/mono_dejavu_sans_24.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_240.png b/fonts/mono_dejavu_sans_240.png deleted file mode 100644 index d2d68c5bb..000000000 Binary files a/fonts/mono_dejavu_sans_240.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_26.xml b/fonts/mono_dejavu_sans_26.xml deleted file mode 100644 index 829f09948..000000000 Binary files a/fonts/mono_dejavu_sans_26.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_260.png b/fonts/mono_dejavu_sans_260.png deleted file mode 100644 index 3a8cb6c57..000000000 Binary files a/fonts/mono_dejavu_sans_260.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_28.xml b/fonts/mono_dejavu_sans_28.xml deleted file mode 100644 index b5b25bd07..000000000 Binary files a/fonts/mono_dejavu_sans_28.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_280.png b/fonts/mono_dejavu_sans_280.png deleted file mode 100644 index ccf62ba48..000000000 Binary files a/fonts/mono_dejavu_sans_280.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_4.xml b/fonts/mono_dejavu_sans_4.xml deleted file mode 100644 index cfebb39b3..000000000 Binary files a/fonts/mono_dejavu_sans_4.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_40.png b/fonts/mono_dejavu_sans_40.png deleted file mode 100644 index 24ed693f7..000000000 Binary files a/fonts/mono_dejavu_sans_40.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_6.xml b/fonts/mono_dejavu_sans_6.xml deleted file mode 100644 index d0e1de21d..000000000 Binary files a/fonts/mono_dejavu_sans_6.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_60.png b/fonts/mono_dejavu_sans_60.png deleted file mode 100644 index 326af996f..000000000 Binary files a/fonts/mono_dejavu_sans_60.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_8.xml b/fonts/mono_dejavu_sans_8.xml deleted file mode 100644 index c48bf7ccc..000000000 Binary files a/fonts/mono_dejavu_sans_8.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_80.png b/fonts/mono_dejavu_sans_80.png deleted file mode 100644 index 04326dbb2..000000000 Binary files a/fonts/mono_dejavu_sans_80.png and /dev/null differ diff --git a/fonts/mono_dejavu_sans_9.xml b/fonts/mono_dejavu_sans_9.xml deleted file mode 100644 index 74e841034..000000000 Binary files a/fonts/mono_dejavu_sans_9.xml and /dev/null differ diff --git a/fonts/mono_dejavu_sans_90.png b/fonts/mono_dejavu_sans_90.png deleted file mode 100644 index 65ac51858..000000000 Binary files a/fonts/mono_dejavu_sans_90.png and /dev/null differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e3389cea9..ed0929564 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -122,16 +122,8 @@ if(BUILD_CLIENT) endif() endif() - -option(ENABLE_FREETYPE "Enable FreeType2 (TrueType fonts and basic unicode support)" TRUE) -set(USE_FREETYPE FALSE) - -if(BUILD_CLIENT AND ENABLE_FREETYPE) - find_package(Freetype) - if(FREETYPE_FOUND) - message(STATUS "Freetype enabled.") - set(USE_FREETYPE TRUE) - endif() +if(BUILD_CLIENT) + find_package(Freetype REQUIRED) endif() option(ENABLE_CURSES "Enable ncurses console" TRUE) @@ -495,13 +487,11 @@ include_directories( ${PROJECT_SOURCE_DIR} ${ZLIB_INCLUDE_DIR} ${ZSTD_INCLUDE_DIR} - ${SOUND_INCLUDE_DIRS} ${SQLITE3_INCLUDE_DIR} ${LUA_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${JSON_INCLUDE_DIR} ${LUA_BIT_INCLUDE_DIR} - ${X11_INCLUDE_DIR} ${PROJECT_SOURCE_DIR}/script ) @@ -509,8 +499,12 @@ if(USE_GETTEXT) include_directories(${GETTEXT_INCLUDE_DIR}) endif() -if(USE_FREETYPE) - include_directories(${FREETYPE_INCLUDE_DIRS}) +if(BUILD_CLIENT) + include_directories( + ${FREETYPE_INCLUDE_DIRS} + ${SOUND_INCLUDE_DIRS} + ${X11_INCLUDE_DIR} + ) endif() if(USE_CURL) @@ -539,6 +533,7 @@ if(BUILD_CLIENT) ${GMP_LIBRARY} ${JSON_LIBRARY} ${LUA_BIT_LIBRARY} + ${FREETYPE_LIBRARY} ${PLATFORM_LIBS} ) if(NOT USE_LUAJIT) @@ -573,17 +568,11 @@ if(BUILD_CLIENT) ${CURL_LIBRARY} ) endif() - if(USE_FREETYPE) - if(FREETYPE_PKGCONFIG_FOUND) - set_target_properties(${PROJECT_NAME} - PROPERTIES - COMPILE_FLAGS "${FREETYPE_CFLAGS_STR}" - ) - endif() - target_link_libraries( - ${PROJECT_NAME} - ${FREETYPE_LIBRARY} - ) + if(FREETYPE_PKGCONFIG_FOUND) + set_target_properties(${PROJECT_NAME} + PROPERTIES + COMPILE_FLAGS "${FREETYPE_CFLAGS_STR}" + ) endif() if (USE_CURSES) target_link_libraries(${PROJECT_NAME} ${CURSES_LIBRARIES}) @@ -896,14 +885,8 @@ if(BUILD_CLIENT) endforeach() endif() - # Install necessary fonts depending on configuration - if(USE_FREETYPE) - install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../fonts" DESTINATION "${SHAREDIR}" - FILES_MATCHING PATTERN "*.ttf" PATTERN "*.txt") - else() - install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../fonts" DESTINATION "${SHAREDIR}" - FILES_MATCHING PATTERN "*.png" PATTERN "*.xml") - endif() + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../fonts" DESTINATION "${SHAREDIR}" + FILES_MATCHING PATTERN "*.ttf" PATTERN "*.txt") endif(BUILD_CLIENT) if(BUILD_SERVER) diff --git a/src/client/fontengine.cpp b/src/client/fontengine.cpp index e537b756c..ad8305b45 100644 --- a/src/client/fontengine.cpp +++ b/src/client/fontengine.cpp @@ -24,10 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "filesys.h" #include "gettext.h" - -#if USE_FREETYPE #include "irrlicht_changes/CGUITTFont.h" -#endif /** maximum size distance for getting a "similar" font size */ #define MAX_FONT_SIZE_OFFSET 10 @@ -45,9 +42,8 @@ static void font_setting_changed(const std::string &name, void *userdata) FontEngine::FontEngine(gui::IGUIEnvironment* env) : m_env(env) { - for (u32 &i : m_default_size) { - i = (FontMode) FONT_SIZE_UNSPECIFIED; + i = FONT_SIZE_UNSPECIFIED; } assert(g_settings != NULL); // pre-condition @@ -56,25 +52,19 @@ FontEngine::FontEngine(gui::IGUIEnvironment* env) : readSettings(); - if (m_currentMode != FM_Simple) { - g_settings->registerChangedCallback("font_size", font_setting_changed, NULL); - g_settings->registerChangedCallback("font_bold", font_setting_changed, NULL); - g_settings->registerChangedCallback("font_italic", font_setting_changed, NULL); - g_settings->registerChangedCallback("font_path", font_setting_changed, NULL); - g_settings->registerChangedCallback("font_path_bold", font_setting_changed, NULL); - g_settings->registerChangedCallback("font_path_italic", font_setting_changed, NULL); - g_settings->registerChangedCallback("font_path_bolditalic", font_setting_changed, NULL); - g_settings->registerChangedCallback("font_shadow", font_setting_changed, NULL); - g_settings->registerChangedCallback("font_shadow_alpha", font_setting_changed, NULL); - g_settings->registerChangedCallback("font_size_divisible_by", font_setting_changed, NULL); - g_settings->registerChangedCallback("fallback_font_path", font_setting_changed, NULL); - } + const char *settings[] = { + "font_size", "font_bold", "font_italic", "font_size_divisible_by", + "mono_font_size", "mono_font_size_divisible_by", + "font_shadow", "font_shadow_alpha", + "font_path", "font_path_bold", "font_path_italic", "font_path_bold_italic", + "mono_font_path", "mono_font_path_bold", "mono_font_path_italic", + "mono_font_path_bold_italic", + "fallback_font_path", + "screen_dpi", "gui_scaling", + }; - g_settings->registerChangedCallback("mono_font_path", font_setting_changed, NULL); - g_settings->registerChangedCallback("mono_font_size", font_setting_changed, NULL); - g_settings->registerChangedCallback("mono_font_size_divisible_by", font_setting_changed, NULL); - g_settings->registerChangedCallback("screen_dpi", font_setting_changed, NULL); - g_settings->registerChangedCallback("gui_scaling", font_setting_changed, NULL); + for (auto name : settings) + g_settings->registerChangedCallback(name, font_setting_changed, NULL); } /******************************************************************************/ @@ -108,16 +98,8 @@ irr::gui::IGUIFont *FontEngine::getFont(FontSpec spec, bool may_fail) { if (spec.mode == FM_Unspecified) { spec.mode = m_currentMode; - } else if (m_currentMode == FM_Simple) { - // Freetype disabled -> Force simple mode - spec.mode = (spec.mode == FM_Mono || - spec.mode == FM_SimpleMono) ? - FM_SimpleMono : FM_Simple; - // Support for those could be added, but who cares? - spec.bold = false; - spec.italic = false; } else if (spec.mode == _FM_Fallback) { - // Fallback font doesn't support these either + // Fallback font doesn't support these spec.bold = false; spec.italic = false; } @@ -134,11 +116,7 @@ irr::gui::IGUIFont *FontEngine::getFont(FontSpec spec, bool may_fail) return it->second; // Font does not yet exist - gui::IGUIFont *font = nullptr; - if (spec.mode == FM_Simple || spec.mode == FM_SimpleMono) - font = initSimpleFont(spec); - else - font = initFont(spec); + gui::IGUIFont *font = initFont(spec); if (!font && !may_fail) { errorstream << "Minetest cannot continue without a valid font. " @@ -185,13 +163,6 @@ unsigned int FontEngine::getDefaultFontSize() unsigned int FontEngine::getFontSize(FontMode mode) { - if (m_currentMode == FM_Simple) { - if (mode == FM_Mono || mode == FM_SimpleMono) - return m_default_size[FM_SimpleMono]; - else - return m_default_size[FM_Simple]; - } - if (mode == FM_Unspecified) return m_default_size[FM_Standard]; @@ -201,20 +172,12 @@ unsigned int FontEngine::getFontSize(FontMode mode) /******************************************************************************/ void FontEngine::readSettings() { - if (USE_FREETYPE && g_settings->getBool("freetype")) { - m_default_size[FM_Standard] = g_settings->getU16("font_size"); - m_default_size[_FM_Fallback] = g_settings->getU16("font_size"); - m_default_size[FM_Mono] = g_settings->getU16("mono_font_size"); + m_default_size[FM_Standard] = g_settings->getU16("font_size"); + m_default_size[_FM_Fallback] = g_settings->getU16("font_size"); + m_default_size[FM_Mono] = g_settings->getU16("mono_font_size"); - m_default_bold = g_settings->getBool("font_bold"); - m_default_italic = g_settings->getBool("font_italic"); - - } else { - m_currentMode = FM_Simple; - } - - m_default_size[FM_Simple] = g_settings->getU16("font_size"); - m_default_size[FM_SimpleMono] = g_settings->getU16("mono_font_size"); + m_default_bold = g_settings->getBool("font_bold"); + m_default_italic = g_settings->getBool("font_italic"); cleanCache(); updateFontCache(); @@ -283,7 +246,6 @@ gui::IGUIFont *FontEngine::initFont(const FontSpec &spec) Settings::getLayer(SL_DEFAULTS)->get(path_setting) }; -#if USE_FREETYPE for (const std::string &font_path : fallback_settings) { gui::CGUITTFont *font = gui::CGUITTFont::createTTFont(m_env, font_path.c_str(), size, true, true, font_shadow, @@ -302,80 +264,5 @@ gui::IGUIFont *FontEngine::initFont(const FontSpec &spec) } return font; } -#else - errorstream << "FontEngine: Tried to load TTF font but Minetest was" - " compiled without Freetype." << std::endl; -#endif return nullptr; } - -/** initialize a font without freetype */ -gui::IGUIFont *FontEngine::initSimpleFont(const FontSpec &spec) -{ - assert(spec.mode == FM_Simple || spec.mode == FM_SimpleMono); - assert(spec.size != FONT_SIZE_UNSPECIFIED); - - const std::string &font_path = g_settings->get( - (spec.mode == FM_SimpleMono) ? "mono_font_path" : "font_path"); - - size_t pos_dot = font_path.find_last_of('.'); - std::string basename = font_path, ending; - if (pos_dot != std::string::npos) - ending = lowercase(font_path.substr(pos_dot)); - - if (ending == ".ttf") { - errorstream << "FontEngine: Found font \"" << font_path - << "\" but freetype is not available." << std::endl; - return nullptr; - } - - if (ending == ".xml" || ending == ".png") - basename = font_path.substr(0, pos_dot); - - u32 size = std::floor( - RenderingEngine::getDisplayDensity() * - g_settings->getFloat("gui_scaling") * - spec.size); - - irr::gui::IGUIFont *font = nullptr; - std::string font_extensions[] = { ".png", ".xml" }; - - // Find nearest matching font scale - // Does a "zig-zag motion" (positibe/negative), from 0 to MAX_FONT_SIZE_OFFSET - for (s32 zoffset = 0; zoffset < MAX_FONT_SIZE_OFFSET * 2; zoffset++) { - std::stringstream path; - - // LSB to sign - s32 sign = (zoffset & 1) ? -1 : 1; - s32 offset = zoffset >> 1; - - for (const std::string &ext : font_extensions) { - path.str(""); // Clear - path << basename << "_" << (size + offset * sign) << ext; - - if (!fs::PathExists(path.str())) - continue; - - font = m_env->getFont(path.str().c_str()); - - if (font) { - verbosestream << "FontEngine: found font: " << path.str() << std::endl; - break; - } - } - - if (font) - break; - } - - // try name direct - if (font == NULL) { - if (fs::PathExists(font_path)) { - font = m_env->getFont(font_path.c_str()); - if (font) - verbosestream << "FontEngine: found font: " << font_path << std::endl; - } - } - - return font; -} diff --git a/src/client/fontengine.h b/src/client/fontengine.h index 403ac2e48..78608e517 100644 --- a/src/client/fontengine.h +++ b/src/client/fontengine.h @@ -34,8 +34,6 @@ enum FontMode : u8 { FM_Standard = 0, FM_Mono, _FM_Fallback, // do not use directly - FM_Simple, - FM_SimpleMono, FM_MaxMode, FM_Unspecified }; @@ -140,9 +138,6 @@ private: /** initialize a new TTF font */ gui::IGUIFont *initFont(const FontSpec &spec); - /** initialize a font without freetype */ - gui::IGUIFont *initSimpleFont(const FontSpec &spec); - /** update current minetest skin with font changes */ void updateSkin(); @@ -165,8 +160,8 @@ private: bool m_default_bold = false; bool m_default_italic = false; - /** current font engine mode */ - FontMode m_currentMode = FM_Standard; + /** default font engine mode (fixed) */ + static const FontMode m_currentMode = FM_Standard; DISABLE_CLASS_COPY(FontEngine); }; diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in index cfcee4b58..cf436d6dc 100644 --- a/src/cmake_config.h.in +++ b/src/cmake_config.h.in @@ -18,7 +18,6 @@ #cmakedefine01 USE_GETTEXT #cmakedefine01 USE_CURL #cmakedefine01 USE_SOUND -#cmakedefine01 USE_FREETYPE #cmakedefine01 USE_CURSES #cmakedefine01 USE_LEVELDB #cmakedefine01 USE_LUAJIT diff --git a/src/constants.h b/src/constants.h index 3cc3af094..ed858912d 100644 --- a/src/constants.h +++ b/src/constants.h @@ -111,4 +111,3 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #define TTF_DEFAULT_FONT_SIZE (16) -#define DEFAULT_FONT_SIZE (10) diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 47790a552..9e4bb14b5 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -303,8 +303,7 @@ void set_default_settings() settings->setDefault("main_menu_path", ""); settings->setDefault("serverlist_file", "favoriteservers.json"); -#if USE_FREETYPE - settings->setDefault("freetype", "true"); + // General font settings settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "Arimo-Regular.ttf")); settings->setDefault("font_path_italic", porting::getDataPath("fonts" DIR_DELIM "Arimo-Italic.ttf")); settings->setDefault("font_path_bold", porting::getDataPath("fonts" DIR_DELIM "Arimo-Bold.ttf")); @@ -322,14 +321,6 @@ void set_default_settings() settings->setDefault("fallback_font_path", porting::getDataPath("fonts" DIR_DELIM "DroidSansFallbackFull.ttf")); std::string font_size_str = std::to_string(TTF_DEFAULT_FONT_SIZE); -#else - settings->setDefault("freetype", "false"); - settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "mono_dejavu_sans")); - settings->setDefault("mono_font_path", porting::getDataPath("fonts" DIR_DELIM "mono_dejavu_sans")); - - std::string font_size_str = std::to_string(DEFAULT_FONT_SIZE); -#endif - // General font settings settings->setDefault("font_size", font_size_str); settings->setDefault("mono_font_size", font_size_str); settings->setDefault("chat_font_size", "0"); // Default "font_size" diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp index 0610c85cc..01e10ea2e 100644 --- a/src/gui/guiChatConsole.cpp +++ b/src/gui/guiChatConsole.cpp @@ -30,12 +30,9 @@ 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 -#if USE_FREETYPE - #include "irrlicht_changes/CGUITTFont.h" -#endif - inline u32 clamp_u8(s32 value) { return (u32) MYMIN(MYMAX(value, 0), 255); @@ -328,19 +325,16 @@ void GUIChatConsole::drawText() core::rect 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(m_font); + // Draw colored text if possible + gui::CGUITTFont *tmp = static_cast(m_font); tmp->draw( fragment.text, destrect, false, false, &AbsoluteClippingRect); - } else -#endif - { + } else { // Otherwise use standard text m_font->draw( fragment.text.c_str(), 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 -#include -#include -using namespace irr::gui; #include "client/fontengine.h" -#include #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(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 +#include +#include +#include +#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/irrlicht_changes/CMakeLists.txt b/src/irrlicht_changes/CMakeLists.txt index 87c88f7e8..19f431af3 100644 --- a/src/irrlicht_changes/CMakeLists.txt +++ b/src/irrlicht_changes/CMakeLists.txt @@ -1,14 +1,9 @@ if (BUILD_CLIENT) set(client_irrlicht_changes_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/static_text.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/CGUITTFont.cpp ) - if (USE_FREETYPE) - set(client_irrlicht_changes_SRCS ${client_irrlicht_changes_SRCS} - ${CMAKE_CURRENT_SOURCE_DIR}/CGUITTFont.cpp - ) - endif() - # CMake require us to set a local scope and then parent scope # Else the last set win in parent scope set(client_irrlicht_changes_SRCS ${client_irrlicht_changes_SRCS} PARENT_SCOPE) diff --git a/src/irrlicht_changes/static_text.cpp b/src/irrlicht_changes/static_text.cpp index f548c3f71..baf0ea626 100644 --- a/src/irrlicht_changes/static_text.cpp +++ b/src/irrlicht_changes/static_text.cpp @@ -12,17 +12,12 @@ #include #include -#if USE_FREETYPE - #include "CGUITTFont.h" -#endif - +#include "CGUITTFont.h" #include "util/string.h" namespace irr { -#if USE_FREETYPE - namespace gui { //! constructor @@ -108,14 +103,12 @@ void StaticText::draw() font->getDimension(str.c_str()).Width; } -#if USE_FREETYPE if (font->getType() == irr::gui::EGFT_CUSTOM) { - irr::gui::CGUITTFont *tmp = static_cast(font); + CGUITTFont *tmp = static_cast(font); tmp->draw(str, r, HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER, (RestrainTextInside ? &AbsoluteClippingRect : NULL)); } else -#endif { // Draw non-colored text font->draw(str.c_str(), @@ -590,8 +583,6 @@ s32 StaticText::getTextWidth() const } // end namespace gui -#endif // USE_FREETYPE - } // end namespace irr diff --git a/src/irrlicht_changes/static_text.h b/src/irrlicht_changes/static_text.h index 17a3bf753..74ef62008 100644 --- a/src/irrlicht_changes/static_text.h +++ b/src/irrlicht_changes/static_text.h @@ -20,7 +20,6 @@ #include "config.h" #include -#if USE_FREETYPE namespace irr { @@ -230,41 +229,6 @@ inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedS } } -#else // USE_FREETYPE - -namespace irr -{ -namespace gui -{ - -class StaticText -{ -public: - static irr::gui::IGUIStaticText *add( - irr::gui::IGUIEnvironment *guienv, - const EnrichedString &text, - const core::rect< s32 > &rectangle, - bool border = false, - bool wordWrap = true, - irr::gui::IGUIElement *parent = NULL, - s32 id = -1, - bool fillBackground = false) - { - return guienv->addStaticText(text.c_str(), rectangle, border, wordWrap, parent, id, fillBackground); - } -}; - -} // end namespace gui - -} // end namespace irr - -inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text) -{ - static_text->setText(text.c_str()); -} - -#endif - inline void setStaticText(irr::gui::IGUIStaticText *static_text, const wchar_t *text) { setStaticText(static_text, EnrichedString(text, static_text->getOverrideColor())); diff --git a/src/version.cpp b/src/version.cpp index c555f30af..f2aac37df 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -37,7 +37,6 @@ const char *g_build_info = #ifndef SERVER "USE_GETTEXT=" STR(USE_GETTEXT) "\n" "USE_SOUND=" STR(USE_SOUND) "\n" - "USE_FREETYPE=" STR(USE_FREETYPE) "\n" #endif "STATIC_SHAREDIR=" STR(STATIC_SHAREDIR) #if USE_GETTEXT && defined(STATIC_LOCALEDIR) diff --git a/util/buildbot/buildwin32.sh b/util/buildbot/buildwin32.sh index 696297aed..2eb9dab11 100755 --- a/util/buildbot/buildwin32.sh +++ b/util/buildbot/buildwin32.sh @@ -129,7 +129,6 @@ cmake -S $sourcedir -B . \ -DENABLE_SOUND=1 \ -DENABLE_CURL=1 \ -DENABLE_GETTEXT=1 \ - -DENABLE_FREETYPE=1 \ -DENABLE_LEVELDB=1 \ \ -DCMAKE_PREFIX_PATH=$libdir/irrlicht \ diff --git a/util/buildbot/buildwin64.sh b/util/buildbot/buildwin64.sh index 20d0b3a6a..3dd4db687 100755 --- a/util/buildbot/buildwin64.sh +++ b/util/buildbot/buildwin64.sh @@ -129,7 +129,6 @@ cmake -S $sourcedir -B . \ -DENABLE_SOUND=1 \ -DENABLE_CURL=1 \ -DENABLE_GETTEXT=1 \ - -DENABLE_FREETYPE=1 \ -DENABLE_LEVELDB=1 \ \ -DCMAKE_PREFIX_PATH=$libdir/irrlicht \ -- cgit v1.2.3 From 8fab406c28bc5a18137d2653356c10880c827613 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sun, 9 Jan 2022 18:33:37 +0100 Subject: Formspec: Fix bgcolor and set_focus checks --- src/gui/guiFormSpecMenu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 770a50bd9..85bd04900 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -2250,7 +2250,7 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element) { std::vector parts; - if (!precheckElement("bgcolor", element, 2, 3, parts)) + if (!precheckElement("bgcolor", element, 1, 3, parts)) return; const u32 parameter_count = parts.size(); @@ -2705,7 +2705,7 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b void GUIFormSpecMenu::parseSetFocus(const std::string &element) { std::vector parts; - if (!precheckElement("set_focus", element, 2, 2, parts)) + if (!precheckElement("set_focus", element, 1, 2, parts)) return; if (m_is_form_regenerated) -- cgit v1.2.3 From 058846d687bef214fe6216938d4cf8bf2c79290e Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 22 Jan 2022 17:56:35 +0100 Subject: Rework drawtime and related timekeeping code to use microseconds --- src/client/camera.cpp | 2 +- src/client/camera.h | 4 +- src/client/game.cpp | 153 ++++++++++++++++++++++++---------------------- src/client/game.h | 2 +- src/client/gameui.cpp | 8 +-- src/client/gameui.h | 2 + src/gui/profilergraph.cpp | 15 ++++- 7 files changed, 101 insertions(+), 85 deletions(-) (limited to 'src/gui') diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 3712d77ea..d1f19adb3 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -308,7 +308,7 @@ void Camera::addArmInertia(f32 player_yaw) } } -void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_reload_ratio) +void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio) { // Get player position // Smooth the movement when walking up stairs diff --git a/src/client/camera.h b/src/client/camera.h index 3e1cb4fdf..403d6024c 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -140,9 +140,7 @@ public: void step(f32 dtime); // Update the camera from the local player's position. - // busytime is used to adjust the viewing range. - void update(LocalPlayer* player, f32 frametime, f32 busytime, - f32 tool_reload_ratio); + void update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio); // Update render distance void updateViewingRange(); diff --git a/src/client/game.cpp b/src/client/game.cpp index 7478e225f..8959b5f15 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -575,10 +575,19 @@ public: /**************************************************************************** ****************************************************************************/ -const float object_hit_delay = 0.2; +const static float object_hit_delay = 0.2; struct FpsControl { - u32 last_time, busy_time, sleep_time; + FpsControl() : last_time(0), busy_time(0), sleep_time(0) {} + + void reset(); + + void limit(IrrlichtDevice *device, f32 *dtime); + + u32 getBusyMs() const { return busy_time / 1000; } + + // all values in microseconds (us) + u64 last_time, busy_time, sleep_time; }; @@ -712,7 +721,7 @@ protected: void updatePlayerControl(const CameraOrientation &cam); void step(f32 *dtime); void processClientEvents(CameraOrientation *cam); - void updateCamera(u32 busy_time, f32 dtime); + void updateCamera(f32 dtime); void updateSound(f32 dtime); void processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug); /*! @@ -743,8 +752,6 @@ protected: void updateShadows(); // Misc - void limitFps(FpsControl *fps_timings, f32 *dtime); - void showOverlayMessage(const char *msg, float dtime, int percent, bool draw_clouds = true); @@ -1056,14 +1063,14 @@ void Game::run() RunStats stats = { 0 }; CameraOrientation cam_view_target = { 0 }; CameraOrientation cam_view = { 0 }; - FpsControl draw_times = { 0 }; + FpsControl draw_times; f32 dtime; // in seconds /* Clear the profiler */ Profiler::GraphValues dummyvalues; g_profiler->graphGet(dummyvalues); - draw_times.last_time = m_rendering_engine->get_timer_time(); + draw_times.reset(); set_light_table(g_settings->getFloat("display_gamma")); @@ -1095,7 +1102,7 @@ void Game::run() // Calculate dtime = // m_rendering_engine->run() from this iteration // + Sleep time until the wanted FPS are reached - limitFps(&draw_times, &dtime); + draw_times.limit(device, &dtime); // Prepare render data for next iteration @@ -1125,7 +1132,7 @@ void Game::run() step(&dtime); processClientEvents(&cam_view_target); updateDebugState(); - updateCamera(draw_times.busy_time, dtime); + updateCamera(dtime); updateSound(dtime); processPlayerInteraction(dtime, m_game_ui->m_flags.show_hud, m_game_ui->m_flags.show_basic_debug); @@ -1495,15 +1502,15 @@ bool Game::connectToServer(const GameStartData &start_data, try { input->clear(); - FpsControl fps_control = { 0 }; + FpsControl fps_control; f32 dtime; f32 wait_time = 0; // in seconds - fps_control.last_time = m_rendering_engine->get_timer_time(); + fps_control.reset(); while (m_rendering_engine->run()) { - limitFps(&fps_control, &dtime); + fps_control.limit(device, &dtime); // Update client and server client->step(dtime); @@ -1570,14 +1577,14 @@ bool Game::getServerContent(bool *aborted) { input->clear(); - FpsControl fps_control = { 0 }; + FpsControl fps_control; f32 dtime; // in seconds - fps_control.last_time = m_rendering_engine->get_timer_time(); + fps_control.reset(); while (m_rendering_engine->run()) { - limitFps(&fps_control, &dtime); + fps_control.limit(device, &dtime); // Update client and server client->step(dtime); @@ -1774,10 +1781,10 @@ void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, } // Update update graphs - g_profiler->graphAdd("Time non-rendering [ms]", + g_profiler->graphAdd("Time non-rendering [us]", draw_times.busy_time - stats.drawtime); - g_profiler->graphAdd("Sleep [ms]", draw_times.sleep_time); + g_profiler->graphAdd("Sleep [us]", draw_times.sleep_time); g_profiler->graphAdd("FPS", 1.0f / dtime); } @@ -1810,9 +1817,9 @@ void Game::updateStats(RunStats *stats, const FpsControl &draw_times, /* Busytime average and jitter calculation */ jp = &stats->busy_time_jitter; - jp->avg = jp->avg + draw_times.busy_time * 0.02; + jp->avg = jp->avg + draw_times.getBusyMs() * 0.02; - jitter = draw_times.busy_time - jp->avg; + jitter = draw_times.getBusyMs() - jp->avg; if (jitter > jp->max) jp->max = jitter; @@ -2932,7 +2939,7 @@ void Game::updateChat(f32 dtime) m_game_ui->updateChatSize(); } -void Game::updateCamera(u32 busy_time, f32 dtime) +void Game::updateCamera(f32 dtime) { LocalPlayer *player = client->getEnv().getLocalPlayer(); @@ -2971,7 +2978,7 @@ void Game::updateCamera(u32 busy_time, f32 dtime) float tool_reload_ratio = runData.time_from_last_punch / full_punch_interval; tool_reload_ratio = MYMIN(tool_reload_ratio, 1.0); - camera->update(player, dtime, busy_time / 1000.0f, tool_reload_ratio); + camera->update(player, dtime, tool_reload_ratio); camera->step(dtime); v3f camera_position = camera->getPosition(); @@ -3847,6 +3854,24 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, ); } + /* + Damage camera tilt + */ + if (player->hurt_tilt_timer > 0.0f) { + player->hurt_tilt_timer -= dtime * 6.0f; + + if (player->hurt_tilt_timer < 0.0f) + player->hurt_tilt_strength = 0.0f; + } + + /* + Update minimap pos and rotation + */ + if (mapper && m_game_ui->m_flags.show_hud) { + mapper->setPos(floatToInt(player->getPosition(), BS)); + mapper->setAngle(player->getYaw()); + } + /* Get chat messages from client */ @@ -3920,11 +3945,11 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, } while (false); /* - Drawing begins + ==================== Drawing begins ==================== */ - const video::SColor &skycolor = sky->getSkyColor(); + const video::SColor skycolor = sky->getSkyColor(); - TimeTaker tt_draw("Draw scene"); + TimeTaker tt_draw("Draw scene", nullptr, PRECISION_MICRO); driver->beginScene(true, true, skycolor); bool draw_wield_tool = (m_game_ui->m_flags.show_hud && @@ -3963,25 +3988,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, } /* - Damage camera tilt - */ - if (player->hurt_tilt_timer > 0.0f) { - player->hurt_tilt_timer -= dtime * 6.0f; - - if (player->hurt_tilt_timer < 0.0f) - player->hurt_tilt_strength = 0.0f; - } - - /* - Update minimap pos and rotation - */ - if (mapper && m_game_ui->m_flags.show_hud) { - mapper->setPos(floatToInt(player->getPosition(), BS)); - mapper->setAngle(player->getYaw()); - } - - /* - End scene + ==================== End scene ==================== */ if (++m_reset_HW_buffer_counter > 500) { /* @@ -4004,11 +4011,12 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, driver->removeAllHardwareBuffers(); m_reset_HW_buffer_counter = 0; } + driver->endScene(); stats->drawtime = tt_draw.stop(true); - g_profiler->avg("Game::updateFrame(): draw scene [ms]", stats->drawtime); - g_profiler->graphAdd("Update frame [ms]", tt_update.stop(true)); + g_profiler->graphAdd("Draw scene [us]", stats->drawtime); + g_profiler->avg("Game::updateFrame(): update frame [ms]", tt_update.stop(true)); } /* Log times and stuff for visualization */ @@ -4052,47 +4060,46 @@ void Game::updateShadows() Misc ****************************************************************************/ -/* On some computers framerate doesn't seem to be automatically limited - */ -inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime) +void FpsControl::reset() { - // not using getRealTime is necessary for wine - device->getTimer()->tick(); // Maker sure device time is up-to-date - u32 time = device->getTimer()->getTime(); - u32 last_time = fps_timings->last_time; - - if (time > last_time) // Make sure time hasn't overflowed - fps_timings->busy_time = time - last_time; - else - fps_timings->busy_time = 0; + last_time = porting::getTimeUs(); +} - u32 frametime_min = 1000 / ( +/* + * On some computers framerate doesn't seem to be automatically limited + */ +void FpsControl::limit(IrrlichtDevice *device, f32 *dtime) +{ + const u64 frametime_min = 1000000.0f / ( device->isWindowFocused() && !g_menumgr.pausesGame() ? g_settings->getFloat("fps_max") : g_settings->getFloat("fps_max_unfocused")); - if (fps_timings->busy_time < frametime_min) { - fps_timings->sleep_time = frametime_min - fps_timings->busy_time; - device->sleep(fps_timings->sleep_time); + u64 time = porting::getTimeUs(); + + if (time > last_time) // Make sure time hasn't overflowed + busy_time = time - last_time; + else + busy_time = 0; + + if (busy_time < frametime_min) { + sleep_time = frametime_min - busy_time; + if (sleep_time > 1000) + sleep_ms(sleep_time / 1000); } else { - fps_timings->sleep_time = 0; + sleep_time = 0; } - /* Get the new value of the device timer. Note that device->sleep() may - * not sleep for the entire requested time as sleep may be interrupted and - * therefore it is arguably more accurate to get the new time from the - * device rather than calculating it by adding sleep_time to time. - */ - - device->getTimer()->tick(); // Update device timer - time = device->getTimer()->getTime(); + // Read the timer again to accurately determine how long we actually slept, + // rather than calculating it by adding sleep_time to time. + time = porting::getTimeUs(); - if (time > last_time) // Make sure last_time hasn't overflowed - *dtime = (time - last_time) / 1000.0; + if (time > last_time) // Make sure last_time hasn't overflowed + *dtime = (time - last_time) / 1000000.0f; else *dtime = 0; - fps_timings->last_time = time; + last_time = time; } void Game::showOverlayMessage(const char *msg, float dtime, int percent, bool draw_clouds) diff --git a/src/client/game.h b/src/client/game.h index fbbf106db..d87e747c5 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -33,7 +33,7 @@ struct Jitter { }; struct RunStats { - u32 drawtime; + u64 drawtime; // (us) Jitter dtime_jitter, busy_time_jitter; }; diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index ecb8e0ec4..bae5241b1 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -104,16 +104,16 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ // Minimal debug text must only contain info that can't give a gameplay advantage if (m_flags.show_minimal_debug) { - static float drawtime_avg = 0; - drawtime_avg = drawtime_avg * 0.95 + stats.drawtime * 0.05; - u16 fps = 1.0 / stats.dtime_jitter.avg; + const u16 fps = 1.0 / stats.dtime_jitter.avg; + m_drawtime_avg *= 0.95f; + m_drawtime_avg += 0.05f * (stats.drawtime / 1000); std::ostringstream os(std::ios_base::binary); os << std::fixed << PROJECT_NAME_C " " << g_version_hash << " | FPS: " << fps << std::setprecision(0) - << " | drawtime: " << drawtime_avg << "ms" + << " | drawtime: " << m_drawtime_avg << "ms" << std::setprecision(1) << " | dtime jitter: " << (stats.dtime_jitter.max_fraction * 100.0) << "%" diff --git a/src/client/gameui.h b/src/client/gameui.h index 3f31f1b57..cc9377bdc 100644 --- a/src/client/gameui.h +++ b/src/client/gameui.h @@ -110,6 +110,8 @@ public: private: Flags m_flags; + float m_drawtime_avg = 0; + gui::IGUIStaticText *m_guitext = nullptr; // First line of debug text gui::IGUIStaticText *m_guitext2 = nullptr; // Second line of debug text 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(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(textx, y - texth, textx2, y), meta.color); + font->draw(utf8_to_wide(id).c_str(), core::rect(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); -- cgit v1.2.3 From 633e23bd6523d4ff14329e8429c2eaf795e6e128 Mon Sep 17 00:00:00 2001 From: DS Date: Tue, 22 Feb 2022 19:17:53 +0100 Subject: FormspecMenu: make drawing of backgrounds less hacky (#9517) --- games/devtest/mods/testformspec/formspec.lua | 29 +++++++++++++++++++++++++- src/gui/guiBackgroundImage.cpp | 2 +- src/gui/guiFormSpecMenu.cpp | 31 +++++++++++----------------- src/gui/guiFormSpecMenu.h | 3 ++- 4 files changed, 43 insertions(+), 22 deletions(-) (limited to 'src/gui') diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index c0db695b7..9f867631f 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -430,6 +430,33 @@ mouse control = true] checkbox[0.5,5.5.5;snd_chk;Sound;] tabheader[0.5,7;8,0.65;snd_tab;Soundtab1,Soundtab2,Soundtab3;1;false;false] ]], + + -- Background + [[ + formspec_version[3] + size[12,13] + box[0,0;12,13;#f0f1] + background[0,0;0,0;testformspec_bg.png;true] + box[3.9,2.9;6.2,4.2;#d00f] + scroll_container[4,3;6,4;scrbar;vertical] + background9[1,0.5;0,0;testformspec_bg_9slice.png;true;4,6] + label[0,0.2;Backgrounds are not be applied to scroll containers,] + label[0,0.5;but to the whole form.] + scroll_container_end[] + scrollbar[3.5,3;0.3,4;vertical;scrbar;0] + container[2,11] + box[-0.1,0.5;3.2,1;#fff5] + background[0,0;2,3;testformspec_bg.png;false] + background9[1,0;2,3;testformspec_bg_9slice.png;false;4,6] + container_end[] + ]], + + -- Unsized + [[ + formspec_version[3] + background9[0,0;0,0;testformspec_bg_9slice.png;true;4,6] + background[1,1;0,0;testformspec_bg.png;true] + ]], } local page_id = 2 @@ -439,7 +466,7 @@ local function show_test_formspec(pname) page = page() end - local fs = page .. "tabheader[0,0;8,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Invs,Window,Anim,Model,ScrollC,Sound;" .. page_id .. ";false;false]" + local fs = page .. "tabheader[0,0;11,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Invs,Window,Anim,Model,ScrollC,Sound,Background,Unsized;" .. page_id .. ";false;false]" minetest.show_formspec(pname, "testformspec:formspec", fs) end diff --git a/src/gui/guiBackgroundImage.cpp b/src/gui/guiBackgroundImage.cpp index 21c1e88cf..85e870771 100644 --- a/src/gui/guiBackgroundImage.cpp +++ b/src/gui/guiBackgroundImage.cpp @@ -44,7 +44,7 @@ void GUIBackgroundImage::draw() core::rect rect = AbsoluteRect; if (m_autoclip) - rect.LowerRightCorner += Parent->getAbsolutePosition().getSize(); + rect.LowerRightCorner += Parent->getAbsoluteClippingRect().getSize(); video::IVideoDriver *driver = Environment->getVideoDriver(); diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 85bd04900..f3570ccaf 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -137,8 +137,6 @@ GUIFormSpecMenu::~GUIFormSpecMenu() checkbox_it.second->drop(); for (auto &scrollbar_it : m_scrollbars) scrollbar_it.second->drop(); - for (auto &background_it : m_backgrounds) - background_it->drop(); for (auto &tooltip_rect_it : m_tooltip_rects) tooltip_rect_it.first->drop(); for (auto &clickthrough_it : m_clickthrough_elements) @@ -1124,17 +1122,15 @@ void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &eleme rect = core::rect(-pos, pos); } - GUIBackgroundImage *e = new GUIBackgroundImage(Environment, this, spec.fid, - rect, name, middle, m_tsrc, clip); + GUIBackgroundImage *e = new GUIBackgroundImage(Environment, data->background_parent.get(), + spec.fid, rect, name, middle, m_tsrc, clip); FATAL_ERROR_IF(!e, "Failed to create background formspec element"); e->setNotClipped(true); - e->setVisible(false); // the element is drawn manually before all others - - m_backgrounds.push_back(e); m_fields.push_back(spec); + e->drop(); } void GUIFormSpecMenu::parseTableOptions(parserData* data, const std::string &element) @@ -3059,8 +3055,6 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) checkbox_it.second->drop(); for (auto &scrollbar_it : m_scrollbars) scrollbar_it.second->drop(); - for (auto &background_it : m_backgrounds) - background_it->drop(); for (auto &tooltip_rect_it : m_tooltip_rects) tooltip_rect_it.first->drop(); for (auto &clickthrough_it : m_clickthrough_elements) @@ -3082,7 +3076,6 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) mydata.current_parent = this; m_inventorylists.clear(); - m_backgrounds.clear(); m_tables.clear(); m_checkboxes.clear(); m_scrollbars.clear(); @@ -3336,6 +3329,15 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) gui::IGUIFont *old_font = skin->getFont(); skin->setFont(m_font); + // Add a new element that will hold all the background elements as its children. + // Because it is the first added element, all backgrounds will be behind all + // the other elements. + // (We use an arbitrarily big rect. The actual size is determined later by + // clipping to `this`.) + core::rect background_parent_rect(0, 0, 100000, 100000); + mydata.background_parent.reset(new gui::IGUIElement(EGUIET_ELEMENT, Environment, + this, -1, background_parent_rect)); + pos_offset = v2f32(); // used for formspec versions < 3 @@ -3591,15 +3593,6 @@ void GUIFormSpecMenu::drawMenu() } } - /* - Draw backgrounds - */ - for (gui::IGUIElement *e : m_backgrounds) { - e->setVisible(true); - e->draw(); - e->setVisible(false); - } - // Some elements are only visible while being drawn for (gui::IGUIElement *e : m_clickthrough_elements) e->setVisible(true); diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 0b4d3879d..3fedb3b78 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "irrlichttypes_extrabloated.h" +#include "irr_ptr.h" #include "inventorymanager.h" #include "modalMenu.h" #include "guiInventoryList.h" @@ -313,7 +314,6 @@ protected: std::vector m_inventorylists; std::vector m_inventory_rings; - std::vector m_backgrounds; std::unordered_map field_close_on_enter; std::unordered_map m_dropdown_index_event; std::vector m_fields; @@ -375,6 +375,7 @@ private: GUITable::TableOptions table_options; GUITable::TableColumns table_columns; gui::IGUIElement *current_parent = nullptr; + irr_ptr background_parent; GUIInventoryList::Options inventorylist_options; -- cgit v1.2.3