GameUI refactor (part 2/X): Move Game::guitext to GameUI + enhancements on StaticText

Other enhancements:
* C++ friendlyness for addStaticText() -> move to static StaticText::add()
This commit is contained in:
Loic Blot 2018-01-03 17:28:57 +01:00 committed by Loïc Blot
parent 0ebaed430a
commit 3a772e7ed6
8 changed files with 159 additions and 134 deletions

@ -19,7 +19,55 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
#include "gameui.h" #include "gameui.h"
#include "settings.h" #include <irrlicht_changes/static_text.h>
#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<s32>(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<s32>(5, 5, screensize.X,
5 + g_fontengine->getTextHeight()));
}
}
void GameUI::initFlags() void GameUI::initFlags()
{ {
@ -29,7 +77,7 @@ void GameUI::initFlags()
m_flags.show_debug = g_settings->getBool("show_debug"); 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; m_flags.show_minimap = show;
} }

@ -20,9 +20,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once #pragma once
#include "IGUIEnvironment.h" #include <IGUIEnvironment.h>
#include "game.h"
using namespace irr; using namespace irr;
class Client;
struct MapDrawControl;
class GameUI class GameUI
{ {
@ -30,6 +33,9 @@ class GameUI
friend class Game; friend class Game;
public: public:
GameUI() = default;
~GameUI() = default;
// Flags that can, or may, change during main game loop // Flags that can, or may, change during main game loop
struct Flags struct Flags
{ {
@ -42,16 +48,19 @@ public:
bool disable_camera_update; bool disable_camera_update;
}; };
void init();
void update(const RunStats &stats, Client *client, const MapDrawControl *draw_control);
void initFlags(); void initFlags();
const Flags &getFlags() const { return m_flags; } const Flags &getFlags() const { return m_flags; }
void showMinimap(const bool show); void showMinimap(bool show);
private: private:
Flags m_flags; Flags m_flags;
gui::IGUIStaticText *m_guitext; // First line of debug text
// @TODO future move // @TODO future move
// gui::IGUIStaticText *m_guitext; // First line of debug text
// gui::IGUIStaticText *m_guitext2; // Second 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_info; // At the middle of the screen
// gui::IGUIStaticText *m_guitext_status; // gui::IGUIStaticText *m_guitext_status;

@ -1174,16 +1174,6 @@ struct GameRunData {
float time_of_day_smooth; 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; class Game;
struct ClientEventHandler struct ClientEventHandler
@ -1452,7 +1442,6 @@ private:
/* GUI stuff /* GUI stuff
*/ */
gui::IGUIStaticText *guitext; // First line of debug text
gui::IGUIStaticText *guitext2; // Second 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_info; // At the middle of the screen
gui::IGUIStaticText *guitext_status; gui::IGUIStaticText *guitext_status;
@ -1998,34 +1987,30 @@ bool Game::createClient(const std::string &playername,
bool Game::initGui() bool Game::initGui()
{ {
// First line of debug text m_game_ui->init();
guitext = addStaticText(guienv,
utf8_to_wide(PROJECT_NAME_C).c_str(),
core::rect<s32>(0, 0, 0, 0),
false, false, guiroot);
// Second line of debug text // Second line of debug text
guitext2 = addStaticText(guienv, guitext2 = gui::StaticText::add(guienv,
L"", L"",
core::rect<s32>(0, 0, 0, 0), core::rect<s32>(0, 0, 0, 0),
false, false, guiroot); false, false, guiroot);
// At the middle of the screen // At the middle of the screen
// Object infos are shown in this // Object infos are shown in this
guitext_info = addStaticText(guienv, guitext_info = gui::StaticText::add(guienv,
L"", L"",
core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) + v2s32(100, 200), core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) + v2s32(100, 200),
false, true, guiroot); false, true, guiroot);
// Status text (displays info when showing and hiding GUI stuff, etc.) // Status text (displays info when showing and hiding GUI stuff, etc.)
guitext_status = addStaticText(guienv, guitext_status = gui::StaticText::add(guienv,
L"<Status>", L"<Status>",
core::rect<s32>(0, 0, 0, 0), core::rect<s32>(0, 0, 0, 0),
false, false, guiroot); false, false, guiroot);
guitext_status->setVisible(false); guitext_status->setVisible(false);
// Chat text // Chat text
guitext_chat = addStaticText( guitext_chat = gui::StaticText::add(
guienv, guienv,
L"", L"",
core::rect<s32>(0, 0, 0, 0), core::rect<s32>(0, 0, 0, 0),
@ -2048,7 +2033,7 @@ bool Game::initGui()
} }
// Profiler text (size is updated when text is updated) // Profiler text (size is updated when text is updated)
guitext_profiler = addStaticText(guienv, guitext_profiler = gui::StaticText::add(guienv,
L"<Profiler>", L"<Profiler>",
core::rect<s32>(0, 0, 0, 0), core::rect<s32>(0, 0, 0, 0),
false, false, guiroot); false, false, guiroot);
@ -4410,38 +4395,7 @@ void Game::updateGui(const RunStats &stats, f32 dtime, const CameraOrientation &
LocalPlayer *player = client->getEnv().getLocalPlayer(); LocalPlayer *player = client->getEnv().getLocalPlayer();
v3f player_position = player->getPosition(); v3f player_position = player->getPosition();
if (m_game_ui->m_flags.show_debug) { m_game_ui->update(stats, client, draw_control);
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<s32> rect(
5, 5,
screensize.X, 5 + g_fontengine->getTextHeight()
);
guitext->setRelativePosition(rect);
}
if (m_game_ui->m_flags.show_debug) { if (m_game_ui->m_flags.show_debug) {
std::ostringstream os(std::ios_base::binary); std::ostringstream os(std::ios_base::binary);

@ -26,6 +26,16 @@ class InputHandler;
class ChatBackend; /* to avoid having to include chat.h */ class ChatBackend; /* to avoid having to include chat.h */
struct SubgameSpec; 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, void the_game(bool *kill,
bool random_input, bool random_input,
InputHandler *input, InputHandler *input,

@ -149,9 +149,8 @@ GUIEngine::GUIEngine(JoystickController *joystick,
g_fontengine->getTextHeight()); g_fontengine->getTextHeight());
rect += v2s32(4, 0); rect += v2s32(4, 0);
m_irr_toplefttext = m_irr_toplefttext = gui::StaticText::add(RenderingEngine::get_gui_env(),
addStaticText(RenderingEngine::get_gui_env(), m_toplefttext, m_toplefttext, rect, false, true, 0, -1);
rect, false, true, 0, -1);
//create formspecsource //create formspecsource
m_formspecgui = new FormspecFormSource(""); m_formspecgui = new FormspecFormSource("");
@ -560,9 +559,8 @@ void GUIEngine::updateTopLeftTextSize()
rect += v2s32(4, 0); rect += v2s32(4, 0);
m_irr_toplefttext->remove(); m_irr_toplefttext->remove();
m_irr_toplefttext = m_irr_toplefttext = gui::StaticText::add(RenderingEngine::get_gui_env(),
addStaticText(RenderingEngine::get_gui_env(), m_toplefttext, m_toplefttext, rect, false, true, 0, -1);
rect, false, true, 0, -1);
} }
/******************************************************************************/ /******************************************************************************/

@ -950,12 +950,12 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
Environment->setFocus(e); Environment->setFocus(e);
} }
if (label.length() >= 1) if (label.length() >= 1) {
{
int font_height = g_fontengine->getTextHeight(); int font_height = g_fontengine->getTextHeight();
rect.UpperLeftCorner.Y -= font_height; rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = 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'*'); e->setPasswordBox(true,L'*');
@ -1017,7 +1017,8 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
if (name.empty()) { if (name.empty()) {
// spec field id to 0, this stops submit searching for a value that isn't there // 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 { } else {
spec.send = true; spec.send = true;
gui::IGUIElement *e; gui::IGUIElement *e;
@ -1050,7 +1051,8 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
int font_height = g_fontengine->getTextHeight(); int font_height = g_fontengine->getTextHeight();
rect.UpperLeftCorner.Y -= font_height; rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = 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<std::string>&
int font_height = g_fontengine->getTextHeight(); int font_height = g_fontengine->getTextHeight();
rect.UpperLeftCorner.Y -= font_height; rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = 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) { if (parts.size() >= 6) {
@ -1237,11 +1240,9 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
L"", L"",
258+m_fields.size() 258+m_fields.size()
); );
gui::IGUIStaticText *e = gui::IGUIStaticText *e = gui::StaticText::add(Environment,
addStaticText(Environment, spec.flabel.c_str(), spec.flabel.c_str(), rect, false, false, this, spec.fid);
rect, false, false, this, spec.fid); e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER);
e->setTextAlignment(gui::EGUIA_UPPERLEFT,
gui::EGUIA_CENTER);
m_fields.push_back(spec); m_fields.push_back(spec);
} }
@ -1291,8 +1292,8 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen
L"", L"",
258+m_fields.size() 258+m_fields.size()
); );
gui::IGUIStaticText *t = gui::IGUIStaticText *t = gui::StaticText::add(Environment, spec.flabel.c_str(),
addStaticText(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid); rect, false, false, this, spec.fid);
t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
m_fields.push_back(spec); m_fields.push_back(spec);
return; return;
@ -2024,7 +2025,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
{ {
assert(!m_tooltip_element); assert(!m_tooltip_element);
// Note: parent != this so that the tooltip isn't clipped by the menu rectangle // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
m_tooltip_element = addStaticText(Environment, L"",core::rect<s32>(0,0,110,18)); m_tooltip_element = gui::StaticText::add(Environment, L"",
core::rect<s32>(0,0,110,18));
m_tooltip_element->enableOverrideColor(true); m_tooltip_element->enableOverrideColor(true);
m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor); m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
m_tooltip_element->setDrawBackground(true); m_tooltip_element->setDrawBackground(true);

@ -42,6 +42,59 @@ namespace gui
//! destructor //! destructor
virtual ~StaticText(); 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 //! draws the element and its children
virtual void draw(); virtual void draw();
@ -171,46 +224,6 @@ namespace gui
} // end namespace irr } // 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) inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text)
{ {
// dynamic_cast not possible due to some distributions shipped // dynamic_cast not possible due to some distributions shipped
@ -245,18 +258,6 @@ inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedS
#endif #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) inline void setStaticText(irr::gui::IGUIStaticText *static_text, const wchar_t *text)
{ {
setStaticText(static_text, EnrichedString(text)); setStaticText(static_text, EnrichedString(text));

@ -47,11 +47,14 @@ void TestGameUI::testInit()
gui.initFlags(); gui.initFlags();
UASSERT(gui.getFlags().show_chat) UASSERT(gui.getFlags().show_chat)
UASSERT(gui.getFlags().show_hud) UASSERT(gui.getFlags().show_hud)
// @TODO verify if we can create non UI nulldevice to test this function
gui.init();
} }
void TestGameUI::testFlagSetters() void TestGameUI::testFlagSetters()
{ {
GameUI gui; GameUI gui{};
gui.showMinimap(true); gui.showMinimap(true);
UASSERT(gui.getFlags().show_minimap); UASSERT(gui.getFlags().show_minimap);