lxgui
gui_font_string.cpp
1 #include "lxgui/gui_font_string.hpp"
2 
3 #include "lxgui/gui_layered_region.hpp"
4 #include "lxgui/gui_localizer.hpp"
5 #include "lxgui/gui_manager.hpp"
6 #include "lxgui/gui_out.hpp"
7 #include "lxgui/gui_region_tpl.hpp"
8 #include "lxgui/gui_renderer.hpp"
9 
10 #include <sstream>
11 
12 namespace lxgui::gui {
13 
15  utils::control_block& block, manager& mgr, const region_core_attributes& attr) :
16  layered_region(block, mgr, attr) {
17 
18  initialize_(*this, attr);
19 
20  // Render font_string above other regions
21  region_level_ = 1;
22 }
23 
24 void font_string::render() const {
25  base::render();
26 
27  if (!text_ || !is_valid_ || !is_visible())
28  return;
29 
30  text_->set_use_vertex_cache(is_vertex_cache_used_());
31 
32  vector2f pos;
33 
34  if (std::isinf(text_->get_box_width())) {
35  switch (align_x_) {
36  case alignment_x::left: pos.x = borders_.left; break;
37  case alignment_x::center: pos.x = (borders_.left + borders_.right) / 2; break;
38  case alignment_x::right: pos.x = borders_.right; break;
39  }
40  } else {
41  pos.x = borders_.left;
42  }
43 
44  if (std::isinf(text_->get_box_height())) {
45  switch (align_y_) {
46  case alignment_y::top: pos.y = borders_.top; break;
47  case alignment_y::middle: pos.y = (borders_.top + borders_.bottom) / 2; break;
48  case alignment_y::bottom: pos.y = borders_.bottom; break;
49  }
50  } else {
51  pos.y = borders_.top;
52  }
53 
54  pos += offset_;
55 
56  text_->set_alpha(get_effective_alpha());
57 
58  if (is_shadow_enabled_) {
59  text_->set_color(shadow_color_, true);
60  text_->render(matrix4f::translation(round_to_pixel(pos + shadow_offset_)));
61  }
62 
63  text_->set_color(text_color_);
64  text_->render(matrix4f::translation(round_to_pixel(pos)));
65 }
66 
67 std::string font_string::serialize(const std::string& tab) const {
68  std::ostringstream str;
69 
70  str << base::serialize(tab);
71 
72  str << tab << " # Font name : " << font_name_ << "\n";
73  str << tab << " # Font height: " << height_ << "\n";
74  str << tab << " # Text ready : " << (text_ != nullptr) << "\n";
75  str << tab << " # Text : \"" << utils::unicode_to_utf8(content_) << "\"\n";
76  str << tab << " # Outlined : " << is_outlined_ << "\n";
77  str << tab << " # Text color : " << text_color_ << "\n";
78  str << tab << " # Spacing : " << spacing_ << "\n";
79  str << tab << " # Justify :\n";
80  str << tab << " #-###\n";
81  str << tab << " | # horizontal: " << utils::to_string(align_x_) << "\n";
82  str << tab << " | # vertical : " << utils::to_string(align_y_) << "\n";
83  str << tab << " #-###\n";
84  str << tab << " # NonSpaceW. : " << non_space_wrap_enabled_ << "\n";
85  if (is_shadow_enabled_) {
86  str << tab << " # Shadow off.: (" << shadow_offset_.x << ", " << shadow_offset_.y << ")\n";
87  str << tab << " # Shadow col.: " << shadow_color_ << "\n";
88  }
89 
90  return str.str();
91 }
92 
93 void font_string::copy_from(const region& obj) {
94  base::copy_from(obj);
95 
96  const font_string* fstr_obj = down_cast<font_string>(&obj);
97  if (!fstr_obj)
98  return;
99 
100  std::string font_name = fstr_obj->get_font_name();
101  float height = fstr_obj->get_font_height();
102  if (!font_name.empty() && height != 0)
103  this->set_font(font_name, height);
104 
105  this->set_alignment_x(fstr_obj->get_alignment_x());
106  this->set_alignment_y(fstr_obj->get_alignment_y());
107  this->set_spacing(fstr_obj->get_spacing());
108  this->set_line_spacing(fstr_obj->get_line_spacing());
109  this->set_text(fstr_obj->get_text());
110  this->set_outlined(fstr_obj->is_outlined());
111  if (fstr_obj->is_shadow_enabled()) {
112  this->set_shadow_enabled(true);
113  this->set_shadow_color(fstr_obj->get_shadow_color());
114  this->set_shadow_offset(fstr_obj->get_shadow_offset());
115  }
116  this->set_text_color(fstr_obj->get_text_color());
118 }
119 
120 const std::string& font_string::get_font_name() const {
121  return font_name_;
122 }
123 
125  return height_;
126 }
127 
128 void font_string::set_outlined(bool is_outlined) {
129  if (is_outlined_ == is_outlined)
130  return;
131 
132  is_outlined_ = is_outlined;
133 
134  if (!is_virtual_) {
135  create_text_object_();
136 
138  }
139 }
140 
142  return is_outlined_;
143 }
144 
146  return align_x_;
147 }
148 
150  return align_y_;
151 }
152 
154  return shadow_color_;
155 }
156 
158  return shadow_offset_;
159 }
160 
162  return offset_;
163 }
164 
166  return spacing_;
167 }
168 
170  return line_spacing_;
171 }
172 
174  return text_color_;
175 }
176 
179 
180  if (text_)
181  set_font(font_name_, height_);
182 }
183 
184 void font_string::create_text_object_() {
185  if (font_name_.empty())
186  return;
187 
188  std::size_t pixel_height = static_cast<std::size_t>(
189  std::round(get_manager().get_interface_scaling_factor() * height_));
190 
191  auto& renderer = get_manager().get_renderer();
192  const auto& localizer = get_manager().get_localizer();
193 
194  const auto& code_points = localizer.get_allowed_code_points();
195  const char32_t default_code_point = localizer.get_fallback_code_point();
196 
197  std::shared_ptr<gui::font> outline_font;
198  if (is_outlined_) {
199  outline_font = renderer.create_atlas_font(
200  "GUI", font_name_, pixel_height,
201  std::min<std::size_t>(2u, static_cast<std::size_t>(std::round(0.2 * pixel_height))),
202  code_points, default_code_point);
203  }
204 
205  auto fnt = renderer.create_atlas_font(
206  "GUI", font_name_, pixel_height, 0u, code_points, default_code_point);
207 
208  text_ = std::unique_ptr<text>(new text(renderer, fnt, outline_font));
209 
210  text_->set_scaling_factor(1.0f / get_manager().get_interface_scaling_factor());
211  text_->set_remove_starting_spaces(true);
212  text_->set_text(content_);
213  text_->set_alignment_x(align_x_);
214  text_->set_alignment_y(align_y_);
215  text_->set_tracking(spacing_);
216  text_->set_word_wrap_enabled(word_wrap_enabled_);
217  text_->set_word_ellipsis_enabled(ellipsis_enabled_);
218  text_->set_formatting_enabled(formatting_enabled_);
219 }
220 
221 void font_string::set_font(const std::string& font_name, float height) {
222  font_name_ = parse_file_name(font_name);
223  height_ = height;
224 
225  create_text_object_();
226 
227  if (!is_virtual_) {
230  }
231 }
232 
234  if (align_x_ == justify_h)
235  return;
236 
237  align_x_ = justify_h;
238  if (text_) {
239  text_->set_alignment_x(align_x_);
240 
241  if (!is_virtual_)
243  }
244 }
245 
247  if (align_y_ == justify_v)
248  return;
249 
250  align_y_ = justify_v;
251  if (text_) {
252  text_->set_alignment_y(align_y_);
253 
254  if (!is_virtual_)
256  }
257 }
258 
259 void font_string::set_shadow_color(const color& shadow_color) {
260  if (shadow_color_ == shadow_color)
261  return;
262 
263  shadow_color_ = shadow_color;
264  if (is_shadow_enabled_ && !is_virtual_)
266 }
267 
268 void font_string::set_shadow_offset(const vector2f& shadow_offset) {
269  if (shadow_offset_ == shadow_offset)
270  return;
271 
272  shadow_offset_ = shadow_offset;
273  if (is_shadow_enabled_ && !is_virtual_)
275 }
276 
277 void font_string::set_offset(const vector2f& offset) {
278  if (offset_ == offset)
279  return;
280 
281  offset_ = offset;
282  if (!is_virtual_)
284 }
285 
286 void font_string::set_spacing(float spacing) {
287  if (spacing_ == spacing)
288  return;
289 
290  spacing_ = spacing;
291  if (text_) {
292  text_->set_tracking(spacing_);
293  if (!is_virtual_)
295  }
296 }
297 
298 void font_string::set_line_spacing(float line_spacing) {
299  if (line_spacing_ == line_spacing)
300  return;
301 
302  line_spacing_ = line_spacing;
303  if (text_) {
304  text_->set_line_spacing(line_spacing_);
305  if (!is_virtual_)
307  }
308 }
309 
310 void font_string::set_text_color(const color& text_color) {
311  if (text_color_ == text_color)
312  return;
313 
314  text_color_ = text_color;
315  if (!is_virtual_)
317 }
318 
320  return non_space_wrap_enabled_;
321 }
322 
324  if (text_)
325  return text_->get_text_height();
326  else
327  return 0.0f;
328 }
329 
331  if (text_)
332  return text_->get_text_width();
333  else
334  return 0.0f;
335 }
336 
337 float font_string::get_string_width(const utils::ustring& content) const {
338  if (text_)
339  return text_->get_string_width(content);
340  else
341  return 0.0f;
342 }
343 
344 const utils::ustring& font_string::get_text() const {
345  return content_;
346 }
347 
348 void font_string::set_non_space_wrap_enabled(bool is_non_space_wrap_enabled) {
349  if (non_space_wrap_enabled_ == is_non_space_wrap_enabled)
350  return;
351 
352  non_space_wrap_enabled_ = is_non_space_wrap_enabled;
353 
354  if (!is_virtual_)
356 }
357 
359  return is_shadow_enabled_;
360 }
361 
362 void font_string::set_shadow_enabled(bool is_shadow_enabled) {
363  if (is_shadow_enabled_ == is_shadow_enabled)
364  return;
365 
366  is_shadow_enabled_ = is_shadow_enabled;
367 
368  if (!is_virtual_)
370 }
371 
373  if (word_wrap_enabled_ == enabled)
374  return;
375 
376  word_wrap_enabled_ = enabled;
377 
378  if (text_) {
379  text_->set_word_wrap_enabled(word_wrap_enabled_);
380  if (!is_virtual_)
382  }
383 }
384 
386  return word_wrap_enabled_;
387 }
388 
390  if (ellipsis_enabled_ == enabled)
391  return;
392 
393  ellipsis_enabled_ = enabled;
394 
395  if (text_) {
396  text_->set_word_ellipsis_enabled(ellipsis_enabled_);
397  if (!is_virtual_)
399  }
400 }
401 
403  return ellipsis_enabled_;
404 }
405 
406 void font_string::set_formatting_enabled(bool formatting) {
407  if (formatting_enabled_ == formatting)
408  return;
409 
410  formatting_enabled_ = formatting;
411 
412  if (text_) {
413  text_->set_formatting_enabled(formatting_enabled_);
414  if (!is_virtual_)
416  }
417 }
418 
420  return formatting_enabled_;
421 }
422 
423 void font_string::set_text(const utils::ustring& content) {
424  if (content_ == content)
425  return;
426 
427  content_ = content;
428 
429  if (text_) {
430  text_->set_text(content_);
431  if (!is_virtual_) {
434  }
435  }
436 }
437 
438 bool font_string::is_vertex_cache_used_() const {
439  auto& renderer = get_manager().get_renderer();
440  switch (vertex_cache_strategy_) {
441  case vertex_cache_strategy::always_disabled: return false;
443  case vertex_cache_strategy::always_enabled: return true;
446  default: return false;
447  }
448 }
449 
451  vertex_cache_strategy_ = strategy;
452 }
453 
455  return vertex_cache_strategy_;
456 }
457 
459  return text_.get();
460 }
461 
463  return text_.get();
464 }
465 
466 void font_string::update_borders_() {
467  if (!text_)
468  return base::update_borders_();
469 
470 //#define DEBUG_LOG(msg) gui::out << (msg) << std::endl
471 #define DEBUG_LOG(msg)
472 
473  const bool old_valid = is_valid_;
474  const auto old_border_list = borders_;
475  is_valid_ = true;
476 
477  if (!anchor_list_.empty()) {
478  float left = 0.0f, right = 0.0f, top = 0.0f, bottom = 0.0f;
479  float x_center = 0.0f, y_center = 0.0f;
480 
481  DEBUG_LOG(" Read anchors");
482  read_anchors_(left, right, top, bottom, x_center, y_center);
483 
484  float box_width = std::numeric_limits<float>::infinity();
485  if (get_dimensions().x != 0.0f)
486  box_width = get_dimensions().x;
488  box_width = right - left;
489 
490  float box_height = std::numeric_limits<float>::infinity();
491  if (get_dimensions().y != 0.0f)
492  box_height = get_dimensions().y;
494  box_height = bottom - top;
495 
498 
499  text_->set_box_dimensions(box_width, box_height);
500 
501  DEBUG_LOG(" Make borders");
502  if (std::isinf(box_height))
503  box_height = text_->get_height();
504  if (std::isinf(box_width))
505  box_width = text_->get_width();
506 
507  if (!make_borders_(top, bottom, y_center, box_height)) {
508  is_valid_ = false;
509  }
510 
511  if (!make_borders_(left, right, x_center, box_width)) {
512  is_valid_ = false;
513  }
514 
515  if (is_valid_) {
516  if (right < left) {
517  right = left + 1.0f;
518  }
519  if (bottom < top) {
520  bottom = top + 1.0f;
521  }
522 
523  borders_.left = left;
524  borders_.right = right;
525  borders_.top = top;
527  } else {
529  }
530  } else {
531  float box_width = get_dimensions().x;
532  if (box_width == 0.0) {
533  box_width = text_->get_width();
534  }
535 
536  float box_height = get_dimensions().y;
537  if (box_height == 0.0) {
538  box_height = text_->get_height();
539  }
540 
541  borders_ = bounds2f(0.0, 0.0, box_width, box_height);
542  is_valid_ = false;
543  }
544 
549 
550  if (borders_ != old_border_list || is_valid_ != old_valid) {
551  DEBUG_LOG(" Fire redraw");
553  }
554  DEBUG_LOG(" @");
555 }
556 
557 const std::vector<std::string>& font_string::get_type_list_() const {
558  return get_type_list_impl_<font_string>();
559 }
560 
561 } // namespace lxgui::gui
Holds a single color (float RGBA, 128 bits)
Definition: gui_color.hpp:12
A layered_region that can draw text on the screen.
const color & get_text_color() const
Returns the text color.
void set_word_wrap_enabled(bool enabled)
Enables/disables word wrap.
void set_non_space_wrap_enabled(bool enabled)
Sets whether large text without whitespace is truncated or wrapped.
const std::string & get_font_name() const
Returns the name of the font file.
const vector2f & get_offset() const
Returns this font_string's offset.
void set_formatting_enabled(bool enabled)
Enables color formatting.
const color & get_shadow_color() const
Returns this font_string's shadow color.
float get_string_width() const
Returns the width of the string if no format or wrapping is applied.
bool is_word_ellipsis_enabled() const
Checks if word ellipsis is enabled.
void set_shadow_color(const color &shadow_color)
Sets this font_string's shadow color.
void copy_from(const region &obj) override
Copies a region's parameters into this font_string (inheritance).
bool is_non_space_wrap_enabled() const
Checks if large text is truncated or wrapped.
float get_string_height() const
Returns the height of the string if no format or wrapping is applied.
const vector2f & get_shadow_offset() const
Returns this font_string's shadow offset.
void set_line_spacing(float line_spacing)
Sets the space between each line as a fraction of the font height.
std::string serialize(const std::string &tab) const override
Prints all relevant information about this region in a string.
void set_alignment_x(alignment_x align_x)
Sets this font_string's horizontal alignment behavior.
bool is_formatting_enabled() const
Checks if color formatting is enabled.
float get_font_height() const
Returns the height of the font.
void render() const override
Renders this region on the current render target.
void set_vertex_cache_strategy(vertex_cache_strategy strategy)
Selects the strategy for using vertex caches.
void set_alignment_y(alignment_y align_y)
Sets this font_string's vertical alignment behavior.
void notify_scaling_factor_updated() override
Tells this region that the global interface scaling factor has changed.
text * get_text_object()
Returns the text used to render this font_string.
void set_offset(const vector2f &offset)
Sets this font_string's offset.
void set_outlined(bool outlined)
Adds or remove the outline around the text.
void set_shadow_offset(const vector2f &shadow_offset)
Sets this font_string's shadow offset.
bool is_outlined() const
Check if this font_string is outlined.
void set_text(const utils::ustring &content)
Sets the rendered text.
float get_line_spacing() const
Returns the space between each line as a fraction of the font height.
bool is_word_wrap_enabled() const
Checks if word wrap is enabled.
font_string(utils::control_block &block, manager &mgr, const region_core_attributes &attr)
Constructor.
void set_text_color(const color &text_color)
Sets the text color.
alignment_y get_alignment_y() const
Returns the vertical alignment behavior.
void set_shadow_enabled(bool enabled)
Sets whether this font_string should draw a shadow under its text.
vertex_cache_strategy get_vertex_cache_strategy() const
Gets the strategy for using vertex caches.
bool is_shadow_enabled() const
Checks if this font_string draws a shadow under its text.
void set_word_ellipsis_enabled(bool enabled)
Sets whether to show an ellipsis "..." if words don't fit in the text box.
float get_spacing() const
Returns the space between each letter.
void set_font(const std::string &font_name, float height)
Sets this font_string's font (file and size).
const utils::ustring & get_text() const
Returns the rendered text (with format tags).
void set_spacing(float spacing)
Sets the space between each letter.
alignment_x get_alignment_x() const
Returns the horizontal alignment behavior.
A region that can be rendered in a layer.
void notify_renderer_need_redraw() override
Notifies the renderer of this region that it needs to be redrawn.
std::string serialize(const std::string &tab) const override
Prints all relevant information about this region in a string.
Utility class to translate strings for display in GUI.
char32_t get_fallback_code_point() const
Returns the default character to display if a character is missing from a font.
const std::vector< code_point_range > & get_allowed_code_points() const
Returns the list of allowed code points (Unicode characters), for text rendering.
Manages the user interface.
Definition: gui_manager.hpp:44
localizer & get_localizer()
Returns the object used for localizing strings.
const renderer & get_renderer() const
Returns the renderer implementation.
The base class of all elements in the GUI.
Definition: gui_region.hpp:161
void initialize_(T &self, const region_core_attributes &attr)
Set up function to call in all derived class constructors.
float get_effective_alpha() const
Returns this region's effective alpha (opacity).
Definition: gui_region.cpp:161
bool is_visible() const
Checks if this region can be seen on the screen.
Definition: gui_region.cpp:207
manager & get_manager()
Returns this region's manager.
Definition: gui_region.hpp:693
float round_to_pixel(float value, utils::rounding_method method=utils::rounding_method::nearest) const
Round an absolute position on screen to the nearest physical pixel.
Definition: gui_region.cpp:573
void read_anchors_(float &left, float &right, float &top, float &bottom, float &x_center, float &y_center) const
Definition: gui_region.cpp:614
virtual void render() const
Renders this region on the current render target.
Definition: gui_region.cpp:765
const vector2f & get_dimensions() const
Returns this region's explicitly-defined width and height (in pixels).
Definition: gui_region.cpp:272
std::array< std::optional< anchor >, 9 > anchor_list_
Definition: gui_region.hpp:814
virtual void notify_borders_need_update()
Tells this region that its borders need updating.
Definition: gui_region.cpp:744
bounds2< bool > defined_borders_
Definition: gui_region.hpp:815
bool make_borders_(float &min, float &max, float center, float size) const
Definition: gui_region.cpp:585
virtual void copy_from(const region &obj)
Copies a region's parameters into this region (inheritance).
Definition: gui_region.cpp:128
std::string parse_file_name(const std::string &file_name) const
Convert an addon-relative file path to a application-relative path.
Definition: gui_region.cpp:818
virtual void notify_scaling_factor_updated()
Tells this region that the global interface scaling factor has changed.
Definition: gui_region.cpp:759
virtual void update_borders_()
Definition: gui_region.cpp:669
Abstract type for implementation specific management.
bool is_quad_batching_enabled() const
Checks if the renderer has quad render batching enabled.
std::shared_ptr< font > create_atlas_font(const std::string &atlas_category, const std::string &font_file, std::size_t size, std::size_t outline, const std::vector< code_point_range > &code_points, char32_t default_code_point)
Creates a new font.
bool is_vertex_cache_enabled() const
Checks if the renderer has enabled support for vertex caches.
Used to draw some text on the screen.
Definition: gui_text.hpp:29
bounds2< float > bounds2f
Holds 2D bounds of a region (as floats).
vertex_cache_strategy
Strategy for using a vertex cache.
@ prefer_enabled
Use vertex cache if renderer supports and allows.
@ automatic
Choose automatically to maximize performance on common case.
@ always_disabled
Never use vertex cache.
@ always_enabled
Use vertex cache if renderer supports, even if not allowed.
@ nearest_not_zero
Equivalent to round() but only returns 0 if input is exactly 0.
static const bounds2 zero
Definition: gui_bounds2.hpp:85
static matrix4f translation(const vector2f &dx) noexcept
Definition: gui_matrix4.cpp:29
Struct holding all the core information about a region necessary for its creation.