The Battle for Wesnoth  1.17.0-dev
floating_label.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2021
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "floating_label.hpp"
17 
18 #include "display.hpp"
19 #include "font/text.hpp"
20 #include "log.hpp"
21 #include "video.hpp"
22 
23 #include <map>
24 #include <set>
25 #include <stack>
26 
27 static lg::log_domain log_font("font");
28 #define DBG_FT LOG_STREAM(debug, log_font)
29 #define LOG_FT LOG_STREAM(info, log_font)
30 #define WRN_FT LOG_STREAM(warn, log_font)
31 #define ERR_FT LOG_STREAM(err, log_font)
32 
33 namespace
34 {
35 typedef std::map<int, font::floating_label> label_map;
36 label_map labels;
37 int label_id = 1;
38 
39 std::stack<std::set<int>> label_contexts;
40 }
41 
42 namespace font
43 {
44 floating_label::floating_label(const std::string& text, const surface& surf)
45 #if 0
46  : img_(),
47 #else
48  : surf_(surf)
49  , buf_(nullptr)
50  , buf_pos_()
51 #endif
52  , fadeout_(0)
53  , time_start_(0)
54  , text_(text)
55  , font_size_(SIZE_SMALL)
56  , color_(NORMAL_COLOR)
57  , bgcolor_()
58  , bgalpha_(0)
59  , xpos_(0)
60  , ypos_(0)
61  , xmove_(0)
62  , ymove_(0)
63  , lifetime_(-1)
64  , width_(-1)
65  , height_(-1)
66  , clip_rect_(CVideo::get_singleton().screen_area())
67  , visible_(true)
68  , align_(CENTER_ALIGN)
69  , border_(0)
70  , scroll_(ANCHOR_LABEL_SCREEN)
71  , use_markup_(true)
72 {
73 }
74 
75 void floating_label::move(double xmove, double ymove)
76 {
77  xpos_ += xmove;
78  ypos_ += ymove;
79 }
80 
81 int floating_label::xpos(std::size_t width) const
82 {
83  int xpos = int(xpos_);
84  if(align_ == font::CENTER_ALIGN) {
85  xpos -= width / 2;
86  } else if(align_ == font::RIGHT_ALIGN) {
87  xpos -= width;
88  }
89 
90  return xpos;
91 }
92 
94 {
95  if(!surf_) {
97 
98  text.set_link_aware(false)
102  .set_alignment(PANGO_ALIGN_LEFT)
105  .set_maximum_height(height_ < 0 ? clip_rect_.h : height_, true)
106  .set_ellipse_mode(PANGO_ELLIPSIZE_END)
108 
109  // ignore last '\n'
110  if(!text_.empty() && *(text_.rbegin()) == '\n') {
111  text.set_text(std::string(text_.begin(), text_.end() - 1), use_markup_);
112  } else {
113  text.set_text(text_, use_markup_);
114  }
115 
116  surface foreground = text.render();
117 
118  if(foreground == nullptr) {
119  ERR_FT << "could not create floating label's text" << std::endl;
120  return nullptr;
121  }
122 
123  // combine foreground text with its background
124  if(bgalpha_ != 0) {
125  // background is a dark tooltip box
126  surface background(foreground->w + border_ * 2, foreground->h + border_ * 2);
127 
128  if(background == nullptr) {
129  ERR_FT << "could not create tooltip box" << std::endl;
130  return surf_ = foreground;
131  }
132 
133  uint32_t color = SDL_MapRGBA(foreground->format, bgcolor_.r, bgcolor_.g, bgcolor_.b, bgalpha_);
134  sdl::fill_surface_rect(background, nullptr, color);
135 
136  // we make the text less transparent, because the blitting on the
137  // dark background will darken the anti-aliased part.
138  // This 1.13 value seems to restore the brightness of version 1.4
139  // (where the text was blitted directly on screen)
140  adjust_surface_alpha(foreground, ftofxp(1.13));
141 
142  SDL_Rect r{border_, border_, 0, 0};
143  adjust_surface_alpha(foreground, SDL_ALPHA_OPAQUE);
144  sdl_blit(foreground, nullptr, background, &r);
145 
146  surf_ = background;
147  } else {
148  // background is blurred shadow of the text
149  surface background(foreground->w + 4, foreground->h + 4);
150  sdl::fill_surface_rect(background, nullptr, 0);
151  SDL_Rect r{2, 2, 0, 0};
152  sdl_blit(foreground, nullptr, background, &r);
153  background = shadow_image(background);
154 
155  if(background == nullptr) {
156  ERR_FT << "could not create floating label's shadow" << std::endl;
157  return surf_ = foreground;
158  }
159  sdl_blit(foreground, nullptr, background, &r);
160  surf_ = background;
161  }
162  }
163 
164  return surf_;
165 }
166 
167 void floating_label::draw(int time, surface screen)
168 {
169  if(!visible_) {
170  buf_ = nullptr;
171  return;
172  }
173 
174  if(screen == nullptr) {
175  return;
176  }
177 
178  create_surface();
179  if(surf_ == nullptr) {
180  return;
181  }
182 
183  if(buf_ == nullptr) {
184  buf_ = surface(surf_->w, surf_->h);
185  if(buf_ == nullptr) {
186  return;
187  }
188  }
189 
190  SDL_Point pos = get_loc(time);
191  buf_pos_ = sdl::create_rect(pos.x, pos.y, surf_->w, surf_->h);
192  const clip_rect_setter clip_setter(screen, &clip_rect_);
193  //important: make a copy of buf_pos_ because sdl_blit modifies dst_rect.
194  SDL_Rect rect = buf_pos_;
195  sdl_copy_portion(screen, &rect, buf_, nullptr);
196  sdl_blit(get_surface(time), nullptr, screen, &rect);
197 }
198 
199 void floating_label::set_lifetime(int lifetime, int fadeout)
200 {
201  lifetime_ = lifetime;
202  fadeout_ = fadeout;
203  time_start_ = SDL_GetTicks();
204 }
205 
206 
207 SDL_Point floating_label::get_loc(int time)
208 {
209  int time_alive = get_time_alive(time);
210  return {
211  static_cast<int>(time_alive * xmove_ + xpos(surf_->w)),
212  static_cast<int>(time_alive * ymove_ + ypos_)
213  };
214 }
215 
217 {
218  if(lifetime_ >= 0 && fadeout_ > 0) {
219  int time_alive = get_time_alive(time);
220  if(time_alive >= lifetime_ && surf_ != nullptr) {
221  // fade out moving floating labels
222  int alpha_add = -255 * (time_alive - lifetime_) / fadeout_;
223  return adjust_surface_alpha_add(surf_, alpha_add);
224  }
225  }
226  return surf_;
227 }
228 
230 {
231  if(screen == nullptr || buf_ == nullptr) {
232  return;
233  }
234 
235  const clip_rect_setter clip_setter(screen, &clip_rect_);
236  SDL_Rect rect = buf_pos_;
237  sdl_blit(buf_, nullptr, screen, &rect);
238 }
239 
241 {
242  if(label_contexts.empty()) {
243  return 0;
244  }
245 
246  ++label_id;
247  labels.emplace(label_id, flabel);
248  label_contexts.top().insert(label_id);
249  return label_id;
250 }
251 
252 void move_floating_label(int handle, double xmove, double ymove)
253 {
254  const label_map::iterator i = labels.find(handle);
255  if(i != labels.end()) {
256  i->second.move(xmove, ymove);
257  }
258 }
259 
260 void scroll_floating_labels(double xmove, double ymove)
261 {
262  for(label_map::iterator i = labels.begin(); i != labels.end(); ++i) {
263  if(i->second.scroll() == ANCHOR_LABEL_MAP) {
264  i->second.move(xmove, ymove);
265  }
266  }
267 }
268 
269 void remove_floating_label(int handle, int fadeout)
270 {
271  const label_map::iterator i = labels.find(handle);
272  if(i != labels.end()) {
273  if(fadeout > 0) {
274  i->second.set_lifetime(0, fadeout);
275  return;
276  }
277  labels.erase(i);
278  }
279 
280  if(!label_contexts.empty()) {
281  label_contexts.top().erase(handle);
282  }
283 }
284 
285 void show_floating_label(int handle, bool value)
286 {
287  const label_map::iterator i = labels.find(handle);
288  if(i != labels.end()) {
289  i->second.show(value);
290  }
291 }
292 
294 {
295  const label_map::iterator i = labels.find(handle);
296  if(i != labels.end()) {
297  const surface surf = i->second.create_surface();
298  if(surf != nullptr) {
299  return {0, 0, surf->w, surf->h};
300  }
301  }
302  return sdl::empty_rect;
303 }
304 
306 {
307  //TODO: 'pause' floating labels in other contexrs
308  label_contexts.emplace();
309 }
310 
312 {
313  //TODO: 'pause' floating labels in other contexrs
314  const std::set<int>& context = label_contexts.top();
315 
316  while(!context.empty()) {
317  // Remove_floating_label removes the passed label from the context.
318  // This loop removes a different label in every iteration.
319  remove_floating_label(*context.begin());
320  }
321 
322  label_contexts.pop();
323 }
324 
326 {
327  if(label_contexts.empty()) {
328  return;
329  }
330  int time = SDL_GetTicks();
331 
332  const std::set<int>& context = label_contexts.top();
333 
334  // draw the labels in the order they were added, so later added labels (likely to be tooltips)
335  // are displayed over earlier added labels.
336  for(label_map::iterator i = labels.begin(); i != labels.end(); ++i) {
337  if(context.count(i->first) > 0) {
338  i->second.draw(time, screen);
339  }
340  }
341 }
342 
344 {
345  if(label_contexts.empty()) {
346  return;
347  }
348  int time = SDL_GetTicks();
349 
350  std::set<int>& context = label_contexts.top();
351 
352  //undraw labels in reverse order, so that a LIFO process occurs, and the screen is restored
353  //into the exact state it started in.
354  for(label_map::reverse_iterator i = labels.rbegin(); i != labels.rend(); ++i) {
355  if(context.count(i->first) > 0) {
356  i->second.undraw(screen);
357  }
358  }
359 
360  //remove expired labels
361  for(label_map::iterator j = labels.begin(); j != labels.end(); ) {
362  if(context.count(j->first) > 0 && j->second.expired(time)) {
363  context.erase(j->first);
364  labels.erase(j++);
365  } else {
366  ++j;
367  }
368  }
369 }
370 }
floating_label(const std::string &text, const surface &surface=nullptr)
int xpos(std::size_t width) const
static lg::log_domain log_font("font")
void remove_floating_label(int handle, int fadeout)
removes the floating label given by &#39;handle&#39; from the screen
bool set_text(const std::string &text, const bool markedup)
Sets the text to render.
Definition: text.cpp:283
Collection of helper functions relating to Pango formatting.
void draw(int time, surface screen)
void scroll_floating_labels(double xmove, double ymove)
moves all floating labels that have &#39;scroll_mode&#39; set to ANCHOR_LABEL_MAP
void show_floating_label(int handle, bool value)
hides or shows a floating label
void set_lifetime(int lifetime, int fadeout=100)
pango_text & set_link_aware(bool b)
Definition: text.cpp:448
void fill_surface_rect(surface &dst, SDL_Rect *dst_rect, const uint32_t color)
Fill a rectangle on a given surface.
Definition: rect.hpp:115
static CVideo & get_singleton()
Definition: video.hpp:49
void move(double xmove, double ymove)
pango_text & set_font_style(const FONT_STYLE font_style)
Definition: text.cpp:343
pango_text & get_text_renderer()
Returns a reference to a static pango_text object.
Definition: text.cpp:891
int get_time_alive(int current_time) const
pango_text & set_ellipse_mode(const PangoEllipsizeMode ellipse_mode)
Definition: text.cpp:410
void move_floating_label(int handle, double xmove, double ymove)
moves the floating label given by &#39;handle&#39; by (xmove,ymove)
pango_text & set_alignment(const PangoAlignment alignment)
Definition: text.cpp:424
void adjust_surface_alpha(surface &surf, fixed_t amount)
Definition: utils.cpp:1087
surface shadow_image(const surface &surf)
create an heavy shadow of the image, by blurring, increasing alpha and darkening
Definition: utils.cpp:867
surface adjust_surface_alpha_add(const surface &surf, int amount)
Definition: utils.cpp:1096
pango_text & set_font_size(const unsigned font_size)
Definition: text.cpp:331
#define ERR_FT
pango_text & set_characters_per_line(const unsigned characters_per_line)
Definition: text.cpp:379
uint8_t r
Red value.
Definition: color.hpp:178
map_display and display: classes which take care of displaying the map and game-data on the screen...
surface get_surface(int time)
const color_t NORMAL_COLOR
pango_text & set_family_class(font::family_class fclass)
Definition: text.cpp:320
#define ftofxp(x)
IN: float or int - OUT: fixed_t.
Definition: math.hpp:318
surface & render(const SDL_Rect &viewport)
Returns the rendered text.
Definition: text.cpp:94
std::size_t i
Definition: function.cpp:967
void undraw_floating_labels(surface screen)
int add_floating_label(const floating_label &flabel)
add a label floating on the screen above everything else.
SDL_Rect get_floating_label_rect(int handle)
void undraw(surface screen)
Text class.
Definition: text.hpp:75
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an SDL_Rect with the given dimensions.
Definition: rect.hpp:40
pango_text & set_maximum_width(int width)
Definition: text.cpp:364
constexpr const SDL_Rect empty_rect
Definition: rect.hpp:32
Standard logging facilities (interface).
void sdl_copy_portion(const surface &screen, SDL_Rect *screen_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:36
pango_text & set_maximum_height(int height, bool multiline)
Definition: text.cpp:391
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:32
uint8_t g
Green value.
Definition: color.hpp:181
uint8_t b
Blue value.
Definition: color.hpp:184
pango_text & set_foreground_color(const color_t &color)
Definition: text.cpp:354
SDL_Point get_loc(int time)
std::shared_ptr< halo_record > handle
Definition: halo.hpp:30
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
void draw_floating_labels(surface screen)
const int SIZE_SMALL
Definition: constants.cpp:24