Changeset View
Changeset View
Standalone View
Standalone View
source/gui/CGUIText.cpp
/* Copyright (C) 2019 Wildfire Games. | /* Copyright (C) 2020 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | CGUIText::CGUIText(const CGUI& pGUI, const CGUIString& string, const CStrW& FontW, const float Width, const float BufferZone, const IGUIObject* pObject) | ||||
// get the alignment type for the control we are computing the text for since | // 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 | // 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 | // to run through the TextCalls a second time in the CalculateTextPosition method again | ||||
EAlign align = EAlign_Left; | EAlign align = EAlign_Left; | ||||
if (pObject->SettingExists("text_align")) | if (pObject->SettingExists("text_align")) | ||||
align = pObject->GetSetting<EAlign>("text_align"); | align = pObject->GetSetting<EAlign>("text_align"); | ||||
bool noWrap = pObject->SettingExists("text_nowrap") && pObject->GetSetting<bool>("text_nowrap"); | |||||
vladislavbelov: `const bool noWrap`. | |||||
// Go through string word by word | // Go through string word by word | ||||
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 prelim_line_height = 0.f; | float prelim_line_height = 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, pos_last_img, Images); | SetupSpriteCalls(pGUI, Feedback.m_Images, y, Width, BufferZone, i, pos_last_img, Images); | ||||
pos_last_img = std::max(pos_last_img, i); | pos_last_img = std::max(pos_last_img, i); | ||||
x += Feedback.m_Size.cx; | x += Feedback.m_Size.cx; | ||||
prelim_line_height = std::max(prelim_line_height, Feedback.m_Size.cy); | 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 is 0, then there's no word-wrapping, disable NewLine. | ||||
if (((Width != 0 && (x > Width - BufferZone || Feedback.m_NewLine)) || i == static_cast<int>(string.m_Words.size()) - 2) && | if (((Width != 0 && ((!noWrap && x > Width - BufferZone) || Feedback.m_NewLine)) || i == static_cast<int>(string.m_Words.size()) - 2) && | ||||
ProcessLine(pGUI, string, Font, pObject, Images, align, prelim_line_height, Width, BufferZone, FirstLine, x, y, i, from)) | ProcessLine(pGUI, string, Font, pObject, Images, align, noWrap, prelim_line_height, Width, BufferZone, FirstLine, x, y, i, from)) | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
// Loop through our images queues, to see if images have been added. | // Loop through our images queues, to see if images have been added. | ||||
void CGUIText::SetupSpriteCalls( | void CGUIText::SetupSpriteCalls( | ||||
const CGUI& pGUI, | const CGUI& pGUI, | ||||
const std::array<std::vector<CStr>, 2>& FeedbackImages, | const std::array<std::vector<CStr>, 2>& FeedbackImages, | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
// couldn't be determined in the first loop (main loop) | // couldn't be determined in the first loop (main loop) | ||||
// because it didn't regard images, so we don't know | // because it didn't regard images, so we don't know | ||||
// if all characters processed, will actually be involved | // if all characters processed, will actually be involved | ||||
// in that line. | // in that line. | ||||
void CGUIText::ComputeLineSize( | void CGUIText::ComputeLineSize( | ||||
const CGUI& pGUI, | const CGUI& pGUI, | ||||
const CGUIString& string, | const CGUIString& string, | ||||
const CStrIntern& Font, | const CStrIntern& Font, | ||||
const bool noWrap, | |||||
const bool FirstLine, | const bool FirstLine, | ||||
const float Width, | const float Width, | ||||
const float width_range_to, | const float width_range_to, | ||||
const int i, | const int i, | ||||
const int temp_from, | const int temp_from, | ||||
float& x, | float& x, | ||||
CSize& line_size) const | CSize& line_size) const | ||||
{ | { | ||||
for (int j = temp_from; j <= i; ++j) | for (int j = temp_from; 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.cx; | x += Feedback2.m_Size.cx; | ||||
if (Width != 0 && x > width_range_to && j != temp_from && !Feedback2.m_NewLine) | if (Width != 0 && !noWrap && x > width_range_to && j != temp_from && !Feedback2.m_NewLine) | ||||
{ | { | ||||
// The calculated width of each word includes the space between the current | // 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 | // 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. | // space after the last word on the line before the wrap. | ||||
CFontMetrics currentFont(Font); | CFontMetrics currentFont(Font); | ||||
line_size.cx -= currentFont.GetCharacterWidth(*L" "); | line_size.cx -= currentFont.GetCharacterWidth(*L" "); | ||||
break; | break; | ||||
} | } | ||||
Show All 14 Lines | |||||
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, | ||||
const EAlign align, | const EAlign align, | ||||
const bool noWrap, | |||||
const float prelim_line_height, | const float prelim_line_height, | ||||
const float Width, | const float Width, | ||||
const float BufferZone, | const float BufferZone, | ||||
bool& FirstLine, | bool& FirstLine, | ||||
float& x, | float& x, | ||||
float& y, | float& y, | ||||
int& i, | int& i, | ||||
int& from) | int& from) | ||||
{ | { | ||||
// Change 'from' to 'i', but first keep a copy of its value. | // Change 'from' to 'i', but first keep a copy of its value. | ||||
int temp_from = from; | int temp_from = from; | ||||
from = i; | from = i; | ||||
float width_range_from = BufferZone; | float width_range_from = BufferZone; | ||||
// If we don't wrap, make sure we have enough space. | |||||
float width_range_to = Width - BufferZone; | float width_range_to = Width - BufferZone; | ||||
ComputeLineRange(Images, y, Width, prelim_line_height, width_range_from, width_range_to); | ComputeLineRange(Images, noWrap, y, Width, prelim_line_height, width_range_from, width_range_to); | ||||
// Reset X for the next loop | // Reset X for the next loop | ||||
x = width_range_from; | x = width_range_from; | ||||
CSize line_size; | CSize line_size; | ||||
ComputeLineSize(pGUI, string, Font, FirstLine, Width, width_range_to, i, temp_from, x, line_size); | ComputeLineSize(pGUI, string, Font, noWrap, FirstLine, Width, width_range_to, i, temp_from, x, line_size); | ||||
// Reset x once more | // Reset x once more | ||||
x = width_range_from; | x = width_range_from; | ||||
// Move down, because font drawing starts from the baseline | // Move down, because font drawing starts from the baseline. | ||||
y += line_size.cy; | y += line_size.cy; | ||||
const float dx = GetLineOffset(align, width_range_from, width_range_to, line_size); | const float dx = GetLineOffset(align, width_range_from, width_range_to, line_size); | ||||
// Do the real processing now | // 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); | const bool done = AssembleCalls(pGUI, string, Font, noWrap, pObject, FirstLine, Width, width_range_to, dx, y, temp_from, i, x, from); | ||||
// Reset X | // Reset X. | ||||
x = BufferZone; | x = BufferZone; | ||||
// Update dimensions | // Update dimensions. | ||||
m_Size.cx = std::max(m_Size.cx, line_size.cx + BufferZone * 2); | m_Size.cx = std::max(m_Size.cx, line_size.cx + BufferZone * 2); | ||||
m_Size.cy = std::max(m_Size.cy, y + BufferZone); | m_Size.cy = std::max(m_Size.cy, y + BufferZone); | ||||
FirstLine = false; | FirstLine = false; | ||||
// Now if we entered as from = i, then we want | // Now if we entered as from = i, then we want | ||||
// i being one minus that, so that it will become | // i being one minus that, so that it will become | ||||
// the same i in the next loop. The difference is that | // the same i in the next loop. The difference is that | ||||
// we're on a new line now. | // we're on a new line now. | ||||
i = from - 1; | i = from - 1; | ||||
return done; | return done; | ||||
} | } | ||||
// Decide width of the line. We need to iterate our floating images. | // 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 | // this won't be exact because we're assuming the line_size.cy | ||||
// will be as our preliminary calculation said. But that may change, | // 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 | // 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 | // 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 | // structures his text in a stylistically pure fashion. Even if not, it | ||||
// is still quite unlikely it will happen. | // is still quite unlikely it will happen. | ||||
// Loop through left and right side, from and to. | // Loop through left and right side, from and to. | ||||
void CGUIText::ComputeLineRange( | void CGUIText::ComputeLineRange( | ||||
const SGenerateTextImages& Images, | const SGenerateTextImages& Images, | ||||
const bool noWrap, | |||||
const float y, | const float y, | ||||
const float Width, | const float Width, | ||||
const float prelim_line_height, | const float prelim_line_height, | ||||
float& width_range_from, | float& width_range_from, | ||||
float& width_range_to) const | float& width_range_to) const | ||||
{ | { | ||||
// Floating images are only applicable if word-wrapping is enabled. | // Floating images are only applicable if word-wrapping is enabled. | ||||
if (Width == 0) | if (Width == 0 || noWrap) | ||||
return; | return; | ||||
for (int j = 0; j < 2; ++j) | for (int j = 0; j < 2; ++j) | ||||
for (const SGenerateTextImage& img : Images[j]) | for (const SGenerateTextImage& img : Images[j]) | ||||
{ | { | ||||
// We're working with two intervals here, the image's and the line height's. | // We're working with two intervals here, the image's and the line height's. | ||||
// let's find the union of these two. | // let's find the union of these two. | ||||
float union_from, union_to; | float union_from, union_to; | ||||
Show All 36 Lines | default: | ||||
return 0.f; | return 0.f; | ||||
} | } | ||||
} | } | ||||
bool CGUIText::AssembleCalls( | bool CGUIText::AssembleCalls( | ||||
const CGUI& pGUI, | const CGUI& pGUI, | ||||
const CGUIString& string, | const CGUIString& string, | ||||
const CStrIntern& Font, | const CStrIntern& Font, | ||||
const bool noWrap, | |||||
const IGUIObject* pObject, | const IGUIObject* pObject, | ||||
const bool FirstLine, | const bool FirstLine, | ||||
const float Width, | const float Width, | ||||
const float width_range_to, | const float width_range_to, | ||||
const float dx, | const float dx, | ||||
const float y, | const float y, | ||||
const int temp_from, | const int temp_from, | ||||
const int i, | const int i, | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | if (Width != 0) // only if word-wrapping is applicable | ||||
// 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. | ||||
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 > width_range_to && j == temp_from) | else if (!noWrap && x > width_range_to && j == temp_from) | ||||
{ | { | ||||
from = j+1; | from = j+1; | ||||
// do not break, since we want it to be added to m_TextCalls | // do not break, since we want it to be added to m_TextCalls | ||||
} | } | ||||
else if (x > width_range_to) | else if (!noWrap && x > width_range_to) | ||||
{ | { | ||||
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 58 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator
const bool noWrap.