Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/gui/CGUIText.cpp
Show First 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | CGUIText::CGUIText(const CGUI& pGUI, const CGUIString& string, const CStrW& fontW, const float width, const float bufferZone, const EAlign align, const IGUIObject* pObject) | ||||
bool firstLine = true; // Necessary because text in the first line is shorter | bool firstLine = true; // Necessary because text in the first line is shorter | ||||
// (it doesn't count the line spacing) | // (it doesn't count the line spacing) | ||||
// Images on the left or the right side. | // Images on the left or the right side. | ||||
SGenerateTextImages images; | SGenerateTextImages images; | ||||
int posLastImage = -1; // Position in the string where last img (either left or right) were encountered. | int posLastImage = -1; // Position in the string where last img (either left or right) were encountered. | ||||
// in order to avoid duplicate processing. | // in order to avoid duplicate processing. | ||||
// Go through string word by word | // 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); | |||||
float spaceWidth = currentFont.GetCharacterWidth(L' '); | |||||
// Go through string word by word. | |||||
// a word is defined as [start, end[ in string.m_Words so we skip the last item. | |||||
for (int i = 0; i < static_cast<int>(string.m_Words.size()) - 1; ++i) | for (int i = 0; i < static_cast<int>(string.m_Words.size()) - 1; ++i) | ||||
{ | { | ||||
// Pre-process each line one time, so we know which floating images | // Pre-process each line one time, so we know which floating images | ||||
// will be added for that line. | // will be added for that line. | ||||
// Generated stuff is stored in feedback. | // Generated stuff is stored in feedback. | ||||
CGUIString::SFeedback feedback; | CGUIString::SFeedback feedback; | ||||
// Preliminary line height, used for word-wrapping with floating images. | // Preliminary line height, used for word-wrapping with floating images. | ||||
float prelimLineHeight = 0.f; | float prelimLineHeight = 0.f; | ||||
// Width and height of all text calls generated. | // Width and height of all text calls generated. | ||||
string.GenerateTextCall(pGUI, feedback, font, string.m_Words[i], string.m_Words[i+1], firstLine); | string.GenerateTextCall(pGUI, feedback, font, string.m_Words[i], string.m_Words[i+1], firstLine); | ||||
SetupSpriteCalls(pGUI, feedback.m_Images, y, width, bufferZone, i, posLastImage, images); | SetupSpriteCalls(pGUI, feedback.m_Images, y, width, bufferZone, i, posLastImage, images); | ||||
posLastImage = std::max(posLastImage, i); | posLastImage = std::max(posLastImage, i); | ||||
lineWidth += feedback.m_Size.Width; | lineWidth += feedback.m_Size.Width; | ||||
prelimLineHeight = std::max(prelimLineHeight, feedback.m_Size.Height); | prelimLineHeight = std::max(prelimLineHeight, feedback.m_Size.Height); | ||||
float spaceCorrection = feedback.m_EndsWithSpace ? spaceWidth : 0.f; | |||||
// If width is 0, then there's no word-wrapping, disable NewLine. | // If width is 0, then there's no word-wrapping, disable NewLine. | ||||
if ((width != 0 && from != i && (lineWidth + 2 * bufferZone > width || feedback.m_NewLine)) || i == static_cast<int>(string.m_Words.size()) - 2) | if ((width != 0 && from != i && (lineWidth - spaceCorrection + 2 * bufferZone > width || feedback.m_NewLine)) || i == static_cast<int>(string.m_Words.size()) - 2) | ||||
{ | { | ||||
if (ProcessLine(pGUI, string, font, pObject, images, align, prelimLineHeight, width, bufferZone, firstLine, y, i, from)) | if (ProcessLine(pGUI, string, font, pObject, images, align, prelimLineHeight, width, bufferZone, firstLine, y, i, from)) | ||||
return; | return; | ||||
lineWidth = 0.f; | lineWidth = 0.f; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | void CGUIText::ComputeLineSize( | ||||
const bool firstLine, | const bool firstLine, | ||||
const float width, | const float width, | ||||
const float widthRangeFrom, | const float widthRangeFrom, | ||||
const float widthRangeTo, | const float widthRangeTo, | ||||
const int i, | const int i, | ||||
const int tempFrom, | const int tempFrom, | ||||
CSize2D& lineSize) const | CSize2D& lineSize) const | ||||
{ | { | ||||
// 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); | |||||
float spaceWidth = currentFont.GetCharacterWidth(L' '); | |||||
float spaceCorrection = 0.f; | |||||
float x = widthRangeFrom; | float x = widthRangeFrom; | ||||
for (int j = tempFrom; j <= i; ++j) | for (int j = tempFrom; j <= i; ++j) | ||||
{ | { | ||||
// We don't want to use feedback now, so we'll have to use another one. | // We don't want to use feedback now, so we'll have to use another one. | ||||
CGUIString::SFeedback feedback2; | CGUIString::SFeedback feedback2; | ||||
// Don't attach object, it'll suppress the errors | // Don't attach object, it'll suppress the errors | ||||
// we want them to be reported in the final GenerateTextCall() | // we want them to be reported in the final GenerateTextCall() | ||||
// so that we don't get duplicates. | // so that we don't get duplicates. | ||||
string.GenerateTextCall(pGUI, feedback2, font, string.m_Words[j], string.m_Words[j+1], firstLine); | string.GenerateTextCall(pGUI, feedback2, font, string.m_Words[j], string.m_Words[j+1], firstLine); | ||||
// Append X value. | // Append X value. | ||||
x += feedback2.m_Size.Width; | x += feedback2.m_Size.Width; | ||||
if (width != 0 && x > widthRangeTo && j != tempFrom && !feedback2.m_NewLine) | if (width != 0 && x - spaceCorrection > widthRangeTo && j != tempFrom && !feedback2.m_NewLine) | ||||
phosit: this does fix it. But its not a good solution | |||||
{ | |||||
// 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); | |||||
lineSize.Width -= currentFont.GetCharacterWidth(*L" "); | |||||
break; | break; | ||||
} | |||||
// Update after the line-break detection, because otherwise spaceCorrection above | |||||
// will refer to the wrapped word and not the last-word-before-the-line-break. | |||||
spaceCorrection = feedback2.m_EndsWithSpace ? spaceWidth : 0.f; | |||||
// Let lineSize.cy be the maximum m_Height we encounter. | // Let lineSize.cy be the maximum m_Height we encounter. | ||||
lineSize.Height = std::max(lineSize.Height, feedback2.m_Size.Height); | lineSize.Height = std::max(lineSize.Height, feedback2.m_Size.Height); | ||||
// If the current word is an explicit new line ("\n"), | // If the current word is an explicit new line ("\n"), | ||||
// break now before adding the width of this character. | // break now before adding the width of this character. | ||||
// ("\n" doesn't have a glyph, thus is given the same width as | // ("\n" doesn't have a glyph, thus is given the same width as | ||||
// the "missing glyph" character by CFont::GetCharacterWidth().) | // the "missing glyph" character by CFont::GetCharacterWidth().) | ||||
if (width != 0 && feedback2.m_NewLine) | if (width != 0 && feedback2.m_NewLine) | ||||
break; | break; | ||||
lineSize.Width += feedback2.m_Size.Width; | lineSize.Width += feedback2.m_Size.Width; | ||||
} | } | ||||
// Remove the space if necessary. | |||||
lineSize.Width -= spaceCorrection; | |||||
} | } | ||||
bool CGUIText::ProcessLine( | bool CGUIText::ProcessLine( | ||||
const CGUI& pGUI, | const CGUI& pGUI, | ||||
const CGUIString& string, | const CGUIString& string, | ||||
const CStrIntern& font, | const CStrIntern& font, | ||||
const IGUIObject* pObject, | const IGUIObject* pObject, | ||||
const SGenerateTextImages& images, | const SGenerateTextImages& images, | ||||
▲ Show 20 Lines • Show All 139 Lines • ▼ Show 20 Lines | for (STextCall& tc : feedback2.m_TextCalls) | ||||
tc.m_Pos = CVector2D(x + xPointer, y); | tc.m_Pos = CVector2D(x + xPointer, y); | ||||
xPointer += tc.m_Size.Width; | xPointer += tc.m_Size.Width; | ||||
if (tc.m_pSpriteCall) | if (tc.m_pSpriteCall) | ||||
tc.m_pSpriteCall->m_Area += tc.m_Pos - CSize2D(0, tc.m_pSpriteCall->m_Area.GetHeight()); | tc.m_pSpriteCall->m_Area += tc.m_Pos - CSize2D(0, tc.m_pSpriteCall->m_Area.GetHeight()); | ||||
} | } | ||||
// Append X value. | |||||
x += feedback2.m_Size.Width; | x += feedback2.m_Size.Width; | ||||
// 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 (width != 0) // only if word-wrapping is applicable | ||||
{ | { | ||||
// Check if we need to wrap, using the same algorithm as ComputeLineSize | |||||
// This means we must ignore the 'space before the next word' for the purposes of wrapping. | |||||
CFontMetrics currentFont(font); | |||||
float spaceWidth = currentFont.GetCharacterWidth(L' '); | |||||
float spaceCorrection = feedback2.m_EndsWithSpace ? spaceWidth : 0.f; | |||||
if (feedback2.m_NewLine) | if (feedback2.m_NewLine) | ||||
{ | { | ||||
from = j + 1; | from = j + 1; | ||||
// Sprite call can exist within only a newline segment, | // Sprite call can exist within only a newline segment, | ||||
// therefore we need this. | // therefore we need this. | ||||
if (!feedback2.m_SpriteCalls.empty()) | if (!feedback2.m_SpriteCalls.empty()) | ||||
{ | { | ||||
auto newEnd = std::remove_if(feedback2.m_TextCalls.begin(), feedback2.m_TextCalls.end(), [](const STextCall& call) { return !call.m_pSpriteCall; }); | auto newEnd = std::remove_if(feedback2.m_TextCalls.begin(), feedback2.m_TextCalls.end(), [](const STextCall& call) { return !call.m_pSpriteCall; }); | ||||
m_TextCalls.insert( | m_TextCalls.insert( | ||||
m_TextCalls.end(), | m_TextCalls.end(), | ||||
std::make_move_iterator(feedback2.m_TextCalls.begin()), | std::make_move_iterator(feedback2.m_TextCalls.begin()), | ||||
std::make_move_iterator(newEnd)); | std::make_move_iterator(newEnd)); | ||||
m_SpriteCalls.insert( | m_SpriteCalls.insert( | ||||
m_SpriteCalls.end(), | m_SpriteCalls.end(), | ||||
std::make_move_iterator(feedback2.m_SpriteCalls.begin()), | std::make_move_iterator(feedback2.m_SpriteCalls.begin()), | ||||
std::make_move_iterator(feedback2.m_SpriteCalls.end())); | std::make_move_iterator(feedback2.m_SpriteCalls.end())); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
else if (x > widthRangeTo && j == tempFrom) | else if (x - spaceCorrection > widthRangeTo && j == tempFrom) | ||||
{ | { | ||||
// The first word overrides the width limit, what we do, | |||||
// in those cases, is just drawing that word even | |||||
// though it'll extend the object. | |||||
// Ergo: do not break, since we want it to be added to m_TextCalls. | |||||
from = j+1; | from = j+1; | ||||
// do not break, since we want it to be added to m_TextCalls | // To avoid doing redundant computations, set up j to exit the loop right away. | ||||
j = i + 1; | |||||
} | } | ||||
else if (x > widthRangeTo) | else if (x - spaceCorrection > widthRangeTo) | ||||
{ | { | ||||
from = j; | from = j; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
// Add the whole feedback2.m_TextCalls to our m_TextCalls. | // Add the whole feedback2.m_TextCalls to our m_TextCalls. | ||||
m_TextCalls.insert( | m_TextCalls.insert( | ||||
▲ Show 20 Lines • Show All 66 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator
this does fix it. But its not a good solution