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