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/gradient_edit.cpp100
-rw-r--r--scene/gui/gradient_edit.h7
-rw-r--r--scene/gui/item_list.cpp3
-rw-r--r--scene/gui/label.cpp10
-rw-r--r--scene/gui/line_edit.cpp20
-rw-r--r--scene/gui/line_edit.h4
-rw-r--r--scene/gui/menu_button.cpp74
-rw-r--r--scene/gui/menu_button.h9
-rw-r--r--scene/gui/option_button.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.cpp29
-rw-r--r--scene/gui/rich_text_label.h2
14 files changed, 320 insertions, 167 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/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 4215c9aff4..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"
@@ -1605,7 +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"), &ItemList::set_item_count);
+ 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);
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index b8cb618171..50908f6a77 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -243,11 +243,9 @@ inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Col
if (p_gl.font_rid != RID()) {
if (p_font_shadow_color.a > 0) {
TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color);
- if (p_shadow_outline_size > 0) {
- TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + Vector2(-shadow_ofs.x, shadow_ofs.y), p_gl.index, p_font_shadow_color);
- TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + Vector2(shadow_ofs.x, -shadow_ofs.y), p_gl.index, p_font_shadow_color);
- TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + Vector2(-shadow_ofs.x, -shadow_ofs.y), p_gl.index, p_font_shadow_color);
- }
+ }
+ if (p_font_shadow_color.a > 0 && p_shadow_outline_size > 0) {
+ TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color);
}
if (p_font_outline_color.a != 0.0 && p_outline_size > 0) {
TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_outline_color);
@@ -397,7 +395,7 @@ void Label::_notification(int p_what) {
int ellipsis_gl_size = TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]);
// Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps.
- if (font_shadow_color.a > 0 || (font_outline_color.a != 0.0 && outline_size > 0)) {
+ if ((outline_size > 0 && font_outline_color.a != 0) || (font_shadow_color.a != 0)) {
Vector2 offset = ofs;
// Draw RTL ellipsis string when necessary.
if (rtl && ellipsis_pos >= 0) {
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 8c33d306a1..15fd22ced5 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -664,7 +664,9 @@ void LineEdit::_notification(int p_what) {
}
Ref<Font> font = get_theme_font(SNAME("font"));
- style->draw(ci, Rect2(Point2(), size));
+ if (!flat) {
+ style->draw(ci, Rect2(Point2(), size));
+ }
if (has_focus()) {
get_theme_stylebox(SNAME("focus"))->draw(ci, Rect2(Point2(), size));
@@ -1966,6 +1968,17 @@ Ref<Texture2D> LineEdit::get_right_icon() {
return right_icon;
}
+void LineEdit::set_flat(bool p_enabled) {
+ if (flat != p_enabled) {
+ flat = p_enabled;
+ update();
+ }
+}
+
+bool LineEdit::is_flat() const {
+ return flat;
+}
+
void LineEdit::_text_changed() {
_emit_text_change();
_clear_redo();
@@ -2135,7 +2148,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;
}
}
@@ -2214,6 +2227,8 @@ void LineEdit::_bind_methods() {
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);
+ ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &LineEdit::set_flat);
+ ClassDB::bind_method(D_METHOD("is_flat"), &LineEdit::is_flat);
ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text")));
ADD_SIGNAL(MethodInfo("text_change_rejected", PropertyInfo(Variant::STRING, "rejected_substring")));
@@ -2269,6 +2284,7 @@ void LineEdit::_bind_methods() {
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::BOOL, "flat"), "set_flat", "is_flat");
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");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars");
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 3364e02e01..2ec7f08865 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -130,6 +130,7 @@ private:
bool middle_mouse_paste_enabled = true;
Ref<Texture2D> right_icon;
+ bool flat = false;
struct Selection {
int begin = 0;
@@ -332,6 +333,9 @@ public:
void set_right_icon(const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_right_icon();
+ void set_flat(bool p_enabled);
+ bool is_flat() const;
+
virtual bool is_text_field() const override;
void show_virtual_keyboard();
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 c00c040048..dcf3cfeb09 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -332,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_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 f1efbbda98..308880ef18 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -621,7 +621,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
}
}
-int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, bool p_shadow_as_outline, const Point2 &p_shadow_ofs) {
+int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs) {
Vector2 off;
ERR_FAIL_COND_V(p_frame == nullptr, 0);
@@ -804,7 +804,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
for (int j = 0; j < frame->lines.size(); j++) {
- _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_as_outline, p_shadow_ofs);
+ _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs);
}
idx++;
}
@@ -824,7 +824,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
int size = _find_outline_size(it, p_outline_size);
Color font_color = _find_outline_color(it, p_outline_color);
- if (size <= 0) {
+ Color font_shadow_color = p_font_shadow_color;
+ if ((size <= 0 || font_color.a == 0) && (font_shadow_color.a == 0)) {
gloff.x += glyphs[i].advance;
continue;
}
@@ -871,9 +872,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
}
font_color.a = faded_visibility;
+ font_shadow_color.a = faded_visibility;
}
- bool visible = (font_color.a != 0);
+ bool visible = (font_color.a != 0) || (font_shadow_color.a != 0);
for (int j = 0; j < fx_stack.size(); j++) {
ItemFX *item_fx = fx_stack[j];
@@ -942,18 +944,19 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
}
- Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
-
// Draw glyph outlines.
for (int j = 0; j < glyphs[i].repeat; j++) {
if (visible) {
if (frid != RID()) {
- if (p_shadow_as_outline) {
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(-shadow_ofs.x, shadow_ofs.y), gl, p_font_shadow_color);
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(shadow_ofs.x, -shadow_ofs.y), gl, p_font_shadow_color);
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(-shadow_ofs.x, -shadow_ofs.y), gl, p_font_shadow_color);
+ if (font_shadow_color.a > 0) {
+ TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, font_shadow_color);
+ }
+ if (font_shadow_color.a > 0 && p_shadow_outline_size > 0) {
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, p_shadow_outline_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, font_shadow_color);
+ }
+ if (font_color.a != 0.0 && size > 0) {
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color);
}
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color);
}
}
gloff.x += glyphs[i].advance;
@@ -1470,7 +1473,7 @@ void RichTextLabel::_notification(int p_what) {
Color outline_color = get_theme_color(SNAME("font_outline_color"));
int outline_size = get_theme_constant(SNAME("outline_size"));
Color font_shadow_color = get_theme_color(SNAME("font_shadow_color"));
- bool use_outline = get_theme_constant(SNAME("shadow_as_outline"));
+ int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size"));
Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
visible_paragraph_count = 0;
@@ -1480,7 +1483,7 @@ void RichTextLabel::_notification(int p_what) {
Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs);
while (ofs.y < size.height && from_line < main->lines.size()) {
visible_paragraph_count++;
- visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, use_outline, shadow_ofs);
+ visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, shadow_outline_size, shadow_ofs);
ofs.y += main->lines[from_line].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
from_line++;
}
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index f3c4c11cc8..5b58f14d96 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -412,7 +412,7 @@ private:
void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset);
void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width);
- int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, bool p_shadow_as_outline, const Point2 &shadow_ofs);
+ int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs);
float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr);
String _roman(int p_num, bool p_capitalize) const;