Index: ps/trunk/source/gui/CButton.cpp =================================================================== --- ps/trunk/source/gui/CButton.cpp +++ ps/trunk/source/gui/CButton.cpp @@ -47,8 +47,7 @@ AddSetting("tooltip"); AddSetting("tooltip_style"); - // Add text - AddText(new SGUIText()); + AddText(); } CButton::~CButton() @@ -70,9 +69,10 @@ 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) Index: ps/trunk/source/gui/CChart.cpp =================================================================== --- ps/trunk/source/gui/CChart.cpp +++ ps/trunk/source/gui/CChart.cpp @@ -208,8 +208,6 @@ void CChart::SetupText() { - for (SGUIText* t : m_GeneratedTexts) - delete t; m_GeneratedTexts.clear(); m_TextPositions.clear(); @@ -290,10 +288,8 @@ 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() Index: ps/trunk/source/gui/CCheckBox.cpp =================================================================== --- ps/trunk/source/gui/CCheckBox.cpp +++ ps/trunk/source/gui/CCheckBox.cpp @@ -57,7 +57,7 @@ AddSetting("tooltip"); AddSetting("tooltip_style"); - AddText(new SGUIText()); + AddText(); } CCheckBox::~CCheckBox() @@ -82,7 +82,7 @@ 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) Index: ps/trunk/source/gui/CGUI.h =================================================================== --- ps/trunk/source/gui/CGUI.h +++ ps/trunk/source/gui/CGUI.h @@ -53,9 +53,8 @@ class JSObject; // The GUI stores a JSObject*, so needs to know that JSObject exists class IGUIObject; class CGUISpriteInstance; -struct SGUIText; struct CGUIColor; -struct SGUIText; +class CGUIText; struct SGUIIcon; class CGUIString; class CGUISprite; @@ -125,17 +124,6 @@ void DrawSprite(const CGUISpriteInstance& Sprite, int CellID, const float& Z, const CRect& Rect, const CRect& Clipping = CRect()); /** - * Draw a SGUIText object - * - * @param Text Text object. - * @param DefaultColor Color used if no tag applied. - * @param pos position - * @param z z value. - * @param clipping - */ - void DrawText(SGUIText& Text, const CGUIColor& DefaultColor, const CPos& pos, const float& z, const CRect& clipping); - - /** * Clean up, call this to clean up all memory allocated * within the GUI. */ @@ -227,25 +215,6 @@ void UpdateResolution(); /** - * Generate a SGUIText object from the inputted string. - * The function will break down the string and its - * tags to calculate exactly which rendering queries - * will be sent to the Renderer. Also, horizontal alignment - * is taken into acount in this method but NOT vertical alignment. - * - * Done through the CGUI since it can communicate with - * - * @param Text Text to generate SGUIText object from - * @param Font Default font, notice both Default color and default font - * can be changed by tags. - * @param Width Width, 0 if no word-wrapping. - * @param BufferZone space between text and edge, and space between text and images. - * @param pObject Optional parameter for error output. Used *only* if error parsing fails, - * and we need to be able to output which object the error occurred in to aid the user. - */ - SGUIText GenerateText(const CGUIString& Text, const CStrW& Font, const float& Width, const float& BufferZone, const IGUIObject* pObject = NULL); - - /** * Check if an icon exists */ bool HasIcon(const CStr& name) const { return (m_Icons.count(name) != 0); } Index: ps/trunk/source/gui/CGUI.cpp =================================================================== --- ps/trunk/source/gui/CGUI.cpp +++ ps/trunk/source/gui/CGUI.cpp @@ -40,7 +40,6 @@ #include "graphics/FontMetrics.h" #include "graphics/ShaderManager.h" -#include "graphics/TextRenderer.h" #include "i18n/L10n.h" #include "lib/bits.h" #include "lib/input.h" @@ -485,400 +484,6 @@ 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); Index: ps/trunk/source/gui/CGUIString.h =================================================================== --- ps/trunk/source/gui/CGUIString.h +++ ps/trunk/source/gui/CGUIString.h @@ -15,27 +15,17 @@ * 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 "gui/CGUIText.h" #include "ps/CStrIntern.h" +#include +#include +#include + class CGUI; /** @@ -43,9 +33,9 @@ * the tags and builds up a list of all text that will * be different when outputted. * - * The difference between CGUIString and SGUIText is that + * The difference between CGUIString and CGUIText is that * CGUIString is a string-class that parses the tags - * when the value is set. The SGUIText is just a container + * when the value is set. The CGUIText is just a container * which stores the positions and settings of all text-calls * that will have to be made to the Renderer. */ @@ -147,14 +137,15 @@ /** * Image stacks, for left and right floating images. */ - std::vector m_Images[2]; // left and right + std::array, 2> m_Images; // 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. + std::vector m_TextCalls; + + // list for consistent mem addresses so that we can point to elements. + std::list m_SpriteCalls; /** * Width and Height *feedback* Index: ps/trunk/source/gui/CGUIString.cpp =================================================================== --- ps/trunk/source/gui/CGUIString.cpp +++ ps/trunk/source/gui/CGUIString.cpp @@ -17,13 +17,15 @@ #include "precompiled.h" -#include +#include "CGUIString.h" +#include "graphics/FontMetrics.h" #include "gui/GUI.h" #include "lib/utf8.h" -#include "graphics/FontMetrics.h" #include "ps/CLogger.h" +#include +#include // List of word delimiter bounds // The list contains ranges of word delimiters. The odd indexed chars are the start @@ -118,10 +120,10 @@ // 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; + CGUIText::STextCall TextCall; // Also add it to the sprites being rendered. - SGUIText::SSpriteCall SpriteCall; + CGUIText::SSpriteCall SpriteCall; // Get Icon from icon database in pGUI const SGUIIcon& icon = pGUI->GetIcon(path); @@ -174,7 +176,7 @@ } else if (_to > _from && !Feedback.m_NewLine) { - SGUIText::STextCall TextCall; + CGUIText::STextCall TextCall; // Set defaults TextCall.m_Font = DefaultFont; Index: ps/trunk/source/gui/CGUIText.h =================================================================== --- ps/trunk/source/gui/CGUIText.h +++ ps/trunk/source/gui/CGUIText.h @@ -0,0 +1,283 @@ +/* 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 "gui/CGUIColor.h" +#include "gui/CGUISprite.h" +#include "gui/GUIutil.h" +#include "graphics/TextRenderer.h" +#include "ps/CStrIntern.h" +#include "ps/Shapes.h" + +#include +#include +#include + +class CGUIString; +struct SGenerateTextImage; +using SGenerateTextImages = std::array, 2>; + +/** + * An CGUIText 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!". + */ +class CGUIText +{ +public: + /** + * 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; + }; + + // The SSpriteCall CGUISpriteInstance makes this uncopyable to avoid invalidating its draw cache. + // Also take advantage of exchanging the containers directly with move semantics. + NONCOPYABLE(CGUIText); + MOVABLE(CGUIText); + + /** + * Generates empty text. + */ + CGUIText() = default; + + /** + * Generate a CGUIText object from the inputted string. + * The function will break down the string and its + * tags to calculate exactly which rendering queries + * will be sent to the Renderer. Also, horizontal alignment + * is taken into acount in this method but NOT vertical alignment. + * + * @param Text Text to generate CGUIText object from + * @param Font Default font, notice both Default color and default font + * can be changed by tags. + * @param Width Width, 0 if no word-wrapping. + * @param BufferZone space between text and edge, and space between text and images. + * @param pObject Optional parameter for error output. Used *only* if error parsing fails, + * and we need to be able to output which object the error occurred in to aid the user. + */ + CGUIText(const CGUI* pGUI, const CGUIString& string, const CStrW& FontW, const float Width, const float BufferZone, const IGUIObject* pObject = nullptr); + + /** + * Draw this CGUIText object + */ + void Draw(CGUI* pGUI, const CGUIColor& DefaultColor, const CPos& pos, const float z, const CRect& clipping) const; + + const CSize& GetSize() const { return m_Size; } + + const std::list& GetSpriteCalls() const { return m_SpriteCalls; } + + const std::vector& GetTextCalls() const { return m_TextCalls; } + + // Helper functions of the constructor + bool ProcessLine( + const CGUI* pGUI, + const CGUIString& string, + const CStrIntern& Font, + const IGUIObject* pObject, + const SGenerateTextImages& Images, + const EAlign align, + const float prelim_line_height, + const float Width, + const float BufferZone, + bool& FirstLine, + float& x, + float& y, + int& i, + int& from); + + void SetupSpriteCalls( + const CGUI* pGUI, + const std::array, 2>& FeedbackImages, + const float y, + const float Width, + const float BufferZone, + const int i, + const int pos_last_img, + SGenerateTextImages& Images); + + float GetLineOffset( + const EAlign align, + const float width_range_from, + const float width_range_to, + const CSize& line_size) const; + + void ComputeLineRange( + const SGenerateTextImages& Images, + const float y, + const float Width, + const float prelim_line_height, + float& width_range_from, + float& width_range_to) const; + + void ComputeLineSize( + const CGUI* pGUI, + const CGUIString& string, + const CStrIntern& Font, + const bool FirstLine, + const float Width, + const float width_range_to, + const int i, + const int temp_from, + float& x, + CSize& line_size) const; + + bool AssembleCalls( + const CGUI* pGUI, + const CGUIString& string, + const CStrIntern& Font, + const IGUIObject* pObject, + const bool FirstLine, + const float Width, + const float width_range_to, + const float dx, + const float y, + const int temp_from, + const int i, + float& x, + int& from); + + /** + * 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; +}; + +struct SGenerateTextImage +{ + // The image's starting location in Y + float m_YFrom; + + // The image's end location in Y + float m_YTo; + + // The image width in other words + float m_Indentation; + + void SetupSpriteCall( + const bool Left, CGUIText::SSpriteCall& SpriteCall, const float width, const float y, + const CSize& Size, const CStr& TextureName, const float BufferZone, const int CellID); +}; + +#endif // INCLUDED_GUITEXT Index: ps/trunk/source/gui/CGUIText.cpp =================================================================== --- ps/trunk/source/gui/CGUIText.cpp +++ ps/trunk/source/gui/CGUIText.cpp @@ -0,0 +1,471 @@ +/* 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 "CGUIText.h" + +#include "gui/CGUIString.h" +#include "gui/IGUIObject.h" +#include "graphics/FontMetrics.h" +#include "graphics/ShaderManager.h" +#include "renderer/Renderer.h" + +#include + +extern int g_xres, g_yres; +extern float g_GuiScale; + +// TODO Gee: CRect => CPoint ? +void SGenerateTextImage::SetupSpriteCall( + const bool Left, CGUIText::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; +} + +CGUIText::CGUIText(const CGUI* pGUI, const CGUIString& string, const CStrW& FontW, const float Width, const float BufferZone, const IGUIObject* pObject) +{ + if (string.m_Words.empty()) + return; + + CStrIntern Font(FontW.ToUTF8()); + float x = BufferZone, y = BufferZone; // drawing pointer + int from = 0; + + 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. + SGenerateTextImages Images; + int pos_last_img = -1; // Position in the string where last img (either left or right) were encountered. + // in order to avoid duplicate processing. + + // 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 < static_cast(string.m_Words.size()) - 1; ++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(pGUI, Feedback, Font, string.m_Words[i], string.m_Words[i+1], FirstLine); + + SetupSpriteCalls(pGUI, Feedback.m_Images, y, Width, BufferZone, i, pos_last_img, Images); + + 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 (((Width != 0 && (x > Width - BufferZone || Feedback.m_NewLine)) || i == static_cast(string.m_Words.size()) - 2) && + ProcessLine(pGUI, string, Font, pObject, Images, align, prelim_line_height, Width, BufferZone, FirstLine, x, y, i, from)) + return; + } +} + +// Loop through our images queues, to see if images have been added. +void CGUIText::SetupSpriteCalls( + const CGUI* pGUI, + const std::array, 2>& FeedbackImages, + const float y, + const float Width, + const float BufferZone, + const int i, + const int pos_last_img, + SGenerateTextImages& Images) +{ + // Check if this has already been processed. + // Also, floating images are only applicable if Word-Wrapping is on + if (Width == 0 || i <= pos_last_img) + return; + + // Loop left/right + for (int j = 0; j < 2; ++j) + for (const CStr& imgname : FeedbackImages[j]) + { + 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; + + const SGUIIcon& icon = pGUI->GetIcon(imgname); + Image.SetupSpriteCall(j == CGUIString::SFeedback::Left, SpriteCall, Width, _y, icon.m_Size, icon.m_SpriteName, BufferZone, icon.m_CellID); + + // Check if image is the lowest thing. + m_Size.cy = std::max(m_Size.cy, Image.m_YTo); + + Images[j].emplace_back(Image); + m_SpriteCalls.emplace_back(std::move(SpriteCall)); + } +} + +// 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. +void CGUIText::ComputeLineSize( + const CGUI* pGUI, + const CGUIString& string, + const CStrIntern& Font, + const bool FirstLine, + const float Width, + const float width_range_to, + const int i, + const int temp_from, + float& x, + CSize& line_size) const +{ + 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; + + // 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(pGUI, Feedback2, Font, string.m_Words[j], string.m_Words[j+1], FirstLine); + + // Append X value. + x += Feedback2.m_Size.cx; + + if (Width != 0 && 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_size.cx -= currentFont.GetCharacterWidth(*L" "); + break; + } + + // Let line_size.cy be the maximum m_Height we encounter. + line_size.cy = std::max(line_size.cy, 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 (Width != 0 && Feedback2.m_NewLine) + break; + + line_size.cx += Feedback2.m_Size.cx; + } +} + +bool CGUIText::ProcessLine( + const CGUI* pGUI, + const CGUIString& string, + const CStrIntern& Font, + const IGUIObject* pObject, + const SGenerateTextImages& Images, + const EAlign align, + const float prelim_line_height, + const float Width, + const float BufferZone, + bool& FirstLine, + float& x, + float& y, + int& i, + int& from) +{ + // Change 'from' to 'i', but first keep a copy of its value. + int temp_from = from; + from = i; + + float width_range_from = BufferZone; + float width_range_to = Width - BufferZone; + ComputeLineRange(Images, y, Width, prelim_line_height, width_range_from, width_range_to); + + // Reset X for the next loop + x = width_range_from; + + CSize line_size; + ComputeLineSize(pGUI, string, Font, FirstLine, Width, width_range_to, i, temp_from, x, line_size); + + // Reset x once more + x = width_range_from; + + // Move down, because font drawing starts from the baseline + y += line_size.cy; + + const float dx = GetLineOffset(align, width_range_from, width_range_to, line_size); + + // Do the real processing now + const bool done = AssembleCalls(pGUI, string, Font, pObject, FirstLine, Width, width_range_to, dx, y, temp_from, i, x, from); + + // Reset X + x = BufferZone; + + // Update dimensions + m_Size.cx = std::max(m_Size.cx, line_size.cx + BufferZone * 2); + m_Size.cy = std::max(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 done; +} + +// Decide width of the line. We need to iterate our floating images. +// this won't be exact because we're assuming the line_size.cy +// 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. +void CGUIText::ComputeLineRange( + const SGenerateTextImages& Images, + const float y, + const float Width, + const float prelim_line_height, + float& width_range_from, + float& width_range_to) const +{ + // Floating images are only applicable if word-wrapping is enabled. + if (Width == 0) + return; + + 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 == 0) + width_range_from = std::max(width_range_from, img.m_Indentation); + else + width_range_to = std::min(width_range_to, Width - img.m_Indentation); + } + } +} + +// compute offset based on what kind of alignment +float CGUIText::GetLineOffset( + const EAlign align, + const float width_range_from, + const float width_range_to, + const CSize& line_size) const +{ + switch (align) + { + case EAlign_Left: + // don't add an offset + return 0.f; + + case EAlign_Center: + return ((width_range_to - width_range_from) - line_size.cx) / 2; + + case EAlign_Right: + return width_range_to - line_size.cx; + + default: + debug_warn(L"Broken EAlign in CGUIText()"); + return 0.f; + } +} + +bool CGUIText::AssembleCalls( + const CGUI* pGUI, + const CGUIString& string, + const CStrIntern& Font, + const IGUIObject* pObject, + const bool FirstLine, + const float Width, + const float width_range_to, + const float dx, + const float y, + const int temp_from, + const int i, + float& x, + int& from) +{ + bool done = false; + + 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(pGUI, 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 (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 (Width != 0) // 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. + m_SpriteCalls.insert( + 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. + m_TextCalls.insert( + m_TextCalls.end(), + std::make_move_iterator(Feedback2.m_TextCalls.begin()), + std::make_move_iterator(Feedback2.m_TextCalls.end())); + + m_SpriteCalls.insert( + m_SpriteCalls.end(), + std::make_move_iterator(Feedback2.m_SpriteCalls.begin()), + std::make_move_iterator(Feedback2.m_SpriteCalls.end())); + + if (j == static_cast(string.m_Words.size()) - 2) + done = true; + } + + return done; +} + +void CGUIText::Draw(CGUI* pGUI, const CGUIColor& DefaultColor, const CPos& pos, const float z, const CRect& clipping) const +{ + 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 STextCall& tc : 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(floorf(pos.x + tc.m_Pos.x), floorf(pos.y + tc.m_Pos.y), &tc.m_String); + } + + textRenderer.Render(); + + for (const SSpriteCall& sc : m_SpriteCalls) + pGUI->DrawSprite(sc.m_Sprite, sc.m_CellID, z, sc.m_Area + pos); + + if (isClipped) + glDisable(GL_SCISSOR_TEST); + + tech->EndPass(); +} Index: ps/trunk/source/gui/CList.cpp =================================================================== --- ps/trunk/source/gui/CList.cpp +++ ps/trunk/source/gui/CList.cpp @@ -82,8 +82,6 @@ // Delete all generated texts. Some could probably be saved, // but this is easier, and this function will never be called // continuously, or even often, so it'll probably be okay. - for (SGUIText* const& t : m_GeneratedTexts) - delete t; m_GeneratedTexts.clear(); CStrW font; @@ -108,23 +106,20 @@ for (size_t i = 0; i < pList->m_Items.size(); ++i) { - // Create a new SGUIText. Later on, input it using AddText() - SGUIText* text = new SGUIText(); + CGUIText* text; if (!pList->m_Items[i].GetOriginalString().empty()) - *text = GetGUI()->GenerateText(pList->m_Items[i], font, width, buffer_zone, this); + text = &AddText(pList->m_Items[i], font, width, buffer_zone, this); else { // Minimum height of a space character of the current font size CGUIString align_string; align_string.SetValue(L" "); - *text = GetGUI()->GenerateText(align_string, font, width, buffer_zone, this); + text = &AddText(align_string, font, width, buffer_zone, this); } m_ItemsYPositions[i] = buffered_y; - buffered_y += text->m_Size.cy; - - AddText(text); + buffered_y += text->GetSize().cy; } m_ItemsYPositions[pList->m_Items.size()] = buffered_y; Index: ps/trunk/source/gui/COList.cpp =================================================================== --- ps/trunk/source/gui/COList.cpp +++ ps/trunk/source/gui/COList.cpp @@ -49,8 +49,6 @@ // Delete all generated texts. Some could probably be saved, // but this is easier, and this function will never be called // continuously, or even often, so it'll probably be okay. - for (SGUIText* const& t : m_GeneratedTexts) - delete t; m_GeneratedTexts.clear(); CStrW font; @@ -78,12 +76,11 @@ if (column.m_Width > 0 && column.m_Width < 1) width *= m_TotalAvailableColumnWidth; - SGUIText* text = new SGUIText(); CGUIString gui_string; gui_string.SetValue(column.m_Heading); - *text = GetGUI()->GenerateText(gui_string, font, width, buffer_zone, this); - AddText(text); - m_HeadingHeight = std::max(m_HeadingHeight, text->m_Size.cy + COLUMN_SHIFT.y); + + const CGUIText& text = AddText(gui_string, font, width, buffer_zone, this); + m_HeadingHeight = std::max(m_HeadingHeight, text.GetSize().cy + COLUMN_SHIFT.y); } // Generate texts @@ -101,18 +98,17 @@ CGUIList* pList_c; GUI::GetSettingPointer(this, "list_" + column.m_Id, pList_c); - SGUIText* text = new SGUIText(); + CGUIText* text; if (!pList_c->m_Items[i].GetOriginalString().empty()) - *text = GetGUI()->GenerateText(pList_c->m_Items[i], font, width, buffer_zone, this); + text = &AddText(pList_c->m_Items[i], font, width, buffer_zone, this); else { // Minimum height of a space character of the current font size CGUIString align_string; align_string.SetValue(L" "); - *text = GetGUI()->GenerateText(align_string, font, width, buffer_zone, this); + text = &AddText(align_string, font, width, buffer_zone, this); } - shift = std::max(shift, text->m_Size.cy); - AddText(text); + shift = std::max(shift, text->GetSize().cy); } buffered_y += shift; } Index: ps/trunk/source/gui/CText.cpp =================================================================== --- ps/trunk/source/gui/CText.cpp +++ ps/trunk/source/gui/CText.cpp @@ -57,7 +57,7 @@ AddScrollBar(bar); // Add text - AddText(new SGUIText()); + AddText(); } CText::~CText() @@ -89,10 +89,10 @@ float buffer_zone = 0.f; GUI::GetSetting(this, "buffer_zone", buffer_zone); - *m_GeneratedTexts[0] = GetGUI()->GenerateText(*caption, font, width, buffer_zone, this); + m_GeneratedTexts[0] = CGUIText(m_pGUI, *caption, font, width, buffer_zone, this); if (!scrollbar) - CalculateTextPosition(m_CachedActualSize, m_TextPos, *m_GeneratedTexts[0]); + CalculateTextPosition(m_CachedActualSize, m_TextPos, m_GeneratedTexts[0]); // Setup scrollbar if (scrollbar) @@ -109,7 +109,7 @@ if (scroll_bottom && GetScrollBar(0).GetPos() > GetScrollBar(0).GetMaxPos() - 1.5f) bottom = true; - GetScrollBar(0).SetScrollRange(m_GeneratedTexts[0]->m_Size.cy); + GetScrollBar(0).SetScrollRange(m_GeneratedTexts[0].GetSize().cy); GetScrollBar(0).SetScrollSpace(m_CachedActualSize.GetHeight()); GetScrollBar(0).SetX(m_CachedActualSize.right); @@ -244,8 +244,8 @@ bool CText::MouseOverIcon() { - for (SGUIText* const& guitext : m_GeneratedTexts) - for (const SGUIText::SSpriteCall& spritecall : guitext->m_SpriteCalls) + for (const CGUIText& guitext : m_GeneratedTexts) + for (const CGUIText::SSpriteCall& spritecall : guitext.GetSpriteCalls()) { // Check mouse over sprite if (!spritecall.m_Area.PointInside(m_pGUI->GetMousePos() - m_CachedActualSize.TopLeft())) Index: ps/trunk/source/gui/CTooltip.cpp =================================================================== --- ps/trunk/source/gui/CTooltip.cpp +++ ps/trunk/source/gui/CTooltip.cpp @@ -54,7 +54,7 @@ // Set up a blank piece of text, to be replaced with a more // interesting message later - AddText(new SGUIText()); + AddText(); } CTooltip::~CTooltip() @@ -78,8 +78,7 @@ float max_width = 0.f; GUI::GetSetting(this, "maxwidth", max_width); - *m_GeneratedTexts[0] = GetGUI()->GenerateText(*caption, font, max_width, buffer_zone, this); - + m_GeneratedTexts[0] = CGUIText(m_pGUI, *caption, font, max_width, buffer_zone, this); // Position the tooltip relative to the mouse: @@ -96,8 +95,8 @@ GUI::GetSetting(this, "offset", offset); GUI::GetSetting(this, "anchor", anchor); - float textwidth = m_GeneratedTexts[0]->m_Size.cx; - float textheight = m_GeneratedTexts[0]->m_Size.cy; + float textwidth = m_GeneratedTexts[0].GetSize().cx; + float textheight = m_GeneratedTexts[0].GetSize().cy; CClientArea size; size.pixel.left = mousepos.x + offset.x; Index: ps/trunk/source/gui/GUI.h =================================================================== --- ps/trunk/source/gui/GUI.h +++ ps/trunk/source/gui/GUI.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015 Wildfire Games. +/* 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 @@ -15,20 +15,6 @@ * along with 0 A.D. If not, see . */ -/* -GUI Inclusion file - ---Overview-- - - Include this file and it will include the whole GUI. - ---More info-- - - Check TDD for GUI Engine Documentation - -*/ - - #ifndef INCLUDED_GUI #define INCLUDED_GUI @@ -41,8 +27,8 @@ #include "CGUIList.h" #include "CGUISeries.h" +#include "CGUIText.h" #include "GUIbase.h" -#include "GUItext.h" #include "GUIutil.h" #include "IGUIButtonBehavior.h" #include "IGUIObject.h" Index: ps/trunk/source/gui/GUItext.h =================================================================== --- ps/trunk/source/gui/GUItext.h +++ ps/trunk/source/gui/GUItext.h @@ -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 Index: ps/trunk/source/gui/IGUIObject.h =================================================================== --- ps/trunk/source/gui/IGUIObject.h +++ ps/trunk/source/gui/IGUIObject.h @@ -25,9 +25,9 @@ #ifndef INCLUDED_IGUIOBJECT #define INCLUDED_IGUIOBJECT -#include "GUIbase.h" -#include "GUItext.h" +#include "IGUIObject.h" +#include "gui/GUIbase.h" #include "gui/scripting/JSInterface_IGUIObject.h" #include "lib/input.h" // just for IN_PASS #include "ps/XML/Xeromyces.h" Index: ps/trunk/source/gui/IGUITextOwner.h =================================================================== --- ps/trunk/source/gui/IGUITextOwner.h +++ ps/trunk/source/gui/IGUITextOwner.h @@ -54,7 +54,12 @@ /** * Adds a text object. */ - void AddText(SGUIText* text); + CGUIText& AddText(); + + /** + * Adds a text generated by the given arguments. + */ + CGUIText& AddText(const CGUIString& Text, const CStrW& Font, const float& Width, const float& BufferZone, const IGUIObject* pObject = nullptr); /** * Subscribe the custom JS methods. @@ -108,12 +113,12 @@ /** * Texts that are generated and ready to be rendered. */ - std::vector m_GeneratedTexts; + std::vector m_GeneratedTexts; /** * Calculate the position for the text, based on the alignment. */ - void CalculateTextPosition(CRect& ObjSize, CPos& TextPos, SGUIText& Text); + void CalculateTextPosition(CRect& ObjSize, CPos& TextPos, CGUIText& Text); /** * Calculate the size of the first generated text. Index: ps/trunk/source/gui/IGUITextOwner.cpp =================================================================== --- ps/trunk/source/gui/IGUITextOwner.cpp +++ ps/trunk/source/gui/IGUITextOwner.cpp @@ -22,6 +22,8 @@ #include "gui/GUI.h" #include "gui/scripting/JSInterface_IGUITextOwner.h" +#include + IGUITextOwner::IGUITextOwner(CGUI* pGUI) : IGUIObject(pGUI), m_GeneratedTextsValid(false) { @@ -29,8 +31,6 @@ IGUITextOwner::~IGUITextOwner() { - for (SGUIText* const& t : m_GeneratedTexts) - delete t; } void IGUITextOwner::CreateJSObject() @@ -41,9 +41,17 @@ GetGUI()->GetScriptInterface()->GetContext(), m_JSObject); } -void IGUITextOwner::AddText(SGUIText* text) +CGUIText& IGUITextOwner::AddText() +{ + m_GeneratedTexts.emplace_back(); + return m_GeneratedTexts.back(); +} + +CGUIText& IGUITextOwner::AddText(const CGUIString& Text, const CStrW& Font, const float& Width, const float& BufferZone, const IGUIObject* pObject) { - m_GeneratedTexts.push_back(text); + // Avoids a move constructor + m_GeneratedTexts.emplace_back(m_pGUI, Text, Font, Width, BufferZone, pObject); + return m_GeneratedTexts.back(); } void IGUITextOwner::HandleMessage(SGUIMessage& Message) @@ -90,11 +98,10 @@ ENSURE(index < m_GeneratedTexts.size() && "Trying to draw a Text Index within a IGUITextOwner that doesn't exist"); - if (GetGUI()) - GetGUI()->DrawText(*m_GeneratedTexts[index], color, pos, z, clipping); + m_GeneratedTexts.at(index).Draw(m_pGUI, color, pos, z, clipping); } -void IGUITextOwner::CalculateTextPosition(CRect& ObjSize, CPos& TextPos, SGUIText& Text) +void IGUITextOwner::CalculateTextPosition(CRect& ObjSize, CPos& TextPos, CGUIText& Text) { EVAlign valign; GUI::GetSetting(this, "text_valign", valign); @@ -110,10 +117,10 @@ break; case EVAlign_Center: // Round to integer pixel values, else the fonts look awful - TextPos.y = floorf(ObjSize.CenterPoint().y - Text.m_Size.cy/2.f); + TextPos.y = floorf(ObjSize.CenterPoint().y - Text.GetSize().cy / 2.f); break; case EVAlign_Bottom: - TextPos.y = ObjSize.bottom - Text.m_Size.cy; + TextPos.y = ObjSize.bottom - Text.GetSize().cy; break; default: debug_warn(L"Broken EVAlign in CButton::SetupText()"); @@ -133,7 +140,7 @@ return CSize(); // GUI Object types that use multiple texts may override this function. - return m_GeneratedTexts[0]->m_Size; + return m_GeneratedTexts[0].GetSize(); } bool IGUITextOwner::MouseOverIcon()