Merge pull request #65446 from YuriSizov/dialogs-make-styleboxes-matter

Make `AcceptDialog` and derivatives utilize `StyleBox` fully
This commit is contained in:
Rémi Verschelde 2022-09-07 09:09:15 +02:00
commit 2841bc358c
5 changed files with 125 additions and 93 deletions

View File

@ -99,14 +99,11 @@
</signal> </signal>
</signals> </signals>
<theme_items> <theme_items>
<theme_item name="button_margin" data_type="constant" type="int" default="32"> <theme_item name="buttons_separation" data_type="constant" type="int" default="10">
Offset that is applied to the content of the window on the bottom, effectively moving the button row. The size of the vertical space between the dialog's content and the button row.
</theme_item>
<theme_item name="margin" data_type="constant" type="int" default="8">
Offset that is applied to the content of the window on top, left, and right.
</theme_item> </theme_item>
<theme_item name="panel" data_type="style" type="StyleBox"> <theme_item name="panel" data_type="style" type="StyleBox">
Panel that fills up the background of the window. The panel that fills the background of the window.
</theme_item> </theme_item>
</theme_items> </theme_items>
</class> </class>

View File

@ -1420,6 +1420,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// AcceptDialog // AcceptDialog
theme->set_stylebox("panel", "AcceptDialog", style_window_title); theme->set_stylebox("panel", "AcceptDialog", style_window_title);
theme->set_constant("buttons_separation", "AcceptDialog", 8 * EDSCALE);
// HScrollBar // HScrollBar
Ref<Texture2D> empty_icon = memnew(ImageTexture); Ref<Texture2D> empty_icon = memnew(ImageTexture);

View File

@ -54,8 +54,7 @@ void AcceptDialog::_update_theme_item_cache() {
Window::_update_theme_item_cache(); Window::_update_theme_item_cache();
theme_cache.panel_style = get_theme_stylebox(SNAME("panel")); theme_cache.panel_style = get_theme_stylebox(SNAME("panel"));
theme_cache.margin = get_theme_constant(SNAME("margin")); theme_cache.buttons_separation = get_theme_constant(SNAME("buttons_separation"));
theme_cache.button_margin = get_theme_constant(SNAME("button_margin"));
} }
void AcceptDialog::_notification(int p_what) { void AcceptDialog::_notification(int p_what) {
@ -64,6 +63,7 @@ void AcceptDialog::_notification(int p_what) {
if (is_visible()) { if (is_visible()) {
get_ok_button()->grab_focus(); get_ok_button()->grab_focus();
_update_child_rects(); _update_child_rects();
parent_visible = get_parent_visible_window(); parent_visible = get_parent_visible_window();
if (parent_visible) { if (parent_visible) {
parent_visible->connect("focus_entered", callable_mp(this, &AcceptDialog::_parent_focused)); parent_visible->connect("focus_entered", callable_mp(this, &AcceptDialog::_parent_focused));
@ -77,10 +77,12 @@ void AcceptDialog::_notification(int p_what) {
} break; } break;
case NOTIFICATION_THEME_CHANGED: { case NOTIFICATION_THEME_CHANGED: {
bg->add_theme_style_override("panel", theme_cache.panel_style); bg_panel->add_theme_style_override("panel", theme_cache.panel_style);
label->set_begin(Point2(theme_cache.margin, theme_cache.margin)); child_controls_changed();
label->set_end(Point2(-theme_cache.margin, -theme_cache.button_margin - 10)); if (is_visible()) {
_update_child_rects();
}
} break; } break;
case NOTIFICATION_EXIT_TREE: { case NOTIFICATION_EXIT_TREE: {
@ -137,14 +139,16 @@ void AcceptDialog::_cancel_pressed() {
} }
String AcceptDialog::get_text() const { String AcceptDialog::get_text() const {
return label->get_text(); return message_label->get_text();
} }
void AcceptDialog::set_text(String p_text) { void AcceptDialog::set_text(String p_text) {
if (label->get_text() == p_text) { if (message_label->get_text() == p_text) {
return; return;
} }
label->set_text(p_text);
message_label->set_text(p_text);
child_controls_changed(); child_controls_changed();
if (is_visible()) { if (is_visible()) {
_update_child_rects(); _update_child_rects();
@ -168,19 +172,24 @@ bool AcceptDialog::get_close_on_escape() const {
} }
void AcceptDialog::set_autowrap(bool p_autowrap) { void AcceptDialog::set_autowrap(bool p_autowrap) {
label->set_autowrap_mode(p_autowrap ? TextServer::AUTOWRAP_WORD : TextServer::AUTOWRAP_OFF); message_label->set_autowrap_mode(p_autowrap ? TextServer::AUTOWRAP_WORD : TextServer::AUTOWRAP_OFF);
} }
bool AcceptDialog::has_autowrap() { bool AcceptDialog::has_autowrap() {
return label->get_autowrap_mode() != TextServer::AUTOWRAP_OFF; return message_label->get_autowrap_mode() != TextServer::AUTOWRAP_OFF;
} }
void AcceptDialog::set_ok_button_text(String p_ok_button_text) { void AcceptDialog::set_ok_button_text(String p_ok_button_text) {
ok->set_text(p_ok_button_text); ok_button->set_text(p_ok_button_text);
child_controls_changed();
if (is_visible()) {
_update_child_rects();
}
} }
String AcceptDialog::get_ok_button_text() const { String AcceptDialog::get_ok_button_text() const {
return ok->get_text(); return ok_button->get_text();
} }
void AcceptDialog::register_text_enter(Control *p_line_edit) { void AcceptDialog::register_text_enter(Control *p_line_edit) {
@ -192,68 +201,79 @@ void AcceptDialog::register_text_enter(Control *p_line_edit) {
} }
void AcceptDialog::_update_child_rects() { void AcceptDialog::_update_child_rects() {
Size2 label_size = label->get_minimum_size();
if (label->get_text().is_empty()) {
label_size.height = 0;
}
Size2 size = get_size(); Size2 size = get_size();
Size2 hminsize = hbc->get_combined_minimum_size(); float h_margins = theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_RIGHT);
float v_margins = theme_cache.panel_style->get_margin(SIDE_TOP) + theme_cache.panel_style->get_margin(SIDE_BOTTOM);
Vector2 cpos(theme_cache.margin, theme_cache.margin + label_size.height); // Fill the entire size of the window with the background.
Vector2 csize(size.x - theme_cache.margin * 2, size.y - theme_cache.margin * 3 - hminsize.y - label_size.height); bg_panel->set_position(Point2());
bg_panel->set_size(size);
// Place the buttons from the bottom edge to their minimum required size.
Size2 buttons_minsize = buttons_hbox->get_combined_minimum_size();
Size2 buttons_size = Size2(size.x - h_margins, buttons_minsize.y);
Point2 buttons_position = Point2(theme_cache.panel_style->get_margin(SIDE_LEFT), size.y - theme_cache.panel_style->get_margin(SIDE_BOTTOM) - buttons_size.y);
buttons_hbox->set_position(buttons_position);
buttons_hbox->set_size(buttons_size);
// Place the content from the top to fill the rest of the space (minus the separation).
Point2 content_position = Point2(theme_cache.panel_style->get_margin(SIDE_LEFT), theme_cache.panel_style->get_margin(SIDE_TOP));
Size2 content_size = Size2(size.x - h_margins, size.y - v_margins - buttons_size.y - theme_cache.buttons_separation);
for (int i = 0; i < get_child_count(); i++) { for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i)); Control *c = Object::cast_to<Control>(get_child(i));
if (!c) { if (!c) {
continue; continue;
} }
if (c == buttons_hbox || c == bg_panel || c->is_set_as_top_level()) {
if (c == hbc || c == label || c == bg || c->is_set_as_top_level()) {
continue; continue;
} }
c->set_position(cpos); c->set_position(content_position);
c->set_size(csize); c->set_size(content_size);
} }
cpos.y += csize.y + theme_cache.margin;
csize.y = hminsize.y;
hbc->set_position(cpos);
hbc->set_size(csize);
bg->set_position(Point2());
bg->set_size(size);
} }
Size2 AcceptDialog::_get_contents_minimum_size() const { Size2 AcceptDialog::_get_contents_minimum_size() const {
Size2 minsize = label->get_combined_minimum_size(); // First, we then iterate over the label and any other custom controls
// to try and find the size that encompasses all content.
Size2 content_minsize;
for (int i = 0; i < get_child_count(); i++) { for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i)); Control *c = Object::cast_to<Control>(get_child(i));
if (!c) { if (!c) {
continue; continue;
} }
if (c == hbc || c == label || c->is_set_as_top_level()) { // Buttons will be included afterwards.
// The panel only displays the stylebox and doesn't contribute to the size.
if (c == buttons_hbox || c == bg_panel || c->is_set_as_top_level()) {
continue; continue;
} }
Size2 cminsize = c->get_combined_minimum_size(); Size2 child_minsize = c->get_combined_minimum_size();
minsize.x = MAX(cminsize.x, minsize.x); content_minsize.x = MAX(child_minsize.x, content_minsize.x);
minsize.y = MAX(cminsize.y, minsize.y); content_minsize.y = MAX(child_minsize.y, content_minsize.y);
} }
Size2 hminsize = hbc->get_combined_minimum_size(); // Then we take the background panel as it provides the offsets,
minsize.x = MAX(hminsize.x, minsize.x); // which are always added to the minimum size.
minsize.y += hminsize.y; if (theme_cache.panel_style.is_valid()) {
minsize.x += theme_cache.margin * 2; content_minsize += theme_cache.panel_style->get_minimum_size();
minsize.y += theme_cache.margin * 3; //one as separation between hbc and child }
Size2 wmsize = get_min_size(); // Then we add buttons. Horizontally we're interested in whichever
minsize.x = MAX(wmsize.x, minsize.x); // value is the biggest. Vertically buttons add to the overall size.
return minsize; Size2 buttons_minsize = buttons_hbox->get_combined_minimum_size();
content_minsize.x = MAX(buttons_minsize.x, content_minsize.x);
content_minsize.y += buttons_minsize.y;
// Plus there is a separation size added on top.
content_minsize.y += theme_cache.buttons_separation;
// Last, we make sure that we aren't below the minimum window size.
Size2 window_minsize = get_min_size();
content_minsize.x = MAX(window_minsize.x, content_minsize.x);
content_minsize.y = MAX(window_minsize.y, content_minsize.y);
return content_minsize;
} }
void AcceptDialog::_custom_action(const String &p_action) { void AcceptDialog::_custom_action(const String &p_action) {
@ -264,13 +284,19 @@ void AcceptDialog::_custom_action(const String &p_action) {
Button *AcceptDialog::add_button(const String &p_text, bool p_right, const String &p_action) { Button *AcceptDialog::add_button(const String &p_text, bool p_right, const String &p_action) {
Button *button = memnew(Button); Button *button = memnew(Button);
button->set_text(p_text); button->set_text(p_text);
if (p_right) { if (p_right) {
hbc->add_child(button); buttons_hbox->add_child(button);
hbc->add_spacer(); buttons_hbox->add_spacer();
} else { } else {
hbc->add_child(button); buttons_hbox->add_child(button);
hbc->move_child(button, 0); buttons_hbox->move_child(button, 0);
hbc->add_spacer(true); buttons_hbox->add_spacer(true);
}
child_controls_changed();
if (is_visible()) {
_update_child_rects();
} }
if (!p_action.is_empty()) { if (!p_action.is_empty()) {
@ -285,24 +311,19 @@ Button *AcceptDialog::add_cancel_button(const String &p_cancel) {
if (p_cancel.is_empty()) { if (p_cancel.is_empty()) {
c = "Cancel"; c = "Cancel";
} }
Button *b = swap_cancel_ok ? add_button(c, true) : add_button(c); Button *b = swap_cancel_ok ? add_button(c, true) : add_button(c);
b->connect("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed)); b->connect("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed));
return b; return b;
} }
void AcceptDialog::remove_button(Control *p_button) { void AcceptDialog::remove_button(Control *p_button) {
Button *button = Object::cast_to<Button>(p_button); Button *button = Object::cast_to<Button>(p_button);
ERR_FAIL_NULL(button); ERR_FAIL_NULL(button);
ERR_FAIL_COND_MSG(button->get_parent() != hbc, vformat("Cannot remove button %s as it does not belong to this dialog.", button->get_name())); 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()));
ERR_FAIL_COND_MSG(button == ok, "Cannot remove dialog's OK button."); ERR_FAIL_COND_MSG(button == ok_button, "Cannot remove dialog's OK button.");
Node *right_spacer = hbc->get_child(button->get_index() + 1);
// Should always be valid but let's avoid crashing
if (right_spacer) {
hbc->remove_child(right_spacer);
memdelete(right_spacer);
}
hbc->remove_child(button);
if (button->is_connected("pressed", callable_mp(this, &AcceptDialog::_custom_action))) { if (button->is_connected("pressed", callable_mp(this, &AcceptDialog::_custom_action))) {
button->disconnect("pressed", callable_mp(this, &AcceptDialog::_custom_action)); button->disconnect("pressed", callable_mp(this, &AcceptDialog::_custom_action));
@ -310,6 +331,19 @@ void AcceptDialog::remove_button(Control *p_button) {
if (button->is_connected("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed))) { if (button->is_connected("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed))) {
button->disconnect("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed)); button->disconnect("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed));
} }
Node *right_spacer = buttons_hbox->get_child(button->get_index() + 1);
// Should always be valid but let's avoid crashing.
if (right_spacer) {
buttons_hbox->remove_child(right_spacer);
memdelete(right_spacer);
}
buttons_hbox->remove_child(button);
child_controls_changed();
if (is_visible()) {
_update_child_rects();
}
} }
void AcceptDialog::_bind_methods() { void AcceptDialog::_bind_methods() {
@ -355,25 +389,25 @@ AcceptDialog::AcceptDialog() {
set_exclusive(true); set_exclusive(true);
set_clamp_to_embedder(true); set_clamp_to_embedder(true);
bg = memnew(Panel); bg_panel = memnew(Panel);
add_child(bg, false, INTERNAL_MODE_FRONT); add_child(bg_panel, false, INTERNAL_MODE_FRONT);
hbc = memnew(HBoxContainer); buttons_hbox = memnew(HBoxContainer);
label = memnew(Label); message_label = memnew(Label);
label->set_anchor(SIDE_RIGHT, Control::ANCHOR_END); message_label->set_anchor(SIDE_RIGHT, Control::ANCHOR_END);
label->set_anchor(SIDE_BOTTOM, Control::ANCHOR_END); message_label->set_anchor(SIDE_BOTTOM, Control::ANCHOR_END);
add_child(label, false, INTERNAL_MODE_FRONT); add_child(message_label, false, INTERNAL_MODE_FRONT);
add_child(hbc, false, INTERNAL_MODE_FRONT); add_child(buttons_hbox, false, INTERNAL_MODE_FRONT);
hbc->add_spacer(); buttons_hbox->add_spacer();
ok = memnew(Button); ok_button = memnew(Button);
ok->set_text("OK"); ok_button->set_text("OK");
hbc->add_child(ok); buttons_hbox->add_child(ok_button);
hbc->add_spacer(); buttons_hbox->add_spacer();
ok->connect("pressed", callable_mp(this, &AcceptDialog::_ok_pressed)); ok_button->connect("pressed", callable_mp(this, &AcceptDialog::_ok_pressed));
set_title(TTRC("Alert!")); set_title(TTRC("Alert!"));

View File

@ -45,17 +45,18 @@ class AcceptDialog : public Window {
GDCLASS(AcceptDialog, Window); GDCLASS(AcceptDialog, Window);
Window *parent_visible = nullptr; Window *parent_visible = nullptr;
Panel *bg = nullptr;
HBoxContainer *hbc = nullptr; Panel *bg_panel = nullptr;
Label *label = nullptr; Label *message_label = nullptr;
Button *ok = nullptr; HBoxContainer *buttons_hbox = nullptr;
Button *ok_button = nullptr;
bool hide_on_ok = true; bool hide_on_ok = true;
bool close_on_escape = true; bool close_on_escape = true;
struct ThemeCache { struct ThemeCache {
Ref<StyleBox> panel_style; Ref<StyleBox> panel_style;
int margin = 0; int buttons_separation = 0;
int button_margin = 0;
} theme_cache; } theme_cache;
void _custom_action(const String &p_action); void _custom_action(const String &p_action);
@ -82,12 +83,12 @@ protected:
void _cancel_pressed(); void _cancel_pressed();
public: public:
Label *get_label() { return label; } Label *get_label() { return message_label; }
static void set_swap_cancel_ok(bool p_swap); static void set_swap_cancel_ok(bool p_swap);
void register_text_enter(Control *p_line_edit); void register_text_enter(Control *p_line_edit);
Button *get_ok_button() { return ok; } Button *get_ok_button() { return ok_button; }
Button *add_button(const String &p_text, bool p_right = false, const String &p_action = ""); Button *add_button(const String &p_text, bool p_right = false, const String &p_action = "");
Button *add_cancel_button(const String &p_cancel = ""); Button *add_cancel_button(const String &p_cancel = "");
void remove_button(Control *p_button); void remove_button(Control *p_button);

View File

@ -609,9 +609,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// Dialogs // Dialogs
// AcceptDialog is currently the base dialog, so this defines styles for all extending nodes. // AcceptDialog is currently the base dialog, so this defines styles for all extending nodes.
theme->set_constant("margin", "AcceptDialog", 8 * scale); theme->set_stylebox("panel", "AcceptDialog", make_flat_stylebox(style_popup_color, 8 * scale, 8 * scale, 8 * scale, 8 * scale));
theme->set_constant("button_margin", "AcceptDialog", 32 * scale); theme->set_constant("buttons_separation", "AcceptDialog", 10 * scale);
theme->set_stylebox("panel", "AcceptDialog", make_flat_stylebox(style_popup_color, 0, 0, 0, 0));
// File Dialog // File Dialog