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 | |
36 | void 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 | |
50 | void 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 | |
61 | String LinkButton::get_text() const { |
62 | return text; |
63 | } |
64 | |
65 | void 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 | |
73 | TextServer::StructuredTextParser LinkButton::get_structured_text_bidi_override() const { |
74 | return st_parser; |
75 | } |
76 | |
77 | void LinkButton::set_structured_text_bidi_override_options(Array p_args) { |
78 | st_args = p_args; |
79 | _shape(); |
80 | queue_redraw(); |
81 | } |
82 | |
83 | Array LinkButton::get_structured_text_bidi_override_options() const { |
84 | return st_args; |
85 | } |
86 | |
87 | void 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 | |
96 | Control::TextDirection LinkButton::get_text_direction() const { |
97 | return text_direction; |
98 | } |
99 | |
100 | void LinkButton::set_language(const String &p_language) { |
101 | if (language != p_language) { |
102 | language = p_language; |
103 | _shape(); |
104 | queue_redraw(); |
105 | } |
106 | } |
107 | |
108 | String LinkButton::get_language() const { |
109 | return language; |
110 | } |
111 | |
112 | void LinkButton::set_uri(const String &p_uri) { |
113 | uri = p_uri; |
114 | } |
115 | |
116 | String LinkButton::get_uri() const { |
117 | return uri; |
118 | } |
119 | |
120 | void 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 | |
129 | LinkButton::UnderlineMode LinkButton::get_underline_mode() const { |
130 | return underline_mode; |
131 | } |
132 | |
133 | void LinkButton::pressed() { |
134 | if (uri.is_empty()) { |
135 | return; |
136 | } |
137 | |
138 | OS::get_singleton()->shell_open(uri); |
139 | } |
140 | |
141 | Size2 LinkButton::get_minimum_size() const { |
142 | return text_buf->get_size(); |
143 | } |
144 | |
145 | void 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 | |
239 | void 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 | |
286 | LinkButton::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 | |