The Battle for Wesnoth  1.19.5+dev
label.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2024
3  by Mark de Wever <koraq@xs4all.nl>
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 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
18 #include "gui/widgets/label.hpp"
19 
20 #include "gui/core/log.hpp"
21 
24 #include "gui/dialogs/message.hpp"
25 
26 #include "cursor.hpp"
27 #include "desktop/clipboard.hpp"
28 #include "desktop/open.hpp"
29 #include "gettext.hpp"
30 #include "wml_exception.hpp"
31 
32 #include <functional>
33 #include <string>
34 
35 namespace gui2
36 {
37 
38 // ------------ WIDGET -----------{
39 
41 
42 label::label(const implementation::builder_label& builder)
43  : styled_widget(builder, type())
44  , state_(ENABLED)
45  , can_wrap_(builder.wrap)
46  , characters_per_line_(builder.characters_per_line)
47  , link_aware_(builder.link_aware)
48  , link_color_(color_t::from_hex_string("ffff00"))
49  , can_shrink_(builder.can_shrink)
50  , text_alpha_(ALPHA_OPAQUE)
51 {
52  connect_signal<event::LEFT_BUTTON_CLICK>(
53  std::bind(&label::signal_handler_left_button_click, this, std::placeholders::_3));
54  connect_signal<event::RIGHT_BUTTON_CLICK>(
55  std::bind(&label::signal_handler_right_button_click, this, std::placeholders::_3));
56  connect_signal<event::MOUSE_MOTION>(
57  std::bind(&label::signal_handler_mouse_motion, this, std::placeholders::_3, std::placeholders::_5));
58  connect_signal<event::MOUSE_LEAVE>(
59  std::bind(&label::signal_handler_mouse_leave, this, std::placeholders::_3));
60 }
61 
63 {
64  // Inherit.
66 
67  for(auto& tmp : get_canvases()) {
68  tmp.set_variable("text_alpha", wfl::variant(text_alpha_));
69  }
70 }
71 
72 void label::set_text_alpha(unsigned short alpha)
73 {
74  if(alpha != text_alpha_) {
75  text_alpha_ = alpha;
76  update_canvas();
77  queue_redraw();
78  }
79 }
80 
81 void label::set_active(const bool active)
82 {
83  if(get_active() != active) {
84  set_state(active ? ENABLED : DISABLED);
85  }
86 }
87 
88 void label::set_link_aware(bool link_aware)
89 {
90  if(link_aware != link_aware_) {
91  link_aware_ = link_aware;
92  update_canvas();
93  queue_redraw();
94  }
95 }
96 
97 void label::set_link_color(const color_t& color)
98 {
99  if(color != link_color_) {
100  link_color_ = color;
101  update_canvas();
102  queue_redraw();
103  }
104 }
105 
106 void label::set_state(const state_t state)
107 {
108  if(state != state_) {
109  state_ = state;
110  queue_redraw();
111  }
112 }
113 
115 {
116  DBG_GUI_E << "label click";
117 
118  if (!get_link_aware()) {
119  return; // without marking event as "handled".
120  }
121 
123  show_message("", _("Opening links is not supported, contact your packager"), dialogs::message::auto_close);
124  handled = true;
125  return;
126  }
127 
128  point mouse = get_mouse_position();
129 
130  mouse.x -= get_x();
131  mouse.y -= get_y();
132 
133  std::string link = get_label_link(mouse);
134 
135  if (link.length() == 0) {
136  return ; // without marking event as "handled"
137  }
138 
139  DBG_GUI_E << "Clicked Link:\"" << link << "\"";
140 
141  const int res = show_message(_("Open link?"), link, dialogs::message::yes_no_buttons);
142  if(res == gui2::retval::OK) {
143  desktop::open_object(link);
144  }
145 
146  handled = true;
147 }
148 
150 {
151  DBG_GUI_E << "label right click";
152 
153  if (!get_link_aware()) {
154  return ; // without marking event as "handled".
155  }
156 
157  point mouse = get_mouse_position();
158 
159  mouse.x -= get_x();
160  mouse.y -= get_y();
161 
162  std::string link = get_label_link(mouse);
163 
164  if (link.length() == 0) {
165  return ; // without marking event as "handled"
166  }
167 
168  DBG_GUI_E << "Right Clicked Link:\"" << link << "\"";
169 
171 
172  (void) show_message("", _("Copied link!"), dialogs::message::auto_close);
173 
174  handled = true;
175 }
176 
178 {
179  DBG_GUI_E << "label mouse motion";
180 
181  if(!get_link_aware()) {
182  return; // without marking event as "handled"
183  }
184 
185  point mouse = coordinate;
186 
187  mouse.x -= get_x();
188  mouse.y -= get_y();
189 
190  update_mouse_cursor(!get_label_link(mouse).empty());
191 
192  handled = true;
193 }
194 
196 {
197  DBG_GUI_E << "label mouse leave";
198 
199  if(!get_link_aware()) {
200  return; // without marking event as "handled"
201  }
202 
203  // We left the widget, so just unconditionally reset the cursor
204  update_mouse_cursor(false);
205 
206  handled = true;
207 }
208 
209 void label::update_mouse_cursor(bool enable)
210 {
211  // Someone else may set the mouse cursor for us to something unusual (e.g.
212  // the WAIT cursor) so we ought to mess with that only if it's set to
213  // NORMAL or HYPERLINK.
214 
215  if(enable && cursor::get() == cursor::NORMAL) {
217  } else if(!enable && cursor::get() == cursor::HYPERLINK) {
219  }
220 }
221 
222 // }---------- DEFINITION ---------{
223 
226 {
227  DBG_GUI_P << "Parsing label " << id;
228 
229  load_resolutions<resolution>(cfg);
230 }
231 
233  : resolution_definition(cfg)
234  , link_color(cfg["link_color"].empty() ? color_t::from_hex_string("ffff00") : color_t::from_rgba_string(cfg["link_color"].str()))
235 {
236  // Note the order should be the same as the enum state_t is label.hpp.
237  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_enabled", missing_mandatory_wml_tag("label_definition][resolution", "state_enabled")));
238  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", missing_mandatory_wml_tag("label_definition][resolution", "state_disabled")));
239 }
240 
241 // }---------- BUILDER -----------{
242 
243 namespace implementation
244 {
245 
246 builder_label::builder_label(const config& cfg)
247  : builder_styled_widget(cfg)
248  , wrap(cfg["wrap"].to_bool())
249  , characters_per_line(cfg["characters_per_line"].to_unsigned())
250  , text_alignment(decode_text_alignment(cfg["text_alignment"]))
251  , can_shrink(cfg["can_shrink"].to_bool(false))
252  , link_aware(cfg["link_aware"].to_bool(false))
253 {
254 }
255 
256 std::unique_ptr<widget> builder_label::build() const
257 {
258  auto lbl = std::make_unique<label>(*this);
259 
260  const auto conf = lbl->cast_config_to<label_definition>();
261  assert(conf);
262 
263  lbl->set_text_alignment(text_alignment);
264  lbl->set_link_color(conf->link_color);
265 
266  DBG_GUI_G << "Window builder: placed label '" << id << "' with definition '"
267  << definition << "'.";
268 
269  return lbl;
270 }
271 
272 } // namespace implementation
273 
274 // }------------ END --------------
275 
276 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
@ yes_no_buttons
Shows a yes and no button.
Definition: message.hpp:81
@ auto_close
Enables auto close.
Definition: message.hpp:71
unsigned short text_alpha_
Definition: label.hpp:159
virtual bool get_link_aware() const override
See styled_widget::get_link_aware.
Definition: label.hpp:51
void signal_handler_left_button_click(bool &handled)
Left click signal handler: checks if we clicked on a hyperlink.
Definition: label.cpp:114
void set_link_color(const color_t &color)
Definition: label.cpp:97
void signal_handler_mouse_leave(bool &handled)
Mouse leave signal handler: checks if the cursor left a hyperlink.
Definition: label.cpp:195
color_t link_color_
What color links will be rendered in.
Definition: label.hpp:155
void signal_handler_right_button_click(bool &handled)
Right click signal handler: checks if we clicked on a hyperlink, copied to clipboard.
Definition: label.cpp:149
bool link_aware_
Whether the label is link aware, rendering links with special formatting and handling click events.
Definition: label.hpp:150
virtual void update_canvas() override
See styled_widget::update_canvas.
Definition: label.cpp:62
void set_state(const state_t state)
Definition: label.cpp:106
state_t
Possible states of the widget.
Definition: label.hpp:121
virtual bool get_active() const override
See styled_widget::get_active.
Definition: label.hpp:66
state_t state_
Current state of the widget.
Definition: label.hpp:134
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: label.cpp:81
void update_mouse_cursor(bool enable)
Implementation detail for (re)setting the hyperlink cursor.
Definition: label.cpp:209
void set_link_aware(bool l)
Definition: label.cpp:88
void set_text_alpha(unsigned short alpha)
Definition: label.cpp:72
void signal_handler_mouse_motion(bool &handled, const point &coordinate)
Mouse motion signal handler: checks if the cursor is on a hyperlink.
Definition: label.cpp:177
std::vector< canvas > & get_canvases()
std::string get_label_link(const point &position) const
virtual void update_canvas()
Updates the canvas(ses).
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:464
int get_x() const
Definition: widget.cpp:326
int get_y() const
Definition: widget.cpp:331
constexpr uint8_t ALPHA_OPAQUE
Definition: color.hpp:45
static std::string _(const char *str)
Definition: gettext.hpp:93
Define the common log macros for the gui toolkit.
#define DBG_GUI_G
Definition: log.hpp:41
#define DBG_GUI_P
Definition: log.hpp:66
#define DBG_GUI_E
Definition: log.hpp:35
std::string label
What to show in the filter's drop-down list.
Definition: manager.cpp:200
CURSOR_TYPE get()
Definition: cursor.cpp:216
@ NORMAL
Definition: cursor.hpp:28
@ HYPERLINK
Definition: cursor.hpp:28
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:176
void copy_to_clipboard(const std::string &text)
Copies text to the clipboard.
Definition: clipboard.cpp:27
bool open_object([[maybe_unused]] const std::string &path_or_url)
Definition: open.cpp:46
constexpr bool open_object_is_supported()
Returns whether open_object() is supported/implemented for the current platform.
Definition: open.hpp:54
Generic file dialog.
point get_mouse_position()
Returns the current mouse position.
Definition: helper.cpp:143
PangoAlignment decode_text_alignment(const std::string &alignment)
Converts a text alignment string to a text alignment.
Definition: helper.cpp:89
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:148
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
Contains the implementation details for lexical_cast and shouldn't be used directly.
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
Desktop environment interaction functions.
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:59
virtual std::unique_ptr< widget > build() const override
Definition: label.cpp:256
std::string definition
Parameters for the styled_widget.
resolution(const config &cfg)
Definition: label.cpp:232
label_definition(const config &cfg)
Definition: label.cpp:224
std::vector< state_definition > state
Holds a 2D point.
Definition: point.hpp:25
std::string missing_mandatory_wml_tag(const std::string &section, const std::string &tag)
Returns a standard message for a missing wml child (tag).
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define VALIDATE_WML_CHILD(cfg, key, message)