The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
label.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
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 "map/label.hpp"
17#include "color.hpp"
18#include "display.hpp"
19#include "floating_label.hpp"
21#include "game_board.hpp"
22#include "game_data.hpp"
23#include "resources.hpp"
24#include "tooltips.hpp"
25#include "utils/general.hpp"
26
27/**
28 * Our definition of map labels being obscured is if the tile is obscured,
29 * or the tile below is obscured. This is because in the case where the tile
30 * itself is visible, but the tile below is obscured, the bottom half of the
31 * tile will still be shrouded, and the label being drawn looks weird.
32 */
33inline bool is_shrouded(const display* disp, const map_location& loc)
34{
36}
37
38/**
39 * Rather simple test for a hex being fogged.
40 * This only exists because is_shrouded() does. (The code looks nicer if
41 * the test for being fogged looks similar to the test for being shrouded.)
42 */
43inline bool is_fogged(const display* disp, const map_location& loc)
44{
45 return disp->fogged(loc);
46}
47
49 : team_(team)
50 , labels_()
51 , enabled_(true)
52 , categories_dirty(true)
53{
54}
55
57 : team_(other.team_)
58 , labels_()
59 , enabled_(true)
60{
61 config cfg;
62 other.write(cfg);
63 read(cfg);
64}
65
70
72{
73 if(this != &other) {
74 this->~map_labels();
75 new(this) map_labels(other);
76 }
77
78 return *this;
79}
80
81void map_labels::write(config& res) const
82{
83 for(const auto& group : labels_) {
84 for(const auto& label : group.second) {
85 config item;
86 label.second.write(item);
87
88 res.add_child("label", std::move(item));
89 }
90 }
91}
92
94{
95 clear_all();
96
97 for(const config& i : cfg.child_range("label")) {
98 add_label(*this, i);
99 }
100
102}
103
104terrain_label* map_labels::get_label_private(const map_location& loc, const std::string& team_name)
105{
106 auto label_map = labels_.find(team_name);
107 if(label_map != labels_.end()) {
108 auto itor = label_map->second.find(loc);
109 if(itor != label_map->second.end()) {
110 return &itor->second;
111 }
112 }
113
114 return nullptr;
115}
116
118{
119 const terrain_label* res = get_label(loc, team_name());
120
121 // No such team label. Try to find global label, except if that's what we just did.
122 // NOTE: This also avoid infinite recursion
123 if(res == nullptr && !team_name().empty()) {
124 return get_label(loc, "");
125 }
126
127 return res;
128}
129
130const std::string& map_labels::team_name() const
131{
132 if(team_) {
133 return team_->team_name();
134 }
135
136 static const std::string empty;
137 return empty;
138}
139
141{
142 if(team_ != team) {
143 team_ = team;
144 categories_dirty = true;
145 }
146}
147
149 const t_string& text,
150 const int creator,
151 const std::string& team_name,
152 const color_t color,
153 const bool visible_in_fog,
154 const bool visible_in_shroud,
155 const bool immutable,
156 const std::string& category,
157 const t_string& tooltip)
158{
159 terrain_label* res = nullptr;
160
161 // See if there is already a label in this location for this team.
162 // (We do not use get_label_private() here because we might need
163 // the label_map as well as the terrain_label.)
164 team_label_map::iterator current_label_map = labels_.find(team_name);
165 label_map::iterator current_label;
166
167 if(current_label_map != labels_.end() &&
168 (current_label = current_label_map->second.find(loc)) != current_label_map->second.end())
169 {
170 // Found old checking if need to erase it
171 if(text.str().empty()) {
172 // Erase the old label.
173 current_label_map->second.erase(current_label);
174
175 // Restore the global label in the same spot, if any.
176 if(terrain_label* global_label = get_label_private(loc, "")) {
177 global_label->recalculate();
178 }
179 } else {
180 current_label->second.update_info(
181 text, creator, tooltip, team_name, color, visible_in_fog, visible_in_shroud, immutable, category);
182
183 res = &current_label->second;
184 }
185 } else if(!text.str().empty()) {
186 // See if we will be replacing a global label.
187 terrain_label* global_label = get_label_private(loc, "");
188
189 // Add the new label.
190 res = add_label(
191 *this, text, creator, team_name, loc, color, visible_in_fog, visible_in_shroud, immutable, category, tooltip);
192
193 // Hide the old label.
194 if(global_label != nullptr) {
195 global_label->recalculate();
196 }
197 }
198
199 categories_dirty = true;
200 return res;
201}
202
203template<typename... T>
205{
206 categories_dirty = true;
207
208 terrain_label t(std::forward<T>(args)...);
209 return &(*labels_[t.team_name()].emplace(t.location(), std::move(t)).first).second;
210}
211
212void map_labels::clear(const std::string& team_name, bool force)
213{
214 team_label_map::iterator i = labels_.find(team_name);
215 if(i != labels_.end()) {
216 clear_map(i->second, force);
217 }
218
219 i = labels_.find("");
220 if(i != labels_.end()) {
221 clear_map(i->second, force);
222 }
223
224 categories_dirty = true;
225}
226
228{
229 label_map::iterator i = m.begin();
230 while(i != m.end()) {
231 if(!i->second.immutable() || force) {
232 m.erase(i++);
233 } else {
234 ++i;
235 }
236 }
237
238 categories_dirty = true;
239}
240
242{
243 labels_.clear();
244}
245
247{
248 for(auto& m : labels_) {
249 for(auto& l : m.second) {
250 l.second.recalculate();
251 }
252 }
253}
254
255void map_labels::enable(bool is_enabled)
256{
257 if(is_enabled != enabled_) {
258 enabled_ = is_enabled;
260 }
261}
262
263/**
264 * Returns whether or not a global (non-team) label can be shown at a
265 * specified location.
266 * (Global labels are suppressed in favor of team labels.)
267 */
269{
270 if(team_ == nullptr) {
271 // We're in the editor. All global labels can be shown.
272 return true;
273 }
274
275 const team_label_map::const_iterator glabels = labels_.find(team_name());
276 return glabels == labels_.end() || glabels->second.find(loc) == glabels->second.end();
277}
278
280{
281 for(auto& m : labels_) {
282 for(auto& l : m.second) {
283 l.second.calculate_shroud();
284 }
285 }
286}
287
288const std::vector<std::string>& map_labels::all_categories() const
289{
290 if(categories_dirty) {
291 categories_dirty = false;
292 categories.clear();
293 categories.push_back("team");
294
295 for(const team& t : resources::gameboard->teams()) {
296 categories.push_back("side:" + std::to_string(t.side()));
297 }
298
299 std::set<std::string> unique_cats;
300 for(const auto& m : labels_) {
301 for(const auto& l : m.second) {
302 if(l.second.category().empty()) {
303 continue;
304 }
305
306 unique_cats.insert("cat:" + l.second.category());
307 }
308 }
309
310 std::copy(unique_cats.begin(), unique_cats.end(), std::back_inserter(categories));
311 }
312
313 return categories;
314}
315
316/** Create a new label. */
318 const t_string& text,
319 const int creator,
320 const std::string& team_name,
321 const map_location& loc,
322 const color_t color,
323 const bool visible_in_fog,
324 const bool visible_in_shroud,
325 const bool immutable,
326 const std::string& category,
327 const t_string& tooltip)
328 : handle_(0)
329 , text_(text)
330 , tooltip_(tooltip)
331 , category_(category)
332 , team_name_(team_name)
333 , visible_in_fog_(visible_in_fog)
334 , visible_in_shroud_(visible_in_shroud)
335 , immutable_(immutable)
336 , creator_(creator)
337 , color_(color)
338 , parent_(&parent)
339 , loc_(loc)
340{
341 recalculate();
342}
343
344/** Load label from config. */
346 : handle_(0)
347 , tooltip_handle_(0)
348 , text_()
349 , tooltip_()
350 , team_name_()
351 , visible_in_fog_(true)
352 , visible_in_shroud_(false)
353 , immutable_(true)
354 , creator_(-1)
355 , color_()
356 , parent_(&parent)
357 , loc_()
358{
359 read(cfg);
360}
361
363 : handle_(l.handle_)
364 , tooltip_handle_(l.tooltip_handle_)
365 , text_(std::move(l.text_))
366 , tooltip_(std::move(l.tooltip_))
367 , category_(std::move(l.category_))
368 , team_name_(std::move(l.team_name_))
369 , visible_in_fog_(l.visible_in_fog_)
370 , visible_in_shroud_(l.visible_in_shroud_)
371 , immutable_(l.immutable_)
372 , creator_(l.creator_)
373 , color_(l.color_)
374 , parent_(l.parent_)
375 , loc_(l.loc_)
376{
377 l.handle_ = 0;
378 l.tooltip_handle_ = 0;
379}
380
385
387{
389
390 loc_ = map_location(cfg, &vs);
392
393 std::string tmp_color = cfg["color"];
394
395 text_ = cfg["text"].t_str();
396 tooltip_ = cfg["tooltip"].t_str();
397 team_name_ = cfg["team_name"].str();
398 visible_in_fog_ = cfg["visible_in_fog"].to_bool(true);
399 visible_in_shroud_ = cfg["visible_in_shroud"].to_bool();
400 immutable_ = cfg["immutable"].to_bool(true);
401 category_ = cfg["category"].str();
402
403 int side = cfg["side"].to_int(-1);
404 if(side >= 0) {
405 creator_ = side - 1;
406 } else if(cfg["side"].str() == "current") {
407 config::attribute_value current_side = vs.get_variable_const("side_number");
408 if(!current_side.empty()) {
409 creator_ = current_side.to_int();
410 }
411 }
412
413 // Not moved to rendering, as that would depend on variables at render-time
415
417 tmp_color = utils::interpolate_variables_into_string(tmp_color, vs);
418
419 if(!tmp_color.empty()) {
420 try {
421 color = color_t::from_rgb_string(tmp_color);
422 } catch(const std::invalid_argument&) {
423 // Prior to the color_t conversion, labels were written to savefiles with an alpha key, despite alpha not
424 // being accepted in color=. Because of this, this enables the loading of older saves without an exception
425 // throwing.
426 color = color_t::from_rgba_string(tmp_color);
427 }
428 }
429
430 color_ = color;
431}
432
434{
435 loc_.write(cfg);
436
437 cfg["text"] = text();
438 cfg["tooltip"] = tooltip();
439 cfg["team_name"] = (this->team_name());
440 cfg["color"] = color_.to_rgb_string();
441 cfg["visible_in_fog"] = visible_in_fog_;
442 cfg["visible_in_shroud"] = visible_in_shroud_;
443 cfg["immutable"] = immutable_;
444 cfg["category"] = category_;
445 cfg["side"] = creator_ + 1;
446}
447
449 const int creator,
450 const t_string& tooltip,
451 const std::string& team_name,
452 const color_t color)
453{
454 color_ = color;
455 text_ = text;
459
460 recalculate();
461}
462
464 const int creator,
465 const t_string& tooltip,
466 const std::string& team_name,
467 const color_t color,
468 const bool visible_in_fog,
469 const bool visible_in_shroud,
470 const bool immutable,
471 const std::string& category)
472{
477
479}
480
482{
483 if(handle_) {
485 }
486
487 if(tooltip_.empty() || hidden()) {
489 tooltip_handle_ = 0;
490 return;
491 }
492
493 // tooltips::update_tooltip(tooltip_handle, get_rect(), tooltip_.str(), "", true);
494
495 if(tooltip_handle_) {
497 } else {
499 }
500}
501
503{
505 if(!disp) {
506 return {};
507 }
508
509 rect res = disp->get_location_rect(loc_);
510 res.x += disp->hex_size() / 4;
511 res.w -= disp->hex_size() / 2;
512
513 return res;
514}
515
516static int scale_to_map_zoom(int val)
517{
518 return val * std::max(1.0, display::get_zoom_factor());
519}
520
522{
524 if(!disp) {
525 return;
526 }
527
528 if(text_.empty() && tooltip_.empty()) {
529 return;
530 }
531
532 clear();
533
534 if(!viewable(*disp)) {
535 return;
536 }
537
538 // Note: the y part of loc_nextx is not used at all.
541 const int xloc = (disp->get_location(loc_).x + disp->get_location(loc_nextx).x * 2) / 3;
542 const int yloc = disp->get_location(loc_nexty).y - scale_to_map_zoom(font::SIZE_NORMAL);
543
544 // If a color is specified don't allow to override it with markup. (prevents faking map labels for example)
545 // FIXME: @todo Better detect if it's team label and not provided by the scenario.
546 bool use_markup = color_ == font::LABEL_COLOR;
547
550 flabel.set_color(color_);
551 flabel.set_position(xloc, yloc);
552 flabel.set_clip_rect(disp->map_outside_area());
556 flabel.use_markup(use_markup);
557
559
561}
562
563/**
564 * This is a lightweight test used to see if labels are revealed as a result
565 * of unit actions (i.e. fog/shroud clearing). It should not contain any tests
566 * that are invariant during unit movement (disregarding potential WML events);
567 * those belong in visible().
568 */
570{
572 if(!disp) {
573 return false;
574 }
575
576 // Respect user's label preferences
577 std::string category = "cat:" + category_;
578 std::string creator = "side:" + std::to_string(creator_ + 1);
579 const std::vector<std::string>& hidden_categories = disp->context().hidden_label_categories();
580
581 if(utils::contains(hidden_categories, category)) {
582 return true;
583 }
584
585 if(creator_ >= 0 && utils::contains(hidden_categories, creator)) {
586 return true;
587 }
588
589 if(!team_name().empty() && utils::contains(hidden_categories, "team")) {
590 return true;
591 }
592
593 // Fog can hide some labels.
594 if(!visible_in_fog_ && is_fogged(disp, loc_)) {
595 return true;
596 }
597
598 // Shroud can hide some labels.
599 if(!visible_in_shroud_ && is_shrouded(disp, loc_)) {
600 return true;
601 }
602
603 return false;
604}
605
606/**
607 * This is a test used to see if we should bother with the overhead of actually
608 * creating a label. Conditions that can change during unit movement (disregarding
609 * potential WML events) should not be listed here; they belong in hidden().
610 */
611bool terrain_label::viewable(const display& disp) const
612{
613 if(!parent_->enabled()) {
614 return false;
615 }
616
617 // In the editor, all labels are viewable.
618 if(disp.in_editor()) {
619 return true;
620 }
621
622 // Observers are not privvy to team labels.
623 const bool can_see_team_labels = !disp.context().is_observer();
624
625 // Global labels are shown unless covered by a team label.
626 if(team_name_.empty()) {
627 return !can_see_team_labels || parent_->visible_global_label(loc_);
628 }
629
630 // Team labels are only shown to members of the team.
631 return can_see_team_labels && parent_->team_name() == team_name_;
632}
633
map_location loc
Definition move.cpp:172
double t
Variant for storing WML attributes.
bool empty() const
Tests for an attribute that either was never set or was set to "".
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
child_itors child_range(std::string_view key)
Definition config.cpp:268
virtual const std::vector< std::string > & hidden_label_categories() const =0
bool is_observer() const
Check if we are an observer in this game.
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition display.hpp:88
point get_location(const map_location &loc) const
Functions to get the on-screen positions of hexes.
Definition display.cpp:674
static int hex_size()
Function which returns the size of a hex in pixels (from left tip to right tip or top edge to bottom ...
Definition display.hpp:259
static double get_zoom_factor()
Returns the current zoom factor.
Definition display.hpp:262
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition display.cpp:669
rect map_outside_area() const
Returns the available area for a map, this may differ from the above.
Definition display.cpp:516
virtual bool in_editor() const
Definition display.hpp:213
rect get_location_rect(const map_location &loc) const
Returns the on-screen rect corresponding to a loc.
Definition display.cpp:685
static display * get_singleton()
Returns the display object if a display object exists.
Definition display.hpp:102
bool shrouded(const map_location &loc) const
Returns true if location (x,y) is covered in shroud.
Definition display.cpp:664
const display_context & context() const
Definition display.hpp:187
void set_position(double xpos, double ypos)
void set_color(const color_t &color)
void set_clip_rect(const rect &r)
void set_scroll_mode(LABEL_SCROLL_MODE scroll)
void set_font_size(int font_size)
virtual const std::vector< team > & teams() const override
void write(config &res) const
Definition label.cpp:81
void clear_map(label_map &, bool)
Definition label.cpp:227
const std::vector< std::string > & all_categories() const
Definition label.cpp:288
const team * team_
Definition label.hpp:97
bool enabled() const
Definition label.hpp:67
const std::string & team_name() const
Definition label.cpp:130
bool visible_global_label(const map_location &) const
Returns whether or not a global (non-team) label can be shown at a specified location.
Definition label.cpp:268
terrain_label * add_label(T &&... args)
Definition label.cpp:204
const terrain_label * set_label(const map_location &loc, const t_string &text, const int creator=-1, const std::string &team="", const color_t color=font::NORMAL_COLOR, const bool visible_in_fog=true, const bool visible_in_shroud=false, const bool immutable=false, const std::string &category="", const t_string &tooltip="")
Definition label.cpp:148
void recalculate_shroud()
Definition label.cpp:279
terrain_label * get_label_private(const map_location &loc, const std::string &team_name)
Definition label.cpp:104
void clear_all()
Definition label.cpp:241
map_labels & operator=(const map_labels &)
Definition label.cpp:71
void enable(bool is_enabled)
Definition label.cpp:255
void set_team(const team *)
Definition label.cpp:140
void clear(const std::string &, bool force)
Definition label.cpp:212
bool enabled_
Definition label.hpp:100
const terrain_label * get_label(const map_location &loc, const std::string &team_name) const
Definition label.hpp:48
team_label_map labels_
Definition label.hpp:99
std::vector< std::string > categories
Definition label.hpp:102
bool categories_dirty
Definition label.hpp:103
map_labels(const map_labels &)
Definition label.cpp:56
void recalculate_labels()
Definition label.cpp:246
~map_labels()
Definition label.cpp:66
std::map< map_location, terrain_label > label_map
Definition label.hpp:35
void read(const config &cfg)
Definition label.cpp:93
bool empty() const
Definition tstring.hpp:200
const std::string & str() const
Definition tstring.hpp:204
This class stores all the data for a single 'side' (in game nomenclature).
Definition team.hpp:74
const std::string & team_name() const
Definition team.hpp:323
To store label data Class implements logic for rendering.
Definition label.hpp:111
terrain_label(const terrain_label &)=delete
Delete copy ctor and assignment ops.
rect get_rect() const
Definition label.cpp:502
bool visible_in_shroud() const
Definition label.hpp:169
const t_string & tooltip() const
Definition label.hpp:144
bool visible_in_fog_
Definition label.hpp:223
t_string tooltip_
Definition label.hpp:218
bool immutable() const
Definition label.hpp:174
int creator() const
Definition label.hpp:149
const std::string & team_name() const
Definition label.hpp:154
std::string category_
Definition label.hpp:220
const color_t & color() const
Definition label.hpp:184
color_t color_
Definition label.hpp:228
void update_info(const t_string &, const int creator, const t_string &, const std::string &, const color_t)
Definition label.cpp:448
bool visible_in_fog() const
Definition label.hpp:164
const t_string & text() const
Definition label.hpp:139
std::string team_name_
Definition label.hpp:221
int tooltip_handle_
Definition label.hpp:215
void write(config &res) const
Definition label.cpp:433
map_location loc_
Definition label.hpp:231
const map_labels * parent_
Definition label.hpp:230
bool immutable_
Definition label.hpp:225
bool hidden() const
This is a lightweight test used to see if labels are revealed as a result of unit actions (i....
Definition label.cpp:569
void clear()
Definition label.cpp:634
void recalculate()
Definition label.cpp:521
bool viewable(const display &disp) const
This is a test used to see if we should bother with the overhead of actually creating a label.
Definition label.cpp:611
bool visible_in_shroud_
Definition label.hpp:224
void calculate_shroud()
Definition label.cpp:481
void read(const config &cfg)
Definition label.cpp:386
const std::string & category() const
Definition label.hpp:159
t_string text_
Definition label.hpp:217
virtual config::attribute_value get_variable_const(const std::string &id) const =0
map_display and display: classes which take care of displaying the map and game-data on the screen.
const config * cfg
std::string label
What to show in the filter's drop-down list.
Definition manager.cpp:201
std::string tooltip
Shown when hovering over an entry in the filter's drop-down list.
Definition manager.cpp:203
bool is_shrouded(const display *disp, const map_location &loc)
Our definition of map labels being obscured is if the tile is obscured, or the tile below is obscured...
Definition label.cpp:33
static int scale_to_map_zoom(int val)
Definition label.cpp:516
bool is_fogged(const display *disp, const map_location &loc)
Rather simple test for a hex being fogged.
Definition label.cpp:43
const color_t LABEL_COLOR
int add_floating_label(const floating_label &flabel)
add a label floating on the screen above everything else.
void show_floating_label(int handle, bool value)
hides or shows a floating label
void remove_floating_label(int handle, const std::chrono::milliseconds &fadeout)
removes the floating label given by 'handle' from the screen
const int SIZE_NORMAL
Definition constants.cpp:20
@ ANCHOR_LABEL_MAP
game_board * gameboard
Definition resources.cpp:20
game_data * gamedata
Definition resources.cpp:22
int add_tooltip(const rect &origin, const std::string &message, const std::string &action)
Definition tooltips.cpp:293
void remove_tooltip(int id)
Definition tooltips.cpp:283
bool update_tooltip(int id, const rect &origin, const std::string &message)
Definition tooltips.cpp:261
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with '$' in the string 'str' with the equivalent ...
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition general.hpp:88
t_string interpolate_variables_into_tstring(const t_string &tstr, const variable_set &variables)
Function that does the same as the above, for t_stringS.
The basic class for representing 8-bit RGB or RGBA colour values.
Definition color.hpp:51
static color_t from_rgb_string(std::string_view c)
Creates a new opaque color_t object from a string variable in "R,G,B" format.
Definition color.cpp:44
std::string to_rgb_string() const
Returns the stored color as an "R,G,B" string.
Definition color.cpp:117
static color_t from_rgba_string(std::string_view c)
Creates a new color_t object from a string variable in "R,G,B,A" format.
Definition color.cpp:23
Encapsulates the map of the game.
Definition location.hpp:46
map_location get_direction(direction dir, unsigned int n=1u) const
Definition location.cpp:362
void write(config &cfg) const
Definition location.cpp:223
An abstract description of a rectangle with integer coordinates.
Definition rect.hpp:49
#define i