| 1 | /**************************************************************************/ |
| 2 | /* dialogs.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 "dialogs.h" |
| 32 | |
| 33 | #include "core/os/keyboard.h" |
| 34 | #include "core/string/print_string.h" |
| 35 | #include "core/string/translation.h" |
| 36 | #include "scene/gui/line_edit.h" |
| 37 | #include "scene/theme/theme_db.h" |
| 38 | |
| 39 | // AcceptDialog |
| 40 | |
| 41 | void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) { |
| 42 | if (close_on_escape && p_event->is_action_pressed(SNAME("ui_cancel" ), false, true)) { |
| 43 | _cancel_pressed(); |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | void AcceptDialog::_parent_focused() { |
| 48 | if (!is_exclusive() && get_flag(FLAG_POPUP)) { |
| 49 | _cancel_pressed(); |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | void AcceptDialog::_notification(int p_what) { |
| 54 | switch (p_what) { |
| 55 | case NOTIFICATION_POST_ENTER_TREE: { |
| 56 | if (is_visible()) { |
| 57 | get_ok_button()->grab_focus(); |
| 58 | } |
| 59 | } break; |
| 60 | case NOTIFICATION_VISIBILITY_CHANGED: { |
| 61 | if (is_visible()) { |
| 62 | if (get_ok_button()->is_inside_tree()) { |
| 63 | get_ok_button()->grab_focus(); |
| 64 | } |
| 65 | _update_child_rects(); |
| 66 | |
| 67 | parent_visible = get_parent_visible_window(); |
| 68 | if (parent_visible) { |
| 69 | parent_visible->connect("focus_entered" , callable_mp(this, &AcceptDialog::_parent_focused)); |
| 70 | } |
| 71 | } else { |
| 72 | if (parent_visible) { |
| 73 | parent_visible->disconnect("focus_entered" , callable_mp(this, &AcceptDialog::_parent_focused)); |
| 74 | parent_visible = nullptr; |
| 75 | } |
| 76 | } |
| 77 | } break; |
| 78 | |
| 79 | case NOTIFICATION_THEME_CHANGED: { |
| 80 | bg_panel->add_theme_style_override("panel" , theme_cache.panel_style); |
| 81 | |
| 82 | child_controls_changed(); |
| 83 | if (is_visible()) { |
| 84 | _update_child_rects(); |
| 85 | } |
| 86 | } break; |
| 87 | |
| 88 | case NOTIFICATION_EXIT_TREE: { |
| 89 | if (parent_visible) { |
| 90 | parent_visible->disconnect("focus_entered" , callable_mp(this, &AcceptDialog::_parent_focused)); |
| 91 | parent_visible = nullptr; |
| 92 | } |
| 93 | } break; |
| 94 | |
| 95 | case NOTIFICATION_READY: |
| 96 | case NOTIFICATION_WM_SIZE_CHANGED: { |
| 97 | if (is_visible()) { |
| 98 | _update_child_rects(); |
| 99 | } |
| 100 | } break; |
| 101 | |
| 102 | case NOTIFICATION_WM_CLOSE_REQUEST: { |
| 103 | _cancel_pressed(); |
| 104 | } break; |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | void AcceptDialog::_text_submitted(const String &p_text) { |
| 109 | if (get_ok_button() && get_ok_button()->is_disabled()) { |
| 110 | return; // Do not allow submission if OK button is disabled. |
| 111 | } |
| 112 | _ok_pressed(); |
| 113 | } |
| 114 | |
| 115 | void AcceptDialog::_ok_pressed() { |
| 116 | if (hide_on_ok) { |
| 117 | set_visible(false); |
| 118 | } |
| 119 | ok_pressed(); |
| 120 | emit_signal(SNAME("confirmed" )); |
| 121 | set_input_as_handled(); |
| 122 | } |
| 123 | |
| 124 | void AcceptDialog::_cancel_pressed() { |
| 125 | Window *parent_window = parent_visible; |
| 126 | if (parent_visible) { |
| 127 | parent_visible->disconnect("focus_entered" , callable_mp(this, &AcceptDialog::_parent_focused)); |
| 128 | parent_visible = nullptr; |
| 129 | } |
| 130 | |
| 131 | call_deferred(SNAME("hide" )); |
| 132 | |
| 133 | emit_signal(SNAME("canceled" )); |
| 134 | |
| 135 | cancel_pressed(); |
| 136 | |
| 137 | if (parent_window) { |
| 138 | //parent_window->grab_focus(); |
| 139 | } |
| 140 | set_input_as_handled(); |
| 141 | } |
| 142 | |
| 143 | String AcceptDialog::get_text() const { |
| 144 | return message_label->get_text(); |
| 145 | } |
| 146 | |
| 147 | void AcceptDialog::set_text(String p_text) { |
| 148 | if (message_label->get_text() == p_text) { |
| 149 | return; |
| 150 | } |
| 151 | |
| 152 | message_label->set_text(p_text); |
| 153 | |
| 154 | child_controls_changed(); |
| 155 | if (is_visible()) { |
| 156 | _update_child_rects(); |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | void AcceptDialog::set_hide_on_ok(bool p_hide) { |
| 161 | hide_on_ok = p_hide; |
| 162 | } |
| 163 | |
| 164 | bool AcceptDialog::get_hide_on_ok() const { |
| 165 | return hide_on_ok; |
| 166 | } |
| 167 | |
| 168 | void AcceptDialog::set_close_on_escape(bool p_hide) { |
| 169 | close_on_escape = p_hide; |
| 170 | } |
| 171 | |
| 172 | bool AcceptDialog::get_close_on_escape() const { |
| 173 | return close_on_escape; |
| 174 | } |
| 175 | |
| 176 | void AcceptDialog::set_autowrap(bool p_autowrap) { |
| 177 | message_label->set_autowrap_mode(p_autowrap ? TextServer::AUTOWRAP_WORD : TextServer::AUTOWRAP_OFF); |
| 178 | } |
| 179 | |
| 180 | bool AcceptDialog::has_autowrap() { |
| 181 | return message_label->get_autowrap_mode() != TextServer::AUTOWRAP_OFF; |
| 182 | } |
| 183 | |
| 184 | void AcceptDialog::set_ok_button_text(String p_ok_button_text) { |
| 185 | ok_button->set_text(p_ok_button_text); |
| 186 | |
| 187 | child_controls_changed(); |
| 188 | if (is_visible()) { |
| 189 | _update_child_rects(); |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | String AcceptDialog::get_ok_button_text() const { |
| 194 | return ok_button->get_text(); |
| 195 | } |
| 196 | |
| 197 | void AcceptDialog::register_text_enter(Control *p_line_edit) { |
| 198 | ERR_FAIL_NULL(p_line_edit); |
| 199 | LineEdit *line_edit = Object::cast_to<LineEdit>(p_line_edit); |
| 200 | if (line_edit) { |
| 201 | line_edit->connect("text_submitted" , callable_mp(this, &AcceptDialog::_text_submitted)); |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | void AcceptDialog::_update_child_rects() { |
| 206 | Size2 dlg_size = get_size(); |
| 207 | float h_margins = theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_RIGHT); |
| 208 | float v_margins = theme_cache.panel_style->get_margin(SIDE_TOP) + theme_cache.panel_style->get_margin(SIDE_BOTTOM); |
| 209 | |
| 210 | // Fill the entire size of the window with the background. |
| 211 | bg_panel->set_position(Point2()); |
| 212 | bg_panel->set_size(dlg_size); |
| 213 | |
| 214 | // Place the buttons from the bottom edge to their minimum required size. |
| 215 | Size2 buttons_minsize = buttons_hbox->get_combined_minimum_size(); |
| 216 | Size2 buttons_size = Size2(dlg_size.x - h_margins, buttons_minsize.y); |
| 217 | Point2 buttons_position = Point2(theme_cache.panel_style->get_margin(SIDE_LEFT), dlg_size.y - theme_cache.panel_style->get_margin(SIDE_BOTTOM) - buttons_size.y); |
| 218 | buttons_hbox->set_position(buttons_position); |
| 219 | buttons_hbox->set_size(buttons_size); |
| 220 | |
| 221 | // Place the content from the top to fill the rest of the space (minus the separation). |
| 222 | Point2 content_position = Point2(theme_cache.panel_style->get_margin(SIDE_LEFT), theme_cache.panel_style->get_margin(SIDE_TOP)); |
| 223 | Size2 content_size = Size2(dlg_size.x - h_margins, dlg_size.y - v_margins - buttons_size.y - theme_cache.buttons_separation); |
| 224 | |
| 225 | for (int i = 0; i < get_child_count(); i++) { |
| 226 | Control *c = Object::cast_to<Control>(get_child(i)); |
| 227 | if (!c) { |
| 228 | continue; |
| 229 | } |
| 230 | if (c == buttons_hbox || c == bg_panel || c->is_set_as_top_level()) { |
| 231 | continue; |
| 232 | } |
| 233 | |
| 234 | c->set_position(content_position); |
| 235 | c->set_size(content_size); |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | Size2 AcceptDialog::_get_contents_minimum_size() const { |
| 240 | // First, we then iterate over the label and any other custom controls |
| 241 | // to try and find the size that encompasses all content. |
| 242 | Size2 content_minsize; |
| 243 | for (int i = 0; i < get_child_count(); i++) { |
| 244 | Control *c = Object::cast_to<Control>(get_child(i)); |
| 245 | if (!c) { |
| 246 | continue; |
| 247 | } |
| 248 | |
| 249 | // Buttons will be included afterwards. |
| 250 | // The panel only displays the stylebox and doesn't contribute to the size. |
| 251 | if (c == buttons_hbox || c == bg_panel || c->is_set_as_top_level()) { |
| 252 | continue; |
| 253 | } |
| 254 | |
| 255 | Size2 child_minsize = c->get_combined_minimum_size(); |
| 256 | content_minsize.x = MAX(child_minsize.x, content_minsize.x); |
| 257 | content_minsize.y = MAX(child_minsize.y, content_minsize.y); |
| 258 | } |
| 259 | |
| 260 | // Then we take the background panel as it provides the offsets, |
| 261 | // which are always added to the minimum size. |
| 262 | if (theme_cache.panel_style.is_valid()) { |
| 263 | content_minsize += theme_cache.panel_style->get_minimum_size(); |
| 264 | } |
| 265 | |
| 266 | // Then we add buttons. Horizontally we're interested in whichever |
| 267 | // value is the biggest. Vertically buttons add to the overall size. |
| 268 | Size2 buttons_minsize = buttons_hbox->get_combined_minimum_size(); |
| 269 | content_minsize.x = MAX(buttons_minsize.x, content_minsize.x); |
| 270 | content_minsize.y += buttons_minsize.y; |
| 271 | // Plus there is a separation size added on top. |
| 272 | content_minsize.y += theme_cache.buttons_separation; |
| 273 | |
| 274 | return content_minsize; |
| 275 | } |
| 276 | |
| 277 | void AcceptDialog::_custom_action(const String &p_action) { |
| 278 | emit_signal(SNAME("custom_action" ), p_action); |
| 279 | custom_action(p_action); |
| 280 | } |
| 281 | |
| 282 | void AcceptDialog::_custom_button_visibility_changed(Button *button) { |
| 283 | Control *right_spacer = Object::cast_to<Control>(button->get_meta("__right_spacer" )); |
| 284 | if (right_spacer) { |
| 285 | right_spacer->set_visible(button->is_visible()); |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | Button *AcceptDialog::add_button(const String &p_text, bool p_right, const String &p_action) { |
| 290 | Button *button = memnew(Button); |
| 291 | button->set_text(p_text); |
| 292 | |
| 293 | Control *right_spacer; |
| 294 | if (p_right) { |
| 295 | buttons_hbox->add_child(button); |
| 296 | right_spacer = buttons_hbox->add_spacer(); |
| 297 | } else { |
| 298 | buttons_hbox->add_child(button); |
| 299 | buttons_hbox->move_child(button, 0); |
| 300 | right_spacer = buttons_hbox->add_spacer(true); |
| 301 | } |
| 302 | button->set_meta("__right_spacer" , right_spacer); |
| 303 | |
| 304 | button->connect("visibility_changed" , callable_mp(this, &AcceptDialog::_custom_button_visibility_changed).bind(button)); |
| 305 | |
| 306 | child_controls_changed(); |
| 307 | if (is_visible()) { |
| 308 | _update_child_rects(); |
| 309 | } |
| 310 | |
| 311 | if (!p_action.is_empty()) { |
| 312 | button->connect("pressed" , callable_mp(this, &AcceptDialog::_custom_action).bind(p_action)); |
| 313 | } |
| 314 | |
| 315 | return button; |
| 316 | } |
| 317 | |
| 318 | Button *AcceptDialog::add_cancel_button(const String &p_cancel) { |
| 319 | String c = p_cancel; |
| 320 | if (p_cancel.is_empty()) { |
| 321 | c = "Cancel" ; |
| 322 | } |
| 323 | |
| 324 | Button *b = swap_cancel_ok ? add_button(c, true) : add_button(c); |
| 325 | |
| 326 | b->connect("pressed" , callable_mp(this, &AcceptDialog::_cancel_pressed)); |
| 327 | |
| 328 | return b; |
| 329 | } |
| 330 | |
| 331 | void AcceptDialog::remove_button(Control *p_button) { |
| 332 | Button *button = Object::cast_to<Button>(p_button); |
| 333 | ERR_FAIL_NULL(button); |
| 334 | ERR_FAIL_COND_MSG(button->get_parent() != buttons_hbox, vformat("Cannot remove button %s as it does not belong to this dialog." , button->get_name())); |
| 335 | ERR_FAIL_COND_MSG(button == ok_button, "Cannot remove dialog's OK button." ); |
| 336 | |
| 337 | Control *right_spacer = Object::cast_to<Control>(button->get_meta("__right_spacer" )); |
| 338 | if (right_spacer) { |
| 339 | ERR_FAIL_COND_MSG(right_spacer->get_parent() != buttons_hbox, vformat("Cannot remove button %s as its associated spacer does not belong to this dialog." , button->get_name())); |
| 340 | } |
| 341 | |
| 342 | button->disconnect("visibility_changed" , callable_mp(this, &AcceptDialog::_custom_button_visibility_changed)); |
| 343 | if (button->is_connected("pressed" , callable_mp(this, &AcceptDialog::_custom_action))) { |
| 344 | button->disconnect("pressed" , callable_mp(this, &AcceptDialog::_custom_action)); |
| 345 | } |
| 346 | if (button->is_connected("pressed" , callable_mp(this, &AcceptDialog::_cancel_pressed))) { |
| 347 | button->disconnect("pressed" , callable_mp(this, &AcceptDialog::_cancel_pressed)); |
| 348 | } |
| 349 | |
| 350 | if (right_spacer) { |
| 351 | buttons_hbox->remove_child(right_spacer); |
| 352 | button->remove_meta("__right_spacer" ); |
| 353 | right_spacer->queue_free(); |
| 354 | } |
| 355 | buttons_hbox->remove_child(button); |
| 356 | |
| 357 | child_controls_changed(); |
| 358 | if (is_visible()) { |
| 359 | _update_child_rects(); |
| 360 | } |
| 361 | } |
| 362 | |
| 363 | void AcceptDialog::_bind_methods() { |
| 364 | ClassDB::bind_method(D_METHOD("get_ok_button" ), &AcceptDialog::get_ok_button); |
| 365 | ClassDB::bind_method(D_METHOD("get_label" ), &AcceptDialog::get_label); |
| 366 | ClassDB::bind_method(D_METHOD("set_hide_on_ok" , "enabled" ), &AcceptDialog::set_hide_on_ok); |
| 367 | ClassDB::bind_method(D_METHOD("get_hide_on_ok" ), &AcceptDialog::get_hide_on_ok); |
| 368 | ClassDB::bind_method(D_METHOD("set_close_on_escape" , "enabled" ), &AcceptDialog::set_close_on_escape); |
| 369 | ClassDB::bind_method(D_METHOD("get_close_on_escape" ), &AcceptDialog::get_close_on_escape); |
| 370 | ClassDB::bind_method(D_METHOD("add_button" , "text" , "right" , "action" ), &AcceptDialog::add_button, DEFVAL(false), DEFVAL("" )); |
| 371 | ClassDB::bind_method(D_METHOD("add_cancel_button" , "name" ), &AcceptDialog::add_cancel_button); |
| 372 | ClassDB::bind_method(D_METHOD("remove_button" , "button" ), &AcceptDialog::remove_button); |
| 373 | ClassDB::bind_method(D_METHOD("register_text_enter" , "line_edit" ), &AcceptDialog::register_text_enter); |
| 374 | ClassDB::bind_method(D_METHOD("set_text" , "text" ), &AcceptDialog::set_text); |
| 375 | ClassDB::bind_method(D_METHOD("get_text" ), &AcceptDialog::get_text); |
| 376 | ClassDB::bind_method(D_METHOD("set_autowrap" , "autowrap" ), &AcceptDialog::set_autowrap); |
| 377 | ClassDB::bind_method(D_METHOD("has_autowrap" ), &AcceptDialog::has_autowrap); |
| 378 | ClassDB::bind_method(D_METHOD("set_ok_button_text" , "text" ), &AcceptDialog::set_ok_button_text); |
| 379 | ClassDB::bind_method(D_METHOD("get_ok_button_text" ), &AcceptDialog::get_ok_button_text); |
| 380 | |
| 381 | ADD_SIGNAL(MethodInfo("confirmed" )); |
| 382 | ADD_SIGNAL(MethodInfo("canceled" )); |
| 383 | ADD_SIGNAL(MethodInfo("custom_action" , PropertyInfo(Variant::STRING_NAME, "action" ))); |
| 384 | |
| 385 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "ok_button_text" ), "set_ok_button_text" , "get_ok_button_text" ); |
| 386 | |
| 387 | ADD_GROUP("Dialog" , "dialog_" ); |
| 388 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "dialog_text" , PROPERTY_HINT_MULTILINE_TEXT), "set_text" , "get_text" ); |
| 389 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_hide_on_ok" ), "set_hide_on_ok" , "get_hide_on_ok" ); |
| 390 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_close_on_escape" ), "set_close_on_escape" , "get_close_on_escape" ); |
| 391 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_autowrap" ), "set_autowrap" , "has_autowrap" ); |
| 392 | |
| 393 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, AcceptDialog, panel_style, "panel" ); |
| 394 | BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, AcceptDialog, buttons_separation); |
| 395 | } |
| 396 | |
| 397 | bool AcceptDialog::swap_cancel_ok = false; |
| 398 | void AcceptDialog::set_swap_cancel_ok(bool p_swap) { |
| 399 | swap_cancel_ok = p_swap; |
| 400 | } |
| 401 | |
| 402 | AcceptDialog::AcceptDialog() { |
| 403 | set_wrap_controls(true); |
| 404 | set_visible(false); |
| 405 | set_transient(true); |
| 406 | set_exclusive(true); |
| 407 | set_clamp_to_embedder(true); |
| 408 | |
| 409 | bg_panel = memnew(Panel); |
| 410 | add_child(bg_panel, false, INTERNAL_MODE_FRONT); |
| 411 | |
| 412 | buttons_hbox = memnew(HBoxContainer); |
| 413 | |
| 414 | message_label = memnew(Label); |
| 415 | message_label->set_anchor(SIDE_RIGHT, Control::ANCHOR_END); |
| 416 | message_label->set_anchor(SIDE_BOTTOM, Control::ANCHOR_END); |
| 417 | add_child(message_label, false, INTERNAL_MODE_FRONT); |
| 418 | |
| 419 | add_child(buttons_hbox, false, INTERNAL_MODE_FRONT); |
| 420 | |
| 421 | buttons_hbox->add_spacer(); |
| 422 | ok_button = memnew(Button); |
| 423 | ok_button->set_text("OK" ); |
| 424 | buttons_hbox->add_child(ok_button); |
| 425 | buttons_hbox->add_spacer(); |
| 426 | |
| 427 | ok_button->connect("pressed" , callable_mp(this, &AcceptDialog::_ok_pressed)); |
| 428 | |
| 429 | set_title(TTRC("Alert!" )); |
| 430 | |
| 431 | connect("window_input" , callable_mp(this, &AcceptDialog::_input_from_window)); |
| 432 | } |
| 433 | |
| 434 | AcceptDialog::~AcceptDialog() { |
| 435 | } |
| 436 | |
| 437 | // ConfirmationDialog |
| 438 | |
| 439 | void ConfirmationDialog::set_cancel_button_text(String p_cancel_button_text) { |
| 440 | cancel->set_text(p_cancel_button_text); |
| 441 | } |
| 442 | |
| 443 | String ConfirmationDialog::get_cancel_button_text() const { |
| 444 | return cancel->get_text(); |
| 445 | } |
| 446 | |
| 447 | void ConfirmationDialog::_bind_methods() { |
| 448 | ClassDB::bind_method(D_METHOD("get_cancel_button" ), &ConfirmationDialog::get_cancel_button); |
| 449 | ClassDB::bind_method(D_METHOD("set_cancel_button_text" , "text" ), &ConfirmationDialog::set_cancel_button_text); |
| 450 | ClassDB::bind_method(D_METHOD("get_cancel_button_text" ), &ConfirmationDialog::get_cancel_button_text); |
| 451 | |
| 452 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "cancel_button_text" ), "set_cancel_button_text" , "get_cancel_button_text" ); |
| 453 | } |
| 454 | |
| 455 | Button *ConfirmationDialog::get_cancel_button() { |
| 456 | return cancel; |
| 457 | } |
| 458 | |
| 459 | ConfirmationDialog::ConfirmationDialog() { |
| 460 | set_title(TTRC("Please Confirm..." )); |
| 461 | set_min_size(Size2(200, 70)); |
| 462 | |
| 463 | cancel = add_cancel_button(); |
| 464 | } |
| 465 | |