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