1 /* Table.cpp
2 Copyright (c) 2014-2020 by Michael Zahniser
3
4 Endless Sky is free software: you can redistribute it and/or modify it under the
5 terms of the GNU General Public License as published by the Free Software
6 Foundation, either version 3 of the License, or (at your option) any later version.
7
8 Endless Sky is distributed in the hope that it will be useful, but WITHOUT ANY
9 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
10 PARTICULAR PURPOSE. See the GNU General Public License for more details.
11 */
12
13 #include "Table.h"
14
15 #include "DisplayText.h"
16 #include "../FillShader.h"
17 #include "Font.h"
18 #include "FontSet.h"
19 #include "Format.h"
20 #include "../Rectangle.h"
21
22 #include <algorithm>
23
24 using namespace std;
25
26
27
Table()28 Table::Table()
29 {
30 Clear();
31 }
32
33
34
35 // Set the column positions. If no columns are set, the Table will draw a
36 // list (one column of text, left aligned).
Clear()37 void Table::Clear()
38 {
39 columns.clear();
40
41 font = &FontSet::Get(14);
42 rowSize = Point(0., 20.);
43 center = Point(0., font->Height() / 2);
44 lineSize = Point(0., 1.);
45 lineOff = Point(0., font->Height() + 1);
46
47 point = Point();
48 it = columns.begin();
49 color = Color(1.f, 0.f);
50 }
51
52
53
AddColumn(int x,Layout layout)54 void Table::AddColumn(int x, Layout layout)
55 {
56 columns.emplace_back(x, layout);
57
58 // This may invalidate iterators, so:
59 it = columns.begin();
60 }
61
62
63
64 // Set the font size. Default is 14 pixels.
SetFontSize(int size)65 void Table::SetFontSize(int size)
66 {
67 font = &FontSet::Get(size);
68 lineOff.Y() = font->Height() + 1;
69 center.Y() = font->Height() / 2;
70 }
71
72
73
74 // Set the row height. Default is 20 pixels.
SetRowHeight(int height)75 void Table::SetRowHeight(int height) noexcept
76 {
77 rowSize.Y() = height;
78 }
79
80
81
82 // Set the width of the highlight area. If the underline has not been set,
83 // this will also set the width of the underline.
SetHighlight(int startX,int endX)84 void Table::SetHighlight(int startX, int endX) noexcept
85 {
86 rowSize.X() = endX - startX;
87 center.X() = (endX + startX) / 2;
88
89 if(!lineSize.X())
90 {
91 lineSize.X() = rowSize.X();
92 lineOff.X() = center.X();
93 }
94 }
95
96
97
98 // Set the X range of the underline. If the highlight has not been set, this
99 // will also set the width of the highlight.
SetUnderline(int startX,int endX)100 void Table::SetUnderline(int startX, int endX) noexcept
101 {
102 lineSize.X() = endX - startX;
103 lineOff.X() = (endX + startX) / 2;
104
105 if(!rowSize.X())
106 {
107 rowSize.X() = lineSize.X();
108 center.X() = lineOff.X();
109 }
110 }
111
112
113
114 // Begin drawing at the given position. Each time text is drawn, it fills a
115 // new column until all columns have been filled. Then, the Y position is
116 // increased based on the row height, and a new row begins.
DrawAt(const Point & point) const117 void Table::DrawAt(const Point &point) const
118 {
119 this->point = point + Point(0., (rowSize.Y() - font->Height()) / 2);
120 it = columns.begin();
121 }
122
123
124
125 // Set the color for drawing text and underlines.
SetColor(const Color & color) const126 void Table::SetColor(const Color &color) const
127 {
128 this->color = color;
129 }
130
131
132
133 // Advance to the next field without drawing anything.
Advance(int fields) const134 void Table::Advance(int fields) const
135 {
136 while(fields-- > 0)
137 {
138 if(columns.empty() || ++it == columns.end())
139 {
140 it = columns.begin();
141 point.Y() += rowSize.Y();
142 }
143 }
144 }
145
146
147
148 // Draw a single text field, and move on to the next one.
Draw(const char * text) const149 void Table::Draw(const char *text) const
150 {
151 Draw(text, nullptr, color);
152 }
153
154
155
Draw(const string & text) const156 void Table::Draw(const string &text) const
157 {
158 Draw(text, nullptr, color);
159 }
160
161
162
163 // If a color is given, this field is drawn using that color, but the
164 // previously set color will be used for future fields.
Draw(const char * text,const Color & color) const165 void Table::Draw(const char *text, const Color &color) const
166 {
167 Draw(text, nullptr, color);
168 }
169
170
171
Draw(const string & text,const Color & color) const172 void Table::Draw(const string &text, const Color &color) const
173 {
174 Draw(text, nullptr, color);
175 }
176
177
178
Draw(double value) const179 void Table::Draw(double value) const
180 {
181 Draw(Format::Number(value), nullptr, color);
182 }
183
184
185
Draw(double value,const Color & color) const186 void Table::Draw(double value, const Color &color) const
187 {
188 Draw(Format::Number(value), nullptr, color);
189 }
190
191
192
DrawCustom(const DisplayText & text) const193 void Table::DrawCustom(const DisplayText &text) const
194 {
195 Draw(text.GetText(), &text.GetLayout(), color);
196 }
197
198
199
DrawCustom(const DisplayText & text,const Color & color) const200 void Table::DrawCustom(const DisplayText &text, const Color &color) const
201 {
202 Draw(text.GetText(), &text.GetLayout(), color);
203 }
204
205
206
DrawTruncatedPair(const string & left,const Color & leftColor,const string & right,const Color & rightColor,Truncate strategy,bool truncateRightColumn) const207 void Table::DrawTruncatedPair(const string &left, const Color &leftColor, const string &right, const Color &rightColor,
208 Truncate strategy, bool truncateRightColumn) const
209 {
210 // Compute the width of the non-truncated string, and the margin we have for the possibly-large text.
211 const auto colWidth = it->layout.width;
212 const auto textWidth = font->FormattedWidth({truncateRightColumn ? left : right, {colWidth}});
213 constexpr auto PAD = 5;
214 const auto remainder = max(colWidth - PAD - textWidth, 0);
215
216 auto lhs = Layout(truncateRightColumn ? colWidth : remainder, Alignment::LEFT, strategy);
217 auto rhs = Layout(truncateRightColumn ? remainder : colWidth, Alignment::RIGHT, strategy);
218 if(truncateRightColumn)
219 lhs.truncate = Truncate::NONE;
220 else
221 rhs.truncate = Truncate::NONE;
222 Draw(left, &lhs, leftColor);
223 Draw(right, &rhs, rightColor);
224 }
225
226
227
228 // Draw an underline under the text for the current row.
DrawUnderline() const229 void Table::DrawUnderline() const
230 {
231 DrawUnderline(color);
232 }
233
234
235
DrawUnderline(const Color & color) const236 void Table::DrawUnderline(const Color &color) const
237 {
238 FillShader::Fill(point + lineOff - Point(0., 2.), lineSize, color);
239 }
240
241
242
243 // Highlight the current row.
DrawHighlight() const244 void Table::DrawHighlight() const
245 {
246 DrawHighlight(color);
247 }
248
249
250
DrawHighlight(const Color & color) const251 void Table::DrawHighlight(const Color &color) const
252 {
253 FillShader::Fill(GetCenterPoint(), GetRowSize(), color);
254 }
255
256
257
258 // Shift the draw position down by the given amount. This usually should not
259 // be called in the middle of a row, or the fields will not line up.
DrawGap(int y) const260 void Table::DrawGap(int y) const
261 {
262 point.Y() += y;
263 }
264
265
266
267 // Get the point that should be passed to DrawAt() to start the next row at
268 // the given location.
GetPoint() const269 Point Table::GetPoint() const
270 {
271 return point - Point(0., (rowSize.Y() - font->Height()) / 2);
272 }
273
274
275
276 // Get the center and size of the current row. This can be used to define
277 // what screen region constitutes a mouse click on this particular row.
GetCenterPoint() const278 Point Table::GetCenterPoint() const
279 {
280 return point + center;
281 }
282
283
284
GetRowSize() const285 Point Table::GetRowSize() const
286 {
287 return rowSize;
288 }
289
290
291
GetRowBounds() const292 Rectangle Table::GetRowBounds() const
293 {
294 return Rectangle(GetCenterPoint(), GetRowSize());
295 }
296
297
298
Column(double offset,Layout layout)299 Table::Column::Column(double offset, Layout layout) noexcept
300 : offset(offset), layout(layout)
301 {
302 }
303
304
305
Draw(const string & text,const Layout * special,const Color & color) const306 void Table::Draw(const string &text, const Layout *special, const Color &color) const
307 {
308 if(font && !columns.empty())
309 {
310 const auto &layout = special ? *special : it->layout;
311 const double alignmentOffset = layout.align == Alignment::RIGHT ? -1.
312 : layout.align == Alignment::CENTER ? -0.5 : 0.;
313 auto pos = point + Point(it->offset + alignmentOffset * (layout.width >= 0 ? layout.width : font->Width(text)), 0.);
314 font->Draw({text, layout}, pos, color);
315 }
316
317 Advance();
318 }
319