diff options
Diffstat (limited to 'scene/gui/option_button.cpp')
-rw-r--r-- | scene/gui/option_button.cpp | 119 |
1 files changed, 90 insertions, 29 deletions
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index e721b01cbc..1e8a149e11 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -32,6 +32,8 @@ #include "core/string/print_string.h" +static const int NONE_SELECTED = -1; + Size2 OptionButton::get_minimum_size() const { Size2 minsize = Button::get_minimum_size(); @@ -89,8 +91,12 @@ void OptionButton::_notification(int p_what) { } arrow->draw(ci, ofs, clr); } break; + case NOTIFICATION_TRANSLATION_CHANGED: - case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + popup->set_layout_direction((Window::LayoutDirection)get_layout_direction()); + [[fallthrough]]; + } case NOTIFICATION_THEME_CHANGED: { if (has_theme_icon(SNAME("arrow"))) { if (is_layout_rtl()) { @@ -102,6 +108,7 @@ void OptionButton::_notification(int p_what) { } } } break; + case NOTIFICATION_VISIBILITY_CHANGED: { if (!is_visible_in_tree()) { popup->hide(); @@ -113,13 +120,18 @@ void OptionButton::_notification(int p_what) { bool OptionButton::_set(const StringName &p_name, const Variant &p_value) { Vector<String> components = String(p_name).split("/", true, 2); if (components.size() >= 2 && components[0] == "popup") { + String property = components[2]; + if (property != "text" && property != "icon" && property != "id" && property != "disabled" && property != "separator") { + return false; + } + bool valid; popup->set(String(p_name).trim_prefix("popup/"), p_value, &valid); int idx = components[1].get_slice("_", 1).to_int(); if (idx == current) { // Force refreshing currently displayed item. - current = -1; + current = NONE_SELECTED; _select(idx, false); } @@ -131,6 +143,11 @@ bool OptionButton::_set(const StringName &p_name, const Variant &p_value) { bool OptionButton::_get(const StringName &p_name, Variant &r_ret) const { Vector<String> components = String(p_name).split("/", true, 2); if (components.size() >= 2 && components[0] == "popup") { + String property = components[2]; + if (property != "text" && property != "icon" && property != "id" && property != "disabled" && property != "separator") { + return false; + } + bool valid; r_ret = popup->get(String(p_name).trim_prefix("popup/"), &valid); return valid; @@ -146,15 +163,7 @@ void OptionButton::_get_property_list(List<PropertyInfo> *p_list) const { pi.usage &= ~(popup->get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0); p_list->push_back(pi); - pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button"); - pi.usage &= ~(!popup->is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - - pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/checked", i)); - pi.usage &= ~(!popup->is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - - pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "1,10,1,or_greater"); + pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "0,10,1,or_greater"); p_list->push_back(pi); pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/disabled", i)); @@ -179,6 +188,17 @@ void OptionButton::pressed() { Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale(); popup->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y)); popup->set_size(Size2(size.width, 0)); + + // If not triggered by the mouse, start the popup with the checked item selected. + if (popup->get_item_count() > 0) { + if ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) || + (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept"))) { + popup->set_current_index(current > -1 ? current : 0); + } else { + popup->scroll_to_item(current > -1 ? current : 0); + } + } + popup->popup(); } @@ -220,6 +240,10 @@ void OptionButton::set_item_metadata(int p_idx, const Variant &p_metadata) { popup->set_item_metadata(p_idx, p_metadata); } +void OptionButton::set_item_tooltip(int p_idx, const String &p_tooltip) { + popup->set_item_tooltip(p_idx, p_tooltip); +} + void OptionButton::set_item_disabled(int p_idx, bool p_disabled) { popup->set_item_disabled(p_idx, p_disabled); } @@ -233,6 +257,10 @@ Ref<Texture2D> OptionButton::get_item_icon(int p_idx) const { } int OptionButton::get_item_id(int p_idx) const { + if (p_idx == NONE_SELECTED) { + return NONE_SELECTED; + } + return popup->get_item_id(p_idx); } @@ -244,13 +272,30 @@ Variant OptionButton::get_item_metadata(int p_idx) const { return popup->get_item_metadata(p_idx); } +String OptionButton::get_item_tooltip(int p_idx) const { + return popup->get_item_tooltip(p_idx); +} + bool OptionButton::is_item_disabled(int p_idx) const { return popup->is_item_disabled(p_idx); } void OptionButton::set_item_count(int p_count) { ERR_FAIL_COND(p_count < 0); + + int count_old = get_item_count(); + if (p_count == count_old) { + return; + } + popup->set_item_count(p_count); + + if (p_count > count_old) { + for (int i = count_old; i < p_count; i++) { + popup->set_item_as_radio_checkable(i, true); + } + } + notify_property_list_changed(); } @@ -265,26 +310,33 @@ void OptionButton::add_separator() { void OptionButton::clear() { popup->clear(); set_text(""); - current = -1; + current = NONE_SELECTED; } void OptionButton::_select(int p_which, bool p_emit) { - if (p_which < 0) { - return; - } if (p_which == current) { return; } - ERR_FAIL_INDEX(p_which, popup->get_item_count()); + if (p_which == NONE_SELECTED) { + for (int i = 0; i < popup->get_item_count(); i++) { + popup->set_item_checked(i, false); + } - for (int i = 0; i < popup->get_item_count(); i++) { - popup->set_item_checked(i, i == p_which); - } + current = NONE_SELECTED; + set_text(""); + set_icon(nullptr); + } else { + ERR_FAIL_INDEX(p_which, popup->get_item_count()); - current = p_which; - set_text(popup->get_item_text(current)); - set_icon(popup->get_item_icon(current)); + for (int i = 0; i < popup->get_item_count(); i++) { + popup->set_item_checked(i, i == p_which); + } + + current = p_which; + set_text(popup->get_item_text(current)); + set_icon(popup->get_item_icon(current)); + } if (is_inside_tree() && p_emit) { emit_signal(SNAME("item_selected"), current); @@ -292,7 +344,7 @@ void OptionButton::_select(int p_which, bool p_emit) { } void OptionButton::_select_int(int p_which) { - if (p_which < 0 || p_which >= popup->get_item_count()) { + if (p_which < NONE_SELECTED || p_which >= popup->get_item_count()) { return; } _select(p_which, false); @@ -307,10 +359,6 @@ int OptionButton::get_selected() const { } int OptionButton::get_selected_id() const { - int idx = get_selected(); - if (idx < 0) { - return 0; - } return get_item_id(current); } @@ -324,6 +372,9 @@ Variant OptionButton::get_selected_metadata() const { void OptionButton::remove_item(int p_idx) { popup->remove_item(p_idx); + if (current == p_idx) { + _select(NONE_SELECTED); + } } PopupMenu *OptionButton::get_popup() const { @@ -334,6 +385,12 @@ void OptionButton::get_translatable_strings(List<String> *p_strings) const { popup->get_translatable_strings(p_strings); } +void OptionButton::_validate_property(PropertyInfo &property) const { + if (property.name == "text" || property.name == "icon") { + property.usage = PROPERTY_USAGE_NONE; + } +} + void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("add_item", "label", "id"), &OptionButton::add_item, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id"), &OptionButton::add_icon_item, DEFVAL(-1)); @@ -342,11 +399,13 @@ void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_disabled", "idx", "disabled"), &OptionButton::set_item_disabled); ClassDB::bind_method(D_METHOD("set_item_id", "idx", "id"), &OptionButton::set_item_id); ClassDB::bind_method(D_METHOD("set_item_metadata", "idx", "metadata"), &OptionButton::set_item_metadata); + ClassDB::bind_method(D_METHOD("set_item_tooltip", "idx", "tooltip"), &OptionButton::set_item_tooltip); ClassDB::bind_method(D_METHOD("get_item_text", "idx"), &OptionButton::get_item_text); ClassDB::bind_method(D_METHOD("get_item_icon", "idx"), &OptionButton::get_item_icon); ClassDB::bind_method(D_METHOD("get_item_id", "idx"), &OptionButton::get_item_id); ClassDB::bind_method(D_METHOD("get_item_index", "id"), &OptionButton::get_item_index); ClassDB::bind_method(D_METHOD("get_item_metadata", "idx"), &OptionButton::get_item_metadata); + ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &OptionButton::get_item_tooltip); ClassDB::bind_method(D_METHOD("is_item_disabled", "idx"), &OptionButton::is_item_disabled); ClassDB::bind_method(D_METHOD("add_separator"), &OptionButton::add_separator); ClassDB::bind_method(D_METHOD("clear"), &OptionButton::clear); @@ -359,8 +418,9 @@ void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_popup"), &OptionButton::get_popup); - ClassDB::bind_method(D_METHOD("set_item_count"), &OptionButton::set_item_count); + ClassDB::bind_method(D_METHOD("set_item_count", "count"), &OptionButton::set_item_count); ClassDB::bind_method(D_METHOD("get_item_count"), &OptionButton::get_item_count); + // "selected" property must come after "item_count", otherwise GH-10213 occurs. ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "popup/item_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected"); @@ -368,7 +428,8 @@ void OptionButton::_bind_methods() { ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "index"))); } -OptionButton::OptionButton() { +OptionButton::OptionButton(const String &p_text) : + Button(p_text) { set_toggle_mode(true); set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT); if (is_layout_rtl()) { |