summaryrefslogtreecommitdiff
path: root/scene/gui
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui')
-rw-r--r--scene/gui/box_container.cpp2
-rw-r--r--scene/gui/button.cpp15
-rw-r--r--scene/gui/control.cpp4
-rw-r--r--scene/gui/file_dialog.cpp8
-rw-r--r--scene/gui/gradient_edit.cpp100
-rw-r--r--scene/gui/gradient_edit.h7
-rw-r--r--scene/gui/item_list.cpp132
-rw-r--r--scene/gui/item_list.h17
-rw-r--r--scene/gui/line_edit.cpp19
-rw-r--r--scene/gui/line_edit.h4
-rw-r--r--scene/gui/link_button.cpp7
-rw-r--r--scene/gui/menu_button.cpp74
-rw-r--r--scene/gui/menu_button.h9
-rw-r--r--scene/gui/option_button.cpp8
-rw-r--r--scene/gui/popup.cpp2
-rw-r--r--scene/gui/popup_menu.cpp217
-rw-r--r--scene/gui/popup_menu.h8
-rw-r--r--scene/gui/rich_text_label.cpp25
-rw-r--r--scene/gui/rich_text_label.h7
-rw-r--r--scene/gui/tab_bar.cpp6
-rw-r--r--scene/gui/text_edit.cpp20
-rw-r--r--scene/gui/text_edit.h4
22 files changed, 479 insertions, 216 deletions
diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp
index cf2df4e1a2..cb9f13e970 100644
--- a/scene/gui/box_container.cpp
+++ b/scene/gui/box_container.cpp
@@ -354,7 +354,7 @@ MarginContainer *VBoxContainer::add_margin_child(const String &p_label, Control
add_child(l, false, INTERNAL_MODE_FRONT);
MarginContainer *mc = memnew(MarginContainer);
mc->add_theme_constant_override("margin_left", 0);
- mc->add_child(p_control);
+ mc->add_child(p_control, true);
add_child(mc, false, INTERNAL_MODE_FRONT);
if (p_expand) {
mc->set_v_size_flags(SIZE_EXPAND_FILL);
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index e253a27e66..9818c8f0cc 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -111,9 +111,18 @@ void Button::_notification(int p_what) {
if (!flat) {
style->draw(ci, Rect2(Point2(0, 0), size));
}
- color = get_theme_color(SNAME("font_color"));
- if (has_theme_color(SNAME("icon_normal_color"))) {
- color_icon = get_theme_color(SNAME("icon_normal_color"));
+
+ // Focus colors only take precedence over normal state.
+ if (has_focus()) {
+ color = get_theme_color(SNAME("font_focus_color"));
+ if (has_theme_color(SNAME("icon_focus_color"))) {
+ color_icon = get_theme_color(SNAME("icon_focus_color"));
+ }
+ } else {
+ color = get_theme_color(SNAME("font_color"));
+ if (has_theme_color(SNAME("icon_normal_color"))) {
+ color_icon = get_theme_color(SNAME("icon_normal_color"));
+ }
}
} break;
case DRAW_HOVER_PRESSED: {
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 77a1efd021..582c8e5860 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -73,8 +73,8 @@ Dictionary Control::_edit_get_state() const {
void Control::_edit_set_state(const Dictionary &p_state) {
ERR_FAIL_COND((p_state.size() <= 0) ||
- !p_state.has("rotation") || !p_state.has("scale") ||
- !p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets"));
+ !p_state.has("rotation") || !p_state.has("scale") ||
+ !p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets"));
Dictionary state = p_state;
set_rotation(state["rotation"]);
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 973b72973d..5f9f09fc50 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -51,26 +51,32 @@ VBoxContainer *FileDialog::get_vbox() {
void FileDialog::_theme_changed() {
Color font_color = vbox->get_theme_color(SNAME("font_color"), SNAME("Button"));
Color font_hover_color = vbox->get_theme_color(SNAME("font_hover_color"), SNAME("Button"));
+ Color font_focus_color = vbox->get_theme_color(SNAME("font_focus_color"), SNAME("Button"));
Color font_pressed_color = vbox->get_theme_color(SNAME("font_pressed_color"), SNAME("Button"));
dir_up->add_theme_color_override("icon_normal_color", font_color);
dir_up->add_theme_color_override("icon_hover_color", font_hover_color);
+ dir_up->add_theme_color_override("icon_focus_color", font_focus_color);
dir_up->add_theme_color_override("icon_pressed_color", font_pressed_color);
dir_prev->add_theme_color_override("icon_color_normal", font_color);
dir_prev->add_theme_color_override("icon_color_hover", font_hover_color);
+ dir_prev->add_theme_color_override("icon_focus_color", font_focus_color);
dir_prev->add_theme_color_override("icon_color_pressed", font_pressed_color);
dir_next->add_theme_color_override("icon_color_normal", font_color);
dir_next->add_theme_color_override("icon_color_hover", font_hover_color);
+ dir_next->add_theme_color_override("icon_focus_color", font_focus_color);
dir_next->add_theme_color_override("icon_color_pressed", font_pressed_color);
refresh->add_theme_color_override("icon_normal_color", font_color);
refresh->add_theme_color_override("icon_hover_color", font_hover_color);
+ refresh->add_theme_color_override("icon_focus_color", font_focus_color);
refresh->add_theme_color_override("icon_pressed_color", font_pressed_color);
show_hidden->add_theme_color_override("icon_normal_color", font_color);
show_hidden->add_theme_color_override("icon_hover_color", font_hover_color);
+ show_hidden->add_theme_color_override("icon_focus_color", font_focus_color);
show_hidden->add_theme_color_override("icon_pressed_color", font_pressed_color);
}
@@ -342,7 +348,7 @@ bool FileDialog::_is_open_should_be_disabled() {
// Opening a file, but selected a folder? Forbidden.
return ((mode == FILE_MODE_OPEN_FILE || mode == FILE_MODE_OPEN_FILES) && d["dir"]) || // Flipped case, also forbidden.
- (mode == FILE_MODE_OPEN_DIR && !d["dir"]);
+ (mode == FILE_MODE_OPEN_DIR && !d["dir"]);
}
void FileDialog::_go_up() {
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index 5d024d3be7..e5b23d8006 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -39,6 +39,10 @@ GradientEdit::GradientEdit() {
picker = memnew(ColorPicker);
popup->add_child(picker);
+ gradient_cache.instantiate();
+ preview_texture.instantiate();
+
+ preview_texture->set_width(1024);
add_child(popup, false, INTERNAL_MODE_FRONT);
}
@@ -47,7 +51,7 @@ int GradientEdit::_get_point_from_pos(int x) {
int total_w = get_size().width - get_size().height - draw_spacing;
float min_distance = 1e20;
for (int i = 0; i < points.size(); i++) {
- //Check if we clicked at point
+ // Check if we clicked at point.
float distance = ABS(x - points[i].offset * total_w);
float min = (draw_point_width / 2 * 1.7); //make it easier to grab
if (distance <= min && distance < min_distance) {
@@ -94,14 +98,14 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
}
Ref<InputEventMouseButton> mb = p_event;
- //Show color picker on double click.
+ // Show color picker on double click.
if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_double_click() && mb->is_pressed()) {
grabbed = _get_point_from_pos(mb->get_position().x);
_show_color_picker();
accept_event();
}
- //Delete point on right click
+ // Delete point on right click.
if (mb.is_valid() && mb->get_button_index() == 2 && mb->is_pressed()) {
grabbed = _get_point_from_pos(mb->get_position().x);
if (grabbed != -1) {
@@ -114,20 +118,20 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
- //Hold alt key to duplicate selected color
+ // Hold alt key to duplicate selected color.
if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed() && mb->is_alt_pressed()) {
int x = mb->get_position().x;
grabbed = _get_point_from_pos(x);
if (grabbed != -1) {
int total_w = get_size().width - get_size().height - draw_spacing;
- Gradient::Point newPoint = points[grabbed];
- newPoint.offset = CLAMP(x / float(total_w), 0, 1);
+ Gradient::Point new_point = points[grabbed];
+ new_point.offset = CLAMP(x / float(total_w), 0, 1);
- points.push_back(newPoint);
+ points.push_back(new_point);
points.sort();
for (int i = 0; i < points.size(); ++i) {
- if (points[i].offset == newPoint.offset) {
+ if (points[i].offset == new_point.offset) {
grabbed = i;
break;
}
@@ -138,7 +142,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
- //select
+ // Select.
if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
update();
int x = mb->get_position().x;
@@ -158,16 +162,16 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- //insert
- Gradient::Point newPoint;
- newPoint.offset = CLAMP(x / float(total_w), 0, 1);
+ // Insert point.
+ Gradient::Point new_point;
+ new_point.offset = CLAMP(x / float(total_w), 0, 1);
Gradient::Point prev;
Gradient::Point next;
int pos = -1;
for (int i = 0; i < points.size(); i++) {
- if (points[i].offset < newPoint.offset) {
+ if (points[i].offset < new_point.offset) {
pos = i;
}
}
@@ -191,12 +195,12 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
prev = points[pos];
}
- newPoint.color = prev.color.lerp(next.color, (newPoint.offset - prev.offset) / (next.offset - prev.offset));
+ new_point.color = prev.color.lerp(next.color, (new_point.offset - prev.offset) / (next.offset - prev.offset));
- points.push_back(newPoint);
+ points.push_back(new_point);
points.sort();
for (int i = 0; i < points.size(); i++) {
- if (points[i].offset == newPoint.offset) {
+ if (points[i].offset == new_point.offset) {
grabbed = i;
break;
}
@@ -223,7 +227,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
float newofs = CLAMP(x / float(total_w), 0, 1);
// Snap to "round" coordinates if holding Ctrl.
- // Be more precise if holding Shift as well
+ // Be more precise if holding Shift as well.
if (mm->is_ctrl_pressed()) {
newofs = Math::snapped(newofs, mm->is_shift_pressed() ? 0.025 : 0.1);
} else if (mm->is_shift_pressed()) {
@@ -299,57 +303,22 @@ void GradientEdit::_notification(int p_what) {
int h = get_size().y;
if (w == 0 || h == 0) {
- return; //Safety check. We have division by 'h'. And in any case there is nothing to draw with such size
+ return; // Safety check. We have division by 'h'. And in any case there is nothing to draw with such size.
}
int total_w = get_size().width - get_size().height - draw_spacing;
- //Draw checker pattern for ramp
+ // Draw checker pattern for ramp.
draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true);
- //Draw color ramp
- Gradient::Point prev;
- prev.offset = 0;
- if (points.size() == 0) {
- prev.color = Color(0, 0, 0); //Draw black rectangle if we have no points
- } else {
- prev.color = points[0].color; //Extend color of first point to the beginning.
- }
+ // Draw color ramp.
- for (int i = -1; i < points.size(); i++) {
- Gradient::Point next;
- //If there is no next point
- if (i + 1 == points.size()) {
- if (points.size() == 0) {
- next.color = Color(0, 0, 0); //Draw black rectangle if we have no points
- } else {
- next.color = points[i].color; //Extend color of last point to the end.
- }
- next.offset = 1;
- } else {
- next = points[i + 1];
- }
+ gradient_cache->set_points(points);
+ gradient_cache->set_interpolation_mode(interpolation_mode);
+ preview_texture->set_gradient(gradient_cache);
+ draw_texture_rect(preview_texture, Rect2(0, 0, total_w, h));
- if (prev.offset == next.offset) {
- prev = next;
- continue;
- }
-
- Vector<Vector2> points;
- Vector<Color> colors;
- points.push_back(Vector2(prev.offset * total_w, h));
- points.push_back(Vector2(prev.offset * total_w, 0));
- points.push_back(Vector2(next.offset * total_w, 0));
- points.push_back(Vector2(next.offset * total_w, h));
- colors.push_back(prev.color);
- colors.push_back(prev.color);
- colors.push_back(next.color);
- colors.push_back(next.color);
- draw_primitive(points, colors, Vector<Point2>());
- prev = next;
- }
-
- //Draw point markers
+ // Draw point markers.
for (int i = 0; i < points.size(); i++) {
Color col = points[i].color.inverted();
col.a = 0.9;
@@ -383,7 +352,7 @@ void GradientEdit::_notification(int p_what) {
draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6));
}
- //Draw borders around color ramp if in focus
+ // Draw borders around color ramp if in focus.
if (has_focus()) {
draw_line(Vector2(-1, -1), Vector2(total_w + 1, -1), Color(1, 1, 1, 0.6));
draw_line(Vector2(total_w + 1, -1), Vector2(total_w + 1, h + 1), Color(1, 1, 1, 0.6));
@@ -448,12 +417,21 @@ void GradientEdit::set_points(Vector<Gradient::Point> &p_points) {
}
points.clear();
points = p_points;
+ points.sort();
}
Vector<Gradient::Point> &GradientEdit::get_points() {
return points;
}
+void GradientEdit::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) {
+ interpolation_mode = p_interp_mode;
+}
+
+Gradient::InterpolationMode GradientEdit::get_interpolation_mode() {
+ return interpolation_mode;
+}
+
void GradientEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("ramp_changed"));
}
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index f3a39daaf6..66b60d87c7 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -45,6 +45,10 @@ class GradientEdit : public Control {
bool grabbing = false;
int grabbed = -1;
Vector<Gradient::Point> points;
+ Gradient::InterpolationMode interpolation_mode = Gradient::GRADIENT_INTERPOLATE_LINEAR;
+
+ Ref<Gradient> gradient_cache;
+ Ref<GradientTexture1D> preview_texture;
// Make sure to use the scaled value below.
const int BASE_SPACING = 3;
@@ -69,6 +73,9 @@ public:
Vector<Color> get_colors() const;
void set_points(Vector<Gradient::Point> &p_points);
Vector<Gradient::Point> &get_points();
+ void set_interpolation_mode(Gradient::InterpolationMode p_interp_mode);
+ Gradient::InterpolationMode get_interpolation_mode();
+
virtual Size2 get_minimum_size() const override;
GradientEdit();
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index d10ad90c1f..8470003b3e 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "item_list.h"
+
#include "core/config/project_settings.h"
#include "core/os/os.h"
#include "core/string/translation.h"
@@ -55,16 +56,8 @@ void ItemList::_shape(int p_idx) {
int ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, bool p_selectable) {
Item item;
item.icon = p_texture;
- item.icon_transposed = false;
- item.icon_region = Rect2i();
- item.icon_modulate = Color(1, 1, 1, 1);
item.text = p_item;
- item.text_buf.instantiate();
item.selectable = p_selectable;
- item.selected = false;
- item.disabled = false;
- item.tooltip_enabled = true;
- item.custom_bg = Color(0, 0, 0, 0);
items.push_back(item);
int item_id = items.size() - 1;
@@ -72,27 +65,20 @@ int ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, bo
update();
shape_changed = true;
+ notify_property_list_changed();
return item_id;
}
int ItemList::add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable) {
Item item;
item.icon = p_item;
- item.icon_transposed = false;
- item.icon_region = Rect2i();
- item.icon_modulate = Color(1, 1, 1, 1);
- //item.text=p_item;
- item.text_buf.instantiate();
item.selectable = p_selectable;
- item.selected = false;
- item.disabled = false;
- item.tooltip_enabled = true;
- item.custom_bg = Color(0, 0, 0, 0);
items.push_back(item);
int item_id = items.size() - 1;
update();
shape_changed = true;
+ notify_property_list_changed();
return item_id;
}
@@ -400,6 +386,15 @@ void ItemList::move_item(int p_from_idx, int p_to_idx) {
update();
shape_changed = true;
+ notify_property_list_changed();
+}
+
+void ItemList::set_item_count(int p_count) {
+ ERR_FAIL_COND(p_count < 0);
+ items.resize(p_count);
+ update();
+ shape_changed = true;
+ notify_property_list_changed();
}
int ItemList::get_item_count() const {
@@ -416,6 +411,7 @@ void ItemList::remove_item(int p_idx) {
update();
shape_changed = true;
defer_select_single = -1;
+ notify_property_list_changed();
}
void ItemList::clear() {
@@ -425,6 +421,7 @@ void ItemList::clear() {
update();
shape_changed = true;
defer_select_single = -1;
+ notify_property_list_changed();
}
void ItemList::set_fixed_column_width(int p_size) {
@@ -1446,32 +1443,6 @@ bool ItemList::is_anything_selected() {
return false;
}
-void ItemList::_set_items(const Array &p_items) {
- ERR_FAIL_COND(p_items.size() % 3);
- clear();
-
- for (int i = 0; i < p_items.size(); i += 3) {
- String text = p_items[i + 0];
- Ref<Texture2D> icon = p_items[i + 1];
- bool disabled = p_items[i + 2];
-
- int idx = get_item_count();
- add_item(text, icon);
- set_item_disabled(idx, disabled);
- }
-}
-
-Array ItemList::_get_items() const {
- Array items;
- for (int i = 0; i < get_item_count(); i++) {
- items.push_back(get_item_text(i));
- items.push_back(get_item_icon(i));
- items.push_back(is_item_disabled(i));
- }
-
- return items;
-}
-
Size2 ItemList::get_minimum_size() const {
if (auto_height) {
return Size2(0, auto_height_value);
@@ -1508,6 +1479,74 @@ TextParagraph::OverrunBehavior ItemList::get_text_overrun_behavior() const {
return text_overrun_behavior;
}
+bool ItemList::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) {
+ int item_index = components[0].trim_prefix("item_").to_int();
+ if (components[1] == "text") {
+ set_item_text(item_index, p_value);
+ return true;
+ } else if (components[1] == "icon") {
+ set_item_icon(item_index, p_value);
+ return true;
+ } else if (components[1] == "disabled") {
+ set_item_disabled(item_index, p_value);
+ return true;
+ }
+ }
+#ifndef DISABLE_DEPRECATED
+ // Compatibility.
+ if (p_name == "items") {
+ Array arr = p_value;
+ ERR_FAIL_COND_V(arr.size() % 3, false);
+ clear();
+
+ for (int i = 0; i < arr.size(); i += 3) {
+ String text = arr[i + 0];
+ Ref<Texture2D> icon = arr[i + 1];
+ bool disabled = arr[i + 2];
+
+ int idx = get_item_count();
+ add_item(text, icon);
+ set_item_disabled(idx, disabled);
+ }
+ }
+#endif
+ return false;
+}
+
+bool ItemList::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) {
+ int item_index = components[0].trim_prefix("item_").to_int();
+ if (components[1] == "text") {
+ r_ret = get_item_text(item_index);
+ return true;
+ } else if (components[1] == "icon") {
+ r_ret = get_item_icon(item_index);
+ return true;
+ } else if (components[1] == "disabled") {
+ r_ret = is_item_disabled(item_index);
+ return true;
+ }
+ }
+ return false;
+}
+
+void ItemList::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < items.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("item_%d/text", i)));
+
+ PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D");
+ pi.usage &= ~(get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("item_%d/disabled", i));
+ pi.usage &= ~(!is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+ }
+}
+
void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_item", "text", "icon", "selectable"), &ItemList::add_item, DEFVAL(Variant()), DEFVAL(true));
ClassDB::bind_method(D_METHOD("add_icon_item", "icon", "selectable"), &ItemList::add_icon_item, DEFVAL(true));
@@ -1567,6 +1606,7 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_item", "from_idx", "to_idx"), &ItemList::move_item);
+ ClassDB::bind_method(D_METHOD("set_item_count", "count"), &ItemList::set_item_count);
ClassDB::bind_method(D_METHOD("get_item_count"), &ItemList::get_item_count);
ClassDB::bind_method(D_METHOD("remove_item", "idx"), &ItemList::remove_item);
@@ -1614,20 +1654,16 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_v_scroll"), &ItemList::get_v_scroll);
- ClassDB::bind_method(D_METHOD("_set_items"), &ItemList::_set_items);
- ClassDB::bind_method(D_METHOD("_get_items"), &ItemList::_get_items);
-
ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &ItemList::set_text_overrun_behavior);
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &ItemList::get_text_overrun_behavior);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
-
ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Multi"), "set_select_mode", "get_select_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_text_lines", PROPERTY_HINT_RANGE, "1,10,1,or_greater"), "set_max_text_lines", "get_max_text_lines");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_height"), "set_auto_height", "has_auto_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
+ ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "item_");
ADD_GROUP("Columns", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_columns", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), "set_max_columns", "get_max_columns");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "same_column_width"), "set_same_column_width", "is_same_column_width");
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index 148fa7ba9f..e780179e7b 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -54,7 +54,7 @@ private:
Ref<Texture2D> icon;
bool icon_transposed = false;
Rect2i icon_region;
- Color icon_modulate;
+ Color icon_modulate = Color(1, 1, 1, 1);
Ref<Texture2D> tag_icon;
String text;
Ref<TextParagraph> text_buf;
@@ -65,11 +65,11 @@ private:
bool selectable = false;
bool selected = false;
bool disabled = false;
- bool tooltip_enabled = false;
+ bool tooltip_enabled = true;
Variant metadata;
String tooltip;
Color custom_fg;
- Color custom_bg;
+ Color custom_bg = Color(0.0, 0.0, 0.0, 0.0);
Rect2 rect_cache;
Rect2 min_rect_cache;
@@ -77,6 +77,10 @@ private:
Size2 get_icon_size() const;
bool operator<(const Item &p_another) const { return text < p_another.text; }
+
+ Item() {
+ text_buf.instantiate();
+ }
};
int current = -1;
@@ -119,14 +123,14 @@ private:
bool do_autoscroll_to_bottom = false;
- Array _get_items() const;
- void _set_items(const Array &p_items);
-
void _scroll_changed(double);
void _shape(int p_idx);
protected:
void _notification(int p_what);
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
@@ -199,6 +203,7 @@ public:
void move_item(int p_from_idx, int p_to_idx);
+ void set_item_count(int p_count);
int get_item_count() const;
void remove_item(int p_idx);
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index dda1151273..0b85b4d3d2 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -920,6 +920,9 @@ void LineEdit::_notification(int p_what) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
}
+ if (deselect_on_focus_loss_enabled) {
+ deselect();
+ }
} break;
case MainLoop::NOTIFICATION_OS_IME_UPDATE: {
if (has_focus()) {
@@ -1938,6 +1941,17 @@ bool LineEdit::is_selecting_enabled() const {
return selecting_enabled;
}
+void LineEdit::set_deselect_on_focus_loss_enabled(const bool p_enabled) {
+ deselect_on_focus_loss_enabled = p_enabled;
+ if (p_enabled && selection.enabled && !has_focus()) {
+ deselect();
+ }
+}
+
+bool LineEdit::is_deselect_on_focus_loss_enabled() const {
+ return deselect_on_focus_loss_enabled;
+}
+
void LineEdit::set_right_icon(const Ref<Texture2D> &p_icon) {
if (right_icon == p_icon) {
return;
@@ -2121,7 +2135,7 @@ void LineEdit::_get_property_list(List<PropertyInfo> *p_list) const {
void LineEdit::_validate_property(PropertyInfo &property) const {
if (!caret_blink_enabled && property.name == "caret_blink_speed") {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
@@ -2196,6 +2210,8 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_middle_mouse_paste_enabled"), &LineEdit::is_middle_mouse_paste_enabled);
ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &LineEdit::set_selecting_enabled);
ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &LineEdit::is_selecting_enabled);
+ ClassDB::bind_method(D_METHOD("set_deselect_on_focus_loss_enabled", "enable"), &LineEdit::set_deselect_on_focus_loss_enabled);
+ ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &LineEdit::is_deselect_on_focus_loss_enabled);
ClassDB::bind_method(D_METHOD("set_right_icon", "icon"), &LineEdit::set_right_icon);
ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon);
@@ -2251,6 +2267,7 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 94179ce05b..3364e02e01 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -97,6 +97,7 @@ private:
float full_width = 0.0;
bool selecting_enabled = true;
+ bool deselect_on_focus_loss_enabled = true;
bool context_menu_enabled = true;
PopupMenu *menu = nullptr;
@@ -325,6 +326,9 @@ public:
void set_selecting_enabled(bool p_enabled);
bool is_selecting_enabled() const;
+ void set_deselect_on_focus_loss_enabled(const bool p_enabled);
+ bool is_deselect_on_focus_loss_enabled() const;
+
void set_right_icon(const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_right_icon();
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index 925e6f5b97..c3201186ea 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -168,7 +168,12 @@ void LinkButton::_notification(int p_what) {
switch (get_draw_mode()) {
case DRAW_NORMAL: {
- color = get_theme_color(SNAME("font_color"));
+ if (has_focus()) {
+ color = get_theme_color(SNAME("font_focus_color"));
+ } else {
+ color = get_theme_color(SNAME("font_color"));
+ }
+
do_underline = underline_mode == UNDERLINE_MODE_ALWAYS;
} break;
case DRAW_HOVER_PRESSED:
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index ceb2092e3a..39c7b04955 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -110,14 +110,6 @@ PopupMenu *MenuButton::get_popup() const {
return popup;
}
-void MenuButton::_set_items(const Array &p_items) {
- popup->set("items", p_items);
-}
-
-Array MenuButton::_get_items() const {
- return popup->get("items");
-}
-
void MenuButton::set_switch_on_hover(bool p_enabled) {
switch_on_hover = p_enabled;
}
@@ -126,6 +118,16 @@ bool MenuButton::is_switch_on_hover() {
return switch_on_hover;
}
+void MenuButton::set_item_count(int p_count) {
+ ERR_FAIL_COND(p_count < 0);
+ popup->set_item_count(p_count);
+ notify_property_list_changed();
+}
+
+int MenuButton::get_item_count() const {
+ return popup->get_item_count();
+}
+
void MenuButton::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
@@ -146,16 +148,66 @@ void MenuButton::_notification(int p_what) {
}
}
+bool MenuButton::_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") {
+ bool valid;
+ popup->set(String(p_name).trim_prefix("popup/"), p_value, &valid);
+ return valid;
+ }
+ return false;
+}
+
+bool MenuButton::_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") {
+ bool valid;
+ r_ret = popup->get(String(p_name).trim_prefix("popup/"), &valid);
+ return valid;
+ }
+ return false;
+}
+
+void MenuButton::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < popup->get_item_count(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("popup/item_%d/text", i)));
+
+ PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("popup/item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D");
+ 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");
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/disabled", i));
+ pi.usage &= ~(!popup->is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/separator", i));
+ pi.usage &= ~(!popup->is_item_separator(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+ }
+}
+
void MenuButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_popup"), &MenuButton::get_popup);
- ClassDB::bind_method(D_METHOD("_set_items"), &MenuButton::_set_items);
- ClassDB::bind_method(D_METHOD("_get_items"), &MenuButton::_get_items);
ClassDB::bind_method(D_METHOD("set_switch_on_hover", "enable"), &MenuButton::set_switch_on_hover);
ClassDB::bind_method(D_METHOD("is_switch_on_hover"), &MenuButton::is_switch_on_hover);
ClassDB::bind_method(D_METHOD("set_disable_shortcuts", "disabled"), &MenuButton::set_disable_shortcuts);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
+ ClassDB::bind_method(D_METHOD("set_item_count", "count"), &MenuButton::set_item_count);
+ ClassDB::bind_method(D_METHOD("get_item_count"), &MenuButton::get_item_count);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "switch_on_hover"), "set_switch_on_hover", "is_switch_on_hover");
+ ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "popup/item_");
ADD_SIGNAL(MethodInfo("about_to_popup"));
}
diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h
index 730495b65d..455b7dc870 100644
--- a/scene/gui/menu_button.h
+++ b/scene/gui/menu_button.h
@@ -44,15 +44,15 @@ class MenuButton : public Button {
Vector2i mouse_pos_adjusted;
- Array _get_items() const;
- void _set_items(const Array &p_items);
-
virtual void gui_input(const Ref<InputEvent> &p_event) override;
void _popup_visibility_changed(bool p_visible);
protected:
void _notification(int p_what);
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
@@ -64,6 +64,9 @@ public:
bool is_switch_on_hover();
void set_disable_shortcuts(bool p_disabled);
+ void set_item_count(int p_count);
+ int get_item_count() const;
+
MenuButton();
~MenuButton();
};
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 2adeb2d947..dcf3cfeb09 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -71,7 +71,11 @@ void OptionButton::_notification(int p_what) {
clr = get_theme_color(SNAME("font_disabled_color"));
break;
default:
- clr = get_theme_color(SNAME("font_color"));
+ if (has_focus()) {
+ clr = get_theme_color(SNAME("font_focus_color"));
+ } else {
+ clr = get_theme_color(SNAME("font_color"));
+ }
}
}
@@ -328,7 +332,7 @@ void OptionButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_items"), &OptionButton::_set_items);
ClassDB::bind_method(D_METHOD("_get_items"), &OptionButton::_get_items);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
// "selected" property must come after "items", otherwise GH-10213 occurs.
ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected");
ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "index")));
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index be05fd5a60..f54ff9228b 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -105,8 +105,6 @@ void Popup::_close_pressed() {
_deinitialize_visible_parents();
call_deferred(SNAME("hide"));
-
- emit_signal(SNAME("cancelled"));
}
void Popup::set_as_minsize() {
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index c0a559e624..54af9f3885 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -820,6 +820,7 @@ void PopupMenu::add_item(const String &p_label, int p_id, uint32_t p_accel) {
_shape_item(items.size() - 1);
control->update();
child_controls_changed();
+ notify_property_list_changed();
}
void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, uint32_t p_accel) {
@@ -830,6 +831,7 @@ void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_labe
_shape_item(items.size() - 1);
control->update();
child_controls_changed();
+ notify_property_list_changed();
}
void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel) {
@@ -1271,6 +1273,14 @@ int PopupMenu::get_current_index() const {
return mouse_over;
}
+void PopupMenu::set_item_count(int p_count) {
+ ERR_FAIL_COND(p_count < 0);
+ items.resize(p_count);
+ control->update();
+ child_controls_changed();
+ notify_property_list_changed();
+}
+
int PopupMenu::get_item_count() const {
return items.size();
}
@@ -1420,27 +1430,7 @@ void PopupMenu::clear() {
mouse_over = -1;
control->update();
child_controls_changed();
-}
-
-Array PopupMenu::_get_items() const {
- Array items;
- for (int i = 0; i < get_item_count(); i++) {
- items.push_back(get_item_text(i));
- items.push_back(get_item_icon(i));
- // For compatibility, use false/true for no/checkbox and integers for other values
- int ct = this->items[i].checkable_type;
- items.push_back(Variant(ct <= Item::CHECKABLE_TYPE_CHECK_BOX ? is_item_checkable(i) : ct));
- items.push_back(is_item_checked(i));
- items.push_back(is_item_disabled(i));
-
- items.push_back(get_item_id(i));
- items.push_back(get_item_accelerator(i));
- items.push_back(get_item_metadata(i));
- items.push_back(get_item_submenu(i));
- items.push_back(is_item_separator(i));
- }
-
- return items;
+ notify_property_list_changed();
}
void PopupMenu::_ref_shortcut(Ref<Shortcut> p_sc) {
@@ -1461,45 +1451,6 @@ void PopupMenu::_unref_shortcut(Ref<Shortcut> p_sc) {
}
}
-void PopupMenu::_set_items(const Array &p_items) {
- ERR_FAIL_COND(p_items.size() % 10);
- clear();
-
- for (int i = 0; i < p_items.size(); i += 10) {
- String text = p_items[i + 0];
- Ref<Texture2D> icon = p_items[i + 1];
- // For compatibility, use false/true for no/checkbox and integers for other values
- bool checkable = p_items[i + 2];
- bool radio_checkable = (int)p_items[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON;
- bool checked = p_items[i + 3];
- bool disabled = p_items[i + 4];
-
- int id = p_items[i + 5];
- int accel = p_items[i + 6];
- Variant meta = p_items[i + 7];
- String subm = p_items[i + 8];
- bool sep = p_items[i + 9];
-
- int idx = get_item_count();
- add_item(text, id);
- set_item_icon(idx, icon);
- if (checkable) {
- if (radio_checkable) {
- set_item_as_radio_checkable(idx, true);
- } else {
- set_item_as_checkable(idx, true);
- }
- }
- set_item_checked(idx, checked);
- set_item_disabled(idx, disabled);
- set_item_id(idx, id);
- set_item_metadata(idx, meta);
- set_item_as_separator(idx, sep);
- set_item_accelerator(idx, accel);
- set_item_submenu(idx, subm);
- }
-}
-
// Hide on item selection determines whether or not the popup will close after item selection
void PopupMenu::set_hide_on_item_selection(bool p_enabled) {
hide_on_item_selection = p_enabled;
@@ -1581,6 +1532,145 @@ void PopupMenu::take_mouse_focus() {
}
}
+bool PopupMenu::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) {
+ int item_index = components[0].trim_prefix("item_").to_int();
+ String property = components[1];
+ if (property == "text") {
+ set_item_text(item_index, p_value);
+ return true;
+ } else if (property == "icon") {
+ set_item_icon(item_index, p_value);
+ return true;
+ } else if (property == "checkable") {
+ bool radio_checkable = (int)p_value == Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ if (radio_checkable) {
+ set_item_as_radio_checkable(item_index, true);
+ } else {
+ bool checkable = p_value;
+ set_item_as_checkable(item_index, checkable);
+ }
+ return true;
+ } else if (property == "checked") {
+ set_item_checked(item_index, p_value);
+ return true;
+ } else if (property == "id") {
+ set_item_id(item_index, p_value);
+ return true;
+ } else if (components[1] == "disabled") {
+ set_item_disabled(item_index, p_value);
+ return true;
+ } else if (property == "separator") {
+ set_item_as_separator(item_index, p_value);
+ return true;
+ }
+ }
+#ifndef DISABLE_DEPRECATED
+ // Compatibility.
+ if (p_name == "items") {
+ Array arr = p_value;
+ ERR_FAIL_COND_V(arr.size() % 10, false);
+ clear();
+
+ for (int i = 0; i < arr.size(); i += 10) {
+ String text = arr[i + 0];
+ Ref<Texture2D> icon = arr[i + 1];
+ // For compatibility, use false/true for no/checkbox and integers for other values
+ bool checkable = arr[i + 2];
+ bool radio_checkable = (int)arr[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ bool checked = arr[i + 3];
+ bool disabled = arr[i + 4];
+
+ int id = arr[i + 5];
+ int accel = arr[i + 6];
+ Variant meta = arr[i + 7];
+ String subm = arr[i + 8];
+ bool sep = arr[i + 9];
+
+ int idx = get_item_count();
+ add_item(text, id);
+ set_item_icon(idx, icon);
+ if (checkable) {
+ if (radio_checkable) {
+ set_item_as_radio_checkable(idx, true);
+ } else {
+ set_item_as_checkable(idx, true);
+ }
+ }
+ set_item_checked(idx, checked);
+ set_item_disabled(idx, disabled);
+ set_item_id(idx, id);
+ set_item_metadata(idx, meta);
+ set_item_as_separator(idx, sep);
+ set_item_accelerator(idx, accel);
+ set_item_submenu(idx, subm);
+ }
+ }
+#endif
+ return false;
+}
+
+bool PopupMenu::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) {
+ int item_index = components[0].trim_prefix("item_").to_int();
+ String property = components[1];
+ if (property == "text") {
+ r_ret = get_item_text(item_index);
+ return true;
+ } else if (property == "icon") {
+ r_ret = get_item_icon(item_index);
+ return true;
+ } else if (property == "checkable") {
+ r_ret = this->items[item_index].checkable_type;
+ return true;
+ } else if (property == "checked") {
+ r_ret = is_item_checked(item_index);
+ return true;
+ } else if (property == "id") {
+ r_ret = get_item_id(item_index);
+ return true;
+ } else if (components[1] == "disabled") {
+ r_ret = is_item_disabled(item_index);
+ return true;
+ } else if (property == "separator") {
+ r_ret = is_item_separator(item_index);
+ return true;
+ }
+ }
+ return false;
+}
+
+void PopupMenu::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < items.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("item_%d/text", i)));
+
+ PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D");
+ pi.usage &= ~(get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::INT, vformat("item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button");
+ pi.usage &= ~(!is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("item_%d/checked", i));
+ pi.usage &= ~(!is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::INT, vformat("item_%d/id", i), PROPERTY_HINT_RANGE, "1,10,1,or_greater");
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("item_%d/disabled", i));
+ pi.usage &= ~(!is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("item_%d/separator", i));
+ pi.usage &= ~(!is_item_separator(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+ }
+}
+
void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_item", "label", "id", "accel"), &PopupMenu::add_item, DEFVAL(-1), DEFVAL(0));
ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_item, DEFVAL(-1), DEFVAL(0));
@@ -1643,6 +1733,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut);
ClassDB::bind_method(D_METHOD("get_current_index"), &PopupMenu::get_current_index);
+ ClassDB::bind_method(D_METHOD("set_item_count", "count"), &PopupMenu::set_item_count);
ClassDB::bind_method(D_METHOD("get_item_count"), &PopupMenu::get_item_count);
ClassDB::bind_method(D_METHOD("remove_item", "idx"), &PopupMenu::remove_item);
@@ -1650,9 +1741,6 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_separator", "label", "id"), &PopupMenu::add_separator, DEFVAL(String()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear);
- ClassDB::bind_method(D_METHOD("_set_items"), &PopupMenu::_set_items);
- ClassDB::bind_method(D_METHOD("_get_items"), &PopupMenu::_get_items);
-
ClassDB::bind_method(D_METHOD("set_hide_on_item_selection", "enable"), &PopupMenu::set_hide_on_item_selection);
ClassDB::bind_method(D_METHOD("is_hide_on_item_selection"), &PopupMenu::is_hide_on_item_selection);
@@ -1668,13 +1756,14 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &PopupMenu::set_allow_search);
ClassDB::bind_method(D_METHOD("get_allow_search"), &PopupMenu::get_allow_search);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search");
+ ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "item_");
+
ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index")));
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 428076c6da..80874ca1e0 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -117,9 +117,6 @@ class PopupMenu : public Popup {
bool hide_on_multistate_item_selection = false;
Vector2 moved;
- Array _get_items() const;
- void _set_items(const Array &p_items);
-
Map<Ref<Shortcut>, int> shortcut_refcount;
void _ref_shortcut(Ref<Shortcut> p_sc);
@@ -141,6 +138,9 @@ class PopupMenu : public Popup {
protected:
void _notification(int p_what);
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
@@ -213,6 +213,8 @@ public:
int get_item_state(int p_idx) const;
int get_current_index() const;
+
+ void set_item_count(int p_count);
int get_item_count() const;
bool activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only = false);
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 31767dd263..f1efbbda98 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -1491,7 +1491,13 @@ void RichTextLabel::_notification(int p_what) {
_update_fx(main, dt);
update();
}
- }
+ } break;
+ case NOTIFICATION_FOCUS_EXIT: {
+ if (deselect_on_focus_loss_enabled) {
+ selection.active = false;
+ update();
+ }
+ } break;
}
}
@@ -3609,6 +3615,14 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) {
}
}
+void RichTextLabel::set_deselect_on_focus_loss_enabled(const bool p_enabled) {
+ deselect_on_focus_loss_enabled = p_enabled;
+ if (p_enabled && selection.active && !has_focus()) {
+ selection.active = false;
+ update();
+ }
+}
+
bool RichTextLabel::_search_table(ItemTable *p_table, List<Item *>::Element *p_from, const String &p_string, bool p_reverse_search) {
List<Item *>::Element *E = p_from;
while (E != nullptr) {
@@ -3858,6 +3872,10 @@ bool RichTextLabel::is_selection_enabled() const {
return selection.enabled;
}
+bool RichTextLabel::is_deselect_on_focus_loss_enabled() const {
+ return deselect_on_focus_loss_enabled;
+}
+
int RichTextLabel::get_selection_from() const {
if (!selection.active || !selection.enabled) {
return -1;
@@ -4111,6 +4129,9 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_selection_enabled", "enabled"), &RichTextLabel::set_selection_enabled);
ClassDB::bind_method(D_METHOD("is_selection_enabled"), &RichTextLabel::is_selection_enabled);
+ ClassDB::bind_method(D_METHOD("set_deselect_on_focus_loss_enabled", "enable"), &RichTextLabel::set_deselect_on_focus_loss_enabled);
+ ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &RichTextLabel::is_deselect_on_focus_loss_enabled);
+
ClassDB::bind_method(D_METHOD("get_selection_from"), &RichTextLabel::get_selection_from);
ClassDB::bind_method(D_METHOD("get_selection_to"), &RichTextLabel::get_selection_to);
@@ -4162,6 +4183,8 @@ void RichTextLabel::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
+
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 48186ca8b8..f3c4c11cc8 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -278,12 +278,12 @@ private:
uint64_t offset_random(int index) {
return (_current_rng >> (index % 64)) |
- (_current_rng << (64 - (index % 64)));
+ (_current_rng << (64 - (index % 64)));
}
uint64_t offset_previous_random(int index) {
return (_previous_rng >> (index % 64)) |
- (_previous_rng << (64 - (index % 64)));
+ (_previous_rng << (64 - (index % 64)));
}
};
@@ -399,6 +399,7 @@ private:
};
Selection selection;
+ bool deselect_on_focus_loss_enabled = true;
int visible_characters = -1;
float percent_visible = 1.0;
@@ -551,6 +552,8 @@ public:
int get_selection_to() const;
String get_selected_text() const;
void selection_copy();
+ void set_deselect_on_focus_loss_enabled(const bool p_enabled);
+ bool is_deselect_on_focus_loss_enabled() const;
void parse_bbcode(const String &p_bbcode);
void append_text(const String &p_bbcode);
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index 78b58c773a..405fbdae75 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -175,7 +175,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
if (cb_hover != -1) {
// pressed
- emit_signal(SNAME("tab_closed"), cb_hover);
+ emit_signal(SNAME("tab_close_pressed"), cb_hover);
}
cb_pressing = false;
@@ -222,7 +222,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (max_drawn_tab <= 0) {
+ if (tabs.is_empty()) {
// Return early if there are no actual tabs to handle input for.
return;
}
@@ -1172,7 +1172,7 @@ void TabBar::_bind_methods() {
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_rmb_clicked", PropertyInfo(Variant::INT, "tab")));
- ADD_SIGNAL(MethodInfo("tab_closed", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_close_pressed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("active_tab_rearranged", PropertyInfo(Variant::INT, "idx_to")));
ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 8cb3b23020..cb7a6c0978 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1364,6 +1364,10 @@ void TextEdit::_notification(int p_what) {
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
}
+
+ if (deselect_on_focus_loss_enabled) {
+ deselect();
+ }
} break;
case MainLoop::NOTIFICATION_OS_IME_UPDATE: {
if (has_focus()) {
@@ -3669,6 +3673,17 @@ bool TextEdit::is_selecting_enabled() const {
return selecting_enabled;
}
+void TextEdit::set_deselect_on_focus_loss_enabled(const bool p_enabled) {
+ deselect_on_focus_loss_enabled = p_enabled;
+ if (p_enabled && selection.active && !has_focus()) {
+ deselect();
+ }
+}
+
+bool TextEdit::is_deselect_on_focus_loss_enabled() const {
+ return deselect_on_focus_loss_enabled;
+}
+
void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) {
override_selected_font_color = p_override_selected_font_color;
}
@@ -4716,6 +4731,9 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &TextEdit::set_selecting_enabled);
ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &TextEdit::is_selecting_enabled);
+ ClassDB::bind_method(D_METHOD("set_deselect_on_focus_loss_enabled", "enable"), &TextEdit::set_deselect_on_focus_loss_enabled);
+ ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &TextEdit::is_deselect_on_focus_loss_enabled);
+
ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &TextEdit::set_override_selected_font_color);
ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &TextEdit::is_overriding_selected_font_color);
@@ -4875,9 +4893,9 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled");
-
ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 8823e44c0d..23f80efce0 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -395,6 +395,7 @@ private:
} selection;
bool selecting_enabled = true;
+ bool deselect_on_focus_loss_enabled = true;
Color font_selected_color = Color(1, 1, 1);
Color selection_color = Color(1, 1, 1);
@@ -753,6 +754,9 @@ public:
void set_selecting_enabled(const bool p_enabled);
bool is_selecting_enabled() const;
+ void set_deselect_on_focus_loss_enabled(const bool p_enabled);
+ bool is_deselect_on_focus_loss_enabled() const;
+
void set_override_selected_font_color(bool p_override_selected_font_color);
bool is_overriding_selected_font_color() const;