Index: source/gui/CChart.cpp
===================================================================
--- source/gui/CChart.cpp
+++ source/gui/CChart.cpp
@@ -20,6 +20,7 @@
#include "CChart.h"
#include "gui/CGUIColor.h"
+#include "gui/CGUIString.h"
#include "gui/GUIMatrix.h"
#include "graphics/ShaderManager.h"
#include "i18n/L10n.h"
Index: source/gui/CGUIList.h
===================================================================
--- source/gui/CGUIList.h
+++ source/gui/CGUIList.h
@@ -15,11 +15,10 @@
* along with 0 A.D. If not, see .
*/
-
#ifndef INCLUDED_CGUILIST
#define INCLUDED_CGUILIST
-#include "GUItext.h"
+#include "gui/CGUIString.h"
class CGUIList
{
Index: source/gui/CGUIString.h
===================================================================
--- source/gui/CGUIString.h
+++ source/gui/CGUIString.h
@@ -27,143 +27,18 @@
*
*/
-#ifndef INCLUDED_GUITEXT
-#define INCLUDED_GUITEXT
+#ifndef INCLUDED_CGUISTRING
+#define INCLUDED_CGUISTRING
#include
-#include "CGUISprite.h"
-#include "gui/CGUIColor.h"
+#include "gui/CGUISprite.h"
+#include "gui/GUItext.h"
#include "ps/CStrIntern.h"
class CGUI;
/**
- * 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.
@@ -351,4 +226,4 @@
CStrW m_OriginalString;
};
-#endif // INCLUDED_GUITEXT
+#endif // INCLUDED_CGUISTRING
Index: source/gui/GUItext.h
===================================================================
--- source/gui/GUItext.h
+++ source/gui/GUItext.h
@@ -15,28 +15,15 @@
* 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 "ps/CStrIntern.h"
-
-class CGUI;
+#include "ps/Shapes.h"
+#include "gui/CGUISprite.h"
/**
* An SGUIText object is a parsed string, divided into
@@ -163,192 +150,4 @@
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
Index: source/gui/GUItext.cpp
===================================================================
--- source/gui/GUItext.cpp
+++ source/gui/GUItext.cpp
@@ -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());
-}