Index: ps/trunk/source/gui/GUItext.cpp
===================================================================
--- ps/trunk/source/gui/GUItext.cpp (revision 22642)
+++ ps/trunk/source/gui/GUItext.cpp (nonexistent)
@@ -1,472 +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 .
- */
-
-#include "precompiled.h"
-
-#include
-
-#include "gui/GUI.h"
-#include "lib/utf8.h"
-#include "graphics/FontMetrics.h"
-#include "ps/CLogger.h"
-
-
-// List of word delimiter bounds
-// The list contains ranges of word delimiters. The odd indexed chars are the start
-// of a range, the even are the end of a range. The list must be sorted in INCREASING ORDER
-static const int NUM_WORD_DELIMITERS = 4*2;
-static const u16 WordDelimiters[NUM_WORD_DELIMITERS] = {
- ' ' , ' ', // spaces
- '-' , '-', // hyphens
- 0x3000, 0x31FF, // ideographic symbols
- 0x3400, 0x9FFF
-// TODO add unicode blocks of other languages that don't use spaces
-};
-
-void CGUIString::SFeedback::Reset()
-{
- m_Images[Left].clear();
- m_Images[Right].clear();
- m_TextCalls.clear();
- m_SpriteCalls.clear();
- m_Size = CSize();
- m_NewLine = false;
-}
-
-void CGUIString::GenerateTextCall(const CGUI* pGUI, SFeedback& Feedback, CStrIntern DefaultFont, const int& from, const int& to, const bool FirstLine, const IGUIObject* pObject) const
-{
- // Reset width and height, because they will be determined with incrementation
- // or comparisons.
- Feedback.Reset();
-
- // Check out which text chunk this is within.
- for (const TextChunk& textChunk : m_TextChunks)
- {
- // Get the area that is overlapped by both the TextChunk and
- // by the from/to inputted.
- int _from = std::max(from, textChunk.m_From);
- int _to = std::min(to, textChunk.m_To);
-
- // If from is larger than to, then they are not overlapping
- if (_to == _from && textChunk.m_From == textChunk.m_To)
- {
- // These should never be able to have more than one tag.
- ENSURE(textChunk.m_Tags.size() == 1);
-
- // Icons and images are placed on exactly one position
- // in the words-list, and they can be counted twice if placed
- // on an edge. But there is always only one logical preference
- // that we want. This check filters the unwanted.
-
- // it's in the end of one word, and the icon
- // should really belong to the beginning of the next one
- if (_to == to && to >= 1 && to < (int)m_RawString.length())
- {
- if (m_RawString[to-1] == ' ' ||
- m_RawString[to-1] == '-' ||
- m_RawString[to-1] == '\n')
- continue;
- }
- // This std::string is just a break
- if (_from == from && from >= 1)
- {
- if (m_RawString[from] == '\n' &&
- m_RawString[from-1] != '\n' &&
- m_RawString[from-1] != ' ' &&
- m_RawString[from-1] != '-')
- continue;
- }
-
- const TextChunk::Tag& tag = textChunk.m_Tags[0];
- ENSURE(tag.m_TagType == TextChunk::Tag::TAG_IMGLEFT ||
- tag.m_TagType == TextChunk::Tag::TAG_IMGRIGHT ||
- tag.m_TagType == TextChunk::Tag::TAG_ICON);
-
- const std::string& path = utf8_from_wstring(tag.m_TagValue);
- if (!pGUI->HasIcon(path))
- {
- if (pObject)
- LOGERROR("Trying to use an icon, imgleft or imgright-tag with an undefined icon (\"%s\").", path.c_str());
- continue;
- }
-
- switch (tag.m_TagType)
- {
- case TextChunk::Tag::TAG_IMGLEFT:
- Feedback.m_Images[SFeedback::Left].push_back(path);
- break;
- case TextChunk::Tag::TAG_IMGRIGHT:
- Feedback.m_Images[SFeedback::Right].push_back(path);
- break;
- case TextChunk::Tag::TAG_ICON:
- {
- // We'll need to setup a text-call that will point
- // to the icon, this is to be able to iterate
- // through the text-calls without having to
- // complex the structure virtually for nothing more.
- SGUIText::STextCall TextCall;
-
- // Also add it to the sprites being rendered.
- SGUIText::SSpriteCall SpriteCall;
-
- // Get Icon from icon database in pGUI
- const SGUIIcon& icon = pGUI->GetIcon(path);
-
- const CSize& size = icon.m_Size;
-
- // append width, and make maximum height the height.
- Feedback.m_Size.cx += size.cx;
- Feedback.m_Size.cy = std::max(Feedback.m_Size.cy, size.cy);
-
- // These are also needed later
- TextCall.m_Size = size;
- SpriteCall.m_Area = size;
-
- // Handle additional attributes
- for (const TextChunk::Tag::TagAttribute& tagAttrib : tag.m_TagAttributes)
- {
- if (tagAttrib.attrib == L"displace" && !tagAttrib.value.empty())
- {
- // Displace the sprite
- CSize displacement;
- // Parse the value
- if (!GUI::ParseString(tagAttrib.value, displacement))
- LOGERROR("Error parsing 'displace' value for tag [ICON]");
- else
- SpriteCall.m_Area += displacement;
- }
- else if (tagAttrib.attrib == L"tooltip")
- SpriteCall.m_Tooltip = tagAttrib.value;
- else if (tagAttrib.attrib == L"tooltip_style")
- SpriteCall.m_TooltipStyle = tagAttrib.value;
- }
-
- SpriteCall.m_Sprite = icon.m_SpriteName;
- SpriteCall.m_CellID = icon.m_CellID;
-
- // Add sprite call
- Feedback.m_SpriteCalls.push_back(std::move(SpriteCall));
-
- // Finalize text call
- TextCall.m_pSpriteCall = &Feedback.m_SpriteCalls.back();
-
- // Add text call
- Feedback.m_TextCalls.emplace_back(std::move(TextCall));
-
- break;
- }
- NODEFAULT;
- }
- }
- else if (_to > _from && !Feedback.m_NewLine)
- {
- SGUIText::STextCall TextCall;
-
- // Set defaults
- TextCall.m_Font = DefaultFont;
- TextCall.m_UseCustomColor = false;
-
- TextCall.m_String = m_RawString.substr(_from, _to-_from);
-
- // Go through tags and apply changes.
- for (const TextChunk::Tag& tag : textChunk.m_Tags)
- {
- switch (tag.m_TagType)
- {
- case TextChunk::Tag::TAG_COLOR:
- TextCall.m_UseCustomColor = true;
-
- if (!GUI::ParseString(tag.m_TagValue, TextCall.m_Color) && pObject)
- LOGERROR("Error parsing the value of a [color]-tag in GUI text when reading object \"%s\".", pObject->GetPresentableName().c_str());
- break;
- case TextChunk::Tag::TAG_FONT:
- // TODO Gee: (2004-08-15) Check if Font exists?
- TextCall.m_Font = CStrIntern(utf8_from_wstring(tag.m_TagValue));
- break;
- default:
- LOGERROR("Encountered unexpected tag applied to text");
- break;
- }
- }
-
- // Calculate the size of the font
- CSize size;
- int cx, cy;
- CFontMetrics font (TextCall.m_Font);
- font.CalculateStringSize(TextCall.m_String.c_str(), cx, cy);
- // For anything other than the first line, the line spacing
- // needs to be considered rather than just the height of the text
- if (!FirstLine)
- cy = font.GetLineSpacing();
-
- size.cx = (float)cx;
- size.cy = (float)cy;
-
- // Append width, and make maximum height the height.
- Feedback.m_Size.cx += size.cx;
- Feedback.m_Size.cy = std::max(Feedback.m_Size.cy, size.cy);
-
- // These are also needed later
- TextCall.m_Size = size;
-
- if (!TextCall.m_String.empty() && TextCall.m_String[0] == '\n')
- Feedback.m_NewLine = true;
-
- // Add text-chunk
- Feedback.m_TextCalls.emplace_back(std::move(TextCall));
- }
- }
-}
-
-bool CGUIString::TextChunk::Tag::SetTagType(const CStrW& tagtype)
-{
- TagType t = GetTagType(tagtype);
- if (t == TAG_INVALID)
- return false;
-
- m_TagType = t;
- return true;
-}
-
-CGUIString::TextChunk::Tag::TagType CGUIString::TextChunk::Tag::GetTagType(const CStrW& tagtype) const
-{
- if (tagtype == L"color")
- return TAG_COLOR;
- if (tagtype == L"font")
- return TAG_FONT;
- if (tagtype == L"icon")
- return TAG_ICON;
- if (tagtype == L"imgleft")
- return TAG_IMGLEFT;
- if (tagtype == L"imgright")
- return TAG_IMGRIGHT;
-
- return TAG_INVALID;
-}
-
-void CGUIString::SetValue(const CStrW& str)
-{
- m_OriginalString = str;
-
- m_TextChunks.clear();
- m_Words.clear();
- m_RawString.clear();
-
- // Current Text Chunk
- CGUIString::TextChunk CurrentTextChunk;
- CurrentTextChunk.m_From = 0;
-
- int l = str.length();
- int rawpos = 0;
- CStrW tag;
- std::vector tags;
- bool closing = false;
- for (int p = 0; p < l; ++p)
- {
- TextChunk::Tag tag_;
- switch (str[p])
- {
- case L'[':
- CurrentTextChunk.m_To = rawpos;
- // Add the current chunks if it is not empty
- if (CurrentTextChunk.m_From != rawpos)
- m_TextChunks.push_back(CurrentTextChunk);
- CurrentTextChunk.m_From = rawpos;
-
- closing = false;
- if (++p == l)
- {
- LOGERROR("Partial tag at end of string '%s'", utf8_from_wstring(str));
- break;
- }
- if (str[p] == L'/')
- {
- closing = true;
- if (tags.empty())
- {
- LOGERROR("Encountered closing tag without having any open tags. At %d in '%s'", p, utf8_from_wstring(str));
- break;
- }
- if (++p == l)
- {
- LOGERROR("Partial closing tag at end of string '%s'", utf8_from_wstring(str));
- break;
- }
- }
- tag.clear();
- // Parse tag
- for (; p < l && str[p] != L']'; ++p)
- {
- CStrW name, param;
- switch (str[p])
- {
- case L' ':
- if (closing) // We still parse them to make error handling cleaner
- LOGERROR("Closing tags do not support parameters (at pos %d '%s')", p, utf8_from_wstring(str));
-
- // parse something="something else"
- for (++p; p < l && str[p] != L'='; ++p)
- name.push_back(str[p]);
-
- if (p == l)
- {
- LOGERROR("Parameter without value at pos %d '%s'", p, utf8_from_wstring(str));
- break;
- }
- FALLTHROUGH;
- case L'=':
- // parse a quoted parameter
- if (closing) // We still parse them to make error handling cleaner
- LOGERROR("Closing tags do not support parameters (at pos %d '%s')", p, utf8_from_wstring(str));
-
- if (++p == l)
- {
- LOGERROR("Expected parameter, got end of string '%s'", utf8_from_wstring(str));
- break;
- }
- if (str[p] != L'"')
- {
- LOGERROR("Unquoted parameters are not supported (at pos %d '%s')", p, utf8_from_wstring(str));
- break;
- }
- for (++p; p < l && str[p] != L'"'; ++p)
- {
- switch (str[p])
- {
- case L'\\':
- if (++p == l)
- {
- LOGERROR("Escape character at end of string '%s'", utf8_from_wstring(str));
- break;
- }
- // NOTE: We do not support \n in tag parameters
- FALLTHROUGH;
- default:
- param.push_back(str[p]);
- }
- }
-
- if (!name.empty())
- {
- TextChunk::Tag::TagAttribute a = {name, param};
- tag_.m_TagAttributes.push_back(a);
- }
- else
- tag_.m_TagValue = param;
- break;
- default:
- tag.push_back(str[p]);
- break;
- }
- }
-
- if (!tag_.SetTagType(tag))
- {
- LOGERROR("Invalid tag '%s' at %d in '%s'", utf8_from_wstring(tag), p, utf8_from_wstring(str));
- break;
- }
- if (!closing)
- {
- if (tag_.m_TagType == TextChunk::Tag::TAG_IMGRIGHT
- || tag_.m_TagType == TextChunk::Tag::TAG_IMGLEFT
- || tag_.m_TagType == TextChunk::Tag::TAG_ICON)
- {
- TextChunk FreshTextChunk = { rawpos, rawpos };
- FreshTextChunk.m_Tags.push_back(tag_);
- m_TextChunks.push_back(FreshTextChunk);
- }
- else
- {
- tags.push_back(tag);
- CurrentTextChunk.m_Tags.push_back(tag_);
- }
- }
- else
- {
- if (tag != tags.back())
- {
- LOGERROR("Closing tag '%s' does not match last opened tag '%s' at %d in '%s'", utf8_from_wstring(tag), utf8_from_wstring(tags.back()), p, utf8_from_wstring(str));
- break;
- }
-
- tags.pop_back();
- CurrentTextChunk.m_Tags.pop_back();
- }
- break;
- case L'\\':
- if (++p == l)
- {
- LOGERROR("Escape character at end of string '%s'", utf8_from_wstring(str));
- break;
- }
- if (str[p] == L'n')
- {
- ++rawpos;
- m_RawString.push_back(L'\n');
- break;
- }
- FALLTHROUGH;
- default:
- ++rawpos;
- m_RawString.push_back(str[p]);
- break;
- }
- }
-
- // Add the chunk after the last tag
- if (CurrentTextChunk.m_From != rawpos)
- {
- CurrentTextChunk.m_To = rawpos;
- m_TextChunks.push_back(CurrentTextChunk);
- }
-
-
- // Add a delimiter at start and at end, it helps when
- // processing later, because we don't have make exceptions for
- // those cases.
- m_Words.push_back(0);
-
- // Add word boundaries in increasing order
- for (u32 i = 0; i < m_RawString.length(); ++i)
- {
- wchar_t c = m_RawString[i];
- if (c == '\n')
- {
- m_Words.push_back((int)i);
- m_Words.push_back((int)i+1);
- continue;
- }
- for (int n = 0; n < NUM_WORD_DELIMITERS; n += 2)
- {
- if (c <= WordDelimiters[n+1])
- {
- if (c >= WordDelimiters[n])
- m_Words.push_back((int)i+1);
- // assume the WordDelimiters list is stored in increasing order
- break;
- }
- }
- }
-
- m_Words.push_back((int)m_RawString.length());
-
- // Remove duplicates (only if larger than 2)
- if (m_Words.size() <= 2)
- return;
-
- m_Words.erase(std::unique(m_Words.begin(), m_Words.end()), m_Words.end());
-}
Property changes on: ps/trunk/source/gui/GUItext.cpp
___________________________________________________________________
Deleted: svn:eol-style
## -1 +0,0 ##
-native
\ No newline at end of property
Index: ps/trunk/source/gui/CChart.cpp
===================================================================
--- ps/trunk/source/gui/CChart.cpp (revision 22642)
+++ ps/trunk/source/gui/CChart.cpp (revision 22643)
@@ -1,327 +1,328 @@
/* 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() && !GUI::ParseColor(pSeriesColor->m_Items[i].GetOriginalString(), data.m_Color, 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;
}
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/CGUIList.h
===================================================================
--- ps/trunk/source/gui/CGUIList.h (revision 22642)
+++ ps/trunk/source/gui/CGUIList.h (revision 22643)
@@ -1,42 +1,41 @@
/* 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_CGUILIST
#define INCLUDED_CGUILIST
-#include "GUItext.h"
+#include "gui/CGUIString.h"
class CGUIList
{
public: // struct:ish (but for consistency I call it _C_GUIList, and
// for the same reason it is a class, so that confusion doesn't
// appear when forward declaring.
// Avoid copying the vector.
NONCOPYABLE(CGUIList);
MOVABLE(CGUIList);
CGUIList() = default;
/**
* List of items (as text), the post-processed result is stored in
* the IGUITextOwner structure of this class.
*/
std::vector m_Items;
};
#endif
Index: ps/trunk/source/gui/CGUIString.cpp
===================================================================
--- ps/trunk/source/gui/CGUIString.cpp (nonexistent)
+++ ps/trunk/source/gui/CGUIString.cpp (revision 22643)
@@ -0,0 +1,472 @@
+/* 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 "gui/GUI.h"
+#include "lib/utf8.h"
+#include "graphics/FontMetrics.h"
+#include "ps/CLogger.h"
+
+
+// List of word delimiter bounds
+// The list contains ranges of word delimiters. The odd indexed chars are the start
+// of a range, the even are the end of a range. The list must be sorted in INCREASING ORDER
+static const int NUM_WORD_DELIMITERS = 4*2;
+static const u16 WordDelimiters[NUM_WORD_DELIMITERS] = {
+ ' ' , ' ', // spaces
+ '-' , '-', // hyphens
+ 0x3000, 0x31FF, // ideographic symbols
+ 0x3400, 0x9FFF
+// TODO add unicode blocks of other languages that don't use spaces
+};
+
+void CGUIString::SFeedback::Reset()
+{
+ m_Images[Left].clear();
+ m_Images[Right].clear();
+ m_TextCalls.clear();
+ m_SpriteCalls.clear();
+ m_Size = CSize();
+ m_NewLine = false;
+}
+
+void CGUIString::GenerateTextCall(const CGUI* pGUI, SFeedback& Feedback, CStrIntern DefaultFont, const int& from, const int& to, const bool FirstLine, const IGUIObject* pObject) const
+{
+ // Reset width and height, because they will be determined with incrementation
+ // or comparisons.
+ Feedback.Reset();
+
+ // Check out which text chunk this is within.
+ for (const TextChunk& textChunk : m_TextChunks)
+ {
+ // Get the area that is overlapped by both the TextChunk and
+ // by the from/to inputted.
+ int _from = std::max(from, textChunk.m_From);
+ int _to = std::min(to, textChunk.m_To);
+
+ // If from is larger than to, then they are not overlapping
+ if (_to == _from && textChunk.m_From == textChunk.m_To)
+ {
+ // These should never be able to have more than one tag.
+ ENSURE(textChunk.m_Tags.size() == 1);
+
+ // Icons and images are placed on exactly one position
+ // in the words-list, and they can be counted twice if placed
+ // on an edge. But there is always only one logical preference
+ // that we want. This check filters the unwanted.
+
+ // it's in the end of one word, and the icon
+ // should really belong to the beginning of the next one
+ if (_to == to && to >= 1 && to < (int)m_RawString.length())
+ {
+ if (m_RawString[to-1] == ' ' ||
+ m_RawString[to-1] == '-' ||
+ m_RawString[to-1] == '\n')
+ continue;
+ }
+ // This std::string is just a break
+ if (_from == from && from >= 1)
+ {
+ if (m_RawString[from] == '\n' &&
+ m_RawString[from-1] != '\n' &&
+ m_RawString[from-1] != ' ' &&
+ m_RawString[from-1] != '-')
+ continue;
+ }
+
+ const TextChunk::Tag& tag = textChunk.m_Tags[0];
+ ENSURE(tag.m_TagType == TextChunk::Tag::TAG_IMGLEFT ||
+ tag.m_TagType == TextChunk::Tag::TAG_IMGRIGHT ||
+ tag.m_TagType == TextChunk::Tag::TAG_ICON);
+
+ const std::string& path = utf8_from_wstring(tag.m_TagValue);
+ if (!pGUI->HasIcon(path))
+ {
+ if (pObject)
+ LOGERROR("Trying to use an icon, imgleft or imgright-tag with an undefined icon (\"%s\").", path.c_str());
+ continue;
+ }
+
+ switch (tag.m_TagType)
+ {
+ case TextChunk::Tag::TAG_IMGLEFT:
+ Feedback.m_Images[SFeedback::Left].push_back(path);
+ break;
+ case TextChunk::Tag::TAG_IMGRIGHT:
+ Feedback.m_Images[SFeedback::Right].push_back(path);
+ break;
+ case TextChunk::Tag::TAG_ICON:
+ {
+ // We'll need to setup a text-call that will point
+ // to the icon, this is to be able to iterate
+ // through the text-calls without having to
+ // complex the structure virtually for nothing more.
+ SGUIText::STextCall TextCall;
+
+ // Also add it to the sprites being rendered.
+ SGUIText::SSpriteCall SpriteCall;
+
+ // Get Icon from icon database in pGUI
+ const SGUIIcon& icon = pGUI->GetIcon(path);
+
+ const CSize& size = icon.m_Size;
+
+ // append width, and make maximum height the height.
+ Feedback.m_Size.cx += size.cx;
+ Feedback.m_Size.cy = std::max(Feedback.m_Size.cy, size.cy);
+
+ // These are also needed later
+ TextCall.m_Size = size;
+ SpriteCall.m_Area = size;
+
+ // Handle additional attributes
+ for (const TextChunk::Tag::TagAttribute& tagAttrib : tag.m_TagAttributes)
+ {
+ if (tagAttrib.attrib == L"displace" && !tagAttrib.value.empty())
+ {
+ // Displace the sprite
+ CSize displacement;
+ // Parse the value
+ if (!GUI::ParseString(tagAttrib.value, displacement))
+ LOGERROR("Error parsing 'displace' value for tag [ICON]");
+ else
+ SpriteCall.m_Area += displacement;
+ }
+ else if (tagAttrib.attrib == L"tooltip")
+ SpriteCall.m_Tooltip = tagAttrib.value;
+ else if (tagAttrib.attrib == L"tooltip_style")
+ SpriteCall.m_TooltipStyle = tagAttrib.value;
+ }
+
+ SpriteCall.m_Sprite = icon.m_SpriteName;
+ SpriteCall.m_CellID = icon.m_CellID;
+
+ // Add sprite call
+ Feedback.m_SpriteCalls.push_back(std::move(SpriteCall));
+
+ // Finalize text call
+ TextCall.m_pSpriteCall = &Feedback.m_SpriteCalls.back();
+
+ // Add text call
+ Feedback.m_TextCalls.emplace_back(std::move(TextCall));
+
+ break;
+ }
+ NODEFAULT;
+ }
+ }
+ else if (_to > _from && !Feedback.m_NewLine)
+ {
+ SGUIText::STextCall TextCall;
+
+ // Set defaults
+ TextCall.m_Font = DefaultFont;
+ TextCall.m_UseCustomColor = false;
+
+ TextCall.m_String = m_RawString.substr(_from, _to-_from);
+
+ // Go through tags and apply changes.
+ for (const TextChunk::Tag& tag : textChunk.m_Tags)
+ {
+ switch (tag.m_TagType)
+ {
+ case TextChunk::Tag::TAG_COLOR:
+ TextCall.m_UseCustomColor = true;
+
+ if (!GUI::ParseString(tag.m_TagValue, TextCall.m_Color) && pObject)
+ LOGERROR("Error parsing the value of a [color]-tag in GUI text when reading object \"%s\".", pObject->GetPresentableName().c_str());
+ break;
+ case TextChunk::Tag::TAG_FONT:
+ // TODO Gee: (2004-08-15) Check if Font exists?
+ TextCall.m_Font = CStrIntern(utf8_from_wstring(tag.m_TagValue));
+ break;
+ default:
+ LOGERROR("Encountered unexpected tag applied to text");
+ break;
+ }
+ }
+
+ // Calculate the size of the font
+ CSize size;
+ int cx, cy;
+ CFontMetrics font (TextCall.m_Font);
+ font.CalculateStringSize(TextCall.m_String.c_str(), cx, cy);
+ // For anything other than the first line, the line spacing
+ // needs to be considered rather than just the height of the text
+ if (!FirstLine)
+ cy = font.GetLineSpacing();
+
+ size.cx = (float)cx;
+ size.cy = (float)cy;
+
+ // Append width, and make maximum height the height.
+ Feedback.m_Size.cx += size.cx;
+ Feedback.m_Size.cy = std::max(Feedback.m_Size.cy, size.cy);
+
+ // These are also needed later
+ TextCall.m_Size = size;
+
+ if (!TextCall.m_String.empty() && TextCall.m_String[0] == '\n')
+ Feedback.m_NewLine = true;
+
+ // Add text-chunk
+ Feedback.m_TextCalls.emplace_back(std::move(TextCall));
+ }
+ }
+}
+
+bool CGUIString::TextChunk::Tag::SetTagType(const CStrW& tagtype)
+{
+ TagType t = GetTagType(tagtype);
+ if (t == TAG_INVALID)
+ return false;
+
+ m_TagType = t;
+ return true;
+}
+
+CGUIString::TextChunk::Tag::TagType CGUIString::TextChunk::Tag::GetTagType(const CStrW& tagtype) const
+{
+ if (tagtype == L"color")
+ return TAG_COLOR;
+ if (tagtype == L"font")
+ return TAG_FONT;
+ if (tagtype == L"icon")
+ return TAG_ICON;
+ if (tagtype == L"imgleft")
+ return TAG_IMGLEFT;
+ if (tagtype == L"imgright")
+ return TAG_IMGRIGHT;
+
+ return TAG_INVALID;
+}
+
+void CGUIString::SetValue(const CStrW& str)
+{
+ m_OriginalString = str;
+
+ m_TextChunks.clear();
+ m_Words.clear();
+ m_RawString.clear();
+
+ // Current Text Chunk
+ CGUIString::TextChunk CurrentTextChunk;
+ CurrentTextChunk.m_From = 0;
+
+ int l = str.length();
+ int rawpos = 0;
+ CStrW tag;
+ std::vector tags;
+ bool closing = false;
+ for (int p = 0; p < l; ++p)
+ {
+ TextChunk::Tag tag_;
+ switch (str[p])
+ {
+ case L'[':
+ CurrentTextChunk.m_To = rawpos;
+ // Add the current chunks if it is not empty
+ if (CurrentTextChunk.m_From != rawpos)
+ m_TextChunks.push_back(CurrentTextChunk);
+ CurrentTextChunk.m_From = rawpos;
+
+ closing = false;
+ if (++p == l)
+ {
+ LOGERROR("Partial tag at end of string '%s'", utf8_from_wstring(str));
+ break;
+ }
+ if (str[p] == L'/')
+ {
+ closing = true;
+ if (tags.empty())
+ {
+ LOGERROR("Encountered closing tag without having any open tags. At %d in '%s'", p, utf8_from_wstring(str));
+ break;
+ }
+ if (++p == l)
+ {
+ LOGERROR("Partial closing tag at end of string '%s'", utf8_from_wstring(str));
+ break;
+ }
+ }
+ tag.clear();
+ // Parse tag
+ for (; p < l && str[p] != L']'; ++p)
+ {
+ CStrW name, param;
+ switch (str[p])
+ {
+ case L' ':
+ if (closing) // We still parse them to make error handling cleaner
+ LOGERROR("Closing tags do not support parameters (at pos %d '%s')", p, utf8_from_wstring(str));
+
+ // parse something="something else"
+ for (++p; p < l && str[p] != L'='; ++p)
+ name.push_back(str[p]);
+
+ if (p == l)
+ {
+ LOGERROR("Parameter without value at pos %d '%s'", p, utf8_from_wstring(str));
+ break;
+ }
+ FALLTHROUGH;
+ case L'=':
+ // parse a quoted parameter
+ if (closing) // We still parse them to make error handling cleaner
+ LOGERROR("Closing tags do not support parameters (at pos %d '%s')", p, utf8_from_wstring(str));
+
+ if (++p == l)
+ {
+ LOGERROR("Expected parameter, got end of string '%s'", utf8_from_wstring(str));
+ break;
+ }
+ if (str[p] != L'"')
+ {
+ LOGERROR("Unquoted parameters are not supported (at pos %d '%s')", p, utf8_from_wstring(str));
+ break;
+ }
+ for (++p; p < l && str[p] != L'"'; ++p)
+ {
+ switch (str[p])
+ {
+ case L'\\':
+ if (++p == l)
+ {
+ LOGERROR("Escape character at end of string '%s'", utf8_from_wstring(str));
+ break;
+ }
+ // NOTE: We do not support \n in tag parameters
+ FALLTHROUGH;
+ default:
+ param.push_back(str[p]);
+ }
+ }
+
+ if (!name.empty())
+ {
+ TextChunk::Tag::TagAttribute a = {name, param};
+ tag_.m_TagAttributes.push_back(a);
+ }
+ else
+ tag_.m_TagValue = param;
+ break;
+ default:
+ tag.push_back(str[p]);
+ break;
+ }
+ }
+
+ if (!tag_.SetTagType(tag))
+ {
+ LOGERROR("Invalid tag '%s' at %d in '%s'", utf8_from_wstring(tag), p, utf8_from_wstring(str));
+ break;
+ }
+ if (!closing)
+ {
+ if (tag_.m_TagType == TextChunk::Tag::TAG_IMGRIGHT
+ || tag_.m_TagType == TextChunk::Tag::TAG_IMGLEFT
+ || tag_.m_TagType == TextChunk::Tag::TAG_ICON)
+ {
+ TextChunk FreshTextChunk = { rawpos, rawpos };
+ FreshTextChunk.m_Tags.push_back(tag_);
+ m_TextChunks.push_back(FreshTextChunk);
+ }
+ else
+ {
+ tags.push_back(tag);
+ CurrentTextChunk.m_Tags.push_back(tag_);
+ }
+ }
+ else
+ {
+ if (tag != tags.back())
+ {
+ LOGERROR("Closing tag '%s' does not match last opened tag '%s' at %d in '%s'", utf8_from_wstring(tag), utf8_from_wstring(tags.back()), p, utf8_from_wstring(str));
+ break;
+ }
+
+ tags.pop_back();
+ CurrentTextChunk.m_Tags.pop_back();
+ }
+ break;
+ case L'\\':
+ if (++p == l)
+ {
+ LOGERROR("Escape character at end of string '%s'", utf8_from_wstring(str));
+ break;
+ }
+ if (str[p] == L'n')
+ {
+ ++rawpos;
+ m_RawString.push_back(L'\n');
+ break;
+ }
+ FALLTHROUGH;
+ default:
+ ++rawpos;
+ m_RawString.push_back(str[p]);
+ break;
+ }
+ }
+
+ // Add the chunk after the last tag
+ if (CurrentTextChunk.m_From != rawpos)
+ {
+ CurrentTextChunk.m_To = rawpos;
+ m_TextChunks.push_back(CurrentTextChunk);
+ }
+
+
+ // Add a delimiter at start and at end, it helps when
+ // processing later, because we don't have make exceptions for
+ // those cases.
+ m_Words.push_back(0);
+
+ // Add word boundaries in increasing order
+ for (u32 i = 0; i < m_RawString.length(); ++i)
+ {
+ wchar_t c = m_RawString[i];
+ if (c == '\n')
+ {
+ m_Words.push_back((int)i);
+ m_Words.push_back((int)i+1);
+ continue;
+ }
+ for (int n = 0; n < NUM_WORD_DELIMITERS; n += 2)
+ {
+ if (c <= WordDelimiters[n+1])
+ {
+ if (c >= WordDelimiters[n])
+ m_Words.push_back((int)i+1);
+ // assume the WordDelimiters list is stored in increasing order
+ break;
+ }
+ }
+ }
+
+ m_Words.push_back((int)m_RawString.length());
+
+ // Remove duplicates (only if larger than 2)
+ if (m_Words.size() <= 2)
+ return;
+
+ m_Words.erase(std::unique(m_Words.begin(), m_Words.end()), m_Words.end());
+}
Property changes on: ps/trunk/source/gui/CGUIString.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/gui/CGUIString.h
===================================================================
--- ps/trunk/source/gui/CGUIString.h (nonexistent)
+++ ps/trunk/source/gui/CGUIString.h (revision 22643)
@@ -0,0 +1,229 @@
+/* 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 .
+ */
+
+/*
+ * GUI text, handles text stuff
+ *
+ * --Overview--
+ * Mainly contains struct SGUIText and friends.
+ * Actual text processing is made in CGUI::GenerateText()
+ *
+ * --More info--
+ * Check GUI.h
+ *
+ */
+
+#ifndef INCLUDED_CGUISTRING
+#define INCLUDED_CGUISTRING
+
+#include
+
+#include "gui/CGUISprite.h"
+#include "gui/GUItext.h"
+#include "ps/CStrIntern.h"
+
+class CGUI;
+
+/**
+ * String class, substitute for CStr, but that parses
+ * the tags and builds up a list of all text that will
+ * be different when outputted.
+ *
+ * The difference between CGUIString and SGUIText is that
+ * CGUIString is a string-class that parses the tags
+ * when the value is set. The SGUIText is just a container
+ * which stores the positions and settings of all text-calls
+ * that will have to be made to the Renderer.
+ */
+class CGUIString
+{
+public:
+ /**
+ * A chunk of text that represents one call to the renderer.
+ * In other words, all text in one chunk, will be drawn
+ * exactly with the same settings.
+ */
+ struct TextChunk
+ {
+ /**
+ * A tag looks like this "Hello [b]there[/b] little"
+ */
+ struct Tag
+ {
+ /**
+ * Tag Type
+ */
+ enum TagType
+ {
+ TAG_B,
+ TAG_I,
+ TAG_FONT,
+ TAG_SIZE,
+ TAG_COLOR,
+ TAG_IMGLEFT,
+ TAG_IMGRIGHT,
+ TAG_ICON,
+ TAG_INVALID
+ };
+
+ struct TagAttribute
+ {
+ std::wstring attrib;
+ std::wstring value;
+ };
+
+ /**
+ * Set tag from string
+ *
+ * @param tagtype TagType by string, like 'img' for [img]
+ * @return True if m_TagType was set.
+ */
+ bool SetTagType(const CStrW& tagtype);
+ TagType GetTagType(const CStrW& tagtype) const;
+
+
+ /**
+ * In [b="Hello"][/b]
+ * m_TagType is TAG_B
+ */
+ TagType m_TagType;
+
+ /**
+ * In [b="Hello"][/b]
+ * m_TagValue is 'Hello'
+ */
+ std::wstring m_TagValue;
+
+ /**
+ * Some tags need an additional attributes
+ */
+ std::vector m_TagAttributes;
+ };
+
+ /**
+ * m_From and m_To is the range of the string
+ */
+ int m_From, m_To;
+
+ /**
+ * Tags that are present. [a][b]
+ */
+ std::vector m_Tags;
+ };
+
+ /**
+ * All data generated in GenerateTextCall()
+ */
+ struct SFeedback
+ {
+ // Avoid copying the vector and list containers.
+ NONCOPYABLE(SFeedback);
+ MOVABLE(SFeedback);
+ SFeedback() = default;
+
+ // Constants
+ static const int Left = 0;
+ static const int Right = 1;
+
+ /**
+ * Reset all member data.
+ */
+ void Reset();
+
+ /**
+ * Image stacks, for left and right floating images.
+ */
+ std::vector m_Images[2]; // left and right
+
+ /**
+ * Text and Sprite Calls.
+ */
+ std::vector m_TextCalls;
+ std::list m_SpriteCalls; // list for consistent mem addresses
+ // so that we can point to elements.
+
+ /**
+ * Width and Height *feedback*
+ */
+ CSize m_Size;
+
+ /**
+ * If the word inputted was a new line.
+ */
+ bool m_NewLine;
+ };
+
+ /**
+ * Set the value, the string will automatically
+ * be parsed when set.
+ */
+ void SetValue(const CStrW& str);
+
+ /**
+ * Get String, with tags
+ */
+ const CStrW& GetOriginalString() const { return m_OriginalString; }
+
+ /**
+ * Get String, stripped of tags
+ */
+ const CStrW& GetRawString() const { return m_RawString; }
+
+ /**
+ * Generate Text Call from specified range. The range
+ * must span only within ONE TextChunk though. Otherwise
+ * it can't be fit into a single Text Call
+ *
+ * Notice it won't make it complete, you will have to add
+ * X/Y values and such.
+ *
+ * @param pGUI Pointer to CGUI object making this call, for e.g. icon retrieval.
+ * @param Feedback contains all info that is generated.
+ * @param DefaultFont Default Font
+ * @param from From character n,
+ * @param to to character n.
+ * @param FirstLine Whether this is the first line of text, to calculate its height correctly
+ * @param pObject Only for Error outputting, optional! If NULL
+ * then no Errors will be reported! Useful when you need
+ * to make several GenerateTextCall in different phases,
+ * it avoids duplicates.
+ */
+ void GenerateTextCall(const CGUI* pGUI, SFeedback& Feedback, CStrIntern DefaultFont, const int& from, const int& to, const bool FirstLine, const IGUIObject* pObject = NULL) const;
+
+ /**
+ * Words
+ */
+ std::vector m_Words;
+
+private:
+ /**
+ * TextChunks
+ */
+ std::vector m_TextChunks;
+
+ /**
+ * The full raw string. Stripped of tags.
+ */
+ CStrW m_RawString;
+
+ /**
+ * The original string value passed to SetValue.
+ */
+ CStrW m_OriginalString;
+};
+
+#endif // INCLUDED_CGUISTRING
Property changes on: ps/trunk/source/gui/CGUIString.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/gui/GUItext.h
===================================================================
--- ps/trunk/source/gui/GUItext.h (revision 22642)
+++ ps/trunk/source/gui/GUItext.h (revision 22643)
@@ -1,354 +1,153 @@
/* 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 .
*/
-/*
- * GUI text, handles text stuff
- *
- * --Overview--
- * Mainly contains struct SGUIText and friends.
- * Actual text processing is made in CGUI::GenerateText()
- *
- * --More info--
- * Check GUI.h
- *
- */
-
#ifndef INCLUDED_GUITEXT
#define INCLUDED_GUITEXT
#include
-#include "CGUISprite.h"
#include "gui/CGUIColor.h"
+#include "gui/CGUISprite.h"
#include "ps/CStrIntern.h"
-
-class CGUI;
+#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;
};
-/**
- * String class, substitute for CStr, but that parses
- * the tags and builds up a list of all text that will
- * be different when outputted.
- *
- * The difference between CGUIString and SGUIText is that
- * CGUIString is a string-class that parses the tags
- * when the value is set. The SGUIText is just a container
- * which stores the positions and settings of all text-calls
- * that will have to be made to the Renderer.
- */
-class CGUIString
-{
-public:
- /**
- * A chunk of text that represents one call to the renderer.
- * In other words, all text in one chunk, will be drawn
- * exactly with the same settings.
- */
- struct TextChunk
- {
- /**
- * A tag looks like this "Hello [b]there[/b] little"
- */
- struct Tag
- {
- /**
- * Tag Type
- */
- enum TagType
- {
- TAG_B,
- TAG_I,
- TAG_FONT,
- TAG_SIZE,
- TAG_COLOR,
- TAG_IMGLEFT,
- TAG_IMGRIGHT,
- TAG_ICON,
- TAG_INVALID
- };
-
- struct TagAttribute
- {
- std::wstring attrib;
- std::wstring value;
- };
-
- /**
- * Set tag from string
- *
- * @param tagtype TagType by string, like 'img' for [img]
- * @return True if m_TagType was set.
- */
- bool SetTagType(const CStrW& tagtype);
- TagType GetTagType(const CStrW& tagtype) const;
-
-
- /**
- * In [b="Hello"][/b]
- * m_TagType is TAG_B
- */
- TagType m_TagType;
-
- /**
- * In [b="Hello"][/b]
- * m_TagValue is 'Hello'
- */
- std::wstring m_TagValue;
-
- /**
- * Some tags need an additional attributes
- */
- std::vector m_TagAttributes;
- };
-
- /**
- * m_From and m_To is the range of the string
- */
- int m_From, m_To;
-
- /**
- * Tags that are present. [a][b]
- */
- std::vector m_Tags;
- };
-
- /**
- * All data generated in GenerateTextCall()
- */
- struct SFeedback
- {
- // Avoid copying the vector and list containers.
- NONCOPYABLE(SFeedback);
- MOVABLE(SFeedback);
- SFeedback() = default;
-
- // Constants
- static const int Left = 0;
- static const int Right = 1;
-
- /**
- * Reset all member data.
- */
- void Reset();
-
- /**
- * Image stacks, for left and right floating images.
- */
- std::vector m_Images[2]; // left and right
-
- /**
- * Text and Sprite Calls.
- */
- std::vector m_TextCalls;
- std::list m_SpriteCalls; // list for consistent mem addresses
- // so that we can point to elements.
-
- /**
- * Width and Height *feedback*
- */
- CSize m_Size;
-
- /**
- * If the word inputted was a new line.
- */
- bool m_NewLine;
- };
-
- /**
- * Set the value, the string will automatically
- * be parsed when set.
- */
- void SetValue(const CStrW& str);
-
- /**
- * Get String, with tags
- */
- const CStrW& GetOriginalString() const { return m_OriginalString; }
-
- /**
- * Get String, stripped of tags
- */
- const CStrW& GetRawString() const { return m_RawString; }
-
- /**
- * Generate Text Call from specified range. The range
- * must span only within ONE TextChunk though. Otherwise
- * it can't be fit into a single Text Call
- *
- * Notice it won't make it complete, you will have to add
- * X/Y values and such.
- *
- * @param pGUI Pointer to CGUI object making this call, for e.g. icon retrieval.
- * @param Feedback contains all info that is generated.
- * @param DefaultFont Default Font
- * @param from From character n,
- * @param to to character n.
- * @param FirstLine Whether this is the first line of text, to calculate its height correctly
- * @param pObject Only for Error outputting, optional! If NULL
- * then no Errors will be reported! Useful when you need
- * to make several GenerateTextCall in different phases,
- * it avoids duplicates.
- */
- void GenerateTextCall(const CGUI* pGUI, SFeedback& Feedback, CStrIntern DefaultFont, const int& from, const int& to, const bool FirstLine, const IGUIObject* pObject = NULL) const;
-
- /**
- * Words
- */
- std::vector m_Words;
-
-private:
- /**
- * TextChunks
- */
- std::vector m_TextChunks;
-
- /**
- * The full raw string. Stripped of tags.
- */
- CStrW m_RawString;
-
- /**
- * The original string value passed to SetValue.
- */
- CStrW m_OriginalString;
-};
-
#endif // INCLUDED_GUITEXT