The Battle for Wesnoth  1.17.21+dev
label.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2023
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 
25 #include "gui/dialogs/message.hpp"
26 #include "gui/widgets/settings.hpp"
27 #include "gui/widgets/window.hpp"
28 
29 #include "cursor.hpp"
30 #include "desktop/clipboard.hpp"
31 #include "desktop/open.hpp"
32 #include "gettext.hpp"
33 #include "wml_exception.hpp"
34 
35 #include <functional>
36 #include <string>
37 #include <sstream>
38 
39 namespace gui2
40 {
41 
42 // ------------ WIDGET -----------{
43 
45 
46 label::label(const implementation::builder_label& builder)
47  : styled_widget(builder, type())
48  , state_(ENABLED)
49  , can_wrap_(builder.wrap)
50  , characters_per_line_(builder.characters_per_line)
51  , link_aware_(builder.link_aware)
52  , link_color_(color_t::from_hex_string("ffff00"))
53  , can_shrink_(builder.can_shrink)
54  , text_alpha_(ALPHA_OPAQUE)
55 {
56  connect_signal<event::LEFT_BUTTON_CLICK>(
57  std::bind(&label::signal_handler_left_button_click, this, std::placeholders::_3));
58  connect_signal<event::RIGHT_BUTTON_CLICK>(
59  std::bind(&label::signal_handler_right_button_click, this, std::placeholders::_3));
60  connect_signal<event::MOUSE_MOTION>(
61  std::bind(&label::signal_handler_mouse_motion, this, std::placeholders::_3, std::placeholders::_5));
62  connect_signal<event::MOUSE_LEAVE>(
63  std::bind(&label::signal_handler_mouse_leave, this, std::placeholders::_3));
64 }
65 
67 {
68  // Inherit.
70 
71  for(auto& tmp : get_canvases()) {
72  tmp.set_variable("text_alpha", wfl::variant(text_alpha_));
73  }
74 }
75 
76 void label::set_text_alpha(unsigned short alpha)
77 {
78  if(alpha != text_alpha_) {
79  text_alpha_ = alpha;
80  update_canvas();
81  queue_redraw();
82  }
83 }
84 
85 void label::set_active(const bool active)
86 {
87  if(get_active() != active) {
88  set_state(active ? ENABLED : DISABLED);
89  }
90 }
91 
92 void label::set_link_aware(bool link_aware)
93 {
94  if(link_aware != link_aware_) {
95  link_aware_ = link_aware;
96  update_canvas();
97  queue_redraw();
98  }
99 }
100 
101 void label::set_link_color(const color_t& color)
102 {
103  if(color != link_color_) {
104  link_color_ = color;
105  update_canvas();
106  queue_redraw();
107  }
108 }
109 
110 void label::set_state(const state_t state)
111 {
112  if(state != state_) {
113  state_ = state;
114  queue_redraw();
115  }
116 }
117 
119 {
120  DBG_GUI_E << "label click";
121 
122  if (!get_link_aware()) {
123  return; // without marking event as "handled".
124  }
125 
127  show_message("", _("Opening links is not supported, contact your packager"), dialogs::message::auto_close);
128  handled = true;
129  return;
130  }
131 
132  point mouse = get_mouse_position();
133 
134  mouse.x -= get_x();
135  mouse.y -= get_y();
136 
137  std::string link = get_label_link(mouse);
138 
139  if (link.length() == 0) {
140  return ; // without marking event as "handled"
141  }
142 
143  DBG_GUI_E << "Clicked Link:\"" << link << "\"";
144 
145  const int res = show_message(_("Open link?"), link, dialogs::message::yes_no_buttons);
146  if(res == gui2::retval::OK) {
147  desktop::open_object(link);
148  }
149 
150  handled = true;
151 }
152 
154 {
155  DBG_GUI_E << "label right click";
156 
157  if (!get_link_aware()) {
158  return ; // without marking event as "handled".
159  }
160 
161  point mouse = get_mouse_position();
162 
163  mouse.x -= get_x();
164  mouse.y -= get_y();
165 
166  std::string link = get_label_link(mouse);
167 
168  if (link.length() == 0) {
169  return ; // without marking event as "handled"
170  }
171 
172  DBG_GUI_E << "Right Clicked Link:\"" << link << "\"";
173 
175 
176  (void) show_message("", _("Copied link!"), dialogs::message::auto_close);
177 
178  handled = true;
179 }
180 
182 {
183  DBG_GUI_E << "label mouse motion";
184 
185  if(!get_link_aware()) {
186  return; // without marking event as "handled"
187  }
188 
189  point mouse = coordinate;
190 
191  mouse.x -= get_x();
192  mouse.y -= get_y();
193 
194  update_mouse_cursor(!get_label_link(mouse).empty());
195 
196  handled = true;
197 }
198 
200 {
201  DBG_GUI_E << "label mouse leave";
202 
203  if(!get_link_aware()) {
204  return; // without marking event as "handled"
205  }
206 
207  // We left the widget, so just unconditionally reset the cursor
208  update_mouse_cursor(false);
209 
210  handled = true;
211 }
212 
213 void label::update_mouse_cursor(bool enable)
214 {
215  // Someone else may set the mouse cursor for us to something unusual (e.g.
216  // the WAIT cursor) so we ought to mess with that only if it's set to
217  // NORMAL or HYPERLINK.
218 
219  if(enable && cursor::get() == cursor::NORMAL) {
221  } else if(!enable && cursor::get() == cursor::HYPERLINK) {
223  }
224 }
225 
226 // }---------- DEFINITION ---------{
227 
230 {
231  DBG_GUI_P << "Parsing label " << id;
232 
233  load_resolutions<resolution>(cfg);
234 }
235 
237  : resolution_definition(cfg)
238  , link_color(cfg["link_color"].empty() ? color_t::from_hex_string("ffff00") : color_t::from_rgba_string(cfg["link_color"].str()))
239 {
240  // Note the order should be the same as the enum state_t is label.hpp.
241  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_enabled", _("Missing required state for text label control")));
242  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", _("Missing required state for text label control")));
243 }
244 
245 // }---------- BUILDER -----------{
246 
247 namespace implementation
248 {
249 
250 builder_label::builder_label(const config& cfg)
251  : builder_styled_widget(cfg)
252  , wrap(cfg["wrap"].to_bool())
253  , characters_per_line(cfg["characters_per_line"])
254  , text_alignment(decode_text_alignment(cfg["text_alignment"]))
255  , can_shrink(cfg["can_shrink"].to_bool(false))
256  , link_aware(cfg["link_aware"].to_bool(false))
257 {
258 }
259 
260 std::unique_ptr<widget> builder_label::build() const
261 {
262  auto lbl = std::make_unique<label>(*this);
263 
264  const auto conf = lbl->cast_config_to<label_definition>();
265  assert(conf);
266 
267  lbl->set_text_alignment(text_alignment);
268  lbl->set_link_color(conf->link_color);
269 
270  DBG_GUI_G << "Window builder: placed label '" << id << "' with definition '"
271  << definition << "'.";
272 
273  return lbl;
274 }
275 
276 } // namespace implementation
277 
278 // }------------ END --------------
279 
280 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
@ yes_no_buttons
Shows a yes and no button.
Definition: message.hpp:81
@ auto_close
Enables auto close.
Definition: message.hpp:71
A label displays text that can be wrapped but no scrollbars are provided.
Definition: label.hpp:57
unsigned short text_alpha_
Definition: label.hpp:184
virtual bool get_link_aware() const override
See styled_widget::get_link_aware.
Definition: label.hpp:76
void signal_handler_left_button_click(bool &handled)
Left click signal handler: checks if we clicked on a hyperlink.
Definition: label.cpp:118
void set_link_color(const color_t &color)
Definition: label.cpp:101
void signal_handler_mouse_leave(bool &handled)
Mouse leave signal handler: checks if the cursor left a hyperlink.
Definition: label.cpp:199
color_t link_color_
What color links will be rendered in.
Definition: label.hpp:180
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:153
bool link_aware_
Whether the label is link aware, rendering links with special formatting and handling click events.
Definition: label.hpp:175
virtual void update_canvas() override
See styled_widget::update_canvas.
Definition: label.cpp:66
void set_state(const state_t state)
Definition: label.cpp:110
state_t
Possible states of the widget.
Definition: label.hpp:146
virtual bool get_active() const override
See styled_widget::get_active.
Definition: label.hpp:91
state_t state_
Current state of the widget.
Definition: label.hpp:159
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: label.cpp:85
void update_mouse_cursor(bool enable)
Implementation detail for (re)setting the hyperlink cursor.
Definition: label.cpp:213
void set_link_aware(bool l)
Definition: label.cpp:92
void set_text_alpha(unsigned short alpha)
Definition: label.cpp:76
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:181
Base class for all visible items.
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:442
int get_x() const
Definition: widget.cpp:316
int get_y() const
Definition: widget.cpp:321
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:217
This file contains the window object, this object is a top level container which has the event manage...
CURSOR_TYPE get()
Definition: cursor.cpp:216
@ NORMAL
Definition: cursor.hpp:29
@ HYPERLINK
Definition: cursor.hpp:29
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:176
void copy_to_clipboard(const std::string &text, const bool)
Copies text to the clipboard.
Definition: clipboard.cpp:34
bool open_object([[maybe_unused]] const std::string &path_or_url)
Definition: open.cpp:47
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:114
PangoAlignment decode_text_alignment(const std::string &alignment)
Converts a text alignment string to a text alignment.
Definition: helper.cpp:60
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:151
@ 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.
This file contains the settings handling of the widget library.
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:260
std::string definition
Parameters for the styled_widget.
resolution(const config &cfg)
Definition: label.cpp:236
label_definition(const config &cfg)
Definition: label.cpp:228
Base class of a resolution, contains the common keys for a resolution.
std::vector< state_definition > state
Holds a 2D point.
Definition: point.hpp:25
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)