| 55ea55dd |
sago007 |
2018-03-24 16:07 |
1 |
/* |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
2 |
Copyright (c) 2018 Poul Sander |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
3 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
4 |
Permission is hereby granted, free of charge, to any person |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
5 |
obtaining a copy of this software and associated documentation files |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
6 |
(the "Software"), to deal in the Software without restriction, |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
7 |
including without limitation the rights to use, copy, modify, merge, |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
8 |
publish, distribute, sublicense, and/or sell copies of the Software, |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
9 |
and to permit persons to whom the Software is furnished to do so, |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
10 |
subject to the following conditions: |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
11 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
12 |
The above copyright notice and this permission notice shall be |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
13 |
included in all copies or substantial portions of the Software. |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
14 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
15 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
16 |
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
17 |
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
18 |
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
19 |
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
20 |
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
21 |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
22 |
SOFTWARE. |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
23 |
*/ |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
24 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
25 |
#include "SagoTextBox.hpp" |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
26 |
#include "SagoTextField.hpp" |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
27 |
#include <vector> |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
28 |
#include <iostream> |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
29 |
#include "utf8.h" |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
30 |
#include <algorithm> |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
31 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
32 |
namespace sago { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
33 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
34 |
struct SagoTextBox::SagoTextBoxData { |
| 1a09fcd5 |
sago007 |
2018-03-25 13:32 |
35 |
const sago::SagoDataHolder* tex = nullptr; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
36 |
std::string fontName = "freeserif"; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
37 |
SDL_Color color = { 255, 255, 255, 0 }; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
38 |
SDL_Color outlineColor = { 255, 255, 0, 0 }; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
39 |
int fontSize = 16; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
40 |
int outline = 0; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
41 |
std::string text = ""; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
42 |
std::string renderedText = ""; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
43 |
std::vector<SagoTextField> lines; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
44 |
int maxWidth = 0; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
45 |
}; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
46 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
47 |
SagoTextBox::SagoTextBox() { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
48 |
data = new SagoTextBoxData(); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
49 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
50 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
51 |
SagoTextBox::~SagoTextBox() { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
52 |
delete data; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
53 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
54 |
|
| 1a09fcd5 |
sago007 |
2018-03-25 13:32 |
55 |
void SagoTextBox::SetHolder(const SagoDataHolder* holder) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
56 |
data->tex = holder; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
57 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
58 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
59 |
void SagoTextBox::SetText(const char* text) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
60 |
data->text = text; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
61 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
62 |
|
| 0b8fb137 |
sago007 |
2018-03-31 19:53 |
63 |
void SagoTextBox::SetText(const std::string& text) { |
| 0b8fb137 |
sago007 |
2018-03-31 19:53 |
64 |
data->text = text; |
| 0b8fb137 |
sago007 |
2018-03-31 19:53 |
65 |
} |
| 0b8fb137 |
sago007 |
2018-03-31 19:53 |
66 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
67 |
void SagoTextBox::SetColor(const SDL_Color& color) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
68 |
data->color = color; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
69 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
70 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
71 |
void SagoTextBox::SetFont(const char* fontName) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
72 |
data->fontName = fontName; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
73 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
74 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
75 |
void SagoTextBox::SetFontSize(int fontSize) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
76 |
data->fontSize = fontSize; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
77 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
78 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
79 |
void SagoTextBox::SetOutline(int outlineSize, const SDL_Color& color) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
80 |
data->outline = outlineSize; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
81 |
data->outlineColor = color; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
82 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
83 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
84 |
void SagoTextBox::SetMaxWidth(int width) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
85 |
data->maxWidth = width; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
86 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
87 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
88 |
const std::string& SagoTextBox::GetText() const { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
89 |
return data->text; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
90 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
91 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
92 |
void SagoTextBox::AppendLineToCache(const std::string& text) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
93 |
data->lines.resize(data->lines.size()+1); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
94 |
SagoTextField& tf = data->lines.back(); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
95 |
tf.SetHolder(data->tex); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
96 |
tf.SetFont(data->fontName.c_str()); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
97 |
tf.SetFontSize(data->fontSize); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
98 |
tf.SetColor(data->color); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
99 |
tf.SetOutline(data->outline, data->outlineColor); |
| 0b8fb137 |
sago007 |
2018-03-31 19:53 |
100 |
tf.SetText(text); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
101 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
102 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
103 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
104 |
void SagoTextBox::SplitAndAppendLineToCache(TTF_Font* font, const std::string& text) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
105 |
int width = data->maxWidth; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
106 |
TTF_SizeUTF8(font, text.c_str(),&width, nullptr); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
107 |
if (data->maxWidth <= 0 || width <= data->maxWidth || text.length() == 1) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
108 |
AppendLineToCache(text); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
109 |
return; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
110 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
111 |
std::string::const_iterator splitLocation = text.begin()+1; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
112 |
bool splitDone = false; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
113 |
while (!splitDone) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
114 |
std::string::const_iterator nextSearchStart = splitLocation+1; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
115 |
if (nextSearchStart == text.end()) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
116 |
splitDone = true; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
117 |
continue; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
118 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
119 |
std::string::const_iterator nextSpace = std::find(nextSearchStart, text.end(), ' '); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
120 |
std::string attemptSubString(text.begin(), nextSpace); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
121 |
TTF_SizeUTF8(font, attemptSubString.c_str(),&width, nullptr); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
122 |
if (width <= data->maxWidth && nextSpace != text.end()) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
123 |
splitLocation = nextSpace; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
124 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
125 |
else { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
126 |
splitDone = true; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
127 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
128 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
129 |
if (splitLocation == text.begin()+1) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
130 |
splitLocation = text.begin(); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
131 |
utf8::advance(splitLocation, 1, text.end()); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
132 |
splitDone = false; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
133 |
while (!splitDone && splitLocation != text.end()) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
134 |
std::string::const_iterator nextSplit = splitLocation; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
135 |
utf8::advance(nextSplit, 1, text.end()); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
136 |
std::string attemptSubString(text.begin(), nextSplit); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
137 |
TTF_SizeUTF8(font, attemptSubString.c_str(), &width, nullptr); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
138 |
if (width <= data->maxWidth) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
139 |
splitLocation = nextSplit; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
140 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
141 |
else { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
142 |
splitDone = true; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
143 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
144 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
145 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
146 |
std::string firstPart(text.begin(), splitLocation); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
147 |
AppendLineToCache(firstPart); |
| 1a09fcd5 |
sago007 |
2018-03-25 13:32 |
148 |
while (splitLocation != text.end() && *splitLocation == ' ') { |
| 1a09fcd5 |
sago007 |
2018-03-25 13:32 |
149 |
//Trim spaces after an automatic line break. |
| 1a09fcd5 |
sago007 |
2018-03-25 13:32 |
150 |
++splitLocation; |
| 1a09fcd5 |
sago007 |
2018-03-25 13:32 |
151 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
152 |
if (splitLocation == text.end()) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
153 |
return; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
154 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
155 |
std::string secondPart(splitLocation, text.end()); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
156 |
SplitAndAppendLineToCache(font, secondPart); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
157 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
158 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
159 |
void SagoTextBox::UpdateCache() { |
| d8fe60bd |
sago007 |
2018-04-02 16:15 |
160 |
if (!data->tex) { |
| d8fe60bd |
sago007 |
2018-04-02 16:15 |
161 |
std::cerr << "FATAL: SagoTextBox::UpdateCache - DataHolder not set!\n"; |
| d8fe60bd |
sago007 |
2018-04-02 16:15 |
162 |
abort(); |
| d8fe60bd |
sago007 |
2018-04-02 16:15 |
163 |
} |
| e94af580 |
sago007 |
2020-12-30 17:45 |
164 |
TTF_Font* font = data->tex->getFontPtr(data->fontName, data->fontSize); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
165 |
const char delim = '\n'; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
166 |
const std::string& s = data->text; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
167 |
auto start = 0U; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
168 |
auto end = s.find(delim); |
| 1a09fcd5 |
sago007 |
2018-03-25 13:32 |
169 |
data->lines.clear(); |
| e94af580 |
sago007 |
2020-12-30 17:45 |
170 |
while (end != std::string::npos) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
171 |
const std::string& theSubString = s.substr(start, end - start); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
172 |
SplitAndAppendLineToCache(font, theSubString); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
173 |
start = end + 1; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
174 |
end = s.find(delim, start); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
175 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
176 |
SplitAndAppendLineToCache(font, s.substr(start, end)); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
177 |
data->renderedText = data->text; |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
178 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
179 |
|
| 4b21c89c |
Poul Sander |
2025-03-04 20:33 |
180 |
void SagoTextBox::Draw(SDL_Renderer* target, int x, int y, SagoTextField::Alignment alignment, SagoLogicalResize* resize ) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
181 |
if (data->text != data->renderedText) { |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
182 |
UpdateCache(); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
183 |
} |
| e94af580 |
sago007 |
2020-12-30 17:45 |
184 |
TTF_Font* font = data->tex->getFontPtr(data->fontName, data->fontSize); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
185 |
int lineSkip = TTF_FontLineSkip(font); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
186 |
for (size_t i = 0; i < data->lines.size(); ++i) { |
| 4b21c89c |
Poul Sander |
2025-03-04 20:33 |
187 |
data->lines[i].Draw(target, x, y+i*lineSkip, alignment, sago::SagoTextField::VerticalAlignment::top, resize); |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
188 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
189 |
} |
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
190 |
|
| 55ea55dd |
sago007 |
2018-03-24 16:07 |
191 |
} //namespace sago |