summaryrefslogtreecommitdiff
path: root/scene/gui
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui')
-rw-r--r--scene/gui/color_picker.cpp30
-rw-r--r--scene/gui/color_picker.h6
-rw-r--r--scene/gui/control.cpp221
-rw-r--r--scene/gui/control.h22
-rw-r--r--scene/gui/item_list.cpp1
-rw-r--r--scene/gui/line_edit.cpp3
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/popup_menu.cpp61
-rw-r--r--scene/gui/popup_menu.h5
-rw-r--r--scene/gui/rich_text_label.cpp2
-rw-r--r--scene/gui/scroll_bar.cpp6
-rw-r--r--scene/gui/slider.cpp25
-rw-r--r--scene/gui/slider.h4
-rw-r--r--scene/gui/tab_container.cpp4
-rw-r--r--scene/gui/tab_container.h6
-rw-r--r--scene/gui/text_edit.cpp148
-rw-r--r--scene/gui/text_edit.h6
17 files changed, 347 insertions, 205 deletions
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 34891832e2..8e232c6f46 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -242,6 +242,14 @@ bool ColorPicker::is_raw_mode() const {
return raw_mode_enabled;
}
+void ColorPicker::set_deferred_mode(bool p_enabled) {
+ deferred_mode_enabled = p_enabled;
+}
+
+bool ColorPicker::is_deferred_mode() const {
+ return deferred_mode_enabled;
+}
+
void ColorPicker::_update_text_value() {
bool visible = true;
if (text_is_constructor) {
@@ -328,7 +336,11 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event) {
last_hsv = color;
set_pick_color(color);
_update_color();
+ if (!deferred_mode_enabled)
+ emit_signal("color_changed", color);
+ } else if (deferred_mode_enabled && !bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) {
emit_signal("color_changed", color);
+ changing_color = false;
} else {
changing_color = false;
}
@@ -347,7 +359,8 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event) {
last_hsv = color;
set_pick_color(color);
_update_color();
- emit_signal("color_changed", color);
+ if (!deferred_mode_enabled)
+ emit_signal("color_changed", color);
}
}
@@ -368,7 +381,10 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
last_hsv = color;
set_pick_color(color);
_update_color();
- emit_signal("color_changed", color);
+ if (!deferred_mode_enabled)
+ emit_signal("color_changed", color);
+ else if (!bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT)
+ emit_signal("color_changed", color);
}
Ref<InputEventMouseMotion> mev = p_event;
@@ -383,7 +399,8 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
last_hsv = color;
set_pick_color(color);
_update_color();
- emit_signal("color_changed", color);
+ if (!deferred_mode_enabled)
+ emit_signal("color_changed", color);
}
}
@@ -500,6 +517,8 @@ void ColorPicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pick_color"), &ColorPicker::get_pick_color);
ClassDB::bind_method(D_METHOD("set_raw_mode", "mode"), &ColorPicker::set_raw_mode);
ClassDB::bind_method(D_METHOD("is_raw_mode"), &ColorPicker::is_raw_mode);
+ ClassDB::bind_method(D_METHOD("set_deferred_mode", "mode"), &ColorPicker::set_deferred_mode);
+ ClassDB::bind_method(D_METHOD("is_deferred_mode"), &ColorPicker::is_deferred_mode);
ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPicker::set_edit_alpha);
ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPicker::is_editing_alpha);
ClassDB::bind_method(D_METHOD("add_preset", "color"), &ColorPicker::add_preset);
@@ -522,6 +541,7 @@ void ColorPicker::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "raw_mode"), "set_raw_mode", "is_raw_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deferred_mode"), "set_deferred_mode", "is_deferred_mode");
ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));
}
@@ -533,6 +553,7 @@ ColorPicker::ColorPicker() :
edit_alpha = true;
text_is_constructor = false;
raw_mode_enabled = false;
+ deferred_mode_enabled = false;
changing_color = false;
screen = NULL;
@@ -722,8 +743,9 @@ ColorPicker *ColorPickerButton::get_picker() {
return picker;
}
-PopupPanel *ColorPickerButton::get_popup() const {
+PopupPanel *ColorPickerButton::get_popup() {
+ _update_picker();
return popup;
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 6b63e5fe60..0166da7118 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -67,6 +67,7 @@ private:
Color color;
bool raw_mode_enabled;
+ bool deferred_mode_enabled;
bool updating;
bool changing_color;
float h, s, v;
@@ -107,6 +108,9 @@ public:
void set_raw_mode(bool p_enabled);
bool is_raw_mode() const;
+ void set_deferred_mode(bool p_enabled);
+ bool is_deferred_mode() const;
+
void set_focus_on_line_edit();
ColorPicker();
@@ -140,7 +144,7 @@ public:
bool is_editing_alpha() const;
ColorPicker *get_picker();
- PopupPanel *get_popup() const;
+ PopupPanel *get_popup();
ColorPickerButton();
};
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index a738687a70..068af42260 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -160,9 +160,16 @@ void Control::_update_minimum_size_cache() {
Size2 minsize = get_minimum_size();
minsize.x = MAX(minsize.x, data.custom_minimum_size.x);
minsize.y = MAX(minsize.y, data.custom_minimum_size.y);
+
+ bool size_changed = false;
+ if (data.minimum_size_cache != minsize)
+ size_changed = true;
+
data.minimum_size_cache = minsize;
data.minimum_size_valid = true;
- minimum_size_changed();
+
+ if (size_changed)
+ minimum_size_changed();
}
Size2 Control::get_combined_minimum_size() const {
@@ -1267,35 +1274,34 @@ bool Control::has_constant(const StringName &p_name, const StringName &p_type) c
return Theme::get_default()->has_constant(p_name, type);
}
-Size2 Control::get_parent_area_size() const {
-
- ERR_FAIL_COND_V(!is_inside_tree(), Size2());
-
- Size2 parent_size;
+Rect2 Control::get_parent_anchorable_rect() const {
+ if (!is_inside_tree())
+ return Rect2();
+ Rect2 parent_rect;
if (data.parent_canvas_item) {
-
- parent_size = data.parent_canvas_item->_edit_get_rect().size;
+ parent_rect = data.parent_canvas_item->get_anchorable_rect();
} else {
-
- parent_size = get_viewport()->get_visible_rect().size;
+ parent_rect = get_viewport()->get_visible_rect();
}
- return parent_size;
+ return parent_rect;
}
-void Control::_size_changed() {
+Size2 Control::get_parent_area_size() const {
- if (!is_inside_tree())
- return;
+ return get_parent_anchorable_rect().size;
+}
- Size2 parent_size = get_parent_area_size();
+void Control::_size_changed() {
+
+ Rect2 parent_rect = get_parent_anchorable_rect();
float margin_pos[4];
for (int i = 0; i < 4; i++) {
- float area = parent_size[i & 1];
+ float area = parent_rect.size[i & 1];
margin_pos[i] = data.margin[i] + (data.anchor[i] * area);
}
@@ -1325,9 +1331,9 @@ void Control::_size_changed() {
}
// We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot()
- if (Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) {
- new_size_cache = new_size_cache.floor();
- new_pos_cache = new_pos_cache.floor();
+ if (is_inside_tree() && Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) {
+ new_size_cache = new_size_cache.round();
+ new_pos_cache = new_pos_cache.round();
}
bool pos_changed = new_pos_cache != data.pos_cache;
bool size_changed = new_size_cache != data.size_cache;
@@ -1335,57 +1341,25 @@ void Control::_size_changed() {
data.pos_cache = new_pos_cache;
data.size_cache = new_size_cache;
- if (size_changed) {
- notification(NOTIFICATION_RESIZED);
- }
- if (pos_changed || size_changed) {
- item_rect_changed(size_changed);
- _change_notify_margins();
- _notify_transform();
- }
-
- if (pos_changed && !size_changed) {
- _update_canvas_item_transform(); //move because it won't be updated
- }
-}
-
-float Control::_get_parent_range(int p_idx) const {
-
- if (!is_inside_tree()) {
-
- return 0;
- }
- if (data.parent_canvas_item) {
+ if (is_inside_tree()) {
+ if (size_changed) {
+ notification(NOTIFICATION_RESIZED);
+ }
+ if (pos_changed || size_changed) {
+ item_rect_changed(size_changed);
+ _change_notify_margins();
+ _notify_transform();
+ }
- return data.parent_canvas_item->_edit_get_rect().size[p_idx & 1];
- } else {
- return get_viewport()->get_visible_rect().size[p_idx & 1];
+ if (pos_changed && !size_changed) {
+ _update_canvas_item_transform(); //move because it won't be updated
+ }
}
-
- return 0;
-}
-
-float Control::_get_range(int p_idx) const {
-
- p_idx &= 1;
-
- float parent_range = _get_parent_range(p_idx);
- float from = _a2s(data.margin[p_idx], data.anchor[p_idx], parent_range);
- float to = _a2s(data.margin[p_idx + 2], data.anchor[p_idx + 2], parent_range);
-
- return to - from;
-}
-
-float Control::_s2a(float p_val, float p_anchor, float p_range) const {
- return p_val - (p_anchor * p_range);
-}
-
-float Control::_a2s(float p_val, float p_anchor, float p_range) const {
- return Math::floor(p_val + (p_anchor * p_range));
}
void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bool p_push_opposite_anchor) {
- float parent_range = _get_parent_range((p_margin == MARGIN_LEFT || p_margin == MARGIN_RIGHT) ? 0 : 1);
+ Rect2 parent_rect = get_parent_anchorable_rect();
+ float parent_range = (p_margin == MARGIN_LEFT || p_margin == MARGIN_RIGHT) ? parent_rect.size.x : parent_rect.size.y;
float previous_margin_pos = data.margin[p_margin] + data.anchor[p_margin] * parent_range;
float previous_opposite_margin_pos = data.margin[(p_margin + 2) % 4] + data.anchor[(p_margin + 2) % 4] * parent_range;
@@ -1401,9 +1375,9 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo
}
if (!p_keep_margin) {
- data.margin[p_margin] = _s2a(previous_margin_pos, data.anchor[p_margin], parent_range);
+ data.margin[p_margin] = previous_margin_pos - data.anchor[p_margin] * parent_range;
if (p_push_opposite_anchor) {
- data.margin[(p_margin + 2) % 4] = _s2a(previous_opposite_margin_pos, data.anchor[(p_margin + 2) % 4], parent_range);
+ data.margin[(p_margin + 2) % 4] = previous_opposite_margin_pos - data.anchor[(p_margin + 2) % 4] * parent_range;
}
}
if (is_inside_tree()) {
@@ -1411,7 +1385,7 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo
}
update();
- _change_notify();
+ _change_notify("anchor");
}
void Control::_set_anchor(Margin p_margin, float p_anchor) {
@@ -1557,8 +1531,7 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
new_size.y = min_size.y;
}
- float pw = _get_parent_range(0);
- float ph = _get_parent_range(1);
+ Rect2 parent_rect = get_parent_anchorable_rect();
//Left
switch (p_preset) {
@@ -1570,21 +1543,21 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_LEFT_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
- data.margin[0] = pw * (0.0 - data.anchor[0]) + p_margin;
+ data.margin[0] = parent_rect.size.x * (0.0 - data.anchor[0]) + p_margin + parent_rect.position.x;
break;
case PRESET_CENTER_TOP:
case PRESET_CENTER_BOTTOM:
case PRESET_CENTER:
case PRESET_VCENTER_WIDE:
- data.margin[0] = pw * (0.5 - data.anchor[0]) - new_size.x / 2;
+ data.margin[0] = parent_rect.size.x * (0.5 - data.anchor[0]) - new_size.x / 2 + parent_rect.position.x;
break;
case PRESET_TOP_RIGHT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_RIGHT:
case PRESET_RIGHT_WIDE:
- data.margin[0] = pw * (1.0 - data.anchor[0]) - new_size.x - p_margin;
+ data.margin[0] = parent_rect.size.x * (1.0 - data.anchor[0]) - new_size.x - p_margin + parent_rect.position.x;
break;
}
@@ -1598,21 +1571,21 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_TOP_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_WIDE:
- data.margin[1] = ph * (0.0 - data.anchor[1]) + p_margin;
+ data.margin[1] = parent_rect.size.y * (0.0 - data.anchor[1]) + p_margin + parent_rect.position.y;
break;
case PRESET_CENTER_LEFT:
case PRESET_CENTER_RIGHT:
case PRESET_CENTER:
case PRESET_HCENTER_WIDE:
- data.margin[1] = ph * (0.5 - data.anchor[1]) - new_size.y / 2;
+ data.margin[1] = parent_rect.size.y * (0.5 - data.anchor[1]) - new_size.y / 2 + parent_rect.position.y;
break;
case PRESET_BOTTOM_LEFT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_BOTTOM:
case PRESET_BOTTOM_WIDE:
- data.margin[1] = ph * (1.0 - data.anchor[1]) - new_size.y - p_margin;
+ data.margin[1] = parent_rect.size.y * (1.0 - data.anchor[1]) - new_size.y - p_margin + parent_rect.position.y;
break;
}
@@ -1622,14 +1595,14 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_BOTTOM_LEFT:
case PRESET_CENTER_LEFT:
case PRESET_LEFT_WIDE:
- data.margin[2] = pw * (0.0 - data.anchor[2]) + new_size.x + p_margin;
+ data.margin[2] = parent_rect.size.x * (0.0 - data.anchor[2]) + new_size.x + p_margin + parent_rect.position.x;
break;
case PRESET_CENTER_TOP:
case PRESET_CENTER_BOTTOM:
case PRESET_CENTER:
case PRESET_VCENTER_WIDE:
- data.margin[2] = pw * (0.5 - data.anchor[2]) + new_size.x / 2;
+ data.margin[2] = parent_rect.size.x * (0.5 - data.anchor[2]) + new_size.x / 2 + parent_rect.position.x;
break;
case PRESET_TOP_RIGHT:
@@ -1640,7 +1613,7 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_BOTTOM_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
- data.margin[2] = pw * (1.0 - data.anchor[2]) - p_margin;
+ data.margin[2] = parent_rect.size.x * (1.0 - data.anchor[2]) - p_margin + parent_rect.position.x;
break;
}
@@ -1650,14 +1623,14 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_TOP_RIGHT:
case PRESET_CENTER_TOP:
case PRESET_TOP_WIDE:
- data.margin[3] = ph * (0.0 - data.anchor[3]) + new_size.y + p_margin;
+ data.margin[3] = parent_rect.size.y * (0.0 - data.anchor[3]) + new_size.y + p_margin + parent_rect.position.y;
break;
case PRESET_CENTER_LEFT:
case PRESET_CENTER_RIGHT:
case PRESET_CENTER:
case PRESET_HCENTER_WIDE:
- data.margin[3] = ph * (0.5 - data.anchor[3]) + new_size.y / 2;
+ data.margin[3] = parent_rect.size.y * (0.5 - data.anchor[3]) + new_size.y / 2 + parent_rect.position.y;
break;
case PRESET_BOTTOM_LEFT:
@@ -1668,7 +1641,7 @@ void Control::set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
case PRESET_BOTTOM_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_WIDE:
- data.margin[3] = ph * (1.0 - data.anchor[3]) - p_margin;
+ data.margin[3] = parent_rect.size.y * (1.0 - data.anchor[3]) - p_margin + parent_rect.position.y;
break;
}
@@ -1747,31 +1720,29 @@ void Control::set_global_position(const Point2 &p_point) {
set_position(inv.xform(p_point));
}
-void Control::set_position(const Size2 &p_point) {
-
- float pw = _get_parent_range(0);
- float ph = _get_parent_range(1);
+Rect2 Control::_compute_child_rect(const float p_anchors[4], const float p_margins[4]) const {
- float x = _a2s(data.margin[0], data.anchor[0], pw);
- float y = _a2s(data.margin[1], data.anchor[1], ph);
- float x2 = _a2s(data.margin[2], data.anchor[2], pw);
- float y2 = _a2s(data.margin[3], data.anchor[3], ph);
+ Rect2 anchorable = get_parent_anchorable_rect();
+ Rect2 result = anchorable;
+ for (int i = 0; i < 4; i++) {
+ result.grow_margin((Margin)i, p_anchors[i] * anchorable.get_size()[i % 2] + p_margins[i]);
+ }
- Size2 ret = Size2(x2 - x, y2 - y);
- Size2 min = get_combined_minimum_size();
+ return result;
+}
- Size2 size = Size2(MAX(min.width, ret.width), MAX(min.height, ret.height));
- float w = size.x;
- float h = size.y;
+void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]) {
- x = p_point.x;
- y = p_point.y;
+ Size2 parent_rect_size = get_parent_anchorable_rect().size;
+ r_margins[0] = Math::floor(p_rect.position.x - (p_anchors[0] * parent_rect_size.x));
+ r_margins[1] = Math::floor(p_rect.position.y - (p_anchors[1] * parent_rect_size.y));
+ r_margins[2] = Math::floor(p_rect.position.x + p_rect.size.x - (p_anchors[2] * parent_rect_size.x));
+ r_margins[3] = Math::floor(p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y));
+}
- data.margin[0] = _s2a(x, data.anchor[0], pw);
- data.margin[1] = _s2a(y, data.anchor[1], ph);
- data.margin[2] = _s2a(x + w, data.anchor[2], pw);
- data.margin[3] = _s2a(y + h, data.anchor[3], ph);
+void Control::set_position(const Size2 &p_point) {
+ _compute_margins(Rect2(p_point, data.size_cache), data.anchor, data.margin);
_size_changed();
}
@@ -1784,18 +1755,7 @@ void Control::set_size(const Size2 &p_size) {
if (new_size.y < min.y)
new_size.y = min.y;
- float pw = _get_parent_range(0);
- float ph = _get_parent_range(1);
-
- float x = _a2s(data.margin[0], data.anchor[0], pw);
- float y = _a2s(data.margin[1], data.anchor[1], ph);
-
- float w = new_size.width;
- float h = new_size.height;
-
- data.margin[2] = _s2a(x + w, data.anchor[2], pw);
- data.margin[3] = _s2a(y + h, data.anchor[3], ph);
-
+ _compute_margins(Rect2(data.pos_cache, new_size), data.anchor, data.margin);
_size_changed();
}
@@ -1826,6 +1786,11 @@ Rect2 Control::get_rect() const {
return Rect2(get_position(), get_size());
}
+Rect2 Control::get_anchorable_rect() const {
+
+ return Rect2(Point2(), get_size());
+}
+
void Control::add_icon_override(const StringName &p_name, const Ref<Texture> &p_icon) {
ERR_FAIL_COND(p_icon.is_null());
@@ -2325,12 +2290,11 @@ Control *Control::_get_focus_neighbour(Margin p_margin, int p_count) {
Point2 points[4];
Transform2D xform = get_global_transform();
- Rect2 rect = _edit_get_rect();
- points[0] = xform.xform(rect.position);
- points[1] = xform.xform(rect.position + Point2(rect.size.x, 0));
- points[2] = xform.xform(rect.position + rect.size);
- points[3] = xform.xform(rect.position + Point2(0, rect.size.y));
+ points[0] = xform.xform(Point2());
+ points[1] = xform.xform(Point2(get_size().x, 0));
+ points[2] = xform.xform(get_size());
+ points[3] = xform.xform(Point2(0, get_size().y));
const Vector2 dir[4] = {
Vector2(-1, 0),
@@ -2384,12 +2348,11 @@ void Control::_window_find_focus_neighbour(const Vector2 &p_dir, Node *p_at, con
Point2 points[4];
Transform2D xform = c->get_global_transform();
- Rect2 rect = c->_edit_get_rect();
- points[0] = xform.xform(rect.position);
- points[1] = xform.xform(rect.position + Point2(rect.size.x, 0));
- points[2] = xform.xform(rect.position + rect.size);
- points[3] = xform.xform(rect.position + Point2(0, rect.size.y));
+ points[0] = xform.xform(Point2());
+ points[1] = xform.xform(Point2(get_size().x, 0));
+ points[2] = xform.xform(get_size());
+ points[3] = xform.xform(Point2(0, get_size().y));
float min = 1e7;
@@ -2888,12 +2851,12 @@ void Control::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip");
ADD_GROUP("Focus", "focus_");
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_left"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_LEFT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_top"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_TOP);
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_right"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_RIGHT);
- ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_bottom"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_BOTTOM);
- ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_next"), "set_focus_next", "get_focus_next");
- ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_previous"), "set_focus_previous", "get_focus_previous");
+ ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_LEFT);
+ ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_top", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_TOP);
+ ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_right", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_RIGHT);
+ ADD_PROPERTYINZ(PropertyInfo(Variant::NODE_PATH, "focus_neighbour_bottom", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbour", "get_focus_neighbour", MARGIN_BOTTOM);
+ ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_next", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_next", "get_focus_next");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "focus_previous", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_previous", "get_focus_previous");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
ADD_GROUP("Mouse", "mouse_");
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 9124256624..fa5274d854 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -202,12 +202,12 @@ private:
NodePath focus_next;
NodePath focus_prev;
- HashMap<StringName, Ref<Texture>, StringNameHasher> icon_override;
- HashMap<StringName, Ref<Shader>, StringNameHasher> shader_override;
- HashMap<StringName, Ref<StyleBox>, StringNameHasher> style_override;
- HashMap<StringName, Ref<Font>, StringNameHasher> font_override;
- HashMap<StringName, Color, StringNameHasher> color_override;
- HashMap<StringName, int, StringNameHasher> constant_override;
+ HashMap<StringName, Ref<Texture> > icon_override;
+ HashMap<StringName, Ref<Shader> > shader_override;
+ HashMap<StringName, Ref<StyleBox> > style_override;
+ HashMap<StringName, Ref<Font> > font_override;
+ HashMap<StringName, Color> color_override;
+ HashMap<StringName, int> constant_override;
Map<Ref<Font>, int> font_refcount;
} data;
@@ -220,10 +220,6 @@ private:
void _set_anchor(Margin p_margin, float p_anchor);
- float _get_parent_range(int p_idx) const;
- float _get_range(int p_idx) const;
- float _s2a(float p_val, float p_anchor, float p_range) const;
- float _a2s(float p_val, float p_anchor, float p_range) const;
void _propagate_theme_changed(CanvasItem *p_at, Control *p_owner, bool p_assign = true);
void _theme_changed();
@@ -233,6 +229,9 @@ private:
void _update_scroll();
void _resize(const Size2 &p_size);
+ Rect2 _compute_child_rect(const float p_anchors[4], const float p_margins[4]) const;
+ void _compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]);
+
void _size_changed();
String _get_tooltip() const;
@@ -283,6 +282,7 @@ public:
};
+ /* EDITOR */
virtual Dictionary _edit_get_state() const;
virtual void _edit_set_state(const Dictionary &p_state);
@@ -358,6 +358,7 @@ public:
Rect2 get_rect() const;
Rect2 get_global_rect() const;
Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the visual server
+ Rect2 get_anchorable_rect() const;
void set_rotation(float p_radians);
void set_rotation_degrees(float p_degrees);
@@ -465,6 +466,7 @@ public:
bool is_toplevel_control() const;
Size2 get_parent_area_size() const;
+ Rect2 get_parent_anchorable_rect() const;
void grab_click_focus();
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 57b9a9a11b..72ed0e9b81 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -942,6 +942,7 @@ void ItemList::_notification(int p_what) {
}
}
+ minimum_size_changed();
shape_changed = false;
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 0cd5219f8f..b71a4dd133 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -711,7 +711,8 @@ void LineEdit::_notification(int p_what) {
if (selected)
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(char_width, caret_height)), selection_color);
- drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, selected ? font_color_selected : font_color);
+ int yofs = y_ofs + (caret_height - font->get_height()) / 2;
+ drawer.draw_char(ci, Point2(x_ofs, yofs + font_ascent), cchar, next, selected ? font_color_selected : font_color);
if (char_ofs == cursor_pos && draw_caret) {
if (ime_text.length() == 0) {
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index c5e4149782..2901176a69 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -36,7 +36,7 @@ Size2 OptionButton::get_minimum_size() const {
Size2 minsize = Button::get_minimum_size();
if (has_icon("arrow"))
- minsize.width += Control::get_icon("arrow")->get_width();
+ minsize.width += Control::get_icon("arrow")->get_width() + get_constant("hseparation");
return minsize;
}
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 18e609c798..ebec61ee6d 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -398,6 +398,15 @@ void PopupMenu::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+
+ PopupMenu *pm = Object::cast_to<PopupMenu>(get_parent());
+ if (pm) {
+ // Inherit submenu's popup delay time from parent menu
+ float pm_delay = pm->get_submenu_popup_delay();
+ set_submenu_popup_delay(pm_delay);
+ }
+ } break;
case NOTIFICATION_TRANSLATION_CHANGED: {
for (int i = 0; i < items.size(); i++) {
@@ -421,6 +430,8 @@ void PopupMenu::_notification(int p_what) {
Ref<Texture> uncheck[] = { get_icon("unchecked"), get_icon("radio_unchecked") };
Ref<Texture> submenu = get_icon("submenu");
Ref<StyleBox> separator = get_stylebox("separator");
+ Ref<StyleBox> labeled_separator_left = get_stylebox("labeled_separator_left");
+ Ref<StyleBox> labeled_separator_right = get_stylebox("labeled_separator_right");
style->draw(ci, Rect2(Point2(), get_size()));
Point2 ofs = style->get_offset();
@@ -457,10 +468,25 @@ void PopupMenu::_notification(int p_what) {
hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(get_size().width - style->get_minimum_size().width + hseparation * 2, h + vseparation)));
}
+ String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text;
+
if (items[i].separator) {
int sep_h = separator->get_center_size().height + separator->get_minimum_size().height;
- separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h)));
+ if (text != String()) {
+ int ss = font->get_string_size(text).width;
+ int center = (get_size().width) / 2;
+ int l = center - ss / 2;
+ int r = center + ss / 2;
+ if (l > item_ofs.x) {
+ labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, l - item_ofs.x), sep_h)));
+ }
+ if (r < get_size().width - style->get_margin(MARGIN_RIGHT)) {
+ labeled_separator_right->draw(ci, Rect2(Point2(r, item_ofs.y + Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, get_size().width - style->get_margin(MARGIN_RIGHT) - r), sep_h)));
+ }
+ } else {
+ separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h)));
+ }
}
if (items[i].checkable_type) {
@@ -480,8 +506,13 @@ void PopupMenu::_notification(int p_what) {
}
item_ofs.y += font->get_ascent();
- String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text;
- if (!items[i].separator) {
+ if (items[i].separator) {
+
+ if (text != String()) {
+ int center = (get_size().width - font->get_string_size(text).width) / 2;
+ font->draw(ci, Point2(center, item_ofs.y + Math::floor((h - font_h) / 2.0)), text, font_color_disabled);
+ }
+ } else {
font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text, items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color));
}
@@ -1064,11 +1095,15 @@ void PopupMenu::remove_item(int p_idx) {
update();
}
-void PopupMenu::add_separator() {
+void PopupMenu::add_separator(const String &p_text) {
Item sep;
sep.separator = true;
sep.ID = -1;
+ if (p_text != String()) {
+ sep.text = p_text;
+ sep.xl_text = tr(p_text);
+ }
items.push_back(sep);
update();
}
@@ -1201,6 +1236,19 @@ bool PopupMenu::is_hide_on_multistate_item_selection() const {
return hide_on_multistate_item_selection;
}
+void PopupMenu::set_submenu_popup_delay(float p_time) {
+
+ if (p_time <= 0)
+ p_time = 0.01;
+
+ submenu_timer->set_wait_time(p_time);
+}
+
+float PopupMenu::get_submenu_popup_delay() const {
+
+ return submenu_timer->get_wait_time();
+}
+
String PopupMenu::get_tooltip(const Point2 &p_pos) const {
int over = _get_mouse_over(p_pos);
@@ -1288,7 +1336,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_item", "idx"), &PopupMenu::remove_item);
- ClassDB::bind_method(D_METHOD("add_separator"), &PopupMenu::add_separator);
+ ClassDB::bind_method(D_METHOD("add_separator", "label"), &PopupMenu::add_separator, DEFVAL(String()));
ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear);
ClassDB::bind_method(D_METHOD("_set_items"), &PopupMenu::_set_items);
@@ -1303,12 +1351,15 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_on_state_item_selection", "enable"), &PopupMenu::set_hide_on_multistate_item_selection);
ClassDB::bind_method(D_METHOD("is_hide_on_state_item_selection"), &PopupMenu::is_hide_on_multistate_item_selection);
+ ClassDB::bind_method(D_METHOD("set_submenu_popup_delay", "seconds"), &PopupMenu::set_submenu_popup_delay);
+ ClassDB::bind_method(D_METHOD("get_submenu_popup_delay"), &PopupMenu::get_submenu_popup_delay);
ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay");
ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "ID")));
ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "ID")));
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index d3ee9be1c0..8ec51c7d3a 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -180,7 +180,7 @@ public:
void remove_item(int p_idx);
- void add_separator();
+ void add_separator(const String &p_text = String());
void clear();
@@ -202,6 +202,9 @@ public:
void set_hide_on_multistate_item_selection(bool p_enabled);
bool is_hide_on_multistate_item_selection() const;
+ void set_submenu_popup_delay(float p_time);
+ float get_submenu_popup_delay() const;
+
virtual void popup(const Rect2 &p_bounds = Rect2());
PopupMenu();
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index f34559fc8d..ce2e3538da 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -324,7 +324,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
color = _find_color(text, p_base_color);
font_color_shadow = _find_color(text, p_font_color_shadow);
underline = _find_underline(text);
- if (_find_meta(text, &meta)) {
+ if (_find_meta(text, &meta) && underline_meta) {
underline = true;
}
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index 6ec67aca6b..e5bd1c453d 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -257,9 +257,7 @@ void ScrollBar::_notification(int p_what) {
Point2 ofs;
- VisualServer *vs = VisualServer::get_singleton();
-
- vs->canvas_item_add_texture_rect(ci, Rect2(Point2(), decr->get_size()), decr->get_rid());
+ decr->draw(ci, Point2());
if (orientation == HORIZONTAL)
ofs.x += decr->get_width();
@@ -280,7 +278,7 @@ void ScrollBar::_notification(int p_what) {
else
ofs.height += area.height;
- vs->canvas_item_add_texture_rect(ci, Rect2(ofs, decr->get_size()), incr->get_rid());
+ incr->draw(ci, ofs);
Rect2 grabber_rect;
if (orientation == HORIZONTAL) {
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index 46215c9277..b820e2eafd 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -65,11 +65,12 @@ void Slider::_gui_input(Ref<InputEvent> p_event) {
} else {
grab.active = false;
}
- } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) {
-
- set_value(get_value() + get_step());
- } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) {
- set_value(get_value() - get_step());
+ } else if (scrollable) {
+ if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) {
+ set_value(get_value() + get_step());
+ } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) {
+ set_value(get_value() - get_step());
+ }
}
}
@@ -247,6 +248,16 @@ bool Slider::is_editable() const {
return editable;
}
+void Slider::set_scrollable(bool p_scrollable) {
+
+ scrollable = p_scrollable;
+}
+
+bool Slider::is_scrollable() const {
+
+ return scrollable;
+}
+
void Slider::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &Slider::_gui_input);
@@ -258,8 +269,11 @@ void Slider::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_editable", "editable"), &Slider::set_editable);
ClassDB::bind_method(D_METHOD("is_editable"), &Slider::is_editable);
+ ClassDB::bind_method(D_METHOD("set_scrollable", "scrollable"), &Slider::set_scrollable);
+ ClassDB::bind_method(D_METHOD("is_scrollable"), &Slider::is_scrollable);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrollable"), "set_scrollable", "is_scrollable");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tick_count", PROPERTY_HINT_RANGE, "0,4096,1"), "set_ticks", "get_ticks");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ticks_on_borders"), "set_ticks_on_borders", "get_ticks_on_borders");
ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
@@ -272,5 +286,6 @@ Slider::Slider(Orientation p_orientation) {
ticks = 0;
custom_step = -1;
editable = true;
+ scrollable = true;
set_focus_mode(FOCUS_ALL);
}
diff --git a/scene/gui/slider.h b/scene/gui/slider.h
index e77a0b7423..4d02348159 100644
--- a/scene/gui/slider.h
+++ b/scene/gui/slider.h
@@ -48,6 +48,7 @@ class Slider : public Range {
Orientation orientation;
float custom_step;
bool editable;
+ bool scrollable;
protected:
void _gui_input(Ref<InputEvent> p_event);
@@ -70,6 +71,9 @@ public:
void set_editable(bool p_editable);
bool is_editable() const;
+ void set_scrollable(bool p_scrollable);
+ bool is_scrollable() const;
+
Slider(Orientation p_orientation = VERTICAL);
};
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 4f72b5c6ed..c30fa96327 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -407,7 +407,7 @@ void TabContainer::_child_renamed_callback() {
void TabContainer::add_child_notify(Node *p_child) {
- Control::add_child_notify(p_child);
+ Container::add_child_notify(p_child);
Control *c = Object::cast_to<Control>(p_child);
if (!c)
@@ -515,7 +515,7 @@ Control *TabContainer::get_current_tab_control() const {
void TabContainer::remove_child_notify(Node *p_child) {
- Control::remove_child_notify(p_child);
+ Container::remove_child_notify(p_child);
call_deferred("_update_current_tab");
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 1afe5f7541..8a3c9d2bb2 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -31,11 +31,11 @@
#ifndef TAB_CONTAINER_H
#define TAB_CONTAINER_H
-#include "scene/gui/control.h"
+#include "scene/gui/container.h"
#include "scene/gui/popup.h"
-class TabContainer : public Control {
+class TabContainer : public Container {
- GDCLASS(TabContainer, Control);
+ GDCLASS(TabContainer, Container);
public:
enum TabAlign {
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 215ba0271f..218b5060a1 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -426,6 +426,9 @@ void TextEdit::_update_scrollbars() {
void TextEdit::_click_selection_held() {
+ // Warning: is_mouse_button_pressed(BUTTON_LEFT) returns false for double+ clicks, so this doesn't work for MODE_WORD
+ // and MODE_LINE. However, moving the mouse triggers _gui_input, which calls these functions too, so that's not a huge problem.
+ // I'm unsure if there's an actual fix that doesn't have a ton of side effects.
if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && selection.selecting_mode != Selection::MODE_NONE) {
switch (selection.selecting_mode) {
case Selection::MODE_POINTER: {
@@ -455,7 +458,7 @@ void TextEdit::_update_selection_mode_pointer() {
select(selection.selecting_line, selection.selecting_column, row, col);
cursor_set_line(row, false);
- cursor_set_column(col, false);
+ cursor_set_column(col);
update();
click_select_held->start();
@@ -496,21 +499,24 @@ void TextEdit::_update_selection_mode_word() {
selection.selected_word_beg = beg;
selection.selected_word_end = end;
selection.selected_word_origin = beg;
+ cursor_set_line(selection.to_line, false);
cursor_set_column(selection.to_column);
} else {
if ((col <= selection.selected_word_origin && row == selection.selecting_line) || row < selection.selecting_line) {
selection.selecting_column = selection.selected_word_end;
select(row, beg, selection.selecting_line, selection.selected_word_end);
+ cursor_set_line(selection.from_line, false);
cursor_set_column(selection.from_column);
} else {
selection.selecting_column = selection.selected_word_beg;
select(selection.selecting_line, selection.selected_word_beg, row, end);
+ cursor_set_line(selection.to_line, false);
cursor_set_column(selection.to_column);
}
}
- cursor_set_line(row, false);
update();
+
click_select_held->start();
}
@@ -531,7 +537,7 @@ void TextEdit::_update_selection_mode_line() {
selection.selecting_column = 0;
col = text[row].length();
}
- cursor_set_column(0, false);
+ cursor_set_column(0);
select(selection.selecting_line, selection.selecting_column, row, col);
update();
@@ -960,12 +966,13 @@ void TextEdit::_notification(int p_what) {
// draw line numbers
if (cache.line_number_w) {
+ int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
String fc = String::num(line + 1);
while (fc.length() < line_number_char_count) {
fc = line_num_padding + fc;
}
- cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color);
+ cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, cache.line_number_color);
}
}
@@ -1088,12 +1095,13 @@ void TextEdit::_notification(int p_what) {
}
if (brace_matching_enabled) {
+ int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
if ((brace_open_match_line == line && brace_open_match_column == last_wrap_column + j) ||
(cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_open_matching || brace_open_mismatch))) {
if (brace_open_mismatch)
color = cache.brace_mismatch_color;
- drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
if ((brace_close_match_line == line && brace_close_match_column == last_wrap_column + j) ||
@@ -1101,7 +1109,7 @@ void TextEdit::_notification(int p_what) {
if (brace_close_mismatch)
color = cache.brace_mismatch_color;
- drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
}
@@ -1161,9 +1169,10 @@ void TextEdit::_notification(int p_what) {
}
if (str[j] >= 32) {
- int w = drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
+ int w = drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
if (underlined) {
- draw_rect(Rect2(char_ofs + char_margin + ofs_x, ofs_y + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ draw_rect(Rect2(char_ofs + char_margin + ofs_x, yofs + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
} else if (draw_tabs && str[j] == '\t') {
int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2;
@@ -1528,8 +1537,11 @@ void TextEdit::backspace_at_cursor() {
if (is_line_hidden(cursor.line))
set_line_as_hidden(prev_line, true);
- if (is_line_set_as_breakpoint(cursor.line))
+ if (is_line_set_as_breakpoint(cursor.line)) {
+ if (!text.is_breakpoint(prev_line))
+ emit_signal("breakpoint_toggled", prev_line);
set_line_as_breakpoint(prev_line, true);
+ }
if (auto_brace_completion_enabled &&
cursor.column > 0 &&
@@ -1660,14 +1672,17 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
rows /= get_row_height();
rows += get_v_scroll_offset();
int first_vis_line = get_first_visible_line();
+ int last_vis_line = get_last_visible_line();
int row = first_vis_line + Math::floor(rows);
int wrap_index = 0;
if (is_wrap_enabled() || is_hiding_enabled()) {
- int f_ofs = num_lines_from_rows(first_vis_line, cursor.wrap_ofs, rows + 1, wrap_index) - 1;
- row = first_vis_line + f_ofs;
- row = CLAMP(row, 0, get_last_visible_line() + 1);
+ int f_ofs = num_lines_from_rows(first_vis_line, cursor.wrap_ofs, rows + (1 * SGN(rows)), wrap_index) - 1;
+ if (rows < 0)
+ row = first_vis_line - f_ofs;
+ else
+ row = first_vis_line + f_ofs;
}
if (row < 0)
@@ -3301,22 +3316,37 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
ERR_FAIL_INDEX(p_line, text.size());
ERR_FAIL_COND(p_char < 0);
- /* STEP 1 add spaces if the char is greater than the end of the line */
+ /* STEP 1 remove \r from source text and separate in substrings */
+
+ Vector<String> substrings = p_text.replace("\r", "").split("\n");
+
+ /* STEP 2 fire breakpoint_toggled signals */
+
+ // Is this just a new empty line?
+ bool shift_first_line = p_char == 0 && p_text.replace("\r", "") == "\n";
+
+ int i = p_line + !shift_first_line;
+ int lines = substrings.size() - 1;
+ for (; i < text.size(); i++) {
+ if (text.is_breakpoint(i)) {
+ if ((i - lines < p_line || !text.is_breakpoint(i - lines)) || (i - lines == p_line && !shift_first_line))
+ emit_signal("breakpoint_toggled", i);
+ if (i + lines >= text.size() || !text.is_breakpoint(i + lines))
+ emit_signal("breakpoint_toggled", i + lines);
+ }
+ }
+
+ /* STEP 3 add spaces if the char is greater than the end of the line */
while (p_char > text[p_line].length()) {
text.set(p_line, text[p_line] + String::chr(' '));
}
- /* STEP 2 separate dest string in pre and post text */
+ /* STEP 4 separate dest string in pre and post text */
String preinsert_text = text[p_line].substr(0, p_char);
String postinsert_text = text[p_line].substr(p_char, text[p_line].size());
- /* STEP 3 remove \r from source text and separate in substrings */
-
- //buh bye \r and split
- Vector<String> substrings = p_text.replace("\r", "").split("\n");
-
for (int i = 0; i < substrings.size(); i++) {
//insert the substrings
@@ -3334,9 +3364,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
}
}
- // if we are just making a new empty line, reset breakpoints and hidden status
- if (p_char == 0 && p_text.replace("\r", "") == "\n") {
-
+ if (shift_first_line) {
text.set_breakpoint(p_line + 1, text.is_breakpoint(p_line));
text.set_hidden(p_line + 1, text.is_hidden(p_line));
text.set_breakpoint(p_line, false);
@@ -3392,11 +3420,20 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
String pre_text = text[p_from_line].substr(0, p_from_column);
String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length());
- for (int i = p_from_line; i < p_to_line; i++) {
+ int lines = p_to_line - p_from_line;
- text.remove(p_from_line + 1);
+ for (int i = p_from_line + 1; i < text.size(); i++) {
+ if (text.is_breakpoint(i)) {
+ if (i + lines >= text.size() || !text.is_breakpoint(i + lines))
+ emit_signal("breakpoint_toggled", i);
+ if (i > p_to_line && (i - lines < 0 || !text.is_breakpoint(i - lines)))
+ emit_signal("breakpoint_toggled", i - lines);
+ }
}
+ for (int i = p_from_line; i < p_to_line; i++) {
+ text.remove(p_from_line + 1);
+ }
text.set(p_from_line, pre_text + post_text);
text.set_line_wrap_amount(p_from_line, -1);
@@ -4869,6 +4906,24 @@ void TextEdit::get_breakpoints(List<int> *p_breakpoints) const {
}
}
+Array TextEdit::get_breakpoints_array() const {
+
+ Array arr;
+ for (int i = 0; i < text.size(); i++) {
+ if (text.is_breakpoint(i))
+ arr.append(i);
+ }
+ return arr;
+}
+
+void TextEdit::remove_breakpoints() {
+ for (int i = 0; i < text.size(); i++) {
+ if (text.is_breakpoint(i))
+ /* Should "breakpoint_toggled" be fired when breakpoints are removed this way? */
+ text.set_breakpoint(i, false);
+ }
+}
+
void TextEdit::set_line_as_hidden(int p_line, bool p_hidden) {
ERR_FAIL_INDEX(p_line, text.size());
@@ -5538,7 +5593,17 @@ void TextEdit::_confirm_completion() {
cursor_set_column(cursor.column - completion_base.length(), false);
insert_text_at_cursor(completion_current);
- if (completion_current.ends_with("(") && auto_brace_completion_enabled) {
+ // When inserted into the middle of an existing string, don't add an unnecessary quote
+ String line = text[cursor.line];
+ CharType next_char = line[cursor.column];
+ CharType last_completion_char = completion_current[completion_current.length() - 1];
+
+ if ((last_completion_char == '"' || last_completion_char == '\'') &&
+ last_completion_char == next_char) {
+ _base_remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
+ }
+
+ if (last_completion_char == '(' && auto_brace_completion_enabled) {
insert_text_at_cursor(")");
cursor.column--;
}
@@ -5765,18 +5830,23 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
if (select_word(s, col, beg, end)) {
bool inside_quotes = false;
+ char selected_quote = '\0';
int qbegin = 0, qend = 0;
for (int i = 0; i < s.length(); i++) {
- if (s[i] == '"') {
- if (inside_quotes) {
- qend = i;
- inside_quotes = false;
- if (col >= qbegin && col <= qend) {
- return s.substr(qbegin, qend - qbegin);
+ if (s[i] == '"' || s[i] == '\'') {
+ if (i == 0 || s[i - 1] != '\\') {
+ if (inside_quotes && selected_quote == s[i]) {
+ qend = i;
+ inside_quotes = false;
+ selected_quote = '\0';
+ if (col >= qbegin && col <= qend) {
+ return s.substr(qbegin, qend - qbegin);
+ }
+ } else if (!inside_quotes) {
+ qbegin = i + 1;
+ inside_quotes = true;
+ selected_quote = s[i];
}
- } else {
- qbegin = i + 1;
- inside_quotes = true;
}
}
}
@@ -5857,12 +5927,12 @@ void TextEdit::set_line_length_guideline_column(int p_column) {
update();
}
-void TextEdit::set_draw_breakpoint_gutter(bool p_draw) {
+void TextEdit::set_breakpoint_gutter_enabled(bool p_draw) {
draw_breakpoint_gutter = p_draw;
update();
}
-bool TextEdit::is_drawing_breakpoint_gutter() const {
+bool TextEdit::is_breakpoint_gutter_enabled() const {
return draw_breakpoint_gutter;
}
@@ -6045,6 +6115,8 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_show_line_numbers", "enable"), &TextEdit::set_show_line_numbers);
ClassDB::bind_method(D_METHOD("is_show_line_numbers_enabled"), &TextEdit::is_show_line_numbers_enabled);
+ ClassDB::bind_method(D_METHOD("set_breakpoint_gutter_enabled", "enable"), &TextEdit::set_breakpoint_gutter_enabled);
+ ClassDB::bind_method(D_METHOD("is_breakpoint_gutter_enabled"), &TextEdit::is_breakpoint_gutter_enabled);
ClassDB::bind_method(D_METHOD("set_hiding_enabled", "enable"), &TextEdit::set_hiding_enabled);
ClassDB::bind_method(D_METHOD("is_hiding_enabled"), &TextEdit::is_hiding_enabled);
@@ -6083,11 +6155,15 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option);
ClassDB::bind_method(D_METHOD("get_menu"), &TextEdit::get_menu);
+ ClassDB::bind_method(D_METHOD("get_breakpoints"), &TextEdit::get_breakpoints_array);
+ ClassDB::bind_method(D_METHOD("remove_breakpoints"), &TextEdit::remove_breakpoints);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "readonly"), "set_readonly", "is_readonly");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "syntax_highlighting"), "set_syntax_coloring", "is_syntax_coloring_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_line_numbers"), "set_show_line_numbers", "is_show_line_numbers_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "breakpoint_gutter"), "set_breakpoint_gutter_enabled", "is_breakpoint_gutter_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_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, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 5c82d1ac20..586f4c8e93 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -473,6 +473,8 @@ public:
void set_line_as_breakpoint(int p_line, bool p_breakpoint);
bool is_line_set_as_breakpoint(int p_line) const;
void get_breakpoints(List<int> *p_breakpoints) const;
+ Array get_breakpoints_array() const;
+ void remove_breakpoints();
void set_line_as_hidden(int p_line, bool p_hidden);
bool is_line_hidden(int p_line) const;
@@ -632,8 +634,8 @@ public:
void set_show_line_length_guideline(bool p_show);
void set_line_length_guideline_column(int p_column);
- void set_draw_breakpoint_gutter(bool p_draw);
- bool is_drawing_breakpoint_gutter() const;
+ void set_breakpoint_gutter_enabled(bool p_draw);
+ bool is_breakpoint_gutter_enabled() const;
void set_breakpoint_gutter_width(int p_gutter_width);
int get_breakpoint_gutter_width() const;