1/**************************************************************************/
2/* link_button.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "link_button.h"
32
33#include "core/string/translation.h"
34#include "scene/theme/theme_db.h"
35
36void LinkButton::_shape() {
37 Ref<Font> font = theme_cache.font;
38 int font_size = theme_cache.font_size;
39
40 text_buf->clear();
41 if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
42 text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
43 } else {
44 text_buf->set_direction((TextServer::Direction)text_direction);
45 }
46 TS->shaped_text_set_bidi_override(text_buf->get_rid(), structured_text_parser(st_parser, st_args, xl_text));
47 text_buf->add_string(xl_text, font, font_size, language);
48}
49
50void LinkButton::set_text(const String &p_text) {
51 if (text == p_text) {
52 return;
53 }
54 text = p_text;
55 xl_text = atr(text);
56 _shape();
57 update_minimum_size();
58 queue_redraw();
59}
60
61String LinkButton::get_text() const {
62 return text;
63}
64
65void LinkButton::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) {
66 if (st_parser != p_parser) {
67 st_parser = p_parser;
68 _shape();
69 queue_redraw();
70 }
71}
72
73TextServer::StructuredTextParser LinkButton::get_structured_text_bidi_override() const {
74 return st_parser;
75}
76
77void LinkButton::set_structured_text_bidi_override_options(Array p_args) {
78 st_args = p_args;
79 _shape();
80 queue_redraw();
81}
82
83Array LinkButton::get_structured_text_bidi_override_options() const {
84 return st_args;
85}
86
87void LinkButton::set_text_direction(Control::TextDirection p_text_direction) {
88 ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
89 if (text_direction != p_text_direction) {
90 text_direction = p_text_direction;
91 _shape();
92 queue_redraw();
93 }
94}
95
96Control::TextDirection LinkButton::get_text_direction() const {
97 return text_direction;
98}
99
100void LinkButton::set_language(const String &p_language) {
101 if (language != p_language) {
102 language = p_language;
103 _shape();
104 queue_redraw();
105 }
106}
107
108String LinkButton::get_language() const {
109 return language;
110}
111
112void LinkButton::set_uri(const String &p_uri) {
113 uri = p_uri;
114}
115
116String LinkButton::get_uri() const {
117 return uri;
118}
119
120void LinkButton::set_underline_mode(UnderlineMode p_underline_mode) {
121 if (underline_mode == p_underline_mode) {
122 return;
123 }
124
125 underline_mode = p_underline_mode;
126 queue_redraw();
127}
128
129LinkButton::UnderlineMode LinkButton::get_underline_mode() const {
130 return underline_mode;
131}
132
133void LinkButton::pressed() {
134 if (uri.is_empty()) {
135 return;
136 }
137
138 OS::get_singleton()->shell_open(uri);
139}
140
141Size2 LinkButton::get_minimum_size() const {
142 return text_buf->get_size();
143}
144
145void LinkButton::_notification(int p_what) {
146 switch (p_what) {
147 case NOTIFICATION_TRANSLATION_CHANGED: {
148 xl_text = atr(text);
149 _shape();
150 update_minimum_size();
151 queue_redraw();
152 } break;
153
154 case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
155 queue_redraw();
156 } break;
157
158 case NOTIFICATION_THEME_CHANGED: {
159 _shape();
160 update_minimum_size();
161 queue_redraw();
162 } break;
163
164 case NOTIFICATION_DRAW: {
165 RID ci = get_canvas_item();
166 Size2 size = get_size();
167 Color color;
168 bool do_underline = false;
169
170 switch (get_draw_mode()) {
171 case DRAW_NORMAL: {
172 if (has_focus()) {
173 color = theme_cache.font_focus_color;
174 } else {
175 color = theme_cache.font_color;
176 }
177
178 do_underline = underline_mode == UNDERLINE_MODE_ALWAYS;
179 } break;
180 case DRAW_HOVER_PRESSED:
181 case DRAW_PRESSED: {
182 if (has_theme_color(SNAME("font_pressed_color"))) {
183 color = theme_cache.font_pressed_color;
184 } else {
185 color = theme_cache.font_color;
186 }
187
188 do_underline = underline_mode != UNDERLINE_MODE_NEVER;
189
190 } break;
191 case DRAW_HOVER: {
192 color = theme_cache.font_hover_color;
193 do_underline = underline_mode != UNDERLINE_MODE_NEVER;
194
195 } break;
196 case DRAW_DISABLED: {
197 color = theme_cache.font_disabled_color;
198 do_underline = underline_mode == UNDERLINE_MODE_ALWAYS;
199
200 } break;
201 }
202
203 if (has_focus()) {
204 Ref<StyleBox> style = theme_cache.focus;
205 style->draw(ci, Rect2(Point2(), size));
206 }
207
208 int width = text_buf->get_line_width();
209
210 Color font_outline_color = theme_cache.font_outline_color;
211 int outline_size = theme_cache.outline_size;
212 if (is_layout_rtl()) {
213 if (outline_size > 0 && font_outline_color.a > 0) {
214 text_buf->draw_outline(get_canvas_item(), Vector2(size.width - width, 0), outline_size, font_outline_color);
215 }
216 text_buf->draw(get_canvas_item(), Vector2(size.width - width, 0), color);
217 } else {
218 if (outline_size > 0 && font_outline_color.a > 0) {
219 text_buf->draw_outline(get_canvas_item(), Vector2(0, 0), outline_size, font_outline_color);
220 }
221 text_buf->draw(get_canvas_item(), Vector2(0, 0), color);
222 }
223
224 if (do_underline) {
225 int underline_spacing = theme_cache.underline_spacing + text_buf->get_line_underline_position();
226 int y = text_buf->get_line_ascent() + underline_spacing;
227 int underline_thickness = MAX(1, text_buf->get_line_underline_thickness());
228
229 if (is_layout_rtl()) {
230 draw_line(Vector2(size.width - width, y), Vector2(size.width, y), color, underline_thickness);
231 } else {
232 draw_line(Vector2(0, y), Vector2(width, y), color, underline_thickness);
233 }
234 }
235 } break;
236 }
237}
238
239void LinkButton::_bind_methods() {
240 ClassDB::bind_method(D_METHOD("set_text", "text"), &LinkButton::set_text);
241 ClassDB::bind_method(D_METHOD("get_text"), &LinkButton::get_text);
242 ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &LinkButton::set_text_direction);
243 ClassDB::bind_method(D_METHOD("get_text_direction"), &LinkButton::get_text_direction);
244 ClassDB::bind_method(D_METHOD("set_language", "language"), &LinkButton::set_language);
245 ClassDB::bind_method(D_METHOD("get_language"), &LinkButton::get_language);
246 ClassDB::bind_method(D_METHOD("set_uri", "uri"), &LinkButton::set_uri);
247 ClassDB::bind_method(D_METHOD("get_uri"), &LinkButton::get_uri);
248 ClassDB::bind_method(D_METHOD("set_underline_mode", "underline_mode"), &LinkButton::set_underline_mode);
249 ClassDB::bind_method(D_METHOD("get_underline_mode"), &LinkButton::get_underline_mode);
250 ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &LinkButton::set_structured_text_bidi_override);
251 ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &LinkButton::get_structured_text_bidi_override);
252 ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &LinkButton::set_structured_text_bidi_override_options);
253 ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &LinkButton::get_structured_text_bidi_override_options);
254
255 BIND_ENUM_CONSTANT(UNDERLINE_MODE_ALWAYS);
256 BIND_ENUM_CONSTANT(UNDERLINE_MODE_ON_HOVER);
257 BIND_ENUM_CONSTANT(UNDERLINE_MODE_NEVER);
258
259 ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
260 ADD_PROPERTY(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode");
261 ADD_PROPERTY(PropertyInfo(Variant::STRING, "uri"), "set_uri", "get_uri");
262
263 ADD_GROUP("BiDi", "");
264 ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
265 ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
266 ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
267 ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
268
269 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, LinkButton, focus);
270
271 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LinkButton, font_color);
272 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LinkButton, font_focus_color);
273 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LinkButton, font_pressed_color);
274 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LinkButton, font_hover_color);
275 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LinkButton, font_hover_pressed_color);
276 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LinkButton, font_disabled_color);
277
278 BIND_THEME_ITEM(Theme::DATA_TYPE_FONT, LinkButton, font);
279 BIND_THEME_ITEM(Theme::DATA_TYPE_FONT_SIZE, LinkButton, font_size);
280 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, LinkButton, outline_size);
281 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LinkButton, font_outline_color);
282
283 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, LinkButton, underline_spacing);
284}
285
286LinkButton::LinkButton(const String &p_text) {
287 text_buf.instantiate();
288 set_focus_mode(FOCUS_NONE);
289 set_default_cursor_shape(CURSOR_POINTING_HAND);
290
291 set_text(p_text);
292}
293