Index: ps/trunk/source/gui/GUItext.h =================================================================== --- ps/trunk/source/gui/GUItext.h (revision 22678) +++ ps/trunk/source/gui/GUItext.h (nonexistent) @@ -1,153 +0,0 @@ -/* Copyright (C) 2019 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with 0 A.D. If not, see . - */ - -#ifndef INCLUDED_GUITEXT -#define INCLUDED_GUITEXT - -#include - -#include "gui/CGUIColor.h" -#include "gui/CGUISprite.h" -#include "ps/CStrIntern.h" -#include "ps/Shapes.h" - -/** - * An SGUIText object is a parsed string, divided into - * text-rendering components. Each component, being a - * call to the Renderer. For instance, if you by tags - * change the color, then the GUI will have to make - * individual calls saying it want that color on the - * text. - * - * For instance: - * "Hello [b]there[/b] bunny!" - * - * That without word-wrapping would mean 3 components. - * i.e. 3 calls to CRenderer. One drawing "Hello", - * one drawing "there" in bold, and one drawing "bunny!". - */ -struct SGUIText -{ - /** - * A sprite call to the CRenderer - */ - struct SSpriteCall - { - // The CGUISpriteInstance makes this uncopyable to avoid invalidating its draw cache - NONCOPYABLE(SSpriteCall); - MOVABLE(SSpriteCall); - - SSpriteCall() : m_CellID(0) {} - - /** - * Size and position of sprite - */ - CRect m_Area; - - /** - * Sprite from global GUI sprite database. - */ - CGUISpriteInstance m_Sprite; - - int m_CellID; - - /** - * Tooltip text - */ - CStrW m_Tooltip; - - /** - * Tooltip style - */ - CStrW m_TooltipStyle; - }; - - /** - * A text call to the CRenderer - */ - struct STextCall - { - NONCOPYABLE(STextCall); - MOVABLE(STextCall); - - STextCall() : - m_UseCustomColor(false), - m_Bold(false), m_Italic(false), m_Underlined(false), - m_pSpriteCall(NULL) {} - - /** - * Position - */ - CPos m_Pos; - - /** - * Size - */ - CSize m_Size; - - /** - * The string that is suppose to be rendered. - */ - CStrW m_String; - - /** - * Use custom color? If true then m_Color is used, - * else the color inputted will be used. - */ - bool m_UseCustomColor; - - /** - * Color setup - */ - CGUIColor m_Color; - - /** - * Font name - */ - CStrIntern m_Font; - - /** - * Settings - */ - bool m_Bold, m_Italic, m_Underlined; - - /** - * *IF* an icon, then this is not NULL. - */ - std::list::pointer m_pSpriteCall; - }; - - /** - * List of TextCalls, for instance "Hello", "there!" - */ - std::vector m_TextCalls; - - /** - * List of sprites, or "icons" that should be rendered - * along with the text. - */ - std::list m_SpriteCalls; // list for consistent mem addresses - // so that we can point to elements. - - /** - * Width and height of the whole output, used when setting up - * scrollbars and such. - */ - CSize m_Size; -}; - -#endif // INCLUDED_GUITEXT Property changes on: ps/trunk/source/gui/GUItext.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/gui/CButton.cpp =================================================================== --- ps/trunk/source/gui/CButton.cpp (revision 22678) +++ ps/trunk/source/gui/CButton.cpp (revision 22679) @@ -1,119 +1,119 @@ /* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "CButton.h" #include "gui/CGUIColor.h" #include "lib/ogl.h" CButton::CButton(CGUI* pGUI) : IGUIObject(pGUI), IGUIButtonBehavior(pGUI), IGUITextOwner(pGUI) { AddSetting("buffer_zone"); AddSetting("caption"); AddSetting("cell_id"); AddSetting("font"); AddSetting("sound_disabled"); AddSetting("sound_enter"); AddSetting("sound_leave"); AddSetting("sound_pressed"); AddSetting("sound_released"); AddSetting("sprite"); AddSetting("sprite_over"); AddSetting("sprite_pressed"); AddSetting("sprite_disabled"); AddSetting("text_align"); AddSetting("text_valign"); AddSetting("textcolor"); AddSetting("textcolor_over"); AddSetting("textcolor_pressed"); AddSetting("textcolor_disabled"); AddSetting("tooltip"); AddSetting("tooltip_style"); - // Add text - AddText(new SGUIText()); + AddText(); } CButton::~CButton() { } void CButton::SetupText() { ENSURE(m_GeneratedTexts.size() == 1); CStrW font; if (GUI::GetSetting(this, "font", font) != PSRETURN_OK || font.empty()) // Use the default if none is specified // TODO Gee: (2004-08-14) Default should not be hard-coded, but be in styles! font = L"default"; CGUIString* caption = nullptr; GUI::GetSettingPointer(this, "caption", caption); float buffer_zone = 0.f; GUI::GetSetting(this, "buffer_zone", buffer_zone); - *m_GeneratedTexts[0] = GetGUI()->GenerateText(*caption, font, m_CachedActualSize.GetWidth(), buffer_zone, this); - CalculateTextPosition(m_CachedActualSize, m_TextPos, *m_GeneratedTexts[0]); + m_GeneratedTexts[0] = CGUIText(m_pGUI, *caption, font, m_CachedActualSize.GetWidth(), buffer_zone, this); + + CalculateTextPosition(m_CachedActualSize, m_TextPos, m_GeneratedTexts[0]); } void CButton::HandleMessage(SGUIMessage& Message) { // Important IGUIButtonBehavior::HandleMessage(Message); IGUITextOwner::HandleMessage(Message); } void CButton::Draw() { float bz = GetBufferedZ(); CGUISpriteInstance* sprite; CGUISpriteInstance* sprite_over; CGUISpriteInstance* sprite_pressed; CGUISpriteInstance* sprite_disabled; int cell_id; // Statically initialise some strings, so we don't have to do // lots of allocation every time this function is called static const CStr strSprite("sprite"); static const CStr strSpriteOver("sprite_over"); static const CStr strSpritePressed("sprite_pressed"); static const CStr strSpriteDisabled("sprite_disabled"); static const CStr strCellId("cell_id"); GUI::GetSettingPointer(this, strSprite, sprite); GUI::GetSettingPointer(this, strSpriteOver, sprite_over); GUI::GetSettingPointer(this, strSpritePressed, sprite_pressed); GUI::GetSettingPointer(this, strSpriteDisabled, sprite_disabled); GUI::GetSetting(this, strCellId, cell_id); DrawButton(m_CachedActualSize, bz, *sprite, *sprite_over, *sprite_pressed, *sprite_disabled, cell_id); CGUIColor color = ChooseColor(); DrawText(0, color, m_TextPos, bz+0.1f); } Index: ps/trunk/source/gui/CChart.cpp =================================================================== --- ps/trunk/source/gui/CChart.cpp (revision 22678) +++ ps/trunk/source/gui/CChart.cpp (revision 22679) @@ -1,328 +1,324 @@ /* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "CChart.h" #include "gui/CGUIColor.h" #include "gui/CGUIString.h" #include "gui/GUIMatrix.h" #include "graphics/ShaderManager.h" #include "i18n/L10n.h" #include "lib/ogl.h" #include "ps/CLogger.h" #include "renderer/Renderer.h" #include "third_party/cppformat/format.h" #include CChart::CChart(CGUI* pGUI) : IGUIObject(pGUI), IGUITextOwner(pGUI) { AddSetting("axis_color"); AddSetting("axis_width"); AddSetting("buffer_zone"); AddSetting("font"); AddSetting("format_x"); AddSetting("format_y"); AddSetting("series_color"); AddSetting("series"); AddSetting("text_align"); GUI::GetSetting(this, "axis_width", m_AxisWidth); GUI::GetSetting(this, "format_x", m_FormatX); GUI::GetSetting(this, "format_y", m_FormatY); } CChart::~CChart() { } void CChart::HandleMessage(SGUIMessage& Message) { // TODO: implement zoom switch (Message.type) { case GUIM_SETTINGS_UPDATED: { GUI::GetSetting(this, "axis_width", m_AxisWidth); GUI::GetSetting(this, "format_x", m_FormatX); GUI::GetSetting(this, "format_y", m_FormatY); UpdateSeries(); break; } } } void CChart::DrawLine(const CShaderProgramPtr& shader, const CGUIColor& color, const std::vector& vertices) const { shader->Uniform(str_color, color); shader->VertexPointer(3, GL_FLOAT, 0, &vertices[0]); shader->AssertPointersBound(); glEnable(GL_LINE_SMOOTH); glLineWidth(1.1f); if (!g_Renderer.m_SkipSubmit) glDrawArrays(GL_LINE_STRIP, 0, vertices.size() / 3); glLineWidth(1.0f); glDisable(GL_LINE_SMOOTH); } void CChart::DrawTriangleStrip(const CShaderProgramPtr& shader, const CGUIColor& color, const std::vector& vertices) const { shader->Uniform(str_color, color); shader->VertexPointer(3, GL_FLOAT, 0, &vertices[0]); shader->AssertPointersBound(); if (!g_Renderer.m_SkipSubmit) glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size() / 3); } void CChart::DrawAxes(const CShaderProgramPtr& shader) const { const float bz = GetBufferedZ(); CRect rect = GetChartRect(); std::vector vertices; vertices.reserve(30); #define ADD(x, y) vertices.push_back(x); vertices.push_back(y); vertices.push_back(bz + 0.5f); ADD(m_CachedActualSize.right, m_CachedActualSize.bottom); ADD(rect.right + m_AxisWidth, rect.bottom); ADD(m_CachedActualSize.left, m_CachedActualSize.bottom); ADD(rect.left, rect.bottom); ADD(m_CachedActualSize.left, m_CachedActualSize.top); ADD(rect.left, rect.top - m_AxisWidth); #undef ADD CGUIColor axis_color(0.5f, 0.5f, 0.5f, 1.f); GUI::GetSetting(this, "axis_color", axis_color); DrawTriangleStrip(shader, axis_color, vertices); } void CChart::Draw() { PROFILE3("render chart"); if (m_Series.empty()) return; const float bz = GetBufferedZ(); CRect rect = GetChartRect(); const float width = rect.GetWidth(); const float height = rect.GetHeight(); // Disable depth updates to prevent apparent z-fighting-related issues // with some drivers causing units to get drawn behind the texture. glDepthMask(0); // Setup the render state CMatrix3D transform = GetDefaultGuiMatrix(); CShaderDefines lineDefines; CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid, g_Renderer.GetSystemShaderDefines(), lineDefines); tech->BeginPass(); CShaderProgramPtr shader = tech->GetShader(); shader->Uniform(str_transform, transform); CVector2D scale(width / (m_RightTop.X - m_LeftBottom.X), height / (m_RightTop.Y - m_LeftBottom.Y)); for (const CChartData& data : m_Series) { if (data.m_Points.empty()) continue; std::vector vertices; for (const CVector2D& point : data.m_Points) { if (fabs(point.X) != std::numeric_limits::infinity() && fabs(point.Y) != std::numeric_limits::infinity()) { vertices.push_back(rect.left + (point.X - m_LeftBottom.X) * scale.X); vertices.push_back(rect.bottom - (point.Y - m_LeftBottom.Y) * scale.Y); vertices.push_back(bz + 0.5f); } else { DrawLine(shader, data.m_Color, vertices); vertices.clear(); } } if (!vertices.empty()) DrawLine(shader, data.m_Color, vertices); } if (m_AxisWidth > 0) DrawAxes(shader); tech->EndPass(); // Reset depth mask glDepthMask(1); for (size_t i = 0; i < m_TextPositions.size(); ++i) DrawText(i, CGUIColor(1.f, 1.f, 1.f, 1.f), m_TextPositions[i], bz + 0.5f); } CRect CChart::GetChartRect() const { return CRect( m_CachedActualSize.TopLeft() + CPos(m_AxisWidth, m_AxisWidth), m_CachedActualSize.BottomRight() - CPos(m_AxisWidth, m_AxisWidth) ); } void CChart::UpdateSeries() { CGUISeries* pSeries; GUI::GetSettingPointer(this, "series", pSeries); CGUIList* pSeriesColor; GUI::GetSettingPointer(this, "series_color", pSeriesColor); m_Series.clear(); m_Series.resize(pSeries->m_Series.size()); for (size_t i = 0; i < pSeries->m_Series.size(); ++i) { CChartData& data = m_Series[i]; if (i < pSeriesColor->m_Items.size() && !data.m_Color.ParseString(m_pGUI, pSeriesColor->m_Items[i].GetOriginalString().ToUTF8(), 0)) LOGWARNING("GUI: Error parsing 'series_color' (\"%s\")", utf8_from_wstring(pSeriesColor->m_Items[i].GetOriginalString())); data.m_Points = pSeries->m_Series[i]; } UpdateBounds(); SetupText(); } void CChart::SetupText() { - for (SGUIText* t : m_GeneratedTexts) - delete t; m_GeneratedTexts.clear(); m_TextPositions.clear(); if (m_Series.empty()) return; CStrW font; if (GUI::GetSetting(this, "font", font) != PSRETURN_OK || font.empty()) font = L"default"; float buffer_zone = 0.f; GUI::GetSetting(this, "buffer_zone", buffer_zone); // Add Y-axis GUI::GetSetting(this, "format_y", m_FormatY); const float height = GetChartRect().GetHeight(); // TODO: split values depend on the format; if (m_EqualY) { // We don't need to generate many items for equal values AddFormattedValue(m_FormatY, m_RightTop.Y, font, buffer_zone); m_TextPositions.emplace_back(GetChartRect().TopLeft()); } else for (int i = 0; i < 3; ++i) { AddFormattedValue(m_FormatY, m_RightTop.Y - (m_RightTop.Y - m_LeftBottom.Y) / 3.f * i, font, buffer_zone); m_TextPositions.emplace_back(GetChartRect().TopLeft() + CPos(0.f, height / 3.f * i)); } // Add X-axis GUI::GetSetting(this, "format_x", m_FormatX); const float width = GetChartRect().GetWidth(); if (m_EqualX) { CSize text_size = AddFormattedValue(m_FormatX, m_RightTop.X, font, buffer_zone); m_TextPositions.emplace_back(GetChartRect().BottomRight() - text_size); } else for (int i = 0; i < 3; ++i) { CSize text_size = AddFormattedValue(m_FormatX, m_RightTop.X - (m_RightTop.X - m_LeftBottom.X) / 3 * i, font, buffer_zone); m_TextPositions.emplace_back(GetChartRect().BottomRight() - text_size - CPos(width / 3 * i, 0.f)); } } CSize CChart::AddFormattedValue(const CStrW& format, const float value, const CStrW& font, const float buffer_zone) { // TODO: we need to catch cases with equal formatted values. CGUIString gui_str; if (format == L"DECIMAL2") { wchar_t buffer[64]; swprintf(buffer, 64, L"%.2f", value); gui_str.SetValue(buffer); } else if (format == L"INTEGER") { wchar_t buffer[64]; swprintf(buffer, 64, L"%d", std::lround(value)); gui_str.SetValue(buffer); } else if (format == L"DURATION_SHORT") { const int seconds = value; wchar_t buffer[64]; swprintf(buffer, 64, L"%d:%02d", seconds / 60, seconds % 60); gui_str.SetValue(buffer); } else if (format == L"PERCENTAGE") { wchar_t buffer[64]; swprintf(buffer, 64, L"%d%%", std::lround(value)); gui_str.SetValue(buffer); } else { LOGERROR("Unsupported chart format: " + format.EscapeToPrintableASCII()); return CSize(); } - SGUIText* text = new SGUIText(); - *text = GetGUI()->GenerateText(gui_str, font, 0, buffer_zone, this); - AddText(text); - return text->m_Size; + + return AddText(gui_str, font, 0, buffer_zone, this).GetSize(); } void CChart::UpdateBounds() { if (m_Series.empty() || m_Series[0].m_Points.empty()) { m_LeftBottom = m_RightTop = CVector2D(0.f, 0.f); return; } m_LeftBottom = m_RightTop = m_Series[0].m_Points[0]; for (const CChartData& data : m_Series) for (const CVector2D& point : data.m_Points) { if (fabs(point.X) != std::numeric_limits::infinity() && point.X < m_LeftBottom.X) m_LeftBottom.X = point.X; if (fabs(point.Y) != std::numeric_limits::infinity() && point.Y < m_LeftBottom.Y) m_LeftBottom.Y = point.Y; if (fabs(point.X) != std::numeric_limits::infinity() && point.X > m_RightTop.X) m_RightTop.X = point.X; if (fabs(point.Y) != std::numeric_limits::infinity() && point.Y > m_RightTop.Y) m_RightTop.Y = point.Y; } m_EqualY = m_RightTop.Y == m_LeftBottom.Y; if (m_EqualY) m_RightTop.Y += 1; m_EqualX = m_RightTop.X == m_LeftBottom.X; if (m_EqualX) m_RightTop.X += 1; } Index: ps/trunk/source/gui/CCheckBox.cpp =================================================================== --- ps/trunk/source/gui/CCheckBox.cpp (revision 22678) +++ ps/trunk/source/gui/CCheckBox.cpp (revision 22679) @@ -1,147 +1,147 @@ /* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "CCheckBox.h" #include "gui/CGUIColor.h" #include "graphics/FontMetrics.h" #include "ps/CLogger.h" #include "ps/CStrIntern.h" /** * TODO: Since there is no call to DrawText, the checkbox won't render any text. * Thus the font, caption, textcolor and other settings have no effect. */ CCheckBox::CCheckBox(CGUI* pGUI) : IGUIObject(pGUI), IGUITextOwner(pGUI), IGUIButtonBehavior(pGUI) { AddSetting("buffer_zone"); AddSetting("caption"); AddSetting("cell_id"); AddSetting("checked"); AddSetting("font"); AddSetting("sound_disabled"); AddSetting("sound_enter"); AddSetting("sound_leave"); AddSetting("sound_pressed"); AddSetting("sound_released"); AddSetting("sprite"); AddSetting("sprite_over"); AddSetting("sprite_pressed"); AddSetting("sprite_disabled"); AddSetting("sprite2"); AddSetting("sprite2_over"); AddSetting("sprite2_pressed"); AddSetting("sprite2_disabled"); AddSetting("square_side"); AddSetting("textcolor"); AddSetting("textcolor_over"); AddSetting("textcolor_pressed"); AddSetting("textcolor_disabled"); AddSetting("tooltip"); AddSetting("tooltip_style"); - AddText(new SGUIText()); + AddText(); } CCheckBox::~CCheckBox() { } void CCheckBox::SetupText() { ENSURE(m_GeneratedTexts.size() == 1); CStrW font; if (GUI::GetSetting(this, "font", font) != PSRETURN_OK || font.empty()) // Use the default if none is specified // TODO Gee: (2004-08-14) Default should not be hard-coded, but be in styles! font = L"default"; float square_side; GUI::GetSetting(this, "square_side", square_side); CGUIString* caption = nullptr; GUI::GetSettingPointer(this, "caption", caption); float buffer_zone = 0.f; GUI::GetSetting(this, "buffer_zone", buffer_zone); - *m_GeneratedTexts[0] = GetGUI()->GenerateText(*caption, font, m_CachedActualSize.GetWidth()-square_side, 0.f, this); + m_GeneratedTexts[0] = CGUIText(m_pGUI, *caption, font, m_CachedActualSize.GetWidth() - square_side, 0.f, this); } void CCheckBox::HandleMessage(SGUIMessage& Message) { // Important IGUIButtonBehavior::HandleMessage(Message); IGUITextOwner::HandleMessage(Message); switch (Message.type) { case GUIM_PRESSED: { bool checked; // Switch to opposite. GUI::GetSetting(this, "checked", checked); checked = !checked; GUI::SetSetting(this, "checked", checked); break; } default: break; } } void CCheckBox::Draw() { float bz = GetBufferedZ(); bool checked; int cell_id; CGUISpriteInstance* sprite; CGUISpriteInstance* sprite_over; CGUISpriteInstance* sprite_pressed; CGUISpriteInstance* sprite_disabled; GUI::GetSetting(this, "checked", checked); GUI::GetSetting(this, "cell_id", cell_id); if (checked) { GUI::GetSettingPointer(this, "sprite2", sprite); GUI::GetSettingPointer(this, "sprite2_over", sprite_over); GUI::GetSettingPointer(this, "sprite2_pressed", sprite_pressed); GUI::GetSettingPointer(this, "sprite2_disabled", sprite_disabled); } else { GUI::GetSettingPointer(this, "sprite", sprite); GUI::GetSettingPointer(this, "sprite_over", sprite_over); GUI::GetSettingPointer(this, "sprite_pressed", sprite_pressed); GUI::GetSettingPointer(this, "sprite_disabled", sprite_disabled); } DrawButton(m_CachedActualSize, bz, *sprite, *sprite_over, *sprite_pressed, *sprite_disabled, cell_id); } Index: ps/trunk/source/gui/CGUI.cpp =================================================================== --- ps/trunk/source/gui/CGUI.cpp (revision 22678) +++ ps/trunk/source/gui/CGUI.cpp (revision 22679) @@ -1,1769 +1,1374 @@ /* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include #include #include "GUI.h" // Types - when including them into the engine. #include "CButton.h" #include "CChart.h" #include "CCheckBox.h" #include "CDropDown.h" #include "CImage.h" #include "CInput.h" #include "CList.h" #include "COList.h" #include "CProgressBar.h" #include "CRadioButton.h" #include "CSlider.h" #include "CText.h" #include "CTooltip.h" #include "MiniMap.h" #include "graphics/FontMetrics.h" #include "graphics/ShaderManager.h" -#include "graphics/TextRenderer.h" #include "i18n/L10n.h" #include "lib/bits.h" #include "lib/input.h" #include "lib/sysdep/sysdep.h" #include "lib/timer.h" #include "lib/utf8.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" #include "ps/GameSetup/Config.h" #include "ps/Globals.h" #include "ps/Hotkey.h" #include "ps/Profile.h" #include "ps/Pyrogenesis.h" #include "ps/XML/Xeromyces.h" #include "renderer/Renderer.h" #include "scripting/ScriptFunctions.h" #include "scriptinterface/ScriptInterface.h" extern int g_yres; const double SELECT_DBLCLICK_RATE = 0.5; const u32 MAX_OBJECT_DEPTH = 100; // Max number of nesting for GUI includes. Used to detect recursive inclusion InReaction CGUI::HandleEvent(const SDL_Event_* ev) { InReaction ret = IN_PASS; if (ev->ev.type == SDL_HOTKEYDOWN || ev->ev.type == SDL_HOTKEYUP) { const char* hotkey = static_cast(ev->ev.user.data1); std::map >::iterator it = m_HotkeyObjects.find(hotkey); if (it != m_HotkeyObjects.end()) for (IGUIObject* const& obj : it->second) { // Update hotkey status before sending the event, // else the status will be outdated when processing the GUI event. HotkeyInputHandler(ev); ret = IN_HANDLED; if (ev->ev.type == SDL_HOTKEYDOWN) obj->SendEvent(GUIM_PRESSED, "press"); else obj->SendEvent(GUIM_RELEASED, "release"); } } else if (ev->ev.type == SDL_MOUSEMOTION) { // Yes the mouse position is stored as float to avoid // constant conversions when operating in a // float-based environment. m_MousePos = CPos((float)ev->ev.motion.x / g_GuiScale, (float)ev->ev.motion.y / g_GuiScale); SGUIMessage msg(GUIM_MOUSE_MOTION); GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject, &IGUIObject::HandleMessage, msg); } // Update m_MouseButtons. (BUTTONUP is handled later.) else if (ev->ev.type == SDL_MOUSEBUTTONDOWN) { switch (ev->ev.button.button) { case SDL_BUTTON_LEFT: case SDL_BUTTON_RIGHT: case SDL_BUTTON_MIDDLE: m_MouseButtons |= Bit(ev->ev.button.button); break; default: break; } } // Update m_MousePos (for delayed mouse button events) CPos oldMousePos = m_MousePos; if (ev->ev.type == SDL_MOUSEBUTTONDOWN || ev->ev.type == SDL_MOUSEBUTTONUP) { m_MousePos = CPos((float)ev->ev.button.x / g_GuiScale, (float)ev->ev.button.y / g_GuiScale); } // Only one object can be hovered IGUIObject* pNearest = NULL; // TODO Gee: (2004-09-08) Big TODO, don't do the below if the SDL_Event is something like a keypress! try { PROFILE("mouse events"); // TODO Gee: Optimizations needed! // these two recursive function are quite overhead heavy. // pNearest will after this point at the hovered object, possibly NULL pNearest = FindObjectUnderMouse(); // Now we'll call UpdateMouseOver on *all* objects, // we'll input the one hovered, and they will each // update their own data and send messages accordingly GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject, &IGUIObject::UpdateMouseOver, pNearest); if (ev->ev.type == SDL_MOUSEBUTTONDOWN) { switch (ev->ev.button.button) { case SDL_BUTTON_LEFT: // Focus the clicked object (or focus none if nothing clicked on) SetFocusedObject(pNearest); if (pNearest) ret = pNearest->SendEvent(GUIM_MOUSE_PRESS_LEFT, "mouseleftpress"); break; case SDL_BUTTON_RIGHT: if (pNearest) ret = pNearest->SendEvent(GUIM_MOUSE_PRESS_RIGHT, "mouserightpress"); break; default: break; } } else if (ev->ev.type == SDL_MOUSEWHEEL && pNearest) { if (ev->ev.wheel.y < 0) ret = pNearest->SendEvent(GUIM_MOUSE_WHEEL_DOWN, "mousewheeldown"); else if (ev->ev.wheel.y > 0) ret = pNearest->SendEvent(GUIM_MOUSE_WHEEL_UP, "mousewheelup"); } else if (ev->ev.type == SDL_MOUSEBUTTONUP) { switch (ev->ev.button.button) { case SDL_BUTTON_LEFT: if (pNearest) { double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_LEFT]; pNearest->m_LastClickTime[SDL_BUTTON_LEFT] = timer_Time(); if (timeElapsed < SELECT_DBLCLICK_RATE) ret = pNearest->SendEvent(GUIM_MOUSE_DBLCLICK_LEFT, "mouseleftdoubleclick"); else ret = pNearest->SendEvent(GUIM_MOUSE_RELEASE_LEFT, "mouseleftrelease"); } break; case SDL_BUTTON_RIGHT: if (pNearest) { double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_RIGHT]; pNearest->m_LastClickTime[SDL_BUTTON_RIGHT] = timer_Time(); if (timeElapsed < SELECT_DBLCLICK_RATE) ret = pNearest->SendEvent(GUIM_MOUSE_DBLCLICK_RIGHT, "mouserightdoubleclick"); else ret = pNearest->SendEvent(GUIM_MOUSE_RELEASE_RIGHT, "mouserightrelease"); } break; } // Reset all states on all visible objects GUI<>::RecurseObject(GUIRR_HIDDEN, m_BaseObject, &IGUIObject::ResetStates); // Since the hover state will have been reset, we reload it. GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject, &IGUIObject::UpdateMouseOver, pNearest); } } catch (PSERROR_GUI& e) { UNUSED2(e); debug_warn(L"CGUI::HandleEvent error"); // TODO Gee: Handle } // BUTTONUP's effect on m_MouseButtons is handled after // everything else, so that e.g. 'press' handlers (activated // on button up) see which mouse button had been pressed. if (ev->ev.type == SDL_MOUSEBUTTONUP) { switch (ev->ev.button.button) { case SDL_BUTTON_LEFT: case SDL_BUTTON_RIGHT: case SDL_BUTTON_MIDDLE: m_MouseButtons &= ~Bit(ev->ev.button.button); break; default: break; } } // Restore m_MousePos (for delayed mouse button events) if (ev->ev.type == SDL_MOUSEBUTTONDOWN || ev->ev.type == SDL_MOUSEBUTTONUP) m_MousePos = oldMousePos; // Handle keys for input boxes if (GetFocusedObject()) { if ((ev->ev.type == SDL_KEYDOWN && ev->ev.key.keysym.sym != SDLK_ESCAPE && !g_keys[SDLK_LCTRL] && !g_keys[SDLK_RCTRL] && !g_keys[SDLK_LALT] && !g_keys[SDLK_RALT]) || ev->ev.type == SDL_HOTKEYDOWN || ev->ev.type == SDL_TEXTINPUT || ev->ev.type == SDL_TEXTEDITING) { ret = GetFocusedObject()->ManuallyHandleEvent(ev); } // else will return IN_PASS because we never used the button. } return ret; } void CGUI::TickObjects() { CStr action = "tick"; GUI::RecurseObject(0, m_BaseObject, &IGUIObject::ScriptEvent, action); m_Tooltip.Update(FindObjectUnderMouse(), m_MousePos, this); } void CGUI::SendEventToAll(const CStr& EventName) { // janwas 2006-03-03: spoke with Ykkrosh about EventName case. // when registering, case is converted to lower - this avoids surprise // if someone were to get the case wrong and then not notice their // handler is never called. however, until now, the other end // (sending events here) wasn't converting to lower case, // leading to a similar problem. // now fixed; case is irrelevant since all are converted to lower. GUI::RecurseObject(0, m_BaseObject, &IGUIObject::ScriptEvent, EventName.LowerCase()); } void CGUI::SendEventToAll(const CStr& EventName, JS::HandleValueArray paramData) { GUI::RecurseObject(0, m_BaseObject, &IGUIObject::ScriptEvent, EventName.LowerCase(), paramData); } CGUI::CGUI(const shared_ptr& runtime) : m_MouseButtons(0), m_FocusedObject(NULL), m_InternalNameNumber(0) { m_ScriptInterface.reset(new ScriptInterface("Engine", "GUIPage", runtime)); m_ScriptInterface->SetCallbackData(this); GuiScriptingInit(*m_ScriptInterface); m_ScriptInterface->LoadGlobalScripts(); m_BaseObject = new CGUIDummyObject(this); } CGUI::~CGUI() { Destroy(); if (m_BaseObject) delete m_BaseObject; } IGUIObject* CGUI::ConstructObject(const CStr& str) { if (m_ObjectTypes.count(str) > 0) return (*m_ObjectTypes[str])(this); // Error reporting will be handled with the nullptr return. return nullptr; } void CGUI::Initialize() { // Add base types! // You can also add types outside the GUI to extend the flexibility of the GUI. // Pyrogenesis though will have all the object types inserted from here. AddObjectType("empty", &CGUIDummyObject::ConstructObject); AddObjectType("button", &CButton::ConstructObject); AddObjectType("image", &CImage::ConstructObject); AddObjectType("text", &CText::ConstructObject); AddObjectType("checkbox", &CCheckBox::ConstructObject); AddObjectType("radiobutton", &CRadioButton::ConstructObject); AddObjectType("progressbar", &CProgressBar::ConstructObject); AddObjectType("minimap", &CMiniMap::ConstructObject); AddObjectType("input", &CInput::ConstructObject); AddObjectType("list", &CList::ConstructObject); AddObjectType("olist", &COList::ConstructObject); AddObjectType("dropdown", &CDropDown::ConstructObject); AddObjectType("tooltip", &CTooltip::ConstructObject); AddObjectType("chart", &CChart::ConstructObject); AddObjectType("slider", &CSlider::ConstructObject); } void CGUI::Draw() { // Clear the depth buffer, so the GUI is // drawn on top of everything else glClear(GL_DEPTH_BUFFER_BIT); try { // Recurse IGUIObject::Draw() with restriction: hidden // meaning all hidden objects won't call Draw (nor will it recurse its children) GUI<>::RecurseObject(GUIRR_HIDDEN, m_BaseObject, &IGUIObject::Draw); } catch (PSERROR_GUI& e) { LOGERROR("GUI draw error: %s", e.what()); } } void CGUI::DrawSprite(const CGUISpriteInstance& Sprite, int CellID, const float& Z, const CRect& Rect, const CRect& UNUSED(Clipping)) { // If the sprite doesn't exist (name == ""), don't bother drawing anything if (Sprite.IsEmpty()) return; // TODO: Clipping? Sprite.Draw(this, Rect, CellID, m_Sprites, Z); } void CGUI::Destroy() { // We can use the map to delete all // now we don't want to cancel all if one Destroy fails for (const std::pair& p : m_pAllObjects) { try { p.second->Destroy(); } catch (PSERROR_GUI& e) { UNUSED2(e); debug_warn(L"CGUI::Destroy error"); // TODO Gee: Handle } delete p.second; } m_pAllObjects.clear(); for (const std::pair& p : m_Sprites) delete p.second; m_Sprites.clear(); m_Icons.clear(); } void CGUI::UpdateResolution() { // Update ALL cached GUI<>::RecurseObject(0, m_BaseObject, &IGUIObject::UpdateCachedSize); } void CGUI::AddObject(IGUIObject* pObject) { try { m_BaseObject->AddChild(pObject); // Cache tree GUI<>::RecurseObject(0, pObject, &IGUIObject::UpdateCachedSize); SGUIMessage msg(GUIM_LOAD); GUI::RecurseObject(0, pObject, &IGUIObject::HandleMessage, msg); } catch (PSERROR_GUI&) { throw; } } void CGUI::UpdateObjects() { // We'll fill a temporary map until we know everything succeeded map_pObjects AllObjects; try { // Fill freshly GUI::RecurseObject(0, m_BaseObject, &IGUIObject::AddToPointersMap, AllObjects); } catch (PSERROR_GUI&) { throw; } // Else actually update the real one m_pAllObjects.swap(AllObjects); } bool CGUI::ObjectExists(const CStr& Name) const { return m_pAllObjects.count(Name) != 0; } IGUIObject* CGUI::FindObjectByName(const CStr& Name) const { map_pObjects::const_iterator it = m_pAllObjects.find(Name); if (it == m_pAllObjects.end()) return NULL; else return it->second; } IGUIObject* CGUI::FindObjectUnderMouse() const { IGUIObject* pNearest = NULL; GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject, &IGUIObject::ChooseMouseOverAndClosest, pNearest); return pNearest; } void CGUI::SetFocusedObject(IGUIObject* pObject) { if (pObject == m_FocusedObject) return; if (m_FocusedObject) { SGUIMessage msg(GUIM_LOST_FOCUS); m_FocusedObject->HandleMessage(msg); } m_FocusedObject = pObject; if (m_FocusedObject) { SGUIMessage msg(GUIM_GOT_FOCUS); m_FocusedObject->HandleMessage(msg); } } const SGUIScrollBarStyle* CGUI::GetScrollBarStyle(const CStr& style) const { std::map::const_iterator it = m_ScrollBarStyles.find(style); if (it == m_ScrollBarStyles.end()) return nullptr; return &it->second; } -// private struct used only in GenerateText(...) -struct SGenerateTextImage -{ - // Only primitve members, so move assignments are the same as copy assignments. - - float m_YFrom, // The image's starting location in Y - m_YTo, // The image's end location in Y - m_Indentation; // The image width in other words - - // Some help functions - // TODO Gee: CRect => CPoint ? - void SetupSpriteCall(const bool Left, SGUIText::SSpriteCall& SpriteCall, - const float width, const float y, - const CSize& Size, const CStr& TextureName, - const float BufferZone, const int CellID) - { - // TODO Gee: Temp hardcoded values - SpriteCall.m_Area.top = y+BufferZone; - SpriteCall.m_Area.bottom = y+BufferZone + Size.cy; - - if (Left) - { - SpriteCall.m_Area.left = BufferZone; - SpriteCall.m_Area.right = Size.cx+BufferZone; - } - else - { - SpriteCall.m_Area.left = width-BufferZone - Size.cx; - SpriteCall.m_Area.right = width-BufferZone; - } - - SpriteCall.m_CellID = CellID; - SpriteCall.m_Sprite = TextureName; - - m_YFrom = SpriteCall.m_Area.top-BufferZone; - m_YTo = SpriteCall.m_Area.bottom+BufferZone; - m_Indentation = Size.cx+BufferZone*2; - } -}; - -SGUIText CGUI::GenerateText(const CGUIString& string, const CStrW& FontW, const float& Width, const float& BufferZone, const IGUIObject* pObject) -{ - SGUIText Text; - - CStrIntern Font(FontW.ToUTF8()); - - if (string.m_Words.empty()) - return Text; - - float x = BufferZone, y = BufferZone; // drawing pointer - int from = 0; - bool done = false; - - bool FirstLine = true; // Necessary because text in the first line is shorter - // (it doesn't count the line spacing) - - // Images on the left or the right side. - std::vector Images[2]; - int pos_last_img = -1; // Position in the string where last img (either left or right) were encountered. - // in order to avoid duplicate processing. - - // Easier to read. - bool WordWrapping = (Width != 0); - - // get the alignment type for the control we are computing the text for since - // we are computing the horizontal alignment in this method in order to not have - // to run through the TextCalls a second time in the CalculateTextPosition method again - EAlign align = EAlign_Left; - if (pObject->SettingExists("text_align")) - GUI::GetSetting(pObject, "text_align", align); - - // Go through string word by word - for (int i = 0; i < (int)string.m_Words.size()-1 && !done; ++i) - { - // Pre-process each line one time, so we know which floating images - // will be added for that line. - - // Generated stuff is stored in Feedback. - CGUIString::SFeedback Feedback; - - // Preliminary line_height, used for word-wrapping with floating images. - float prelim_line_height = 0.f; - - // Width and height of all text calls generated. - string.GenerateTextCall(this, Feedback, Font, - string.m_Words[i], string.m_Words[i+1], - FirstLine); - - // Loop through our images queues, to see if images has been added. - - // Check if this has already been processed. - // Also, floating images are only applicable if Word-Wrapping is on - if (WordWrapping && i > pos_last_img) - { - // Loop left/right - for (int j = 0; j < 2; ++j) - { - for (const CStr& imgname : Feedback.m_Images[j]) - { - SGUIText::SSpriteCall SpriteCall; - SGenerateTextImage Image; - - // Y is if no other floating images is above, y. Else it is placed - // after the last image, like a stack downwards. - float _y; - if (!Images[j].empty()) - _y = std::max(y, Images[j].back().m_YTo); - else - _y = y; - - // Get Size from Icon database - const SGUIIcon& icon = GetIcon(imgname); - - const CSize& size = icon.m_Size; - Image.SetupSpriteCall((j == CGUIString::SFeedback::Left), SpriteCall, Width, _y, size, icon.m_SpriteName, BufferZone, icon.m_CellID); - - // Check if image is the lowest thing. - Text.m_Size.cy = std::max(Text.m_Size.cy, Image.m_YTo); - - Images[j].emplace_back(Image); - Text.m_SpriteCalls.emplace_back(std::move(SpriteCall)); - } - } - } - - pos_last_img = std::max(pos_last_img, i); - - x += Feedback.m_Size.cx; - prelim_line_height = std::max(prelim_line_height, Feedback.m_Size.cy); - - // If Width is 0, then there's no word-wrapping, disable NewLine. - if ((WordWrapping && (x > Width-BufferZone || Feedback.m_NewLine)) || i == (int)string.m_Words.size()-2) - { - // Change 'from' to 'i', but first keep a copy of its value. - int temp_from = from; - from = i; - - static const int From = 0, To = 1; - //int width_from=0, width_to=width; - float width_range[2]; - width_range[From] = BufferZone; - width_range[To] = Width - BufferZone; - - // Floating images are only applicable if word-wrapping is enabled. - if (WordWrapping) - { - // Decide width of the line. We need to iterate our floating images. - // this won't be exact because we're assuming the line_height - // will be as our preliminary calculation said. But that may change, - // although we'd have to add a couple of more loops to try straightening - // this problem out, and it is very unlikely to happen noticeably if one - // structures his text in a stylistically pure fashion. Even if not, it - // is still quite unlikely it will happen. - // Loop through left and right side, from and to. - for (int j = 0; j < 2; ++j) - { - for (const SGenerateTextImage& img : Images[j]) - { - // We're working with two intervals here, the image's and the line height's. - // let's find the union of these two. - float union_from, union_to; - - union_from = std::max(y, img.m_YFrom); - union_to = std::min(y+prelim_line_height, img.m_YTo); - - // The union is not empty - if (union_to > union_from) - { - if (j == From) - width_range[From] = std::max(width_range[From], img.m_Indentation); - else - width_range[To] = std::min(width_range[To], Width - img.m_Indentation); - } - } - } - } - - // Reset X for the next loop - x = width_range[From]; - - // Now we'll do another loop to figure out the height and width of - // the line (the height of the largest character and the width is - // the sum of all of the individual widths). This - // couldn't be determined in the first loop (main loop) - // because it didn't regard images, so we don't know - // if all characters processed, will actually be involved - // in that line. - float line_height = 0.f; - float line_width = 0.f; - for (int j = temp_from; j <= i; ++j) - { - // We don't want to use Feedback now, so we'll have to use - // another. - CGUIString::SFeedback Feedback2; - - // Don't attach object, it'll suppress the errors - // we want them to be reported in the final GenerateTextCall() - // so that we don't get duplicates. - string.GenerateTextCall(this, Feedback2, Font, - string.m_Words[j], string.m_Words[j+1], - FirstLine); - - // Append X value. - x += Feedback2.m_Size.cx; - - if (WordWrapping && x > width_range[To] && j!=temp_from && !Feedback2.m_NewLine) - { - // The calculated width of each word includes the space between the current - // word and the next. When we're wrapping, we need subtract the width of the - // space after the last word on the line before the wrap. - CFontMetrics currentFont(Font); - line_width -= currentFont.GetCharacterWidth(*L" "); - break; - } - - // Let line_height be the maximum m_Height we encounter. - line_height = std::max(line_height, Feedback2.m_Size.cy); - - // If the current word is an explicit new line ("\n"), - // break now before adding the width of this character. - // ("\n" doesn't have a glyph, thus is given the same width as - // the "missing glyph" character by CFont::GetCharacterWidth().) - if (WordWrapping && Feedback2.m_NewLine) - break; - - line_width += Feedback2.m_Size.cx; - } - - float dx = 0.f; - // compute offset based on what kind of alignment - switch (align) - { - case EAlign_Left: - // don't add an offset - dx = 0.f; - break; - - case EAlign_Center: - dx = ((width_range[To] - width_range[From]) - line_width) / 2; - break; - - case EAlign_Right: - dx = width_range[To] - line_width; - break; - - default: - debug_warn(L"Broken EAlign in CGUI::GenerateText()"); - break; - } - // Reset x once more - x = width_range[From]; - // Move down, because font drawing starts from the baseline - y += line_height; - - // Do the real processing now - for (int j = temp_from; j <= i; ++j) - { - // We don't want to use Feedback now, so we'll have to use - // another one. - CGUIString::SFeedback Feedback2; - - // Defaults - string.GenerateTextCall(this, Feedback2, Font, - string.m_Words[j], string.m_Words[j+1], - FirstLine, pObject); - - // Iterate all and set X/Y values - // Since X values are not set, we need to make an internal - // iteration with an increment that will append the internal - // x, that is what x_pointer is for. - float x_pointer = 0.f; - - for (SGUIText::STextCall& tc : Feedback2.m_TextCalls) - { - tc.m_Pos = CPos(dx + x + x_pointer, y); - - x_pointer += tc.m_Size.cx; - - if (tc.m_pSpriteCall) - tc.m_pSpriteCall->m_Area += tc.m_Pos - CSize(0, tc.m_pSpriteCall->m_Area.GetHeight()); - } - - // Append X value. - x += Feedback2.m_Size.cx; - - // The first word overrides the width limit, what we - // do, in those cases, are just drawing that word even - // though it'll extend the object. - if (WordWrapping) // only if word-wrapping is applicable - { - if (Feedback2.m_NewLine) - { - from = j+1; - - // Sprite call can exist within only a newline segment, - // therefore we need this. - Text.m_SpriteCalls.insert( - Text.m_SpriteCalls.end(), - std::make_move_iterator(Feedback2.m_SpriteCalls.begin()), - std::make_move_iterator(Feedback2.m_SpriteCalls.end())); - break; - } - else if (x > width_range[To] && j == temp_from) - { - from = j+1; - // do not break, since we want it to be added to m_TextCalls - } - else if (x > width_range[To]) - { - from = j; - break; - } - } - - // Add the whole Feedback2.m_TextCalls to our m_TextCalls. - Text.m_TextCalls.insert( - Text.m_TextCalls.end(), - std::make_move_iterator(Feedback2.m_TextCalls.begin()), - std::make_move_iterator(Feedback2.m_TextCalls.end())); - - Text.m_SpriteCalls.insert( - Text.m_SpriteCalls.end(), - std::make_move_iterator(Feedback2.m_SpriteCalls.begin()), - std::make_move_iterator(Feedback2.m_SpriteCalls.end())); - - if (j == (int)string.m_Words.size()-2) - done = true; - } - - // Reset X - x = BufferZone; - - // Update dimensions - Text.m_Size.cx = std::max(Text.m_Size.cx, line_width + BufferZone * 2); - Text.m_Size.cy = std::max(Text.m_Size.cy, y + BufferZone); - - FirstLine = false; - - // Now if we entered as from = i, then we want - // i being one minus that, so that it will become - // the same i in the next loop. The difference is that - // we're on a new line now. - i = from-1; - } - } - - return Text; -} - -void CGUI::DrawText(SGUIText& Text, const CGUIColor& DefaultColor, const CPos& pos, const float& z, const CRect& clipping) -{ - CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_gui_text); - - tech->BeginPass(); - - bool isClipped = (clipping != CRect()); - if (isClipped) - { - glEnable(GL_SCISSOR_TEST); - glScissor( - clipping.left * g_GuiScale, - g_yres - clipping.bottom * g_GuiScale, - clipping.GetWidth() * g_GuiScale, - clipping.GetHeight() * g_GuiScale); - } - - CTextRenderer textRenderer(tech->GetShader()); - textRenderer.SetClippingRect(clipping); - textRenderer.Translate(0.0f, 0.0f, z); - - for (const SGUIText::STextCall& tc : Text.m_TextCalls) - { - // If this is just a placeholder for a sprite call, continue - if (tc.m_pSpriteCall) - continue; - - CGUIColor color = tc.m_UseCustomColor ? tc.m_Color : DefaultColor; - - textRenderer.Color(color); - textRenderer.Font(tc.m_Font); - textRenderer.Put((float)(int)(pos.x + tc.m_Pos.x), (float)(int)(pos.y + tc.m_Pos.y), &tc.m_String); - } - - textRenderer.Render(); - - for (const SGUIText::SSpriteCall& sc : Text.m_SpriteCalls) - DrawSprite(sc.m_Sprite, sc.m_CellID, z, sc.m_Area + pos); - - if (isClipped) - glDisable(GL_SCISSOR_TEST); - - tech->EndPass(); -} - bool CGUI::GetPreDefinedColor(const CStr& name, CGUIColor& Output) const { std::map::const_iterator cit = m_PreDefinedColors.find(name); if (cit == m_PreDefinedColors.end()) return false; Output = cit->second; return true; } /** * @callgraph */ void CGUI::LoadXmlFile(const VfsPath& Filename, boost::unordered_set& Paths) { Paths.insert(Filename); CXeromyces XeroFile; if (XeroFile.Load(g_VFS, Filename, "gui") != PSRETURN_OK) return; XMBElement node = XeroFile.GetRoot(); CStr root_name(XeroFile.GetElementString(node.GetNodeName())); try { if (root_name == "objects") { Xeromyces_ReadRootObjects(node, &XeroFile, Paths); // Re-cache all values so these gets cached too. //UpdateResolution(); } else if (root_name == "sprites") Xeromyces_ReadRootSprites(node, &XeroFile); else if (root_name == "styles") Xeromyces_ReadRootStyles(node, &XeroFile); else if (root_name == "setup") Xeromyces_ReadRootSetup(node, &XeroFile); else debug_warn(L"CGUI::LoadXmlFile error"); } catch (PSERROR_GUI& e) { LOGERROR("Errors loading GUI file %s (%u)", Filename.string8(), e.getCode()); return; } } //=================================================================== // XML Reading Xeromyces Specific Sub-Routines //=================================================================== void CGUI::Xeromyces_ReadRootObjects(XMBElement Element, CXeromyces* pFile, boost::unordered_set& Paths) { int el_script = pFile->GetElementID("script"); std::vector > subst; // Iterate main children // they should all be or