From 3a772e7ed6c02f91de57320b1694c7d11e1c7618 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Wed, 3 Jan 2018 17:28:57 +0100 Subject: [PATCH] GameUI refactor (part 2/X): Move Game::guitext to GameUI + enhancements on StaticText Other enhancements: * C++ friendlyness for addStaticText() -> move to static StaticText::add() --- src/client/gameui.cpp | 52 +++++++++++++- src/client/gameui.h | 15 ++++- src/game.cpp | 60 ++--------------- src/game.h | 10 +++ src/gui/guiEngine.cpp | 10 ++- src/gui/guiFormSpecMenu.cpp | 36 +++++----- src/irrlicht_changes/static_text.h | 105 +++++++++++++++-------------- src/unittest/test_gameui.cpp | 5 +- 8 files changed, 159 insertions(+), 134 deletions(-) diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index 7307daf75..53d9f4a9c 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -19,7 +19,55 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "gameui.h" -#include "settings.h" +#include +#include "gui/mainmenumanager.h" +#include "client.h" +#include "fontengine.h" +#include "clientmap.h" +#include "version.h" +#include "renderingengine.h" + +void GameUI::init() +{ + // First line of debug text + m_guitext = gui::StaticText::add(guienv, utf8_to_wide(PROJECT_NAME_C).c_str(), + core::rect(0, 0, 0, 0), false, false, guiroot); +} + +void GameUI::update(const RunStats &stats, Client *client, + const MapDrawControl *draw_control) +{ + if (m_flags.show_debug) { + static float drawtime_avg = 0; + drawtime_avg = drawtime_avg * 0.95 + stats.drawtime * 0.05; + u16 fps = 1.0 / stats.dtime_jitter.avg; + + 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" + << std::setprecision(1) + << ", dtime jitter: " + << (stats.dtime_jitter.max_fraction * 100.0) << "%" + << std::setprecision(1) + << ", view range: " + << (draw_control->range_all ? "All" : itos(draw_control->wanted_range)) + << std::setprecision(3) + << ", RTT: " << client->getRTT() << "s"; + setStaticText(m_guitext, utf8_to_wide(os.str()).c_str()); + m_guitext->setVisible(true); + } else { + m_guitext->setVisible(false); + } + + if (m_guitext->isVisible()) { + v2u32 screensize = RenderingEngine::get_instance()->getWindowSize(); + m_guitext->setRelativePosition(core::rect(5, 5, screensize.X, + 5 + g_fontengine->getTextHeight())); + } +} void GameUI::initFlags() { @@ -29,7 +77,7 @@ void GameUI::initFlags() m_flags.show_debug = g_settings->getBool("show_debug"); } -void GameUI::showMinimap(const bool show) +void GameUI::showMinimap(bool show) { m_flags.show_minimap = show; } diff --git a/src/client/gameui.h b/src/client/gameui.h index bd9c0f626..593fd4ff3 100644 --- a/src/client/gameui.h +++ b/src/client/gameui.h @@ -20,9 +20,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "IGUIEnvironment.h" +#include +#include "game.h" using namespace irr; +class Client; +struct MapDrawControl; class GameUI { @@ -30,6 +33,9 @@ class GameUI friend class Game; public: + GameUI() = default; + ~GameUI() = default; + // Flags that can, or may, change during main game loop struct Flags { @@ -42,16 +48,19 @@ public: bool disable_camera_update; }; + void init(); + void update(const RunStats &stats, Client *client, const MapDrawControl *draw_control); + void initFlags(); const Flags &getFlags() const { return m_flags; } - void showMinimap(const bool show); + void showMinimap(bool show); private: Flags m_flags; + gui::IGUIStaticText *m_guitext; // First line of debug text // @TODO future move - // gui::IGUIStaticText *m_guitext; // First line of debug text // gui::IGUIStaticText *m_guitext2; // Second line of debug text // gui::IGUIStaticText *m_guitext_info; // At the middle of the screen // gui::IGUIStaticText *m_guitext_status; diff --git a/src/game.cpp b/src/game.cpp index f1ee9f8e2..d31daf32b 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1174,16 +1174,6 @@ struct GameRunData { float time_of_day_smooth; }; -struct Jitter { - f32 max, min, avg, counter, max_sample, min_sample, max_fraction; -}; - -struct RunStats { - u32 drawtime; - - Jitter dtime_jitter, busy_time_jitter; -}; - class Game; struct ClientEventHandler @@ -1452,7 +1442,6 @@ private: /* GUI stuff */ - gui::IGUIStaticText *guitext; // First line of debug text gui::IGUIStaticText *guitext2; // Second line of debug text gui::IGUIStaticText *guitext_info; // At the middle of the screen gui::IGUIStaticText *guitext_status; @@ -1998,34 +1987,30 @@ bool Game::createClient(const std::string &playername, bool Game::initGui() { - // First line of debug text - guitext = addStaticText(guienv, - utf8_to_wide(PROJECT_NAME_C).c_str(), - core::rect(0, 0, 0, 0), - false, false, guiroot); + m_game_ui->init(); // Second line of debug text - guitext2 = addStaticText(guienv, + guitext2 = gui::StaticText::add(guienv, L"", core::rect(0, 0, 0, 0), false, false, guiroot); // At the middle of the screen // Object infos are shown in this - guitext_info = addStaticText(guienv, + guitext_info = gui::StaticText::add(guienv, L"", core::rect(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) + v2s32(100, 200), false, true, guiroot); // Status text (displays info when showing and hiding GUI stuff, etc.) - guitext_status = addStaticText(guienv, + guitext_status = gui::StaticText::add(guienv, L"", core::rect(0, 0, 0, 0), false, false, guiroot); guitext_status->setVisible(false); // Chat text - guitext_chat = addStaticText( + guitext_chat = gui::StaticText::add( guienv, L"", core::rect(0, 0, 0, 0), @@ -2048,7 +2033,7 @@ bool Game::initGui() } // Profiler text (size is updated when text is updated) - guitext_profiler = addStaticText(guienv, + guitext_profiler = gui::StaticText::add(guienv, L"", core::rect(0, 0, 0, 0), false, false, guiroot); @@ -4410,38 +4395,7 @@ void Game::updateGui(const RunStats &stats, f32 dtime, const CameraOrientation & LocalPlayer *player = client->getEnv().getLocalPlayer(); v3f player_position = player->getPosition(); - if (m_game_ui->m_flags.show_debug) { - static float drawtime_avg = 0; - drawtime_avg = drawtime_avg * 0.95 + stats.drawtime * 0.05; - u16 fps = 1.0 / stats.dtime_jitter.avg; - - 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" - << std::setprecision(1) - << ", dtime jitter: " - << (stats.dtime_jitter.max_fraction * 100.0) << "%" - << std::setprecision(1) - << ", view range: " - << (draw_control->range_all ? "All" : itos(draw_control->wanted_range)) - << std::setprecision(3) - << ", RTT: " << client->getRTT() << "s"; - setStaticText(guitext, utf8_to_wide(os.str()).c_str()); - guitext->setVisible(true); - } else { - guitext->setVisible(false); - } - - if (guitext->isVisible()) { - core::rect rect( - 5, 5, - screensize.X, 5 + g_fontengine->getTextHeight() - ); - guitext->setRelativePosition(rect); - } + m_game_ui->update(stats, client, draw_control); if (m_game_ui->m_flags.show_debug) { std::ostringstream os(std::ios_base::binary); diff --git a/src/game.h b/src/game.h index 223278006..6101c4f7a 100644 --- a/src/game.h +++ b/src/game.h @@ -26,6 +26,16 @@ class InputHandler; class ChatBackend; /* to avoid having to include chat.h */ struct SubgameSpec; +struct Jitter { + f32 max, min, avg, counter, max_sample, min_sample, max_fraction; +}; + +struct RunStats { + u32 drawtime; + + Jitter dtime_jitter, busy_time_jitter; +}; + void the_game(bool *kill, bool random_input, InputHandler *input, diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index e9b4e54c1..3f9bcd888 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -149,9 +149,8 @@ GUIEngine::GUIEngine(JoystickController *joystick, g_fontengine->getTextHeight()); rect += v2s32(4, 0); - m_irr_toplefttext = - addStaticText(RenderingEngine::get_gui_env(), m_toplefttext, - rect, false, true, 0, -1); + m_irr_toplefttext = gui::StaticText::add(RenderingEngine::get_gui_env(), + m_toplefttext, rect, false, true, 0, -1); //create formspecsource m_formspecgui = new FormspecFormSource(""); @@ -560,9 +559,8 @@ void GUIEngine::updateTopLeftTextSize() rect += v2s32(4, 0); m_irr_toplefttext->remove(); - m_irr_toplefttext = - addStaticText(RenderingEngine::get_gui_env(), m_toplefttext, - rect, false, true, 0, -1); + m_irr_toplefttext = gui::StaticText::add(RenderingEngine::get_gui_env(), + m_toplefttext, rect, false, true, 0, -1); } /******************************************************************************/ diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index c83e6aa49..5926cca3a 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -950,12 +950,12 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element Environment->setFocus(e); } - if (label.length() >= 1) - { + if (label.length() >= 1) { int font_height = g_fontengine->getTextHeight(); rect.UpperLeftCorner.Y -= font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; - addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0); + gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true, + this, 0); } e->setPasswordBox(true,L'*'); @@ -1017,7 +1017,8 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data, if (name.empty()) { // spec field id to 0, this stops submit searching for a value that isn't there - addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid); + gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true, this, + spec.fid); } else { spec.send = true; gui::IGUIElement *e; @@ -1050,7 +1051,8 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data, int font_height = g_fontengine->getTextHeight(); rect.UpperLeftCorner.Y -= font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; - addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0); + gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true, + this, 0); } } @@ -1162,7 +1164,8 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector& int font_height = g_fontengine->getTextHeight(); rect.UpperLeftCorner.Y -= font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; - addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0); + gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true, + this, 0); } if (parts.size() >= 6) { @@ -1237,11 +1240,9 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) L"", 258+m_fields.size() ); - gui::IGUIStaticText *e = - addStaticText(Environment, spec.flabel.c_str(), - rect, false, false, this, spec.fid); - e->setTextAlignment(gui::EGUIA_UPPERLEFT, - gui::EGUIA_CENTER); + gui::IGUIStaticText *e = gui::StaticText::add(Environment, + spec.flabel.c_str(), rect, false, false, this, spec.fid); + e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); m_fields.push_back(spec); } @@ -1291,8 +1292,8 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen L"", 258+m_fields.size() ); - gui::IGUIStaticText *t = - addStaticText(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid); + gui::IGUIStaticText *t = gui::StaticText::add(Environment, spec.flabel.c_str(), + rect, false, false, this, spec.fid); t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); m_fields.push_back(spec); return; @@ -2024,7 +2025,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) { assert(!m_tooltip_element); // Note: parent != this so that the tooltip isn't clipped by the menu rectangle - m_tooltip_element = addStaticText(Environment, L"",core::rect(0,0,110,18)); + m_tooltip_element = gui::StaticText::add(Environment, L"", + core::rect(0,0,110,18)); m_tooltip_element->enableOverrideColor(true); m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor); m_tooltip_element->setDrawBackground(true); @@ -3669,14 +3671,14 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) m_invmgr->inventoryAction(a); } else if (craft_amount > 0) { assert(s.isValid()); - + // if there are no items selected or the selected item // belongs to craftresult list, proceed with crafting if (m_selected_item == NULL || !m_selected_item->isValid() || m_selected_item->listname == "craftresult") { - + m_selected_content_guess = ItemStack(); // Clear - + assert(inv_s); // Send IACTION_CRAFT diff --git a/src/irrlicht_changes/static_text.h b/src/irrlicht_changes/static_text.h index de7b47e76..6bb85bd6b 100644 --- a/src/irrlicht_changes/static_text.h +++ b/src/irrlicht_changes/static_text.h @@ -42,6 +42,59 @@ namespace gui //! destructor virtual ~StaticText(); + 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) + { + if (parent == NULL) { + // parent is NULL, so we must find one, or we need not to drop + // result, but then there will be a memory leak. + // + // What Irrlicht does is to use guienv as a parent, but the problem + // is that guienv is here only an IGUIEnvironment, while it is a + // CGUIEnvironment in Irrlicht, which inherits from both IGUIElement + // and IGUIEnvironment. + // + // A solution would be to dynamic_cast guienv to a + // IGUIElement*, but Irrlicht is shipped without rtti support + // in some distributions, causing the dymanic_cast to segfault. + // + // Thus, to find the parent, we create a dummy StaticText and ask + // for its parent, and then remove it. + irr::gui::IGUIStaticText *dummy_text = + guienv->addStaticText(L"", rectangle, border, wordWrap, + parent, id, fillBackground); + parent = dummy_text->getParent(); + dummy_text->remove(); + } + irr::gui::IGUIStaticText *result = new irr::gui::StaticText( + text, border, guienv, parent, + id, rectangle, fillBackground); + + result->setWordWrap(wordWrap); + result->drop(); + return result; + } + + static irr::gui::IGUIStaticText *add( + irr::gui::IGUIEnvironment *guienv, + const wchar_t *text, + const core::rect< s32 > &rectangle, + bool border = false, + bool wordWrap = true, + irr::gui::IGUIElement *parent = NULL, + s32 id = -1, + bool fillBackground = false) + { + return add(guienv, EnrichedString(text), rectangle, border, wordWrap, parent, id, fillBackground); + } + //! draws the element and its children virtual void draw(); @@ -171,46 +224,6 @@ namespace gui } // end namespace irr -inline irr::gui::IGUIStaticText *addStaticText( - 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) -{ - if (parent == NULL) { - // parent is NULL, so we must find one, or we need not to drop - // result, but then there will be a memory leak. - // - // What Irrlicht does is to use guienv as a parent, but the problem - // is that guienv is here only an IGUIEnvironment, while it is a - // CGUIEnvironment in Irrlicht, which inherits from both IGUIElement - // and IGUIEnvironment. - // - // A solution would be to dynamic_cast guienv to a - // IGUIElement*, but Irrlicht is shipped without rtti support - // in some distributions, causing the dymanic_cast to segfault. - // - // Thus, to find the parent, we create a dummy StaticText and ask - // for its parent, and then remove it. - irr::gui::IGUIStaticText *dummy_text = - guienv->addStaticText(L"", rectangle, border, wordWrap, - parent, id, fillBackground); - parent = dummy_text->getParent(); - dummy_text->remove(); - } - irr::gui::IGUIStaticText *result = new irr::gui::StaticText( - text, border, guienv, parent, - id, rectangle, fillBackground); - - result->setWordWrap(wordWrap); - result->drop(); - return result; -} - inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text) { // dynamic_cast not possible due to some distributions shipped @@ -245,18 +258,6 @@ inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedS #endif -inline irr::gui::IGUIStaticText *addStaticText( - irr::gui::IGUIEnvironment *guienv, - const wchar_t *text, - const core::rect< s32 > &rectangle, - bool border = false, - bool wordWrap = true, - irr::gui::IGUIElement *parent = NULL, - s32 id = -1, - bool fillBackground = false) { - return addStaticText(guienv, EnrichedString(text), rectangle, border, wordWrap, parent, id, fillBackground); -} - inline void setStaticText(irr::gui::IGUIStaticText *static_text, const wchar_t *text) { setStaticText(static_text, EnrichedString(text)); diff --git a/src/unittest/test_gameui.cpp b/src/unittest/test_gameui.cpp index 56f44ed87..3c6f2a5a4 100644 --- a/src/unittest/test_gameui.cpp +++ b/src/unittest/test_gameui.cpp @@ -47,11 +47,14 @@ void TestGameUI::testInit() gui.initFlags(); UASSERT(gui.getFlags().show_chat) UASSERT(gui.getFlags().show_hud) + + // @TODO verify if we can create non UI nulldevice to test this function + gui.init(); } void TestGameUI::testFlagSetters() { - GameUI gui; + GameUI gui{}; gui.showMinimap(true); UASSERT(gui.getFlags().show_minimap);