summaryrefslogtreecommitdiff
path: root/scene/gui
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui')
-rw-r--r--scene/gui/base_button.cpp24
-rw-r--r--scene/gui/base_button.h20
-rw-r--r--scene/gui/box_container.cpp15
-rw-r--r--scene/gui/box_container.h6
-rw-r--r--scene/gui/button.cpp60
-rw-r--r--scene/gui/button.h10
-rw-r--r--scene/gui/center_container.cpp4
-rw-r--r--scene/gui/center_container.h2
-rw-r--r--scene/gui/check_box.cpp6
-rw-r--r--scene/gui/color_picker.cpp549
-rw-r--r--scene/gui/color_picker.h93
-rw-r--r--scene/gui/container.cpp13
-rw-r--r--scene/gui/container.h4
-rw-r--r--scene/gui/control.cpp239
-rw-r--r--scene/gui/control.h54
-rw-r--r--scene/gui/dialogs.cpp11
-rw-r--r--scene/gui/dialogs.h4
-rw-r--r--scene/gui/file_dialog.cpp176
-rw-r--r--scene/gui/file_dialog.h18
-rw-r--r--scene/gui/gradient_edit.cpp6
-rw-r--r--scene/gui/gradient_edit.h4
-rw-r--r--scene/gui/graph_edit.cpp173
-rw-r--r--scene/gui/graph_edit.h47
-rw-r--r--scene/gui/graph_node.cpp167
-rw-r--r--scene/gui/graph_node.h37
-rw-r--r--scene/gui/grid_container.cpp4
-rw-r--r--scene/gui/grid_container.h2
-rw-r--r--scene/gui/item_list.cpp73
-rw-r--r--scene/gui/item_list.h50
-rw-r--r--scene/gui/label.cpp48
-rw-r--r--scene/gui/label.h2
-rw-r--r--scene/gui/line_edit.cpp1160
-rw-r--r--scene/gui/line_edit.h75
-rw-r--r--scene/gui/link_button.cpp19
-rw-r--r--scene/gui/link_button.h2
-rw-r--r--scene/gui/menu_button.cpp3
-rw-r--r--scene/gui/menu_button.h6
-rw-r--r--scene/gui/nine_patch_rect.cpp25
-rw-r--r--scene/gui/nine_patch_rect.h7
-rw-r--r--scene/gui/option_button.cpp7
-rw-r--r--scene/gui/option_button.h2
-rw-r--r--scene/gui/popup_menu.cpp114
-rw-r--r--scene/gui/popup_menu.h59
-rw-r--r--scene/gui/progress_bar.cpp9
-rw-r--r--scene/gui/progress_bar.h2
-rw-r--r--scene/gui/range.cpp33
-rw-r--r--scene/gui/range.h17
-rw-r--r--scene/gui/rich_text_label.cpp327
-rw-r--r--scene/gui/rich_text_label.h118
-rw-r--r--scene/gui/scroll_bar.cpp10
-rw-r--r--scene/gui/scroll_bar.h14
-rw-r--r--scene/gui/scroll_container.cpp151
-rw-r--r--scene/gui/scroll_container.h38
-rw-r--r--scene/gui/separator.h2
-rw-r--r--scene/gui/slider.cpp17
-rw-r--r--scene/gui/slider.h18
-rw-r--r--scene/gui/spin_box.cpp35
-rw-r--r--scene/gui/spin_box.h11
-rw-r--r--scene/gui/split_container.cpp13
-rw-r--r--scene/gui/split_container.h20
-rw-r--r--scene/gui/subviewport_container.cpp6
-rw-r--r--scene/gui/subviewport_container.h4
-rw-r--r--scene/gui/tab_container.cpp73
-rw-r--r--scene/gui/tab_container.h28
-rw-r--r--scene/gui/tabs.cpp159
-rw-r--r--scene/gui/tabs.h54
-rw-r--r--scene/gui/text_edit.cpp2569
-rw-r--r--scene/gui/text_edit.h280
-rw-r--r--scene/gui/texture_button.cpp17
-rw-r--r--scene/gui/texture_button.h10
-rw-r--r--scene/gui/texture_progress_bar.cpp18
-rw-r--r--scene/gui/texture_progress_bar.h14
-rw-r--r--scene/gui/texture_rect.cpp4
-rw-r--r--scene/gui/texture_rect.h8
-rw-r--r--scene/gui/tree.cpp196
-rw-r--r--scene/gui/tree.h203
-rw-r--r--scene/gui/video_player.cpp20
-rw-r--r--scene/gui/video_player.h27
78 files changed, 4197 insertions, 3728 deletions
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 27e33be9d7..ac067aa001 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -53,6 +53,8 @@ void BaseButton::_unpress_group() {
}
void BaseButton::_gui_input(Ref<InputEvent> p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
if (status.disabled) { // no interaction with disabled button
return;
}
@@ -153,6 +155,9 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) {
}
status.pressed = !status.pressed;
_unpress_group();
+ if (button_group.is_valid()) {
+ button_group->emit_signal("pressed", this);
+ }
_toggled(status.pressed);
_pressed();
}
@@ -199,7 +204,6 @@ void BaseButton::set_disabled(bool p_disabled) {
status.pressing_inside = false;
}
update();
- _change_notify("disabled");
}
bool BaseButton::is_disabled() const {
@@ -213,11 +217,13 @@ void BaseButton::set_pressed(bool p_pressed) {
if (status.pressed == p_pressed) {
return;
}
- _change_notify("pressed");
status.pressed = p_pressed;
if (p_pressed) {
_unpress_group();
+ if (button_group.is_valid()) {
+ button_group->emit_signal("pressed", this);
+ }
}
_toggled(status.pressed);
@@ -325,6 +331,8 @@ Ref<Shortcut> BaseButton::get_shortcut() const {
}
void BaseButton::_unhandled_key_input(Ref<InputEvent> p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
if (!_is_focus_owner_in_shorcut_context()) {
return;
}
@@ -448,18 +456,7 @@ void BaseButton::_bind_methods() {
}
BaseButton::BaseButton() {
- toggle_mode = false;
- shortcut_in_tooltip = true;
- keep_pressed_outside = false;
- status.pressed = false;
- status.press_attempt = false;
- status.hovering = false;
- status.pressing_inside = false;
- status.disabled = false;
set_focus_mode(FOCUS_ALL);
- action_mode = ACTION_MODE_BUTTON_RELEASE;
- button_mask = BUTTON_MASK_LEFT;
- shortcut_context = ObjectID();
}
BaseButton::~BaseButton() {
@@ -496,6 +493,7 @@ BaseButton *ButtonGroup::get_pressed_button() {
void ButtonGroup::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pressed_button"), &ButtonGroup::get_pressed_button);
ClassDB::bind_method(D_METHOD("get_buttons"), &ButtonGroup::_get_buttons);
+ ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::OBJECT, "button")));
}
ButtonGroup::ButtonGroup() {
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index b349b75761..6c7a8f3433 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -45,21 +45,21 @@ public:
};
private:
- int button_mask;
- bool toggle_mode;
- bool shortcut_in_tooltip;
- bool keep_pressed_outside;
+ int button_mask = MOUSE_BUTTON_MASK_LEFT;
+ bool toggle_mode = false;
+ bool shortcut_in_tooltip = true;
+ bool keep_pressed_outside = false;
Ref<Shortcut> shortcut;
ObjectID shortcut_context;
- ActionMode action_mode;
+ ActionMode action_mode = ACTION_MODE_BUTTON_RELEASE;
struct Status {
- bool pressed;
- bool hovering;
- bool press_attempt;
- bool pressing_inside;
+ bool pressed = false;
+ bool hovering = false;
+ bool press_attempt = false;
+ bool pressing_inside = false;
- bool disabled;
+ bool disabled = false;
} status;
diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp
index a00d755c9c..7407ad5b8f 100644
--- a/scene/gui/box_container.cpp
+++ b/scene/gui/box_container.cpp
@@ -33,9 +33,9 @@
#include "margin_container.h"
struct _MinSizeCache {
- int min_size;
- bool will_stretch;
- int final_size;
+ int min_size = 0;
+ bool will_stretch = false;
+ int final_size = 0;
};
void BoxContainer::_resort() {
@@ -50,7 +50,7 @@ void BoxContainer::_resort() {
int children_count = 0;
int stretch_min = 0;
int stretch_avail = 0;
- float stretch_ratio_total = 0;
+ float stretch_ratio_total = 0.0;
Map<Control *, _MinSizeCache> min_size_cache;
for (int i = 0; i < get_child_count(); i++) {
@@ -105,7 +105,7 @@ void BoxContainer::_resort() {
has_stretched = true;
bool refit_successful = true; //assume refit-test will go well
- float error = 0; // Keep track of accumulated error in pixels
+ float error = 0.0; // Keep track of accumulated error in pixels
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
@@ -313,7 +313,7 @@ BoxContainer::AlignMode BoxContainer::get_alignment() const {
return align;
}
-void BoxContainer::add_spacer(bool p_begin) {
+Control *BoxContainer::add_spacer(bool p_begin) {
Control *c = memnew(Control);
c->set_mouse_filter(MOUSE_FILTER_PASS); //allow spacer to pass mouse events
@@ -327,11 +327,12 @@ void BoxContainer::add_spacer(bool p_begin) {
if (p_begin) {
move_child(c, 0);
}
+
+ return c;
}
BoxContainer::BoxContainer(bool p_vertical) {
vertical = p_vertical;
- align = ALIGN_BEGIN;
}
void BoxContainer::_bind_methods() {
diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h
index 8285c2b9a2..23feea565c 100644
--- a/scene/gui/box_container.h
+++ b/scene/gui/box_container.h
@@ -44,8 +44,8 @@ public:
};
private:
- bool vertical;
- AlignMode align;
+ bool vertical = false;
+ AlignMode align = ALIGN_BEGIN;
void _resort();
@@ -55,7 +55,7 @@ protected:
static void _bind_methods();
public:
- void add_spacer(bool p_begin = false);
+ Control *add_spacer(bool p_begin = false);
void set_alignment(AlignMode p_align);
AlignMode get_alignment() const;
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 119f3539e1..b0bcde8865 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -56,6 +56,11 @@ Size2 Button::get_minimum_size() const {
}
}
+ Ref<Font> font = get_theme_font("font");
+ float font_height = font->get_height(get_theme_font_size("font_size"));
+
+ minsize.height = MAX(font_height, minsize.height);
+
return get_theme_stylebox("normal")->get_minimum_size() + minsize;
}
@@ -102,8 +107,8 @@ void Button::_notification(int p_what) {
style->draw(ci, Rect2(Point2(0, 0), size));
}
color = get_theme_color("font_color");
- if (has_theme_color("icon_color_normal")) {
- color_icon = get_theme_color("icon_color_normal");
+ if (has_theme_color("icon_normal_color")) {
+ color_icon = get_theme_color("icon_normal_color");
}
} break;
case DRAW_HOVER_PRESSED: {
@@ -117,13 +122,13 @@ void Button::_notification(int p_what) {
if (!flat) {
style->draw(ci, Rect2(Point2(0, 0), size));
}
- if (has_theme_color("font_color_hover_pressed")) {
- color = get_theme_color("font_color_hover_pressed");
+ if (has_theme_color("font_hover_pressed_color")) {
+ color = get_theme_color("font_hover_pressed_color");
} else {
color = get_theme_color("font_color");
}
- if (has_theme_color("icon_color_hover_pressed")) {
- color_icon = get_theme_color("icon_color_hover_pressed");
+ if (has_theme_color("icon_hover_pressed_color")) {
+ color_icon = get_theme_color("icon_hover_pressed_color");
}
break;
@@ -140,13 +145,13 @@ void Button::_notification(int p_what) {
if (!flat) {
style->draw(ci, Rect2(Point2(0, 0), size));
}
- if (has_theme_color("font_color_pressed")) {
- color = get_theme_color("font_color_pressed");
+ if (has_theme_color("font_pressed_color")) {
+ color = get_theme_color("font_pressed_color");
} else {
color = get_theme_color("font_color");
}
- if (has_theme_color("icon_color_pressed")) {
- color_icon = get_theme_color("icon_color_pressed");
+ if (has_theme_color("icon_pressed_color")) {
+ color_icon = get_theme_color("icon_pressed_color");
}
} break;
@@ -160,9 +165,9 @@ void Button::_notification(int p_what) {
if (!flat) {
style->draw(ci, Rect2(Point2(0, 0), size));
}
- color = get_theme_color("font_color_hover");
- if (has_theme_color("icon_color_hover")) {
- color_icon = get_theme_color("icon_color_hover");
+ color = get_theme_color("font_hover_color");
+ if (has_theme_color("icon_hover_color")) {
+ color_icon = get_theme_color("icon_hover_color");
}
} break;
@@ -176,9 +181,9 @@ void Button::_notification(int p_what) {
if (!flat) {
style->draw(ci, Rect2(Point2(0, 0), size));
}
- color = get_theme_color("font_color_disabled");
- if (has_theme_color("icon_color_disabled")) {
- color_icon = get_theme_color("icon_color_disabled");
+ color = get_theme_color("font_disabled_color");
+ if (has_theme_color("icon_disabled_color")) {
+ color_icon = get_theme_color("icon_disabled_color");
}
} break;
@@ -203,7 +208,7 @@ void Button::_notification(int p_what) {
color_icon.a = 0.4;
}
- float icon_ofs_region = 0;
+ float icon_ofs_region = 0.0;
if (rtl) {
if (_internal_margin[SIDE_RIGHT] > 0) {
icon_ofs_region = _internal_margin[SIDE_RIGHT] + get_theme_constant("hseparation");
@@ -303,13 +308,13 @@ void Button::_notification(int p_what) {
text_ofs.x -= icon_ofs.x;
}
- Color font_outline_modulate = get_theme_color("font_outline_modulate");
+ Color font_outline_color = get_theme_color("font_outline_color");
int outline_size = get_theme_constant("outline_size");
- if (outline_size > 0 && font_outline_modulate.a > 0) {
- text_buf->draw_outline(ci, text_ofs.floor(), outline_size, font_outline_modulate);
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ text_buf->draw_outline(ci, text_ofs, outline_size, font_outline_color);
}
- text_buf->draw(ci, text_ofs.floor(), color);
+ text_buf->draw(ci, text_ofs, color);
if (!_icon.is_null() && icon_region.size.width > 0) {
draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), _icon->get_size()), color_icon);
@@ -338,7 +343,6 @@ void Button::set_text(const String &p_text) {
_shape();
update();
- _change_notify("text");
minimum_size_changed();
}
}
@@ -399,7 +403,6 @@ void Button::set_icon(const Ref<Texture2D> &p_icon) {
if (icon != p_icon) {
icon = p_icon;
update();
- _change_notify("icon");
minimum_size_changed();
}
}
@@ -424,7 +427,6 @@ void Button::set_flat(bool p_flat) {
if (flat != p_flat) {
flat = p_flat;
update();
- _change_notify("flat");
}
}
@@ -474,7 +476,7 @@ bool Button::_set(const StringName &p_name, const Variant &p_value) {
update();
}
}
- _change_notify();
+ notify_property_list_changed();
return true;
}
@@ -544,16 +546,8 @@ Button::Button(const String &p_text) {
text_buf.instance();
text_buf->set_flags(TextServer::BREAK_MANDATORY);
- flat = false;
- clip_text = false;
- expand_icon = false;
set_mouse_filter(MOUSE_FILTER_STOP);
set_text(p_text);
- align = ALIGN_CENTER;
-
- for (int i = 0; i < 4; i++) {
- _internal_margin[i] = 0;
- }
}
Button::~Button() {
diff --git a/scene/gui/button.h b/scene/gui/button.h
index d633fddc8a..d968f63f51 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -45,7 +45,7 @@ public:
};
private:
- bool flat;
+ bool flat = false;
String text;
String xl_text;
Ref<TextParagraph> text_buf;
@@ -55,10 +55,10 @@ private:
TextDirection text_direction = TEXT_DIRECTION_AUTO;
Ref<Texture2D> icon;
- bool expand_icon;
- bool clip_text;
- TextAlign align;
- float _internal_margin[4];
+ bool expand_icon = false;
+ bool clip_text = false;
+ TextAlign align = ALIGN_CENTER;
+ float _internal_margin[4] = {};
void _shape();
diff --git a/scene/gui/center_container.cpp b/scene/gui/center_container.cpp
index f2d1dee0fc..909516e7ef 100644
--- a/scene/gui/center_container.cpp
+++ b/scene/gui/center_container.cpp
@@ -95,6 +95,4 @@ void CenterContainer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_top_left"), "set_use_top_left", "is_using_top_left");
}
-CenterContainer::CenterContainer() {
- use_top_left = false;
-}
+CenterContainer::CenterContainer() {}
diff --git a/scene/gui/center_container.h b/scene/gui/center_container.h
index ee8b2e0e48..0944f200fc 100644
--- a/scene/gui/center_container.h
+++ b/scene/gui/center_container.h
@@ -36,7 +36,7 @@
class CenterContainer : public Container {
GDCLASS(CenterContainer, Container);
- bool use_top_left;
+ bool use_top_left = false;
protected:
void _notification(int p_what);
diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp
index 9df328dd11..c0650a8f3f 100644
--- a/scene/gui/check_box.cpp
+++ b/scene/gui/check_box.cpp
@@ -34,7 +34,9 @@
Size2 CheckBox::get_icon_size() const {
Ref<Texture2D> checked = Control::get_theme_icon("checked");
+ Ref<Texture2D> checked_disabled = Control::get_theme_icon("checked_disabled");
Ref<Texture2D> unchecked = Control::get_theme_icon("unchecked");
+ Ref<Texture2D> unchecked_disabled = Control::get_theme_icon("unchecked_disabled");
Ref<Texture2D> radio_checked = Control::get_theme_icon("radio_checked");
Ref<Texture2D> radio_unchecked = Control::get_theme_icon("radio_unchecked");
@@ -79,8 +81,8 @@ void CheckBox::_notification(int p_what) {
} else if (p_what == NOTIFICATION_DRAW) {
RID ci = get_canvas_item();
- Ref<Texture2D> on = Control::get_theme_icon(is_radio() ? "radio_checked" : "checked");
- Ref<Texture2D> off = Control::get_theme_icon(is_radio() ? "radio_unchecked" : "unchecked");
+ Ref<Texture2D> on = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_checked" : "checked", is_disabled() ? "_disabled" : ""));
+ Ref<Texture2D> off = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_unchecked" : "unchecked", is_disabled() ? "_disabled" : ""));
Ref<StyleBox> sb = get_theme_stylebox("normal");
Vector2 ofs;
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 6c81ef0998..114abbd4da 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -52,6 +52,7 @@ void ColorPicker::_notification(int p_what) {
btn_pick->set_icon(get_theme_icon("screen_picker", "ColorPicker"));
bt_add_preset->set_icon(get_theme_icon("add_preset"));
+ _update_controls();
_update_color();
#ifdef TOOLS_ENABLED
@@ -112,6 +113,27 @@ void ColorPicker::_update_controls() {
btn_hsv->set_disabled(false);
}
+ if (raw_mode_enabled) {
+ for (int i = 0; i < 3; i++) {
+ scroll[i]->remove_theme_icon_override("grabber");
+ scroll[i]->remove_theme_icon_override("grabber_highlight");
+ scroll[i]->remove_theme_style_override("slider");
+ scroll[i]->remove_theme_style_override("grabber_area");
+ scroll[i]->remove_theme_style_override("grabber_area_highlight");
+ }
+ } else {
+ Ref<StyleBoxEmpty> style_box_empty(memnew(StyleBoxEmpty));
+ Ref<Texture2D> bar_arrow = get_theme_icon("bar_arrow");
+
+ for (int i = 0; i < 4; i++) {
+ scroll[i]->add_theme_icon_override("grabber", bar_arrow);
+ scroll[i]->add_theme_icon_override("grabber_highlight", bar_arrow);
+ scroll[i]->add_theme_style_override("slider", style_box_empty);
+ scroll[i]->add_theme_style_override("grabber_area", style_box_empty);
+ scroll[i]->add_theme_style_override("grabber_area_highlight", style_box_empty);
+ }
+ }
+
if (edit_alpha) {
values[3]->show();
scroll[3]->show();
@@ -121,6 +143,30 @@ void ColorPicker::_update_controls() {
scroll[3]->hide();
labels[3]->hide();
}
+
+ switch (picker_type) {
+ case SHAPE_HSV_RECTANGLE:
+ wheel_edit->hide();
+ w_edit->show();
+ uv_edit->show();
+ break;
+ case SHAPE_HSV_WHEEL:
+ wheel_edit->show();
+ w_edit->hide();
+ uv_edit->hide();
+
+ wheel->set_material(wheel_mat);
+ break;
+ case SHAPE_VHS_CIRCLE:
+ wheel_edit->show();
+ w_edit->show();
+ uv_edit->hide();
+
+ wheel->set_material(circle_mat);
+ break;
+ default: {
+ }
+ }
}
void ColorPicker::_set_pick_color(const Color &p_color, bool p_update_sliders) {
@@ -143,6 +189,18 @@ void ColorPicker::set_pick_color(const Color &p_color) {
_set_pick_color(p_color, true); //because setters can't have more arguments
}
+void ColorPicker::set_old_color(const Color &p_color) {
+ old_color = p_color;
+}
+
+void ColorPicker::set_display_old_color(bool p_enabled) {
+ display_old_color = p_enabled;
+}
+
+bool ColorPicker::is_displaying_old_color() const {
+ return display_old_color;
+}
+
void ColorPicker::set_edit_alpha(bool p_show) {
edit_alpha = p_show;
_update_controls();
@@ -165,10 +223,13 @@ void ColorPicker::_value_changed(double) {
}
if (hsv_mode_enabled) {
- color.set_hsv(scroll[0]->get_value() / 360.0,
- scroll[1]->get_value() / 100.0,
- scroll[2]->get_value() / 100.0,
- scroll[3]->get_value() / 255.0);
+ h = scroll[0]->get_value() / 360.0;
+ s = scroll[1]->get_value() / 100.0;
+ v = scroll[2]->get_value() / 100.0;
+ color.set_hsv(h, s, v, scroll[3]->get_value() / 255.0);
+
+ last_hsv = color;
+
} else {
for (int i = 0; i < 4; i++) {
color.components[i] = scroll[i]->get_value() / (raw_mode_enabled ? 1.0 : 255.0);
@@ -239,6 +300,11 @@ void ColorPicker::_update_color(bool p_update_sliders) {
sample->update();
uv_edit->update();
w_edit->update();
+ for (int i = 0; i < 4; i++) {
+ scroll[i]->update();
+ }
+ wheel->update();
+ wheel_uv->update();
updating = false;
}
@@ -281,6 +347,18 @@ Color ColorPicker::get_pick_color() const {
return color;
}
+void ColorPicker::set_picker_shape(PickerShapeType p_picker_type) {
+ ERR_FAIL_INDEX(p_picker_type, SHAPE_MAX);
+ picker_type = p_picker_type;
+
+ _update_controls();
+ _update_color();
+}
+
+ColorPicker::PickerShapeType ColorPicker::get_picker_shape() const {
+ return picker_type;
+}
+
void ColorPicker::add_preset(const Color &p_color) {
if (presets.find(p_color)) {
presets.move_to_back(presets.find(p_color));
@@ -393,17 +471,39 @@ void ColorPicker::_update_text_value() {
}
void ColorPicker::_sample_draw() {
- const Rect2 r = Rect2(Point2(), Size2(uv_edit->get_size().width, sample->get_size().height * 0.95));
+ // Covers the right half of the sample if the old color is being displayed,
+ // or the whole sample if it's not being displayed.
+ Rect2 rect_new;
+
+ if (display_old_color) {
+ rect_new = Rect2(Point2(sample->get_size().width * 0.5, 0), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95));
+
+ // Draw both old and new colors for easier comparison (only if spawned from a ColorPickerButton).
+ const Rect2 rect_old = Rect2(Point2(), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95));
+
+ if (display_old_color && old_color.a < 1.0) {
+ sample->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), rect_old, true);
+ }
+
+ sample->draw_rect(rect_old, old_color);
+
+ if (old_color.r > 1 || old_color.g > 1 || old_color.b > 1) {
+ // Draw an indicator to denote that the old color is "overbright" and can't be displayed accurately in the preview.
+ sample->draw_texture(get_theme_icon("overbright_indicator", "ColorPicker"), Point2());
+ }
+ } else {
+ rect_new = Rect2(Point2(), Size2(sample->get_size().width, sample->get_size().height * 0.95));
+ }
if (color.a < 1.0) {
- sample->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), r, true);
+ sample->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), rect_new, true);
}
- sample->draw_rect(r, color);
+ sample->draw_rect(rect_new, color);
if (color.r > 1 || color.g > 1 || color.b > 1) {
- // Draw an indicator to denote that the color is "overbright" and can't be displayed accurately in the preview
- sample->draw_texture(get_theme_icon("overbright_indicator", "ColorPicker"), Point2());
+ // Draw an indicator to denote that the new color is "overbright" and can't be displayed accurately in the preview.
+ sample->draw_texture(get_theme_icon("overbright_indicator", "ColorPicker"), Point2(uv_edit->get_size().width * 0.5, 0));
}
}
@@ -413,55 +513,242 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) {
}
if (p_which == 0) {
Vector<Point2> points;
- points.push_back(Vector2());
- points.push_back(Vector2(c->get_size().x, 0));
- points.push_back(c->get_size());
- points.push_back(Vector2(0, c->get_size().y));
Vector<Color> colors;
- colors.push_back(Color(1, 1, 1, 1));
- colors.push_back(Color(1, 1, 1, 1));
- colors.push_back(Color(0, 0, 0, 1));
- colors.push_back(Color(0, 0, 0, 1));
- c->draw_polygon(points, colors);
Vector<Color> colors2;
Color col = color;
+ Vector2 center = c->get_size() / 2.0;
+
+ switch (picker_type) {
+ case SHAPE_HSV_WHEEL: {
+ points.resize(4);
+ colors.resize(4);
+ colors2.resize(4);
+ real_t ring_radius_x = Math_SQRT12 * c->get_size().width * 0.42;
+ real_t ring_radius_y = Math_SQRT12 * c->get_size().height * 0.42;
+
+ points.set(0, center - Vector2(ring_radius_x, ring_radius_y));
+ points.set(1, center + Vector2(ring_radius_x, -ring_radius_y));
+ points.set(2, center + Vector2(ring_radius_x, ring_radius_y));
+ points.set(3, center + Vector2(-ring_radius_x, ring_radius_y));
+ colors.set(0, Color(1, 1, 1, 1));
+ colors.set(1, Color(1, 1, 1, 1));
+ colors.set(2, Color(0, 0, 0, 1));
+ colors.set(3, Color(0, 0, 0, 1));
+ c->draw_polygon(points, colors);
+
+ col.set_hsv(h, 1, 1);
+ col.a = 0;
+ colors2.set(0, col);
+ col.a = 1;
+ colors2.set(1, col);
+ col.set_hsv(h, 1, 0);
+ colors2.set(2, col);
+ col.a = 0;
+ colors2.set(3, col);
+ c->draw_polygon(points, colors2);
+ break;
+ }
+ case SHAPE_HSV_RECTANGLE: {
+ points.resize(4);
+ colors.resize(4);
+ colors2.resize(4);
+ points.set(0, Vector2());
+ points.set(1, Vector2(c->get_size().x, 0));
+ points.set(2, c->get_size());
+ points.set(3, Vector2(0, c->get_size().y));
+ colors.set(0, Color(1, 1, 1, 1));
+ colors.set(1, Color(1, 1, 1, 1));
+ colors.set(2, Color(0, 0, 0, 1));
+ colors.set(3, Color(0, 0, 0, 1));
+ c->draw_polygon(points, colors);
+ col = color;
+ col.set_hsv(h, 1, 1);
+ col.a = 0;
+ colors2.set(0, col);
+ col.a = 1;
+ colors2.set(1, col);
+ col.set_hsv(h, 1, 0);
+ colors2.set(2, col);
+ col.a = 0;
+ colors2.set(3, col);
+ c->draw_polygon(points, colors2);
+ break;
+ }
+ default: {
+ }
+ }
+ Ref<Texture2D> cursor = get_theme_icon("picker_cursor", "ColorPicker");
+ int x;
+ int y;
+ if (picker_type == SHAPE_VHS_CIRCLE) {
+ x = center.x + (center.x * Math::cos(h * Math_TAU) * s) - (cursor->get_width() / 2);
+ y = center.y + (center.y * Math::sin(h * Math_TAU) * s) - (cursor->get_height() / 2);
+ } else {
+ real_t corner_x = (c == wheel_uv) ? center.x - Math_SQRT12 * c->get_size().width * 0.42 : 0;
+ real_t corner_y = (c == wheel_uv) ? center.y - Math_SQRT12 * c->get_size().height * 0.42 : 0;
+
+ Size2 real_size(c->get_size().x - corner_x * 2, c->get_size().y - corner_y * 2);
+ x = CLAMP(real_size.x * s, 0, real_size.x) + corner_x - (cursor->get_width() / 2);
+ y = CLAMP(real_size.y - real_size.y * v, 0, real_size.y) + corner_y - (cursor->get_height() / 2);
+ }
+ c->draw_texture(cursor, Point2(x, y));
+
col.set_hsv(h, 1, 1);
- col.a = 0;
- colors2.push_back(col);
- col.a = 1;
- colors2.push_back(col);
- col.set_hsv(h, 1, 0);
- colors2.push_back(col);
- col.a = 0;
- colors2.push_back(col);
- c->draw_polygon(points, colors2);
- int x = CLAMP(c->get_size().x * s, 0, c->get_size().x);
- int y = CLAMP(c->get_size().y - c->get_size().y * v, 0, c->get_size().y);
- col = color;
- col.a = 1;
- c->draw_line(Point2(x, 0), Point2(x, c->get_size().y), col.inverted());
- c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted());
- c->draw_line(Point2(x, y), Point2(x, y), Color(1, 1, 1), 2);
+ if (picker_type == SHAPE_HSV_WHEEL) {
+ points.resize(4);
+ double h1 = h - (0.5 / 360);
+ double h2 = h + (0.5 / 360);
+ points.set(0, Point2(center.x + (center.x * Math::cos(h1 * Math_TAU)), center.y + (center.y * Math::sin(h1 * Math_TAU))));
+ points.set(1, Point2(center.x + (center.x * Math::cos(h1 * Math_TAU) * 0.84), center.y + (center.y * Math::sin(h1 * Math_TAU) * 0.84)));
+ points.set(2, Point2(center.x + (center.x * Math::cos(h2 * Math_TAU)), center.y + (center.y * Math::sin(h2 * Math_TAU))));
+ points.set(3, Point2(center.x + (center.x * Math::cos(h2 * Math_TAU) * 0.84), center.y + (center.y * Math::sin(h2 * Math_TAU) * 0.84)));
+ c->draw_multiline(points, col.inverted());
+ }
+
} else if (p_which == 1) {
- Ref<Texture2D> hue = get_theme_icon("color_hue", "ColorPicker");
- c->draw_texture_rect(hue, Rect2(Point2(), c->get_size()));
- int y = c->get_size().y - c->get_size().y * (1.0 - h);
- Color col = Color();
- col.set_hsv(h, 1, 1);
- c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted());
+ if (picker_type == SHAPE_HSV_RECTANGLE) {
+ Ref<Texture2D> hue = get_theme_icon("color_hue", "ColorPicker");
+ c->draw_texture_rect(hue, Rect2(Point2(), c->get_size()));
+ int y = c->get_size().y - c->get_size().y * (1.0 - h);
+ Color col;
+ col.set_hsv(h, 1, 1);
+ c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted());
+ } else if (picker_type == SHAPE_VHS_CIRCLE) {
+ Vector<Point2> points;
+ Vector<Color> colors;
+ Color col;
+ col.set_hsv(h, s, 1);
+ points.resize(4);
+ colors.resize(4);
+ points.set(0, Vector2());
+ points.set(1, Vector2(c->get_size().x, 0));
+ points.set(2, c->get_size());
+ points.set(3, Vector2(0, c->get_size().y));
+ colors.set(0, col);
+ colors.set(1, col);
+ colors.set(2, Color(0, 0, 0));
+ colors.set(3, Color(0, 0, 0));
+ c->draw_polygon(points, colors);
+ int y = c->get_size().y - c->get_size().y * CLAMP(v, 0, 1);
+ col.set_hsv(h, 1, v);
+ c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted());
+ }
+ } else if (p_which == 2) {
+ c->draw_rect(Rect2(Point2(), c->get_size()), Color(1, 1, 1));
+ if (picker_type == SHAPE_VHS_CIRCLE) {
+ circle_mat->set_shader_param("v", v);
+ }
}
}
-void ColorPicker::_uv_input(const Ref<InputEvent> &p_event) {
+void ColorPicker::_slider_draw(int p_which) {
+ Vector<Vector2> pos;
+ pos.resize(4);
+ Vector<Color> col;
+ col.resize(4);
+ Size2 size = scroll[p_which]->get_size();
+ Color left_color;
+ Color right_color;
+#ifdef TOOLS_ENABLED
+ const real_t margin = 4 * EDSCALE;
+#else
+ const real_t margin = 4;
+#endif
+
+ if (p_which == 3) {
+ scroll[p_which]->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), Rect2(Point2(0, margin), Size2(size.x, margin)), true);
+
+ left_color = color;
+ left_color.a = 0;
+ right_color = color;
+ right_color.a = 1;
+ } else {
+ if (raw_mode_enabled) {
+ return;
+ }
+ if (hsv_mode_enabled) {
+ if (p_which == 0) {
+ Ref<Texture2D> hue = get_theme_icon("color_hue", "ColorPicker");
+ scroll[p_which]->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0));
+ scroll[p_which]->draw_texture_rect(hue, Rect2(Vector2(margin * -2, 0), Vector2(scroll[p_which]->get_size().x, margin)), false, Color(1, 1, 1), true);
+ return;
+ }
+ Color s_col;
+ Color v_col;
+ s_col.set_hsv(h, 0, v);
+ left_color = (p_which == 1) ? s_col : Color(0, 0, 0);
+ s_col.set_hsv(h, 1, v);
+ v_col.set_hsv(h, s, 1);
+ right_color = (p_which == 1) ? s_col : v_col;
+ } else {
+ left_color = Color(
+ p_which == 0 ? 0 : color.r,
+ p_which == 1 ? 0 : color.g,
+ p_which == 2 ? 0 : color.b);
+ right_color = Color(
+ p_which == 0 ? 1 : color.r,
+ p_which == 1 ? 1 : color.g,
+ p_which == 2 ? 1 : color.b);
+ }
+ }
+
+ col.set(0, left_color);
+ col.set(1, right_color);
+ col.set(2, right_color);
+ col.set(3, left_color);
+ pos.set(0, Vector2(0, margin));
+ pos.set(1, Vector2(size.x, margin));
+ pos.set(2, Vector2(size.x, margin * 2));
+ pos.set(3, Vector2(0, margin * 2));
+
+ scroll[p_which]->draw_polygon(pos, col);
+}
+
+void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) {
Ref<InputEventMouseButton> bev = p_event;
if (bev.is_valid()) {
- if (bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) {
+ if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) {
+ Vector2 center = c->get_size() / 2.0;
+ if (picker_type == SHAPE_VHS_CIRCLE) {
+ real_t dist = center.distance_to(bev->get_position());
+
+ if (dist <= center.x) {
+ real_t rad = Math::atan2(bev->get_position().y - center.y, bev->get_position().x - center.x);
+ h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU;
+ s = CLAMP(dist / center.x, 0, 1);
+ } else {
+ return;
+ }
+ } else {
+ real_t corner_x = (c == wheel_uv) ? center.x - Math_SQRT12 * c->get_size().width * 0.42 : 0;
+ real_t corner_y = (c == wheel_uv) ? center.y - Math_SQRT12 * c->get_size().height * 0.42 : 0;
+ Size2 real_size(c->get_size().x - corner_x * 2, c->get_size().y - corner_y * 2);
+
+ if (bev->get_position().x < corner_x || bev->get_position().x > c->get_size().x - corner_x ||
+ bev->get_position().y < corner_y || bev->get_position().y > c->get_size().y - corner_y) {
+ {
+ real_t dist = center.distance_to(bev->get_position());
+
+ if (dist >= center.x * 0.84 && dist <= center.x) {
+ real_t rad = Math::atan2(bev->get_position().y - center.y, bev->get_position().x - center.x);
+ h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU;
+ spinning = true;
+ } else {
+ return;
+ }
+ }
+ }
+
+ if (!spinning) {
+ real_t x = CLAMP(bev->get_position().x, corner_x, c->get_size().x - corner_x);
+ real_t y = CLAMP(bev->get_position().y, corner_x, c->get_size().y - corner_y);
+
+ s = (x - c->get_position().x - corner_x) / real_size.x;
+ v = 1.0 - (y - c->get_position().y - corner_y) / real_size.y;
+ }
+ }
changing_color = true;
- float x = CLAMP((float)bev->get_position().x, 0, uv_edit->get_size().width);
- float y = CLAMP((float)bev->get_position().y, 0, uv_edit->get_size().height);
- s = x / uv_edit->get_size().width;
- v = 1.0 - y / uv_edit->get_size().height;
color.set_hsv(h, s, v, color.a);
last_hsv = color;
set_pick_color(color);
@@ -469,11 +756,13 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event) {
if (!deferred_mode_enabled) {
emit_signal("color_changed", color);
}
- } else if (deferred_mode_enabled && !bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) {
+ } else if (deferred_mode_enabled && !bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) {
emit_signal("color_changed", color);
changing_color = false;
+ spinning = false;
} else {
changing_color = false;
+ spinning = false;
}
}
@@ -483,10 +772,30 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event) {
if (!changing_color) {
return;
}
- float x = CLAMP((float)mev->get_position().x, 0, uv_edit->get_size().width);
- float y = CLAMP((float)mev->get_position().y, 0, uv_edit->get_size().height);
- s = x / uv_edit->get_size().width;
- v = 1.0 - y / uv_edit->get_size().height;
+
+ Vector2 center = c->get_size() / 2.0;
+ if (picker_type == SHAPE_VHS_CIRCLE) {
+ real_t dist = center.distance_to(mev->get_position());
+ real_t rad = Math::atan2(mev->get_position().y - center.y, mev->get_position().x - center.x);
+ h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU;
+ s = CLAMP(dist / center.x, 0, 1);
+ } else {
+ if (spinning) {
+ real_t rad = Math::atan2(mev->get_position().y - center.y, mev->get_position().x - center.x);
+ h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU;
+ } else {
+ real_t corner_x = (c == wheel_uv) ? center.x - Math_SQRT12 * c->get_size().width * 0.42 : 0;
+ real_t corner_y = (c == wheel_uv) ? center.y - Math_SQRT12 * c->get_size().height * 0.42 : 0;
+ Size2 real_size(c->get_size().x - corner_x * 2, c->get_size().y - corner_y * 2);
+
+ real_t x = CLAMP(mev->get_position().x, corner_x, c->get_size().x - corner_x);
+ real_t y = CLAMP(mev->get_position().y, corner_x, c->get_size().y - corner_y);
+
+ s = (x - corner_x) / real_size.x;
+ v = 1.0 - (y - corner_y) / real_size.y;
+ }
+ }
+
color.set_hsv(h, s, v, color.a);
last_hsv = color;
set_pick_color(color);
@@ -501,10 +810,14 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> bev = p_event;
if (bev.is_valid()) {
- if (bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) {
+ if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) {
changing_color = true;
float y = CLAMP((float)bev->get_position().y, 0, w_edit->get_size().height);
- h = y / w_edit->get_size().height;
+ if (picker_type == SHAPE_VHS_CIRCLE) {
+ v = 1.0 - (y / w_edit->get_size().height);
+ } else {
+ h = y / w_edit->get_size().height;
+ }
} else {
changing_color = false;
}
@@ -514,7 +827,7 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
_update_color();
if (!deferred_mode_enabled) {
emit_signal("color_changed", color);
- } else if (!bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) {
+ } else if (!bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) {
emit_signal("color_changed", color);
}
}
@@ -526,7 +839,11 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
return;
}
float y = CLAMP((float)mev->get_position().y, 0, w_edit->get_size().height);
- h = y / w_edit->get_size().height;
+ if (picker_type == SHAPE_VHS_CIRCLE) {
+ v = 1.0 - (y / w_edit->get_size().height);
+ } else {
+ h = y / w_edit->get_size().height;
+ }
color.set_hsv(h, s, v, color.a);
last_hsv = color;
set_pick_color(color);
@@ -542,7 +859,7 @@ void ColorPicker::_preset_input(const Ref<InputEvent> &p_event) {
if (bev.is_valid()) {
int index = 0;
- if (bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) {
+ if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) {
for (int i = 0; i < presets.size(); i++) {
int x = (i % presets_per_row) * bt_add_preset->get_size().x;
int y = (Math::floor((float)i / presets_per_row)) * bt_add_preset->get_size().y;
@@ -553,7 +870,7 @@ void ColorPicker::_preset_input(const Ref<InputEvent> &p_event) {
set_pick_color(presets[index]);
_update_color();
emit_signal("color_changed", color);
- } else if (bev->is_pressed() && bev->get_button_index() == BUTTON_RIGHT && presets_enabled) {
+ } else if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_RIGHT && presets_enabled) {
index = bev->get_position().x / (preset->get_size().x / presets.size());
Color clicked_preset = presets[index];
erase_preset(clicked_preset);
@@ -577,8 +894,12 @@ void ColorPicker::_preset_input(const Ref<InputEvent> &p_event) {
}
void ColorPicker::_screen_input(const Ref<InputEvent> &p_event) {
+ if (!is_inside_tree()) {
+ return;
+ }
+
Ref<InputEventMouseButton> bev = p_event;
- if (bev.is_valid() && bev->get_button_index() == BUTTON_LEFT && !bev->is_pressed()) {
+ if (bev.is_valid() && bev->get_button_index() == MOUSE_BUTTON_LEFT && !bev->is_pressed()) {
emit_signal("color_changed", color);
screen->hide();
}
@@ -590,7 +911,7 @@ void ColorPicker::_screen_input(const Ref<InputEvent> &p_event) {
return;
}
- Ref<Image> img = r->get_texture()->get_data();
+ Ref<Image> img = r->get_texture()->get_image();
if (img.is_valid() && !img->is_empty()) {
Vector2 ofs = mev->get_global_position() - r->get_visible_rect().get_position();
Color c = img->get_pixel(ofs.x, r->get_visible_rect().size.height - ofs.y);
@@ -606,6 +927,10 @@ void ColorPicker::_add_preset_pressed() {
}
void ColorPicker::_screen_pick_pressed() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
Viewport *r = get_tree()->get_root();
if (!screen) {
screen = memnew(Control);
@@ -702,67 +1027,52 @@ void ColorPicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_preset", "color"), &ColorPicker::add_preset);
ClassDB::bind_method(D_METHOD("erase_preset", "color"), &ColorPicker::erase_preset);
ClassDB::bind_method(D_METHOD("get_presets"), &ColorPicker::get_presets);
+ ClassDB::bind_method(D_METHOD("set_picker_shape", "picker"), &ColorPicker::set_picker_shape);
+ ClassDB::bind_method(D_METHOD("get_picker_shape"), &ColorPicker::get_picker_shape);
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, "hsv_mode"), "set_hsv_mode", "is_hsv_mode");
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_PROPERTY(PropertyInfo(Variant::INT, "picker_shape", PROPERTY_HINT_ENUM, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle"), "set_picker_shape", "get_picker_shape");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "presets_enabled"), "set_presets_enabled", "are_presets_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "presets_visible"), "set_presets_visible", "are_presets_visible");
ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));
ADD_SIGNAL(MethodInfo("preset_added", PropertyInfo(Variant::COLOR, "color")));
ADD_SIGNAL(MethodInfo("preset_removed", PropertyInfo(Variant::COLOR, "color")));
+
+ BIND_ENUM_CONSTANT(SHAPE_HSV_RECTANGLE);
+ BIND_ENUM_CONSTANT(SHAPE_HSV_WHEEL);
+ BIND_ENUM_CONSTANT(SHAPE_VHS_CIRCLE);
}
ColorPicker::ColorPicker() :
BoxContainer(true) {
- updating = true;
- edit_alpha = true;
- text_is_constructor = false;
- hsv_mode_enabled = false;
- raw_mode_enabled = false;
- deferred_mode_enabled = false;
- changing_color = false;
- presets_enabled = true;
- presets_visible = true;
- screen = nullptr;
-
HBoxContainer *hb_edit = memnew(HBoxContainer);
add_child(hb_edit);
hb_edit->set_v_size_flags(SIZE_EXPAND_FILL);
- uv_edit = memnew(Control);
hb_edit->add_child(uv_edit);
- uv_edit->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input));
+ uv_edit->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input), make_binds(uv_edit));
uv_edit->set_mouse_filter(MOUSE_FILTER_PASS);
uv_edit->set_h_size_flags(SIZE_EXPAND_FILL);
uv_edit->set_v_size_flags(SIZE_EXPAND_FILL);
uv_edit->set_custom_minimum_size(Size2(get_theme_constant("sv_width"), get_theme_constant("sv_height")));
uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, uv_edit));
- w_edit = memnew(Control);
- hb_edit->add_child(w_edit);
- w_edit->set_custom_minimum_size(Size2(get_theme_constant("h_width"), 0));
- w_edit->set_h_size_flags(SIZE_FILL);
- w_edit->set_v_size_flags(SIZE_EXPAND_FILL);
- w_edit->connect("gui_input", callable_mp(this, &ColorPicker::_w_input));
- w_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(1, w_edit));
-
HBoxContainer *hb_smpl = memnew(HBoxContainer);
add_child(hb_smpl);
- sample = memnew(TextureRect);
hb_smpl->add_child(sample);
sample->set_h_size_flags(SIZE_EXPAND_FILL);
sample->connect("draw", callable_mp(this, &ColorPicker::_sample_draw));
- btn_pick = memnew(Button);
btn_pick->set_flat(true);
hb_smpl->add_child(btn_pick);
btn_pick->set_toggle_mode(true);
- btn_pick->set_tooltip(TTR("Pick a color from the editor window."));
+ btn_pick->set_tooltip(RTR("Pick a color from the editor window."));
btn_pick->connect("pressed", callable_mp(this, &ColorPicker::_screen_pick_pressed));
VBoxContainer *vbl = memnew(VBoxContainer);
@@ -798,25 +1108,24 @@ ColorPicker::ColorPicker() :
scroll[i]->set_h_size_flags(SIZE_EXPAND_FILL);
scroll[i]->connect("value_changed", callable_mp(this, &ColorPicker::_value_changed));
+ scroll[i]->connect("draw", callable_mp(this, &ColorPicker::_slider_draw), make_binds(i));
vbr->add_child(hbc);
}
+
labels[3]->set_text("A");
HBoxContainer *hhb = memnew(HBoxContainer);
vbr->add_child(hhb);
- btn_hsv = memnew(CheckButton);
hhb->add_child(btn_hsv);
- btn_hsv->set_text(TTR("HSV"));
+ btn_hsv->set_text(RTR("HSV"));
btn_hsv->connect("toggled", callable_mp(this, &ColorPicker::set_hsv_mode));
- btn_raw = memnew(CheckButton);
hhb->add_child(btn_raw);
- btn_raw->set_text(TTR("Raw"));
+ btn_raw->set_text(RTR("Raw"));
btn_raw->connect("toggled", callable_mp(this, &ColorPicker::set_raw_mode));
- text_type = memnew(Button);
hhb->add_child(text_type);
text_type->set_text("#");
text_type->set_tooltip(TTR("Switch between hexadecimal and code values."));
@@ -830,41 +1139,80 @@ ColorPicker::ColorPicker() :
text_type->set_mouse_filter(MOUSE_FILTER_IGNORE);
}
- c_text = memnew(LineEdit);
hhb->add_child(c_text);
c_text->set_h_size_flags(SIZE_EXPAND_FILL);
c_text->connect("text_entered", callable_mp(this, &ColorPicker::_html_entered));
c_text->connect("focus_entered", callable_mp(this, &ColorPicker::_focus_enter));
c_text->connect("focus_exited", callable_mp(this, &ColorPicker::_html_focus_exit));
+ wheel_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ wheel_edit->set_v_size_flags(SIZE_EXPAND_FILL);
+ wheel_edit->set_custom_minimum_size(Size2(get_theme_constant("sv_width"), get_theme_constant("sv_height")));
+ hb_edit->add_child(wheel_edit);
+
+ wheel_mat.instance();
+ circle_mat.instance();
+
+ Ref<Shader> wheel_shader(memnew(Shader));
+ wheel_shader->set_code("shader_type canvas_item;const float TAU=6.28318530718;void fragment(){float x=UV.x-0.5;float y=UV.y-0.5;float a=atan(y,x);x+=0.001;y+=0.001;float b=float(sqrt(x*x+y*y)<0.5)*float(sqrt(x*x+y*y)>0.42);x-=0.002;float b2=float(sqrt(x*x+y*y)<0.5)*float(sqrt(x*x+y*y)>0.42);y-=0.002;float b3=float(sqrt(x*x+y*y)<0.5)*float(sqrt(x*x+y*y)>0.42);x+=0.002;float b4=float(sqrt(x*x+y*y)<0.5)*float(sqrt(x*x+y*y)>0.42);COLOR=vec4(clamp((abs(fract(((a-TAU)/TAU)+vec3(3.0,2.0,1.0)/3.0)*6.0-3.0)-1.0),0.0,1.0),(b+b2+b3+b4)/4.00);}");
+ wheel_mat->set_shader(wheel_shader);
+
+ Ref<Shader> circle_shader(memnew(Shader));
+ circle_shader->set_code("shader_type canvas_item;const float TAU=6.28318530718;uniform float v=1.0;void fragment(){float x=UV.x-0.5;float y=UV.y-0.5;float a=atan(y,x);x+=0.001;y+=0.001;float b=float(sqrt(x*x+y*y)<0.5);x-=0.002;float b2=float(sqrt(x*x+y*y)<0.5);y-=0.002;float b3=float(sqrt(x*x+y*y)<0.5);x+=0.002;float b4=float(sqrt(x*x+y*y)<0.5);COLOR=vec4(mix(vec3(1.0),clamp(abs(fract(vec3((a-TAU)/TAU)+vec3(1.0,2.0/3.0,1.0/3.0))*6.0-vec3(3.0))-vec3(1.0),0.0,1.0),((float(sqrt(x*x+y*y))*2.0))/1.0)*vec3(v),(b+b2+b3+b4)/4.00);}");
+ circle_mat->set_shader(circle_shader);
+
+ MarginContainer *wheel_margin(memnew(MarginContainer));
+#ifdef TOOLS_ENABLED
+ wheel_margin->add_theme_constant_override("margin_bottom", 8 * EDSCALE);
+#else
+ wheel_margin->add_theme_constant_override("margin_bottom", 8);
+#endif
+ wheel_edit->add_child(wheel_margin);
+
+ wheel_margin->add_child(wheel);
+ wheel->set_mouse_filter(MOUSE_FILTER_PASS);
+ wheel->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(2, wheel));
+
+ wheel_margin->add_child(wheel_uv);
+ wheel_uv->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input), make_binds(wheel_uv));
+ wheel_uv->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, wheel_uv));
+
+ hb_edit->add_child(w_edit);
+ w_edit->set_custom_minimum_size(Size2(get_theme_constant("h_width"), 0));
+ w_edit->set_h_size_flags(SIZE_FILL);
+ w_edit->set_v_size_flags(SIZE_EXPAND_FILL);
+ w_edit->connect("gui_input", callable_mp(this, &ColorPicker::_w_input));
+ w_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(1, w_edit));
+
+ picker_type = SHAPE_HSV_RECTANGLE;
_update_controls();
updating = false;
set_pick_color(Color(1, 1, 1));
- preset_separator = memnew(HSeparator);
add_child(preset_separator);
- preset_container = memnew(HBoxContainer);
preset_container->set_h_size_flags(SIZE_EXPAND_FILL);
add_child(preset_container);
- preset = memnew(TextureRect);
preset_container->add_child(preset);
preset->connect("gui_input", callable_mp(this, &ColorPicker::_preset_input));
preset->connect("draw", callable_mp(this, &ColorPicker::_update_presets));
- preset_container2 = memnew(HBoxContainer);
preset_container2->set_h_size_flags(SIZE_EXPAND_FILL);
add_child(preset_container2);
- bt_add_preset = memnew(Button);
preset_container2->add_child(bt_add_preset);
- bt_add_preset->set_tooltip(TTR("Add current color as a preset."));
+ bt_add_preset->set_tooltip(RTR("Add current color as a preset."));
bt_add_preset->connect("pressed", callable_mp(this, &ColorPicker::_add_preset_pressed));
}
/////////////////
+void ColorPickerButton::_about_to_popup() {
+ set_pressed(true);
+ picker->set_old_color(color);
+}
+
void ColorPickerButton::_color_changed(const Color &p_color) {
color = p_color;
update();
@@ -977,10 +1325,11 @@ void ColorPickerButton::_update_picker() {
popup->add_child(picker);
add_child(popup);
picker->connect("color_changed", callable_mp(this, &ColorPickerButton::_color_changed));
- popup->connect("about_to_popup", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(true));
+ popup->connect("about_to_popup", callable_mp(this, &ColorPickerButton::_about_to_popup));
popup->connect("popup_hide", callable_mp(this, &ColorPickerButton::_modal_closed));
picker->set_pick_color(color);
picker->set_edit_alpha(edit_alpha);
+ picker->set_display_old_color(true);
emit_signal("picker_created");
}
}
@@ -992,6 +1341,7 @@ void ColorPickerButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_popup"), &ColorPickerButton::get_popup);
ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPickerButton::set_edit_alpha);
ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPickerButton::is_editing_alpha);
+ ClassDB::bind_method(D_METHOD("_about_to_popup"), &ColorPickerButton::_about_to_popup);
ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));
ADD_SIGNAL(MethodInfo("popup_closed"));
@@ -1001,12 +1351,5 @@ void ColorPickerButton::_bind_methods() {
}
ColorPickerButton::ColorPickerButton() {
- // Initialization is now done deferred,
- // this improves performance in the inspector as the color picker
- // can be expensive to initialize.
- picker = nullptr;
- popup = nullptr;
- edit_alpha = true;
-
set_toggle_mode(true);
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 3695820a79..13fe5fd60e 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -31,6 +31,7 @@
#ifndef COLOR_PICKER_H
#define COLOR_PICKER_H
+#include "scene/gui/aspect_ratio_container.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/check_button.h"
@@ -45,39 +46,59 @@
class ColorPicker : public BoxContainer {
GDCLASS(ColorPicker, BoxContainer);
+public:
+ enum PickerShapeType {
+ SHAPE_HSV_RECTANGLE,
+ SHAPE_HSV_WHEEL,
+ SHAPE_VHS_CIRCLE,
+
+ SHAPE_MAX
+ };
+
private:
- Control *screen;
- Control *uv_edit;
- Control *w_edit;
- TextureRect *sample;
- TextureRect *preset;
- HBoxContainer *preset_container;
- HBoxContainer *preset_container2;
- HSeparator *preset_separator;
- Button *bt_add_preset;
+ Control *screen = nullptr;
+ Control *uv_edit = memnew(Control);
+ Control *w_edit = memnew(Control);
+ AspectRatioContainer *wheel_edit = memnew(AspectRatioContainer);
+ Ref<ShaderMaterial> wheel_mat;
+ Ref<ShaderMaterial> circle_mat;
+ Control *wheel = memnew(Control);
+ Control *wheel_uv = memnew(Control);
+ TextureRect *sample = memnew(TextureRect);
+ TextureRect *preset = memnew(TextureRect);
+ HBoxContainer *preset_container = memnew(HBoxContainer);
+ HBoxContainer *preset_container2 = memnew(HBoxContainer);
+ HSeparator *preset_separator = memnew(HSeparator);
+ Button *bt_add_preset = memnew(Button);
List<Color> presets;
- Button *btn_pick;
- CheckButton *btn_hsv;
- CheckButton *btn_raw;
+ Button *btn_pick = memnew(Button);
+ CheckButton *btn_hsv = memnew(CheckButton);
+ CheckButton *btn_raw = memnew(CheckButton);
HSlider *scroll[4];
SpinBox *values[4];
Label *labels[4];
- Button *text_type;
- LineEdit *c_text;
- bool edit_alpha;
+ Button *text_type = memnew(Button);
+ LineEdit *c_text = memnew(LineEdit);
+ bool edit_alpha = true;
Size2i ms;
- bool text_is_constructor;
- int presets_per_row;
+ bool text_is_constructor = false;
+ int presets_per_row = 0;
+ PickerShapeType picker_type = SHAPE_HSV_WHEEL;
Color color;
- bool raw_mode_enabled;
- bool hsv_mode_enabled;
- bool deferred_mode_enabled;
- bool updating;
- bool changing_color;
- bool presets_enabled;
- bool presets_visible;
- float h, s, v;
+ Color old_color;
+ bool display_old_color = false;
+ bool raw_mode_enabled = false;
+ bool hsv_mode_enabled = false;
+ bool deferred_mode_enabled = false;
+ bool updating = true;
+ bool changing_color = false;
+ bool spinning = false;
+ bool presets_enabled = true;
+ bool presets_visible = true;
+ float h = 0.0;
+ float s = 0.0;
+ float v = 0.0;
Color last_hsv;
void _html_entered(const String &p_html);
@@ -89,8 +110,9 @@ private:
void _text_type_toggled();
void _sample_draw();
void _hsv_draw(int p_which, Control *c);
+ void _slider_draw(int p_which);
- void _uv_input(const Ref<InputEvent> &p_event);
+ void _uv_input(const Ref<InputEvent> &p_event, Control *c);
void _w_input(const Ref<InputEvent> &p_event);
void _preset_input(const Ref<InputEvent> &p_event);
void _screen_input(const Ref<InputEvent> &p_event);
@@ -111,6 +133,13 @@ public:
void _set_pick_color(const Color &p_color, bool p_update_sliders);
void set_pick_color(const Color &p_color);
Color get_pick_color() const;
+ void set_old_color(const Color &p_color);
+
+ void set_display_old_color(bool p_enabled);
+ bool is_displaying_old_color() const;
+
+ void set_picker_shape(PickerShapeType p_picker_type);
+ PickerShapeType get_picker_shape() const;
void add_preset(const Color &p_color);
void erase_preset(const Color &p_color);
@@ -139,11 +168,16 @@ public:
class ColorPickerButton : public Button {
GDCLASS(ColorPickerButton, Button);
- PopupPanel *popup;
- ColorPicker *picker;
+ // Initialization is now done deferred,
+ // this improves performance in the inspector as the color picker
+ // can be expensive to initialize.
+
+ PopupPanel *popup = nullptr;
+ ColorPicker *picker = nullptr;
Color color;
- bool edit_alpha;
+ bool edit_alpha = true;
+ void _about_to_popup();
void _color_changed(const Color &p_color);
void _modal_closed();
@@ -168,4 +202,5 @@ public:
ColorPickerButton();
};
+VARIANT_ENUM_CAST(ColorPicker::PickerShapeType);
#endif // COLOR_PICKER_H
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index 03bade6702..dea69aae6b 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -159,16 +159,14 @@ void Container::_notification(int p_what) {
}
}
-String Container::get_configuration_warning() const {
- String warning = Control::get_configuration_warning();
+TypedArray<String> Container::get_configuration_warnings() const {
+ TypedArray<String> warnings = Control::get_configuration_warnings();
if (get_class() == "Container" && get_script().is_null()) {
- if (!warning.is_empty()) {
- warning += "\n\n";
- }
- warning += TTR("Container by itself serves no purpose unless a script configures its children placement behavior.\nIf you don't intend to add a script, use a plain Control node instead.");
+ warnings.push_back(TTR("Container by itself serves no purpose unless a script configures its children placement behavior.\nIf you don't intend to add a script, use a plain Control node instead."));
}
- return warning;
+
+ return warnings;
}
void Container::_bind_methods() {
@@ -180,7 +178,6 @@ void Container::_bind_methods() {
}
Container::Container() {
- pending_sort = false;
// All containers should let mouse events pass by default.
set_mouse_filter(MOUSE_FILTER_PASS);
}
diff --git a/scene/gui/container.h b/scene/gui/container.h
index 5e60ca04dc..bce3085f0c 100644
--- a/scene/gui/container.h
+++ b/scene/gui/container.h
@@ -36,7 +36,7 @@
class Container : public Control {
GDCLASS(Container, Control);
- bool pending_sort;
+ bool pending_sort = false;
void _sort_children();
void _child_minsize_changed();
@@ -56,7 +56,7 @@ public:
void fit_child_in_rect(Control *p_child, const Rect2 &p_rect);
- virtual String get_configuration_warning() const override;
+ TypedArray<String> get_configuration_warnings() const override;
Container();
};
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index ad21c351d0..191f94b2b8 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -73,6 +73,9 @@ 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"));
Dictionary state = p_state;
set_rotation(state["rotation"]);
@@ -93,7 +96,8 @@ void Control::_edit_set_state(const Dictionary &p_state) {
void Control::_edit_set_position(const Point2 &p_position) {
#ifdef TOOLS_ENABLED
- set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
+ ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins.");
+ set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled() && Object::cast_to<Control>(data.parent));
#else
// Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED
set_position(p_position);
@@ -114,6 +118,7 @@ Size2 Control::_edit_get_scale() const {
void Control::_edit_set_rect(const Rect2 &p_edit_rect) {
#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins.");
set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
set_size(p_edit_rect.size.snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
#else
@@ -131,11 +136,11 @@ bool Control::_edit_use_rect() const {
return true;
}
-void Control::_edit_set_rotation(float p_rotation) {
+void Control::_edit_set_rotation(real_t p_rotation) {
set_rotation(p_rotation);
}
-float Control::_edit_get_rotation() const {
+real_t Control::_edit_get_rotation() const {
return get_rotation();
}
@@ -285,15 +290,11 @@ void Control::_update_minimum_size() {
}
Size2 minsize = get_combined_minimum_size();
- if (minsize.x > data.size_cache.x ||
- minsize.y > data.size_cache.y) {
- _size_changed();
- }
-
data.updating_last_minimum_size = false;
if (minsize != data.last_minimum_size) {
data.last_minimum_size = minsize;
+ _size_changed();
emit_signal(SceneStringNames::get_singleton()->minimum_size_changed);
}
}
@@ -322,7 +323,6 @@ bool Control::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = data.color_override.has(name) ? Variant(data.color_override[name]) : Variant();
} else if (sname.begins_with("custom_constants/")) {
String name = sname.get_slicec('/', 1);
-
r_ret = data.constant_override.has(name) ? Variant(data.constant_override[name]) : Variant();
} else {
return false;
@@ -439,14 +439,14 @@ bool Control::is_layout_rtl() const {
} else if (parent_window) {
return parent_window->is_layout_rtl();
} else {
- if (GLOBAL_GET("display/window/force_right_to_left_layout_direction")) {
+ if (GLOBAL_GET("internationalization/rendering/force_right_to_left_layout_direction")) {
return true;
}
String locale = TranslationServer::get_singleton()->get_tool_locale();
return TS->is_locale_right_to_left(locale);
}
} else if (data.layout_dir == LAYOUT_DIRECTION_LOCALE) {
- if (GLOBAL_GET("display/window/force_right_to_left_layout_direction")) {
+ if (GLOBAL_GET("internationalization/rendering/force_right_to_left_layout_direction")) {
return true;
}
String locale = TranslationServer::get_singleton()->get_tool_locale();
@@ -456,6 +456,10 @@ bool Control::is_layout_rtl() const {
}
}
+void Control::_clear_size_warning() {
+ data.size_warning = false;
+}
+
//moved theme configuration here, so controls can set up even if still not inside active scene
void Control::add_child_notify(Node *p_child) {
@@ -503,7 +507,9 @@ void Control::_notification(int p_notification) {
} break;
case NOTIFICATION_EXIT_TREE: {
get_viewport()->_gui_remove_control(this);
-
+ } break;
+ case NOTIFICATION_READY: {
+ connect("ready", callable_mp(this, &Control::_clear_size_warning), varray(), CONNECT_DEFERRED | CONNECT_ONESHOT);
} break;
case NOTIFICATION_ENTER_CANVAS: {
@@ -575,7 +581,7 @@ void Control::_notification(int p_notification) {
} break;
case NOTIFICATION_MOVED_IN_PARENT: {
- // some parents need to know the order of the childrens to draw (like TabContainer)
+ // some parents need to know the order of the children to draw (like TabContainer)
// update if necessary
if (data.parent) {
data.parent->update();
@@ -709,7 +715,7 @@ bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const
}
}
- return Variant();
+ return false;
}
void Control::drop_data(const Point2 &p_point, const Variant &p_data) {
@@ -1227,10 +1233,10 @@ Size2 Control::get_parent_area_size() const {
void Control::_size_changed() {
Rect2 parent_rect = get_parent_anchorable_rect();
- float edge_pos[4];
+ real_t edge_pos[4];
for (int i = 0; i < 4; i++) {
- float area = parent_rect.size[i & 1];
+ real_t area = parent_rect.size[i & 1];
edge_pos[i] = data.offset[i] + (data.anchor[i] * area);
}
@@ -1275,7 +1281,6 @@ void Control::_size_changed() {
}
if (pos_changed || size_changed) {
item_rect_changed(size_changed);
- _change_notify_offsets();
_notify_transform();
}
@@ -1285,13 +1290,13 @@ void Control::_size_changed() {
}
}
-void Control::set_anchor(Side p_side, float p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) {
+void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) {
ERR_FAIL_INDEX((int)p_side, 4);
Rect2 parent_rect = get_parent_anchorable_rect();
- float parent_range = (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) ? parent_rect.size.x : parent_rect.size.y;
- float previous_pos = data.offset[p_side] + data.anchor[p_side] * parent_range;
- float previous_opposite_pos = data.offset[(p_side + 2) % 4] + data.anchor[(p_side + 2) % 4] * parent_range;
+ real_t parent_range = (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) ? parent_rect.size.x : parent_rect.size.y;
+ real_t previous_pos = data.offset[p_side] + data.anchor[p_side] * parent_range;
+ real_t previous_opposite_pos = data.offset[(p_side + 2) % 4] + data.anchor[(p_side + 2) % 4] * parent_range;
data.anchor[p_side] = p_anchor;
@@ -1315,17 +1320,13 @@ void Control::set_anchor(Side p_side, float p_anchor, bool p_keep_offset, bool p
}
update();
- _change_notify("anchor_left");
- _change_notify("anchor_right");
- _change_notify("anchor_top");
- _change_notify("anchor_bottom");
}
-void Control::_set_anchor(Side p_side, float p_anchor) {
+void Control::_set_anchor(Side p_side, real_t p_anchor) {
set_anchor(p_side, p_anchor);
}
-void Control::set_anchor_and_offset(Side p_side, float p_anchor, float p_pos, bool p_push_opposite_anchor) {
+void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor) {
set_anchor(p_side, p_anchor, false, p_push_opposite_anchor);
set_offset(p_side, p_pos);
}
@@ -1462,7 +1463,7 @@ void Control::set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resiz
Rect2 parent_rect = get_parent_anchorable_rect();
- float x = parent_rect.size.x;
+ real_t x = parent_rect.size.x;
if (is_layout_rtl()) {
x = parent_rect.size.x - x - new_size.x;
}
@@ -1586,23 +1587,13 @@ void Control::set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPreset
set_offsets_preset(p_preset, p_resize_mode, p_margin);
}
-float Control::get_anchor(Side p_side) const {
+real_t Control::get_anchor(Side p_side) const {
ERR_FAIL_INDEX_V(int(p_side), 4, 0.0);
return data.anchor[p_side];
}
-void Control::_change_notify_offsets() {
- // this avoids sending the whole object data again on a change
- _change_notify("offset_left");
- _change_notify("offset_top");
- _change_notify("offset_right");
- _change_notify("offset_bottom");
- _change_notify("rect_position");
- _change_notify("rect_size");
-}
-
-void Control::set_offset(Side p_side, float p_value) {
+void Control::set_offset(Side p_side, real_t p_value) {
ERR_FAIL_INDEX((int)p_side, 4);
data.offset[p_side] = p_value;
@@ -1621,7 +1612,7 @@ void Control::set_end(const Size2 &p_point) {
_size_changed();
}
-float Control::get_offset(Side p_side) const {
+real_t Control::get_offset(Side p_side) const {
ERR_FAIL_INDEX_V((int)p_side, 4, 0);
return data.offset[p_side];
@@ -1664,12 +1655,12 @@ void Control::set_global_position(const Point2 &p_point, bool p_keep_offsets) {
set_position(inv.xform(p_point), p_keep_offsets);
}
-void Control::_compute_anchors(Rect2 p_rect, const float p_offsets[4], float (&r_anchors)[4]) {
+void Control::_compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]) {
Size2 parent_rect_size = get_parent_anchorable_rect().size;
ERR_FAIL_COND(parent_rect_size.x == 0.0);
ERR_FAIL_COND(parent_rect_size.y == 0.0);
- float x = p_rect.position.x;
+ real_t x = p_rect.position.x;
if (is_layout_rtl()) {
x = parent_rect_size.x - x - p_rect.size.x;
}
@@ -1679,10 +1670,10 @@ void Control::_compute_anchors(Rect2 p_rect, const float p_offsets[4], float (&r
r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_offsets[3]) / parent_rect_size.y;
}
-void Control::_compute_offsets(Rect2 p_rect, const float p_anchors[4], float (&r_offsets)[4]) {
+void Control::_compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]) {
Size2 parent_rect_size = get_parent_anchorable_rect().size;
- float x = p_rect.position.x;
+ real_t x = p_rect.position.x;
if (is_layout_rtl()) {
x = parent_rect_size.x - x - p_rect.size.x;
}
@@ -1699,10 +1690,6 @@ void Control::_set_position(const Size2 &p_point) {
void Control::set_position(const Size2 &p_point, bool p_keep_offsets) {
if (p_keep_offsets) {
_compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor);
- _change_notify("anchor_left");
- _change_notify("anchor_right");
- _change_notify("anchor_top");
- _change_notify("anchor_bottom");
} else {
_compute_offsets(Rect2(p_point, data.size_cache), data.anchor, data.offset);
}
@@ -1721,6 +1708,11 @@ void Control::set_rect(const Rect2 &p_rect) {
}
void Control::_set_size(const Size2 &p_size) {
+#ifdef DEBUG_ENABLED
+ if (data.size_warning) {
+ WARN_PRINT("Adjusting the size of Control nodes before they are fully initialized is unreliable. Consider deferring it with set_deferred().");
+ }
+#endif
set_size(p_size);
}
@@ -1736,10 +1728,6 @@ void Control::set_size(const Size2 &p_size, bool p_keep_offsets) {
if (p_keep_offsets) {
_compute_anchors(Rect2(data.pos_cache, new_size), data.offset, data.anchor);
- _change_notify("anchor_left");
- _change_notify("anchor_right");
- _change_notify("anchor_top");
- _change_notify("anchor_bottom");
} else {
_compute_offsets(Rect2(data.pos_cache, new_size), data.anchor, data.offset);
}
@@ -1787,53 +1775,38 @@ Rect2 Control::get_anchorable_rect() const {
}
void Control::add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon) {
+ ERR_FAIL_COND(!p_icon.is_valid());
+
if (data.icon_override.has(p_name)) {
data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
}
- // clear if "null" is passed instead of a icon
- if (p_icon.is_null()) {
- data.icon_override.erase(p_name);
- } else {
- data.icon_override[p_name] = p_icon;
- if (data.icon_override[p_name].is_valid()) {
- data.icon_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- }
- }
+ data.icon_override[p_name] = p_icon;
+ data.icon_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) {
+ ERR_FAIL_COND(!p_style.is_valid());
+
if (data.style_override.has(p_name)) {
data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
}
- // clear if "null" is passed instead of a style
- if (p_style.is_null()) {
- data.style_override.erase(p_name);
- } else {
- data.style_override[p_name] = p_style;
- if (data.style_override[p_name].is_valid()) {
- data.style_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- }
- }
+ data.style_override[p_name] = p_style;
+ data.style_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) {
+ ERR_FAIL_COND(!p_font.is_valid());
+
if (data.font_override.has(p_name)) {
data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
}
- // clear if "null" is passed instead of a font
- if (p_font.is_null()) {
- data.font_override.erase(p_name);
- } else {
- data.font_override[p_name] = p_font;
- if (data.font_override[p_name].is_valid()) {
- data.font_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- }
- }
+ data.font_override[p_name] = p_font;
+ data.font_override[p_name]->connect("changed", callable_mp(this, &Control::_override_changed), Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
notification(NOTIFICATION_THEME_CHANGED);
}
@@ -1852,6 +1825,48 @@ void Control::add_theme_constant_override(const StringName &p_name, int p_consta
notification(NOTIFICATION_THEME_CHANGED);
}
+void Control::remove_theme_icon_override(const StringName &p_name) {
+ if (data.icon_override.has(p_name)) {
+ data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+ }
+
+ data.icon_override.erase(p_name);
+ notification(NOTIFICATION_THEME_CHANGED);
+}
+
+void Control::remove_theme_style_override(const StringName &p_name) {
+ if (data.style_override.has(p_name)) {
+ data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+ }
+
+ data.style_override.erase(p_name);
+ notification(NOTIFICATION_THEME_CHANGED);
+}
+
+void Control::remove_theme_font_override(const StringName &p_name) {
+ if (data.font_override.has(p_name)) {
+ data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_override_changed));
+ }
+
+ data.font_override.erase(p_name);
+ notification(NOTIFICATION_THEME_CHANGED);
+}
+
+void Control::remove_theme_font_size_override(const StringName &p_name) {
+ data.font_size_override.erase(p_name);
+ notification(NOTIFICATION_THEME_CHANGED);
+}
+
+void Control::remove_theme_color_override(const StringName &p_name) {
+ data.color_override.erase(p_name);
+ notification(NOTIFICATION_THEME_CHANGED);
+}
+
+void Control::remove_theme_constant_override(const StringName &p_name) {
+ data.constant_override.erase(p_name);
+ notification(NOTIFICATION_THEME_CHANGED);
+}
+
void Control::set_focus_mode(FocusMode p_focus_mode) {
ERR_FAIL_INDEX((int)p_focus_mode, 3);
@@ -2168,7 +2183,7 @@ Ref<Theme> Control::get_theme() const {
void Control::set_tooltip(const String &p_tooltip) {
data.tooltip = p_tooltip;
- update_configuration_warning();
+ update_configuration_warnings();
}
String Control::get_tooltip(const Point2 &p_pos) const {
@@ -2264,7 +2279,7 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) {
return c;
}
- float dist = 1e7;
+ real_t dist = 1e7;
Control *result = nullptr;
Point2 points[4];
@@ -2285,10 +2300,10 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) {
Vector2 vdir = dir[p_side];
- float maxd = -1e7;
+ real_t maxd = -1e7;
for (int i = 0; i < 4; i++) {
- float d = vdir.dot(points[i]);
+ real_t d = vdir.dot(points[i]);
if (d > maxd) {
maxd = d;
}
@@ -2315,7 +2330,7 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) {
return result;
}
-void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, float p_min, float &r_closest_dist, Control **r_closest) {
+void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest) {
if (Object::cast_to<Viewport>(p_at)) {
return; //bye
}
@@ -2332,10 +2347,10 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons
points[2] = xform.xform(c->get_size());
points[3] = xform.xform(Point2(0, c->get_size().y));
- float min = 1e7;
+ real_t min = 1e7;
for (int i = 0; i < 4; i++) {
- float d = p_dir.dot(points[i]);
+ real_t d = p_dir.dot(points[i]);
if (d < min) {
min = d;
}
@@ -2351,8 +2366,8 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons
Vector2 fb = points[(j + 1) % 4];
Vector2 pa, pb;
- float d = Geometry2D::get_closest_points_between_segments(la, lb, fa, fb, pa, pb);
- //float d = Geometry2D::get_closest_distance_between_segments(Vector3(la.x,la.y,0),Vector3(lb.x,lb.y,0),Vector3(fa.x,fa.y,0),Vector3(fb.x,fb.y,0));
+ real_t d = Geometry2D::get_closest_points_between_segments(la, lb, fa, fb, pa, pb);
+ //real_t d = Geometry2D::get_closest_distance_between_segments(Vector3(la.x,la.y,0),Vector3(lb.x,lb.y,0),Vector3(fa.x,fa.y,0),Vector3(fb.x,fb.y,0));
if (d < r_closest_dist) {
r_closest_dist = d;
*r_closest = c;
@@ -2392,7 +2407,7 @@ void Control::set_v_size_flags(int p_flags) {
emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
}
-void Control::set_stretch_ratio(float p_ratio) {
+void Control::set_stretch_ratio(real_t p_ratio) {
if (data.expand == p_ratio) {
return;
}
@@ -2401,7 +2416,7 @@ void Control::set_stretch_ratio(float p_ratio) {
emit_signal(SceneStringNames::get_singleton()->size_flags_changed);
}
-float Control::get_stretch_ratio() const {
+real_t Control::get_stretch_ratio() const {
return data.expand;
}
@@ -2453,7 +2468,7 @@ int Control::get_v_size_flags() const {
void Control::set_mouse_filter(MouseFilter p_filter) {
ERR_FAIL_INDEX(p_filter, 3);
data.mouse_filter = p_filter;
- update_configuration_warning();
+ update_configuration_warnings();
}
Control::MouseFilter Control::get_mouse_filter() const {
@@ -2573,22 +2588,21 @@ Vector<Vector2i> Control::structured_text_parser(StructuredTextParser p_node_typ
return ret;
}
-void Control::set_rotation(float p_radians) {
+void Control::set_rotation(real_t p_radians) {
data.rotation = p_radians;
update();
_notify_transform();
- _change_notify("rect_rotation");
}
-float Control::get_rotation() const {
+real_t Control::get_rotation() const {
return data.rotation;
}
-void Control::set_rotation_degrees(float p_degrees) {
+void Control::set_rotation_degrees(real_t p_degrees) {
set_rotation(Math::deg2rad(p_degrees));
}
-float Control::get_rotation_degrees() const {
+real_t Control::get_rotation_degrees() const {
return Math::rad2deg(get_rotation());
}
@@ -2602,7 +2616,6 @@ void Control::set_pivot_offset(const Vector2 &p_pivot) {
data.pivot_offset = p_pivot;
update();
_notify_transform();
- _change_notify("rect_pivot_offset");
}
Vector2 Control::get_pivot_offset() const {
@@ -2675,15 +2688,15 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List
if (p_idx == 0) {
List<StringName> sn;
String pf = p_function;
- if (pf == "add_color_override" || pf == "has_color" || pf == "has_color_override" || pf == "get_color") {
+ if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color") {
Theme::get_default()->get_color_list(get_class(), &sn);
- } else if (pf == "add_style_override" || pf == "has_style" || pf == "has_style_override" || pf == "get_style") {
+ } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") {
Theme::get_default()->get_stylebox_list(get_class(), &sn);
- } else if (pf == "add_font_override" || pf == "has_font" || pf == "has_font_override" || pf == "get_font") {
+ } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font") {
Theme::get_default()->get_font_list(get_class(), &sn);
- } else if (pf == "add_font_size_override" || pf == "has_font_size" || pf == "has_font_size_override" || pf == "get_font_size") {
+ } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size") {
Theme::get_default()->get_font_size_list(get_class(), &sn);
- } else if (pf == "add_constant_override" || pf == "has_constant" || pf == "has_constant_override" || pf == "get_constant") {
+ } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") {
Theme::get_default()->get_constant_list(get_class(), &sn);
}
@@ -2694,17 +2707,14 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List
}
}
-String Control::get_configuration_warning() const {
- String warning = CanvasItem::get_configuration_warning();
+TypedArray<String> Control::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node::get_configuration_warnings();
if (data.mouse_filter == MOUSE_FILTER_IGNORE && data.tooltip != "") {
- if (!warning.is_empty()) {
- warning += "\n\n";
- }
- warning += TTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\".");
+ warnings.push_back(TTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."));
}
- return warning;
+ return warnings;
}
void Control::set_clip_contents(bool p_clip) {
@@ -2785,6 +2795,8 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_focus"), &Control::has_focus);
ClassDB::bind_method(D_METHOD("grab_focus"), &Control::grab_focus);
ClassDB::bind_method(D_METHOD("release_focus"), &Control::release_focus);
+ ClassDB::bind_method(D_METHOD("find_prev_valid_focus"), &Control::find_prev_valid_focus);
+ ClassDB::bind_method(D_METHOD("find_next_valid_focus"), &Control::find_next_valid_focus);
ClassDB::bind_method(D_METHOD("get_focus_owner"), &Control::get_focus_owner);
ClassDB::bind_method(D_METHOD("set_h_size_flags", "flags"), &Control::set_h_size_flags);
@@ -2806,6 +2818,13 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_theme_color_override", "name", "color"), &Control::add_theme_color_override);
ClassDB::bind_method(D_METHOD("add_theme_constant_override", "name", "constant"), &Control::add_theme_constant_override);
+ ClassDB::bind_method(D_METHOD("remove_theme_icon_override", "name"), &Control::remove_theme_icon_override);
+ ClassDB::bind_method(D_METHOD("remove_theme_stylebox_override", "name"), &Control::remove_theme_style_override);
+ ClassDB::bind_method(D_METHOD("remove_theme_font_override", "name"), &Control::remove_theme_font_override);
+ ClassDB::bind_method(D_METHOD("remove_theme_font_size_override", "name"), &Control::remove_theme_font_size_override);
+ ClassDB::bind_method(D_METHOD("remove_theme_color_override", "name"), &Control::remove_theme_color_override);
+ ClassDB::bind_method(D_METHOD("remove_theme_constant_override", "name"), &Control::remove_theme_constant_override);
+
ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "node_type"), &Control::get_theme_icon, DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "node_type"), &Control::get_theme_stylebox, DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_theme_font", "name", "node_type"), &Control::get_theme_font, DEFVAL(""));
diff --git a/scene/gui/control.h b/scene/gui/control.h
index ac2a1b35de..1f397df589 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -171,21 +171,22 @@ private:
Size2 last_minimum_size;
bool updating_last_minimum_size = false;
- float offset[4] = { 0.0, 0.0, 0.0, 0.0 };
- float anchor[4] = { ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN };
+ real_t offset[4] = { 0.0, 0.0, 0.0, 0.0 };
+ real_t anchor[4] = { ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN };
FocusMode focus_mode = FOCUS_NONE;
GrowDirection h_grow = GROW_DIRECTION_END;
GrowDirection v_grow = GROW_DIRECTION_END;
LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED;
- float rotation = 0;
+ real_t rotation = 0.0;
Vector2 scale = Vector2(1, 1);
Vector2 pivot_offset;
+ bool size_warning = true;
int h_size_flags = SIZE_FILL;
int v_size_flags = SIZE_FILL;
- float expand = 1;
+ real_t expand = 1.0;
Point2 custom_minimum_size;
MouseFilter mouse_filter = MOUSE_FILTER_STOP;
@@ -223,23 +224,23 @@ private:
// used internally
Control *_find_control_at_pos(CanvasItem *p_node, const Point2 &p_pos, const Transform2D &p_xform, Transform2D &r_inv_xform);
- void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, float p_min, float &r_closest_dist, Control **r_closest);
+ void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest);
Control *_get_focus_neighbor(Side p_side, int p_count = 0);
- void _set_anchor(Side p_side, float p_anchor);
+ void _set_anchor(Side p_side, real_t p_anchor);
void _set_position(const Point2 &p_point);
void _set_global_position(const Point2 &p_point);
void _set_size(const Size2 &p_size);
void _theme_changed();
- void _change_notify_offsets();
void _update_minimum_size();
+ void _clear_size_warning();
void _update_scroll();
- void _compute_offsets(Rect2 p_rect, const float p_anchors[4], float (&r_offsets)[4]);
- void _compute_anchors(Rect2 p_rect, const float p_offsets[4], float (&r_anchors)[4]);
+ void _compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]);
+ void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]);
void _size_changed();
String _get_tooltip() const;
@@ -324,8 +325,8 @@ public:
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_use_rect() const override;
- virtual void _edit_set_rotation(float p_rotation) override;
- virtual float _edit_get_rotation() const override;
+ virtual void _edit_set_rotation(real_t p_rotation) override;
+ virtual real_t _edit_get_rotation() const override;
virtual bool _edit_use_rotation() const override;
virtual void _edit_set_pivot(const Point2 &p_pivot) override;
@@ -363,13 +364,13 @@ public:
void set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
void set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
- void set_anchor(Side p_side, float p_anchor, bool p_keep_offset = true, bool p_push_opposite_anchor = true);
- float get_anchor(Side p_side) const;
+ void set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset = true, bool p_push_opposite_anchor = true);
+ real_t get_anchor(Side p_side) const;
- void set_offset(Side p_side, float p_value);
- float get_offset(Side p_side) const;
+ void set_offset(Side p_side, real_t p_value);
+ real_t get_offset(Side p_side) const;
- void set_anchor_and_offset(Side p_side, float p_anchor, float p_pos, bool p_push_opposite_anchor = true);
+ void set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, bool p_push_opposite_anchor = true);
void set_begin(const Point2 &p_point); // helper
void set_end(const Point2 &p_point); // helper
@@ -394,10 +395,10 @@ public:
void set_rect(const Rect2 &p_rect); // Reset anchors to begin and set rect, for faster container children sorting.
- void set_rotation(float p_radians);
- void set_rotation_degrees(float p_degrees);
- float get_rotation() const;
- float get_rotation_degrees() const;
+ void set_rotation(real_t p_radians);
+ void set_rotation_degrees(real_t p_degrees);
+ real_t get_rotation() const;
+ real_t get_rotation_degrees() const;
void set_h_grow_direction(GrowDirection p_direction);
GrowDirection get_h_grow_direction() const;
@@ -420,8 +421,8 @@ public:
void set_v_size_flags(int p_flags);
int get_v_size_flags() const;
- void set_stretch_ratio(float p_ratio);
- float get_stretch_ratio() const;
+ void set_stretch_ratio(real_t p_ratio);
+ real_t get_stretch_ratio() const;
void minimum_size_changed();
@@ -458,6 +459,13 @@ public:
void add_theme_color_override(const StringName &p_name, const Color &p_color);
void add_theme_constant_override(const StringName &p_name, int p_constant);
+ void remove_theme_icon_override(const StringName &p_name);
+ void remove_theme_style_override(const StringName &p_name);
+ void remove_theme_font_override(const StringName &p_name);
+ void remove_theme_font_size_override(const StringName &p_name);
+ void remove_theme_color_override(const StringName &p_name);
+ void remove_theme_constant_override(const StringName &p_name);
+
Ref<Texture2D> get_theme_icon(const StringName &p_name, const StringName &p_node_type = StringName()) const;
Ref<StyleBox> get_theme_stylebox(const StringName &p_name, const StringName &p_node_type = StringName()) const;
Ref<Font> get_theme_font(const StringName &p_name, const StringName &p_node_type = StringName()) const;
@@ -516,7 +524,7 @@ public:
bool is_visibility_clip_disabled() const;
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
- virtual String get_configuration_warning() const override;
+ TypedArray<String> get_configuration_warnings() const override;
Control() {}
};
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index 773c609a70..b6884bd37d 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -256,7 +256,7 @@ Button *AcceptDialog::add_button(const String &p_text, bool p_right, const Strin
Button *AcceptDialog::add_cancel_button(const String &p_cancel) {
String c = p_cancel;
if (p_cancel == "") {
- c = RTR("Cancel");
+ c = TTRC("Cancel");
}
Button *b = swap_cancel_ok ? add_button(c, true) : add_button(c);
b->connect("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed));
@@ -292,8 +292,6 @@ void AcceptDialog::set_swap_cancel_ok(bool p_swap) {
}
AcceptDialog::AcceptDialog() {
- parent_visible = nullptr;
-
set_wrap_controls(true);
set_visible(false);
set_transient(true);
@@ -319,14 +317,13 @@ AcceptDialog::AcceptDialog() {
hbc->add_spacer();
ok = memnew(Button);
- ok->set_text(RTR("OK"));
+ ok->set_text(TTRC("OK"));
hbc->add_child(ok);
hbc->add_spacer();
ok->connect("pressed", callable_mp(this, &AcceptDialog::_ok_pressed));
- hide_on_ok = true;
- set_title(RTR("Alert!"));
+ set_title(TTRC("Alert!"));
connect("window_input", callable_mp(this, &AcceptDialog::_input_from_window));
}
@@ -345,7 +342,7 @@ Button *ConfirmationDialog::get_cancel_button() {
}
ConfirmationDialog::ConfirmationDialog() {
- set_title(RTR("Please Confirm..."));
+ set_title(TTRC("Please Confirm..."));
#ifdef TOOLS_ENABLED
set_min_size(Size2(200, 70) * EDSCALE);
#endif
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index e450a3c30a..b072055d49 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -44,12 +44,12 @@ class LineEdit;
class AcceptDialog : public Window {
GDCLASS(AcceptDialog, Window);
- Window *parent_visible;
+ Window *parent_visible = nullptr;
Panel *bg;
HBoxContainer *hbc;
Label *label;
Button *ok;
- bool hide_on_ok;
+ bool hide_on_ok = true;
void _custom_action(const String &p_action);
void _update_child_rects();
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 5765d6b932..5409b44b9e 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -50,20 +50,28 @@ VBoxContainer *FileDialog::get_vbox() {
void FileDialog::_theme_changed() {
Color font_color = vbox->get_theme_color("font_color", "Button");
- Color font_color_hover = vbox->get_theme_color("font_color_hover", "Button");
- Color font_color_pressed = vbox->get_theme_color("font_color_pressed", "Button");
+ Color font_hover_color = vbox->get_theme_color("font_hover_color", "Button");
+ Color font_pressed_color = vbox->get_theme_color("font_pressed_color", "Button");
- dir_up->add_theme_color_override("icon_color_normal", font_color);
- dir_up->add_theme_color_override("icon_color_hover", font_color_hover);
- dir_up->add_theme_color_override("icon_color_pressed", font_color_pressed);
+ 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_pressed_color", font_pressed_color);
- refresh->add_theme_color_override("icon_color_normal", font_color);
- refresh->add_theme_color_override("icon_color_hover", font_color_hover);
- refresh->add_theme_color_override("icon_color_pressed", font_color_pressed);
+ 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_color_pressed", font_pressed_color);
- show_hidden->add_theme_color_override("icon_color_normal", font_color);
- show_hidden->add_theme_color_override("icon_color_hover", font_color_hover);
- show_hidden->add_theme_color_override("icon_color_pressed", font_color_pressed);
+ 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_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_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_pressed_color", font_pressed_color);
}
void FileDialog::_notification(int p_what) {
@@ -74,6 +82,13 @@ void FileDialog::_notification(int p_what) {
}
if (p_what == NOTIFICATION_ENTER_TREE) {
dir_up->set_icon(vbox->get_theme_icon("parent_folder", "FileDialog"));
+ if (vbox->is_layout_rtl()) {
+ dir_prev->set_icon(vbox->get_theme_icon("forward_folder", "FileDialog"));
+ dir_next->set_icon(vbox->get_theme_icon("back_folder", "FileDialog"));
+ } else {
+ dir_prev->set_icon(vbox->get_theme_icon("back_folder", "FileDialog"));
+ dir_next->set_icon(vbox->get_theme_icon("forward_folder", "FileDialog"));
+ }
refresh->set_icon(vbox->get_theme_icon("reload", "FileDialog"));
show_hidden->set_icon(vbox->get_theme_icon("toggle_hidden", "FileDialog"));
_theme_changed();
@@ -81,6 +96,8 @@ void FileDialog::_notification(int p_what) {
}
void FileDialog::_unhandled_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
Ref<InputEventKey> k = p_event;
if (k.is_valid() && has_focus()) {
if (k->is_pressed()) {
@@ -144,6 +161,7 @@ void FileDialog::_dir_entered(String p_dir) {
file->set_text("");
invalidate();
update_dir();
+ _push_history();
}
void FileDialog::_file_entered(const String &p_file) {
@@ -177,6 +195,21 @@ void FileDialog::_post_popup() {
} else {
file_box->set_visible(true);
}
+
+ local_history.clear();
+ local_history_pos = -1;
+ _push_history();
+}
+
+void FileDialog::_push_history() {
+ local_history.resize(local_history_pos + 1);
+ String new_path = dir_access->get_current_dir();
+ if (local_history.size() == 0 || new_path != local_history[local_history_pos]) {
+ local_history.push_back(new_path);
+ local_history_pos++;
+ dir_prev->set_disabled(local_history_pos == 0);
+ dir_next->set_disabled(true);
+ }
}
void FileDialog::_action_pressed() {
@@ -272,7 +305,7 @@ void FileDialog::_action_pressed() {
}
if (dir_access->file_exists(f)) {
- confirm_save->set_text(RTR("File exists, overwrite?"));
+ confirm_save->set_text(TTRC("File exists, overwrite?"));
confirm_save->popup_centered(Size2(200, 80));
} else {
emit_signal("file_selected", f);
@@ -316,6 +349,35 @@ void FileDialog::_go_up() {
dir_access->change_dir("..");
update_file_list();
update_dir();
+ _push_history();
+}
+
+void FileDialog::_go_back() {
+ if (local_history_pos <= 0) {
+ return;
+ }
+
+ local_history_pos--;
+ dir_access->change_dir(local_history[local_history_pos]);
+ update_file_list();
+ update_dir();
+
+ dir_prev->set_disabled(local_history_pos == 0);
+ dir_next->set_disabled(local_history_pos == local_history.size() - 1);
+}
+
+void FileDialog::_go_forward() {
+ if (local_history_pos == local_history.size() - 1) {
+ return;
+ }
+
+ local_history_pos++;
+ dir_access->change_dir(local_history[local_history_pos]);
+ update_file_list();
+ update_dir();
+
+ dir_prev->set_disabled(local_history_pos == 0);
+ dir_next->set_disabled(local_history_pos == local_history.size() - 1);
}
void FileDialog::deselect_all() {
@@ -329,10 +391,10 @@ void FileDialog::deselect_all() {
switch (mode) {
case FILE_MODE_OPEN_FILE:
case FILE_MODE_OPEN_FILES:
- get_ok_button()->set_text(RTR("Open"));
+ get_ok_button()->set_text(TTRC("Open"));
break;
case FILE_MODE_OPEN_DIR:
- get_ok_button()->set_text(RTR("Select Current Folder"));
+ get_ok_button()->set_text(TTRC("Select Current Folder"));
break;
case FILE_MODE_OPEN_ANY:
case FILE_MODE_SAVE_FILE:
@@ -356,7 +418,7 @@ void FileDialog::_tree_selected() {
if (!d["dir"]) {
file->set_text(d["name"]);
} else if (mode == FILE_MODE_OPEN_DIR) {
- get_ok_button()->set_text(RTR("Select This Folder"));
+ get_ok_button()->set_text(TTRC("Select This Folder"));
}
get_ok_button()->set_disabled(_is_open_should_be_disabled());
@@ -377,6 +439,7 @@ void FileDialog::_tree_item_activated() {
}
call_deferred("_update_file_list");
call_deferred("_update_dir");
+ _push_history();
} else {
_action_pressed();
}
@@ -415,6 +478,13 @@ void FileDialog::update_file_list() {
bool is_hidden;
String item;
+ if (dir_access->is_readable(dir_access->get_current_dir().utf8().get_data())) {
+ message->hide();
+ } else {
+ message->set_text(TTRC("You don't have permission to access contents of this folder."));
+ message->show();
+ }
+
while ((item = dir_access->get_next()) != "") {
if (item == "." || item == "..") {
continue;
@@ -549,7 +619,7 @@ void FileDialog::update_filters() {
all_filters += ", ...";
}
- filter->add_item(RTR("All Recognized") + " (" + all_filters + ")");
+ filter->add_item(String(TTRC("All Recognized")) + " (" + all_filters + ")");
}
for (int i = 0; i < filters.size(); i++) {
String flt = filters[i].get_slice(";", 0).strip_edges();
@@ -561,7 +631,7 @@ void FileDialog::update_filters() {
}
}
- filter->add_item(RTR("All Files (*)"));
+ filter->add_item(TTRC("All Files (*)"));
}
void FileDialog::clear_filters() {
@@ -602,6 +672,7 @@ void FileDialog::set_current_dir(const String &p_dir) {
dir_access->change_dir(p_dir);
update_dir();
invalidate();
+ _push_history();
}
void FileDialog::set_current_file(const String &p_file) {
@@ -646,37 +717,37 @@ void FileDialog::set_file_mode(FileMode p_mode) {
mode = p_mode;
switch (mode) {
case FILE_MODE_OPEN_FILE:
- get_ok_button()->set_text(RTR("Open"));
+ get_ok_button()->set_text(TTRC("Open"));
if (mode_overrides_title) {
- set_title(RTR("Open a File"));
+ set_title(TTRC("Open a File"));
}
makedir->hide();
break;
case FILE_MODE_OPEN_FILES:
- get_ok_button()->set_text(RTR("Open"));
+ get_ok_button()->set_text(TTRC("Open"));
if (mode_overrides_title) {
- set_title(RTR("Open File(s)"));
+ set_title(TTRC("Open File(s)"));
}
makedir->hide();
break;
case FILE_MODE_OPEN_DIR:
- get_ok_button()->set_text(RTR("Select Current Folder"));
+ get_ok_button()->set_text(TTRC("Select Current Folder"));
if (mode_overrides_title) {
- set_title(RTR("Open a Directory"));
+ set_title(TTRC("Open a Directory"));
}
makedir->show();
break;
case FILE_MODE_OPEN_ANY:
- get_ok_button()->set_text(RTR("Open"));
+ get_ok_button()->set_text(TTRC("Open"));
if (mode_overrides_title) {
- set_title(RTR("Open a File or Directory"));
+ set_title(TTRC("Open a File or Directory"));
}
makedir->show();
break;
case FILE_MODE_SAVE_FILE:
- get_ok_button()->set_text(RTR("Save"));
+ get_ok_button()->set_text(TTRC("Save"));
if (mode_overrides_title) {
- set_title(RTR("Save a File"));
+ set_title(TTRC("Save a File"));
}
makedir->show();
break;
@@ -731,12 +802,13 @@ FileDialog::Access FileDialog::get_access() const {
}
void FileDialog::_make_dir_confirm() {
- Error err = dir_access->make_dir(makedirname->get_text());
+ Error err = dir_access->make_dir(makedirname->get_text().strip_edges());
if (err == OK) {
- dir_access->change_dir(makedirname->get_text());
+ dir_access->change_dir(makedirname->get_text().strip_edges());
invalidate();
update_filters();
update_dir();
+ _push_history();
} else {
mkdirerr->popup_centered(Size2(250, 50));
}
@@ -754,6 +826,7 @@ void FileDialog::_select_drive(int p_idx) {
file->set_text("");
invalidate();
update_dir();
+ _push_history();
}
void FileDialog::_update_drives() {
@@ -852,24 +925,32 @@ void FileDialog::set_default_show_hidden_files(bool p_show) {
FileDialog::FileDialog() {
show_hidden_files = default_show_hidden_files;
- mode_overrides_title = true;
-
vbox = memnew(VBoxContainer);
add_child(vbox);
vbox->connect("theme_changed", callable_mp(this, &FileDialog::_theme_changed));
mode = FILE_MODE_SAVE_FILE;
- set_title(RTR("Save a File"));
+ set_title(TTRC("Save a File"));
HBoxContainer *hbc = memnew(HBoxContainer);
+ dir_prev = memnew(Button);
+ dir_prev->set_flat(true);
+ dir_prev->set_tooltip(TTRC("Go to previous folder."));
+ dir_next = memnew(Button);
+ dir_next->set_flat(true);
+ dir_next->set_tooltip(TTRC("Go to next folder."));
dir_up = memnew(Button);
dir_up->set_flat(true);
- dir_up->set_tooltip(RTR("Go to parent folder."));
+ dir_up->set_tooltip(TTRC("Go to parent folder."));
+ hbc->add_child(dir_prev);
+ hbc->add_child(dir_next);
hbc->add_child(dir_up);
+ dir_prev->connect("pressed", callable_mp(this, &FileDialog::_go_back));
+ dir_next->connect("pressed", callable_mp(this, &FileDialog::_go_forward));
dir_up->connect("pressed", callable_mp(this, &FileDialog::_go_up));
- hbc->add_child(memnew(Label(RTR("Path:"))));
+ hbc->add_child(memnew(Label(TTRC("Path:"))));
drives_container = memnew(HBoxContainer);
hbc->add_child(drives_container);
@@ -885,7 +966,7 @@ FileDialog::FileDialog() {
refresh = memnew(Button);
refresh->set_flat(true);
- refresh->set_tooltip(RTR("Refresh files."));
+ refresh->set_tooltip(TTRC("Refresh files."));
refresh->connect("pressed", callable_mp(this, &FileDialog::update_file_list));
hbc->add_child(refresh);
@@ -893,7 +974,7 @@ FileDialog::FileDialog() {
show_hidden->set_flat(true);
show_hidden->set_toggle_mode(true);
show_hidden->set_pressed(is_showing_hidden_files());
- show_hidden->set_tooltip(RTR("Toggle the visibility of hidden files."));
+ show_hidden->set_tooltip(TTRC("Toggle the visibility of hidden files."));
show_hidden->connect("toggled", callable_mp(this, &FileDialog::set_show_hidden_files));
hbc->add_child(show_hidden);
@@ -901,17 +982,24 @@ FileDialog::FileDialog() {
hbc->add_child(shortcuts_container);
makedir = memnew(Button);
- makedir->set_text(RTR("Create Folder"));
+ makedir->set_text(TTRC("Create Folder"));
makedir->connect("pressed", callable_mp(this, &FileDialog::_make_dir));
hbc->add_child(makedir);
vbox->add_child(hbc);
tree = memnew(Tree);
tree->set_hide_root(true);
- vbox->add_margin_child(RTR("Directories & Files:"), tree, true);
+ vbox->add_margin_child(TTRC("Directories & Files:"), tree, true);
+
+ message = memnew(Label);
+ message->hide();
+ message->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ message->set_align(Label::ALIGN_CENTER);
+ message->set_valign(Label::VALIGN_CENTER);
+ tree->add_child(message);
file_box = memnew(HBoxContainer);
- file_box->add_child(memnew(Label(RTR("File:"))));
+ file_box->add_child(memnew(Label(TTRC("File:"))));
file = memnew(LineEdit);
file->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE);
file->set_stretch_ratio(4);
@@ -925,7 +1013,6 @@ FileDialog::FileDialog() {
vbox->add_child(file_box);
dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- access = ACCESS_RESOURCES;
_update_drives();
connect("confirmed", callable_mp(this, &FileDialog::_action_pressed));
@@ -944,22 +1031,22 @@ FileDialog::FileDialog() {
confirm_save->connect("confirmed", callable_mp(this, &FileDialog::_save_confirm_pressed));
makedialog = memnew(ConfirmationDialog);
- makedialog->set_title(RTR("Create Folder"));
+ makedialog->set_title(TTRC("Create Folder"));
VBoxContainer *makevb = memnew(VBoxContainer);
makedialog->add_child(makevb);
makedirname = memnew(LineEdit);
makedirname->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE);
- makevb->add_margin_child(RTR("Name:"), makedirname);
+ makevb->add_margin_child(TTRC("Name:"), makedirname);
add_child(makedialog);
makedialog->register_text_enter(makedirname);
makedialog->connect("confirmed", callable_mp(this, &FileDialog::_make_dir_confirm));
mkdirerr = memnew(AcceptDialog);
- mkdirerr->set_text(RTR("Could not create folder."));
+ mkdirerr->set_text(TTRC("Could not create folder."));
add_child(mkdirerr);
exterr = memnew(AcceptDialog);
- exterr->set_text(RTR("Must use a valid extension."));
+ exterr->set_text(TTRC("Must use a valid extension."));
add_child(exterr);
update_filters();
@@ -967,7 +1054,6 @@ FileDialog::FileDialog() {
set_hide_on_ok(false);
- invalidated = true;
if (register_func) {
register_func(this);
}
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 626bb78d42..4996f00cb3 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -69,7 +69,7 @@ private:
LineEdit *makedirname;
Button *makedir;
- Access access;
+ Access access = ACCESS_RESOURCES;
//Button *action;
VBoxContainer *vbox;
FileMode mode;
@@ -86,6 +86,10 @@ private:
DirAccess *dir_access;
ConfirmationDialog *confirm_save;
+ Label *message;
+
+ Button *dir_prev;
+ Button *dir_next;
Button *dir_up;
Button *refresh;
@@ -93,12 +97,16 @@ private:
Vector<String> filters;
- bool mode_overrides_title;
+ Vector<String> local_history;
+ int local_history_pos = 0;
+ void _push_history();
+
+ bool mode_overrides_title = true;
static bool default_show_hidden_files;
- bool show_hidden_files;
+ bool show_hidden_files = false;
- bool invalidated;
+ bool invalidated = true;
void update_dir();
void update_file_name();
@@ -119,6 +127,8 @@ private:
void _make_dir();
void _make_dir_confirm();
void _go_up();
+ void _go_back();
+ void _go_forward();
void _update_drives();
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index d18fff887d..ebefb2938f 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -42,8 +42,6 @@
#endif
GradientEdit::GradientEdit() {
- grabbed = -1;
- grabbing = false;
set_focus_mode(FOCUS_ALL);
popup = memnew(PopupPanel);
@@ -94,6 +92,8 @@ GradientEdit::~GradientEdit() {
}
void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
Ref<InputEventKey> k = p_event;
if (k.is_valid() && k->is_pressed() && k->get_keycode() == KEY_DELETE && grabbed != -1) {
@@ -107,7 +107,7 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
//Show color picker on double click.
- if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_doubleclick() && mb->is_pressed()) {
+ 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();
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index 189b33f563..eb7367d598 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -44,8 +44,8 @@ class GradientEdit : public Control {
Ref<ImageTexture> checker;
- bool grabbing;
- int grabbed;
+ bool grabbing = false;
+ int grabbed = -1;
Vector<Gradient::Point> points;
void _draw_checker(int x, int y, int w, int h);
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index d5b12b6bb6..06c9cf1b63 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -154,10 +154,16 @@ Vector2 GraphEditMinimap::_convert_to_graph_position(const Vector2 &p_position)
}
void GraphEditMinimap::_gui_input(const Ref<InputEvent> &p_ev) {
+ ERR_FAIL_COND(p_ev.is_null());
+
+ if (!ge->is_minimap_enabled()) {
+ return;
+ }
+
Ref<InputEventMouseButton> mb = p_ev;
Ref<InputEventMouseMotion> mm = p_ev;
- if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) {
+ if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
if (mb->is_pressed()) {
is_pressing = true;
@@ -176,7 +182,12 @@ void GraphEditMinimap::_gui_input(const Ref<InputEvent> &p_ev) {
accept_event();
} else if (mm.is_valid() && is_pressing) {
if (is_resizing) {
- ge->set_minimap_size(ge->get_minimap_size() - mm->get_relative());
+ // Prevent setting minimap wider than GraphEdit
+ Vector2 new_minimap_size;
+ new_minimap_size.x = MIN(get_size().x - mm->get_relative().x, ge->get_size().x - 2.0 * minimap_padding.x);
+ new_minimap_size.y = MIN(get_size().y - mm->get_relative().y, ge->get_size().y - 2.0 * minimap_padding.y);
+ ge->set_minimap_size(new_minimap_size);
+
update();
} else {
Vector2 click_position = _convert_to_graph_position(mm->get_position() - minimap_padding) - graph_padding;
@@ -381,6 +392,15 @@ void GraphEdit::_graph_node_moved(Node *p_gn) {
connections_layer->update();
}
+void GraphEdit::_graph_node_slot_updated(int p_index, Node *p_gn) {
+ GraphNode *gn = Object::cast_to<GraphNode>(p_gn);
+ ERR_FAIL_COND(!gn);
+ top_layer->update();
+ minimap->update();
+ update();
+ connections_layer->update();
+}
+
void GraphEdit::add_child_notify(Node *p_child) {
Control::add_child_notify(p_child);
@@ -390,6 +410,7 @@ void GraphEdit::add_child_notify(Node *p_child) {
if (gn) {
gn->set_scale(Vector2(zoom, zoom));
gn->connect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved), varray(gn));
+ gn->connect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated), varray(gn));
gn->connect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised), varray(gn));
gn->connect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::update));
gn->connect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::update));
@@ -401,16 +422,30 @@ void GraphEdit::add_child_notify(Node *p_child) {
void GraphEdit::remove_child_notify(Node *p_child) {
Control::remove_child_notify(p_child);
- if (is_inside_tree()) {
+ if (p_child == top_layer) {
+ top_layer = nullptr;
+ minimap = nullptr;
+ } else if (p_child == connections_layer) {
+ connections_layer = nullptr;
+ }
+
+ if (top_layer != nullptr && is_inside_tree()) {
top_layer->call_deferred("raise"); // Top layer always on top!
}
GraphNode *gn = Object::cast_to<GraphNode>(p_child);
if (gn) {
gn->disconnect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved));
+ gn->disconnect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated));
gn->disconnect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised));
- gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::update));
- gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::update));
+
+ // In case of the whole GraphEdit being destroyed these references can already be freed.
+ if (connections_layer != nullptr && connections_layer->is_inside_tree()) {
+ gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::update));
+ }
+ if (minimap != nullptr && minimap->is_inside_tree()) {
+ gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::update));
+ }
}
}
@@ -502,14 +537,14 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
- if (is_in_hot_zone(pos, p_point)) {
+ if (is_in_hot_zone(pos / zoom, p_point / zoom)) {
return true;
}
}
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
- if (is_in_hot_zone(pos, p_point)) {
+ if (is_in_hot_zone(pos / zoom, p_point / zoom)) {
return true;
}
}
@@ -520,10 +555,10 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventMouseButton> mb = p_ev;
- if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
+ if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_pressed()) {
connecting_valid = false;
Ref<Texture2D> port = get_theme_icon("port", "GraphNode");
- click_pos = mb->get_position();
+ click_pos = mb->get_position() / zoom;
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
if (!gn) {
@@ -532,7 +567,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
- if (is_in_hot_zone(pos, click_pos)) {
+ if (is_in_hot_zone(pos / zoom, click_pos)) {
if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) {
//check disconnect
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
@@ -574,7 +609,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
- if (is_in_hot_zone(pos, click_pos)) {
+ if (is_in_hot_zone(pos / zoom, click_pos)) {
if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) {
//check disconnect
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
@@ -623,11 +658,11 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_target = false;
top_layer->update();
minimap->update();
- connecting_valid = just_disconnected || click_pos.distance_to(connecting_to) > 20.0 * zoom;
+ connecting_valid = just_disconnected || click_pos.distance_to(connecting_to / zoom) > 20.0 * zoom;
if (connecting_valid) {
Ref<Texture2D> port = get_theme_icon("port", "GraphNode");
- Vector2 mpos = mm->get_position();
+ Vector2 mpos = mm->get_position() / zoom;
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
if (!gn) {
@@ -638,7 +673,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
int type = gn->get_connection_output_type(j);
- if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos, mpos)) {
+ if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos)) {
connecting_target = true;
connecting_to = pos;
connecting_target_to = gn->get_name();
@@ -650,7 +685,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
int type = gn->get_connection_input_type(j);
- if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos, mpos)) {
+ if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos)) {
connecting_target = true;
connecting_to = pos;
connecting_target_to = gn->get_name();
@@ -663,7 +698,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
}
- if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && !mb->is_pressed()) {
+ if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT && !mb->is_pressed()) {
if (connecting_valid) {
if (connecting && connecting_target) {
String from = connecting_from;
@@ -732,6 +767,11 @@ bool GraphEdit::is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos) {
continue;
}
Rect2 rect = child->get_rect();
+
+ // To prevent intersections with other nodes.
+ rect.position *= zoom;
+ rect.size *= zoom;
+
if (rect.has_point(p_mouse_pos)) {
//check sub-controls
Vector2 subpos = p_mouse_pos - rect.position;
@@ -1028,8 +1068,10 @@ void GraphEdit::set_selected(Node *p_child) {
}
void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
+ ERR_FAIL_COND(p_ev.is_null());
+
Ref<InputEventMouseMotion> mm = p_ev;
- if (mm.is_valid() && (mm->get_button_mask() & BUTTON_MASK_MIDDLE || (mm->get_button_mask() & BUTTON_MASK_LEFT && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) {
+ if (mm.is_valid() && (mm->get_button_mask() & MOUSE_BUTTON_MASK_MIDDLE || (mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) {
h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x);
v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y);
}
@@ -1085,7 +1127,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
}
gn->set_selected(box_selection_mode_additive);
} else {
- bool select = (previus_selected.find(gn) != nullptr);
+ bool select = (previous_selected.find(gn) != nullptr);
if (gn->is_selected() && !select) {
emit_signal("node_deselected", gn);
} else if (!gn->is_selected() && select) {
@@ -1101,7 +1143,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventMouseButton> b = p_ev;
if (b.is_valid()) {
- if (b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) {
+ if (b->get_button_index() == MOUSE_BUTTON_RIGHT && b->is_pressed()) {
if (box_selecting) {
box_selecting = false;
for (int i = get_child_count() - 1; i >= 0; i--) {
@@ -1110,7 +1152,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
continue;
}
- bool select = (previus_selected.find(gn) != nullptr);
+ bool select = (previous_selected.find(gn) != nullptr);
if (gn->is_selected() && !select) {
emit_signal("node_deselected", gn);
} else if (!gn->is_selected() && select) {
@@ -1131,7 +1173,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
}
}
- if (b->get_button_index() == BUTTON_LEFT && !b->is_pressed() && dragging) {
+ if (b->get_button_index() == MOUSE_BUTTON_LEFT && !b->is_pressed() && dragging) {
if (!just_selected && drag_accum == Vector2() && Input::get_singleton()->is_key_pressed(KEY_CONTROL)) {
//deselect current node
for (int i = get_child_count() - 1; i >= 0; i--) {
@@ -1170,7 +1212,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
connections_layer->update();
}
- if (b->get_button_index() == BUTTON_LEFT && b->is_pressed()) {
+ if (b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed()) {
GraphNode *gn = nullptr;
for (int i = get_child_count() - 1; i >= 0; i--) {
@@ -1181,7 +1223,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
continue;
}
- if (gn_selected->has_point(b->get_position() - gn_selected->get_position())) {
+ if (gn_selected->has_point((b->get_position() - gn_selected->get_position()) / zoom)) {
gn = gn_selected;
break;
}
@@ -1235,29 +1277,29 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
box_selecting_from = b->get_position();
if (b->get_control()) {
box_selection_mode_additive = true;
- previus_selected.clear();
+ previous_selected.clear();
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i));
if (!gn2 || !gn2->is_selected()) {
continue;
}
- previus_selected.push_back(gn2);
+ previous_selected.push_back(gn2);
}
} else if (b->get_shift()) {
box_selection_mode_additive = false;
- previus_selected.clear();
+ previous_selected.clear();
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i));
if (!gn2 || !gn2->is_selected()) {
continue;
}
- previus_selected.push_back(gn2);
+ previous_selected.push_back(gn2);
}
} else {
box_selection_mode_additive = true;
- previus_selected.clear();
+ previous_selected.clear();
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i));
if (!gn2) {
@@ -1272,55 +1314,40 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
}
}
- if (b->get_button_index() == BUTTON_LEFT && !b->is_pressed() && box_selecting) {
+ if (b->get_button_index() == MOUSE_BUTTON_LEFT && !b->is_pressed() && box_selecting) {
box_selecting = false;
- previus_selected.clear();
+ box_selecting_rect = Rect2();
+ previous_selected.clear();
top_layer->update();
minimap->update();
}
- if (b->get_button_index() == BUTTON_WHEEL_UP && b->is_pressed()) {
- //too difficult to get right
- //set_zoom(zoom*ZOOM_SCALE);
- }
-
- if (b->get_button_index() == BUTTON_WHEEL_DOWN && b->is_pressed()) {
- //too difficult to get right
- //set_zoom(zoom/ZOOM_SCALE);
- }
- if (b->get_button_index() == BUTTON_WHEEL_UP && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ if (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP && Input::get_singleton()->is_key_pressed(KEY_CONTROL)) {
+ set_zoom_custom(zoom * ZOOM_SCALE, b->get_position());
+ } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && Input::get_singleton()->is_key_pressed(KEY_CONTROL)) {
+ set_zoom_custom(zoom / ZOOM_SCALE, b->get_position());
+ } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() * b->get_factor() / 8);
- }
- if (b->get_button_index() == BUTTON_WHEEL_DOWN && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * b->get_factor() / 8);
- }
- if (b->get_button_index() == BUTTON_WHEEL_RIGHT || (b->get_button_index() == BUTTON_WHEEL_DOWN && Input::get_singleton()->is_key_pressed(KEY_SHIFT))) {
+ } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT || (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && Input::get_singleton()->is_key_pressed(KEY_SHIFT))) {
h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * b->get_factor() / 8);
- }
- if (b->get_button_index() == BUTTON_WHEEL_LEFT || (b->get_button_index() == BUTTON_WHEEL_UP && Input::get_singleton()->is_key_pressed(KEY_SHIFT))) {
+ } else if (b->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT || (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP && Input::get_singleton()->is_key_pressed(KEY_SHIFT))) {
h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() * b->get_factor() / 8);
}
}
- Ref<InputEventKey> k = p_ev;
-
- if (k.is_valid()) {
- if (k->get_keycode() == KEY_D && k->is_pressed() && k->get_command()) {
+ if (p_ev->is_pressed()) {
+ if (p_ev->is_action("ui_graph_duplicate")) {
emit_signal("duplicate_nodes_request");
accept_event();
- }
-
- if (k->get_keycode() == KEY_C && k->is_pressed() && k->get_command()) {
+ } else if (p_ev->is_action("ui_copy")) {
emit_signal("copy_nodes_request");
accept_event();
- }
-
- if (k->get_keycode() == KEY_V && k->is_pressed() && k->get_command()) {
+ } else if (p_ev->is_action("ui_paste")) {
emit_signal("paste_nodes_request");
accept_event();
- }
-
- if (k->get_keycode() == KEY_DELETE && k->is_pressed()) {
+ } else if (p_ev->is_action("ui_graph_delete")) {
emit_signal("delete_nodes_request");
accept_event();
}
@@ -1533,7 +1560,12 @@ bool GraphEdit::is_minimap_enabled() const {
}
void GraphEdit::_minimap_toggled() {
- minimap->update();
+ if (is_minimap_enabled()) {
+ minimap->set_visible(true);
+ minimap->update();
+ } else {
+ minimap->set_visible(false);
+ }
}
void GraphEdit::set_connection_lines_thickness(float p_thickness) {
@@ -1576,7 +1608,7 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_valid_connection_type", "from_type", "to_type"), &GraphEdit::remove_valid_connection_type);
ClassDB::bind_method(D_METHOD("is_valid_connection_type", "from_type", "to_type"), &GraphEdit::is_valid_connection_type);
- ClassDB::bind_method(D_METHOD("set_zoom", "p_zoom"), &GraphEdit::set_zoom);
+ ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &GraphEdit::set_zoom);
ClassDB::bind_method(D_METHOD("get_zoom"), &GraphEdit::get_zoom);
ClassDB::bind_method(D_METHOD("set_snap", "pixels"), &GraphEdit::set_snap);
@@ -1591,9 +1623,9 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_connection_lines_antialiased", "pixels"), &GraphEdit::set_connection_lines_antialiased);
ClassDB::bind_method(D_METHOD("is_connection_lines_antialiased"), &GraphEdit::is_connection_lines_antialiased);
- ClassDB::bind_method(D_METHOD("set_minimap_size", "p_size"), &GraphEdit::set_minimap_size);
+ ClassDB::bind_method(D_METHOD("set_minimap_size", "size"), &GraphEdit::set_minimap_size);
ClassDB::bind_method(D_METHOD("get_minimap_size"), &GraphEdit::get_minimap_size);
- ClassDB::bind_method(D_METHOD("set_minimap_opacity", "p_opacity"), &GraphEdit::set_minimap_opacity);
+ ClassDB::bind_method(D_METHOD("set_minimap_opacity", "opacity"), &GraphEdit::set_minimap_opacity);
ClassDB::bind_method(D_METHOD("get_minimap_opacity"), &GraphEdit::get_minimap_opacity);
ClassDB::bind_method(D_METHOD("set_minimap_enabled", "enable"), &GraphEdit::set_minimap_enabled);
@@ -1640,8 +1672,6 @@ void GraphEdit::_bind_methods() {
GraphEdit::GraphEdit() {
set_focus_mode(FOCUS_ALL);
- awaiting_scroll_offset_update = false;
- top_layer = nullptr;
top_layer = memnew(GraphEditFilter(this));
add_child(top_layer);
top_layer->set_mouse_filter(MOUSE_FILTER_PASS);
@@ -1664,13 +1694,6 @@ GraphEdit::GraphEdit() {
v_scroll->set_name("_v_scroll");
top_layer->add_child(v_scroll);
- updating = false;
- connecting = false;
- right_disconnects = false;
-
- box_selecting = false;
- dragging = false;
-
//set large minmax so it can scroll even if not resized yet
h_scroll->set_min(-10000);
h_scroll->set_max(10000);
@@ -1681,8 +1704,6 @@ GraphEdit::GraphEdit() {
h_scroll->connect("value_changed", callable_mp(this, &GraphEdit::_scroll_moved));
v_scroll->connect("value_changed", callable_mp(this, &GraphEdit::_scroll_moved));
- zoom = 1;
-
zoom_hb = memnew(HBoxContainer);
top_layer->add_child(zoom_hb);
zoom_hb->set_position(Vector2(10, 10));
@@ -1741,7 +1762,7 @@ GraphEdit::GraphEdit() {
top_layer->add_child(minimap);
minimap->set_name("_minimap");
minimap->set_modulate(Color(1, 1, 1, minimap_opacity));
- minimap->set_mouse_filter(MOUSE_FILTER_STOP);
+ minimap->set_mouse_filter(MOUSE_FILTER_PASS);
minimap->set_custom_minimum_size(Vector2(50, 50));
minimap->set_size(minimap_size);
minimap->set_anchors_preset(Control::PRESET_BOTTOM_RIGHT);
@@ -1751,7 +1772,5 @@ GraphEdit::GraphEdit() {
minimap->set_offset(Side::SIDE_BOTTOM, -MINIMAP_OFFSET);
minimap->connect("draw", callable_mp(this, &GraphEdit::_minimap_draw));
- setting_scroll_ofs = false;
- just_disconnected = false;
set_clip_contents(true);
}
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 4525152bd5..fa3b113705 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -99,9 +99,9 @@ public:
struct Connection {
StringName from;
StringName to;
- int from_port;
- int to_port;
- float activity;
+ int from_port = 0;
+ int to_port = 0;
+ float activity = 0.0;
};
private:
@@ -121,41 +121,41 @@ private:
HScrollBar *h_scroll;
VScrollBar *v_scroll;
- float port_grab_distance_horizontal;
+ float port_grab_distance_horizontal = 0.0;
float port_grab_distance_vertical;
- bool connecting;
+ bool connecting = false;
String connecting_from;
- bool connecting_out;
- int connecting_index;
- int connecting_type;
+ bool connecting_out = false;
+ int connecting_index = 0;
+ int connecting_type = 0;
Color connecting_color;
- bool connecting_target;
+ bool connecting_target = false;
Vector2 connecting_to;
String connecting_target_to;
int connecting_target_index;
- bool just_disconnected;
- bool connecting_valid;
+ bool just_disconnected = false;
+ bool connecting_valid = false;
Vector2 click_pos;
- bool dragging;
- bool just_selected;
- bool moving_selection;
+ bool dragging = false;
+ bool just_selected = false;
+ bool moving_selection = false;
Vector2 drag_accum;
- float zoom;
+ float zoom = 1.0;
- bool box_selecting;
- bool box_selection_mode_additive;
+ bool box_selecting = false;
+ bool box_selection_mode_additive = false;
Point2 box_selecting_from;
Point2 box_selecting_to;
Rect2 box_selecting_rect;
- List<GraphNode *> previus_selected;
+ List<GraphNode *> previous_selected;
- bool setting_scroll_ofs;
- bool right_disconnects;
- bool updating;
- bool awaiting_scroll_offset_update;
+ bool setting_scroll_ofs = false;
+ bool right_disconnects = false;
+ bool updating = false;
+ bool awaiting_scroll_offset_update = false;
List<Connection> connections;
float lines_thickness = 2.0f;
@@ -167,6 +167,7 @@ private:
void _graph_node_raised(Node *p_gn);
void _graph_node_moved(Node *p_gn);
+ void _graph_node_slot_updated(int p_index, Node *p_gn);
void _update_scroll();
void _scroll_moved(double);
@@ -194,7 +195,7 @@ private:
uint32_t type_a;
uint32_t type_b;
};
- uint64_t key;
+ uint64_t key = 0;
};
bool operator<(const ConnType &p_type) const {
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index 9f5c87377f..7d5c53effe 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -32,6 +32,12 @@
#include "core/string/translation.h"
+struct _MinSizeCache {
+ int min_size;
+ bool will_stretch;
+ int final_size;
+};
+
bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
String str = p_name;
if (str.begins_with("opentype_features/")) {
@@ -51,7 +57,7 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
update();
}
}
- _change_notify();
+ notify_property_list_changed();
return true;
}
@@ -71,6 +77,8 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
si.enable_left = p_value;
} else if (what == "left_type") {
si.type_left = p_value;
+ } else if (what == "left_icon") {
+ si.custom_slot_left = p_value;
} else if (what == "left_color") {
si.color_left = p_value;
} else if (what == "right_enabled") {
@@ -79,11 +87,13 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
si.type_right = p_value;
} else if (what == "right_color") {
si.color_right = p_value;
+ } else if (what == "right_icon") {
+ si.custom_slot_right = p_value;
} else {
return false;
}
- set_slot(idx, si.enable_left, si.type_left, si.color_left, si.enable_right, si.type_right, si.color_right);
+ set_slot(idx, si.enable_left, si.type_left, si.color_left, si.enable_right, si.type_right, si.color_right, si.custom_slot_left, si.custom_slot_right);
update();
return true;
}
@@ -120,12 +130,16 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = si.type_left;
} else if (what == "left_color") {
r_ret = si.color_left;
+ } else if (what == "left_icon") {
+ r_ret = si.custom_slot_left;
} else if (what == "right_enabled") {
r_ret = si.enable_right;
} else if (what == "right_type") {
r_ret = si.type_right;
} else if (what == "right_color") {
r_ret = si.color_right;
+ } else if (what == "right_icon") {
+ r_ret = si.custom_slot_right;
} else {
return false;
}
@@ -152,24 +166,34 @@ void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::BOOL, base + "left_enabled"));
p_list->push_back(PropertyInfo(Variant::INT, base + "left_type"));
p_list->push_back(PropertyInfo(Variant::COLOR, base + "left_color"));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, base + "left_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
p_list->push_back(PropertyInfo(Variant::BOOL, base + "right_enabled"));
p_list->push_back(PropertyInfo(Variant::INT, base + "right_type"));
p_list->push_back(PropertyInfo(Variant::COLOR, base + "right_color"));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, base + "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
idx++;
}
}
void GraphNode::_resort() {
- int sep = get_theme_constant("separation");
+ /** First pass, determine minimum size AND amount of stretchable elements */
+
+ Size2i new_size = get_size();
Ref<StyleBox> sb = get_theme_stylebox("frame");
- bool first = true;
- Size2 minsize;
+ int sep = get_theme_constant("separation");
+
+ bool first = true;
+ int children_count = 0;
+ int stretch_min = 0;
+ int stretch_avail = 0;
+ float stretch_ratio_total = 0;
+ Map<Control *, _MinSizeCache> min_size_cache;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
- if (!c) {
+ if (!c || !c->is_visible_in_tree()) {
continue;
}
if (c->is_set_as_top_level()) {
@@ -177,38 +201,120 @@ void GraphNode::_resort() {
}
Size2i size = c->get_combined_minimum_size();
+ _MinSizeCache msc;
- minsize.y += size.y;
- minsize.x = MAX(minsize.x, size.x);
+ stretch_min += size.height;
+ msc.min_size = size.height;
+ msc.will_stretch = c->get_v_size_flags() & SIZE_EXPAND;
- if (first) {
- first = false;
- } else {
- minsize.y += sep;
+ if (msc.will_stretch) {
+ stretch_avail += msc.min_size;
+ stretch_ratio_total += c->get_stretch_ratio();
}
+ msc.final_size = msc.min_size;
+ min_size_cache[c] = msc;
+ children_count++;
}
- int vofs = 0;
- int w = get_size().x - sb->get_minimum_size().x;
+ if (children_count == 0) {
+ return;
+ }
+
+ int stretch_max = new_size.height - (children_count - 1) * sep;
+ int stretch_diff = stretch_max - stretch_min;
+ if (stretch_diff < 0) {
+ //avoid negative stretch space
+ stretch_diff = 0;
+ }
+
+ stretch_avail += stretch_diff - sb->get_margin(SIDE_BOTTOM) - sb->get_margin(SIDE_TOP); //available stretch space.
+ /** Second, pass sucessively to discard elements that can't be stretched, this will run while stretchable
+ elements exist */
+
+ while (stretch_ratio_total > 0) { // first of all, don't even be here if no stretchable objects exist
+ bool refit_successful = true; //assume refit-test will go well
+
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c || !c->is_visible_in_tree()) {
+ continue;
+ }
+ if (c->is_set_as_top_level()) {
+ continue;
+ }
+
+ ERR_FAIL_COND(!min_size_cache.has(c));
+ _MinSizeCache &msc = min_size_cache[c];
+
+ if (msc.will_stretch) { //wants to stretch
+ //let's see if it can really stretch
+
+ int final_pixel_size = stretch_avail * c->get_stretch_ratio() / stretch_ratio_total;
+ if (final_pixel_size < msc.min_size) {
+ //if available stretching area is too small for widget,
+ //then remove it from stretching area
+ msc.will_stretch = false;
+ stretch_ratio_total -= c->get_stretch_ratio();
+ refit_successful = false;
+ stretch_avail -= msc.min_size;
+ msc.final_size = msc.min_size;
+ break;
+ } else {
+ msc.final_size = final_pixel_size;
+ }
+ }
+ }
+
+ if (refit_successful) { //uf refit went well, break
+ break;
+ }
+ }
+
+ /** Final pass, draw and stretch elements **/
+ int ofs = sb->get_margin(SIDE_TOP);
+
+ first = true;
+ int idx = 0;
cache_y.clear();
+ int w = new_size.width - sb->get_minimum_size().x;
+
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
- if (!c) {
+ if (!c || !c->is_visible_in_tree()) {
continue;
}
if (c->is_set_as_top_level()) {
continue;
}
- Size2i size = c->get_combined_minimum_size();
+ _MinSizeCache &msc = min_size_cache[c];
+
+ if (first) {
+ first = false;
+ } else {
+ ofs += sep;
+ }
- Rect2 r(sb->get_margin(SIDE_LEFT), sb->get_margin(SIDE_TOP) + vofs, w, size.y);
+ int from = ofs;
+ int to = ofs + msc.final_size;
+
+ if (msc.will_stretch && idx == children_count - 1) {
+ //adjust so the last one always fits perfect
+ //compensating for numerical imprecision
+
+ to = new_size.height - sb->get_margin(SIDE_BOTTOM);
+ }
- fit_child_in_rect(c, r);
- cache_y.push_back(vofs + size.y * 0.5);
+ int size = to - from;
- vofs += size.y + sep;
+ Rect2 rect(sb->get_margin(SIDE_LEFT), from, w, size);
+
+ fit_child_in_rect(c, rect);
+ cache_y.push_back(from - sb->get_margin(SIDE_TOP) + size * 0.5);
+
+ ofs = to;
+ idx++;
}
update();
@@ -355,7 +461,9 @@ void GraphNode::_shape() {
void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left, const Ref<Texture2D> &p_custom_right) {
ERR_FAIL_COND(p_idx < 0);
- if (!p_enable_left && p_type_left == 0 && p_color_left == Color(1, 1, 1, 1) && !p_enable_right && p_type_right == 0 && p_color_right == Color(1, 1, 1, 1)) {
+ if (!p_enable_left && p_type_left == 0 && p_color_left == Color(1, 1, 1, 1) &&
+ !p_enable_right && p_type_right == 0 && p_color_right == Color(1, 1, 1, 1) &&
+ !p_custom_left.is_valid() && !p_custom_right.is_valid()) {
slot_info.erase(p_idx);
return;
}
@@ -372,6 +480,8 @@ void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const C
slot_info[p_idx] = s;
update();
connpos_dirty = true;
+
+ emit_signal("slot_updated", p_idx);
}
void GraphNode::clear_slot(int p_idx) {
@@ -472,7 +582,6 @@ void GraphNode::set_title(const String &p_title) {
_shape();
update();
- _change_notify("title");
minimum_size_changed();
}
@@ -695,11 +804,13 @@ Color GraphNode::get_connection_output_color(int p_idx) {
}
void GraphNode::_gui_input(const Ref<InputEvent> &p_ev) {
+ ERR_FAIL_COND(p_ev.is_null());
+
Ref<InputEventMouseButton> mb = p_ev;
if (mb.is_valid()) {
ERR_FAIL_COND_MSG(get_parent_control() == nullptr, "GraphNode must be the child of a GraphEdit node.");
- if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
Vector2 mpos = Vector2(mb->get_position().x, mb->get_position().y);
if (close_rect.size != Size2() && close_rect.has_point(mpos)) {
//send focus to parent
@@ -722,7 +833,7 @@ void GraphNode::_gui_input(const Ref<InputEvent> &p_ev) {
emit_signal("raise_request");
}
- if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ if (!mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
resizing = false;
}
}
@@ -826,6 +937,7 @@ void GraphNode::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "overlay", PROPERTY_HINT_ENUM, "Disabled,Breakpoint,Position"), "set_overlay", "get_overlay");
ADD_SIGNAL(MethodInfo("position_offset_changed"));
+ ADD_SIGNAL(MethodInfo("slot_updated", PropertyInfo(Variant::INT, "idx")));
ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::VECTOR2, "from"), PropertyInfo(Variant::VECTOR2, "to")));
ADD_SIGNAL(MethodInfo("raise_request"));
ADD_SIGNAL(MethodInfo("close_request"));
@@ -838,12 +950,5 @@ void GraphNode::_bind_methods() {
GraphNode::GraphNode() {
title_buf.instance();
- overlay = OVERLAY_DISABLED;
- show_close = false;
- connpos_dirty = true;
set_mouse_filter(MOUSE_FILTER_STOP);
- comment = false;
- resizable = false;
- resizing = false;
- selected = false;
}
diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h
index cf66586e6b..1bc54dddb7 100644
--- a/scene/gui/graph_node.h
+++ b/scene/gui/graph_node.h
@@ -46,23 +46,14 @@ public:
private:
struct Slot {
- bool enable_left;
- int type_left;
- Color color_left;
- bool enable_right;
- int type_right;
- Color color_right;
+ bool enable_left = false;
+ int type_left = 0;
+ Color color_left = Color(1, 1, 1, 1);
+ bool enable_right = false;
+ int type_right = 0;
+ Color color_right = Color(1, 1, 1, 1);
Ref<Texture2D> custom_slot_left;
Ref<Texture2D> custom_slot_right;
-
- Slot() {
- enable_left = false;
- type_left = 0;
- color_left = Color(1, 1, 1, 1);
- enable_right = false;
- type_right = 0;
- color_right = Color(1, 1, 1, 1);
- }
};
String title;
@@ -72,12 +63,12 @@ private:
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
- bool show_close;
+ bool show_close = false;
Vector2 position_offset;
- bool comment;
- bool resizable;
+ bool comment = false;
+ bool resizable = false;
- bool resizing;
+ bool resizing = false;
Vector2 resizing_from;
Vector2 resizing_from_size;
@@ -87,7 +78,7 @@ private:
struct ConnCache {
Vector2 pos;
- int type;
+ int type = 0;
Color color;
};
@@ -96,16 +87,16 @@ private:
Map<int, Slot> slot_info;
- bool connpos_dirty;
+ bool connpos_dirty = true;
void _connpos_update();
void _resort();
void _shape();
Vector2 drag_from;
- bool selected;
+ bool selected = false;
- Overlay overlay;
+ Overlay overlay = OVERLAY_DISABLED;
protected:
void _gui_input(const Ref<InputEvent> &p_ev);
diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp
index 11335db54a..541925a802 100644
--- a/scene/gui/grid_container.cpp
+++ b/scene/gui/grid_container.cpp
@@ -261,6 +261,4 @@ Size2 GridContainer::get_minimum_size() const {
return ms;
}
-GridContainer::GridContainer() {
- columns = 1;
-}
+GridContainer::GridContainer() {}
diff --git a/scene/gui/grid_container.h b/scene/gui/grid_container.h
index 8045c94b9a..9b43a5bc7e 100644
--- a/scene/gui/grid_container.h
+++ b/scene/gui/grid_container.h
@@ -36,7 +36,7 @@
class GridContainer : public Container {
GDCLASS(GridContainer, Container);
- int columns;
+ int columns = 1;
protected:
void _notification(int p_what);
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 69ca96b28e..4fddb4b661 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -50,7 +50,7 @@ void ItemList::_shape(int p_idx) {
}
}
-void ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, bool p_selectable) {
+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;
@@ -64,14 +64,16 @@ void ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, b
item.tooltip_enabled = true;
item.custom_bg = Color(0, 0, 0, 0);
items.push_back(item);
+ int item_id = items.size() - 1;
_shape(items.size() - 1);
update();
shape_changed = true;
+ return item_id;
}
-void ItemList::add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable) {
+int ItemList::add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable) {
Item item;
item.icon = p_item;
item.icon_transposed = false;
@@ -85,9 +87,11 @@ void ItemList::add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable) {
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;
+ return item_id;
}
void ItemList::set_item_text(int p_idx, const String &p_text) {
@@ -526,6 +530,8 @@ Size2 ItemList::Item::get_icon_size() const {
}
void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
double prev_scroll = scroll_bar->get_value();
Ref<InputEventMouseMotion> mm = p_event;
@@ -536,7 +542,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
- if (defer_select_single >= 0 && mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && !mb->is_pressed()) {
+ if (defer_select_single >= 0 && mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT && !mb->is_pressed()) {
select(defer_select_single, true);
emit_signal("multi_selected", defer_select_single, true);
@@ -544,7 +550,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (mb.is_valid() && (mb->get_button_index() == BUTTON_LEFT || (allow_rmb_select && mb->get_button_index() == BUTTON_RIGHT)) && mb->is_pressed()) {
+ if (mb.is_valid() && (mb->get_button_index() == MOUSE_BUTTON_LEFT || (allow_rmb_select && mb->get_button_index() == MOUSE_BUTTON_RIGHT)) && mb->is_pressed()) {
search_string = ""; //any mousepress cancels
Vector2 pos = mb->get_position();
Ref<StyleBox> bg = get_theme_stylebox("bg");
@@ -590,16 +596,16 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (mb->get_button_index() == BUTTON_RIGHT) {
+ if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
emit_signal("item_rmb_selected", i, get_local_mouse_position());
}
} else {
- if (!mb->is_doubleclick() && !mb->get_command() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == BUTTON_LEFT) {
+ if (!mb->is_double_click() && !mb->get_command() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
defer_select_single = i;
return;
}
- if (items[i].selected && mb->get_button_index() == BUTTON_RIGHT) {
+ if (items[i].selected && mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
emit_signal("item_rmb_selected", i, get_local_mouse_position());
} else {
bool selected = items[i].selected;
@@ -614,9 +620,9 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (mb->get_button_index() == BUTTON_RIGHT) {
+ if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
emit_signal("item_rmb_selected", i, get_local_mouse_position());
- } else if (/*select_mode==SELECT_SINGLE &&*/ mb->is_doubleclick()) {
+ } else if (/*select_mode==SELECT_SINGLE &&*/ mb->is_double_click()) {
emit_signal("item_activated", i);
}
}
@@ -624,7 +630,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (mb->get_button_index() == BUTTON_RIGHT) {
+ if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
emit_signal("rmb_clicked", mb->get_position());
return;
@@ -633,10 +639,10 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
// Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting.
emit_signal("nothing_selected");
}
- if (mb.is_valid() && mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) {
+ if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_pressed()) {
scroll_bar->set_value(scroll_bar->get_value() - scroll_bar->get_page() * mb->get_factor() / 8);
}
- if (mb.is_valid() && mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed()) {
+ if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_pressed()) {
scroll_bar->set_value(scroll_bar->get_value() + scroll_bar->get_page() * mb->get_factor() / 8);
}
@@ -764,7 +770,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
}
}
} else if (p_event->is_action("ui_accept")) {
- search_string = ""; //any mousepress cance
+ search_string = ""; //any mousepress cancels
if (current >= 0 && current < items.size()) {
emit_signal("item_activated", current);
@@ -879,6 +885,8 @@ void ItemList::_notification(int p_what) {
int vseparation = get_theme_constant("vseparation");
int icon_margin = get_theme_constant("icon_margin");
int line_separation = get_theme_constant("line_separation");
+ Color font_outline_color = get_theme_color("font_outline_color");
+ int outline_size = get_theme_constant("outline_size");
Ref<StyleBox> sbsel = has_focus() ? get_theme_stylebox("selected_focus") : get_theme_stylebox("selected");
Ref<StyleBox> cursor = has_focus() ? get_theme_stylebox("cursor") : get_theme_stylebox("cursor_unfocused");
@@ -886,7 +894,7 @@ void ItemList::_notification(int p_what) {
Color guide_color = get_theme_color("guide_color");
Color font_color = get_theme_color("font_color");
- Color font_color_selected = get_theme_color("font_color_selected");
+ Color font_selected_color = get_theme_color("font_selected_color");
if (has_focus()) {
RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true);
@@ -895,7 +903,7 @@ void ItemList::_notification(int p_what) {
}
if (shape_changed) {
- float max_column_width = 0;
+ float max_column_width = 0.0;
//1- compute item minimum sizes
for (int i = 0; i < items.size(); i++) {
@@ -1184,13 +1192,12 @@ void ItemList::_notification(int p_what) {
max_len = size2.x;
}
- Color modulate = items[i].selected ? font_color_selected : (items[i].custom_fg != Color() ? items[i].custom_fg : font_color);
+ Color modulate = items[i].selected ? font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : font_color);
if (items[i].disabled) {
modulate.a *= 0.5;
}
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
- text_ofs = text_ofs.floor();
text_ofs += base_ofs;
text_ofs += items[i].rect_cache.position;
@@ -1201,6 +1208,10 @@ void ItemList::_notification(int p_what) {
items.write[i].text_buf->set_width(max_len);
items.write[i].text_buf->set_align(HALIGN_CENTER);
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+ }
+
items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
} else {
if (fixed_column_width > 0) {
@@ -1213,7 +1224,6 @@ void ItemList::_notification(int p_what) {
text_ofs.y += (items[i].rect_cache.size.height - size2.y) / 2;
}
- text_ofs = text_ofs.floor();
text_ofs += base_ofs;
text_ofs += items[i].rect_cache.position;
@@ -1228,6 +1238,11 @@ void ItemList::_notification(int p_what) {
} else {
items.write[i].text_buf->set_align(HALIGN_LEFT);
}
+
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+ }
+
items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
}
}
@@ -1613,34 +1628,12 @@ void ItemList::_bind_methods() {
}
ItemList::ItemList() {
- current = -1;
-
- select_mode = SELECT_SINGLE;
- icon_mode = ICON_MODE_LEFT;
-
- fixed_column_width = 0;
- same_column_width = false;
- max_text_lines = 1;
- max_columns = 1;
- auto_height = false;
- auto_height_value = 0.0f;
-
scroll_bar = memnew(VScrollBar);
add_child(scroll_bar);
- shape_changed = true;
scroll_bar->connect("value_changed", callable_mp(this, &ItemList::_scroll_changed));
set_focus_mode(FOCUS_ALL);
- current_columns = 1;
- search_time_msec = 0;
- ensure_selected_visible = false;
- defer_select_single = -1;
- allow_rmb_select = false;
- allow_reselect = false;
- do_autoscroll_to_bottom = false;
-
- icon_scale = 1.0f;
set_clip_contents(true);
}
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index d08823c398..86a0174a20 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -52,7 +52,7 @@ public:
private:
struct Item {
Ref<Texture2D> icon;
- bool icon_transposed;
+ bool icon_transposed = false;
Rect2i icon_region;
Color icon_modulate;
Ref<Texture2D> tag_icon;
@@ -62,10 +62,10 @@ private:
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
- bool selectable;
- bool selected;
- bool disabled;
- bool tooltip_enabled;
+ bool selectable = false;
+ bool selected = false;
+ bool disabled = false;
+ bool tooltip_enabled = false;
Variant metadata;
String tooltip;
Color custom_fg;
@@ -79,44 +79,44 @@ private:
bool operator<(const Item &p_another) const { return text < p_another.text; }
};
- int current;
+ int current = -1;
- bool shape_changed;
+ bool shape_changed = true;
- bool ensure_selected_visible;
- bool same_column_width;
+ bool ensure_selected_visible = false;
+ bool same_column_width = false;
- bool auto_height;
- float auto_height_value;
+ bool auto_height = false;
+ float auto_height_value = 0.0;
Vector<Item> items;
Vector<int> separators;
- SelectMode select_mode;
- IconMode icon_mode;
+ SelectMode select_mode = SELECT_SINGLE;
+ IconMode icon_mode = ICON_MODE_LEFT;
VScrollBar *scroll_bar;
- uint64_t search_time_msec;
+ uint64_t search_time_msec = 0;
String search_string;
- int current_columns;
- int fixed_column_width;
- int max_text_lines;
- int max_columns;
+ int current_columns = 1;
+ int fixed_column_width = 0;
+ int max_text_lines = 1;
+ int max_columns = 1;
Size2 fixed_icon_size;
Size2 max_item_size_cache;
- int defer_select_single;
+ int defer_select_single = -1;
- bool allow_rmb_select;
+ bool allow_rmb_select = false;
- bool allow_reselect;
+ bool allow_reselect = false;
- real_t icon_scale;
+ real_t icon_scale = 1.0;
- bool do_autoscroll_to_bottom;
+ bool do_autoscroll_to_bottom = false;
Array _get_items() const;
void _set_items(const Array &p_items);
@@ -130,8 +130,8 @@ protected:
static void _bind_methods();
public:
- void add_item(const String &p_item, const Ref<Texture2D> &p_texture = Ref<Texture2D>(), bool p_selectable = true);
- void add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable = true);
+ int add_item(const String &p_item, const Ref<Texture2D> &p_texture = Ref<Texture2D>(), bool p_selectable = true);
+ int add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable = true);
void set_item_text(int p_idx, const String &p_text);
String get_item_text(int p_idx) const;
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index bd89fe441c..be73fd8f51 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -184,17 +184,17 @@ void Label::_notification(int p_what) {
Ref<StyleBox> style = get_theme_stylebox("normal");
Ref<Font> font = get_theme_font("font");
Color font_color = get_theme_color("font_color");
- Color font_color_shadow = get_theme_color("font_color_shadow");
+ Color font_shadow_color = get_theme_color("font_shadow_color");
Point2 shadow_ofs(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y"));
int line_spacing = get_theme_constant("line_spacing");
- Color font_outline_modulate = get_theme_color("font_outline_modulate");
+ Color font_outline_color = get_theme_color("font_outline_color");
int outline_size = get_theme_constant("outline_size");
int shadow_outline_size = get_theme_constant("shadow_outline_size");
bool rtl = is_layout_rtl();
style->draw(ci, Rect2(Point2(0, 0), get_size()));
- float total_h = 0;
+ float total_h = 0.0;
int lines_visible = 0;
// Get number of lines to fit to the height.
@@ -260,7 +260,8 @@ void Label::_notification(int p_what) {
}
}
}
- visible_glyphs = total_glyphs * percent_visible;
+
+ visible_glyphs = MIN(total_glyphs, visible_chars);
}
Vector2 ofs;
@@ -298,17 +299,17 @@ void Label::_notification(int p_what) {
for (int j = 0; j < gl_size; j++) {
for (int k = 0; k < glyphs[j].repeat; k++) {
if (glyphs[j].font_rid != RID()) {
- if (font_color_shadow.a > 0) {
- TS->font_draw_glyph(glyphs[j].font_rid, ci, glyphs[j].font_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off) + shadow_ofs, glyphs[j].index, font_color_shadow);
+ if (font_shadow_color.a > 0) {
+ TS->font_draw_glyph(glyphs[j].font_rid, ci, glyphs[j].font_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off) + shadow_ofs, glyphs[j].index, font_shadow_color);
if (shadow_outline_size > 0) {
//draw shadow
- TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, shadow_outline_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off) + Vector2(-shadow_ofs.x, shadow_ofs.y), glyphs[j].index, font_color_shadow);
- TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, shadow_outline_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off) + Vector2(shadow_ofs.x, -shadow_ofs.y), glyphs[j].index, font_color_shadow);
- TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, shadow_outline_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off) + Vector2(-shadow_ofs.x, -shadow_ofs.y), glyphs[j].index, font_color_shadow);
+ TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, shadow_outline_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off) + Vector2(-shadow_ofs.x, shadow_ofs.y), glyphs[j].index, font_shadow_color);
+ TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, shadow_outline_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off) + Vector2(shadow_ofs.x, -shadow_ofs.y), glyphs[j].index, font_shadow_color);
+ TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, shadow_outline_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off) + Vector2(-shadow_ofs.x, -shadow_ofs.y), glyphs[j].index, font_shadow_color);
}
}
- if (font_outline_modulate.a != 0.0 && outline_size > 0) {
- TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, outline_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off), glyphs[j].index, font_outline_modulate);
+ if (font_outline_color.a != 0.0 && outline_size > 0) {
+ TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, outline_size, ofs + Vector2(glyphs[j].x_off, glyphs[j].y_off), glyphs[j].index, font_outline_color);
}
}
ofs.x += glyphs[j].advance;
@@ -357,21 +358,25 @@ void Label::_notification(int p_what) {
}
Size2 Label::get_minimum_size() const {
- Size2 min_style = get_theme_stylebox("normal")->get_minimum_size();
-
// don't want to mutable everything
if (dirty || lines_dirty) {
const_cast<Label *>(this)->_shape();
}
+ Size2 min_size = minsize;
+
+ Ref<Font> font = get_theme_font("font");
+ min_size.height = MAX(min_size.height, font->get_height(get_theme_font_size("font_size")) + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM));
+
+ Size2 min_style = get_theme_stylebox("normal")->get_minimum_size();
if (autowrap) {
- return Size2(1, clip ? 1 : minsize.height) + min_style;
+ return Size2(1, clip ? 1 : min_size.height) + min_style;
} else {
- Size2 ms = minsize;
if (clip) {
- ms.width = 1;
+ min_size.width = 1;
}
- return ms + min_style;
+
+ return min_size + min_style;
}
}
@@ -391,7 +396,7 @@ int Label::get_visible_line_count() const {
Ref<StyleBox> style = get_theme_stylebox("normal");
int line_spacing = get_theme_constant("line_spacing");
int lines_visible = 0;
- float total_h = 0;
+ float total_h = 0.0;
for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM) + line_spacing;
if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
@@ -537,8 +542,9 @@ void Label::set_visible_characters(int p_amount) {
visible_chars = p_amount;
if (get_total_character_count() > 0) {
percent_visible = (float)p_amount / (float)get_total_character_count();
+ } else {
+ percent_visible = 1.0;
}
- _change_notify("percent_visible");
update();
}
@@ -555,7 +561,6 @@ void Label::set_percent_visible(float p_percent) {
visible_chars = get_total_character_count() * p_percent;
percent_visible = p_percent;
}
- _change_notify("visible_chars");
update();
}
@@ -564,6 +569,7 @@ float Label::get_percent_visible() const {
}
void Label::set_lines_skipped(int p_lines) {
+ ERR_FAIL_COND(p_lines < 0);
lines_skipped = p_lines;
_update_visible();
update();
@@ -610,7 +616,7 @@ bool Label::_set(const StringName &p_name, const Variant &p_value) {
update();
}
}
- _change_notify();
+ notify_property_list_changed();
return true;
}
diff --git a/scene/gui/label.h b/scene/gui/label.h
index 219c6af39e..032b4112e1 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -72,7 +72,7 @@ private:
Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
Array st_args;
- float percent_visible = 1;
+ float percent_visible = 1.0;
int visible_chars = -1;
int lines_skipped = 0;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 5f0bb453f3..eb836b3bf7 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -30,6 +30,7 @@
#include "line_edit.h"
+#include "core/input/input_map.h"
#include "core/object/message_queue.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
@@ -44,7 +45,180 @@
#endif
#include "scene/main/window.h"
+void LineEdit::_swap_current_input_direction() {
+ if (input_direction == TEXT_DIRECTION_LTR) {
+ input_direction = TEXT_DIRECTION_RTL;
+ } else {
+ input_direction = TEXT_DIRECTION_LTR;
+ }
+ set_caret_column(get_caret_column());
+ update();
+}
+
+void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) {
+ if (selection.enabled && !p_select) {
+ set_caret_column(selection.begin);
+ deselect();
+ return;
+ }
+
+ shift_selection_check_pre(p_select);
+
+ if (p_move_by_word) {
+ int cc = caret_column;
+
+ Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
+ for (int i = words.size() - 1; i >= 0; i--) {
+ if (words[i].x < cc) {
+ cc = words[i].x;
+ break;
+ }
+ }
+
+ set_caret_column(cc);
+ } else {
+ if (caret_mid_grapheme_enabled) {
+ set_caret_column(get_caret_column() - 1);
+ } else {
+ set_caret_column(TS->shaped_text_prev_grapheme_pos(text_rid, get_caret_column()));
+ }
+ }
+
+ shift_selection_check_post(p_select);
+}
+
+void LineEdit::_move_caret_right(bool p_select, bool p_move_by_word) {
+ if (selection.enabled && !p_select) {
+ set_caret_column(selection.end);
+ deselect();
+ return;
+ }
+
+ shift_selection_check_pre(p_select);
+
+ if (p_move_by_word) {
+ int cc = caret_column;
+
+ Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
+ for (int i = 0; i < words.size(); i++) {
+ if (words[i].y > cc) {
+ cc = words[i].y;
+ break;
+ }
+ }
+
+ set_caret_column(cc);
+ } else {
+ if (caret_mid_grapheme_enabled) {
+ set_caret_column(get_caret_column() + 1);
+ } else {
+ set_caret_column(TS->shaped_text_next_grapheme_pos(text_rid, get_caret_column()));
+ }
+ }
+
+ shift_selection_check_post(p_select);
+}
+
+void LineEdit::_move_caret_start(bool p_select) {
+ shift_selection_check_pre(p_select);
+ set_caret_column(0);
+ shift_selection_check_post(p_select);
+}
+
+void LineEdit::_move_caret_end(bool p_select) {
+ shift_selection_check_pre(p_select);
+ set_caret_column(text.length());
+ shift_selection_check_post(p_select);
+}
+
+void LineEdit::_backspace(bool p_word, bool p_all_to_left) {
+ if (!editable) {
+ return;
+ }
+
+ if (p_all_to_left) {
+ deselect();
+ text = text.substr(0, caret_column);
+ _text_changed();
+ return;
+ }
+
+ if (selection.enabled) {
+ selection_delete();
+ return;
+ }
+
+ if (p_word) {
+ int cc = caret_column;
+
+ Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
+ for (int i = words.size() - 1; i >= 0; i--) {
+ if (words[i].x < cc) {
+ cc = words[i].x;
+ break;
+ }
+ }
+
+ delete_text(cc, caret_column);
+
+ set_caret_column(cc);
+ } else {
+ delete_char();
+ }
+}
+
+void LineEdit::_delete(bool p_word, bool p_all_to_right) {
+ if (!editable) {
+ return;
+ }
+
+ if (p_all_to_right) {
+ deselect();
+ text = text.substr(caret_column, text.length() - caret_column);
+ _shape();
+ set_caret_column(0);
+ _text_changed();
+ return;
+ }
+
+ if (selection.enabled) {
+ selection_delete();
+ return;
+ }
+
+ int text_len = text.length();
+
+ if (caret_column == text_len) {
+ return; // Nothing to do.
+ }
+
+ if (p_word) {
+ int cc = caret_column;
+ Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
+ for (int i = 0; i < words.size(); i++) {
+ if (words[i].y > cc) {
+ cc = words[i].y;
+ break;
+ }
+ }
+
+ delete_text(caret_column, cc);
+ set_caret_column(caret_column);
+ } else {
+ if (caret_mid_grapheme_enabled) {
+ set_caret_column(caret_column + 1);
+ delete_char();
+ } else {
+ int cc = caret_column;
+ set_caret_column(TS->shaped_text_next_grapheme_pos(text_rid, caret_column));
+ delete_text(cc, caret_column);
+ }
+ }
+}
+
void LineEdit::_gui_input(Ref<InputEvent> p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
Ref<InputEventMouseButton> b = p_event;
if (b.is_valid()) {
@@ -52,17 +226,17 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
// Ignore mouse clicks in IME input mode.
return;
}
- if (b->is_pressed() && b->get_button_index() == BUTTON_RIGHT && context_menu_enabled) {
+ if (b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_RIGHT && context_menu_enabled) {
menu->set_position(get_screen_transform().xform(get_local_mouse_position()));
menu->set_size(Vector2(1, 1));
- //menu->set_scale(get_global_transform().get_scale());
+ _generate_context_menu();
menu->popup();
grab_focus();
accept_event();
return;
}
- if (b->get_button_index() != BUTTON_LEFT) {
+ if (b->get_button_index() != MOUSE_BUTTON_LEFT) {
return;
}
@@ -78,25 +252,44 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
shift_selection_check_pre(b->get_shift());
- set_cursor_at_pixel_pos(b->get_position().x);
+ set_caret_at_pixel_pos(b->get_position().x);
if (b->get_shift()) {
- selection_fill_at_cursor();
+ selection_fill_at_caret();
selection.creating = true;
} else {
- if (b->is_doubleclick() && selecting_enabled) {
- selection.enabled = true;
- selection.begin = 0;
- selection.end = text.length();
- selection.doubleclick = true;
+ if (selecting_enabled) {
+ if (!b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - selection.last_dblclk) < 600) {
+ // Triple-click select all.
+ selection.enabled = true;
+ selection.begin = 0;
+ selection.end = text.length();
+ selection.double_click = true;
+ selection.last_dblclk = 0;
+ caret_column = selection.begin;
+ } else if (b->is_double_click()) {
+ // Double-click select word.
+ Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
+ for (int i = 0; i < words.size(); i++) {
+ if (words[i].x < caret_column && words[i].y > caret_column) {
+ selection.enabled = true;
+ selection.begin = words[i].x;
+ selection.end = words[i].y;
+ selection.double_click = true;
+ selection.last_dblclk = OS::get_singleton()->get_ticks_msec();
+ caret_column = selection.end;
+ break;
+ }
+ }
+ }
}
selection.drag_attempt = false;
- if ((cursor_pos < selection.begin) || (cursor_pos > selection.end) || !selection.enabled) {
+ if ((caret_column < selection.begin) || (caret_column > selection.end) || !selection.enabled) {
deselect();
- selection.cursor_start = cursor_pos;
+ selection.start_column = caret_column;
selection.creating = true;
} else if (selection.enabled) {
selection.drag_attempt = true;
@@ -115,19 +308,13 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
}
}
- if ((!selection.creating) && (!selection.doubleclick)) {
+ if ((!selection.creating) && (!selection.double_click)) {
deselect();
}
selection.creating = false;
- selection.doubleclick = false;
+ selection.double_click = false;
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
- if (selection.enabled) {
- DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, selection.begin, selection.end);
- } else {
- DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, cursor_pos);
- }
- }
+ show_virtual_keyboard();
}
update();
@@ -144,10 +331,10 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
}
}
- if (m->get_button_mask() & BUTTON_LEFT) {
+ if (m->get_button_mask() & MOUSE_BUTTON_LEFT) {
if (selection.creating) {
- set_cursor_at_pixel_pos(m->get_position().x);
- selection_fill_at_cursor();
+ set_caret_at_pixel_pos(m->get_position().x);
+ selection_fill_at_caret();
}
}
}
@@ -159,453 +346,163 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
return;
}
-#ifdef APPLE_STYLE_KEYS
- if (k->get_control() && !k->get_shift() && !k->get_alt() && !k->get_command()) {
- uint32_t remap_key = KEY_UNKNOWN;
- switch (k->get_keycode()) {
- case KEY_F: {
- remap_key = KEY_RIGHT;
- } break;
- case KEY_B: {
- remap_key = KEY_LEFT;
- } break;
- case KEY_P: {
- remap_key = KEY_UP;
- } break;
- case KEY_N: {
- remap_key = KEY_DOWN;
- } break;
- case KEY_D: {
- remap_key = KEY_DELETE;
- } break;
- case KEY_H: {
- remap_key = KEY_BACKSPACE;
- } break;
- case KEY_A: {
- remap_key = KEY_HOME;
- } break;
- case KEY_E: {
- remap_key = KEY_END;
- } break;
+ if (context_menu_enabled) {
+ if (k->is_action("ui_menu", true)) {
+ Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + get_theme_font("font")->get_height(get_theme_font_size("font_size"))) / 2);
+ menu->set_position(get_global_transform().xform(pos));
+ menu->set_size(Vector2(1, 1));
+ _generate_context_menu();
+ menu->popup();
+ menu->grab_focus();
}
+ }
- if (remap_key != KEY_UNKNOWN) {
- k->set_keycode(remap_key);
- k->set_control(false);
+ // Default is ENTER, KP_ENTER. Cannot use ui_accept as default includes SPACE
+ if (k->is_action("ui_text_newline", true)) {
+ emit_signal("text_entered", text);
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
+ DisplayServer::get_singleton()->virtual_keyboard_hide();
}
}
-#endif
-
- unsigned int code = k->get_keycode();
-
- if (k->get_command() && is_shortcut_keys_enabled()) {
- bool handled = true;
-
- switch (code) {
- case (KEY_QUOTELEFT): { // Swap current input direction (primary cursor)
-
- if (input_direction == TEXT_DIRECTION_LTR) {
- input_direction = TEXT_DIRECTION_RTL;
- } else {
- input_direction = TEXT_DIRECTION_LTR;
- }
- set_cursor_position(get_cursor_position());
- update();
-
- } break;
-
- case (KEY_X): { // CUT.
-
- if (editable) {
- cut_text();
- }
-
- } break;
- case (KEY_C): { // COPY.
-
- copy_text();
-
- } break;
-
- case (KEY_Y): // PASTE (Yank for unix users).
- case (KEY_V): { // PASTE.
-
- if (editable) {
- paste_text();
- }
-
- } break;
-
- case (KEY_Z): { // Undo/redo.
- if (editable) {
- if (k->get_shift()) {
- redo();
- } else {
- undo();
- }
- }
- } break;
-
- case (KEY_U): { // Delete from start to cursor.
-
- if (editable) {
- deselect();
- text = text.substr(cursor_pos, text.length() - cursor_pos);
- _shape();
- set_cursor_position(0);
- _text_changed();
- }
-
- } break;
-
- case (KEY_K): { // Delete from cursor_pos to end.
-
- if (editable) {
- deselect();
- text = text.substr(0, cursor_pos);
- _text_changed();
- }
+ if (is_shortcut_keys_enabled()) {
+ if (k->is_action("ui_copy", true)) {
+ copy_text();
+ accept_event();
+ return;
+ }
- } break;
- case (KEY_A): { // Select all.
- select();
+ if (k->is_action("ui_text_select_all", true)) {
+ select();
+ accept_event();
+ return;
+ }
- } break;
-#ifdef APPLE_STYLE_KEYS
- case (KEY_LEFT): { // Go to start of text - like HOME key.
- shift_selection_check_pre(k->get_shift());
- set_cursor_position(0);
- shift_selection_check_post(k->get_shift());
- } break;
- case (KEY_RIGHT): { // Go to end of text - like END key.
- shift_selection_check_pre(k->get_shift());
- set_cursor_position(text.length());
- shift_selection_check_post(k->get_shift());
- } break;
- case (KEY_BACKSPACE): {
- if (!editable)
- break;
+ // Cut / Paste
+ if (k->is_action("ui_cut", true)) {
+ cut_text();
+ accept_event();
+ return;
+ }
- // If selected, delete the selection
- if (selection.enabled) {
- selection_delete();
- break;
- }
+ if (k->is_action("ui_paste", true)) {
+ paste_text();
+ accept_event();
+ return;
+ }
- // Otherwise delete from cursor to beginning of text edit
- int current_pos = get_cursor_position();
- if (current_pos != 0) {
- delete_text(0, current_pos);
- }
- } break;
-#endif
- default: {
- handled = false;
- }
+ // Undo / Redo
+ if (k->is_action("ui_undo", true)) {
+ undo();
+ accept_event();
+ return;
}
- if (handled) {
+ if (k->is_action("ui_redo", true)) {
+ redo();
accept_event();
return;
}
}
- _reset_caret_blink_timer();
- if (!k->get_metakey()) {
- bool handled = true;
- switch (code) {
- case KEY_KP_ENTER:
- case KEY_ENTER: {
- emit_signal("text_entered", text);
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
- DisplayServer::get_singleton()->virtual_keyboard_hide();
- }
-
- } break;
-
- case KEY_BACKSPACE: {
- if (!editable) {
- break;
- }
-
- if (selection.enabled) {
- selection_delete();
- break;
- }
-
-#ifdef APPLE_STYLE_KEYS
- if (k->get_alt()) {
-#else
- if (k->get_alt()) {
- handled = false;
- break;
- } else if (k->get_command()) {
-#endif
- int cc = cursor_pos;
-
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
- for (int i = words.size() - 1; i >= 0; i--) {
- if (words[i].x < cc) {
- cc = words[i].x;
- break;
- }
- }
-
- delete_text(cc, cursor_pos);
-
- set_cursor_position(cc);
-
- } else {
- delete_char();
- }
-
- } break;
- case KEY_KP_4: {
- if (k->get_unicode() != 0) {
- handled = false;
- break;
- }
- [[fallthrough]];
- }
- case KEY_LEFT: {
-#ifndef APPLE_STYLE_KEYS
- if (!k->get_alt()) {
-#endif
- if (selection.enabled && !k->get_shift()) {
- set_cursor_position(selection.begin);
- deselect();
- handled = true;
- break;
- }
-
- shift_selection_check_pre(k->get_shift());
-#ifndef APPLE_STYLE_KEYS
- }
-#endif
-
-#ifdef APPLE_STYLE_KEYS
- if (k->get_command()) {
- set_cursor_position(0);
- } else if (k->get_alt()) {
-#else
- if (k->get_alt()) {
- handled = false;
- break;
- } else if (k->get_command()) {
-#endif
- int cc = cursor_pos;
-
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
- for (int i = words.size() - 1; i >= 0; i--) {
- if (words[i].x < cc) {
- cc = words[i].x;
- break;
- }
- }
-
- set_cursor_position(cc);
-
- } else {
- if (mid_grapheme_caret_enabled) {
- set_cursor_position(get_cursor_position() - 1);
- } else {
- set_cursor_position(TS->shaped_text_prev_grapheme_pos(text_rid, get_cursor_position()));
- }
- }
-
- shift_selection_check_post(k->get_shift());
-
- } break;
- case KEY_KP_6: {
- if (k->get_unicode() != 0) {
- handled = false;
- break;
- }
- [[fallthrough]];
- }
- case KEY_RIGHT: {
-#ifndef APPLE_STYLE_KEYS
- if (!k->get_alt()) {
-#endif
- if (selection.enabled && !k->get_shift()) {
- set_cursor_position(selection.end);
- deselect();
- handled = true;
- break;
- }
-
- shift_selection_check_pre(k->get_shift());
-#ifndef APPLE_STYLE_KEYS
- }
-#endif
-
-#ifdef APPLE_STYLE_KEYS
- if (k->get_command()) {
- set_cursor_position(text.length());
- } else if (k->get_alt()) {
-#else
- if (k->get_alt()) {
- handled = false;
- break;
- } else if (k->get_command()) {
-#endif
- int cc = cursor_pos;
-
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
- for (int i = 0; i < words.size(); i++) {
- if (words[i].y > cc) {
- cc = words[i].y;
- break;
- }
- }
-
- set_cursor_position(cc);
-
- } else {
- if (mid_grapheme_caret_enabled) {
- set_cursor_position(get_cursor_position() + 1);
- } else {
- set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, get_cursor_position()));
- }
- }
-
- shift_selection_check_post(k->get_shift());
-
- } break;
- case KEY_UP: {
- shift_selection_check_pre(k->get_shift());
- if (get_cursor_position() == 0) {
- handled = false;
- }
- set_cursor_position(0);
- shift_selection_check_post(k->get_shift());
- } break;
- case KEY_DOWN: {
- shift_selection_check_pre(k->get_shift());
- if (get_cursor_position() == text.length()) {
- handled = false;
- }
- set_cursor_position(text.length());
- shift_selection_check_post(k->get_shift());
- } break;
- case KEY_DELETE: {
- if (!editable) {
- break;
- }
-
- if (k->get_shift() && !k->get_command() && !k->get_alt()) {
- cut_text();
- break;
- }
-
- if (selection.enabled) {
- selection_delete();
- break;
- }
-
- int text_len = text.length();
+ // BACKSPACE
+ if (k->is_action("ui_text_backspace_all_to_left", true)) {
+ _backspace(false, true);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_backspace_word", true)) {
+ _backspace(true);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_backspace", true)) {
+ _backspace();
+ accept_event();
+ return;
+ }
- if (cursor_pos == text_len) {
- break; // Nothing to do.
- }
+ // DELETE
+ if (k->is_action("ui_text_delete_all_to_right", true)) {
+ _delete(false, true);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_delete_word", true)) {
+ _delete(true);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_delete", true)) {
+ _delete();
+ accept_event();
+ return;
+ }
-#ifdef APPLE_STYLE_KEYS
- if (k->get_alt()) {
-#else
- if (k->get_alt()) {
- handled = false;
- break;
- } else if (k->get_command()) {
-#endif
- int cc = cursor_pos;
+ // Cursor Movement
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
- for (int i = 0; i < words.size(); i++) {
- if (words[i].y > cc) {
- cc = words[i].y;
- break;
- }
- }
+ k = k->duplicate();
+ bool shift_pressed = k->get_shift();
+ // Remove shift or else actions will not match. Use above variable for selection.
+ k->set_shift(false);
- delete_text(cursor_pos, cc);
+ if (k->is_action("ui_text_caret_word_left", true)) {
+ _move_caret_left(shift_pressed, true);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_caret_left", true)) {
+ _move_caret_left(shift_pressed);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_caret_word_right", true)) {
+ _move_caret_right(shift_pressed, true);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_caret_right", true)) {
+ _move_caret_right(shift_pressed, false);
+ accept_event();
+ return;
+ }
- } else {
- if (mid_grapheme_caret_enabled) {
- set_cursor_position(cursor_pos + 1);
- delete_char();
- } else {
- int cc = cursor_pos;
- set_cursor_position(TS->shaped_text_next_grapheme_pos(text_rid, cursor_pos));
- delete_text(cc, cursor_pos);
- }
- }
+ // Up = Home, Down = End
+ if (k->is_action("ui_text_caret_up", true) || k->is_action("ui_text_caret_line_start", true) || k->is_action("ui_text_caret_page_up", true)) {
+ _move_caret_start(shift_pressed);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_caret_down", true) || k->is_action("ui_text_caret_line_end", true) || k->is_action("ui_text_caret_page_down", true)) {
+ _move_caret_end(shift_pressed);
+ accept_event();
+ return;
+ }
- } break;
- case KEY_KP_7: {
- if (k->get_unicode() != 0) {
- handled = false;
- break;
- }
- [[fallthrough]];
- }
- case KEY_HOME: {
- shift_selection_check_pre(k->get_shift());
- set_cursor_position(0);
- shift_selection_check_post(k->get_shift());
- } break;
- case KEY_KP_1: {
- if (k->get_unicode() != 0) {
- handled = false;
- break;
- }
- [[fallthrough]];
- }
- case KEY_END: {
- shift_selection_check_pre(k->get_shift());
- set_cursor_position(text.length());
- shift_selection_check_post(k->get_shift());
- } break;
- case KEY_MENU: {
- if (context_menu_enabled) {
- Point2 pos = Point2(get_cursor_pixel_pos().x, (get_size().y + get_theme_font("font")->get_height(get_theme_font_size("font_size"))) / 2);
- menu->set_position(get_global_transform().xform(pos));
- menu->set_size(Vector2(1, 1));
- //menu->set_scale(get_global_transform().get_scale());
- menu->popup();
- menu->grab_focus();
- }
- } break;
+ // Misc
+ if (k->is_action("ui_swap_input_direction", true)) {
+ _swap_current_input_direction();
+ accept_event();
+ return;
+ }
- default: {
- handled = false;
- } break;
- }
+ _reset_caret_blink_timer();
- if (handled) {
- accept_event();
- } else if (!k->get_command()) {
- if (k->get_unicode() >= 32 && k->get_keycode() != KEY_DELETE) {
- if (editable) {
- selection_delete();
- char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 };
- int prev_len = text.length();
- append_at_cursor(ucodestr);
- if (text.length() != prev_len) {
- _text_changed();
- }
- accept_event();
- }
+ // Allow unicode handling if:
+ // * No Modifiers are pressed (except shift)
+ bool allow_unicode_handling = !(k->get_command() || k->get_control() || k->get_alt() || k->get_metakey());
- } else {
- return;
- }
+ if (allow_unicode_handling && editable && k->get_unicode() >= 32) {
+ // Handle Unicode (if no modifiers active)
+ selection_delete();
+ char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 };
+ int prev_len = text.length();
+ insert_text_at_caret(ucodestr);
+ if (text.length() != prev_len) {
+ _text_changed();
}
-
- update();
+ accept_event();
}
-
- return;
}
}
@@ -635,20 +532,27 @@ Variant LineEdit::get_drag_data(const Point2 &p_point) {
}
bool LineEdit::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
+ bool drop_override = Control::can_drop_data(p_point, p_data); // In case user wants to drop custom data.
+ if (drop_override) {
+ return drop_override;
+ }
+
return p_data.get_type() == Variant::STRING;
}
void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
+ Control::drop_data(p_point, p_data);
+
if (p_data.get_type() == Variant::STRING) {
- set_cursor_at_pixel_pos(p_point.x);
+ set_caret_at_pixel_pos(p_point.x);
int selected = selection.end - selection.begin;
text.erase(selection.begin, selected);
_shape();
- append_at_cursor(p_data);
- selection.begin = cursor_pos - selected;
- selection.end = cursor_pos;
+ insert_text_at_caret(p_data);
+ selection.begin = caret_column - selected;
+ selection.end = caret_column;
}
}
@@ -673,8 +577,8 @@ void LineEdit::_notification(int p_what) {
#ifdef TOOLS_ENABLED
case NOTIFICATION_ENTER_TREE: {
if (Engine::get_singleton()->is_editor_hint() && !get_tree()->is_node_being_edited(this)) {
- cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false));
- cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65));
+ set_caret_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false));
+ set_caret_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65));
if (!EditorSettings::get_singleton()->is_connected("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed))) {
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed));
@@ -685,7 +589,7 @@ void LineEdit::_notification(int p_what) {
case NOTIFICATION_RESIZED: {
_fit_to_width();
scroll_offset = 0;
- set_cursor_position(get_cursor_position());
+ set_caret_column(get_caret_column());
} break;
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
@@ -770,9 +674,9 @@ void LineEdit::_notification(int p_what) {
int y_ofs = style->get_offset().y + (y_area - text_height) / 2;
Color selection_color = get_theme_color("selection_color");
- Color font_color = is_editable() ? get_theme_color("font_color") : get_theme_color("font_color_uneditable");
- Color font_color_selected = get_theme_color("font_color_selected");
- Color cursor_color = get_theme_color("cursor_color");
+ Color font_color = is_editable() ? get_theme_color("font_color") : get_theme_color("font_uneditable_color");
+ Color font_selected_color = get_theme_color("font_selected_color");
+ Color caret_color = get_theme_color("caret_color");
// Draw placeholder color.
if (using_placeholder) {
@@ -834,14 +738,32 @@ void LineEdit::_notification(int p_what) {
// Draw text.
ofs.y += TS->shaped_text_get_ascent(text_rid);
+ Color font_outline_color = get_theme_color("font_outline_color");
+ int outline_size = get_theme_constant("outline_size");
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ Vector2 oofs = ofs;
+ for (int i = 0; i < gl_size; i++) {
+ for (int j = 0; j < glyphs[i].repeat; j++) {
+ if (ceil(oofs.x) >= x_ofs && (oofs.x + glyphs[i].advance) <= ofs_max) {
+ if (glyphs[i].font_rid != RID()) {
+ TS->font_draw_glyph_outline(glyphs[i].font_rid, ci, glyphs[i].font_size, outline_size, oofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, font_outline_color);
+ }
+ }
+ oofs.x += glyphs[i].advance;
+ }
+ if (oofs.x >= ofs_max) {
+ break;
+ }
+ }
+ }
for (int i = 0; i < gl_size; i++) {
bool selected = selection.enabled && glyphs[i].start >= selection.begin && glyphs[i].end <= selection.end;
for (int j = 0; j < glyphs[i].repeat; j++) {
if (ceil(ofs.x) >= x_ofs && (ofs.x + glyphs[i].advance) <= ofs_max) {
if (glyphs[i].font_rid != RID()) {
- TS->font_draw_glyph(glyphs[i].font_rid, ci, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_color_selected : font_color);
+ TS->font_draw_glyph(glyphs[i].font_rid, ci, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_selected_color : font_color);
} else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
- TS->draw_hex_code_box(ci, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_color_selected : font_color);
+ TS->draw_hex_code_box(ci, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, selected ? font_selected_color : font_color);
}
}
ofs.x += glyphs[i].advance;
@@ -858,7 +780,7 @@ void LineEdit::_notification(int p_what) {
// Normal caret.
Rect2 l_caret, t_caret;
TextServer::Direction l_dir, t_dir;
- TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir);
+ TS->shaped_text_get_carets(text_rid, caret_column, l_caret, l_dir, t_caret, t_dir);
if (l_caret == Rect2() && t_caret == Rect2()) {
// No carets, add one at the start.
@@ -871,28 +793,28 @@ void LineEdit::_notification(int p_what) {
l_dir = TextServer::DIRECTION_LTR;
l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h));
}
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, caret_color);
} else {
if (l_caret != Rect2() && l_dir == TextServer::DIRECTION_AUTO) {
// Draw extra marker on top of mid caret.
Rect2 trect = Rect2(l_caret.position.x - 3 * caret_width, l_caret.position.y, 6 * caret_width, caret_width);
trect.position += ofs;
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, cursor_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color);
}
l_caret.position += ofs;
l_caret.size.x = caret_width;
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, cursor_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, caret_color);
t_caret.position += ofs;
t_caret.size.x = caret_width;
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, t_caret, cursor_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, t_caret, caret_color);
}
} else {
{
- // IME intermidiet text range.
- Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos, cursor_pos + ime_text.length());
+ // IME intermediate text range.
+ Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column, caret_column + ime_text.length());
for (int i = 0; i < sel.size(); i++) {
Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height);
if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) {
@@ -905,12 +827,12 @@ void LineEdit::_notification(int p_what) {
rect.size.x = ofs_max - rect.position.x;
}
rect.size.y = caret_width;
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color);
}
}
{
// IME caret.
- Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, cursor_pos + ime_selection.x, cursor_pos + ime_selection.x + ime_selection.y);
+ Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column + ime_selection.x, caret_column + ime_selection.x + ime_selection.y);
for (int i = 0; i < sel.size(); i++) {
Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height);
if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) {
@@ -923,14 +845,14 @@ void LineEdit::_notification(int p_what) {
rect.size.x = ofs_max - rect.position.x;
}
rect.size.y = caret_width * 3;
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, cursor_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color);
}
}
}
}
if (has_focus()) {
- if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) {
+ if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id());
DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + Point2(using_placeholder ? 0 : x_ofs, y_ofs + TS->shaped_text_get_size(text_rid).y), get_viewport()->get_window_id());
}
@@ -947,34 +869,27 @@ void LineEdit::_notification(int p_what) {
}
}
- if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) {
+ if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id());
- Point2 cursor_pos = Point2(get_cursor_position(), 1) * get_minimum_size().height;
- DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos, get_viewport()->get_window_id());
- }
-
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
- if (selection.enabled) {
- DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, selection.begin, selection.end);
- } else {
- DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, cursor_pos);
- }
+ Point2 caret_column = Point2(get_caret_column(), 1) * get_minimum_size().height;
+ DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + caret_column, get_viewport()->get_window_id());
}
+ show_virtual_keyboard();
} break;
case NOTIFICATION_FOCUS_EXIT: {
if (caret_blink_enabled && !caret_force_displayed) {
caret_blink_timer->stop();
}
- if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) {
+ if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
DisplayServer::get_singleton()->window_set_ime_position(Point2(), get_viewport()->get_window_id());
DisplayServer::get_singleton()->window_set_ime_active(false, get_viewport()->get_window_id());
}
ime_text = "";
ime_selection = Point2();
_shape();
- set_cursor_position(cursor_pos); // Update scroll_offset
+ set_caret_column(caret_column); // Update scroll_offset
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
@@ -986,7 +901,7 @@ void LineEdit::_notification(int p_what) {
ime_text = DisplayServer::get_singleton()->ime_get_text();
ime_selection = DisplayServer::get_singleton()->ime_get_selection();
_shape();
- set_cursor_position(cursor_pos); // Update scroll_offset
+ set_caret_column(caret_column); // Update scroll_offset
update();
}
@@ -1001,13 +916,17 @@ void LineEdit::copy_text() {
}
void LineEdit::cut_text() {
- if (selection.enabled && !pass) {
+ if (editable && selection.enabled && !pass) {
DisplayServer::get_singleton()->clipboard_set(text.substr(selection.begin, selection.end - selection.begin));
selection_delete();
}
}
void LineEdit::paste_text() {
+ if (!editable) {
+ return;
+ }
+
// Strip escape characters like \n and \t as they can't be displayed on LineEdit.
String paste_buffer = DisplayServer::get_singleton()->clipboard_get().strip_escapes();
@@ -1016,7 +935,7 @@ void LineEdit::paste_text() {
if (selection.enabled) {
selection_delete();
}
- append_at_cursor(paste_buffer);
+ insert_text_at_caret(paste_buffer);
if (!text_changed_dirty) {
if (is_inside_tree() && text.length() != prev_len) {
@@ -1028,6 +947,10 @@ void LineEdit::paste_text() {
}
void LineEdit::undo() {
+ if (!editable) {
+ return;
+ }
+
if (undo_stack_pos == nullptr) {
if (undo_stack.size() <= 1) {
return;
@@ -1040,13 +963,17 @@ void LineEdit::undo() {
TextOperation op = undo_stack_pos->get();
text = op.text;
scroll_offset = op.scroll_offset;
- set_cursor_position(op.cursor_pos);
+ set_caret_column(op.caret_column);
_shape();
_emit_text_change();
}
void LineEdit::redo() {
+ if (!editable) {
+ return;
+ }
+
if (undo_stack_pos == nullptr) {
return;
}
@@ -1057,7 +984,7 @@ void LineEdit::redo() {
TextOperation op = undo_stack_pos->get();
text = op.text;
scroll_offset = op.scroll_offset;
- set_cursor_position(op.cursor_pos);
+ set_caret_column(op.caret_column);
_shape();
_emit_text_change();
@@ -1065,7 +992,7 @@ void LineEdit::redo() {
void LineEdit::shift_selection_check_pre(bool p_shift) {
if (!selection.enabled && p_shift) {
- selection.cursor_start = cursor_pos;
+ selection.start_column = caret_column;
}
if (!p_shift) {
deselect();
@@ -1074,11 +1001,11 @@ void LineEdit::shift_selection_check_pre(bool p_shift) {
void LineEdit::shift_selection_check_post(bool p_shift) {
if (p_shift) {
- selection_fill_at_cursor();
+ selection_fill_at_caret();
}
}
-void LineEdit::set_cursor_at_pixel_pos(int p_x) {
+void LineEdit::set_caret_at_pixel_pos(int p_x) {
Ref<StyleBox> style = get_theme_stylebox("normal");
bool rtl = is_layout_rtl();
@@ -1123,10 +1050,10 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) {
}
int ofs = TS->shaped_text_hit_test_position(text_rid, p_x - x_ofs - scroll_offset);
- set_cursor_position(ofs);
+ set_caret_column(ofs);
}
-Vector2i LineEdit::get_cursor_pixel_pos() {
+Vector2i LineEdit::get_caret_pixel_pos() {
Ref<StyleBox> style = get_theme_stylebox("normal");
bool rtl = is_layout_rtl();
@@ -1175,9 +1102,9 @@ Vector2i LineEdit::get_cursor_pixel_pos() {
TextServer::Direction l_dir, t_dir;
// Get position of the start of caret.
if (ime_text.length() != 0 && ime_selection.x != 0) {
- TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x, l_caret, l_dir, t_caret, t_dir);
+ TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x, l_caret, l_dir, t_caret, t_dir);
} else {
- TS->shaped_text_get_carets(text_rid, cursor_pos, l_caret, l_dir, t_caret, t_dir);
+ TS->shaped_text_get_carets(text_rid, caret_column, l_caret, l_dir, t_caret, t_dir);
}
if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) {
@@ -1189,9 +1116,9 @@ Vector2i LineEdit::get_cursor_pixel_pos() {
// Get position of the end of caret.
if (ime_text.length() != 0) {
if (ime_selection.y != 0) {
- TS->shaped_text_get_carets(text_rid, cursor_pos + ime_selection.x + ime_selection.y, l_caret, l_dir, t_caret, t_dir);
+ TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x + ime_selection.y, l_caret, l_dir, t_caret, t_dir);
} else {
- TS->shaped_text_get_carets(text_rid, cursor_pos + ime_text.size(), l_caret, l_dir, t_caret, t_dir);
+ TS->shaped_text_get_carets(text_rid, caret_column + ime_text.size(), l_caret, l_dir, t_caret, t_dir);
}
if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) {
ret.y = x_ofs + l_caret.position.x + scroll_offset;
@@ -1205,19 +1132,19 @@ Vector2i LineEdit::get_cursor_pixel_pos() {
return ret;
}
-void LineEdit::set_mid_grapheme_caret_enabled(const bool p_enabled) {
- mid_grapheme_caret_enabled = p_enabled;
+void LineEdit::set_caret_mid_grapheme_enabled(const bool p_enabled) {
+ caret_mid_grapheme_enabled = p_enabled;
}
-bool LineEdit::get_mid_grapheme_caret_enabled() const {
- return mid_grapheme_caret_enabled;
+bool LineEdit::is_caret_mid_grapheme_enabled() const {
+ return caret_mid_grapheme_enabled;
}
-bool LineEdit::cursor_get_blink_enabled() const {
+bool LineEdit::is_caret_blink_enabled() const {
return caret_blink_enabled;
}
-void LineEdit::cursor_set_blink_enabled(const bool p_enabled) {
+void LineEdit::set_caret_blink_enabled(const bool p_enabled) {
caret_blink_enabled = p_enabled;
if (has_focus() || caret_force_displayed) {
@@ -1231,23 +1158,25 @@ void LineEdit::cursor_set_blink_enabled(const bool p_enabled) {
}
draw_caret = true;
+
+ notify_property_list_changed();
}
-bool LineEdit::cursor_get_force_displayed() const {
+bool LineEdit::is_caret_force_displayed() const {
return caret_force_displayed;
}
-void LineEdit::cursor_set_force_displayed(const bool p_enabled) {
+void LineEdit::set_caret_force_displayed(const bool p_enabled) {
caret_force_displayed = p_enabled;
- cursor_set_blink_enabled(caret_blink_enabled);
+ set_caret_blink_enabled(caret_blink_enabled);
update();
}
-float LineEdit::cursor_get_blink_speed() const {
+float LineEdit::get_caret_blink_speed() const {
return caret_blink_timer->get_wait_time();
}
-void LineEdit::cursor_set_blink_speed(const float p_speed) {
+void LineEdit::set_caret_blink_speed(const float p_speed) {
ERR_FAIL_COND(p_speed <= 0);
caret_blink_timer->set_wait_time(p_speed);
}
@@ -1271,14 +1200,14 @@ void LineEdit::_toggle_draw_caret() {
}
void LineEdit::delete_char() {
- if ((text.length() <= 0) || (cursor_pos == 0)) {
+ if ((text.length() <= 0) || (caret_column == 0)) {
return;
}
- text.erase(cursor_pos - 1, 1);
+ text.erase(caret_column - 1, 1);
_shape();
- set_cursor_position(get_cursor_position() - 1);
+ set_caret_column(get_caret_column() - 1);
_text_changed();
}
@@ -1290,10 +1219,10 @@ void LineEdit::delete_text(int p_from_column, int p_to_column) {
text.erase(p_from_column, p_to_column - p_from_column);
_shape();
- cursor_pos -= CLAMP(cursor_pos - p_from_column, 0, p_to_column - p_from_column);
+ caret_column -= CLAMP(caret_column - p_from_column, 0, p_to_column - p_from_column);
- if (cursor_pos >= text.length()) {
- cursor_pos = text.length();
+ if (caret_column >= text.length()) {
+ caret_column = text.length();
}
if (!text_changed_dirty) {
@@ -1306,10 +1235,11 @@ void LineEdit::delete_text(int p_from_column, int p_to_column) {
void LineEdit::set_text(String p_text) {
clear_internal();
- append_at_cursor(p_text);
+ insert_text_at_caret(p_text);
+ _create_undo_state();
update();
- cursor_pos = 0;
+ caret_column = 0;
scroll_offset = 0;
}
@@ -1407,6 +1337,21 @@ Array LineEdit::get_structured_text_bidi_override_options() const {
void LineEdit::clear() {
clear_internal();
_text_changed();
+
+ // This should reset virtual keyboard state if needed.
+ if (has_focus()) {
+ show_virtual_keyboard();
+ }
+}
+
+void LineEdit::show_virtual_keyboard() {
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
+ if (selection.enabled) {
+ DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, selection.begin, selection.end);
+ } else {
+ DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, caret_column);
+ }
+ }
}
String LineEdit::get_text() const {
@@ -1433,16 +1378,16 @@ float LineEdit::get_placeholder_alpha() const {
return placeholder_alpha;
}
-void LineEdit::set_cursor_position(int p_pos) {
- if (p_pos > (int)text.length()) {
- p_pos = text.length();
+void LineEdit::set_caret_column(int p_column) {
+ if (p_column > (int)text.length()) {
+ p_column = text.length();
}
- if (p_pos < 0) {
- p_pos = 0;
+ if (p_column < 0) {
+ p_column = 0;
}
- cursor_pos = p_pos;
+ caret_column = p_column;
// Fit to window.
@@ -1496,8 +1441,8 @@ void LineEdit::set_cursor_position(int p_pos) {
ofs_max -= r_icon->get_width();
}
- // Note: Use too coordinates to fit IME input range.
- Vector2i primary_catret_offset = get_cursor_pixel_pos();
+ // Note: Use two coordinates to fit IME input range.
+ Vector2i primary_catret_offset = get_caret_pixel_pos();
if (MIN(primary_catret_offset.x, primary_catret_offset.y) <= x_ofs) {
scroll_offset += (x_ofs - MIN(primary_catret_offset.x, primary_catret_offset.y));
@@ -1509,8 +1454,8 @@ void LineEdit::set_cursor_position(int p_pos) {
update();
}
-int LineEdit::get_cursor_position() const {
- return cursor_pos;
+int LineEdit::get_caret_column() const {
+ return caret_column;
}
void LineEdit::set_scroll_offset(int p_pos) {
@@ -1524,17 +1469,17 @@ int LineEdit::get_scroll_offset() const {
return scroll_offset;
}
-void LineEdit::append_at_cursor(String p_text) {
+void LineEdit::insert_text_at_caret(String p_text) {
if ((max_length <= 0) || (text.length() + p_text.length() <= max_length)) {
- String pre = text.substr(0, cursor_pos);
- String post = text.substr(cursor_pos, text.length() - cursor_pos);
+ String pre = text.substr(0, caret_column);
+ String post = text.substr(caret_column, text.length() - caret_column);
text = pre + p_text + post;
_shape();
- TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, cursor_pos, cursor_pos + p_text.length());
+ TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, caret_column, caret_column + p_text.length());
if (dir != TextServer::DIRECTION_AUTO) {
input_direction = (TextDirection)dir;
}
- set_cursor_position(cursor_pos + p_text.length());
+ set_caret_column(caret_column + p_text.length());
} else {
emit_signal("text_change_rejected");
}
@@ -1543,7 +1488,7 @@ void LineEdit::append_at_cursor(String p_text) {
void LineEdit::clear_internal() {
deselect();
_clear_undo_stack();
- cursor_pos = 0;
+ caret_column = 0;
scroll_offset = 0;
undo_text = "";
text = "";
@@ -1559,12 +1504,12 @@ Size2 LineEdit::get_minimum_size() const {
Size2 min_size;
// Minimum size of text.
- int space_size = font->get_char_size('m', 0, font_size).x;
- min_size.width = get_theme_constant("minimum_spaces") * space_size;
+ int em_space_size = font->get_char_size('M', 0, font_size).x;
+ min_size.width = get_theme_constant("minimum_character_width") * em_space_size;
if (expand_to_text_length) {
- // Add a space because some fonts are too exact, and because cursor needs a bit more when at the end.
- min_size.width = MAX(min_size.width, full_width + space_size);
+ // Add a space because some fonts are too exact, and because caret needs a bit more when at the end.
+ min_size.width = MAX(min_size.width, full_width + em_space_size);
}
min_size.height = MAX(TS->shaped_text_get_size(text_rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM), font->get_height(font_size));
@@ -1584,10 +1529,10 @@ Size2 LineEdit::get_minimum_size() const {
void LineEdit::deselect() {
selection.begin = 0;
selection.end = 0;
- selection.cursor_start = 0;
+ selection.start_column = 0;
selection.enabled = false;
selection.creating = false;
- selection.doubleclick = false;
+ selection.double_click = false;
update();
}
@@ -1609,13 +1554,13 @@ int LineEdit::get_max_length() const {
return max_length;
}
-void LineEdit::selection_fill_at_cursor() {
+void LineEdit::selection_fill_at_caret() {
if (!selecting_enabled) {
return;
}
- selection.begin = cursor_pos;
- selection.end = selection.cursor_start;
+ selection.begin = caret_column;
+ selection.end = selection.start_column;
if (selection.end < selection.begin) {
int aux = selection.end;
@@ -1714,7 +1659,7 @@ void LineEdit::select(int p_from, int p_to) {
selection.begin = p_from;
selection.end = p_to;
selection.creating = false;
- selection.doubleclick = false;
+ selection.double_click = false;
update();
}
@@ -1772,82 +1717,82 @@ void LineEdit::menu_option(int p_option) {
} break;
case MENU_INSERT_LRM: {
if (editable) {
- append_at_cursor(String::chr(0x200E));
+ insert_text_at_caret(String::chr(0x200E));
}
} break;
case MENU_INSERT_RLM: {
if (editable) {
- append_at_cursor(String::chr(0x200F));
+ insert_text_at_caret(String::chr(0x200F));
}
} break;
case MENU_INSERT_LRE: {
if (editable) {
- append_at_cursor(String::chr(0x202A));
+ insert_text_at_caret(String::chr(0x202A));
}
} break;
case MENU_INSERT_RLE: {
if (editable) {
- append_at_cursor(String::chr(0x202B));
+ insert_text_at_caret(String::chr(0x202B));
}
} break;
case MENU_INSERT_LRO: {
if (editable) {
- append_at_cursor(String::chr(0x202D));
+ insert_text_at_caret(String::chr(0x202D));
}
} break;
case MENU_INSERT_RLO: {
if (editable) {
- append_at_cursor(String::chr(0x202E));
+ insert_text_at_caret(String::chr(0x202E));
}
} break;
case MENU_INSERT_PDF: {
if (editable) {
- append_at_cursor(String::chr(0x202C));
+ insert_text_at_caret(String::chr(0x202C));
}
} break;
case MENU_INSERT_ALM: {
if (editable) {
- append_at_cursor(String::chr(0x061C));
+ insert_text_at_caret(String::chr(0x061C));
}
} break;
case MENU_INSERT_LRI: {
if (editable) {
- append_at_cursor(String::chr(0x2066));
+ insert_text_at_caret(String::chr(0x2066));
}
} break;
case MENU_INSERT_RLI: {
if (editable) {
- append_at_cursor(String::chr(0x2067));
+ insert_text_at_caret(String::chr(0x2067));
}
} break;
case MENU_INSERT_FSI: {
if (editable) {
- append_at_cursor(String::chr(0x2068));
+ insert_text_at_caret(String::chr(0x2068));
}
} break;
case MENU_INSERT_PDI: {
if (editable) {
- append_at_cursor(String::chr(0x2069));
+ insert_text_at_caret(String::chr(0x2069));
}
} break;
case MENU_INSERT_ZWJ: {
if (editable) {
- append_at_cursor(String::chr(0x200D));
+ insert_text_at_caret(String::chr(0x200D));
}
} break;
case MENU_INSERT_ZWNJ: {
if (editable) {
- append_at_cursor(String::chr(0x200C));
+ insert_text_at_caret(String::chr(0x200C));
}
} break;
case MENU_INSERT_WJ: {
if (editable) {
- append_at_cursor(String::chr(0x2060));
+ insert_text_at_caret(String::chr(0x2060));
}
} break;
case MENU_INSERT_SHY: {
if (editable) {
- append_at_cursor(String::chr(0x00AD));
+ insert_text_at_caret(String::chr(0x00AD));
}
}
}
@@ -1867,18 +1812,18 @@ PopupMenu *LineEdit::get_menu() const {
void LineEdit::_editor_settings_changed() {
#ifdef TOOLS_ENABLED
- cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false));
- cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65));
+ set_caret_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false));
+ set_caret_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65));
#endif
}
-void LineEdit::set_expand_to_text_length(bool p_enabled) {
+void LineEdit::set_expand_to_text_length_enabled(bool p_enabled) {
expand_to_text_length = p_enabled;
minimum_size_changed();
- set_cursor_position(cursor_pos);
+ set_caret_column(caret_column);
}
-bool LineEdit::get_expand_to_text_length() const {
+bool LineEdit::is_expand_to_text_length_enabled() const {
return expand_to_text_length;
}
@@ -1949,7 +1894,6 @@ void LineEdit::_text_changed() {
void LineEdit::_emit_text_change() {
emit_signal("text_changed", text);
- _change_notify("text");
text_changed_dirty = false;
}
@@ -1964,7 +1908,7 @@ void LineEdit::_shape() {
t = secret_character.repeat(text.length() + ime_text.length());
} else {
if (ime_text.length() > 0) {
- t = text.substr(0, cursor_pos) + ime_text + text.substr(cursor_pos, text.length());
+ t = text.substr(0, caret_column) + ime_text + text.substr(caret_column, text.length());
} else {
t = text;
}
@@ -2029,30 +1973,55 @@ void LineEdit::_clear_undo_stack() {
void LineEdit::_create_undo_state() {
TextOperation op;
op.text = text;
- op.cursor_pos = cursor_pos;
+ op.caret_column = caret_column;
op.scroll_offset = scroll_offset;
undo_stack.push_back(op);
}
+int LineEdit::_get_menu_action_accelerator(const String &p_action) {
+ const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action);
+ if (!events) {
+ return 0;
+ }
+
+ // Use first event in the list for the accelerator.
+ const List<Ref<InputEvent>>::Element *first_event = events->front();
+ if (!first_event) {
+ return 0;
+ }
+
+ const Ref<InputEventKey> event = first_event->get();
+ if (event.is_null()) {
+ return 0;
+ }
+
+ // Use physical keycode if non-zero
+ if (event->get_physical_keycode() != 0) {
+ return event->get_physical_keycode_with_modifiers();
+ } else {
+ return event->get_keycode_with_modifiers();
+ }
+}
+
void LineEdit::_generate_context_menu() {
// Reorganize context menu.
menu->clear();
if (editable) {
- menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_X : 0);
+ menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : 0);
}
- menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_C : 0);
+ menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : 0);
if (editable) {
- menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_V : 0);
+ menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : 0);
}
menu->add_separator();
if (is_selecting_enabled()) {
- menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_A : 0);
+ menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : 0);
}
if (editable) {
menu->add_item(RTR("Clear"), MENU_CLEAR);
menu->add_separator();
- menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_Z : 0);
- menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z : 0);
+ menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : 0);
+ menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : 0);
}
menu->add_separator();
menu->add_submenu_item(RTR("Text writing direction"), "DirMenu");
@@ -2082,7 +2051,7 @@ bool LineEdit::_set(const StringName &p_name, const Variant &p_value) {
update();
}
}
- _change_notify();
+ notify_property_list_changed();
return true;
}
@@ -2113,6 +2082,12 @@ void LineEdit::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}
+void LineEdit::_validate_property(PropertyInfo &property) const {
+ if (!caret_blink_enabled && property.name == "caret_blink_speed") {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+}
+
void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("_text_changed"), &LineEdit::_text_changed);
@@ -2143,23 +2118,23 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_placeholder"), &LineEdit::get_placeholder);
ClassDB::bind_method(D_METHOD("set_placeholder_alpha", "alpha"), &LineEdit::set_placeholder_alpha);
ClassDB::bind_method(D_METHOD("get_placeholder_alpha"), &LineEdit::get_placeholder_alpha);
- ClassDB::bind_method(D_METHOD("set_cursor_position", "position"), &LineEdit::set_cursor_position);
- ClassDB::bind_method(D_METHOD("get_cursor_position"), &LineEdit::get_cursor_position);
+ ClassDB::bind_method(D_METHOD("set_caret_column", "position"), &LineEdit::set_caret_column);
+ ClassDB::bind_method(D_METHOD("get_caret_column"), &LineEdit::get_caret_column);
ClassDB::bind_method(D_METHOD("get_scroll_offset"), &LineEdit::get_scroll_offset);
- ClassDB::bind_method(D_METHOD("set_expand_to_text_length", "enabled"), &LineEdit::set_expand_to_text_length);
- ClassDB::bind_method(D_METHOD("get_expand_to_text_length"), &LineEdit::get_expand_to_text_length);
- ClassDB::bind_method(D_METHOD("cursor_set_blink_enabled", "enabled"), &LineEdit::cursor_set_blink_enabled);
- ClassDB::bind_method(D_METHOD("cursor_get_blink_enabled"), &LineEdit::cursor_get_blink_enabled);
- ClassDB::bind_method(D_METHOD("set_mid_grapheme_caret_enabled", "enabled"), &LineEdit::set_mid_grapheme_caret_enabled);
- ClassDB::bind_method(D_METHOD("get_mid_grapheme_caret_enabled"), &LineEdit::get_mid_grapheme_caret_enabled);
- ClassDB::bind_method(D_METHOD("cursor_set_force_displayed", "enabled"), &LineEdit::cursor_set_force_displayed);
- ClassDB::bind_method(D_METHOD("cursor_get_force_displayed"), &LineEdit::cursor_get_force_displayed);
- ClassDB::bind_method(D_METHOD("cursor_set_blink_speed", "blink_speed"), &LineEdit::cursor_set_blink_speed);
- ClassDB::bind_method(D_METHOD("cursor_get_blink_speed"), &LineEdit::cursor_get_blink_speed);
+ ClassDB::bind_method(D_METHOD("set_expand_to_text_length_enabled", "enabled"), &LineEdit::set_expand_to_text_length_enabled);
+ ClassDB::bind_method(D_METHOD("is_expand_to_text_length_enabled"), &LineEdit::is_expand_to_text_length_enabled);
+ ClassDB::bind_method(D_METHOD("set_caret_blink_enabled", "enabled"), &LineEdit::set_caret_blink_enabled);
+ ClassDB::bind_method(D_METHOD("is_caret_blink_enabled"), &LineEdit::is_caret_blink_enabled);
+ ClassDB::bind_method(D_METHOD("set_caret_mid_grapheme_enabled", "enabled"), &LineEdit::set_caret_mid_grapheme_enabled);
+ ClassDB::bind_method(D_METHOD("is_caret_mid_grapheme_enabled"), &LineEdit::is_caret_mid_grapheme_enabled);
+ ClassDB::bind_method(D_METHOD("set_caret_force_displayed", "enabled"), &LineEdit::set_caret_force_displayed);
+ ClassDB::bind_method(D_METHOD("is_caret_force_displayed"), &LineEdit::is_caret_force_displayed);
+ ClassDB::bind_method(D_METHOD("set_caret_blink_speed", "blink_speed"), &LineEdit::set_caret_blink_speed);
+ ClassDB::bind_method(D_METHOD("get_caret_blink_speed"), &LineEdit::get_caret_blink_speed);
ClassDB::bind_method(D_METHOD("set_max_length", "chars"), &LineEdit::set_max_length);
ClassDB::bind_method(D_METHOD("get_max_length"), &LineEdit::get_max_length);
- ClassDB::bind_method(D_METHOD("append_at_cursor", "text"), &LineEdit::append_at_cursor);
- ClassDB::bind_method(D_METHOD("delete_char_at_cursor"), &LineEdit::delete_char);
+ ClassDB::bind_method(D_METHOD("insert_text_at_caret", "text"), &LineEdit::insert_text_at_caret);
+ ClassDB::bind_method(D_METHOD("delete_char_at_caret"), &LineEdit::delete_char);
ClassDB::bind_method(D_METHOD("delete_text", "from_column", "to_column"), &LineEdit::delete_text);
ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &LineEdit::set_editable);
ClassDB::bind_method(D_METHOD("is_editable"), &LineEdit::is_editable);
@@ -2227,7 +2202,7 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length_enabled", "is_expand_to_text_length_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled");
@@ -2244,20 +2219,17 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha");
ADD_GROUP("Caret", "caret_");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_position"), "set_cursor_position", "get_cursor_position");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "cursor_set_force_displayed", "cursor_get_force_displayed");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_mid_grapheme_caret_enabled", "get_mid_grapheme_caret_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_column"), "set_caret_column", "get_caret_column");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "set_caret_force_displayed", "is_caret_force_displayed");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled");
}
LineEdit::LineEdit() {
text_rid = TS->create_shaped_text();
_create_undo_state();
- clear_button_status.press_attempt = false;
- clear_button_status.pressing_inside = false;
-
deselect();
set_focus_mode(FOCUS_ALL);
set_default_cursor_shape(CURSOR_IBEAM);
@@ -2267,7 +2239,7 @@ LineEdit::LineEdit() {
add_child(caret_blink_timer);
caret_blink_timer->set_wait_time(0.65);
caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret));
- cursor_set_blink_enabled(false);
+ set_caret_blink_enabled(false);
menu = memnew(PopupMenu);
add_child(menu);
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 1e7495e734..12fec2f98b 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -94,7 +94,7 @@ private:
Point2 ime_selection;
RID text_rid;
- float full_width = 0;
+ float full_width = 0.0;
bool selecting_enabled = true;
@@ -103,9 +103,9 @@ private:
PopupMenu *menu_dir = nullptr;
PopupMenu *menu_ctl = nullptr;
- bool mid_grapheme_caret_enabled = false;
+ bool caret_mid_grapheme_enabled = false;
- int cursor_pos = 0;
+ int caret_column = 0;
int scroll_offset = 0;
int max_length = 0; // 0 for no maximum.
@@ -129,19 +129,20 @@ private:
Ref<Texture2D> right_icon;
struct Selection {
- int begin;
- int end;
- int cursor_start;
- bool enabled;
- bool creating;
- bool doubleclick;
- bool drag_attempt;
+ int begin = 0;
+ int end = 0;
+ int start_column = 0;
+ bool enabled = false;
+ bool creating = false;
+ bool double_click = false;
+ bool drag_attempt = false;
+ uint64_t last_dblclk = 0;
} selection;
struct TextOperation {
- int cursor_pos;
- int scroll_offset;
- int cached_width;
+ int caret_column = 0;
+ int scroll_offset = 0;
+ int cached_width = 0;
String text;
};
List<TextOperation> undo_stack;
@@ -163,6 +164,7 @@ private:
void _clear_redo();
void _create_undo_state();
+ int _get_menu_action_accelerator(const String &p_action);
void _generate_context_menu();
void _shape();
@@ -173,12 +175,12 @@ private:
void shift_selection_check_pre(bool);
void shift_selection_check_post(bool);
- void selection_fill_at_cursor();
+ void selection_fill_at_caret();
void set_scroll_offset(int p_pos);
int get_scroll_offset() const;
- void set_cursor_at_pixel_pos(int p_x);
- Vector2i get_cursor_pixel_pos();
+ void set_caret_at_pixel_pos(int p_x);
+ Vector2i get_caret_pixel_pos();
void _reset_caret_blink_timer();
void _toggle_draw_caret();
@@ -188,15 +190,23 @@ private:
void _editor_settings_changed();
- void _gui_input(Ref<InputEvent> p_event);
- void _notification(int p_what);
+ void _swap_current_input_direction();
+ void _move_caret_left(bool p_select, bool p_move_by_word = false);
+ void _move_caret_right(bool p_select, bool p_move_by_word = false);
+ void _move_caret_start(bool p_select);
+ void _move_caret_end(bool p_select);
+ void _backspace(bool p_word = false, bool p_all_to_left = false);
+ void _delete(bool p_word = false, bool p_all_to_right = false);
protected:
+ void _notification(int p_what);
static void _bind_methods();
+ void _gui_input(Ref<InputEvent> p_event);
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;
+ void _validate_property(PropertyInfo &property) const override;
public:
void set_align(Align p_align);
@@ -249,26 +259,26 @@ public:
void set_placeholder_alpha(float p_alpha);
float get_placeholder_alpha() const;
- void set_cursor_position(int p_pos);
- int get_cursor_position() const;
+ void set_caret_column(int p_column);
+ int get_caret_column() const;
void set_max_length(int p_max_length);
int get_max_length() const;
- void append_at_cursor(String p_text);
+ void insert_text_at_caret(String p_text);
void clear();
- void set_mid_grapheme_caret_enabled(const bool p_enabled);
- bool get_mid_grapheme_caret_enabled() const;
+ void set_caret_mid_grapheme_enabled(const bool p_enabled);
+ bool is_caret_mid_grapheme_enabled() const;
- bool cursor_get_blink_enabled() const;
- void cursor_set_blink_enabled(const bool p_enabled);
+ bool is_caret_blink_enabled() const;
+ void set_caret_blink_enabled(const bool p_enabled);
- float cursor_get_blink_speed() const;
- void cursor_set_blink_speed(const float p_speed);
+ float get_caret_blink_speed() const;
+ void set_caret_blink_speed(const float p_speed);
- bool cursor_get_force_displayed() const;
- void cursor_set_force_displayed(const bool p_enabled);
+ void set_caret_force_displayed(const bool p_enabled);
+ bool is_caret_force_displayed() const;
void copy_text();
void cut_text();
@@ -287,8 +297,8 @@ public:
virtual Size2 get_minimum_size() const override;
- void set_expand_to_text_length(bool p_enabled);
- bool get_expand_to_text_length() const;
+ void set_expand_to_text_length_enabled(bool p_enabled);
+ bool is_expand_to_text_length_enabled() const;
void set_clear_button_enabled(bool p_enabled);
bool is_clear_button_enabled() const;
@@ -306,6 +316,9 @@ public:
Ref<Texture2D> get_right_icon();
virtual bool is_text_field() const override;
+
+ void show_virtual_keyboard();
+
LineEdit();
~LineEdit();
};
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index 495529017a..1f7b61e3d1 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -163,8 +163,8 @@ void LinkButton::_notification(int p_what) {
} break;
case DRAW_HOVER_PRESSED:
case DRAW_PRESSED: {
- if (has_theme_color("font_color_pressed")) {
- color = get_theme_color("font_color_pressed");
+ if (has_theme_color("font_pressed_color")) {
+ color = get_theme_color("font_pressed_color");
} else {
color = get_theme_color("font_color");
}
@@ -173,12 +173,12 @@ void LinkButton::_notification(int p_what) {
} break;
case DRAW_HOVER: {
- color = get_theme_color("font_color_hover");
+ color = get_theme_color("font_hover_color");
do_underline = underline_mode != UNDERLINE_MODE_NEVER;
} break;
case DRAW_DISABLED: {
- color = get_theme_color("font_color_disabled");
+ color = get_theme_color("font_disabled_color");
do_underline = underline_mode == UNDERLINE_MODE_ALWAYS;
} break;
@@ -191,9 +191,17 @@ void LinkButton::_notification(int p_what) {
int width = text_buf->get_line_width();
+ Color font_outline_color = get_theme_color("font_outline_color");
+ int outline_size = get_theme_constant("outline_size");
if (is_layout_rtl()) {
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ text_buf->draw_outline(get_canvas_item(), Vector2(size.width - width, 0), outline_size, font_outline_color);
+ }
text_buf->draw(get_canvas_item(), Vector2(size.width - width, 0), color);
} else {
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ text_buf->draw_outline(get_canvas_item(), Vector2(0, 0), outline_size, font_outline_color);
+ }
text_buf->draw(get_canvas_item(), Vector2(0, 0), color);
}
@@ -231,7 +239,7 @@ bool LinkButton::_set(const StringName &p_name, const Variant &p_value) {
update();
}
}
- _change_notify();
+ notify_property_list_changed();
return true;
}
@@ -294,7 +302,6 @@ void LinkButton::_bind_methods() {
LinkButton::LinkButton() {
text_buf.instance();
- underline_mode = UNDERLINE_MODE_ALWAYS;
set_focus_mode(FOCUS_NONE);
set_default_cursor_shape(CURSOR_POINTING_HAND);
}
diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h
index 5ab6aba122..7eaa9f88b6 100644
--- a/scene/gui/link_button.h
+++ b/scene/gui/link_button.h
@@ -48,7 +48,7 @@ public:
private:
String text;
Ref<TextLine> text_buf;
- UnderlineMode underline_mode;
+ UnderlineMode underline_mode = UNDERLINE_MODE_ALWAYS;
Dictionary opentype_features;
String language;
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index 94cb5ef0f4..1e9baa77fc 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -34,6 +34,8 @@
#include "scene/main/window.h"
void MenuButton::_unhandled_key_input(Ref<InputEvent> p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
if (!_is_focus_owner_in_shorcut_context()) {
return;
}
@@ -118,7 +120,6 @@ void MenuButton::set_disable_shortcuts(bool p_disabled) {
}
MenuButton::MenuButton() {
- switch_on_hover = false;
set_flat(true);
set_toggle_mode(true);
set_disable_shortcuts(false);
diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h
index cd4ed5bcb2..fd9ae6021e 100644
--- a/scene/gui/menu_button.h
+++ b/scene/gui/menu_button.h
@@ -37,9 +37,9 @@
class MenuButton : public Button {
GDCLASS(MenuButton, Button);
- bool clicked;
- bool switch_on_hover;
- bool disable_shortcuts;
+ bool clicked = false;
+ bool switch_on_hover = false;
+ bool disable_shortcuts = false;
PopupMenu *popup;
Array _get_items() const;
diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp
index c274eb5c10..29a38ad5e3 100644
--- a/scene/gui/nine_patch_rect.cpp
+++ b/scene/gui/nine_patch_rect.cpp
@@ -98,7 +98,6 @@ void NinePatchRect::set_texture(const Ref<Texture2D> &p_tex) {
*/
minimum_size_changed();
emit_signal("texture_changed");
- _change_notify("texture");
}
Ref<Texture2D> NinePatchRect::get_texture() const {
@@ -110,20 +109,6 @@ void NinePatchRect::set_patch_margin(Side p_side, int p_size) {
margin[p_side] = p_size;
update();
minimum_size_changed();
- switch (p_side) {
- case SIDE_LEFT:
- _change_notify("patch_margin_left");
- break;
- case SIDE_TOP:
- _change_notify("patch_margin_top");
- break;
- case SIDE_RIGHT:
- _change_notify("patch_margin_right");
- break;
- case SIDE_BOTTOM:
- _change_notify("patch_margin_bottom");
- break;
- }
}
int NinePatchRect::get_patch_margin(Side p_side) const {
@@ -139,7 +124,6 @@ void NinePatchRect::set_region_rect(const Rect2 &p_region_rect) {
region_rect = p_region_rect;
item_rect_changed();
- _change_notify("region_rect");
}
Rect2 NinePatchRect::get_region_rect() const {
@@ -174,16 +158,7 @@ NinePatchRect::AxisStretchMode NinePatchRect::get_v_axis_stretch_mode() const {
}
NinePatchRect::NinePatchRect() {
- margin[SIDE_LEFT] = 0;
- margin[SIDE_RIGHT] = 0;
- margin[SIDE_BOTTOM] = 0;
- margin[SIDE_TOP] = 0;
-
set_mouse_filter(MOUSE_FILTER_IGNORE);
- draw_center = true;
-
- axis_h = AXIS_STRETCH_MODE_STRETCH;
- axis_v = AXIS_STRETCH_MODE_STRETCH;
}
NinePatchRect::~NinePatchRect() {
diff --git a/scene/gui/nine_patch_rect.h b/scene/gui/nine_patch_rect.h
index fcf25018aa..f9a3f31fe5 100644
--- a/scene/gui/nine_patch_rect.h
+++ b/scene/gui/nine_patch_rect.h
@@ -43,12 +43,13 @@ public:
AXIS_STRETCH_MODE_TILE_FIT,
};
- bool draw_center;
- int margin[4];
+ bool draw_center = true;
+ int margin[4] = {};
Rect2 region_rect;
Ref<Texture2D> texture;
- AxisStretchMode axis_h, axis_v;
+ AxisStretchMode axis_h = AXIS_STRETCH_MODE_STRETCH;
+ AxisStretchMode axis_v = AXIS_STRETCH_MODE_STRETCH;
protected:
void _notification(int p_what);
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 4f274595a2..e52b6917be 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -62,13 +62,13 @@ void OptionButton::_notification(int p_what) {
if (get_theme_constant("modulate_arrow")) {
switch (get_draw_mode()) {
case DRAW_PRESSED:
- clr = get_theme_color("font_color_pressed");
+ clr = get_theme_color("font_pressed_color");
break;
case DRAW_HOVER:
- clr = get_theme_color("font_color_hover");
+ clr = get_theme_color("font_hover_color");
break;
case DRAW_DISABLED:
- clr = get_theme_color("font_color_disabled");
+ clr = get_theme_color("font_disabled_color");
break;
default:
clr = get_theme_color("font_color");
@@ -336,7 +336,6 @@ void OptionButton::_bind_methods() {
}
OptionButton::OptionButton() {
- current = -1;
set_toggle_mode(true);
set_text_align(ALIGN_LEFT);
if (is_layout_rtl()) {
diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h
index 9cb296baa9..d846e395ad 100644
--- a/scene/gui/option_button.h
+++ b/scene/gui/option_button.h
@@ -38,7 +38,7 @@ class OptionButton : public Button {
GDCLASS(OptionButton, Button);
PopupMenu *popup;
- int current;
+ int current = -1;
void _focused(int p_which);
void _selected(int p_which);
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index e777e6c26b..44df8eafdc 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -52,8 +52,8 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
Size2 minsize = get_theme_stylebox("panel")->get_minimum_size(); // Accounts for margin in the margin container
minsize.x += scroll_container->get_v_scrollbar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content
- float max_w = 0;
- float icon_w = 0;
+ float max_w = 0.0;
+ float icon_w = 0.0;
int check_w = MAX(get_theme_icon("checked")->get_width(), get_theme_icon("radio_checked")->get_width()) + hseparation;
int accel_max_w = 0;
bool has_check = false;
@@ -62,7 +62,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
Size2 size;
Size2 icon_size = items[i].get_icon_size();
- size.height = MAX(icon_size.height, items[i].text_buf->get_size().y);
+ size.height = _get_item_height(i);
icon_w = MAX(icon_size.width, icon_w);
size.width += items[i].h_ofs;
@@ -89,7 +89,9 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
minsize.height += size.height;
}
- minsize.width += max_w + icon_w + accel_max_w;
+ int item_side_padding = get_theme_constant("item_start_padding") + get_theme_constant("item_end_padding");
+ minsize.width += max_w + icon_w + accel_max_w + item_side_padding;
+
if (has_check) {
minsize.width += check_w;
}
@@ -104,13 +106,35 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
return minsize;
}
+int PopupMenu::_get_item_height(int p_item) const {
+ ERR_FAIL_INDEX_V(p_item, items.size(), 0);
+ ERR_FAIL_COND_V(p_item < 0, 0);
+
+ int icon_height = items[p_item].get_icon_size().height;
+ if (items[p_item].checkable_type) {
+ icon_height = MAX(icon_height, MAX(get_theme_icon("checked")->get_height(), get_theme_icon("radio_checked")->get_height()));
+ }
+
+ int text_height = items[p_item].text_buf->get_size().height;
+ if (text_height == 0 && !items[p_item].separator) {
+ text_height = get_theme_font("font")->get_height(get_theme_font_size("font_size"));
+ }
+
+ int separator_height = 0;
+ if (items[p_item].separator) {
+ separator_height = MAX(get_theme_stylebox("separator")->get_minimum_size().height, MAX(get_theme_stylebox("labeled_separator_left")->get_minimum_size().height, get_theme_stylebox("labeled_separator_right")->get_minimum_size().height));
+ }
+
+ return MAX(separator_height, MAX(text_height, icon_height));
+}
+
int PopupMenu::_get_items_total_height() const {
int vsep = get_theme_constant("vseparation");
// Get total height of all items by taking max of icon height and font height
int items_total_height = 0;
for (int i = 0; i < items.size(); i++) {
- items_total_height += MAX(items[i].get_icon_size().height, items[i].text_buf->get_size().y) + vsep;
+ items_total_height += _get_item_height(i) + vsep;
}
// Subtract a separator which is not needed for the last item.
@@ -152,7 +176,7 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
for (int i = 0; i < items.size(); i++) {
ofs.y += i > 0 ? vseparation : (float)vseparation / 2;
- ofs.y += MAX(items[i].get_icon_size().height, items[i].text_buf->get_size().y);
+ ofs.y += _get_item_height(i);
if (p_over.y - control->get_position().y < ofs.y) {
return i;
@@ -198,7 +222,7 @@ void PopupMenu::_activate_submenu(int p_over) {
submenu_popup->set_close_on_parent_focus(false);
submenu_popup->set_position(submenu_pos);
- submenu_popup->set_as_minsize(); // Shrink the popup size to it's contents.
+ submenu_popup->set_as_minsize(); // Shrink the popup size to its contents.
submenu_popup->popup();
// Set autohide areas
@@ -228,6 +252,8 @@ void PopupMenu::_submenu_timeout() {
}
void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
if (p_event->is_action("ui_down") && p_event->is_pressed()) {
int search_from = mouse_over + 1;
if (search_from >= items.size()) {
@@ -335,7 +361,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
if (b->is_pressed() || (!b->is_pressed() && during_grabbed_click)) {
// Allow activating item by releasing the LMB or any that was down when the popup appeared.
// However, if button was not held when opening menu, do not allow release to activate item.
- if (button_idx == BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) {
+ if (button_idx == MOUSE_BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) {
bool was_during_grabbed_click = during_grabbed_click;
during_grabbed_click = false;
initial_button_mask = 0;
@@ -451,6 +477,10 @@ void PopupMenu::_draw_items() {
margin_size.width = margin_container->get_theme_constant("margin_right") + margin_container->get_theme_constant("margin_left");
margin_size.height = margin_container->get_theme_constant("margin_top") + margin_container->get_theme_constant("margin_bottom");
+ // Space between the item content and the sides of popup menu.
+ int item_start_padding = get_theme_constant("item_start_padding");
+ int item_end_padding = get_theme_constant("item_end_padding");
+
bool rtl = control->is_layout_rtl();
Ref<StyleBox> style = get_theme_stylebox("panel");
Ref<StyleBox> hover = get_theme_stylebox("hover");
@@ -471,10 +501,10 @@ void PopupMenu::_draw_items() {
int vseparation = get_theme_constant("vseparation");
int hseparation = get_theme_constant("hseparation");
Color font_color = get_theme_color("font_color");
- Color font_color_disabled = get_theme_color("font_color_disabled");
- Color font_color_accel = get_theme_color("font_color_accel");
- Color font_color_hover = get_theme_color("font_color_hover");
- Color font_color_separator = get_theme_color("font_color_separator");
+ Color font_disabled_color = get_theme_color("font_disabled_color");
+ Color font_accelerator_color = get_theme_color("font_accelerator_color");
+ Color font_hover_color = get_theme_color("font_hover_color");
+ Color font_separator_color = get_theme_color("font_separator_color");
float scroll_width = scroll_container->get_v_scrollbar()->is_visible_in_tree() ? scroll_container->get_v_scrollbar()->get_size().width : 0;
float display_width = control->get_size().width - scroll_width;
@@ -509,7 +539,7 @@ void PopupMenu::_draw_items() {
Point2 item_ofs = ofs;
Size2 icon_size = items[i].get_icon_size();
- float h = MAX(icon_size.height, items[i].text_buf->get_size().y);
+ float h = _get_item_height(i);
if (i == mouse_over) {
if (rtl) {
@@ -525,24 +555,28 @@ void PopupMenu::_draw_items() {
item_ofs.x += items[i].h_ofs;
if (items[i].separator) {
int sep_h = separator->get_center_size().height + separator->get_minimum_size().height;
+ int sep_ofs = Math::floor((h - sep_h) / 2.0);
if (text != String()) {
int text_size = items[i].text_buf->get_size().width;
int text_center = display_width / 2;
int text_left = text_center - text_size / 2;
int text_right = text_center + text_size / 2;
if (text_left > item_ofs.x) {
- labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, text_left - item_ofs.x), sep_h)));
+ labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, text_left - item_ofs.x), sep_h)));
}
if (text_right < display_width) {
- labeled_separator_right->draw(ci, Rect2(Point2(text_right, item_ofs.y + Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, display_width - text_right), sep_h)));
+ labeled_separator_right->draw(ci, Rect2(Point2(text_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - text_right), sep_h)));
}
} else {
- separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(display_width, sep_h)));
+ separator->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(display_width, sep_h)));
}
}
Color icon_color(1, 1, 1, items[i].disabled ? 0.5 : 1);
+ // For non-separator items, add some padding for the content.
+ item_ofs.x += item_start_padding;
+
// Checkboxes
if (items[i].checkable_type) {
Texture2D *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr();
@@ -565,35 +599,53 @@ void PopupMenu::_draw_items() {
// Submenu arrow on right hand side
if (items[i].submenu != "") {
if (rtl) {
- submenu->draw(ci, Point2(scroll_width + style->get_margin(SIDE_LEFT), item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
+ submenu->draw(ci, Point2(scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
} else {
- submenu->draw(ci, Point2(display_width - style->get_margin(SIDE_RIGHT) - submenu->get_width(), item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
+ submenu->draw(ci, Point2(display_width - style->get_margin(SIDE_RIGHT) - submenu->get_width() - item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
}
}
// Text
+ Color font_outline_color = get_theme_color("font_outline_color");
+ int outline_size = get_theme_constant("outline_size");
if (items[i].separator) {
if (text != String()) {
int center = (display_width - items[i].text_buf->get_size().width) / 2;
- items[i].text_buf->draw(ci, Point2(center, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0)), font_color_separator);
+ Vector2 text_pos = Point2(center, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
+ items[i].text_buf->draw(ci, text_pos, font_separator_color);
}
} else {
item_ofs.x += icon_ofs + check_ofs;
if (rtl) {
- items[i].text_buf->draw(ci, Size2(control->get_size().width - items[i].text_buf->get_size().width - item_ofs.x, item_ofs.y) + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)), items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color));
+ Vector2 text_pos = Size2(control->get_size().width - items[i].text_buf->get_size().width - item_ofs.x, item_ofs.y) + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
+ items[i].text_buf->draw(ci, text_pos, items[i].disabled ? font_disabled_color : (i == mouse_over ? font_hover_color : font_color));
} else {
- items[i].text_buf->draw(ci, item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)), items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color));
+ Vector2 text_pos = item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
+ items[i].text_buf->draw(ci, text_pos, items[i].disabled ? font_disabled_color : (i == mouse_over ? font_hover_color : font_color));
}
}
// Accelerator / Shortcut
if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->is_valid())) {
if (rtl) {
- item_ofs.x = scroll_width + style->get_margin(SIDE_LEFT);
+ item_ofs.x = scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding;
} else {
- item_ofs.x = display_width - style->get_margin(SIDE_RIGHT) - items[i].accel_text_buf->get_size().x;
+ item_ofs.x = display_width - style->get_margin(SIDE_RIGHT) - items[i].accel_text_buf->get_size().x - item_end_padding;
+ }
+ Vector2 text_pos = item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ items[i].accel_text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
}
- items[i].accel_text_buf->draw(ci, item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)), i == mouse_over ? font_color_hover : font_color_accel);
+ items[i].accel_text_buf->draw(ci, text_pos, i == mouse_over ? font_hover_color : font_accelerator_color);
}
// Cache the item vertical offset from the first item and the height
@@ -672,6 +724,7 @@ void PopupMenu::_notification(int p_what) {
for (int i = 0; i < items.size(); i++) {
items.write[i].xl_text = tr(items[i].text);
items.write[i].dirty = true;
+ _shape_item(i);
}
child_controls_changed();
@@ -1656,19 +1709,6 @@ PopupMenu::PopupMenu() {
connect("window_input", callable_mp(this, &PopupMenu::_gui_input));
- mouse_over = -1;
- submenu_over = -1;
- initial_button_mask = 0;
- during_grabbed_click = false;
-
- allow_search = true;
- search_time_msec = 0;
- search_string = "";
-
- set_hide_on_item_selection(true);
- set_hide_on_checkable_item_selection(true);
- set_hide_on_multistate_item_selection(false);
-
submenu_timer = memnew(Timer);
submenu_timer->set_wait_time(0.3);
submenu_timer->set_one_shot(true);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index c050e61d50..e4cbe984c9 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -51,28 +51,28 @@ class PopupMenu : public Popup {
String language;
Control::TextDirection text_direction = Control::TEXT_DIRECTION_AUTO;
- bool checked;
+ bool checked = false;
enum {
CHECKABLE_TYPE_NONE,
CHECKABLE_TYPE_CHECK_BOX,
CHECKABLE_TYPE_RADIO_BUTTON,
} checkable_type;
- int max_states;
- int state;
- bool separator;
- bool disabled;
- bool dirty;
- int id;
+ int max_states = 0;
+ int state = 0;
+ bool separator = false;
+ bool disabled = false;
+ bool dirty = true;
+ int id = 0;
Variant metadata;
String submenu;
String tooltip;
- uint32_t accel;
- int _ofs_cache;
- int _height_cache;
- int h_ofs;
+ uint32_t accel = 0;
+ int _ofs_cache = 0;
+ int _height_cache = 0;
+ int h_ofs = 0;
Ref<Shortcut> shortcut;
- bool shortcut_is_global;
- bool shortcut_is_disabled;
+ bool shortcut_is_global = false;
+ bool shortcut_is_disabled = false;
// Returns (0,0) if icon is null.
Size2 get_icon_size() const {
@@ -82,19 +82,7 @@ class PopupMenu : public Popup {
Item() {
text_buf.instance();
accel_text_buf.instance();
- dirty = true;
- checked = false;
checkable_type = CHECKABLE_TYPE_NONE;
- separator = false;
- max_states = 0;
- state = 0;
- accel = 0;
- disabled = false;
- _ofs_cache = 0;
- _height_cache = 0;
- h_ofs = 0;
- shortcut_is_global = false;
- shortcut_is_disabled = false;
}
};
@@ -104,15 +92,16 @@ class PopupMenu : public Popup {
Timer *submenu_timer;
List<Rect2> autohide_areas;
Vector<Item> items;
- int initial_button_mask;
- bool during_grabbed_click;
- int mouse_over;
- int submenu_over;
+ int initial_button_mask = 0;
+ bool during_grabbed_click = false;
+ int mouse_over = -1;
+ int submenu_over = -1;
Rect2 parent_rect;
String _get_accel_text(const Item &p_item) const;
int _get_mouse_over(const Point2 &p_over) const;
virtual Size2 _get_contents_minimum_size() const override;
+ int _get_item_height(int p_item) const;
int _get_items_total_height() const;
void _scroll_to_item(int p_item);
@@ -123,9 +112,9 @@ class PopupMenu : public Popup {
void _submenu_timeout();
uint64_t popup_time_msec = 0;
- bool hide_on_item_selection;
- bool hide_on_checkable_item_selection;
- bool hide_on_multistate_item_selection;
+ bool hide_on_item_selection = true;
+ bool hide_on_checkable_item_selection = true;
+ bool hide_on_multistate_item_selection = false;
Vector2 moved;
Array _get_items() const;
@@ -136,9 +125,9 @@ class PopupMenu : public Popup {
void _ref_shortcut(Ref<Shortcut> p_sc);
void _unref_shortcut(Ref<Shortcut> p_sc);
- bool allow_search;
- uint64_t search_time_msec;
- String search_string;
+ bool allow_search = true;
+ uint64_t search_time_msec = 0;
+ String search_string = "";
MarginContainer *margin_container;
ScrollContainer *scroll_container;
diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp
index 1b33cc6ffb..6e8dfd5994 100644
--- a/scene/gui/progress_bar.cpp
+++ b/scene/gui/progress_bar.cpp
@@ -74,7 +74,13 @@ void ProgressBar::_notification(int p_what) {
if (percent_visible) {
String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign();
TextLine tl = TextLine(txt, font, font_size);
- tl.draw(get_canvas_item(), (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round(), font_color);
+ Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round();
+ Color font_outline_color = get_theme_color("font_outline_color");
+ int outline_size = get_theme_constant("outline_size");
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ tl.draw_outline(get_canvas_item(), text_pos, outline_size, font_outline_color);
+ }
+ tl.draw(get_canvas_item(), text_pos, font_color);
}
}
}
@@ -98,5 +104,4 @@ void ProgressBar::_bind_methods() {
ProgressBar::ProgressBar() {
set_v_size_flags(0);
set_step(0.01);
- percent_visible = true;
}
diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h
index 3317846108..fb6060d932 100644
--- a/scene/gui/progress_bar.h
+++ b/scene/gui/progress_bar.h
@@ -36,7 +36,7 @@
class ProgressBar : public Range {
GDCLASS(ProgressBar, Range);
- bool percent_visible;
+ bool percent_visible = true;
protected:
void _notification(int p_what);
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index b9ac6d7505..adc1ed67ca 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -30,24 +30,20 @@
#include "range.h"
-String Range::get_configuration_warning() const {
- String warning = Control::get_configuration_warning();
+TypedArray<String> Range::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node::get_configuration_warnings();
if (shared->exp_ratio && shared->min <= 0) {
- if (!warning.is_empty()) {
- warning += "\n\n";
- }
- warning += TTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0.");
+ warnings.push_back(TTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0."));
}
- return warning;
+ return warnings;
}
void Range::_value_changed_notify() {
_value_changed(shared->val);
emit_signal("value_changed", shared->val);
update();
- _change_notify("value");
}
void Range::Shared::emit_value_changed() {
@@ -63,7 +59,6 @@ void Range::Shared::emit_value_changed() {
void Range::_changed_notify(const char *p_what) {
emit_signal("changed");
update();
- _change_notify(p_what);
}
void Range::Shared::emit_changed(const char *p_what) {
@@ -108,7 +103,7 @@ void Range::set_min(double p_min) {
shared->emit_changed("min");
- update_configuration_warning();
+ update_configuration_warnings();
}
void Range::set_max(double p_max) {
@@ -171,7 +166,10 @@ void Range::set_as_ratio(double p_value) {
}
double Range::get_as_ratio() const {
- ERR_FAIL_COND_V_MSG(Math::is_equal_approx(get_max(), get_min()), 0.0, "Cannot get ratio when minimum and maximum value are equal.");
+ if (Math::is_equal_approx(get_max(), get_min())) {
+ // Avoid division by zero.
+ return 1.0;
+ }
if (shared->exp_ratio && get_min() >= 0) {
double exp_min = get_min() == 0 ? 0.0 : Math::log(get_min()) / Math::log((double)2);
@@ -180,7 +178,6 @@ double Range::get_as_ratio() const {
double v = Math::log(value) / Math::log((double)2);
return CLAMP((v - exp_min) / (exp_max - exp_min), 0, 1);
-
} else {
float value = CLAMP(get_value(), shared->min, shared->max);
return CLAMP((value - get_min()) / (get_max() - get_min()), 0, 1);
@@ -286,7 +283,7 @@ bool Range::is_using_rounded_values() const {
void Range::set_exp_ratio(bool p_enable) {
shared->exp_ratio = p_enable;
- update_configuration_warning();
+ update_configuration_warnings();
}
bool Range::is_ratio_exp() const {
@@ -311,17 +308,7 @@ bool Range::is_lesser_allowed() const {
Range::Range() {
shared = memnew(Shared);
- shared->min = 0;
- shared->max = 100;
- shared->val = 0;
- shared->step = 1;
- shared->page = 0;
shared->owners.insert(this);
- shared->exp_ratio = false;
- shared->allow_greater = false;
- shared->allow_lesser = false;
-
- _rounded_values = false;
}
Range::~Range() {
diff --git a/scene/gui/range.h b/scene/gui/range.h
index 68ff46bd74..7a129e88d6 100644
--- a/scene/gui/range.h
+++ b/scene/gui/range.h
@@ -37,11 +37,14 @@ class Range : public Control {
GDCLASS(Range, Control);
struct Shared {
- double val, min, max;
- double step, page;
- bool exp_ratio;
- bool allow_greater;
- bool allow_lesser;
+ double val = 0.0;
+ double min = 0.0;
+ double max = 100.0;
+ double step = 1.0;
+ double page = 0.0;
+ bool exp_ratio = false;
+ bool allow_greater = false;
+ bool allow_lesser = false;
Set<Range *> owners;
void emit_value_changed();
void emit_changed(const char *p_what = "");
@@ -62,7 +65,7 @@ protected:
static void _bind_methods();
- bool _rounded_values;
+ bool _rounded_values = false;
public:
void set_value(double p_val);
@@ -94,7 +97,7 @@ public:
void share(Range *p_range);
void unshare();
- virtual String get_configuration_warning() const override;
+ TypedArray<String> get_configuration_warnings() const override;
Range();
~Range();
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index a1aa72b29a..e8a908c30e 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -45,7 +45,7 @@
#include "editor/editor_scale.h"
#endif
-RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) {
+RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) const {
if (p_free) {
if (p_item->subitems.size()) {
return p_item->subitems.front()->get();
@@ -90,7 +90,7 @@ RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) {
return nullptr;
}
-RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) {
+RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) const {
if (p_free) {
if (p_item->subitems.size()) {
return p_item->subitems.back()->get();
@@ -220,7 +220,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
if (tab_size > 0) { // Align inline tabs.
Vector<float> tabs;
- tabs.push_back(tab_size * p_base_font->get_char_size('m', 0, p_base_font_size).width);
+ tabs.push_back(tab_size * p_base_font->get_char_size(' ', 0, p_base_font_size).width);
l.text_buf->tab_align(tabs);
}
@@ -314,7 +314,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
table->rows.clear();
Vector2 offset;
- float row_height = 0;
+ float row_height = 0.0;
for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
@@ -392,7 +392,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
if (tab_size > 0) { // Align inline tabs.
Vector<float> tabs;
- tabs.push_back(tab_size * p_base_font->get_char_size('m', 0, p_base_font_size).width);
+ tabs.push_back(tab_size * p_base_font->get_char_size(' ', 0, p_base_font_size).width);
l.text_buf->tab_align(tabs);
}
@@ -454,6 +454,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
ItemImage *img = (ItemImage *)it;
l.text_buf->add_object((uint64_t)it, img->image->get_size(), img->inline_align, 1);
text += String::chr(0xfffc);
+ l.char_count += 1;
} break;
case ITEM_TABLE: {
ItemTable *table = static_cast<ItemTable *>(it);
@@ -553,7 +554,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
table->rows.clear();
Vector2 offset;
- float row_height = 0;
+ float row_height = 0.0;
for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames.
@@ -587,7 +588,8 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
offset.x += table->columns[column].width + hseparation + frame->padding.size.x;
row_height = MAX(yofs, row_height);
- if (column == col_count - 1) {
+ // Add row height after last column of the row or last cell of the table.
+ if (column == col_count - 1 || E->next() == nullptr) {
offset.x = 0;
row_height += vseparation;
table->total_height += row_height;
@@ -618,11 +620,11 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
}
}
-void 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_color_shadow, 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, bool p_shadow_as_outline, const Point2 &p_shadow_ofs) {
Vector2 off;
- ERR_FAIL_COND(p_frame == nullptr);
- ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
+ ERR_FAIL_COND_V(p_frame == nullptr, 0);
+ ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), 0);
Line &l = p_frame->lines.write[p_line];
@@ -631,7 +633,7 @@ void RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_
Variant meta;
if (it_from == nullptr) {
- return;
+ return 0;
}
RID ci = get_canvas_item();
@@ -699,14 +701,24 @@ void RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_
}
l.text_buf->draw_dropcap(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_color);
+ int line_count = 0;
+ Size2 ctrl_size = get_size();
// Draw text.
for (int line = 0; line < l.text_buf->get_line_count(); line++) {
RID rid = l.text_buf->get_line_rid(line);
+ if (p_ofs.y + off.y >= ctrl_size.height) {
+ break;
+ }
+ if (p_ofs.y + off.y + TS->shaped_text_get_size(rid).y <= 0) {
+ off.y += TS->shaped_text_get_size(rid).y;
+ continue;
+ }
float width = l.text_buf->get_width();
float length = TS->shaped_text_get_width(rid);
// Draw line.
+ line_count++;
if (rtl) {
off.x = p_width - l.offset.x - width;
@@ -748,7 +760,7 @@ void RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_
//draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS
- off.y += TS->shaped_text_get_ascent(rid);
+ off.y += TS->shaped_text_get_ascent(rid) + l.text_buf->get_spacing_top();
// Draw inlined objects.
Array objects = TS->shaped_text_get_objects(rid);
for (int i = 0; i < objects.size(); i++) {
@@ -766,6 +778,7 @@ void RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_
Color odd_row_bg = get_theme_color("table_odd_row_bg");
Color even_row_bg = get_theme_color("table_even_row_bg");
Color border = get_theme_color("table_border");
+ int hseparation = get_theme_constant("table_hseparation");
int col_count = table->columns.size();
int row_count = table->rows.size();
@@ -782,15 +795,15 @@ void RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_
coff.x = rect.size.width - table->columns[col].width - coff.x;
}
if (row % 2 == 0) {
- draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width, table->rows[row])), (frame->odd_row_bg != Color(0, 0, 0, 0) ? frame->odd_row_bg : odd_row_bg), true);
+ draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + hseparation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->odd_row_bg != Color(0, 0, 0, 0) ? frame->odd_row_bg : odd_row_bg), true);
} else {
- draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width, table->rows[row])), (frame->even_row_bg != Color(0, 0, 0, 0) ? frame->even_row_bg : even_row_bg), true);
+ draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + hseparation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->even_row_bg != Color(0, 0, 0, 0) ? frame->even_row_bg : even_row_bg), true);
}
- draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width, table->rows[row])), (frame->border != Color(0, 0, 0, 0) ? frame->border : border), false);
+ draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + hseparation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->border != Color(0, 0, 0, 0) ? frame->border : border), false);
}
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_color_shadow, 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_as_outline, p_shadow_ofs);
}
idx++;
}
@@ -809,8 +822,8 @@ void RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_
// Draw oulines and shadow.
for (int i = 0; i < gl_size; i++) {
Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
- int size = _find_outline_size(it);
- Color font_color = _find_outline_color(it, Color(0, 0, 0, 0));
+ int size = _find_outline_size(it, p_outline_size);
+ Color font_color = _find_outline_color(it, p_outline_color);
if (size <= 0) {
gloff.x += glyphs[i].advance;
continue;
@@ -910,9 +923,9 @@ void RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_
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_color_shadow);
- 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_color_shadow);
- 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_color_shadow);
+ 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);
}
TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color);
}
@@ -922,7 +935,7 @@ void RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_
}
// Draw main text.
- Color selection_fg = get_theme_color("font_color_selected");
+ Color selection_fg = get_theme_color("font_selected_color");
Color selection_bg = get_theme_color("selection_color");
int sel_start = -1;
@@ -1066,8 +1079,10 @@ void RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_
off.x += glyphs[i].advance;
}
}
- off.y += TS->shaped_text_get_descent(rid);
+ off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom();
}
+
+ return line_count;
}
void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame, int *r_click_line, Item **r_click_item, int *r_click_char, bool *r_outside) {
@@ -1103,7 +1118,8 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
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()) {
- ofs.y += _find_click_in_line(p_frame, from_line, ofs, text_rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char);
+ _find_click_in_line(p_frame, from_line, ofs, text_rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char);
+ ofs.y += main->lines[from_line].text_buf->get_size().y;
if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) {
if (r_outside != nullptr) {
*r_outside = false;
@@ -1158,7 +1174,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
} break;
}
- off.y += TS->shaped_text_get_ascent(rid);
+ off.y += TS->shaped_text_get_ascent(rid) + l.text_buf->get_spacing_top();
Array objects = TS->shaped_text_get_objects(rid);
for (int i = 0; i < objects.size(); i++) {
@@ -1222,7 +1238,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
if (rect.has_point(p_click) && !table_hit) {
char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x);
}
- off.y += TS->shaped_text_get_descent(rid);
+ off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom();
}
if (char_pos >= 0) {
@@ -1230,8 +1246,19 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
if (r_click_item != nullptr) {
Item *it = p_frame->lines[p_line].from;
Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
- it = _get_item_at_pos(it, it_to, char_pos);
- *r_click_item = it;
+ if (char_pos == p_frame->lines[p_line].char_count) {
+ // Selection after the end of line, select last item.
+ if (it_to != nullptr) {
+ *r_click_item = _get_prev_item(it_to);
+ } else {
+ for (Item *i = it; i && i != it_to; i = _get_next_item(i)) {
+ *r_click_item = i;
+ }
+ }
+ } else {
+ // Selection in the line.
+ *r_click_item = _get_item_at_pos(it, it_to, char_pos);
+ }
}
if (r_click_frame != nullptr) {
@@ -1386,19 +1413,20 @@ void RichTextLabel::_notification(int p_what) {
}
Ref<Font> base_font = get_theme_font("normal_font");
Color base_color = get_theme_color("default_color");
- Color outline_color = get_theme_color("outline_color");
+ Color outline_color = get_theme_color("font_outline_color");
int outline_size = get_theme_constant("outline_size");
- Color font_color_shadow = get_theme_color("font_color_shadow");
+ Color font_shadow_color = get_theme_color("font_shadow_color");
bool use_outline = get_theme_constant("shadow_as_outline");
Point2 shadow_ofs(get_theme_constant("shadow_offset_x"), get_theme_constant("shadow_offset_y"));
+ visible_paragraph_count = 0;
visible_line_count = 0;
// New cache draw.
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_line_count++;
- _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_color_shadow, use_outline, shadow_ofs);
+ 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);
ofs.y += main->lines[from_line].text_buf->get_size().y;
from_line++;
}
@@ -1441,6 +1469,8 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const
}
void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
Ref<InputEventMouseButton> b = p_event;
if (b.is_valid()) {
@@ -1451,8 +1481,8 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
return;
}
- if (b->get_button_index() == BUTTON_LEFT) {
- if (b->is_pressed() && !b->is_doubleclick()) {
+ if (b->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (b->is_pressed() && !b->is_double_click()) {
scroll_updated = false;
ItemFrame *c_frame = nullptr;
int c_line = 0;
@@ -1484,8 +1514,8 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
}
}
}
- } else if (b->is_pressed() && b->is_doubleclick() && selection.enabled) {
- //doubleclick: select word
+ } else if (b->is_pressed() && b->is_double_click() && selection.enabled) {
+ //double_click: select word
ItemFrame *c_frame = nullptr;
int c_line = 0;
@@ -1519,7 +1549,7 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
} else if (!b->is_pressed()) {
selection.click_item = nullptr;
- if (!b->is_doubleclick() && !scroll_updated) {
+ if (!b->is_double_click() && !scroll_updated) {
Item *c_item = nullptr;
bool outside = true;
@@ -1536,12 +1566,12 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
}
}
- if (b->get_button_index() == BUTTON_WHEEL_UP) {
+ if (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP) {
if (scroll_active) {
vscroll->set_value(vscroll->get_value() - vscroll->get_page() * b->get_factor() * 0.5 / 8);
}
}
- if (b->get_button_index() == BUTTON_WHEEL_DOWN) {
+ if (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) {
if (scroll_active) {
vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8);
}
@@ -1560,53 +1590,36 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
Ref<InputEventKey> k = p_event;
if (k.is_valid()) {
- if (k->is_pressed() && !k->get_alt() && !k->get_shift()) {
+ if (k->is_pressed()) {
bool handled = false;
- switch (k->get_keycode()) {
- case KEY_PAGEUP: {
- if (vscroll->is_visible_in_tree()) {
- vscroll->set_value(vscroll->get_value() - vscroll->get_page());
- handled = true;
- }
- } break;
- case KEY_PAGEDOWN: {
- if (vscroll->is_visible_in_tree()) {
- vscroll->set_value(vscroll->get_value() + vscroll->get_page());
- handled = true;
- }
- } break;
- case KEY_UP: {
- if (vscroll->is_visible_in_tree()) {
- vscroll->set_value(vscroll->get_value() - get_theme_font("normal_font")->get_height(get_theme_font_size("normal_font_size")));
- handled = true;
- }
- } break;
- case KEY_DOWN: {
- if (vscroll->is_visible_in_tree()) {
- vscroll->set_value(vscroll->get_value() + get_theme_font("normal_font")->get_height(get_theme_font_size("normal_font_size")));
- handled = true;
- }
- } break;
- case KEY_HOME: {
- if (vscroll->is_visible_in_tree()) {
- vscroll->set_value(0);
- handled = true;
- }
- } break;
- case KEY_END: {
- if (vscroll->is_visible_in_tree()) {
- vscroll->set_value(vscroll->get_max());
- handled = true;
- }
- } break;
- case KEY_INSERT:
- case KEY_C: {
- if (k->get_command()) {
- selection_copy();
- handled = true;
- }
- } break;
+ if (k->is_action("ui_page_up") && vscroll->is_visible_in_tree()) {
+ vscroll->set_value(vscroll->get_value() - vscroll->get_page());
+ handled = true;
+ }
+ if (k->is_action("ui_page_down") && vscroll->is_visible_in_tree()) {
+ vscroll->set_value(vscroll->get_value() + vscroll->get_page());
+ handled = true;
+ }
+ if (k->is_action("ui_up") && vscroll->is_visible_in_tree()) {
+ vscroll->set_value(vscroll->get_value() - get_theme_font("normal_font")->get_height(get_theme_font_size("normal_font_size")));
+ handled = true;
+ }
+ if (k->is_action("ui_down") && vscroll->is_visible_in_tree()) {
+ vscroll->set_value(vscroll->get_value() + get_theme_font("normal_font")->get_height(get_theme_font_size("normal_font_size")));
+ handled = true;
+ }
+ if (k->is_action("ui_home") && vscroll->is_visible_in_tree()) {
+ vscroll->set_value(0);
+ handled = true;
+ }
+ if (k->is_action("ui_end") && vscroll->is_visible_in_tree()) {
+ vscroll->set_value(vscroll->get_max());
+ handled = true;
+ }
+ if (k->is_action("ui_copy")) {
+ selection_copy();
+ handled = true;
}
if (handled) {
@@ -1740,7 +1753,7 @@ int RichTextLabel::_find_font_size(Item *p_item) {
return -1;
}
-int RichTextLabel::_find_outline_size(Item *p_item) {
+int RichTextLabel::_find_outline_size(Item *p_item, int p_default) {
Item *sizeitem = p_item;
while (sizeitem) {
@@ -1752,7 +1765,7 @@ int RichTextLabel::_find_outline_size(Item *p_item) {
sizeitem = sizeitem->parent;
}
- return 0;
+ return p_default;
}
Dictionary RichTextLabel::_find_font_features(Item *p_item) {
@@ -1834,7 +1847,7 @@ int RichTextLabel::_find_list(Item *p_item, Vector<int> &r_index, Vector<ItemLis
int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size) {
Item *item = p_item;
- float margin = 0;
+ float margin = 0.0;
while (item) {
if (item->type == ITEM_INDENT) {
@@ -1846,7 +1859,7 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int
if (font_size == -1) {
font_size = p_base_font_size;
}
- margin += tab_size * font->get_char_size('m', 0, font_size).width;
+ margin += tab_size * font->get_char_size(' ', 0, font_size).width;
} else if (item->type == ITEM_LIST) {
Ref<Font> font = _find_font(item);
@@ -1857,7 +1870,7 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int
if (font_size == -1) {
font_size = p_base_font_size;
}
- margin += tab_size * font->get_char_size('m', 0, font_size).width;
+ margin += tab_size * font->get_char_size(' ', 0, font_size).width;
}
item = item->parent;
@@ -2239,6 +2252,8 @@ void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width,
}
ERR_FAIL_COND(p_image.is_null());
+ ERR_FAIL_COND(p_image->get_width() == 0);
+ ERR_FAIL_COND(p_image->get_height() == 0);
ItemImage *item = memnew(ItemImage);
item->image = p_image;
@@ -2597,6 +2612,14 @@ void RichTextLabel::pop() {
current = current->parent;
}
+// Creates a new line without adding an ItemNewline to the previous line.
+// Useful when wanting to calling remove_line and add a new line immediately after.
+void RichTextLabel::increment_line_count() {
+ _validate_line_caches(main);
+ current_frame->lines.resize(current_frame->lines.size() + 1);
+ _invalidate_current_line(current_frame);
+}
+
void RichTextLabel::clear() {
main->_clear_children();
current = main;
@@ -3367,14 +3390,46 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
return OK;
}
+void RichTextLabel::scroll_to_paragraph(int p_paragraph) {
+ ERR_FAIL_INDEX(p_paragraph, main->lines.size());
+ _validate_line_caches(main);
+ vscroll->set_value(main->lines[p_paragraph].offset.y);
+}
+
+int RichTextLabel::get_paragraph_count() const {
+ return current_frame->lines.size();
+}
+
+int RichTextLabel::get_visible_paragraph_count() const {
+ if (!is_visible()) {
+ return 0;
+ }
+ return visible_paragraph_count;
+}
+
void RichTextLabel::scroll_to_line(int p_line) {
- ERR_FAIL_INDEX(p_line, main->lines.size());
_validate_line_caches(main);
- vscroll->set_value(main->lines[p_line].offset.y);
+
+ int line_count = 0;
+ for (int i = 0; i < main->lines.size(); i++) {
+ if ((line_count <= p_line) && (line_count + main->lines[i].text_buf->get_line_count() >= p_line)) {
+ float line_offset = 0.f;
+ for (int j = 0; j < p_line - line_count; j++) {
+ line_offset += main->lines[i].text_buf->get_line_size(j).y;
+ }
+ vscroll->set_value(main->lines[i].offset.y + line_offset);
+ return;
+ }
+ line_count += main->lines[i].text_buf->get_line_count();
+ }
}
int RichTextLabel::get_line_count() const {
- return current_frame->lines.size();
+ int line_count = 0;
+ for (int i = 0; i < main->lines.size(); i++) {
+ line_count += main->lines[i].text_buf->get_line_count();
+ }
+ return line_count;
}
int RichTextLabel::get_visible_line_count() const {
@@ -3475,7 +3530,7 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p
return false;
}
-String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p_selection) {
+String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p_selection) const {
String text;
ERR_FAIL_COND_V(p_frame == nullptr, text);
ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), text);
@@ -3542,7 +3597,7 @@ String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p
return text;
}
-String RichTextLabel::get_selected_text() {
+String RichTextLabel::get_selected_text() const {
if (!selection.active || !selection.enabled) {
return "";
}
@@ -3566,6 +3621,22 @@ bool RichTextLabel::is_selection_enabled() const {
return selection.enabled;
}
+int RichTextLabel::get_selection_from() const {
+ if (!selection.active || !selection.enabled) {
+ return -1;
+ }
+
+ return selection.from_frame->lines[selection.from_line].char_offset + selection.from_char;
+}
+
+int RichTextLabel::get_selection_to() const {
+ if (!selection.active || !selection.enabled) {
+ return -1;
+ }
+
+ return selection.to_frame->lines[selection.to_line].char_offset + selection.to_char - 1;
+}
+
void RichTextLabel::set_bbcode(const String &p_bbcode) {
bbcode = p_bbcode;
if (is_inside_tree() && use_bbcode) {
@@ -3586,6 +3657,7 @@ void RichTextLabel::set_use_bbcode(bool p_enable) {
}
use_bbcode = p_enable;
set_bbcode(bbcode);
+ notify_property_list_changed();
}
bool RichTextLabel::is_using_bbcode() const {
@@ -3601,6 +3673,8 @@ String RichTextLabel::get_text() {
text += t->text;
} else if (it->type == ITEM_NEWLINE) {
text += "\n";
+ } else if (it->type == ITEM_IMAGE) {
+ text += " ";
} else if (it->type == ITEM_INDENT || it->type == ITEM_LIST) {
text += "\t";
}
@@ -3692,7 +3766,9 @@ void RichTextLabel::set_effects(const Vector<Variant> &effects) {
custom_effects.push_back(effect);
}
- parse_bbcode(bbcode);
+ if ((bbcode != "") && use_bbcode) {
+ parse_bbcode(bbcode);
+ }
}
Vector<Variant> RichTextLabel::get_effects() {
@@ -3709,7 +3785,9 @@ void RichTextLabel::install_effect(const Variant effect) {
if (rteffect.is_valid()) {
custom_effects.push_back(effect);
- parse_bbcode(bbcode);
+ if ((bbcode != "") && use_bbcode) {
+ parse_bbcode(bbcode);
+ }
}
}
@@ -3721,6 +3799,12 @@ int RichTextLabel::get_content_height() const {
return total_height;
}
+void RichTextLabel::_validate_property(PropertyInfo &property) const {
+ if (!use_bbcode && property.name == "bbcode_text") {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+}
+
void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &RichTextLabel::_gui_input);
ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text);
@@ -3782,6 +3866,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_v_scroll"), &RichTextLabel::get_v_scroll);
ClassDB::bind_method(D_METHOD("scroll_to_line", "line"), &RichTextLabel::scroll_to_line);
+ ClassDB::bind_method(D_METHOD("scroll_to_paragraph", "paragraph"), &RichTextLabel::scroll_to_paragraph);
ClassDB::bind_method(D_METHOD("set_tab_size", "spaces"), &RichTextLabel::set_tab_size);
ClassDB::bind_method(D_METHOD("get_tab_size"), &RichTextLabel::get_tab_size);
@@ -3792,6 +3877,11 @@ 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("get_selection_from"), &RichTextLabel::get_selection_from);
+ ClassDB::bind_method(D_METHOD("get_selection_to"), &RichTextLabel::get_selection_to);
+
+ ClassDB::bind_method(D_METHOD("get_selected_text"), &RichTextLabel::get_selected_text);
+
ClassDB::bind_method(D_METHOD("parse_bbcode", "bbcode"), &RichTextLabel::parse_bbcode);
ClassDB::bind_method(D_METHOD("append_bbcode", "bbcode"), &RichTextLabel::append_bbcode);
@@ -3812,6 +3902,9 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_line_count"), &RichTextLabel::get_line_count);
ClassDB::bind_method(D_METHOD("get_visible_line_count"), &RichTextLabel::get_visible_line_count);
+ ClassDB::bind_method(D_METHOD("get_paragraph_count"), &RichTextLabel::get_paragraph_count);
+ ClassDB::bind_method(D_METHOD("get_visible_paragraph_count"), &RichTextLabel::get_visible_paragraph_count);
+
ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height);
ClassDB::bind_method(D_METHOD("parse_expressions_for_values", "expressions"), &RichTextLabel::parse_expressions_for_values);
@@ -3890,6 +3983,14 @@ void RichTextLabel::_bind_methods() {
void RichTextLabel::set_visible_characters(int p_visible) {
visible_characters = p_visible;
+ if (p_visible == -1) {
+ percent_visible = 1;
+ } else {
+ int total_char_count = get_total_character_count();
+ if (total_char_count > 0) {
+ percent_visible = (float)p_visible / (float)total_char_count;
+ }
+ }
update();
}
@@ -3912,14 +4013,16 @@ void RichTextLabel::set_fixed_size_to_width(int p_width) {
}
Size2 RichTextLabel::get_minimum_size() const {
- Size2 size(0, 0);
+ Ref<StyleBox> style = get_theme_stylebox("normal");
+ Size2 size = style->get_minimum_size();
+
if (fixed_width != -1) {
- size.x = fixed_width;
+ size.x += fixed_width;
}
if (fixed_width != -1 || fit_content_height) {
const_cast<RichTextLabel *>(this)->_validate_line_caches(main);
- size.y = get_content_height();
+ size.y += get_content_height();
}
return size;
@@ -4007,19 +4110,6 @@ RichTextLabel::RichTextLabel() {
main->first_invalid_line = 0;
main->first_resized_line = 0;
current_frame = main;
- tab_size = 4;
- default_align = ALIGN_LEFT;
- underline_meta = true;
- meta_hovering = nullptr;
- override_selected_font_color = false;
-
- scroll_visible = false;
- scroll_follow = false;
- scroll_following = false;
- updating_scroll = false;
- scroll_active = true;
- scroll_w = 0;
- scroll_updated = false;
vscroll = memnew(VScrollBar);
add_child(vscroll);
@@ -4031,19 +4121,6 @@ RichTextLabel::RichTextLabel() {
vscroll->connect("value_changed", callable_mp(this, &RichTextLabel::_scroll_changed));
vscroll->set_step(1);
vscroll->hide();
- use_bbcode = false;
-
- selection.click_frame = nullptr;
- selection.click_item = nullptr;
- selection.active = false;
- selection.enabled = false;
-
- visible_characters = -1;
- percent_visible = 1;
- visible_line_count = 0;
-
- fixed_width = -1;
- fit_content_height = false;
set_clip_contents(true);
}
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index a65cc5a451..afc88e070a 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -81,7 +81,9 @@ public:
};
protected:
+ void _notification(int p_what);
static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const override;
private:
struct Item;
@@ -147,9 +149,9 @@ private:
struct ItemDropcap : public Item {
String text;
Ref<Font> font;
- int font_size;
+ int font_size = 0;
Color color;
- int ol_size;
+ int ol_size = 0;
Color ol_color;
Rect2 dropcap_margins;
ItemDropcap() { type = ITEM_DROPCAP; }
@@ -232,11 +234,11 @@ private:
struct ItemTable : public Item {
struct Column {
- bool expand;
- int expand_ratio;
- int min_width;
- int max_width;
- int width;
+ bool expand = false;
+ int expand_ratio = 0;
+ int min_width = 0;
+ int max_width = 0;
+ int width = 0;
};
Vector<Column> columns;
@@ -322,30 +324,31 @@ private:
}
};
- ItemFrame *main;
- Item *current;
- ItemFrame *current_frame;
+ ItemFrame *main = nullptr;
+ Item *current = nullptr;
+ ItemFrame *current_frame = nullptr;
- VScrollBar *vscroll;
+ VScrollBar *vscroll = nullptr;
- bool scroll_visible;
- bool scroll_follow;
- bool scroll_following;
- bool scroll_active;
- int scroll_w;
- bool scroll_updated;
- bool updating_scroll;
+ bool scroll_visible = false;
+ bool scroll_follow = false;
+ bool scroll_following = false;
+ bool scroll_active = true;
+ int scroll_w = 0;
+ bool scroll_updated = false;
+ bool updating_scroll = false;
int current_idx = 1;
int current_char_ofs = 0;
- int visible_line_count;
+ int visible_paragraph_count = 0;
+ int visible_line_count = 0;
- int tab_size;
- bool underline_meta;
- bool override_selected_font_color;
+ int tab_size = 4;
+ bool underline_meta = true;
+ bool override_selected_font_color = false;
- Align default_align;
+ Align default_align = ALIGN_LEFT;
- ItemMeta *meta_hovering;
+ ItemMeta *meta_hovering = nullptr;
Variant current_meta;
Vector<Ref<RichTextEffect>> custom_effects;
@@ -362,38 +365,38 @@ private:
Array st_args;
struct Selection {
- ItemFrame *click_frame;
- int click_line;
- Item *click_item;
- int click_char;
-
- ItemFrame *from_frame;
- int from_line;
- Item *from_item;
- int from_char;
-
- ItemFrame *to_frame;
- int to_line;
- Item *to_item;
- int to_char;
-
- bool active; // anything selected? i.e. from, to, etc. valid?
- bool enabled; // allow selections?
+ ItemFrame *click_frame = nullptr;
+ int click_line = 0;
+ Item *click_item = nullptr;
+ int click_char = 0;
+
+ ItemFrame *from_frame = nullptr;
+ int from_line = 0;
+ Item *from_item = nullptr;
+ int from_char = 0;
+
+ ItemFrame *to_frame = nullptr;
+ int to_line = 0;
+ Item *to_item = nullptr;
+ int to_char = 0;
+
+ bool active = false; // anything selected? i.e. from, to, etc. valid?
+ bool enabled = false; // allow selections?
};
Selection selection;
- int visible_characters;
- float percent_visible;
+ int visible_characters = -1;
+ float percent_visible = 1.0;
void _find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr);
- String _get_line_text(ItemFrame *p_frame, int p_line, Selection p_sel);
+ String _get_line_text(ItemFrame *p_frame, int p_line, Selection p_sel) const;
bool _search_line(ItemFrame *p_frame, int p_line, const String &p_string, Item *p_from, Item *p_to);
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);
- void _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_color_shadow, 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, bool p_shadow_as_outline, const Point2 &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;
@@ -404,7 +407,7 @@ private:
Ref<Font> _find_font(Item *p_item);
int _find_font_size(Item *p_item);
Dictionary _find_font_features(Item *p_item);
- int _find_outline_size(Item *p_item);
+ int _find_outline_size(Item *p_item, int p_default);
ItemList *_find_list_item(Item *p_item);
ItemDropcap *_find_dc_item(Item *p_item);
int _find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list);
@@ -426,22 +429,19 @@ private:
void _scroll_changed(double);
void _gui_input(Ref<InputEvent> p_event);
- Item *_get_next_item(Item *p_item, bool p_free = false);
- Item *_get_prev_item(Item *p_item, bool p_free = false);
+ Item *_get_next_item(Item *p_item, bool p_free = false) const;
+ Item *_get_prev_item(Item *p_item, bool p_free = false) const;
Rect2 _get_text_rect();
Ref<RichTextEffect> _get_custom_effect_by_code(String p_bbcode_identifier);
virtual Dictionary parse_expressions_for_values(Vector<String> p_expressions);
- bool use_bbcode;
+ bool use_bbcode = false;
String bbcode;
- int fixed_width;
-
- bool fit_content_height;
+ int fixed_width = -1;
-protected:
- void _notification(int p_what);
+ bool fit_content_height = false;
public:
String get_text();
@@ -483,6 +483,8 @@ public:
void push_cell();
void pop();
+ void increment_line_count();
+
void clear();
void set_offset(int p_pixel);
@@ -507,6 +509,10 @@ public:
bool search(const String &p_string, bool p_from_selection = false, bool p_search_previous = false);
+ void scroll_to_paragraph(int p_paragraph);
+ int get_paragraph_count() const;
+ int get_visible_paragraph_count() const;
+
void scroll_to_line(int p_line);
int get_line_count() const;
int get_visible_line_count() const;
@@ -519,7 +525,9 @@ public:
void set_selection_enabled(bool p_enabled);
bool is_selection_enabled() const;
- String get_selected_text();
+ int get_selection_from() const;
+ int get_selection_to() const;
+ String get_selected_text() const;
void selection_copy();
Error parse_bbcode(const String &p_bbcode);
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index 79a0de35fc..62276e3af0 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -42,6 +42,8 @@ void ScrollBar::set_can_focus_by_default(bool p_can_focus) {
}
void ScrollBar::_gui_input(Ref<InputEvent> p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
Ref<InputEventMouseMotion> m = p_event;
if (!m.is_valid() || drag.active) {
emit_signal("scrolling");
@@ -52,17 +54,17 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) {
if (b.is_valid()) {
accept_event();
- if (b->get_button_index() == BUTTON_WHEEL_DOWN && b->is_pressed()) {
+ if (b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && b->is_pressed()) {
set_value(get_value() + get_page() / 4.0);
accept_event();
}
- if (b->get_button_index() == BUTTON_WHEEL_UP && b->is_pressed()) {
+ if (b->get_button_index() == MOUSE_BUTTON_WHEEL_UP && b->is_pressed()) {
set_value(get_value() - get_page() / 4.0);
accept_event();
}
- if (b->get_button_index() != BUTTON_LEFT) {
+ if (b->get_button_index() != MOUSE_BUTTON_LEFT) {
return;
}
@@ -434,7 +436,7 @@ double ScrollBar::get_area_size() const {
}
double ScrollBar::get_area_offset() const {
- double ofs = 0;
+ double ofs = 0.0;
if (orientation == VERTICAL) {
ofs += get_theme_stylebox("hscroll")->get_margin(SIDE_TOP);
diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h
index 7dd71cc269..24b3b33e82 100644
--- a/scene/gui/scroll_bar.h
+++ b/scene/gui/scroll_bar.h
@@ -47,14 +47,14 @@ class ScrollBar : public Range {
Orientation orientation;
Size2 size;
- float custom_step = -1;
+ float custom_step = -1.0;
HighlightStatus highlight = HIGHLIGHT_NONE;
struct Drag {
bool active = false;
- float pos_at_click = 0;
- float value_at_click = 0;
+ float pos_at_click = 0.0;
+ float value_at_click = 0.0;
} drag;
double get_grabber_size() const;
@@ -73,14 +73,14 @@ class ScrollBar : public Range {
Vector2 drag_node_accum;
Vector2 drag_node_from;
Vector2 last_drag_node_accum;
- float last_drag_node_time;
- float time_since_motion;
+ float last_drag_node_time = 0.0;
+ float time_since_motion = 0.0;
bool drag_node_touching = false;
bool drag_node_touching_deaccel = false;
- bool click_handled;
+ bool click_handled = false;
bool scrolling = false;
- double target_scroll = 0;
+ double target_scroll = 0.0;
bool smooth_scroll_enabled = false;
void _drag_node_exit();
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 52feb2ab23..67dcf458b0 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -88,13 +88,15 @@ void ScrollContainer::_cancel_drag() {
}
void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
+ ERR_FAIL_COND(p_gui_input.is_null());
+
double prev_v_scroll = v_scroll->get_value();
double prev_h_scroll = h_scroll->get_value();
Ref<InputEventMouseButton> mb = p_gui_input;
if (mb.is_valid()) {
- if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) {
+ if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_pressed()) {
// only horizontal is enabled, scroll horizontally
if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->get_shift())) {
h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() / 8 * mb->get_factor());
@@ -103,7 +105,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed()) {
+ if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_pressed()) {
// only horizontal is enabled, scroll horizontally
if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->get_shift())) {
h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() / 8 * mb->get_factor());
@@ -112,13 +114,13 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- if (mb->get_button_index() == BUTTON_WHEEL_LEFT && mb->is_pressed()) {
+ if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT && mb->is_pressed()) {
if (h_scroll->is_visible_in_tree()) {
h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() * mb->get_factor() / 8);
}
}
- if (mb->get_button_index() == BUTTON_WHEEL_RIGHT && mb->is_pressed()) {
+ if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT && mb->is_pressed()) {
if (h_scroll->is_visible_in_tree()) {
h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * mb->get_factor() / 8);
}
@@ -132,7 +134,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
return;
}
- if (mb->get_button_index() != BUTTON_LEFT) {
+ if (mb->get_button_index() != MOUSE_BUTTON_LEFT) {
return;
}
@@ -244,11 +246,11 @@ void ScrollContainer::_ensure_focused_visible(Control *p_control) {
if (is_a_parent_of(p_control)) {
Rect2 global_rect = get_global_rect();
Rect2 other_rect = p_control->get_global_rect();
- float right_margin = 0;
+ float right_margin = 0.0;
if (v_scroll->is_visible()) {
right_margin += v_scroll->get_size().x;
}
- float bottom_margin = 0;
+ float bottom_margin = 0.0;
if (h_scroll->is_visible()) {
bottom_margin += h_scroll->get_size().y;
}
@@ -297,7 +299,7 @@ void ScrollContainer::_update_dimensions() {
child_max_size.x = MAX(child_max_size.x, minsize.x);
child_max_size.y = MAX(child_max_size.y, minsize.y);
- Rect2 r = Rect2(-scroll, minsize);
+ Rect2 r = Rect2(-Size2(get_h_scroll(), get_v_scroll()), minsize);
if (!scroll_h || (!h_scroll->is_visible_in_tree() && c->get_h_size_flags() & SIZE_EXPAND)) {
r.position.x = 0;
if (c->get_h_size_flags() & SIZE_EXPAND) {
@@ -432,40 +434,16 @@ void ScrollContainer::update_scrollbars() {
Size2 min = child_max_size;
- bool hide_scroll_v = !scroll_v || min.height <= size.height;
- bool hide_scroll_h = !scroll_h || min.width <= size.width;
-
- v_scroll->set_max(min.height);
- if (hide_scroll_v) {
- v_scroll->set_page(size.height);
- v_scroll->hide();
- scroll.y = 0;
- } else {
- v_scroll->show();
- if (hide_scroll_h) {
- v_scroll->set_page(size.height);
- } else {
- v_scroll->set_page(size.height - hmin.height);
- }
-
- scroll.y = v_scroll->get_value();
- }
+ bool hide_scroll_h = !scroll_h || min.width <= size.width || !h_scroll_visible;
+ bool hide_scroll_v = !scroll_v || min.height <= size.height || !v_scroll_visible;
h_scroll->set_max(min.width);
- if (hide_scroll_h) {
- h_scroll->set_page(size.width);
- h_scroll->hide();
- scroll.x = 0;
- } else {
- h_scroll->show();
- if (hide_scroll_v) {
- h_scroll->set_page(size.width);
- } else {
- h_scroll->set_page(size.width - vmin.width);
- }
+ h_scroll->set_page(size.width - (hide_scroll_v ? 0 : vmin.width));
+ h_scroll->set_visible(!hide_scroll_h);
- scroll.x = h_scroll->get_value();
- }
+ v_scroll->set_max(min.height);
+ v_scroll->set_page(size.height - (hide_scroll_h ? 0 : hmin.height));
+ v_scroll->set_visible(!hide_scroll_v);
// Avoid scrollbar overlapping.
h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, hide_scroll_v ? 0 : -vmin.width);
@@ -473,13 +451,28 @@ void ScrollContainer::update_scrollbars() {
}
void ScrollContainer::_scroll_moved(float) {
- scroll.x = h_scroll->get_value();
- scroll.y = v_scroll->get_value();
queue_sort();
-
update();
};
+void ScrollContainer::set_h_scroll(int p_pos) {
+ h_scroll->set_value(p_pos);
+ _cancel_drag();
+}
+
+int ScrollContainer::get_h_scroll() const {
+ return h_scroll->get_value();
+}
+
+void ScrollContainer::set_v_scroll(int p_pos) {
+ v_scroll->set_value(p_pos);
+ _cancel_drag();
+}
+
+int ScrollContainer::get_v_scroll() const {
+ return v_scroll->get_value();
+}
+
void ScrollContainer::set_enable_h_scroll(bool p_enable) {
if (scroll_h == p_enable) {
return;
@@ -508,22 +501,30 @@ bool ScrollContainer::is_v_scroll_enabled() const {
return scroll_v;
}
-int ScrollContainer::get_v_scroll() const {
- return v_scroll->get_value();
+void ScrollContainer::set_h_scroll_visible(bool p_visible) {
+ if (h_scroll_visible == p_visible) {
+ return;
+ }
+
+ h_scroll_visible = p_visible;
+ update_scrollbars();
}
-void ScrollContainer::set_v_scroll(int p_pos) {
- v_scroll->set_value(p_pos);
- _cancel_drag();
+bool ScrollContainer::is_h_scroll_visible() const {
+ return h_scroll_visible;
}
-int ScrollContainer::get_h_scroll() const {
- return h_scroll->get_value();
+void ScrollContainer::set_v_scroll_visible(bool p_visible) {
+ if (v_scroll_visible == p_visible) {
+ return;
+ }
+
+ v_scroll_visible = p_visible;
+ update_scrollbars();
}
-void ScrollContainer::set_h_scroll(int p_pos) {
- h_scroll->set_value(p_pos);
- _cancel_drag();
+bool ScrollContainer::is_v_scroll_visible() const {
+ return v_scroll_visible;
}
int ScrollContainer::get_deadzone() const {
@@ -542,8 +543,8 @@ void ScrollContainer::set_follow_focus(bool p_follow) {
follow_focus = p_follow;
}
-String ScrollContainer::get_configuration_warning() const {
- String warning = Container::get_configuration_warning();
+TypedArray<String> ScrollContainer::get_configuration_warnings() const {
+ TypedArray<String> warnings = Container::get_configuration_warnings();
int found = 0;
@@ -563,12 +564,10 @@ String ScrollContainer::get_configuration_warning() const {
}
if (found != 1) {
- if (!warning.is_empty()) {
- warning += "\n\n";
- }
- warning += TTR("ScrollContainer is intended to work with a single child control.\nUse a container as child (VBox, HBox, etc.), or a Control and set the custom minimum size manually.");
+ warnings.push_back(TTR("ScrollContainer is intended to work with a single child control.\nUse a container as child (VBox, HBox, etc.), or a Control and set the custom minimum size manually."));
}
- return warning;
+
+ return warnings;
}
HScrollBar *ScrollContainer::get_h_scrollbar() {
@@ -581,17 +580,29 @@ VScrollBar *ScrollContainer::get_v_scrollbar() {
void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &ScrollContainer::_gui_input);
- ClassDB::bind_method(D_METHOD("set_enable_h_scroll", "enable"), &ScrollContainer::set_enable_h_scroll);
- ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &ScrollContainer::is_h_scroll_enabled);
- ClassDB::bind_method(D_METHOD("set_enable_v_scroll", "enable"), &ScrollContainer::set_enable_v_scroll);
- ClassDB::bind_method(D_METHOD("is_v_scroll_enabled"), &ScrollContainer::is_v_scroll_enabled);
ClassDB::bind_method(D_METHOD("_update_scrollbar_position"), &ScrollContainer::_update_scrollbar_position);
+
ClassDB::bind_method(D_METHOD("set_h_scroll", "value"), &ScrollContainer::set_h_scroll);
ClassDB::bind_method(D_METHOD("get_h_scroll"), &ScrollContainer::get_h_scroll);
+
ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &ScrollContainer::set_v_scroll);
ClassDB::bind_method(D_METHOD("get_v_scroll"), &ScrollContainer::get_v_scroll);
+
+ ClassDB::bind_method(D_METHOD("set_enable_h_scroll", "enable"), &ScrollContainer::set_enable_h_scroll);
+ ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &ScrollContainer::is_h_scroll_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_enable_v_scroll", "enable"), &ScrollContainer::set_enable_v_scroll);
+ ClassDB::bind_method(D_METHOD("is_v_scroll_enabled"), &ScrollContainer::is_v_scroll_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_h_scroll_visible", "visible"), &ScrollContainer::set_h_scroll_visible);
+ ClassDB::bind_method(D_METHOD("is_h_scroll_visible"), &ScrollContainer::is_h_scroll_visible);
+
+ ClassDB::bind_method(D_METHOD("set_v_scroll_visible", "visible"), &ScrollContainer::set_v_scroll_visible);
+ ClassDB::bind_method(D_METHOD("is_v_scroll_visible"), &ScrollContainer::is_v_scroll_visible);
+
ClassDB::bind_method(D_METHOD("set_deadzone", "deadzone"), &ScrollContainer::set_deadzone);
ClassDB::bind_method(D_METHOD("get_deadzone"), &ScrollContainer::get_deadzone);
+
ClassDB::bind_method(D_METHOD("set_follow_focus", "enabled"), &ScrollContainer::set_follow_focus);
ClassDB::bind_method(D_METHOD("is_following_focus"), &ScrollContainer::is_following_focus);
@@ -604,10 +615,12 @@ void ScrollContainer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_focus"), "set_follow_focus", "is_following_focus");
ADD_GROUP("Scroll", "scroll_");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_enable_h_scroll", "is_h_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_enable_v_scroll", "is_v_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical"), "set_v_scroll", "get_v_scroll");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_enable_h_scroll", "is_h_scroll_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_enable_v_scroll", "is_v_scroll_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_visible"), "set_h_scroll_visible", "is_h_scroll_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_visible"), "set_v_scroll_visible", "is_v_scroll_visible");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone");
GLOBAL_DEF("gui/common/default_scroll_deadzone", 0);
@@ -624,15 +637,7 @@ ScrollContainer::ScrollContainer() {
add_child(v_scroll);
v_scroll->connect("value_changed", callable_mp(this, &ScrollContainer::_scroll_moved));
- drag_speed = Vector2();
- drag_touching = false;
- drag_touching_deaccel = false;
- beyond_deadzone = false;
- scroll_h = true;
- scroll_v = true;
-
deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone");
- follow_focus = false;
set_clip_contents(true);
};
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 7e32cbed1c..f61df70b85 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -42,7 +42,6 @@ class ScrollContainer : public Container {
VScrollBar *v_scroll;
Size2 child_max_size;
- Size2 scroll;
void update_scrollbars();
@@ -50,18 +49,19 @@ class ScrollContainer : public Container {
Vector2 drag_accum;
Vector2 drag_from;
Vector2 last_drag_accum;
- float last_drag_time;
- float time_since_motion;
- bool drag_touching;
- bool drag_touching_deaccel;
- bool click_handled;
- bool beyond_deadzone;
+ float time_since_motion = 0.0f;
+ bool drag_touching = false;
+ bool drag_touching_deaccel = false;
+ bool beyond_deadzone = false;
- bool scroll_h;
- bool scroll_v;
+ bool scroll_h = true;
+ bool scroll_v = true;
- int deadzone;
- bool follow_focus;
+ bool h_scroll_visible = true;
+ bool v_scroll_visible = true;
+
+ int deadzone = 0;
+ bool follow_focus = false;
void _cancel_drag();
@@ -80,11 +80,11 @@ protected:
void _ensure_focused_visible(Control *p_node);
public:
- int get_v_scroll() const;
- void set_v_scroll(int p_pos);
-
- int get_h_scroll() const;
void set_h_scroll(int p_pos);
+ int get_h_scroll() const;
+
+ void set_v_scroll(int p_pos);
+ int get_v_scroll() const;
void set_enable_h_scroll(bool p_enable);
bool is_h_scroll_enabled() const;
@@ -92,6 +92,12 @@ public:
void set_enable_v_scroll(bool p_enable);
bool is_v_scroll_enabled() const;
+ void set_h_scroll_visible(bool p_visible);
+ bool is_h_scroll_visible() const;
+
+ void set_v_scroll_visible(bool p_visible);
+ bool is_v_scroll_visible() const;
+
int get_deadzone() const;
void set_deadzone(int p_deadzone);
@@ -103,7 +109,7 @@ public:
virtual bool clips_input() const override;
- virtual String get_configuration_warning() const override;
+ TypedArray<String> get_configuration_warnings() const override;
ScrollContainer();
};
diff --git a/scene/gui/separator.h b/scene/gui/separator.h
index 1ac0f2c503..77162c68fa 100644
--- a/scene/gui/separator.h
+++ b/scene/gui/separator.h
@@ -36,7 +36,7 @@ class Separator : public Control {
GDCLASS(Separator, Control);
protected:
- Orientation orientation;
+ Orientation orientation = Orientation::HORIZONTAL;
void _notification(int p_what);
public:
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index 0edf1856de..a407ef21cb 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -46,6 +46,8 @@ Size2 Slider::get_minimum_size() const {
}
void Slider::_gui_input(Ref<InputEvent> p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
if (!editable) {
return;
}
@@ -53,7 +55,7 @@ void Slider::_gui_input(Ref<InputEvent> p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == BUTTON_LEFT) {
+ if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
if (mb->is_pressed()) {
Ref<Texture2D> grabber = get_theme_icon(mouse_inside || has_focus() ? "grabber_highlight" : "grabber");
grab.pos = orientation == VERTICAL ? mb->get_position().y : mb->get_position().x;
@@ -72,9 +74,11 @@ void Slider::_gui_input(Ref<InputEvent> p_event) {
grab.active = false;
}
} else if (scrollable) {
- if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) {
+ if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP) {
+ grab_focus();
set_value(get_value() + get_step());
- } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) {
+ } else if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) {
+ grab_focus();
set_value(get_value() - get_step());
}
}
@@ -269,12 +273,5 @@ void Slider::_bind_methods() {
Slider::Slider(Orientation p_orientation) {
orientation = p_orientation;
- mouse_inside = false;
- grab.active = false;
- ticks = 0;
- ticks_on_borders = false;
- 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 292931b7b5..65a4036cd1 100644
--- a/scene/gui/slider.h
+++ b/scene/gui/slider.h
@@ -37,23 +37,23 @@ class Slider : public Range {
GDCLASS(Slider, Range);
struct Grab {
- int pos;
- float uvalue;
- bool active;
+ int pos = 0;
+ float uvalue = 0.0;
+ bool active = false;
} grab;
- int ticks;
- bool mouse_inside;
+ int ticks = 0;
+ bool mouse_inside = false;
Orientation orientation;
- float custom_step;
- bool editable;
- bool scrollable;
+ float custom_step = -1.0;
+ bool editable = true;
+ bool scrollable = true;
protected:
void _gui_input(Ref<InputEvent> p_event);
void _notification(int p_what);
static void _bind_methods();
- bool ticks_on_borders;
+ bool ticks_on_borders = false;
public:
virtual Size2 get_minimum_size() const override;
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 037a60810e..9dc2afdb2d 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -76,7 +76,7 @@ void SpinBox::_line_edit_input(const Ref<InputEvent> &p_event) {
}
void SpinBox::_range_click_timeout() {
- if (!drag.enabled && Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT)) {
+ if (!drag.enabled && Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT)) {
bool up = get_local_mouse_position().y < (get_size().height / 2);
set_value(get_value() + (up ? get_step() : -get_step()));
@@ -91,7 +91,17 @@ void SpinBox::_range_click_timeout() {
}
}
+void SpinBox::_release_mouse() {
+ if (drag.enabled) {
+ drag.enabled = false;
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+ warp_mouse(drag.capture_pos);
+ }
+}
+
void SpinBox::_gui_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
if (!is_editable()) {
return;
}
@@ -102,7 +112,7 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) {
bool up = mb->get_position().y < (get_size().height / 2);
switch (mb->get_button_index()) {
- case BUTTON_LEFT: {
+ case MOUSE_BUTTON_LEFT: {
line_edit->grab_focus();
set_value(get_value() + (up ? get_step() : -get_step()));
@@ -114,17 +124,17 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) {
drag.allowed = true;
drag.capture_pos = mb->get_position();
} break;
- case BUTTON_RIGHT: {
+ case MOUSE_BUTTON_RIGHT: {
line_edit->grab_focus();
set_value((up ? get_max() : get_min()));
} break;
- case BUTTON_WHEEL_UP: {
+ case MOUSE_BUTTON_WHEEL_UP: {
if (line_edit->has_focus()) {
set_value(get_value() + get_step() * mb->get_factor());
accept_event();
}
} break;
- case BUTTON_WHEEL_DOWN: {
+ case MOUSE_BUTTON_WHEEL_DOWN: {
if (line_edit->has_focus()) {
set_value(get_value() - get_step() * mb->get_factor());
accept_event();
@@ -133,21 +143,16 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
//set_default_cursor_shape(CURSOR_ARROW);
range_click_timer->stop();
-
- if (drag.enabled) {
- drag.enabled = false;
- Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
- warp_mouse(drag.capture_pos);
- }
+ _release_mouse();
drag.allowed = false;
}
Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
+ if (mm.is_valid() && mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) {
if (drag.enabled) {
drag.diff_y += mm->get_relative().y;
float diff_y = -0.01 * Math::pow(ABS(drag.diff_y), 1.8f) * SGN(drag.diff_y);
@@ -199,6 +204,8 @@ void SpinBox::_notification(int p_what) {
} else if (p_what == NOTIFICATION_ENTER_TREE) {
_adjust_width_for_icon(get_theme_icon("updown"));
_value_changed(0);
+ } else if (p_what == NOTIFICATION_EXIT_TREE) {
+ _release_mouse();
} else if (p_what == NOTIFICATION_TRANSLATION_CHANGED) {
_value_changed(0);
} else if (p_what == NOTIFICATION_THEME_CHANGED) {
@@ -268,7 +275,6 @@ void SpinBox::_bind_methods() {
}
SpinBox::SpinBox() {
- last_w = 0;
line_edit = memnew(LineEdit);
add_child(line_edit);
@@ -280,7 +286,6 @@ SpinBox::SpinBox() {
line_edit->connect("text_entered", callable_mp(this, &SpinBox::_text_entered), Vector<Variant>(), CONNECT_DEFERRED);
line_edit->connect("focus_exited", callable_mp(this, &SpinBox::_line_edit_focus_exit), Vector<Variant>(), CONNECT_DEFERRED);
line_edit->connect("gui_input", callable_mp(this, &SpinBox::_line_edit_input));
- drag.enabled = false;
range_click_timer = memnew(Timer);
range_click_timer->connect("timeout", callable_mp(this, &SpinBox::_range_click_timeout));
diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h
index 0647ec005b..e116adb64c 100644
--- a/scene/gui/spin_box.h
+++ b/scene/gui/spin_box.h
@@ -39,10 +39,11 @@ class SpinBox : public Range {
GDCLASS(SpinBox, Range);
LineEdit *line_edit;
- int last_w;
+ int last_w = 0;
Timer *range_click_timer;
void _range_click_timeout();
+ void _release_mouse();
void _text_entered(const String &p_string);
virtual void _value_changed(double) override;
@@ -52,11 +53,11 @@ class SpinBox : public Range {
void _line_edit_input(const Ref<InputEvent> &p_event);
struct Drag {
- float base_val;
- bool allowed;
- bool enabled;
+ float base_val = 0.0;
+ bool allowed = false;
+ bool enabled = false;
Vector2 capture_pos;
- float diff_y;
+ float diff_y = 0.0;
} drag;
void _line_edit_focus_exit();
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index d6f2df1d8c..13ff2c5b86 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -102,7 +102,7 @@ void SplitContainer::_resort() {
middle_sep += clamped_split_offset;
if (should_clamp_split_offset) {
split_offset = clamped_split_offset;
- _change_notify("split_offset");
+
should_clamp_split_offset = false;
}
}
@@ -207,6 +207,8 @@ void SplitContainer::_notification(int p_what) {
}
void SplitContainer::_gui_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
if (collapsed || !_getch(0) || !_getch(1) || dragger_visibility != DRAGGER_VISIBLE) {
return;
}
@@ -214,7 +216,7 @@ void SplitContainer::_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == BUTTON_LEFT) {
+ if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
if (mb->is_pressed()) {
int sep = get_theme_constant("separation");
@@ -359,12 +361,5 @@ void SplitContainer::_bind_methods() {
}
SplitContainer::SplitContainer(bool p_vertical) {
- mouse_inside = false;
- split_offset = 0;
- should_clamp_split_offset = false;
- middle_sep = 0;
vertical = p_vertical;
- dragging = false;
- collapsed = false;
- dragger_visibility = DRAGGER_VISIBLE;
}
diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h
index 46a6792206..6cb94d6ecf 100644
--- a/scene/gui/split_container.h
+++ b/scene/gui/split_container.h
@@ -44,16 +44,16 @@ public:
};
private:
- bool should_clamp_split_offset;
- int split_offset;
- int middle_sep;
- bool vertical;
- bool dragging;
- int drag_from;
- int drag_ofs;
- bool collapsed;
- DraggerVisibility dragger_visibility;
- bool mouse_inside;
+ bool should_clamp_split_offset = false;
+ int split_offset = 0;
+ int middle_sep = 0;
+ bool vertical = false;
+ bool dragging = false;
+ int drag_from = 0;
+ int drag_ofs = 0;
+ bool collapsed = false;
+ DraggerVisibility dragger_visibility = DRAGGER_VISIBLE;
+ bool mouse_inside = false;
Control *_getch(int p_idx) const;
diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp
index 68eef0f96b..bfc7e29f9c 100644
--- a/scene/gui/subviewport_container.cpp
+++ b/scene/gui/subviewport_container.cpp
@@ -140,6 +140,8 @@ void SubViewportContainer::_notification(int p_what) {
}
void SubViewportContainer::_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
@@ -165,6 +167,8 @@ void SubViewportContainer::_input(const Ref<InputEvent> &p_event) {
}
void SubViewportContainer::_unhandled_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
@@ -203,8 +207,6 @@ void SubViewportContainer::_bind_methods() {
}
SubViewportContainer::SubViewportContainer() {
- stretch = false;
- shrink = 1;
set_process_input(true);
set_process_unhandled_input(true);
}
diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h
index 91fc29d377..77cf4c16b3 100644
--- a/scene/gui/subviewport_container.h
+++ b/scene/gui/subviewport_container.h
@@ -36,8 +36,8 @@
class SubViewportContainer : public Container {
GDCLASS(SubViewportContainer, Container);
- bool stretch;
- int shrink;
+ bool stretch = false;
+ int shrink = 1;
protected:
void _notification(int p_what);
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 64a2a1843d..ff9dafa0f9 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -43,11 +43,11 @@ int TabContainer::_get_top_margin() const {
}
// Respect the minimum tab height.
- Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg");
- Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg");
+ Ref<StyleBox> tab_unselected = get_theme_stylebox("tab_unselected");
+ Ref<StyleBox> tab_selected = get_theme_stylebox("tab_selected");
Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled");
- int tab_height = MAX(MAX(tab_bg->get_minimum_size().height, tab_fg->get_minimum_size().height), tab_disabled->get_minimum_size().height);
+ int tab_height = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height);
// Font height or higher icon wins.
int content_height = 0;
@@ -72,11 +72,13 @@ int TabContainer::_get_top_margin() const {
}
void TabContainer::_gui_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
Ref<InputEventMouseButton> mb = p_event;
Popup *popup = get_popup();
- if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
Point2 pos(mb->get_position().x, mb->get_position().y);
Size2 size = get_size();
@@ -337,8 +339,8 @@ void TabContainer::_notification(int p_what) {
}
Vector<Control *> tabs = _get_tabs();
- Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg");
- Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg");
+ Ref<StyleBox> tab_unselected = get_theme_stylebox("tab_unselected");
+ Ref<StyleBox> tab_selected = get_theme_stylebox("tab_selected");
Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled");
Ref<Texture2D> increment = get_theme_icon("increment");
Ref<Texture2D> increment_hl = get_theme_icon("increment_highlight");
@@ -346,9 +348,9 @@ void TabContainer::_notification(int p_what) {
Ref<Texture2D> decrement_hl = get_theme_icon("decrement_highlight");
Ref<Texture2D> menu = get_theme_icon("menu");
Ref<Texture2D> menu_hl = get_theme_icon("menu_highlight");
- Color font_color_fg = get_theme_color("font_color_fg");
- Color font_color_bg = get_theme_color("font_color_bg");
- Color font_color_disabled = get_theme_color("font_color_disabled");
+ Color font_selected_color = get_theme_color("font_selected_color");
+ Color font_unselected_color = get_theme_color("font_unselected_color");
+ Color font_disabled_color = get_theme_color("font_disabled_color");
int side_margin = get_theme_constant("side_margin");
// Find out start and width of the header area.
@@ -433,17 +435,17 @@ void TabContainer::_notification(int p_what) {
int tab_width = tab_widths[i];
if (get_tab_disabled(index)) {
if (rtl) {
- _draw_tab(tab_disabled, font_color_disabled, index, size.width - (tabs_ofs_cache + x) - tab_width);
+ _draw_tab(tab_disabled, font_disabled_color, index, size.width - (tabs_ofs_cache + x) - tab_width);
} else {
- _draw_tab(tab_disabled, font_color_disabled, index, tabs_ofs_cache + x);
+ _draw_tab(tab_disabled, font_disabled_color, index, tabs_ofs_cache + x);
}
} else if (index == current) {
x_current = x;
} else {
if (rtl) {
- _draw_tab(tab_bg, font_color_bg, index, size.width - (tabs_ofs_cache + x) - tab_width);
+ _draw_tab(tab_unselected, font_unselected_color, index, size.width - (tabs_ofs_cache + x) - tab_width);
} else {
- _draw_tab(tab_bg, font_color_bg, index, tabs_ofs_cache + x);
+ _draw_tab(tab_unselected, font_unselected_color, index, tabs_ofs_cache + x);
}
}
@@ -459,9 +461,9 @@ void TabContainer::_notification(int p_what) {
// Draw selected tab in front. only draw selected tab when it's in visible range.
if (tabs.size() > 0 && current - first_tab_cache < tab_widths.size() && current >= first_tab_cache) {
if (rtl) {
- _draw_tab(tab_fg, font_color_fg, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]);
+ _draw_tab(tab_selected, font_selected_color, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]);
} else {
- _draw_tab(tab_fg, font_color_fg, current, tabs_ofs_cache + x_current);
+ _draw_tab(tab_selected, font_selected_color, current, tabs_ofs_cache + x_current);
}
}
@@ -535,6 +537,8 @@ void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, in
Vector<Control *> tabs = _get_tabs();
RID canvas = get_canvas_item();
Ref<Font> font = get_theme_font("font");
+ Color font_outline_color = get_theme_color("font_outline_color");
+ int outline_size = get_theme_constant("outline_size");
int icon_text_distance = get_theme_constant("icon_separation");
int tab_width = _get_tab_width(p_index);
int header_height = _get_top_margin();
@@ -565,6 +569,9 @@ void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, in
// Draw the tab text.
Point2i text_pos(x_content, y_center - text_buf[p_index]->get_size().y / 2);
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ text_buf[p_index]->draw_outline(canvas, text_pos, outline_size, font_outline_color);
+ }
text_buf[p_index]->draw(canvas, text_pos, p_font_color);
}
@@ -640,7 +647,7 @@ int TabContainer::_get_tab_width(int p_index) const {
// Get the width of the text displayed on the tab.
Ref<Font> font = get_theme_font("font");
int font_size = get_theme_font_size("font_size");
- String text = control->has_meta("_tab_name") ? String(tr(String(control->get_meta("_tab_name")))) : String(control->get_name());
+ String text = control->has_meta("_tab_name") ? String(tr(String(control->get_meta("_tab_name")))) : String(tr(control->get_name()));
int width = font->get_string_size(text, font_size).width;
// Add space for a tab icon.
@@ -655,15 +662,15 @@ int TabContainer::_get_tab_width(int p_index) const {
}
// Respect a minimum size.
- Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg");
- Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg");
+ Ref<StyleBox> tab_unselected = get_theme_stylebox("tab_unselected");
+ Ref<StyleBox> tab_selected = get_theme_stylebox("tab_selected");
Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled");
if (get_tab_disabled(p_index)) {
width += tab_disabled->get_minimum_size().width;
} else if (p_index == current) {
- width += tab_fg->get_minimum_size().width;
+ width += tab_selected->get_minimum_size().width;
} else {
- width += tab_bg->get_minimum_size().width;
+ width += tab_unselected->get_minimum_size().width;
}
return width;
@@ -747,8 +754,6 @@ void TabContainer::set_current_tab(int p_current) {
_repaint();
- _change_notify("current_tab");
-
if (pending_previous == current) {
emit_signal("tab_selected", current);
} else {
@@ -967,8 +972,6 @@ void TabContainer::set_tab_align(TabAlign p_align) {
ERR_FAIL_INDEX(p_align, 3);
align = p_align;
update();
-
- _change_notify("tab_align");
}
TabContainer::TabAlign TabContainer::get_tab_align() const {
@@ -1022,6 +1025,7 @@ void TabContainer::set_tab_title(int p_tab, const String &p_title) {
Control *child = _get_tab(p_tab);
ERR_FAIL_COND(!child);
child->set_meta("_tab_name", p_title);
+ _refresh_texts();
update();
}
@@ -1131,13 +1135,13 @@ Size2 TabContainer::get_minimum_size() const {
ms.y = MAX(ms.y, cms.y);
}
- Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg");
- Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg");
+ Ref<StyleBox> tab_unselected = get_theme_stylebox("tab_unselected");
+ Ref<StyleBox> tab_selected = get_theme_stylebox("tab_selected");
Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled");
Ref<Font> font = get_theme_font("font");
if (tabs_visible) {
- ms.y += MAX(MAX(tab_bg->get_minimum_size().y, tab_fg->get_minimum_size().y), tab_disabled->get_minimum_size().y);
+ ms.y += MAX(MAX(tab_unselected->get_minimum_size().y, tab_selected->get_minimum_size().y), tab_disabled->get_minimum_size().y);
ms.y += _get_top_margin();
}
@@ -1243,20 +1247,5 @@ void TabContainer::_bind_methods() {
}
TabContainer::TabContainer() {
- first_tab_cache = 0;
- last_tab_cache = 0;
- buttons_visible_cache = false;
- menu_hovered = false;
- highlight_arrow = -1;
- tabs_ofs_cache = 0;
- current = 0;
- previous = 0;
- align = ALIGN_CENTER;
- tabs_visible = true;
- all_tabs_in_front = false;
- drag_to_rearrange_enabled = false;
- tabs_rearrange_group = -1;
- use_hidden_tabs_for_min_size = false;
-
connect("mouse_exited", callable_mp(this, &TabContainer::_on_mouse_exited));
}
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 58742c5be8..4ed5255729 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -46,23 +46,23 @@ public:
};
private:
- int first_tab_cache;
- int tabs_ofs_cache;
- int last_tab_cache;
- int current;
- int previous;
- bool tabs_visible;
- bool all_tabs_in_front;
- bool buttons_visible_cache;
- bool menu_hovered;
- int highlight_arrow;
- TabAlign align;
+ int first_tab_cache = 0;
+ int tabs_ofs_cache = 0;
+ int last_tab_cache = 0;
+ int current = 0;
+ int previous = 0;
+ bool tabs_visible = true;
+ bool all_tabs_in_front = false;
+ bool buttons_visible_cache = false;
+ bool menu_hovered = false;
+ int highlight_arrow = -1;
+ TabAlign align = ALIGN_CENTER;
Control *_get_tab(int p_idx) const;
int _get_top_margin() const;
mutable ObjectID popup_obj_id;
- bool drag_to_rearrange_enabled;
- bool use_hidden_tabs_for_min_size;
- int tabs_rearrange_group;
+ bool drag_to_rearrange_enabled = false;
+ bool use_hidden_tabs_for_min_size = false;
+ int tabs_rearrange_group = -1;
Vector<Ref<TextLine>> text_buf;
Vector<Control *> _get_tabs() const;
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index 3bf71d6c01..6cbc5890ce 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -38,11 +38,11 @@
#include "scene/gui/texture_rect.h"
Size2 Tabs::get_minimum_size() const {
- Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg");
- Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg");
+ Ref<StyleBox> tab_unselected = get_theme_stylebox("tab_unselected");
+ Ref<StyleBox> tab_selected = get_theme_stylebox("tab_selected");
Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled");
- int y_margin = MAX(MAX(tab_bg->get_minimum_size().height, tab_fg->get_minimum_size().height), tab_disabled->get_minimum_size().height);
+ int y_margin = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height);
Size2 ms(0, 0);
@@ -61,9 +61,9 @@ Size2 Tabs::get_minimum_size() const {
if (tabs[i].disabled) {
ms.width += tab_disabled->get_minimum_size().width;
} else if (current == i) {
- ms.width += tab_fg->get_minimum_size().width;
+ ms.width += tab_selected->get_minimum_size().width;
} else {
- ms.width += tab_bg->get_minimum_size().width;
+ ms.width += tab_unselected->get_minimum_size().width;
}
if (tabs[i].right_button.is_valid()) {
@@ -71,7 +71,7 @@ Size2 Tabs::get_minimum_size() const {
Size2 bms = rb->get_size();
bms.width += get_theme_constant("hseparation");
ms.width += bms.width;
- ms.height = MAX(bms.height + tab_bg->get_minimum_size().height, ms.height);
+ ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height);
}
if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
@@ -79,15 +79,20 @@ Size2 Tabs::get_minimum_size() const {
Size2 bms = cb->get_size();
bms.width += get_theme_constant("hseparation");
ms.width += bms.width;
- ms.height = MAX(bms.height + tab_bg->get_minimum_size().height, ms.height);
+ ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height);
}
}
- ms.width = 0; //TODO: should make this optional
+ if (clip_tabs) {
+ ms.width = 0;
+ }
+
return ms;
}
void Tabs::_gui_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
@@ -105,10 +110,10 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) {
highlight_arrow = 0;
}
} else {
- int limit = get_size().width - incr->get_width() - decr->get_width();
- if (pos.x > limit + decr->get_width()) {
+ int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
+ if (pos.x > limit_minus_buttons + decr->get_width()) {
highlight_arrow = 1;
- } else if (pos.x > limit) {
+ } else if (pos.x > limit_minus_buttons) {
highlight_arrow = 0;
}
}
@@ -122,7 +127,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP && !mb->get_command()) {
+ if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && !mb->get_command()) {
if (scrolling_enabled && buttons_visible) {
if (offset > 0) {
offset--;
@@ -131,7 +136,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN && !mb->get_command()) {
+ if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && !mb->get_command()) {
if (scrolling_enabled && buttons_visible) {
if (missing_right) {
offset++;
@@ -140,7 +145,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
if (rb_hover != -1) {
//pressed
emit_signal("right_button_pressed", rb_hover);
@@ -150,7 +155,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) {
update();
}
- if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
if (cb_hover != -1) {
//pressed
emit_signal("tab_closed", cb_hover);
@@ -160,7 +165,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) {
update();
}
- if (mb->is_pressed() && (mb->get_button_index() == BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == BUTTON_RIGHT))) {
+ if (mb->is_pressed() && (mb->get_button_index() == MOUSE_BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == MOUSE_BUTTON_RIGHT))) {
// clicks
Point2 pos(mb->get_position().x, mb->get_position().y);
@@ -268,13 +273,16 @@ void Tabs::_notification(int p_what) {
_update_cache();
RID ci = get_canvas_item();
- Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg");
- Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg");
+ Ref<StyleBox> tab_unselected = get_theme_stylebox("tab_unselected");
+ Ref<StyleBox> tab_selected = get_theme_stylebox("tab_selected");
Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled");
- Color color_fg = get_theme_color("font_color_fg");
- Color color_bg = get_theme_color("font_color_bg");
- Color color_disabled = get_theme_color("font_color_disabled");
+ Color font_selected_color = get_theme_color("font_selected_color");
+ Color font_unselected_color = get_theme_color("font_unselected_color");
+ Color font_disabled_color = get_theme_color("font_disabled_color");
Ref<Texture2D> close = get_theme_icon("close");
+ Color font_outline_color = get_theme_color("font_outline_color");
+ int outline_size = get_theme_constant("outline_size");
+
Vector2 size = get_size();
bool rtl = is_layout_rtl();
@@ -302,7 +310,8 @@ void Tabs::_notification(int p_what) {
Ref<Texture2D> incr_hl = get_theme_icon("increment_highlight");
Ref<Texture2D> decr_hl = get_theme_icon("decrement_highlight");
- int limit = get_size().width - incr->get_size().width - decr->get_size().width;
+ int limit = get_size().width;
+ int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
missing_right = false;
@@ -316,16 +325,17 @@ void Tabs::_notification(int p_what) {
if (tabs[i].disabled) {
sb = tab_disabled;
- col = color_disabled;
+ col = font_disabled_color;
} else if (i == current) {
- sb = tab_fg;
- col = color_fg;
+ sb = tab_selected;
+ col = font_selected_color;
} else {
- sb = tab_bg;
- col = color_bg;
+ sb = tab_unselected;
+ col = font_unselected_color;
}
- if (w + lsize > limit) {
+ int new_width = w + lsize;
+ if (new_width > limit || (i < tabs.size() - 1 && new_width > limit_minus_buttons)) { // For the last tab, we accept if the tab covers the buttons.
max_drawn_tab = i - 1;
missing_right = true;
break;
@@ -357,9 +367,17 @@ void Tabs::_notification(int p_what) {
}
if (rtl) {
- tabs[i].text_buf->draw(ci, Point2i(size.width - w - tabs[i].text_buf->get_size().x, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2), col);
+ Vector2 text_pos = Point2i(size.width - w - tabs[i].text_buf->get_size().x, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2);
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
+ tabs[i].text_buf->draw(ci, text_pos, col);
} else {
- tabs[i].text_buf->draw(ci, Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2), col);
+ Vector2 text_pos = Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2);
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
+ tabs[i].text_buf->draw(ci, text_pos, col);
}
w += tabs[i].size_text;
@@ -448,15 +466,15 @@ void Tabs::_notification(int p_what) {
}
} else {
if (offset > 0) {
- draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit, vofs));
+ draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit_minus_buttons, vofs));
} else {
- draw_texture(decr, Point2(limit, vofs), Color(1, 1, 1, 0.5));
+ draw_texture(decr, Point2(limit_minus_buttons, vofs), Color(1, 1, 1, 0.5));
}
if (missing_right) {
- draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit + decr->get_size().width, vofs));
+ draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit_minus_buttons + decr->get_size().width, vofs));
} else {
- draw_texture(incr, Point2(limit + decr->get_size().width, vofs), Color(1, 1, 1, 0.5));
+ draw_texture(incr, Point2(limit_minus_buttons + decr->get_size().width, vofs), Color(1, 1, 1, 0.5));
}
}
@@ -481,7 +499,6 @@ void Tabs::set_current_tab(int p_current) {
previous = current;
current = p_current;
- _change_notify("current_tab");
_update_cache();
update();
@@ -652,11 +669,11 @@ void Tabs::_update_hover() {
void Tabs::_update_cache() {
Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled");
- Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg");
- Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg");
+ Ref<StyleBox> tab_unselected = get_theme_stylebox("tab_unselected");
+ Ref<StyleBox> tab_selected = get_theme_stylebox("tab_selected");
Ref<Texture2D> incr = get_theme_icon("increment");
Ref<Texture2D> decr = get_theme_icon("decrement");
- int limit = get_size().width - incr->get_width() - decr->get_width();
+ int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
int w = 0;
int mw = 0;
@@ -676,20 +693,20 @@ void Tabs::_update_cache() {
}
int m_width = min_width;
if (count_resize > 0) {
- m_width = MAX((limit - size_fixed) / count_resize, min_width);
+ m_width = MAX((limit_minus_buttons - size_fixed) / count_resize, min_width);
}
for (int i = offset; i < tabs.size(); i++) {
Ref<StyleBox> sb;
if (tabs[i].disabled) {
sb = tab_disabled;
} else if (i == current) {
- sb = tab_fg;
+ sb = tab_selected;
} else {
- sb = tab_bg;
+ sb = tab_unselected;
}
int lsize = tabs[i].size_cache;
int slen = tabs[i].size_text;
- if (min_width > 0 && mw > limit && i != current) {
+ if (min_width > 0 && mw > limit_minus_buttons && i != current) {
if (lsize > m_width) {
slen = m_width - (sb->get_margin(SIDE_LEFT) + sb->get_margin(SIDE_RIGHT));
if (tabs[i].icon.is_valid()) {
@@ -899,6 +916,19 @@ Tabs::TabAlign Tabs::get_tab_align() const {
return tab_align;
}
+void Tabs::set_clip_tabs(bool p_clip_tabs) {
+ if (clip_tabs == p_clip_tabs) {
+ return;
+ }
+ clip_tabs = p_clip_tabs;
+ update();
+ minimum_size_changed();
+}
+
+bool Tabs::get_clip_tabs() const {
+ return clip_tabs;
+}
+
void Tabs::move_tab(int from, int to) {
if (from == to) {
return;
@@ -918,8 +948,8 @@ void Tabs::move_tab(int from, int to) {
int Tabs::get_tab_width(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, tabs.size(), 0);
- Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg");
- Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg");
+ Ref<StyleBox> tab_unselected = get_theme_stylebox("tab_unselected");
+ Ref<StyleBox> tab_selected = get_theme_stylebox("tab_selected");
Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled");
int x = 0;
@@ -937,9 +967,9 @@ int Tabs::get_tab_width(int p_idx) const {
if (tabs[p_idx].disabled) {
x += tab_disabled->get_minimum_size().width;
} else if (current == p_idx) {
- x += tab_fg->get_minimum_size().width;
+ x += tab_selected->get_minimum_size().width;
} else {
- x += tab_bg->get_minimum_size().width;
+ x += tab_unselected->get_minimum_size().width;
}
if (tabs[p_idx].right_button.is_valid()) {
@@ -965,7 +995,8 @@ void Tabs::_ensure_no_over_offset() {
Ref<Texture2D> incr = get_theme_icon("increment");
Ref<Texture2D> decr = get_theme_icon("decrement");
- int limit = get_size().width - incr->get_width() - decr->get_width();
+ int limit = get_size().width;
+ int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
while (offset > 0) {
int total_w = 0;
@@ -973,7 +1004,7 @@ void Tabs::_ensure_no_over_offset() {
total_w += tabs[i].size_cache;
}
- if (total_w < limit) {
+ if ((buttons_visible && total_w < limit_minus_buttons) || total_w < limit) { // For the last tab, we accept if the tab covers the buttons.
offset--;
update();
} else {
@@ -1004,9 +1035,12 @@ void Tabs::ensure_tab_visible(int p_idx) {
int prev_offset = offset;
Ref<Texture2D> incr = get_theme_icon("increment");
Ref<Texture2D> decr = get_theme_icon("decrement");
- int limit = get_size().width - incr->get_width() - decr->get_width();
+ int limit = get_size().width;
+ int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
+
for (int i = offset; i <= p_idx; i++) {
- if (tabs[i].ofs_cache + tabs[i].size_cache > limit) {
+ int total_w = tabs[i].ofs_cache + tabs[i].size_cache;
+ if (total_w > limit || (buttons_visible && total_w > limit_minus_buttons)) {
offset++;
}
}
@@ -1095,6 +1129,8 @@ void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_tab", "title", "icon"), &Tabs::add_tab, DEFVAL(""), DEFVAL(Ref<Texture2D>()));
ClassDB::bind_method(D_METHOD("set_tab_align", "align"), &Tabs::set_tab_align);
ClassDB::bind_method(D_METHOD("get_tab_align"), &Tabs::get_tab_align);
+ ClassDB::bind_method(D_METHOD("set_clip_tabs", "clip_tabs"), &Tabs::set_clip_tabs);
+ ClassDB::bind_method(D_METHOD("get_clip_tabs"), &Tabs::get_clip_tabs);
ClassDB::bind_method(D_METHOD("get_tab_offset"), &Tabs::get_tab_offset);
ClassDB::bind_method(D_METHOD("get_offset_buttons_visible"), &Tabs::get_offset_buttons_visible);
ClassDB::bind_method(D_METHOD("ensure_tab_visible", "idx"), &Tabs::ensure_tab_visible);
@@ -1121,6 +1157,7 @@ void Tabs::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
@@ -1137,27 +1174,5 @@ void Tabs::_bind_methods() {
}
Tabs::Tabs() {
- current = 0;
- previous = 0;
- tab_align = ALIGN_CENTER;
- rb_hover = -1;
- rb_pressing = false;
- highlight_arrow = -1;
-
- cb_hover = -1;
- cb_pressing = false;
- cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER;
- offset = 0;
- max_drawn_tab = 0;
-
- select_with_rmb = false;
-
- min_width = 0;
- scrolling_enabled = true;
- buttons_visible = false;
- hover = -1;
- drag_to_rearrange_enabled = false;
- tabs_rearrange_group = -1;
-
connect("mouse_exited", callable_mp(this, &Tabs::_on_mouse_exited));
}
diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h
index 4396981004..61c9a5d96a 100644
--- a/scene/gui/tabs.h
+++ b/scene/gui/tabs.h
@@ -63,42 +63,43 @@ private:
Ref<TextLine> text_buf;
Ref<Texture2D> icon;
- int ofs_cache;
- bool disabled;
- int size_cache;
- int size_text;
- int x_cache;
- int x_size_cache;
+ int ofs_cache = 0;
+ bool disabled = false;
+ int size_cache = 0;
+ int size_text = 0;
+ int x_cache = 0;
+ int x_size_cache = 0;
Ref<Texture2D> right_button;
Rect2 rb_rect;
Rect2 cb_rect;
};
- int offset;
- int max_drawn_tab;
- int highlight_arrow;
- bool buttons_visible;
- bool missing_right;
+ int offset = 0;
+ int max_drawn_tab = 0;
+ int highlight_arrow = -1;
+ bool buttons_visible = false;
+ bool missing_right = false;
Vector<Tab> tabs;
- int current;
- int previous;
+ int current = 0;
+ int previous = 0;
int _get_top_margin() const;
- TabAlign tab_align;
- int rb_hover;
- bool rb_pressing;
+ TabAlign tab_align = ALIGN_CENTER;
+ bool clip_tabs = true;
+ int rb_hover = -1;
+ bool rb_pressing = false;
- bool select_with_rmb;
+ bool select_with_rmb = false;
- int cb_hover;
- bool cb_pressing;
- CloseButtonDisplayPolicy cb_displaypolicy;
+ int cb_hover = -1;
+ bool cb_pressing = false;
+ CloseButtonDisplayPolicy cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER;
- int hover; // Hovered tab.
- int min_width;
- bool scrolling_enabled;
- bool drag_to_rearrange_enabled;
- int tabs_rearrange_group;
+ int hover = -1; // Hovered tab.
+ int min_width = 0;
+ bool scrolling_enabled = true;
+ bool drag_to_rearrange_enabled = false;
+ int tabs_rearrange_group = -1;
int get_tab_width(int p_idx) const;
void _ensure_no_over_offset();
@@ -148,6 +149,9 @@ public:
void set_tab_align(TabAlign p_align);
TabAlign get_tab_align() const;
+ void set_clip_tabs(bool p_clip_tabs);
+ bool get_clip_tabs() const;
+
void move_tab(int from, int to);
void set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 7557d36298..ded912591f 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/input/input.h"
+#include "core/input/input_map.h"
#include "core/object/message_queue.h"
#include "core/object/script_language.h"
#include "core/os/keyboard.h"
@@ -202,7 +203,7 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_
// Apply tab align.
if (indent_size > 0) {
Vector<float> tabs;
- tabs.push_back(font->get_char_size('m', 0, font_size).width * indent_size);
+ tabs.push_back(font->get_char_size(' ', 0, font_size).width * indent_size);
text.write[p_line].data_buf->tab_align(tabs);
}
}
@@ -212,7 +213,7 @@ void TextEdit::Text::invalidate_all_lines() {
text.write[i].data_buf->set_width(width);
if (indent_size > 0) {
Vector<float> tabs;
- tabs.push_back(font->get_char_size('m', 0, font_size).width * indent_size);
+ tabs.push_back(font->get_char_size(' ', 0, font_size).width * indent_size);
text.write[i].data_buf->tab_align(tabs);
}
}
@@ -356,10 +357,10 @@ 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
+ // Warning: is_mouse_button_pressed(MOUSE_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 != SelectionMode::SELECTION_MODE_NONE) {
+ if (Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT) && selection.selecting_mode != SelectionMode::SELECTION_MODE_NONE) {
switch (selection.selecting_mode) {
case SelectionMode::SELECTION_MODE_POINTER: {
_update_selection_mode_pointer();
@@ -411,25 +412,16 @@ void TextEdit::_update_selection_mode_word() {
_get_mouse_pos(Point2i(mp.x, mp.y), row, col);
String line = text[row];
- int beg = CLAMP(col, 0, line.length());
- // If its the first selection and on whitespace make sure we grab the word instead.
- if (!selection.active) {
- while (beg > 0 && line[beg] <= 32) {
- beg--;
- }
- }
+ int cursor_pos = CLAMP(col, 0, line.length());
+ int beg = cursor_pos;
int end = beg;
- bool symbol = beg < line.length() && _is_symbol(line[beg]);
-
- // Get the word end and begin points.
- while (beg > 0 && line[beg - 1] > 32 && (symbol == _is_symbol(line[beg - 1]))) {
- beg--;
- }
- while (end < line.length() && line[end + 1] > 32 && (symbol == _is_symbol(line[end + 1]))) {
- end++;
- }
- if (end < line.length()) {
- end += 1;
+ Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(row)->get_rid());
+ for (int i = 0; i < words.size(); i++) {
+ if (words[i].x < cursor_pos && words[i].y > cursor_pos) {
+ beg = words[i].x;
+ end = words[i].y;
+ break;
+ }
}
// Initial selection.
@@ -500,7 +492,7 @@ void TextEdit::_update_minimap_click() {
int row;
_get_minimap_mouse_row(Point2i(mp.x, mp.y), row);
- if (row >= get_first_visible_line() && (row < get_last_visible_line() || row >= (text.size() - 1))) {
+ if (row >= get_first_visible_line() && (row < get_last_full_visible_line() || row >= (text.size() - 1))) {
minimap_scroll_ratio = v_scroll->get_as_ratio();
minimap_scroll_click_pos = mp.y;
can_drag_minimap = true;
@@ -637,7 +629,7 @@ void TextEdit::_notification(int p_what) {
int visible_rows = get_visible_rows() + 1;
- Color color = readonly ? cache.font_color_readonly : cache.font_color;
+ Color color = readonly ? cache.font_readonly_color : cache.font_color;
if (cache.background_color.a > 0.01) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), cache.background_color);
@@ -810,13 +802,13 @@ void TextEdit::_notification(int p_what) {
}
}
+ bool is_cursor_line_visible = false;
Point2 cursor_pos;
- int cursor_insert_offset_y = 0;
// Get the highlighted words.
String highlighted_text = get_selection_text();
- // Check if highlighted words contains only whitespaces (tabs or spaces).
+ // Check if highlighted words contain only whitespaces (tabs or spaces).
bool only_whitespaces_highlighted = highlighted_text.strip_edges() == String();
int cursor_wrap_index = get_cursor_wrap_index();
@@ -877,7 +869,7 @@ void TextEdit::_notification(int p_what) {
Color current_color = cache.font_color;
if (readonly) {
- current_color = cache.font_color_readonly;
+ current_color = cache.font_readonly_color;
}
Vector<String> wrap_rows = get_wrap_rows_text(minimap_line);
@@ -918,7 +910,7 @@ void TextEdit::_notification(int p_what) {
if (color_map.has(last_wrap_column + j)) {
current_color = color_map[last_wrap_column + j].get("color");
if (readonly) {
- current_color.a = cache.font_color_readonly.a;
+ current_color.a = cache.font_readonly_color.a;
}
}
color = current_color;
@@ -977,6 +969,16 @@ void TextEdit::_notification(int p_what) {
}
}
+ int top_limit_y = 0;
+ int bottom_limit_y = get_size().height;
+ if (readonly) {
+ top_limit_y += cache.style_readonly->get_margin(SIDE_TOP);
+ bottom_limit_y -= cache.style_readonly->get_margin(SIDE_BOTTOM);
+ } else {
+ top_limit_y += cache.style_normal->get_margin(SIDE_TOP);
+ bottom_limit_y -= cache.style_normal->get_margin(SIDE_BOTTOM);
+ }
+
// draw main text
int row_height = get_row_height();
int line = first_visible_line;
@@ -1001,7 +1003,7 @@ void TextEdit::_notification(int p_what) {
Dictionary color_map = _get_line_syntax_highlighting(line);
// Ensure we at least use the font color.
- Color current_color = readonly ? cache.font_color_readonly : cache.font_color;
+ Color current_color = readonly ? cache.font_readonly_color : cache.font_color;
const Ref<TextParagraph> ldata = text.get_line_data(line);
@@ -1019,17 +1021,33 @@ void TextEdit::_notification(int p_what) {
const String &str = wrap_rows[line_wrap_index];
int char_margin = xmargin_beg - cursor.x_ofs;
- int ofs_readonly = 0;
int ofs_x = 0;
+ int ofs_y = 0;
if (readonly) {
- ofs_readonly = cache.style_readonly->get_offset().y / 2;
ofs_x = cache.style_readonly->get_offset().x / 2;
+ ofs_x -= cache.style_normal->get_offset().x / 2;
+ ofs_y = cache.style_readonly->get_offset().y / 2;
+ } else {
+ ofs_y = cache.style_normal->get_offset().y / 2;
}
- int ofs_y = (i * row_height + cache.line_spacing / 2) + ofs_readonly;
+ ofs_y += i * row_height + cache.line_spacing / 2;
ofs_y -= cursor.wrap_ofs * row_height;
ofs_y -= get_v_scroll_offset() * row_height;
+ bool clipped = false;
+ if (ofs_y + row_height < top_limit_y) {
+ // Line is outside the top margin, clip current line.
+ // Still need to go through the process to prepare color changes for next lines.
+ clipped = true;
+ }
+
+ if (ofs_y > bottom_limit_y) {
+ // Line is outside the bottom margin, clip any remaining text.
+ i = draw_amount;
+ break;
+ }
+
if (text.is_marked(line)) {
if (rtl) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end - xmargin_beg, row_height), cache.mark_color);
@@ -1039,7 +1057,7 @@ void TextEdit::_notification(int p_what) {
}
if (str.length() == 0) {
- // Draw line background if empty as we won't loop at at all.
+ // Draw line background if empty as we won't loop at all.
if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
if (rtl) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), cache.current_line_color);
@@ -1050,7 +1068,7 @@ void TextEdit::_notification(int p_what) {
// Give visual indication of empty selected line.
if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) {
- int char_w = cache.font->get_char_size('m', 0, cache.font_size).width;
+ int char_w = cache.font->get_char_size(' ', 0, cache.font_size).width;
if (rtl) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - xmargin_beg - ofs_x - char_w, ofs_y, char_w, row_height), cache.selection_color);
} else {
@@ -1091,6 +1109,9 @@ void TextEdit::_notification(int p_what) {
tl->add_string(text, cache.font, cache.font_size);
int yofs = ofs_y + (row_height - tl->get_size().y) / 2;
+ if (cache.outline_size > 0 && cache.outline_color.a > 0) {
+ tl->draw_outline(ci, Point2(gutter_offset + ofs_x, yofs), cache.outline_size, cache.outline_color);
+ }
tl->draw(ci, Point2(gutter_offset + ofs_x, yofs), get_line_gutter_item_color(line, g));
} break;
case GUTTER_TPYE_ICON: {
@@ -1147,7 +1168,7 @@ void TextEdit::_notification(int p_what) {
char_margin = size.width - char_margin - TS->shaped_text_get_size(rid).x;
}
- if (selection.active && line >= selection.from_line && line <= selection.to_line) { // Selection
+ if (!clipped && selection.active && line >= selection.from_line && line <= selection.to_line) { // Selection
int sel_from = (line > selection.from_line) ? TS->shaped_text_get_range(rid).x : selection.from_column;
int sel_to = (line < selection.to_line) ? TS->shaped_text_get_range(rid).y : selection.to_column;
Vector<Vector2> sel = TS->shaped_text_get_selection(rid, sel_from, sel_to);
@@ -1167,7 +1188,7 @@ void TextEdit::_notification(int p_what) {
}
int start = TS->shaped_text_get_range(rid).x;
- if (!search_text.is_empty()) { // Search highhlight
+ if (!clipped && !search_text.is_empty()) { // Search highhlight
int search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0);
while (search_text_col != -1) {
Vector<Vector2> sel = TS->shaped_text_get_selection(rid, search_text_col + start, search_text_col + search_text.length() + start);
@@ -1190,7 +1211,7 @@ void TextEdit::_notification(int p_what) {
}
}
- if (highlight_all_occurrences && !only_whitespaces_highlighted && !highlighted_text.is_empty()) { // Highlight
+ if (!clipped && highlight_all_occurrences && !only_whitespaces_highlighted && !highlighted_text.is_empty()) { // Highlight
int highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
while (highlighted_text_col != -1) {
Vector<Vector2> sel = TS->shaped_text_get_selection(rid, highlighted_text_col + start, highlighted_text_col + highlighted_text.length() + start);
@@ -1212,7 +1233,7 @@ void TextEdit::_notification(int p_what) {
}
}
- if (select_identifiers_enabled && highlighted_word.length() != 0) { // Highlight word
+ if (!clipped && select_identifiers_enabled && highlighted_word.length() != 0) { // Highlight word
if (_is_char(highlighted_word[0]) || highlighted_word[0] == '.') {
int highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
while (highlighted_word_col != -1) {
@@ -1230,7 +1251,7 @@ void TextEdit::_notification(int p_what) {
}
rect.position.y = TS->shaped_text_get_ascent(rid) + cache.font->get_underline_position(cache.font_size);
rect.size.y = cache.font->get_underline_thickness(cache.font_size);
- draw_rect(rect, cache.font_color_selected);
+ draw_rect(rect, cache.font_selected_color);
}
highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, highlighted_word_col + 1);
@@ -1238,6 +1259,7 @@ void TextEdit::_notification(int p_what) {
}
}
+ const int line_top_offset_y = ofs_y;
ofs_y += (row_height - text_height) / 2;
const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(rid);
@@ -1246,11 +1268,27 @@ void TextEdit::_notification(int p_what) {
ofs_y += ldata->get_line_ascent(line_wrap_index);
int char_ofs = 0;
+ if (cache.outline_size > 0 && cache.outline_color.a > 0) {
+ for (int j = 0; j < gl_size; j++) {
+ for (int k = 0; k < glyphs[j].repeat; k++) {
+ if ((char_ofs + char_margin) >= xmargin_beg && (char_ofs + glyphs[j].advance + char_margin) <= xmargin_end) {
+ if (glyphs[j].font_rid != RID()) {
+ TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, cache.outline_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, cache.outline_color);
+ }
+ }
+ char_ofs += glyphs[j].advance;
+ }
+ if ((char_ofs + char_margin) >= xmargin_end) {
+ break;
+ }
+ }
+ char_ofs = 0;
+ }
for (int j = 0; j < gl_size; j++) {
if (color_map.has(glyphs[j].start)) {
current_color = color_map[glyphs[j].start].get("color");
- if (readonly && current_color.a > cache.font_color_readonly.a) {
- current_color.a = cache.font_color_readonly.a;
+ if (readonly && current_color.a > cache.font_readonly_color.a) {
+ current_color.a = cache.font_readonly_color.a;
}
}
@@ -1259,39 +1297,44 @@ void TextEdit::_notification(int p_what) {
int sel_to = (line < selection.to_line) ? TS->shaped_text_get_range(rid).y : selection.to_column;
if (glyphs[j].start >= sel_from && glyphs[j].end <= sel_to && override_selected_font_color) {
- current_color = cache.font_color_selected;
+ current_color = cache.font_selected_color;
}
}
- if (brace_matching_enabled) {
- if ((brace_open_match_line == line && brace_open_match_column == glyphs[j].start) ||
- (cursor.column == glyphs[j].start && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_open_matching || brace_open_mismatch))) {
- if (brace_open_mismatch) {
- current_color = cache.brace_mismatch_color;
+ int char_pos = char_ofs + char_margin + ofs_x;
+ if (char_pos >= xmargin_beg) {
+ if (brace_matching_enabled) {
+ if ((brace_open_match_line == line && brace_open_match_column == glyphs[j].start) ||
+ (cursor.column == glyphs[j].start && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_open_matching || brace_open_mismatch))) {
+ if (brace_open_mismatch) {
+ current_color = cache.brace_mismatch_color;
+ }
+ Rect2 rect = Rect2(char_pos, ofs_y + cache.font->get_underline_position(cache.font_size), glyphs[j].advance * glyphs[j].repeat, cache.font->get_underline_thickness(cache.font_size));
+ draw_rect(rect, current_color);
}
- Rect2 rect = Rect2(char_ofs + char_margin + ofs_x, ofs_y + cache.font->get_underline_position(cache.font_size), glyphs[j].advance * glyphs[j].repeat, cache.font->get_underline_thickness(cache.font_size));
- draw_rect(rect, current_color);
- }
- if ((brace_close_match_line == line && brace_close_match_column == glyphs[j].start) ||
- (cursor.column == glyphs[j].start + 1 && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_close_matching || brace_close_mismatch))) {
- if (brace_close_mismatch) {
- current_color = cache.brace_mismatch_color;
+ if ((brace_close_match_line == line && brace_close_match_column == glyphs[j].start) ||
+ (cursor.column == glyphs[j].start + 1 && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_close_matching || brace_close_mismatch))) {
+ if (brace_close_mismatch) {
+ current_color = cache.brace_mismatch_color;
+ }
+ Rect2 rect = Rect2(char_pos, ofs_y + cache.font->get_underline_position(cache.font_size), glyphs[j].advance * glyphs[j].repeat, cache.font->get_underline_thickness(cache.font_size));
+ draw_rect(rect, current_color);
}
- Rect2 rect = Rect2(char_ofs + char_margin + ofs_x, ofs_y + cache.font->get_underline_position(cache.font_size), glyphs[j].advance * glyphs[j].repeat, cache.font->get_underline_thickness(cache.font_size));
- draw_rect(rect, current_color);
+ }
+
+ if (draw_tabs && ((glyphs[j].flags & TextServer::GRAPHEME_IS_TAB) == TextServer::GRAPHEME_IS_TAB)) {
+ int yofs = (text_height - cache.tab_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index);
+ cache.tab_icon->draw(ci, Point2(char_pos, ofs_y + yofs), current_color);
+ } else if (draw_spaces && ((glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE)) {
+ int yofs = (text_height - cache.space_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index);
+ int xofs = (glyphs[j].advance * glyphs[j].repeat - cache.space_icon->get_width()) / 2;
+ cache.space_icon->draw(ci, Point2(char_pos + xofs, ofs_y + yofs), current_color);
}
}
- if (draw_tabs && ((glyphs[j].flags & TextServer::GRAPHEME_IS_TAB) == TextServer::GRAPHEME_IS_TAB)) {
- int yofs = (text_height - cache.tab_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index);
- cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), current_color);
- } else if (draw_spaces && ((glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE)) {
- int yofs = (text_height - cache.space_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index);
- int xofs = (glyphs[j].advance * glyphs[j].repeat - cache.space_icon->get_width()) / 2;
- cache.space_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x + xofs, ofs_y + yofs), current_color);
- }
+
for (int k = 0; k < glyphs[j].repeat; k++) {
- if ((char_ofs + char_margin) >= xmargin_beg && (char_ofs + glyphs[j].advance + char_margin) <= xmargin_end) {
+ if (!clipped && (char_ofs + char_margin) >= xmargin_beg && (char_ofs + glyphs[j].advance + char_margin) <= xmargin_end) {
if (glyphs[j].font_rid != RID()) {
TS->font_draw_glyph(glyphs[j].font_rid, ci, glyphs[j].font_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, current_color);
} else if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
@@ -1306,11 +1349,13 @@ void TextEdit::_notification(int p_what) {
}
if (line_wrap_index == line_wrap_amount && is_folded(line)) {
- int yofs = (text_height - cache.folded_eol_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index);
- int xofs = cache.folded_eol_icon->get_width() / 2;
- Color eol_color = cache.code_folding_color;
- eol_color.a = 1;
- cache.folded_eol_icon->draw(ci, Point2(char_ofs + char_margin + xofs + ofs_x, ofs_y + yofs), eol_color);
+ int xofs = char_ofs + char_margin + ofs_x + (cache.folded_eol_icon->get_width() / 2);
+ if (xofs >= xmargin_beg && xofs < xmargin_end) {
+ int yofs = (text_height - cache.folded_eol_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index);
+ Color eol_color = cache.code_folding_color;
+ eol_color.a = 1;
+ cache.folded_eol_icon->draw(ci, Point2(xofs, ofs_y + yofs), eol_color);
+ }
}
// Carets
@@ -1319,8 +1364,10 @@ void TextEdit::_notification(int p_what) {
#else
int caret_width = 1;
#endif
- if (cursor.line == line && ((line_wrap_index == line_wrap_amount) || (cursor.column != TS->shaped_text_get_range(rid).y))) {
- cursor_pos.y = ofs_y + ldata->get_line_descent(line_wrap_index);
+ if (!clipped && cursor.line == line && ((line_wrap_index == line_wrap_amount) || (cursor.column != TS->shaped_text_get_range(rid).y))) {
+ is_cursor_line_visible = true;
+ cursor_pos.y = line_top_offset_y;
+
if (ime_text.length() == 0) {
Rect2 l_caret, t_caret;
TextServer::Direction l_dir, t_dir;
@@ -1345,7 +1392,7 @@ void TextEdit::_notification(int p_what) {
cursor_pos.x = char_margin + ofs_x + t_caret.position.x;
}
- if (draw_caret) {
+ if (draw_caret && cursor_pos.x >= xmargin_beg && cursor_pos.x < xmargin_end) {
if (block_caret || insert_mode) {
//Block or underline caret, draw trailing carets at full height.
int h = cache.font->get_height(cache.font_size);
@@ -1370,7 +1417,7 @@ void TextEdit::_notification(int p_what) {
l_caret.size.y = h;
}
l_caret.position += Vector2(char_margin + ofs_x, ofs_y);
- l_caret.size.x = cache.font->get_char_size('m', 0, cache.font_size).x;
+ l_caret.size.x = cache.font->get_char_size('M', 0, cache.font_size).x;
draw_rect(l_caret, cache.caret_color, false);
}
@@ -1395,7 +1442,7 @@ void TextEdit::_notification(int p_what) {
}
} else {
{
- // IME intermidiet text range.
+ // IME Intermediate text range.
Vector<Vector2> sel = TS->shaped_text_get_selection(rid, cursor.column, cursor.column + ime_text.length());
for (int j = 0; j < sel.size(); j++) {
Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, text_height);
@@ -1434,83 +1481,101 @@ void TextEdit::_notification(int p_what) {
}
}
}
- ofs_y += ldata->get_line_descent(line_wrap_index);
}
}
bool completion_below = false;
- if (completion_active && completion_options.size() > 0) {
- // Code completion box.
- Ref<StyleBox> csb = get_theme_stylebox("completion");
- int maxlines = get_theme_constant("completion_lines");
- int cmax_width = get_theme_constant("completion_max_width") * cache.font->get_char_size('x', 0, cache.font_size).x;
- int scrollw = get_theme_constant("completion_scroll_width");
- Color scrollc = get_theme_color("completion_scroll_color");
+ if (completion_active && is_cursor_line_visible && completion_options.size() > 0) {
+ // Completion panel
+
+ const Ref<StyleBox> csb = get_theme_stylebox("completion");
+ const int maxlines = get_theme_constant("completion_lines");
+ const int cmax_width = get_theme_constant("completion_max_width") * cache.font->get_char_size('x', 0, cache.font_size).x;
+ const Color scrollc = get_theme_color("completion_scroll_color");
const int completion_options_size = completion_options.size();
- int lines = MIN(completion_options_size, maxlines);
- int w = 0;
- int h = lines * row_height;
- int nofs = cache.font->get_string_size(completion_base, cache.font_size).width;
+ const int row_count = MIN(completion_options_size, maxlines);
+ const int completion_rows_height = row_count * row_height;
+ const int completion_base_width = cache.font->get_string_size(completion_base, cache.font_size).width;
+ int scroll_rectangle_width = get_theme_constant("completion_scroll_width");
+ int width = 0;
+
+ // Compute max width of the panel based on the longest completion option
if (completion_options_size < 50) {
for (int i = 0; i < completion_options_size; i++) {
- int w2 = MIN(cache.font->get_string_size(completion_options[i].display, cache.font_size).x, cmax_width);
- if (w2 > w) {
- w = w2;
+ int line_width = MIN(cache.font->get_string_size(completion_options[i].display, cache.font_size).x, cmax_width);
+ if (line_width > width) {
+ width = line_width;
}
}
} else {
- w = cmax_width;
+ width = cmax_width;
}
// Add space for completion icons.
const int icon_hsep = get_theme_constant("hseparation", "ItemList");
- Size2 icon_area_size(row_height, row_height);
- w += icon_area_size.width + icon_hsep;
+ const Size2 icon_area_size(row_height, row_height);
+ const int icon_area_width = icon_area_size.width + icon_hsep;
+ width += icon_area_width;
- int line_from = CLAMP(completion_index - lines / 2, 0, completion_options_size - lines);
+ const int line_from = CLAMP(completion_index - row_count / 2, 0, completion_options_size - row_count);
- for (int i = 0; i < lines; i++) {
+ for (int i = 0; i < row_count; i++) {
int l = line_from + i;
ERR_CONTINUE(l < 0 || l >= completion_options_size);
if (completion_options[l].default_value.get_type() == Variant::COLOR) {
- w += icon_area_size.width;
+ width += icon_area_size.width;
break;
}
}
- int th = h + csb->get_minimum_size().y;
+ // Position completion panel
+ completion_rect.size.width = width + 2;
+ completion_rect.size.height = completion_rows_height;
- if (cursor_pos.y + row_height + th > get_size().height) {
- completion_rect.position.y = cursor_pos.y - th - (cache.line_spacing / 2.0f) - cursor_insert_offset_y;
- } else {
- completion_rect.position.y = cursor_pos.y + cache.font->get_height(cache.font_size) + (cache.line_spacing / 2.0f) + csb->get_offset().y - cursor_insert_offset_y;
- completion_below = true;
+ if (completion_options_size <= maxlines) {
+ scroll_rectangle_width = 0;
}
- if (cursor_pos.x - nofs + w + scrollw > get_size().width) {
- completion_rect.position.x = get_size().width - w - scrollw;
+ const Point2 csb_offset = csb->get_offset();
+
+ const int total_width = completion_rect.size.width + csb->get_minimum_size().x + scroll_rectangle_width;
+ const int total_height = completion_rect.size.height + csb->get_minimum_size().y;
+
+ const int rect_left_border_x = cursor_pos.x - completion_base_width - icon_area_width - csb_offset.x;
+ const int rect_right_border_x = rect_left_border_x + total_width;
+
+ if (rect_left_border_x < 0) {
+ // Anchor the completion panel to the left
+ completion_rect.position.x = 0;
+ } else if (rect_right_border_x > get_size().width) {
+ // Anchor the completion panel to the right
+ completion_rect.position.x = get_size().width - total_width;
} else {
- completion_rect.position.x = cursor_pos.x - nofs;
+ // Let the completion panel float with the cursor
+ completion_rect.position.x = rect_left_border_x;
}
- completion_rect.size.width = w + 2;
- completion_rect.size.height = h;
- if (completion_options_size <= maxlines) {
- scrollw = 0;
+ if (cursor_pos.y + row_height + total_height > get_size().height && cursor_pos.y > total_height) {
+ // Completion panel above the cursor line
+ completion_rect.position.y = cursor_pos.y - total_height;
+ } else {
+ // Completion panel below the cursor line
+ completion_rect.position.y = cursor_pos.y + row_height;
+ completion_below = true;
}
- draw_style_box(csb, Rect2(completion_rect.position - csb->get_offset(), completion_rect.size + csb->get_minimum_size() + Size2(scrollw, 0)));
+ draw_style_box(csb, Rect2(completion_rect.position - csb_offset, completion_rect.size + csb->get_minimum_size() + Size2(scroll_rectangle_width, 0)));
if (cache.completion_background_color.a > 0.01) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(completion_rect.position, completion_rect.size + Size2(scrollw, 0)), cache.completion_background_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(completion_rect.position, completion_rect.size + Size2(scroll_rectangle_width, 0)), cache.completion_background_color);
}
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(completion_rect.position.x, completion_rect.position.y + (completion_index - line_from) * get_row_height()), Size2(completion_rect.size.width, get_row_height())), cache.completion_selected_color);
- draw_rect(Rect2(completion_rect.position + Vector2(icon_area_size.x + icon_hsep, 0), Size2(MIN(nofs, completion_rect.size.width - (icon_area_size.x + icon_hsep)), completion_rect.size.height)), cache.completion_existing_color);
+ draw_rect(Rect2(completion_rect.position + Vector2(icon_area_size.x + icon_hsep, 0), Size2(MIN(completion_base_width, completion_rect.size.width - (icon_area_size.x + icon_hsep)), completion_rect.size.height)), cache.completion_existing_color);
- for (int i = 0; i < lines; i++) {
+ for (int i = 0; i < row_count; i++) {
int l = line_from + i;
ERR_CONTINUE(l < 0 || l >= completion_options_size);
@@ -1547,14 +1612,17 @@ void TextEdit::_notification(int p_what) {
}
tl->set_align(HALIGN_LEFT);
}
+ if (cache.outline_size > 0 && cache.outline_color.a > 0) {
+ tl->draw_outline(ci, title_pos, cache.outline_size, cache.outline_color);
+ }
tl->draw(ci, title_pos, completion_options[l].font_color);
}
- if (scrollw) {
+ if (scroll_rectangle_width) {
// Draw a small scroll rectangle to show a position in the options.
float r = (float)maxlines / completion_options_size;
float o = (float)line_from / completion_options_size;
- draw_rect(Rect2(completion_rect.position.x + completion_rect.size.width, completion_rect.position.y + o * completion_rect.size.y, scrollw, completion_rect.size.y * r), scrollc);
+ draw_rect(Rect2(completion_rect.position.x + completion_rect.size.width, completion_rect.position.y + o * completion_rect.size.y, scroll_rectangle_width, completion_rect.size.y * r), scrollc);
}
completion_line_ofs = line_from;
@@ -1562,7 +1630,7 @@ void TextEdit::_notification(int p_what) {
// Check to see if the hint should be drawn.
bool show_hint = false;
- if (completion_hint != "") {
+ if (is_cursor_line_visible && completion_hint != "") {
if (completion_active) {
if (completion_below && !callhint_below) {
show_hint = true;
@@ -1634,7 +1702,7 @@ void TextEdit::_notification(int p_what) {
}
if (has_focus()) {
- if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) {
+ if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id());
DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos, get_viewport()->get_window_id());
}
@@ -1647,7 +1715,7 @@ void TextEdit::_notification(int p_what) {
draw_caret = true;
}
- if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) {
+ if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id());
DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + _get_cursor_pixel_pos(false), get_viewport()->get_window_id());
}
@@ -1676,7 +1744,7 @@ void TextEdit::_notification(int p_what) {
caret_blink_timer->stop();
}
- if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) {
+ if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
DisplayServer::get_singleton()->window_set_ime_position(Point2(), get_viewport()->get_window_id());
DisplayServer::get_singleton()->window_set_ime_active(false, get_viewport()->get_window_id());
}
@@ -1905,11 +1973,11 @@ void TextEdit::backspace_at_cursor() {
}
}
- cursor_set_line(prev_line, true, true);
+ cursor_set_line(prev_line, false, true);
cursor_set_column(prev_column);
}
-void TextEdit::indent_right() {
+void TextEdit::indent_selected_lines_right() {
int start_line;
int end_line;
@@ -1941,7 +2009,7 @@ void TextEdit::indent_right() {
// We don't really care where selection is - we just need to know indentation level at the beginning of the line.
int left = _find_first_non_whitespace_column_of_line(line_text);
int spaces_to_add = _calculate_spaces_till_next_right_indent(left);
- // Since we will add this much spaces we want move whole selection and cursor by this much.
+ // Since we will add these many spaces, we want to move the whole selection and cursor by this much.
selection_offset = spaces_to_add;
for (int j = 0; j < spaces_to_add; j++) {
line_text = ' ' + line_text;
@@ -1961,12 +2029,12 @@ void TextEdit::indent_right() {
update();
}
-void TextEdit::indent_left() {
+void TextEdit::indent_selected_lines_left() {
int start_line;
int end_line;
// Moving cursor and selection after unindenting can get tricky because
- // changing content of line can move cursor and selection on it's own (if new line ends before previous position of either),
+ // changing content of line can move cursor and selection on its own (if new line ends before previous position of either),
// therefore we just remember initial values and at the end of the operation offset them by number of removed characters.
int removed_characters = 0;
int initial_selection_end_column = selection.to_column;
@@ -1986,6 +2054,7 @@ void TextEdit::indent_left() {
if (is_selection_active() && get_selection_to_column() == 0) {
end_line--;
}
+ String first_line_text = get_line(start_line);
String last_line_text = get_line(end_line);
for (int i = start_line; i <= end_line; i++) {
@@ -2010,10 +2079,17 @@ void TextEdit::indent_left() {
}
}
- // Fix selection and cursor being off by one on the last line.
- if (is_selection_active() && last_line_text != get_line(end_line)) {
- select(selection.from_line, selection.from_column - removed_characters,
- selection.to_line, initial_selection_end_column - removed_characters);
+ if (is_selection_active()) {
+ // Fix selection being off by one on the first line.
+ if (first_line_text != get_line(start_line)) {
+ select(selection.from_line, selection.from_column - removed_characters,
+ selection.to_line, initial_selection_end_column);
+ }
+ // Fix selection being off by one on the last line.
+ if (last_line_text != get_line(end_line)) {
+ select(selection.from_line, selection.from_column,
+ selection.to_line, initial_selection_end_column - removed_characters);
+ }
}
cursor_set_column(initial_cursor_column - removed_characters, false);
end_complex_operation();
@@ -2032,6 +2108,623 @@ int TextEdit::_calculate_spaces_till_next_right_indent(int column) {
return indent_size - column % indent_size;
}
+void TextEdit::_swap_current_input_direction() {
+ if (input_direction == TEXT_DIRECTION_LTR) {
+ input_direction = TEXT_DIRECTION_RTL;
+ } else {
+ input_direction = TEXT_DIRECTION_LTR;
+ }
+ cursor_set_column(cursor.column);
+ update();
+}
+
+void TextEdit::_new_line(bool p_split_current_line, bool p_above) {
+ if (readonly) {
+ return;
+ }
+
+ String ins = "\n";
+
+ // Keep indentation.
+ int space_count = 0;
+ for (int i = 0; i < cursor.column; i++) {
+ if (text[cursor.line][i] == '\t') {
+ if (indent_using_spaces) {
+ ins += space_indent;
+ } else {
+ ins += "\t";
+ }
+ space_count = 0;
+ } else if (text[cursor.line][i] == ' ') {
+ space_count++;
+
+ if (space_count == indent_size) {
+ if (indent_using_spaces) {
+ ins += space_indent;
+ } else {
+ ins += "\t";
+ }
+ space_count = 0;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (is_folded(cursor.line)) {
+ unfold_line(cursor.line);
+ }
+
+ bool brace_indent = false;
+
+ // No need to indent if we are going upwards.
+ if (auto_indent && !p_above) {
+ // Indent once again if previous line will end with ':','{','[','(' and the line is not a comment
+ // (i.e. colon/brace precedes current cursor position).
+ if (cursor.column > 0) {
+ bool indent_char_found = false;
+ bool should_indent = false;
+ char indent_char = ':';
+ char c = text[cursor.line][cursor.column];
+
+ for (int i = 0; i < cursor.column; i++) {
+ c = text[cursor.line][i];
+ switch (c) {
+ case ':':
+ case '{':
+ case '[':
+ case '(':
+ indent_char_found = true;
+ should_indent = true;
+ indent_char = c;
+ continue;
+ }
+
+ if (indent_char_found && is_line_comment(cursor.line)) {
+ should_indent = true;
+ break;
+ } else if (indent_char_found && !_is_whitespace(c)) {
+ should_indent = false;
+ indent_char_found = false;
+ }
+ }
+
+ if (!is_line_comment(cursor.line) && should_indent) {
+ if (indent_using_spaces) {
+ ins += space_indent;
+ } else {
+ ins += "\t";
+ }
+
+ // No need to move the brace below if we are not taking the text with us.
+ char32_t closing_char = _get_right_pair_symbol(indent_char);
+ if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column])) {
+ if (p_split_current_line) {
+ brace_indent = true;
+ ins += "\n" + ins.substr(1, ins.length() - 2);
+ } else {
+ brace_indent = false;
+ ins = "\n" + ins.substr(1, ins.length() - 2);
+ }
+ }
+ }
+ }
+ }
+ begin_complex_operation();
+ bool first_line = false;
+ if (!p_split_current_line) {
+ if (p_above) {
+ if (cursor.line > 0) {
+ cursor_set_line(cursor.line - 1, false);
+ cursor_set_column(text[cursor.line].length());
+ } else {
+ cursor_set_column(0);
+ first_line = true;
+ }
+ } else {
+ cursor_set_column(text[cursor.line].length());
+ }
+ }
+
+ insert_text_at_cursor(ins);
+
+ if (first_line) {
+ cursor_set_line(0);
+ } else if (brace_indent) {
+ cursor_set_line(cursor.line - 1, false);
+ cursor_set_column(text[cursor.line].length());
+ }
+ end_complex_operation();
+}
+
+void TextEdit::_indent_right() {
+ if (readonly) {
+ return;
+ }
+
+ if (is_selection_active()) {
+ indent_selected_lines_right();
+ } else {
+ // Simple indent.
+ if (indent_using_spaces) {
+ // Insert only as much spaces as needed till next indentation level.
+ int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor.column);
+ String indent_to_insert = String();
+ for (int i = 0; i < spaces_to_add; i++) {
+ indent_to_insert = ' ' + indent_to_insert;
+ }
+ _insert_text_at_cursor(indent_to_insert);
+ } else {
+ _insert_text_at_cursor("\t");
+ }
+ }
+}
+
+void TextEdit::_indent_left() {
+ if (readonly) {
+ return;
+ }
+
+ if (is_selection_active()) {
+ indent_selected_lines_left();
+ } else {
+ // Simple unindent.
+ int cc = cursor.column;
+ const String &line = text[cursor.line];
+
+ int left = _find_first_non_whitespace_column_of_line(line);
+ cc = MIN(cc, left);
+
+ while (cc < indent_size && cc < left && line[cc] == ' ') {
+ cc++;
+ }
+
+ if (cc > 0 && cc <= text[cursor.line].length()) {
+ if (text[cursor.line][cc - 1] == '\t') {
+ // Tabs unindentation.
+ _remove_text(cursor.line, cc - 1, cursor.line, cc);
+ if (cursor.column >= left) {
+ cursor_set_column(MAX(0, cursor.column - 1));
+ }
+ update();
+ } else {
+ // Spaces unindentation.
+ int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc);
+ if (spaces_to_remove > 0) {
+ _remove_text(cursor.line, cc - spaces_to_remove, cursor.line, cc);
+ if (cursor.column > left - spaces_to_remove) { // Inside text?
+ cursor_set_column(MAX(0, cursor.column - spaces_to_remove));
+ }
+ update();
+ }
+ }
+ } else if (cc == 0 && line.length() > 0 && line[0] == '\t') {
+ _remove_text(cursor.line, 0, cursor.line, 1);
+ update();
+ }
+ }
+}
+
+void TextEdit::_move_cursor_left(bool p_select, bool p_move_by_word) {
+ // Handle selection
+ if (p_select) {
+ _pre_shift_selection();
+ } else {
+ deselect();
+ }
+
+ if (p_move_by_word) {
+ int cc = cursor.column;
+
+ if (cc == 0 && cursor.line > 0) {
+ cursor_set_line(cursor.line - 1);
+ cursor_set_column(text[cursor.line].length());
+ } else {
+ Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid());
+ for (int i = words.size() - 1; i >= 0; i--) {
+ if (words[i].x < cc) {
+ cc = words[i].x;
+ break;
+ }
+ }
+ cursor_set_column(cc);
+ }
+ } else {
+ // If the cursor is at the start of the line, and not on the first line, move it up to the end of the previous line.
+ if (cursor.column == 0) {
+ if (cursor.line > 0) {
+ cursor_set_line(cursor.line - num_lines_from(CLAMP(cursor.line - 1, 0, text.size() - 1), -1));
+ cursor_set_column(text[cursor.line].length());
+ }
+ } else {
+ if (mid_grapheme_caret_enabled) {
+ cursor_set_column(cursor_get_column() - 1);
+ } else {
+ cursor_set_column(TS->shaped_text_prev_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), cursor_get_column()));
+ }
+ }
+ }
+
+ if (p_select) {
+ _post_shift_selection();
+ }
+}
+
+void TextEdit::_move_cursor_right(bool p_select, bool p_move_by_word) {
+ // Handle selection
+ if (p_select) {
+ _pre_shift_selection();
+ } else {
+ deselect();
+ }
+
+ if (p_move_by_word) {
+ int cc = cursor.column;
+
+ if (cc == text[cursor.line].length() && cursor.line < text.size() - 1) {
+ cursor_set_line(cursor.line + 1);
+ cursor_set_column(0);
+ } else {
+ Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid());
+ for (int i = 0; i < words.size(); i++) {
+ if (words[i].y > cc) {
+ cc = words[i].y;
+ break;
+ }
+ }
+ cursor_set_column(cc);
+ }
+ } else {
+ // If we are at the end of the line, move the caret to the next line down.
+ if (cursor.column == text[cursor.line].length()) {
+ if (cursor.line < text.size() - 1) {
+ cursor_set_line(cursor_get_line() + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1), true, false);
+ cursor_set_column(0);
+ }
+ } else {
+ if (mid_grapheme_caret_enabled) {
+ cursor_set_column(cursor_get_column() + 1);
+ } else {
+ cursor_set_column(TS->shaped_text_next_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), cursor_get_column()));
+ }
+ }
+ }
+
+ if (p_select) {
+ _post_shift_selection();
+ }
+}
+
+void TextEdit::_move_cursor_up(bool p_select) {
+ if (p_select) {
+ _pre_shift_selection();
+ } else {
+ deselect();
+ }
+
+ int cur_wrap_index = get_cursor_wrap_index();
+ if (cur_wrap_index > 0) {
+ cursor_set_line(cursor.line, true, false, cur_wrap_index - 1);
+ } else if (cursor.line == 0) {
+ cursor_set_column(0);
+ } else {
+ int new_line = cursor.line - num_lines_from(cursor.line - 1, -1);
+ if (line_wraps(new_line)) {
+ cursor_set_line(new_line, true, false, times_line_wraps(new_line));
+ } else {
+ cursor_set_line(new_line, true, false);
+ }
+ }
+
+ if (p_select) {
+ _post_shift_selection();
+ }
+
+ _cancel_code_hint();
+}
+
+void TextEdit::_move_cursor_down(bool p_select) {
+ if (p_select) {
+ _pre_shift_selection();
+ } else {
+ deselect();
+ }
+
+ int cur_wrap_index = get_cursor_wrap_index();
+ if (cur_wrap_index < times_line_wraps(cursor.line)) {
+ cursor_set_line(cursor.line, true, false, cur_wrap_index + 1);
+ } else if (cursor.line == get_last_unhidden_line()) {
+ cursor_set_column(text[cursor.line].length());
+ } else {
+ int new_line = cursor.line + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1);
+ cursor_set_line(new_line, true, false, 0);
+ }
+
+ if (p_select) {
+ _post_shift_selection();
+ }
+
+ _cancel_code_hint();
+}
+
+void TextEdit::_move_cursor_to_line_start(bool p_select) {
+ if (p_select) {
+ _pre_shift_selection();
+ } else {
+ deselect();
+ }
+
+ // Move cursor column to start of wrapped row and then to start of text.
+ Vector<String> rows = get_wrap_rows_text(cursor.line);
+ int wi = get_cursor_wrap_index();
+ int row_start_col = 0;
+ for (int i = 0; i < wi; i++) {
+ row_start_col += rows[i].length();
+ }
+ if (cursor.column == row_start_col || wi == 0) {
+ // Compute whitespace symbols sequence length.
+ int current_line_whitespace_len = 0;
+ while (current_line_whitespace_len < text[cursor.line].length()) {
+ char32_t c = text[cursor.line][current_line_whitespace_len];
+ if (c != '\t' && c != ' ') {
+ break;
+ }
+ current_line_whitespace_len++;
+ }
+
+ if (cursor_get_column() == current_line_whitespace_len) {
+ cursor_set_column(0);
+ } else {
+ cursor_set_column(current_line_whitespace_len);
+ }
+ } else {
+ cursor_set_column(row_start_col);
+ }
+
+ if (p_select) {
+ _post_shift_selection();
+ }
+
+ _cancel_completion();
+ completion_hint = "";
+}
+
+void TextEdit::_move_cursor_to_line_end(bool p_select) {
+ if (p_select) {
+ _pre_shift_selection();
+ } else {
+ deselect();
+ }
+
+ // Move cursor column to end of wrapped row and then to end of text.
+ Vector<String> rows = get_wrap_rows_text(cursor.line);
+ int wi = get_cursor_wrap_index();
+ int row_end_col = -1;
+ for (int i = 0; i < wi + 1; i++) {
+ row_end_col += rows[i].length();
+ }
+ if (wi == rows.size() - 1 || cursor.column == row_end_col) {
+ cursor_set_column(text[cursor.line].length());
+ } else {
+ cursor_set_column(row_end_col);
+ }
+
+ if (p_select) {
+ _post_shift_selection();
+ }
+ _cancel_completion();
+ completion_hint = "";
+}
+
+void TextEdit::_move_cursor_page_up(bool p_select) {
+ if (p_select) {
+ _pre_shift_selection();
+ } else {
+ deselect();
+ }
+
+ int wi;
+ int n_line = cursor.line - num_lines_from_rows(cursor.line, get_cursor_wrap_index(), -get_visible_rows(), wi) + 1;
+ cursor_set_line(n_line, true, false, wi);
+
+ if (p_select) {
+ _post_shift_selection();
+ }
+
+ _cancel_completion();
+ completion_hint = "";
+}
+
+void TextEdit::_move_cursor_page_down(bool p_select) {
+ if (p_select) {
+ _pre_shift_selection();
+ } else {
+ deselect();
+ }
+
+ int wi;
+ int n_line = cursor.line + num_lines_from_rows(cursor.line, get_cursor_wrap_index(), get_visible_rows(), wi) - 1;
+ cursor_set_line(n_line, true, false, wi);
+
+ if (p_select) {
+ _post_shift_selection();
+ }
+
+ _cancel_completion();
+ completion_hint = "";
+}
+
+void TextEdit::_backspace(bool p_word, bool p_all_to_left) {
+ if (readonly) {
+ return;
+ }
+
+ if (is_selection_active()) {
+ _delete_selection();
+ return;
+ }
+ if (p_all_to_left) {
+ int cursor_current_column = cursor.column;
+ cursor.column = 0;
+ _remove_text(cursor.line, 0, cursor.line, cursor_current_column);
+ } else if (p_word) {
+ int line = cursor.line;
+ int column = cursor.column;
+
+ Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
+ for (int i = words.size() - 1; i >= 0; i--) {
+ if (words[i].x < column) {
+ column = words[i].x;
+ break;
+ }
+ }
+
+ _remove_text(line, column, cursor.line, cursor.column);
+
+ cursor_set_line(line, false);
+ cursor_set_column(column);
+ } else {
+ // One character.
+ if (cursor.line > 0 && is_line_hidden(cursor.line - 1)) {
+ unfold_line(cursor.line - 1);
+ }
+ backspace_at_cursor();
+ }
+}
+
+void TextEdit::_delete(bool p_word, bool p_all_to_right) {
+ if (readonly) {
+ return;
+ }
+
+ if (is_selection_active()) {
+ _delete_selection();
+ return;
+ }
+ int curline_len = text[cursor.line].length();
+
+ if (cursor.line == text.size() - 1 && cursor.column == curline_len) {
+ return; // Last line, last column: Nothing to do.
+ }
+
+ int next_line = cursor.column < curline_len ? cursor.line : cursor.line + 1;
+ int next_column;
+
+ if (p_all_to_right) {
+ // Delete everything to right of cursor
+ next_column = curline_len;
+ next_line = cursor.line;
+ } else if (p_word && cursor.column < curline_len - 1) {
+ // Delete next word to right of cursor
+ int line = cursor.line;
+ int column = cursor.column;
+
+ Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
+ for (int i = 0; i < words.size(); i++) {
+ if (words[i].y > column) {
+ column = words[i].y;
+ break;
+ }
+ }
+
+ next_line = line;
+ next_column = column;
+ } else {
+ // Delete one character
+ next_column = cursor.column < curline_len ? (cursor.column + 1) : 0;
+ if (mid_grapheme_caret_enabled) {
+ next_column = cursor.column < curline_len ? (cursor.column + 1) : 0;
+ } else {
+ next_column = cursor.column < curline_len ? TS->shaped_text_next_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), (cursor.column)) : 0;
+ }
+ }
+
+ _remove_text(cursor.line, cursor.column, next_line, next_column);
+ update();
+}
+
+void TextEdit::_delete_selection() {
+ if (is_selection_active()) {
+ selection.active = false;
+ update();
+ _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
+ cursor_set_line(selection.from_line, false, false);
+ cursor_set_column(selection.from_column);
+ update();
+ }
+}
+
+void TextEdit::_move_cursor_document_start(bool p_select) {
+ if (p_select) {
+ _pre_shift_selection();
+ } else {
+ deselect();
+ }
+
+ cursor_set_line(0);
+ cursor_set_column(0);
+
+ if (p_select) {
+ _post_shift_selection();
+ }
+}
+
+void TextEdit::_move_cursor_document_end(bool p_select) {
+ if (p_select) {
+ _pre_shift_selection();
+ } else {
+ deselect();
+ }
+
+ cursor_set_line(get_last_unhidden_line(), true, false, 9999);
+ cursor_set_column(text[cursor.line].length());
+
+ if (p_select) {
+ _post_shift_selection();
+ }
+}
+
+void TextEdit::_handle_unicode_character(uint32_t unicode, bool p_had_selection, bool p_update_auto_complete) {
+ if (p_update_auto_complete) {
+ _reset_caret_blink_timer();
+ }
+
+ if (p_had_selection) {
+ _delete_selection();
+ }
+
+ // Remove the old character if in insert mode and no selection.
+ if (insert_mode && !p_had_selection) {
+ begin_complex_operation();
+
+ // Make sure we don't try and remove empty space.
+ if (cursor.column < get_line(cursor.line).length()) {
+ _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
+ }
+ }
+
+ const char32_t chr[2] = { (char32_t)unicode, 0 };
+
+ // Clear completion hint when function closed
+ if (completion_hint != "" && unicode == ')') {
+ completion_hint = "";
+ }
+
+ if (auto_brace_completion_enabled && _is_pair_symbol(chr[0])) {
+ _consume_pair_symbol(chr[0]);
+ } else {
+ _insert_text_at_cursor(chr);
+ }
+
+ if ((insert_mode && !p_had_selection) || (selection.active != p_had_selection)) {
+ end_complex_operation();
+ }
+
+ if (p_update_auto_complete) {
+ _update_completion_candidates();
+ }
+}
+
void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const {
float rows = p_mouse.y;
rows -= cache.style_normal->get_margin(SIDE_TOP);
@@ -2062,6 +2755,18 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
} else {
int colx = p_mouse.x - (cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding);
colx += cursor.x_ofs;
+ col = get_char_pos_for_line(colx, row, wrap_index);
+ if (is_wrap_enabled() && wrap_index < times_line_wraps(row)) {
+ // Move back one if we are at the end of the row.
+ Vector<String> rows2 = get_wrap_rows_text(row);
+ int row_end_col = 0;
+ for (int i = 0; i < wrap_index + 1; i++) {
+ row_end_col += rows2[i].length();
+ }
+ if (col >= row_end_col) {
+ col -= 1;
+ }
+ }
RID text_rid = text.get_line_data(row)->get_line_rid(wrap_index);
if (is_layout_rtl()) {
@@ -2157,6 +2862,8 @@ void TextEdit::_get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const
}
void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
+ ERR_FAIL_COND(p_gui_input.is_null());
+
double prev_v_scroll = v_scroll->get_value();
double prev_h_scroll = h_scroll->get_value();
@@ -2176,14 +2883,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
return;
}
- if (mb->get_button_index() == BUTTON_WHEEL_UP) {
+ if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP) {
if (completion_index > 0) {
completion_index--;
completion_current = completion_options[completion_index];
update();
}
}
- if (mb->get_button_index() == BUTTON_WHEEL_DOWN) {
+ if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) {
if (completion_index < completion_options.size() - 1) {
completion_index++;
completion_current = completion_options[completion_index];
@@ -2191,12 +2898,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- if (mb->get_button_index() == BUTTON_LEFT) {
+ if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
completion_index = CLAMP(completion_line_ofs + (mpos.y - completion_rect.position.y) / get_row_height(), 0, completion_options.size() - 1);
completion_current = completion_options[completion_index];
update();
- if (mb->is_doubleclick()) {
+ if (mb->is_double_click()) {
_confirm_completion();
}
}
@@ -2207,27 +2914,27 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
if (mb->is_pressed()) {
- if (mb->get_button_index() == BUTTON_WHEEL_UP && !mb->get_command()) {
+ if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && !mb->get_command()) {
if (mb->get_shift()) {
h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor()));
} else if (v_scroll->is_visible()) {
_scroll_up(3 * mb->get_factor());
}
}
- if (mb->get_button_index() == BUTTON_WHEEL_DOWN && !mb->get_command()) {
+ if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && !mb->get_command()) {
if (mb->get_shift()) {
h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor()));
} else if (v_scroll->is_visible()) {
_scroll_down(3 * mb->get_factor());
}
}
- if (mb->get_button_index() == BUTTON_WHEEL_LEFT) {
+ if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT) {
h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor()));
}
- if (mb->get_button_index() == BUTTON_WHEEL_RIGHT) {
+ if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT) {
h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor()));
}
- if (mb->get_button_index() == BUTTON_LEFT) {
+ if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
_reset_caret_blink_timer();
int row, col;
@@ -2267,7 +2974,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
int prev_col = cursor.column;
int prev_line = cursor.line;
- cursor_set_line(row, true, false);
+ cursor_set_line(row, false, false);
cursor_set_column(col);
if (mb->get_shift() && (cursor.column != prev_col || cursor.line != prev_line)) {
@@ -2292,8 +2999,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
} else {
if (cursor.line < selection.selecting_line || (cursor.line == selection.selecting_line && cursor.column < selection.selecting_column)) {
if (selection.shiftclick_left) {
- SWAP(selection.from_column, selection.to_column);
- SWAP(selection.from_line, selection.to_line);
selection.shiftclick_left = !selection.shiftclick_left;
}
selection.from_column = cursor.column;
@@ -2314,7 +3019,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
update();
}
-
} else {
selection.active = false;
selection.selecting_mode = SelectionMode::SELECTION_MODE_POINTER;
@@ -2322,12 +3026,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
selection.selecting_column = col;
}
- if (!mb->is_doubleclick() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < 600 && cursor.line == prev_line) {
+ if (!mb->is_double_click() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < 600 && cursor.line == prev_line) {
// Triple-click select line.
selection.selecting_mode = SelectionMode::SELECTION_MODE_LINE;
_update_selection_mode_line();
last_dblclk = 0;
- } else if (mb->is_doubleclick() && text[cursor.line].length()) {
+ } else if (mb->is_double_click() && text[cursor.line].length()) {
// Double-click select word.
selection.selecting_mode = SelectionMode::SELECTION_MODE_WORD;
_update_selection_mode_word();
@@ -2337,7 +3041,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
update();
}
- if (mb->get_button_index() == BUTTON_RIGHT && context_menu_enabled) {
+ if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && context_menu_enabled) {
_reset_caret_blink_timer();
int row, col;
@@ -2363,12 +3067,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
menu->set_position(get_screen_transform().xform(mpos));
menu->set_size(Vector2(1, 1));
- // menu->set_scale(get_global_transform().get_scale());
+ _generate_context_menu();
menu->popup();
grab_focus();
}
} else {
- if (mb->get_button_index() == BUTTON_LEFT) {
+ if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
if (mb->get_command() && highlighted_word != String()) {
int row, col;
_get_mouse_pos(Point2i(mpos.x, mpos.y), row, col);
@@ -2424,7 +3128,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- if (mm->get_button_mask() & BUTTON_MASK_LEFT && get_viewport()->gui_get_drag_data() == Variant()) { // Ignore if dragging.
+ if (mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT && get_viewport()->gui_get_drag_data() == Variant()) { // Ignore if dragging.
_reset_caret_blink_timer();
if (draw_minimap && !dragging_selection) {
@@ -2457,13 +3161,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
Ref<InputEventKey> k = p_gui_input;
if (k.is_valid()) {
- k = k->duplicate(); // It will be modified later on.
-
+ // Ctrl + Hover symbols
#ifdef OSX_ENABLED
if (k->get_keycode() == KEY_META) {
#else
if (k->get_keycode() == KEY_CONTROL) {
-
#endif
if (select_identifiers_enabled) {
if (k->is_pressed() && !dragging_minimap && !dragging_selection) {
@@ -2473,1191 +3175,347 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
set_highlighted_word(String());
}
}
+ return;
}
if (!k->is_pressed()) {
return;
}
- if (completion_active) {
- if (readonly) {
- return;
- }
-
- bool valid = true;
- if (k->get_command() || k->get_metakey()) {
- valid = false;
- }
-
- if (valid) {
- if (!k->get_alt()) {
- if (k->get_keycode() == KEY_UP) {
- if (completion_index > 0) {
- completion_index--;
- } else {
- completion_index = completion_options.size() - 1;
- }
- completion_current = completion_options[completion_index];
- update();
-
- accept_event();
- return;
- }
-
- if (k->get_keycode() == KEY_DOWN) {
- if (completion_index < completion_options.size() - 1) {
- completion_index++;
- } else {
- completion_index = 0;
- }
- completion_current = completion_options[completion_index];
- update();
-
- accept_event();
- return;
- }
-
- if (k->get_keycode() == KEY_PAGEUP) {
- completion_index -= get_theme_constant("completion_lines");
- if (completion_index < 0) {
- completion_index = 0;
- }
- completion_current = completion_options[completion_index];
- update();
- accept_event();
- return;
- }
-
- if (k->get_keycode() == KEY_PAGEDOWN) {
- completion_index += get_theme_constant("completion_lines");
- if (completion_index >= completion_options.size()) {
- completion_index = completion_options.size() - 1;
- }
- completion_current = completion_options[completion_index];
- update();
- accept_event();
- return;
- }
-
- if (k->get_keycode() == KEY_HOME && completion_index > 0) {
- completion_index = 0;
- completion_current = completion_options[completion_index];
- update();
- accept_event();
- return;
- }
-
- if (k->get_keycode() == KEY_END && completion_index < completion_options.size() - 1) {
- completion_index = completion_options.size() - 1;
- completion_current = completion_options[completion_index];
- update();
- accept_event();
- return;
- }
-
- if (k->get_keycode() == KEY_KP_ENTER || k->get_keycode() == KEY_ENTER || k->get_keycode() == KEY_TAB) {
- _confirm_completion();
- accept_event();
- return;
- }
-
- if (k->get_keycode() == KEY_BACKSPACE) {
- _reset_caret_blink_timer();
-
- backspace_at_cursor();
- _update_completion_candidates();
- accept_event();
- return;
- }
-
- if (k->get_keycode() == KEY_SHIFT) {
- accept_event();
- return;
- }
- }
-
- if (k->get_unicode() > 32) {
- _reset_caret_blink_timer();
-
- const char32_t chr[2] = { (char32_t)k->get_unicode(), 0 };
- if (auto_brace_completion_enabled && _is_pair_symbol(chr[0])) {
- _consume_pair_symbol(chr[0]);
- } else {
- // Remove the old character if in insert mode.
- if (insert_mode) {
- begin_complex_operation();
-
- // Make sure we don't try and remove empty space.
- if (cursor.column < get_line(cursor.line).length()) {
- _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
- }
- }
-
- _insert_text_at_cursor(chr);
-
- if (insert_mode) {
- end_complex_operation();
- }
- }
- _update_completion_candidates();
- accept_event();
-
- return;
- }
- }
-
- _cancel_completion();
- }
-
- /* TEST CONTROL FIRST! */
-
- // Some remaps for duplicate functions.
- if (k->get_command() && !k->get_shift() && !k->get_alt() && !k->get_metakey() && k->get_keycode() == KEY_INSERT) {
- k->set_keycode(KEY_C);
- }
- if (!k->get_command() && k->get_shift() && !k->get_alt() && !k->get_metakey() && k->get_keycode() == KEY_INSERT) {
- k->set_keycode(KEY_V);
- k->set_command(true);
- k->set_shift(false);
- }
-#ifdef APPLE_STYLE_KEYS
- if (k->get_control() && !k->get_shift() && !k->get_alt() && !k->get_command()) {
- uint32_t remap_key = KEY_UNKNOWN;
- switch (k->get_keycode()) {
- case KEY_F: {
- remap_key = KEY_RIGHT;
- } break;
- case KEY_B: {
- remap_key = KEY_LEFT;
- } break;
- case KEY_P: {
- remap_key = KEY_UP;
- } break;
- case KEY_N: {
- remap_key = KEY_DOWN;
- } break;
- case KEY_D: {
- remap_key = KEY_DELETE;
- } break;
- case KEY_H: {
- remap_key = KEY_BACKSPACE;
- } break;
- }
-
- if (remap_key != KEY_UNKNOWN) {
- k->set_keycode(remap_key);
- k->set_control(false);
- }
+ // If a modifier has been pressed, and nothing else, return.
+ if (k->get_keycode() == KEY_CONTROL || k->get_keycode() == KEY_ALT || k->get_keycode() == KEY_SHIFT || k->get_keycode() == KEY_META) {
+ return;
}
-#endif
_reset_caret_blink_timer();
+ // Allow unicode handling if:
+ // * No Modifiers are pressed (except shift)
+ bool allow_unicode_handling = !(k->get_command() || k->get_control() || k->get_alt() || k->get_metakey());
+
// Save here for insert mode, just in case it is cleared in the following section.
bool had_selection = selection.active;
- // Stuff to do when selection is active.
- if (!readonly && selection.active) {
- bool clear = false;
- bool unselect = false;
- bool dobreak = false;
-
- switch (k->get_keycode()) {
- case KEY_TAB: {
- if (k->get_shift()) {
- indent_left();
- } else {
- indent_right();
- }
- dobreak = true;
- accept_event();
- } break;
- case KEY_X:
- case KEY_C:
- // Special keys often used with control, wait.
- clear = (!k->get_command() || k->get_shift() || k->get_alt());
- break;
- case KEY_DELETE:
- if (!k->get_shift()) {
- accept_event();
- clear = true;
- dobreak = true;
- } else if (k->get_command() || k->get_alt()) {
- dobreak = true;
- }
- break;
- case KEY_BACKSPACE:
- accept_event();
- clear = true;
- dobreak = true;
- break;
- case KEY_LEFT:
- case KEY_RIGHT:
- case KEY_UP:
- case KEY_DOWN:
- case KEY_PAGEUP:
- case KEY_PAGEDOWN:
- case KEY_HOME:
- case KEY_END:
- // Ignore arrows if any modifiers are held (shift = selecting, others may be used for editor hotkeys).
- if (k->get_command() || k->get_shift() || k->get_alt()) {
- break;
- }
- unselect = true;
- break;
-
- default:
- if (k->get_unicode() >= 32 && !k->get_command() && !k->get_alt() && !k->get_metakey()) {
- clear = true;
- }
- if (auto_brace_completion_enabled && _is_pair_left_symbol(k->get_unicode())) {
- clear = false;
- }
- }
-
- if (unselect) {
- selection.active = false;
- selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
- update();
- }
- if (clear) {
- if (!dobreak) {
- begin_complex_operation();
- }
- selection.active = false;
- update();
- _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
- cursor_set_line(selection.from_line, true, false);
- cursor_set_column(selection.from_column);
- update();
- }
- if (dobreak) {
- return;
- }
- }
-
selection.selecting_text = false;
- bool keycode_handled = true;
-
- // Special keycode test.
-
- switch (k->get_keycode()) {
- case KEY_KP_ENTER:
- case KEY_ENTER: {
- if (readonly) {
- break;
- }
-
- String ins = "\n";
-
- // Keep indentation.
- int space_count = 0;
- for (int i = 0; i < cursor.column; i++) {
- if (text[cursor.line][i] == '\t') {
- if (indent_using_spaces) {
- ins += space_indent;
- } else {
- ins += "\t";
- }
- space_count = 0;
- } else if (text[cursor.line][i] == ' ') {
- space_count++;
-
- if (space_count == indent_size) {
- if (indent_using_spaces) {
- ins += space_indent;
- } else {
- ins += "\t";
- }
- space_count = 0;
- }
- } else {
- break;
- }
- }
-
- if (is_folded(cursor.line)) {
- unfold_line(cursor.line);
- }
-
- bool brace_indent = false;
-
- // No need to indent if we are going upwards.
- if (auto_indent && !(k->get_command() && k->get_shift())) {
- // Indent once again if previous line will end with ':','{','[','(' and the line is not a comment
- // (i.e. colon/brace precedes current cursor position).
- if (cursor.column > 0) {
- bool indent_char_found = false;
- bool should_indent = false;
- char indent_char = ':';
- char c = text[cursor.line][cursor.column];
-
- for (int i = 0; i < cursor.column; i++) {
- c = text[cursor.line][i];
- switch (c) {
- case ':':
- case '{':
- case '[':
- case '(':
- indent_char_found = true;
- should_indent = true;
- indent_char = c;
- continue;
- }
-
- if (indent_char_found && is_line_comment(cursor.line)) {
- should_indent = true;
- break;
- } else if (indent_char_found && !_is_whitespace(c)) {
- should_indent = false;
- indent_char_found = false;
- }
- }
-
- if (!is_line_comment(cursor.line) && should_indent) {
- if (indent_using_spaces) {
- ins += space_indent;
- } else {
- ins += "\t";
- }
-
- // No need to move the brace below if we are not taking the text with us.
- char32_t closing_char = _get_right_pair_symbol(indent_char);
- if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column]) && !k->get_command()) {
- brace_indent = true;
- ins += "\n" + ins.substr(1, ins.length() - 2);
- }
- }
- }
- }
- begin_complex_operation();
- bool first_line = false;
- if (k->get_command()) {
- if (k->get_shift()) {
- if (cursor.line > 0) {
- cursor_set_line(cursor.line - 1);
- cursor_set_column(text[cursor.line].length());
- } else {
- cursor_set_column(0);
- first_line = true;
- }
- } else {
- cursor_set_column(text[cursor.line].length());
- }
- }
-
- insert_text_at_cursor(ins);
-
- if (first_line) {
- cursor_set_line(0);
- } else if (brace_indent) {
- cursor_set_line(cursor.line - 1);
- cursor_set_column(text[cursor.line].length());
- }
- end_complex_operation();
- } break;
- case KEY_ESCAPE: {
- if (completion_hint != "") {
- completion_hint = "";
- update();
- } else {
- keycode_handled = false;
- }
- } break;
- case KEY_TAB: {
- if (k->get_command()) {
- break; // Avoid tab when command.
- }
-
- if (readonly) {
- break;
- }
+ // Check and handle all built in shortcuts.
- if (is_selection_active()) {
- if (k->get_shift()) {
- indent_left();
- } else {
- indent_right();
- }
- } else {
- if (k->get_shift()) {
- // Simple unindent.
- int cc = cursor.column;
- const String &line = text[cursor.line];
-
- int left = _find_first_non_whitespace_column_of_line(line);
- cc = MIN(cc, left);
-
- while (cc < indent_size && cc < left && line[cc] == ' ') {
- cc++;
- }
+ // AUTO-COMPLETE
- if (cc > 0 && cc <= text[cursor.line].length()) {
- if (text[cursor.line][cc - 1] == '\t') {
- // Tabs unindentation.
- _remove_text(cursor.line, cc - 1, cursor.line, cc);
- if (cursor.column >= left) {
- cursor_set_column(MAX(0, cursor.column - 1));
- }
- update();
- } else {
- // Spaces unindentation.
- int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc);
- if (spaces_to_remove > 0) {
- _remove_text(cursor.line, cc - spaces_to_remove, cursor.line, cc);
- if (cursor.column > left - spaces_to_remove) { // Inside text?
- cursor_set_column(MAX(0, cursor.column - spaces_to_remove));
- }
- update();
- }
- }
- } else if (cc == 0 && line.length() > 0 && line[0] == '\t') {
- _remove_text(cursor.line, 0, cursor.line, 1);
- update();
- }
- } else {
- // Simple indent.
- if (indent_using_spaces) {
- // Insert only as much spaces as needed till next indentation level.
- int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor.column);
- String indent_to_insert = String();
- for (int i = 0; i < spaces_to_add; i++) {
- indent_to_insert = ' ' + indent_to_insert;
- }
- _insert_text_at_cursor(indent_to_insert);
- } else {
- _insert_text_at_cursor("\t");
- }
- }
- }
-
- } break;
- case KEY_BACKSPACE: {
- if (readonly) {
- break;
- }
-
-#ifdef APPLE_STYLE_KEYS
- if (k->get_alt() && cursor.column > 1) {
-#else
- if (k->get_alt()) {
- keycode_handled = false;
- break;
- } else if (k->get_command() && cursor.column > 1) {
-#endif
- int line = cursor.line;
- int column = cursor.column;
-
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
- for (int i = words.size() - 1; i >= 0; i--) {
- if (words[i].x < column) {
- column = words[i].x;
- break;
- }
- }
-
- _remove_text(line, column, cursor.line, cursor.column);
-
- cursor_set_line(line);
- cursor_set_column(column);
-
-#ifdef APPLE_STYLE_KEYS
- } else if (k->get_command()) {
- int cursor_current_column = cursor.column;
- cursor.column = 0;
- _remove_text(cursor.line, 0, cursor.line, cursor_current_column);
-#endif
- } else {
- if (cursor.line > 0 && is_line_hidden(cursor.line - 1)) {
- unfold_line(cursor.line - 1);
- }
- backspace_at_cursor();
- }
-
- } break;
- case KEY_KP_4: {
- if (k->get_unicode() != 0) {
- keycode_handled = false;
- break;
- }
- [[fallthrough]];
- }
- case KEY_LEFT: {
- if (k->get_shift()) {
- _pre_shift_selection();
-#ifdef APPLE_STYLE_KEYS
- } else {
-#else
- } else if (!k->get_alt()) {
-#endif
- deselect();
- }
-
-#ifdef APPLE_STYLE_KEYS
- if (k->get_command()) {
- // Start at first column (it's slightly faster that way) and look for the first non-whitespace character.
- int new_cursor_pos = 0;
- for (int i = 0; i < text[cursor.line].length(); ++i) {
- if (!_is_whitespace(text[cursor.line][i])) {
- new_cursor_pos = i;
- break;
- }
- }
- if (new_cursor_pos == cursor.column) {
- // We're already at the first text character, so move to the very beginning of the line.
- cursor_set_column(0);
- } else {
- // We're somewhere to the right of the first text character; move to the first one.
- cursor_set_column(new_cursor_pos);
- }
- } else if (k->get_alt()) {
-#else
- if (k->get_alt()) {
- keycode_handled = false;
- break;
- } else if (k->get_command()) {
-#endif
- int cc = cursor.column;
-
- if (cc == 0 && cursor.line > 0) {
- cursor_set_line(cursor.line - 1);
- cursor_set_column(text[cursor.line].length());
- } else {
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid());
- for (int i = words.size() - 1; i >= 0; i--) {
- if (words[i].x < cc) {
- cc = words[i].x;
- break;
- }
- }
- cursor_set_column(cc);
- }
+ if (k->is_action("ui_text_completion_query", true)) {
+ query_code_comple();
+ accept_event();
+ return;
+ }
- } else if (cursor.column == 0) {
- if (cursor.line > 0) {
- cursor_set_line(cursor.line - num_lines_from(CLAMP(cursor.line - 1, 0, text.size() - 1), -1));
- cursor_set_column(text[cursor.line].length());
- }
+ if (completion_active) {
+ if (k->is_action("ui_up", true)) {
+ if (completion_index > 0) {
+ completion_index--;
} else {
- if (mid_grapheme_caret_enabled) {
- cursor_set_column(cursor_get_column() - 1);
- } else {
- cursor_set_column(TS->shaped_text_prev_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), cursor_get_column()));
- }
+ completion_index = completion_options.size() - 1;
}
-
- if (k->get_shift()) {
- _post_shift_selection();
- }
-
- } break;
- case KEY_KP_6: {
- if (k->get_unicode() != 0) {
- keycode_handled = false;
- break;
- }
- [[fallthrough]];
+ completion_current = completion_options[completion_index];
+ update();
+ accept_event();
+ return;
}
- case KEY_RIGHT: {
- if (k->get_shift()) {
- _pre_shift_selection();
-#ifdef APPLE_STYLE_KEYS
- } else {
-#else
- } else if (!k->get_alt()) {
-#endif
- deselect();
- }
-
-#ifdef APPLE_STYLE_KEYS
- if (k->get_command()) {
- cursor_set_column(text[cursor.line].length());
- } else if (k->get_alt()) {
-#else
- if (k->get_alt()) {
- keycode_handled = false;
- break;
- } else if (k->get_command()) {
-#endif
- int cc = cursor.column;
-
- if (cc == text[cursor.line].length() && cursor.line < text.size() - 1) {
- cursor_set_line(cursor.line + 1);
- cursor_set_column(0);
- } else {
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid());
- for (int i = 0; i < words.size(); i++) {
- if (words[i].y > cc) {
- cc = words[i].y;
- break;
- }
- }
- cursor_set_column(cc);
- }
-
- } else if (cursor.column == text[cursor.line].length()) {
- if (cursor.line < text.size() - 1) {
- cursor_set_line(cursor_get_line() + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1), true, false);
- cursor_set_column(0);
- }
+ if (k->is_action("ui_down", true)) {
+ if (completion_index < completion_options.size() - 1) {
+ completion_index++;
} else {
- if (mid_grapheme_caret_enabled) {
- cursor_set_column(cursor_get_column() + 1);
- } else {
- cursor_set_column(TS->shaped_text_next_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), cursor_get_column()));
- }
- }
-
- if (k->get_shift()) {
- _post_shift_selection();
+ completion_index = 0;
}
-
- } break;
- case KEY_KP_8: {
- if (k->get_unicode() != 0) {
- keycode_handled = false;
- break;
- }
- [[fallthrough]];
+ completion_current = completion_options[completion_index];
+ update();
+ accept_event();
+ return;
}
- case KEY_UP: {
- if (k->get_alt()) {
- keycode_handled = false;
- break;
- }
-#ifndef APPLE_STYLE_KEYS
- if (k->get_command()) {
-#else
- if (k->get_command() && k->get_alt()) {
-#endif
- _scroll_lines_up();
- break;
- }
-
- if (k->get_shift()) {
- _pre_shift_selection();
- }
-
-#ifdef APPLE_STYLE_KEYS
- if (k->get_command()) {
- cursor_set_line(0);
- } else
-#endif
- {
- int cur_wrap_index = get_cursor_wrap_index();
- if (cur_wrap_index > 0) {
- cursor_set_line(cursor.line, true, false, cur_wrap_index - 1);
- } else if (cursor.line == 0) {
- cursor_set_column(0);
- } else {
- int new_line = cursor.line - num_lines_from(cursor.line - 1, -1);
- if (line_wraps(new_line)) {
- cursor_set_line(new_line, true, false, times_line_wraps(new_line));
- } else {
- cursor_set_line(new_line, true, false);
- }
- }
- }
-
- if (k->get_shift()) {
- _post_shift_selection();
+ if (k->is_action("ui_page_up", true)) {
+ completion_index -= get_theme_constant("completion_lines");
+ if (completion_index < 0) {
+ completion_index = 0;
}
- _cancel_code_hint();
-
- } break;
- case KEY_KP_2: {
- if (k->get_unicode() != 0) {
- keycode_handled = false;
- break;
- }
- [[fallthrough]];
+ completion_current = completion_options[completion_index];
+ update();
+ accept_event();
+ return;
}
- case KEY_DOWN: {
- if (k->get_alt()) {
- keycode_handled = false;
- break;
- }
-#ifndef APPLE_STYLE_KEYS
- if (k->get_command()) {
-#else
- if (k->get_command() && k->get_alt()) {
-#endif
- _scroll_lines_down();
- break;
- }
-
- if (k->get_shift()) {
- _pre_shift_selection();
- }
-
-#ifdef APPLE_STYLE_KEYS
- if (k->get_command()) {
- cursor_set_line(get_last_unhidden_line(), true, false, 9999);
- } else
-#endif
- {
- int cur_wrap_index = get_cursor_wrap_index();
- if (cur_wrap_index < times_line_wraps(cursor.line)) {
- cursor_set_line(cursor.line, true, false, cur_wrap_index + 1);
- } else if (cursor.line == get_last_unhidden_line()) {
- cursor_set_column(text[cursor.line].length());
- } else {
- int new_line = cursor.line + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1);
- cursor_set_line(new_line, true, false, 0);
- }
- }
-
- if (k->get_shift()) {
- _post_shift_selection();
- }
- _cancel_code_hint();
-
- } break;
- case KEY_DELETE: {
- if (readonly) {
- break;
- }
-
- if (k->get_shift() && !k->get_command() && !k->get_alt() && is_shortcut_keys_enabled()) {
- cut();
- break;
- }
-
- int curline_len = text[cursor.line].length();
-
- if (cursor.line == text.size() - 1 && cursor.column == curline_len) {
- break; // Nothing to do.
+ if (k->is_action("ui_page_down", true)) {
+ completion_index += get_theme_constant("completion_lines");
+ if (completion_index >= completion_options.size()) {
+ completion_index = completion_options.size() - 1;
}
-
- int next_line = cursor.column < curline_len ? cursor.line : cursor.line + 1;
- int next_column;
-
-#ifdef APPLE_STYLE_KEYS
- if (k->get_alt() && cursor.column < curline_len - 1) {
-#else
- if (k->get_alt()) {
- keycode_handled = false;
- break;
- } else if (k->get_command() && cursor.column < curline_len - 1) {
-#endif
-
- int line = cursor.line;
- int column = cursor.column;
-
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
- for (int i = 0; i < words.size(); i++) {
- if (words[i].y > column) {
- column = words[i].y;
- break;
- }
- }
-
- next_line = line;
- next_column = column;
-#ifdef APPLE_STYLE_KEYS
- } else if (k->get_command()) {
- next_column = curline_len;
- next_line = cursor.line;
-#endif
- } else {
- if (mid_grapheme_caret_enabled) {
- next_column = cursor.column < curline_len ? (cursor.column + 1) : 0;
- } else {
- next_column = cursor.column < curline_len ? TS->shaped_text_next_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), (cursor.column)) : 0;
- }
- }
-
- _remove_text(cursor.line, cursor.column, next_line, next_column);
+ completion_current = completion_options[completion_index];
update();
-
- } break;
- case KEY_KP_7: {
- if (k->get_unicode() != 0) {
- keycode_handled = false;
- break;
- }
- [[fallthrough]];
+ accept_event();
+ return;
}
- case KEY_HOME: {
-#ifdef APPLE_STYLE_KEYS
- if (k->get_shift())
- _pre_shift_selection();
-
- cursor_set_line(0);
-
- if (k->get_shift())
- _post_shift_selection();
- else if (k->get_command() || k->get_control())
- deselect();
-#else
- if (k->get_shift()) {
- _pre_shift_selection();
- }
-
- if (k->get_command()) {
- cursor_set_line(0);
- cursor_set_column(0);
- } else {
- // Move cursor column to start of wrapped row and then to start of text.
- Vector<String> rows = get_wrap_rows_text(cursor.line);
- int wi = get_cursor_wrap_index();
- int row_start_col = 0;
- for (int i = 0; i < wi; i++) {
- row_start_col += rows[i].length();
- }
- if (cursor.column == row_start_col || wi == 0) {
- // Compute whitespace symbols seq length.
- int current_line_whitespace_len = 0;
- while (current_line_whitespace_len < text[cursor.line].length()) {
- char32_t c = text[cursor.line][current_line_whitespace_len];
- if (c != '\t' && c != ' ') {
- break;
- }
- current_line_whitespace_len++;
- }
-
- if (cursor_get_column() == current_line_whitespace_len) {
- cursor_set_column(0);
- } else {
- cursor_set_column(current_line_whitespace_len);
- }
- } else {
- cursor_set_column(row_start_col);
- }
- }
-
- if (k->get_shift()) {
- _post_shift_selection();
- } else if (k->get_command() || k->get_control()) {
- deselect();
- }
- _cancel_completion();
- completion_hint = "";
-#endif
- } break;
- case KEY_KP_1: {
- if (k->get_unicode() != 0) {
- keycode_handled = false;
- break;
+ if (k->is_action("ui_home", true)) {
+ if (completion_index > 0) {
+ completion_index = 0;
+ completion_current = completion_options[completion_index];
+ update();
}
- [[fallthrough]];
+ accept_event();
+ return;
}
- case KEY_END: {
-#ifdef APPLE_STYLE_KEYS
- if (k->get_shift())
- _pre_shift_selection();
-
- cursor_set_line(get_last_unhidden_line(), true, false, 9999);
-
- if (k->get_shift())
- _post_shift_selection();
- else if (k->get_command() || k->get_control())
- deselect();
-#else
- if (k->get_shift()) {
- _pre_shift_selection();
- }
-
- if (k->get_command()) {
- cursor_set_line(get_last_unhidden_line(), true, false, 9999);
- }
-
- // Move cursor column to end of wrapped row and then to end of text.
- Vector<String> rows = get_wrap_rows_text(cursor.line);
- int wi = get_cursor_wrap_index();
- int row_end_col = -1;
- for (int i = 0; i < wi + 1; i++) {
- row_end_col += rows[i].length();
- }
- if (wi == rows.size() - 1 || cursor.column == row_end_col) {
- cursor_set_column(text[cursor.line].length());
- } else {
- cursor_set_column(row_end_col);
- }
-
- if (k->get_shift()) {
- _post_shift_selection();
- } else if (k->get_command() || k->get_control()) {
- deselect();
- }
-
- _cancel_completion();
- completion_hint = "";
-#endif
- } break;
- case KEY_KP_9: {
- if (k->get_unicode() != 0) {
- keycode_handled = false;
- break;
+ if (k->is_action("ui_end", true)) {
+ if (completion_index < completion_options.size() - 1) {
+ completion_index = completion_options.size() - 1;
+ completion_current = completion_options[completion_index];
+ update();
}
- [[fallthrough]];
+ accept_event();
+ return;
}
- case KEY_PAGEUP: {
- if (k->get_shift()) {
- _pre_shift_selection();
- }
-
- int wi;
- int n_line = cursor.line - num_lines_from_rows(cursor.line, get_cursor_wrap_index(), -get_visible_rows(), wi) + 1;
- cursor_set_line(n_line, true, false, wi);
-
- if (k->get_shift()) {
- _post_shift_selection();
- }
-
+ if (k->is_action("ui_text_completion_accept", true)) {
+ _confirm_completion();
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_cancel", true)) {
_cancel_completion();
- completion_hint = "";
+ accept_event();
+ return;
+ }
- } break;
- case KEY_KP_3: {
- if (k->get_unicode() != 0) {
- keycode_handled = false;
- break;
+ // Handle Unicode here (if no modifiers active) and update autocomplete.
+ if (k->get_unicode() >= 32) {
+ if (allow_unicode_handling && !readonly) {
+ _handle_unicode_character(k->get_unicode(), had_selection, true);
+ accept_event();
+ return;
}
- [[fallthrough]];
}
- case KEY_PAGEDOWN: {
- if (k->get_shift()) {
- _pre_shift_selection();
- }
-
- int wi;
- int n_line = cursor.line + num_lines_from_rows(cursor.line, get_cursor_wrap_index(), get_visible_rows(), wi) - 1;
- cursor_set_line(n_line, true, false, wi);
+ }
- if (k->get_shift()) {
- _post_shift_selection();
- }
+ // NEWLINES.
+ if (k->is_action("ui_text_newline_above", true)) {
+ _new_line(false, true);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_newline_blank", true)) {
+ _new_line(false);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_newline", true)) {
+ _new_line();
+ accept_event();
+ return;
+ }
- _cancel_completion();
- completion_hint = "";
+ // INDENTATION.
+ if (k->is_action("ui_text_dedent", true)) {
+ _indent_left();
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_indent", true)) {
+ _indent_right();
+ accept_event();
+ return;
+ }
- } break;
- case KEY_A: {
-#ifndef APPLE_STYLE_KEYS
- if (!k->get_control() || k->get_shift() || k->get_alt()) {
- keycode_handled = false;
- break;
- }
- if (is_shortcut_keys_enabled()) {
- select_all();
- }
-#else
- if ((!k->get_command() && !k->get_control())) {
- keycode_handled = false;
- break;
- }
- if (!k->get_shift() && k->get_command() && is_shortcut_keys_enabled())
- select_all();
- else if (k->get_control()) {
- if (k->get_shift())
- _pre_shift_selection();
-
- int current_line_whitespace_len = 0;
- while (current_line_whitespace_len < text[cursor.line].length()) {
- char32_t c = text[cursor.line][current_line_whitespace_len];
- if (c != '\t' && c != ' ')
- break;
- current_line_whitespace_len++;
- }
+ // BACKSPACE AND DELETE.
+ if (k->is_action("ui_text_backspace_all_to_left", true)) {
+ _backspace(false, true);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_backspace_word", true)) {
+ _backspace(true);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_backspace", true)) {
+ _backspace();
+ if (completion_active) {
+ _update_completion_candidates();
+ }
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_delete_all_to_right", true)) {
+ _delete(false, true);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_delete_word", true)) {
+ _delete(true);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_delete", true)) {
+ _delete();
+ accept_event();
+ return;
+ }
- if (cursor_get_column() == current_line_whitespace_len)
- cursor_set_column(0);
- else
- cursor_set_column(current_line_whitespace_len);
+ // SCROLLING.
+ if (k->is_action("ui_text_scroll_up", true)) {
+ _scroll_lines_up();
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_scroll_down", true)) {
+ _scroll_lines_down();
+ accept_event();
+ return;
+ }
- if (k->get_shift())
- _post_shift_selection();
- else if (k->get_command() || k->get_control())
- deselect();
- }
- } break;
- case KEY_E: {
- if (!k->get_control() || k->get_command() || k->get_alt()) {
- keycode_handled = false;
- break;
- }
+ // SELECT ALL, CUT, COPY, PASTE.
- if (k->get_shift())
- _pre_shift_selection();
+ if (k->is_action("ui_text_select_all", true)) {
+ select_all();
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_cut", true)) {
+ cut();
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_copy", true)) {
+ copy();
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_paste", true)) {
+ paste();
+ accept_event();
+ return;
+ }
- if (k->get_command())
- cursor_set_line(text.size() - 1, true, false);
- cursor_set_column(text[cursor.line].length());
+ // UNDO/REDO.
+ if (k->is_action("ui_undo", true)) {
+ undo();
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_redo", true)) {
+ redo();
+ accept_event();
+ return;
+ }
- if (k->get_shift())
- _post_shift_selection();
- else if (k->get_command() || k->get_control())
- deselect();
+ // MISC.
- _cancel_completion();
+ if (k->is_action("ui_menu", true)) {
+ if (context_menu_enabled) {
+ menu->set_position(get_screen_transform().xform(_get_cursor_pixel_pos()));
+ menu->set_size(Vector2(1, 1));
+ _generate_context_menu();
+ menu->popup();
+ menu->grab_focus();
+ }
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_toggle_insert_mode", true)) {
+ set_insert_mode(!insert_mode);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_cancel", true)) {
+ if (completion_hint != "") {
completion_hint = "";
-#endif
- } break;
- case (KEY_QUOTELEFT): { // Swap current input direction (primary cursor)
- if (!k->get_command()) {
- keycode_handled = false;
- break;
- }
-
- if (input_direction == TEXT_DIRECTION_LTR) {
- input_direction = TEXT_DIRECTION_RTL;
- } else {
- input_direction = TEXT_DIRECTION_LTR;
- }
- cursor_set_column(cursor.column);
update();
- } break;
- case KEY_X: {
- if (readonly) {
- break;
- }
- if (!k->get_command() || k->get_shift() || k->get_alt()) {
- keycode_handled = false;
- break;
- }
- if (is_shortcut_keys_enabled()) {
- cut();
- }
-
- } break;
- case KEY_C: {
- if (!k->get_command() || k->get_shift() || k->get_alt()) {
- keycode_handled = false;
- break;
- }
-
- if (is_shortcut_keys_enabled()) {
- copy();
- }
-
- } break;
- case KEY_Z: {
- if (readonly) {
- break;
- }
-
- if (!k->get_command()) {
- keycode_handled = false;
- break;
- }
-
- if (is_shortcut_keys_enabled()) {
- if (k->get_shift()) {
- redo();
- } else {
- undo();
- }
- }
- } break;
- case KEY_Y: {
- if (readonly) {
- break;
- }
-
- if (!k->get_command()) {
- keycode_handled = false;
- break;
- }
-
- if (is_shortcut_keys_enabled()) {
- redo();
- }
- } break;
- case KEY_V: {
- if (readonly) {
- break;
- }
- if (!k->get_command() || k->get_shift() || k->get_alt()) {
- keycode_handled = false;
- break;
- }
-
- if (is_shortcut_keys_enabled()) {
- paste();
- }
-
- } break;
- case KEY_SPACE: {
-#ifdef OSX_ENABLED
- if (completion_enabled && k->get_metakey()) { // cmd-space is spotlight shortcut in OSX
-#else
- if (completion_enabled && k->get_command()) {
-#endif
-
- query_code_comple();
- keycode_handled = true;
- } else {
- keycode_handled = false;
- }
+ }
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_swap_input_direction", true)) {
+ _swap_current_input_direction();
+ accept_event();
+ return;
+ }
- } break;
+ // CURSOR MOVEMENT
- case KEY_MENU: {
- if (context_menu_enabled) {
- menu->set_position(get_screen_transform().xform(_get_cursor_pixel_pos()));
- menu->set_size(Vector2(1, 1));
- menu->popup();
- menu->grab_focus();
- }
- } break;
+ k = k->duplicate();
+ bool shift_pressed = k->get_shift();
+ // Remove shift or else actions will not match. Use above variable for selection.
+ k->set_shift(false);
- default: {
- keycode_handled = false;
- } break;
+ // CURSOR MOVEMENT - LEFT, RIGHT.
+ if (k->is_action("ui_text_caret_word_left", true)) {
+ _move_cursor_left(shift_pressed, true);
+ accept_event();
+ return;
}
-
- if (keycode_handled) {
+ if (k->is_action("ui_text_caret_left", true)) {
+ _move_cursor_left(shift_pressed, false);
accept_event();
+ return;
}
-
- if (k->get_keycode() == KEY_INSERT) {
- set_insert_mode(!insert_mode);
+ if (k->is_action("ui_text_caret_word_right", true)) {
+ _move_cursor_right(shift_pressed, true);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_caret_right", true)) {
+ _move_cursor_right(shift_pressed, false);
accept_event();
return;
}
- if (!keycode_handled && !k->get_command()) { // For German keyboards.
-
- if (k->get_unicode() >= 32) {
- if (readonly) {
- return;
- }
-
- // Remove the old character if in insert mode and no selection.
- if (insert_mode && !had_selection) {
- begin_complex_operation();
-
- // Make sure we don't try and remove empty space.
- if (cursor.column < get_line(cursor.line).length()) {
- _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
- }
- }
-
- const char32_t chr[2] = { (char32_t)k->get_unicode(), 0 };
+ // CURSOR MOVEMENT - UP, DOWN.
+ if (k->is_action("ui_text_caret_up", true)) {
+ _move_cursor_up(shift_pressed);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_caret_down", true)) {
+ _move_cursor_down(shift_pressed);
+ accept_event();
+ return;
+ }
- if (completion_hint != "" && k->get_unicode() == ')') {
- completion_hint = "";
- }
- if (auto_brace_completion_enabled && _is_pair_symbol(chr[0])) {
- _consume_pair_symbol(chr[0]);
- } else {
- _insert_text_at_cursor(chr);
- }
+ // CURSOR MOVEMENT - DOCUMENT START/END.
+ if (k->is_action("ui_text_caret_document_start", true)) { // && shift_pressed) {
+ _move_cursor_document_start(shift_pressed);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_caret_document_end", true)) { // && shift_pressed) {
+ _move_cursor_document_end(shift_pressed);
+ accept_event();
+ return;
+ }
- if (insert_mode && !had_selection) {
- end_complex_operation();
- }
+ // CURSOR MOVEMENT - LINE START/END.
+ if (k->is_action("ui_text_caret_line_start", true)) {
+ _move_cursor_to_line_start(shift_pressed);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_caret_line_end", true)) {
+ _move_cursor_to_line_end(shift_pressed);
+ accept_event();
+ return;
+ }
- if (selection.active != had_selection) {
- end_complex_operation();
- }
- accept_event();
- }
+ // CURSOR MOVEMENT - PAGE UP/DOWN.
+ if (k->is_action("ui_text_caret_page_up", true)) {
+ _move_cursor_page_up(shift_pressed);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_caret_page_down", true)) {
+ _move_cursor_page_down(shift_pressed);
+ accept_event();
+ return;
}
- return;
+ if (allow_unicode_handling && !readonly && k->get_unicode() >= 32) {
+ // Handle Unicode (if no modifiers active).
+ _handle_unicode_character(k->get_unicode(), had_selection, false);
+ accept_event();
+ return;
+ }
}
}
@@ -3746,8 +3604,8 @@ void TextEdit::_scroll_lines_up() {
if (!selection.active) {
int cur_line = cursor.line;
int cur_wrap = get_cursor_wrap_index();
- int last_vis_line = get_last_visible_line();
- int last_vis_wrap = get_last_visible_line_wrap_index();
+ int last_vis_line = get_last_full_visible_line();
+ int last_vis_wrap = get_last_full_visible_line_wrap_index();
if (cur_line > last_vis_line || (cur_line == last_vis_line && cur_wrap > last_vis_wrap)) {
cursor_set_line(last_vis_line, false, false, last_vis_wrap);
@@ -4001,7 +3859,7 @@ void TextEdit::_insert_text_at_cursor(const String &p_text) {
int new_column, new_line;
_insert_text(cursor.line, cursor.column, p_text, &new_line, &new_column);
_update_scrollbars();
- cursor_set_line(new_line);
+ cursor_set_line(new_line, false);
cursor_set_column(new_column);
update();
@@ -4033,25 +3891,50 @@ int TextEdit::_get_control_height() const {
return control_height;
}
+int TextEdit::_get_menu_action_accelerator(const String &p_action) {
+ const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action);
+ if (!events) {
+ return 0;
+ }
+
+ // Use first event in the list for the accelerator.
+ const List<Ref<InputEvent>>::Element *first_event = events->front();
+ if (!first_event) {
+ return 0;
+ }
+
+ const Ref<InputEventKey> event = first_event->get();
+ if (event.is_null()) {
+ return 0;
+ }
+
+ // Use physical keycode if non-zero
+ if (event->get_physical_keycode() != 0) {
+ return event->get_physical_keycode_with_modifiers();
+ } else {
+ return event->get_keycode_with_modifiers();
+ }
+}
+
void TextEdit::_generate_context_menu() {
// Reorganize context menu.
menu->clear();
if (!readonly) {
- menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_X : 0);
+ menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : 0);
}
- menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_C : 0);
+ menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : 0);
if (!readonly) {
- menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_V : 0);
+ menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : 0);
}
menu->add_separator();
if (is_selecting_enabled()) {
- menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_A : 0);
+ menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : 0);
}
if (!readonly) {
menu->add_item(RTR("Clear"), MENU_CLEAR);
menu->add_separator();
- menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_Z : 0);
- menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z : 0);
+ menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : 0);
+ menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : 0);
}
menu->add_separator();
menu->add_submenu_item(RTR("Text writing direction"), "DirMenu");
@@ -4120,8 +4003,8 @@ void TextEdit::adjust_viewport_to_cursor() {
int first_vis_line = get_first_visible_line();
int first_vis_wrap = cursor.wrap_ofs;
- int last_vis_line = get_last_visible_line();
- int last_vis_wrap = get_last_visible_line_wrap_index();
+ int last_vis_line = get_last_full_visible_line();
+ int last_vis_wrap = get_last_full_visible_line_wrap_index();
if (cur_line < first_vis_line || (cur_line == first_vis_line && cur_wrap < first_vis_wrap)) {
// Cursor is above screen.
@@ -4550,7 +4433,7 @@ int TextEdit::get_column_x_offset_for_line(int p_char, int p_line) const {
void TextEdit::insert_text_at_cursor(const String &p_text) {
if (selection.active) {
- cursor_set_line(selection.from_line);
+ cursor_set_line(selection.from_line, false);
cursor_set_column(selection.from_column);
_remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
@@ -4890,11 +4773,13 @@ void TextEdit::_update_caches() {
cache.completion_font_color = get_theme_color("completion_font_color");
cache.font = get_theme_font("font");
cache.font_size = get_theme_font_size("font_size");
+ cache.outline_color = get_theme_color("font_outline_color");
+ cache.outline_size = get_theme_constant("outline_size");
cache.caret_color = get_theme_color("caret_color");
cache.caret_background_color = get_theme_color("caret_background_color");
cache.font_color = get_theme_color("font_color");
- cache.font_color_selected = get_theme_color("font_color_selected");
- cache.font_color_readonly = get_theme_color("font_color_readonly");
+ cache.font_selected_color = get_theme_color("font_selected_color");
+ cache.font_readonly_color = get_theme_color("font_readonly_color");
cache.selection_color = get_theme_color("selection_color");
cache.mark_color = get_theme_color("mark_color");
cache.current_line_color = get_theme_color("current_line_color");
@@ -5139,6 +5024,10 @@ void TextEdit::set_auto_indent(bool p_auto_indent) {
}
void TextEdit::cut() {
+ if (readonly) {
+ return;
+ }
+
if (!selection.active) {
String clipboard = text[cursor.line];
DisplayServer::get_singleton()->clipboard_set(clipboard);
@@ -5161,7 +5050,7 @@ void TextEdit::cut() {
DisplayServer::get_singleton()->clipboard_set(clipboard);
_remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
- cursor_set_line(selection.from_line); // Set afterwards else it causes the view to be offset.
+ cursor_set_line(selection.from_line, false); // Set afterwards else it causes the view to be offset.
cursor_set_column(selection.from_column);
selection.active = false;
@@ -5186,6 +5075,10 @@ void TextEdit::copy() {
}
void TextEdit::paste() {
+ if (readonly) {
+ return;
+ }
+
String clipboard = DisplayServer::get_singleton()->clipboard_get();
begin_complex_operation();
@@ -5193,7 +5086,7 @@ void TextEdit::paste() {
selection.active = false;
selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
_remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
- cursor_set_line(selection.from_line);
+ cursor_set_line(selection.from_line, false);
cursor_set_column(selection.from_column);
} else if (!cut_copy_line.is_empty() && cut_copy_line == clipboard) {
@@ -5889,6 +5782,10 @@ void TextEdit::_clear_redo() {
}
void TextEdit::undo() {
+ if (readonly) {
+ return;
+ }
+
_push_current_op();
if (undo_stack_pos == nullptr) {
@@ -5928,17 +5825,20 @@ void TextEdit::undo() {
_update_scrollbars();
if (undo_stack_pos->get().type == TextOperation::TYPE_REMOVE) {
- cursor_set_line(undo_stack_pos->get().to_line);
+ cursor_set_line(undo_stack_pos->get().to_line, false);
cursor_set_column(undo_stack_pos->get().to_column);
_cancel_code_hint();
} else {
- cursor_set_line(undo_stack_pos->get().from_line);
+ cursor_set_line(undo_stack_pos->get().from_line, false);
cursor_set_column(undo_stack_pos->get().from_column);
}
update();
}
void TextEdit::redo() {
+ if (readonly) {
+ return;
+ }
_push_current_op();
if (undo_stack_pos == nullptr) {
@@ -5964,7 +5864,7 @@ void TextEdit::redo() {
}
_update_scrollbars();
- cursor_set_line(undo_stack_pos->get().to_line);
+ cursor_set_line(undo_stack_pos->get().to_line, false);
cursor_set_column(undo_stack_pos->get().to_column);
undo_stack_pos = undo_stack_pos->next();
update();
@@ -6099,7 +5999,7 @@ double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const {
}
// Count the number of visible lines up to this line.
- double new_line_scroll_pos = 0;
+ double new_line_scroll_pos = 0.0;
int to = CLAMP(p_line, 0, text.size() - 1);
for (int i = 0; i < to; i++) {
if (!text.is_hidden(i)) {
@@ -6134,19 +6034,19 @@ int TextEdit::get_first_visible_line() const {
return CLAMP(cursor.line_ofs, 0, text.size() - 1);
}
-int TextEdit::get_last_visible_line() const {
+int TextEdit::get_last_full_visible_line() const {
int first_vis_line = get_first_visible_line();
int last_vis_line = 0;
int wi;
- last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi) - 1;
+ last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows(), wi) - 1;
last_vis_line = CLAMP(last_vis_line, 0, text.size() - 1);
return last_vis_line;
}
-int TextEdit::get_last_visible_line_wrap_index() const {
+int TextEdit::get_last_full_visible_line_wrap_index() const {
int first_vis_line = get_first_visible_line();
int wi;
- num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi);
+ num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows(), wi);
return wi;
}
@@ -6877,7 +6777,7 @@ bool TextEdit::_set(const StringName &p_name, const Variant &p_value) {
update();
}
}
- _change_notify();
+ notify_property_list_changed();
return true;
}
@@ -7176,23 +7076,11 @@ void TextEdit::_bind_methods() {
}
TextEdit::TextEdit() {
- setting_row = false;
- draw_tabs = false;
- draw_spaces = false;
- override_selected_font_color = false;
- draw_caret = true;
- max_chars = 0;
clear();
- wrap_enabled = false;
- wrap_at = 0;
- wrap_right_offset = 10;
set_focus_mode(FOCUS_ALL);
_update_caches();
- cache.line_spacing = 1;
- cache.font_size = 16;
set_default_cursor_shape(CURSOR_IBEAM);
- indent_size = 4;
text.set_indent_size(indent_size);
text.clear();
@@ -7202,31 +7090,16 @@ TextEdit::TextEdit() {
add_child(h_scroll);
add_child(v_scroll);
- updating_scrolls = false;
- selection.active = false;
-
h_scroll->connect("value_changed", callable_mp(this, &TextEdit::_scroll_moved));
v_scroll->connect("value_changed", callable_mp(this, &TextEdit::_scroll_moved));
v_scroll->connect("scrolling", callable_mp(this, &TextEdit::_v_scroll_input));
- cursor_changed_dirty = false;
- text_changed_dirty = false;
-
- selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
- selection.selecting_line = 0;
- selection.selecting_column = 0;
- selection.selecting_text = false;
- selection.active = false;
-
- block_caret = false;
- caret_blink_enabled = false;
caret_blink_timer = memnew(Timer);
add_child(caret_blink_timer);
caret_blink_timer->set_wait_time(0.65);
caret_blink_timer->connect("timeout", callable_mp(this, &TextEdit::_toggle_draw_caret));
cursor_set_blink_enabled(false);
- right_click_moves_caret = true;
idle_detect = memnew(Timer);
add_child(idle_detect);
@@ -7239,54 +7112,8 @@ TextEdit::TextEdit() {
click_select_held->set_wait_time(0.05);
click_select_held->connect("timeout", callable_mp(this, &TextEdit::_click_selection_held));
- current_op.type = TextOperation::TYPE_NONE;
- undo_enabled = true;
undo_stack_max_size = GLOBAL_GET("gui/common/text_edit_undo_stack_max_size");
- undo_stack_pos = nullptr;
- setting_text = false;
- last_dblclk = 0;
- current_op.version = 0;
- version = 0;
- saved_version = 0;
- completion_enabled = false;
- completion_active = false;
- completion_line_ofs = 0;
- tooltip_obj = nullptr;
- line_length_guidelines = false;
- line_length_guideline_soft_col = 80;
- line_length_guideline_hard_col = 100;
- hiding_enabled = false;
- next_operation_is_complex = false;
- scroll_past_end_of_file_enabled = false;
- auto_brace_completion_enabled = false;
- brace_matching_enabled = false;
- highlight_all_occurrences = false;
- highlight_current_line = false;
- indent_using_spaces = false;
- space_indent = " ";
- auto_indent = false;
- insert_mode = false;
- window_has_focus = true;
- select_identifiers_enabled = false;
- smooth_scroll_enabled = false;
- scrolling = false;
- minimap_clicked = false;
- dragging_minimap = false;
- can_drag_minimap = false;
- minimap_scroll_ratio = 0;
- minimap_scroll_click_pos = 0;
- dragging_selection = false;
- target_v_scroll = 0;
- v_scroll_speed = 80;
- draw_minimap = false;
- minimap_width = 80;
- minimap_char_size = Point2(1, 2);
- minimap_line_spacing = 1;
-
- selecting_enabled = true;
- context_menu_enabled = true;
- shortcut_keys_enabled = true;
menu = memnew(PopupMenu);
add_child(menu);
@@ -7321,12 +7148,10 @@ TextEdit::TextEdit() {
menu_ctl->add_item(RTR("Soft hyphen (SHY)"), MENU_INSERT_SHY);
menu->add_child(menu_ctl);
- readonly = true; // Initialise to opposite first, so we get past the early-out in set_readonly.
set_readonly(false);
menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
menu_dir->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
menu_ctl->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
- first_draw = true;
}
TextEdit::~TextEdit() {
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index d5b9b46fe2..b0c7314c65 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -92,14 +92,11 @@ private:
Vector<Vector2i> bidi_override;
Ref<TextParagraph> data_buf;
- bool marked;
- bool hidden;
+ bool marked = false;
+ bool hidden = false;
Line() {
data_buf.instance();
-
- marked = false;
- hidden = false;
}
};
@@ -173,46 +170,31 @@ private:
};
struct Cursor {
- int last_fit_x;
- int line, column; ///< cursor
- int x_ofs, line_ofs, wrap_ofs;
- Cursor() {
- last_fit_x = 0;
- line = 0;
- column = 0; ///< cursor
- x_ofs = 0;
- line_ofs = 0;
- wrap_ofs = 0;
- }
+ int last_fit_x = 0;
+ int line = 0;
+ int column = 0; ///< cursor
+ int x_ofs = 0;
+ int line_ofs = 0;
+ int wrap_ofs = 0;
} cursor;
struct Selection {
- SelectionMode selecting_mode;
- int selecting_line, selecting_column;
- int selected_word_beg, selected_word_end, selected_word_origin;
- bool selecting_text;
-
- bool active;
-
- int from_line, from_column;
- int to_line, to_column;
-
- bool shiftclick_left;
- Selection() {
- selecting_mode = SelectionMode::SELECTION_MODE_NONE;
- selecting_line = 0;
- selecting_column = 0;
- selected_word_beg = 0;
- selected_word_end = 0;
- selected_word_origin = 0;
- selecting_text = false;
- active = false;
- from_line = 0;
- from_column = 0;
- to_line = 0;
- to_column = 0;
- shiftclick_left = false;
- }
+ SelectionMode selecting_mode = SelectionMode::SELECTION_MODE_NONE;
+ int selecting_line = 0;
+ int selecting_column = 0;
+ int selected_word_beg = 0;
+ int selected_word_end = 0;
+ int selected_word_origin = 0;
+ bool selecting_text = false;
+
+ bool active = false;
+
+ int from_line = 0;
+ int from_column = 0;
+ int to_line = 0;
+ int to_column = 0;
+
+ bool shiftclick_left = false;
} selection;
Map<int, Dictionary> syntax_highlighting_cache;
@@ -224,25 +206,16 @@ private:
TYPE_REMOVE
};
- Type type;
- int from_line, from_column;
- int to_line, to_column;
+ Type type = TYPE_NONE;
+ int from_line = 0;
+ int from_column = 0;
+ int to_line = 0;
+ int to_column = 0;
String text;
- uint32_t prev_version;
- uint32_t version;
- bool chain_forward;
- bool chain_backward;
- TextOperation() {
- type = TYPE_NONE;
- from_line = 0;
- from_column = 0;
- to_line = 0;
- to_column = 0;
- prev_version = 0;
- version = 0;
- chain_forward = false;
- chain_backward = false;
- }
+ uint32_t prev_version = 0;
+ uint32_t version = 0;
+ bool chain_forward = false;
+ bool chain_backward = false;
};
String ime_text;
@@ -251,7 +224,7 @@ private:
TextOperation current_op;
List<TextOperation> undo_stack;
- List<TextOperation>::Element *undo_stack_pos;
+ List<TextOperation>::Element *undo_stack_pos = nullptr;
int undo_stack_max_size;
void _clear_redo();
@@ -264,20 +237,20 @@ private:
Dictionary _get_line_syntax_highlighting(int p_line);
Set<String> completion_prefixes;
- bool completion_enabled;
+ bool completion_enabled = false;
List<ScriptCodeCompletionOption> completion_sources;
Vector<ScriptCodeCompletionOption> completion_options;
- bool completion_active;
- bool completion_forced;
+ bool completion_active = false;
+ bool completion_forced = false;
ScriptCodeCompletionOption completion_current;
String completion_base;
- int completion_index;
+ int completion_index = 0;
Rect2i completion_rect;
- int completion_line_ofs;
+ int completion_line_ofs = 0;
String completion_hint;
- int completion_hint_offset;
+ int completion_hint_offset = 0;
- bool setting_text;
+ bool setting_text = false;
// data
Text text;
@@ -290,93 +263,93 @@ private:
Array st_args;
bool draw_control_chars = false;
- uint32_t version;
- uint32_t saved_version;
+ uint32_t version = 0;
+ uint32_t saved_version = 0;
- int max_chars;
- bool readonly;
- bool indent_using_spaces;
- int indent_size;
- String space_indent;
+ int max_chars = 0;
+ bool readonly = true; // Initialise to opposite first, so we get past the early-out in set_readonly.
+ bool indent_using_spaces = false;
+ int indent_size = 4;
+ String space_indent = " ";
Timer *caret_blink_timer;
- bool caret_blink_enabled;
- bool draw_caret;
- bool window_has_focus;
- bool block_caret;
- bool right_click_moves_caret;
+ bool caret_blink_enabled = false;
+ bool draw_caret = true;
+ bool window_has_focus = true;
+ bool block_caret = false;
+ bool right_click_moves_caret = true;
bool mid_grapheme_caret_enabled = false;
- bool wrap_enabled;
- int wrap_at;
- int wrap_right_offset;
-
- bool first_draw;
- bool setting_row;
- bool draw_tabs;
- bool draw_spaces;
- bool override_selected_font_color;
- bool cursor_changed_dirty;
- bool text_changed_dirty;
- bool undo_enabled;
- bool line_length_guidelines;
- int line_length_guideline_soft_col;
- int line_length_guideline_hard_col;
- bool hiding_enabled;
- bool draw_minimap;
- int minimap_width;
- Point2 minimap_char_size;
- int minimap_line_spacing;
-
- bool highlight_all_occurrences;
- bool scroll_past_end_of_file_enabled;
- bool auto_brace_completion_enabled;
- bool brace_matching_enabled;
- bool highlight_current_line;
- bool auto_indent;
+ bool wrap_enabled = false;
+ int wrap_at = 0;
+ int wrap_right_offset = 10;
+
+ bool first_draw = true;
+ bool setting_row = false;
+ bool draw_tabs = false;
+ bool draw_spaces = false;
+ bool override_selected_font_color = false;
+ bool cursor_changed_dirty = false;
+ bool text_changed_dirty = false;
+ bool undo_enabled = true;
+ bool line_length_guidelines = false;
+ int line_length_guideline_soft_col = 80;
+ int line_length_guideline_hard_col = 100;
+ bool hiding_enabled = false;
+ bool draw_minimap = false;
+ int minimap_width = 80;
+ Point2 minimap_char_size = Point2(1, 2);
+ int minimap_line_spacing = 1;
+
+ bool highlight_all_occurrences = false;
+ bool scroll_past_end_of_file_enabled = false;
+ bool auto_brace_completion_enabled = false;
+ bool brace_matching_enabled = false;
+ bool highlight_current_line = false;
+ bool auto_indent = false;
String cut_copy_line;
- bool insert_mode;
- bool select_identifiers_enabled;
-
- bool smooth_scroll_enabled;
- bool scrolling;
- bool dragging_selection;
- bool dragging_minimap;
- bool can_drag_minimap;
- bool minimap_clicked;
- double minimap_scroll_ratio;
- double minimap_scroll_click_pos;
- float target_v_scroll;
- float v_scroll_speed;
+ bool insert_mode = false;
+ bool select_identifiers_enabled = false;
+
+ bool smooth_scroll_enabled = false;
+ bool scrolling = false;
+ bool dragging_selection = false;
+ bool dragging_minimap = false;
+ bool can_drag_minimap = false;
+ bool minimap_clicked = false;
+ double minimap_scroll_ratio = 0.0;
+ double minimap_scroll_click_pos = 0.0;
+ float target_v_scroll = 0.0;
+ float v_scroll_speed = 80.0;
String highlighted_word;
- uint64_t last_dblclk;
+ uint64_t last_dblclk = 0;
Timer *idle_detect;
Timer *click_select_held;
HScrollBar *h_scroll;
VScrollBar *v_scroll;
- bool updating_scrolls;
+ bool updating_scrolls = false;
- Object *tooltip_obj;
+ Object *tooltip_obj = nullptr;
StringName tooltip_func;
Variant tooltip_ud;
- bool next_operation_is_complex;
+ bool next_operation_is_complex = false;
- bool callhint_below;
+ bool callhint_below = false;
Vector2 callhint_offset;
String search_text;
- uint32_t search_flags;
- int search_result_line;
- int search_result_col;
+ uint32_t search_flags = 0;
+ int search_result_line = 0;
+ int search_result_col = 0;
- bool selecting_enabled;
+ bool selecting_enabled = true;
- bool context_menu_enabled;
- bool shortcut_keys_enabled;
+ bool context_menu_enabled = true;
+ bool shortcut_keys_enabled = true;
bool virtual_keyboard_enabled = true;
void _generate_context_menu();
@@ -400,8 +373,8 @@ private:
void set_line_as_center_visible(int p_line, int p_wrap_index = 0);
void set_line_as_last_visible(int p_line, int p_wrap_index = 0);
int get_first_visible_line() const;
- int get_last_visible_line() const;
- int get_last_visible_line_wrap_index() const;
+ int get_last_full_visible_line() const;
+ int get_last_full_visible_line_wrap_index() const;
double get_visible_rows_offset() const;
double get_v_scroll_offset() const;
@@ -435,6 +408,7 @@ private:
int _get_control_height() const;
Point2 _get_local_mouse_pos() const;
+ int _get_menu_action_accelerator(const String &p_action);
void _reset_caret_blink_timer();
void _toggle_draw_caret();
@@ -468,6 +442,26 @@ private:
int _calculate_spaces_till_next_left_indent(int column);
int _calculate_spaces_till_next_right_indent(int column);
+ // Methods used in shortcuts
+ void _swap_current_input_direction();
+ void _new_line(bool p_split_current = true, bool p_above = false);
+ void _indent_right();
+ void _indent_left();
+ void _move_cursor_left(bool p_select, bool p_move_by_word = false);
+ void _move_cursor_right(bool p_select, bool p_move_by_word = false);
+ void _move_cursor_up(bool p_select);
+ void _move_cursor_down(bool p_select);
+ void _move_cursor_to_line_start(bool p_select);
+ void _move_cursor_to_line_end(bool p_select);
+ void _move_cursor_page_up(bool p_select);
+ void _move_cursor_page_down(bool p_select);
+ void _backspace(bool p_word = false, bool p_all_to_left = false);
+ void _delete(bool p_word = false, bool p_all_to_right = false);
+ void _delete_selection();
+ void _move_cursor_document_start(bool p_select);
+ void _move_cursor_document_end(bool p_select);
+ void _handle_unicode_character(uint32_t unicode, bool p_had_selection, bool p_update_auto_complete);
+
protected:
struct Cache {
Ref<Texture2D> tab_icon;
@@ -477,7 +471,9 @@ protected:
Ref<StyleBox> style_focus;
Ref<StyleBox> style_readonly;
Ref<Font> font;
- int font_size;
+ int font_size = 16;
+ int outline_size = 0;
+ Color outline_color;
Color completion_background_color;
Color completion_selected_color;
Color completion_existing_color;
@@ -485,8 +481,8 @@ protected:
Color caret_color;
Color caret_background_color;
Color font_color;
- Color font_color_selected;
- Color font_color_readonly;
+ Color font_selected_color;
+ Color font_readonly_color;
Color selection_color;
Color mark_color;
Color code_folding_color;
@@ -498,12 +494,8 @@ protected:
Color search_result_border_color;
Color background_color;
- int line_spacing;
- int minimap_width;
- Cache() {
- line_spacing = 0;
- minimap_width = 0;
- }
+ int line_spacing = 1;
+ int minimap_width = 0;
} cache;
virtual String get_tooltip(const Point2 &p_pos) const override;
@@ -668,8 +660,8 @@ public:
int get_row_height() const;
void backspace_at_cursor();
- void indent_left();
- void indent_right();
+ void indent_selected_lines_left();
+ void indent_selected_lines_right();
int get_indent_level(int p_line) const;
bool is_line_comment(int p_line) const;
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index 23c48b0906..f43e3d1a9d 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -29,7 +29,9 @@
/*************************************************************************/
#include "texture_button.h"
+
#include "core/typedefs.h"
+
#include <stdlib.h>
Size2 TextureButton::get_minimum_size() const {
@@ -247,8 +249,8 @@ void TextureButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_disabled_texture", "texture"), &TextureButton::set_disabled_texture);
ClassDB::bind_method(D_METHOD("set_focused_texture", "texture"), &TextureButton::set_focused_texture);
ClassDB::bind_method(D_METHOD("set_click_mask", "mask"), &TextureButton::set_click_mask);
- ClassDB::bind_method(D_METHOD("set_expand", "p_expand"), &TextureButton::set_expand);
- ClassDB::bind_method(D_METHOD("set_stretch_mode", "p_mode"), &TextureButton::set_stretch_mode);
+ ClassDB::bind_method(D_METHOD("set_expand", "expand"), &TextureButton::set_expand);
+ ClassDB::bind_method(D_METHOD("set_stretch_mode", "mode"), &TextureButton::set_stretch_mode);
ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &TextureButton::set_flip_h);
ClassDB::bind_method(D_METHOD("is_flipped_h"), &TextureButton::is_flipped_h);
ClassDB::bind_method(D_METHOD("set_flip_v", "enable"), &TextureButton::set_flip_v);
@@ -375,13 +377,4 @@ bool TextureButton::is_flipped_v() const {
return vflip;
}
-TextureButton::TextureButton() {
- expand = false;
- stretch_mode = STRETCH_SCALE;
- hflip = false;
- vflip = false;
-
- _texture_region = Rect2();
- _position_rect = Rect2();
- _tile = false;
-}
+TextureButton::TextureButton() {}
diff --git a/scene/gui/texture_button.h b/scene/gui/texture_button.h
index 4b750c3fb1..8361f3c341 100644
--- a/scene/gui/texture_button.h
+++ b/scene/gui/texture_button.h
@@ -54,15 +54,15 @@ private:
Ref<Texture2D> disabled;
Ref<Texture2D> focused;
Ref<BitMap> click_mask;
- bool expand;
- StretchMode stretch_mode;
+ bool expand = false;
+ StretchMode stretch_mode = STRETCH_SCALE;
Rect2 _texture_region;
Rect2 _position_rect;
- bool _tile;
+ bool _tile = false;
- bool hflip;
- bool vflip;
+ bool hflip = false;
+ bool vflip = false;
protected:
virtual Size2 get_minimum_size() const override;
diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp
index 76564e2b17..46ce9d5ca9 100644
--- a/scene/gui/texture_progress_bar.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -145,9 +145,9 @@ Point2 TextureProgressBar::unit_val_to_uv(float val) {
float angle = (val * Math_TAU) - Math_PI * 0.5;
Point2 dir = Vector2(Math::cos(angle), Math::sin(angle));
float t1 = 1.0;
- float cp = 0;
- float cq = 0;
- float cr = 0;
+ float cp = 0.0;
+ float cq = 0.0;
+ float cr = 0.0;
float edgeLeft = 0.0;
float edgeRight = 1.0;
float edgeBottom = 0.0;
@@ -540,17 +540,5 @@ void TextureProgressBar::_bind_methods() {
}
TextureProgressBar::TextureProgressBar() {
- mode = FILL_LEFT_TO_RIGHT;
- rad_init_angle = 0;
- rad_center_off = Point2();
- rad_max_degrees = 360;
set_mouse_filter(MOUSE_FILTER_PASS);
-
- nine_patch_stretch = false;
- stretch_margin[SIDE_LEFT] = 0;
- stretch_margin[SIDE_RIGHT] = 0;
- stretch_margin[SIDE_BOTTOM] = 0;
- stretch_margin[SIDE_TOP] = 0;
-
- tint_under = tint_progress = tint_over = Color(1, 1, 1);
}
diff --git a/scene/gui/texture_progress_bar.h b/scene/gui/texture_progress_bar.h
index ee33a915f7..a3883a7017 100644
--- a/scene/gui/texture_progress_bar.h
+++ b/scene/gui/texture_progress_bar.h
@@ -98,13 +98,15 @@ public:
TextureProgressBar();
private:
- FillMode mode;
- float rad_init_angle;
- float rad_max_degrees;
+ FillMode mode = FILL_LEFT_TO_RIGHT;
+ float rad_init_angle = 0.0;
+ float rad_max_degrees = 360.0;
Point2 rad_center_off;
- bool nine_patch_stretch;
- int stretch_margin[4];
- Color tint_under, tint_progress, tint_over;
+ bool nine_patch_stretch = false;
+ int stretch_margin[4] = {};
+ Color tint_under = Color(1, 1, 1);
+ Color tint_progress = Color(1, 1, 1);
+ Color tint_over = Color(1, 1, 1);
Point2 unit_val_to_uv(float val);
Point2 get_relative_center();
diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp
index beb06b7739..1cba88e06f 100644
--- a/scene/gui/texture_rect.cpp
+++ b/scene/gui/texture_rect.cpp
@@ -217,11 +217,7 @@ bool TextureRect::is_flipped_v() const {
}
TextureRect::TextureRect() {
- expand = false;
- hflip = false;
- vflip = false;
set_mouse_filter(MOUSE_FILTER_PASS);
- stretch_mode = STRETCH_SCALE_ON_EXPAND;
}
TextureRect::~TextureRect() {
diff --git a/scene/gui/texture_rect.h b/scene/gui/texture_rect.h
index 0f3172f5bd..0f93d5732f 100644
--- a/scene/gui/texture_rect.h
+++ b/scene/gui/texture_rect.h
@@ -49,11 +49,11 @@ public:
};
private:
- bool expand;
- bool hflip;
- bool vflip;
+ bool expand = false;
+ bool hflip = false;
+ bool vflip = false;
Ref<Texture2D> texture;
- StretchMode stretch_mode;
+ StretchMode stretch_mode = STRETCH_SCALE_ON_EXPAND;
void _texture_changed();
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index a968a83dad..d10ce584b7 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -410,6 +410,14 @@ bool TreeItem::is_collapsed() {
return collapsed;
}
+void TreeItem::uncollapse_tree() {
+ TreeItem *t = this;
+ while (t) {
+ t->set_collapsed(false);
+ t = t->parent;
+ }
+}
+
void TreeItem::set_custom_minimum_height(int p_height) {
custom_min_height = p_height;
_changed_notify();
@@ -842,6 +850,8 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collapsed", "enable"), &TreeItem::set_collapsed);
ClassDB::bind_method(D_METHOD("is_collapsed"), &TreeItem::is_collapsed);
+ ClassDB::bind_method(D_METHOD("uncollapse_tree"), &TreeItem::uncollapse_tree);
+
ClassDB::bind_method(D_METHOD("set_custom_minimum_height", "height"), &TreeItem::set_custom_minimum_height);
ClassDB::bind_method(D_METHOD("get_custom_minimum_height"), &TreeItem::get_custom_minimum_height);
@@ -1018,7 +1028,7 @@ void Tree::update_cache() {
cache.custom_button_font_highlight = get_theme_color("custom_button_font_highlight");
cache.font_color = get_theme_color("font_color");
- cache.font_color_selected = get_theme_color("font_color_selected");
+ cache.font_selected_color = get_theme_color("font_selected_color");
cache.guide_color = get_theme_color("guide_color");
cache.drop_position_color = get_theme_color("drop_position_color");
cache.hseparation = get_theme_constant("hseparation");
@@ -1118,7 +1128,7 @@ int Tree::get_item_height(TreeItem *p_item) const {
return height;
}
-void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color) {
+void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color) {
ERR_FAIL_COND(cache.font.is_null());
Rect2i rect = p_rect;
@@ -1160,6 +1170,9 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
Point2 draw_pos = rect.position;
draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) / 2.0);
p_cell.text_buf->set_width(MAX(0, rect.size.width));
+ if (p_ol_size > 0 && p_ol_color.a > 0) {
+ p_cell.text_buf->draw_outline(ci, draw_pos, p_ol_size, p_ol_color);
+ }
p_cell.text_buf->draw(ci, draw_pos, p_color);
rect.position.x += ts.width + cache.hseparation;
rect.size.x -= ts.width + cache.hseparation;
@@ -1182,6 +1195,9 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
Point2 draw_pos = rect.position;
draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) / 2.0);
p_cell.text_buf->set_width(MAX(0, rect.size.width));
+ if (p_ol_size > 0 && p_ol_color.a > 0) {
+ p_cell.text_buf->draw_outline(ci, draw_pos, p_ol_size, p_ol_color);
+ }
p_cell.text_buf->draw(ci, draw_pos, p_color);
}
}
@@ -1372,7 +1388,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
}
}
- if ((select_mode == SELECT_ROW && selected_item == p_item) || p_item->cells[i].selected) {
+ if ((select_mode == SELECT_ROW && selected_item == p_item) || p_item->cells[i].selected || !p_item->has_meta("__focus_rect")) {
Rect2i r(cell_rect.position, cell_rect.size);
p_item->set_meta("__focus_rect", Rect2(r.position, r.size));
@@ -1433,7 +1449,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
}
}
- Color col = p_item->cells[i].custom_color ? p_item->cells[i].color : get_theme_color(p_item->cells[i].selected ? "font_color_selected" : "font_color");
+ Color col = p_item->cells[i].custom_color ? p_item->cells[i].color : get_theme_color(p_item->cells[i].selected ? "font_selected_color" : "font_color");
+ Color font_outline_color = get_theme_color("font_outline_color");
+ int outline_size = get_theme_constant("outline_size");
Color icon_col = p_item->cells[i].icon_color;
if (p_item->cells[i].dirty) {
@@ -1450,7 +1468,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
switch (p_item->cells[i].mode) {
case TreeItem::CELL_MODE_STRING: {
- draw_item_rect(p_item->cells.write[i], item_rect, col, icon_col);
+ draw_item_rect(p_item->cells.write[i], item_rect, col, icon_col, outline_size, font_outline_color);
} break;
case TreeItem::CELL_MODE_CHECK: {
Ref<Texture2D> checked = cache.checked;
@@ -1471,7 +1489,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
item_rect.size.x -= check_w;
item_rect.position.x += check_w;
- draw_item_rect(p_item->cells.write[i], item_rect, col, icon_col);
+ draw_item_rect(p_item->cells.write[i], item_rect, col, icon_col, outline_size, font_outline_color);
} break;
case TreeItem::CELL_MODE_RANGE: {
@@ -1485,8 +1503,14 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
p_item->cells.write[i].text_buf->set_width(cell_width);
if (rtl) {
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ p_item->cells[i].text_buf->draw_outline(ci, text_pos + Vector2(cell_width - text_width, 0), outline_size, font_outline_color);
+ }
p_item->cells[i].text_buf->draw(ci, text_pos + Vector2(cell_width - text_width, 0), col);
} else {
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ p_item->cells[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
p_item->cells[i].text_buf->draw(ci, text_pos, col);
}
@@ -1501,8 +1525,14 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
int cell_width = item_rect.size.x - updown->get_width();
if (rtl) {
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ p_item->cells[i].text_buf->draw_outline(ci, text_pos + Vector2(cell_width - text_width, 0), outline_size, font_outline_color);
+ }
p_item->cells[i].text_buf->draw(ci, text_pos + Vector2(cell_width - text_width, 0), col);
} else {
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ p_item->cells[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
p_item->cells[i].text_buf->draw(ci, text_pos, col);
}
@@ -1543,7 +1573,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
}
if (!p_item->cells[i].editable) {
- draw_item_rect(p_item->cells.write[i], item_rect, col, icon_col);
+ draw_item_rect(p_item->cells.write[i], item_rect, col, icon_col, outline_size, font_outline_color);
break;
}
@@ -1558,7 +1588,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
if (p_item->cells[i].custom_button) {
if (cache.hover_item == p_item && cache.hover_cell == i) {
- if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT)) {
+ if (Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT)) {
draw_style_box(cache.custom_button_pressed, ir);
} else {
draw_style_box(cache.custom_button_hover, ir);
@@ -1571,7 +1601,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
ir.position += cache.custom_button->get_offset();
}
- draw_item_rect(p_item->cells.write[i], ir, col, icon_col);
+ draw_item_rect(p_item->cells.write[i], ir, col, icon_col, outline_size, font_outline_color);
downarrow->draw(ci, arrow_pos);
@@ -1642,7 +1672,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
float line_width = 1.0;
#ifdef TOOLS_ENABLED
- line_width *= EDSCALE;
+ line_width *= Math::round(EDSCALE);
#endif
Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs;
@@ -1795,7 +1825,7 @@ Rect2 Tree::search_item_rect(TreeItem *p_from, TreeItem *p_item) {
}
void Tree::_range_click_timeout() {
- if (range_item_last && !range_drag_enabled && Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT)) {
+ if (range_item_last && !range_drag_enabled && Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT)) {
Point2 pos = get_local_mouse_position() - cache.bg->get_offset();
if (show_column_titles) {
pos.y -= _get_title_button_height();
@@ -1813,7 +1843,7 @@ void Tree::_range_click_timeout() {
propagate_mouse_activated = false; // done from outside, so signal handler can't clear the tree in the middle of emit (which is a common case)
blocked++;
- propagate_mouse_event(pos + cache.offset, 0, 0, false, root, BUTTON_LEFT, mb);
+ propagate_mouse_event(pos + cache.offset, 0, 0, false, root, MOUSE_BUTTON_LEFT, mb);
blocked--;
if (range_click_timer->is_one_shot()) {
@@ -1836,7 +1866,7 @@ void Tree::_range_click_timeout() {
}
}
-int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_doubleclick, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod) {
+int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod) {
int item_h = compute_item_height(p_item) + cache.vseparation;
bool skip = (p_item == root && hide_root);
@@ -1930,10 +1960,10 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
col_width -= w + cache.button_margin;
}
- if (p_button == BUTTON_LEFT || (p_button == BUTTON_RIGHT && allow_rmb_select)) {
+ if (p_button == MOUSE_BUTTON_LEFT || (p_button == MOUSE_BUTTON_RIGHT && allow_rmb_select)) {
/* process selection */
- if (p_doubleclick && (!c.editable || c.mode == TreeItem::CELL_MODE_CUSTOM || c.mode == TreeItem::CELL_MODE_ICON /*|| c.mode==TreeItem::CELL_MODE_CHECK*/)) { //it's confusing for check
+ if (p_double_click && (!c.editable || c.mode == TreeItem::CELL_MODE_CUSTOM || c.mode == TreeItem::CELL_MODE_ICON /*|| c.mode==TreeItem::CELL_MODE_CHECK*/)) { //it's confusing for check
propagate_mouse_activated = true;
@@ -1942,10 +1972,10 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
}
if (select_mode == SELECT_MULTI && p_mod->get_command() && c.selectable) {
- if (!c.selected || p_button == BUTTON_RIGHT) {
+ if (!c.selected || p_button == MOUSE_BUTTON_RIGHT) {
p_item->select(col);
emit_signal("multi_selected", p_item, col, true);
- if (p_button == BUTTON_RIGHT) {
+ if (p_button == MOUSE_BUTTON_RIGHT) {
emit_signal("item_rmb_selected", get_local_mouse_position());
}
@@ -1962,21 +1992,21 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
bool inrange = false;
select_single_item(p_item, root, col, selected_item, &inrange);
- if (p_button == BUTTON_RIGHT) {
+ if (p_button == MOUSE_BUTTON_RIGHT) {
emit_signal("item_rmb_selected", get_local_mouse_position());
}
} else {
int icount = _count_selected_items(root);
- if (select_mode == SELECT_MULTI && icount > 1 && p_button != BUTTON_RIGHT) {
+ if (select_mode == SELECT_MULTI && icount > 1 && p_button != MOUSE_BUTTON_RIGHT) {
single_select_defer = p_item;
single_select_defer_column = col;
} else {
- if (p_button != BUTTON_RIGHT || !c.selected) {
+ if (p_button != MOUSE_BUTTON_RIGHT || !c.selected) {
select_single_item(p_item, root, col);
}
- if (p_button == BUTTON_RIGHT) {
+ if (p_button == MOUSE_BUTTON_RIGHT) {
emit_signal("item_rmb_selected", get_local_mouse_position());
}
}
@@ -2046,7 +2076,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
/* touching the combo */
bool up = p_pos.y < (item_h / 2);
- if (p_button == BUTTON_LEFT) {
+ if (p_button == MOUSE_BUTTON_LEFT) {
if (range_click_timer->get_time_left() == 0) {
range_item_last = p_item;
range_up_last = up;
@@ -2063,13 +2093,13 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
item_edited(col, p_item);
- } else if (p_button == BUTTON_RIGHT) {
+ } else if (p_button == MOUSE_BUTTON_RIGHT) {
p_item->set_range(col, (up ? c.max : c.min));
item_edited(col, p_item);
- } else if (p_button == BUTTON_WHEEL_UP) {
+ } else if (p_button == MOUSE_BUTTON_WHEEL_UP) {
p_item->set_range(col, c.val + c.step);
item_edited(col, p_item);
- } else if (p_button == BUTTON_WHEEL_DOWN) {
+ } else if (p_button == MOUSE_BUTTON_WHEEL_DOWN) {
p_item->set_range(col, c.val - c.step);
item_edited(col, p_item);
}
@@ -2102,14 +2132,14 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
}
if (!p_item->cells[col].custom_button || !on_arrow) {
- item_edited(col, p_item, p_button == BUTTON_LEFT);
+ item_edited(col, p_item, p_button == MOUSE_BUTTON_LEFT);
}
click_handled = true;
return -1;
} break;
};
- if (!bring_up_editor || p_button != BUTTON_LEFT) {
+ if (!bring_up_editor || p_button != MOUSE_BUTTON_LEFT) {
return -1;
}
@@ -2137,7 +2167,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
TreeItem *c = p_item->children;
while (c) {
- int child_h = propagate_mouse_event(new_pos, x_ofs, y_ofs, p_doubleclick, c, p_button, p_mod);
+ int child_h = propagate_mouse_event(new_pos, x_ofs, y_ofs, p_double_click, c, p_button, p_mod);
if (child_h < 0) {
return -1; // break, stop propagating, no need to anymore
@@ -2149,7 +2179,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
item_h += child_h;
}
}
- if (p_item == root && p_button == BUTTON_RIGHT) {
+ if (p_item == root && p_button == MOUSE_BUTTON_RIGHT) {
emit_signal("empty_rmb", get_local_mouse_position());
}
}
@@ -2372,6 +2402,8 @@ void Tree::_go_down() {
}
void Tree::_gui_input(Ref<InputEvent> p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
Ref<InputEventKey> k = p_event;
bool is_command = k.is_valid() && k->get_command();
@@ -2690,7 +2722,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
bool rtl = is_layout_rtl();
if (!b->is_pressed()) {
- if (b->get_button_index() == BUTTON_LEFT) {
+ if (b->get_button_index() == MOUSE_BUTTON_LEFT) {
Point2 pos = b->get_position();
if (rtl) {
pos.x = get_size().width - pos.x;
@@ -2771,8 +2803,8 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
}
switch (b->get_button_index()) {
- case BUTTON_RIGHT:
- case BUTTON_LEFT: {
+ case MOUSE_BUTTON_RIGHT:
+ case MOUSE_BUTTON_LEFT: {
Ref<StyleBox> bg = cache.bg;
Point2 pos = b->get_position();
@@ -2785,7 +2817,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
pos.y -= _get_title_button_height();
if (pos.y < 0) {
- if (b->get_button_index() == BUTTON_LEFT) {
+ if (b->get_button_index() == MOUSE_BUTTON_LEFT) {
pos.x += cache.offset.x;
int len = 0;
for (int i = 0; i < columns.size(); i++) {
@@ -2803,7 +2835,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
}
}
if (!root || (!root->get_children() && hide_root)) {
- if (b->get_button_index() == BUTTON_RIGHT && allow_rmb_select) {
+ if (b->get_button_index() == MOUSE_BUTTON_RIGHT && allow_rmb_select) {
emit_signal("empty_tree_rmb_selected", get_local_mouse_position());
}
break;
@@ -2814,7 +2846,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
propagate_mouse_activated = false;
blocked++;
- propagate_mouse_event(pos + cache.offset, 0, 0, b->is_doubleclick(), root, b->get_button_index(), b);
+ propagate_mouse_event(pos + cache.offset, 0, 0, b->is_double_click(), root, b->get_button_index(), b);
blocked--;
if (pressing_for_editor) {
@@ -2824,7 +2856,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
}
}
- if (b->get_button_index() == BUTTON_RIGHT) {
+ if (b->get_button_index() == MOUSE_BUTTON_RIGHT) {
break;
}
@@ -2847,7 +2879,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
set_physics_process_internal(true);
}
- if (b->get_button_index() == BUTTON_LEFT) {
+ if (b->get_button_index() == MOUSE_BUTTON_LEFT) {
if (get_item_at_position(b->get_position()) == nullptr && !b->get_shift() && !b->get_control() && !b->get_command()) {
emit_signal("nothing_selected");
}
@@ -2860,7 +2892,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
}
} break;
- case BUTTON_WHEEL_UP: {
+ case MOUSE_BUTTON_WHEEL_UP: {
double prev_value = v_scroll->get_value();
v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() * b->get_factor() / 8);
if (v_scroll->get_value() != prev_value) {
@@ -2868,7 +2900,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
}
} break;
- case BUTTON_WHEEL_DOWN: {
+ case MOUSE_BUTTON_WHEEL_DOWN: {
double prev_value = v_scroll->get_value();
v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * b->get_factor() / 8);
if (v_scroll->get_value() != prev_value) {
@@ -2981,6 +3013,10 @@ bool Tree::edit_selected() {
return false;
}
+bool Tree::is_editing() {
+ return popup_editor->is_visible();
+}
+
Size2 Tree::get_internal_min_size() const {
Size2i size = cache.bg->get_offset();
if (root) {
@@ -3143,6 +3179,8 @@ void Tree::_notification(int p_what) {
Ref<StyleBox> bg = cache.bg;
Ref<StyleBox> bg_focus = get_theme_stylebox("bg_focus");
+ Color font_outline_color = get_theme_color("font_outline_color");
+ int outline_size = get_theme_constant("outline_size");
Point2 draw_ofs;
draw_ofs += bg->get_offset();
@@ -3179,7 +3217,12 @@ void Tree::_notification(int p_what) {
//text
int clip_w = tbrect.size.width - sb->get_minimum_size().width;
columns.write[i].text_buf->set_width(clip_w);
- columns[i].text_buf->draw(ci, tbrect.position + Point2i(sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2, (tbrect.size.height - columns[i].text_buf->get_size().y) / 2), cache.title_button_color);
+
+ Vector2 text_pos = tbrect.position + Point2i(sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2, (tbrect.size.height - columns[i].text_buf->get_size().y) / 2);
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ columns[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
+ columns[i].text_buf->draw(ci, text_pos, cache.title_button_color);
}
}
}
@@ -3315,6 +3358,9 @@ void Tree::item_selected(int p_column, TreeItem *p_item) {
//emit_signal("multi_selected",p_item,p_column,true); - NO this is for TreeItem::select
selected_col = p_column;
+ if (!selected_item) {
+ selected_item = p_item;
+ }
} else {
select_single_item(p_item, root, p_column);
}
@@ -3322,6 +3368,14 @@ void Tree::item_selected(int p_column, TreeItem *p_item) {
}
void Tree::item_deselected(int p_column, TreeItem *p_item) {
+ if (selected_item == p_item) {
+ selected_item = nullptr;
+
+ if (selected_col == p_column) {
+ selected_col = -1;
+ }
+ }
+
if (select_mode == SELECT_MULTI || select_mode == SELECT_SINGLE) {
p_item->cells.write[p_column].selected = false;
}
@@ -3468,9 +3522,13 @@ int Tree::get_column_width(int p_column) const {
return columns[p_column].min_width;
}
+ int expand_area = get_size().width;
+
Ref<StyleBox> bg = cache.bg;
- int expand_area = get_size().width - (bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT));
+ if (bg.is_valid()) {
+ expand_area -= bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT);
+ }
if (v_scroll->is_visible_in_tree()) {
expand_area -= v_scroll->get_combined_minimum_size().width;
@@ -4149,6 +4207,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_edited"), &Tree::get_edited);
ClassDB::bind_method(D_METHOD("get_edited_column"), &Tree::get_edited_column);
+ ClassDB::bind_method(D_METHOD("edit_selected"), &Tree::edit_selected);
ClassDB::bind_method(D_METHOD("get_custom_popup_rect"), &Tree::get_custom_popup_rect);
ClassDB::bind_method(D_METHOD("get_item_area_rect", "item", "column"), &Tree::_get_item_rect, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_item_at_position", "position"), &Tree::get_item_at_position);
@@ -4174,6 +4233,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_column_title_language", "column"), &Tree::get_column_title_language);
ClassDB::bind_method(D_METHOD("get_scroll"), &Tree::get_scroll);
+ ClassDB::bind_method(D_METHOD("scroll_to_item", "item"), &Tree::_scroll_to_item);
ClassDB::bind_method(D_METHOD("set_hide_folding", "hide"), &Tree::set_hide_folding);
ClassDB::bind_method(D_METHOD("is_folding_hidden"), &Tree::is_folding_hidden);
@@ -4206,7 +4266,7 @@ void Tree::_bind_methods() {
ADD_SIGNAL(MethodInfo("item_custom_button_pressed"));
ADD_SIGNAL(MethodInfo("item_double_clicked"));
ADD_SIGNAL(MethodInfo("item_collapsed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem")));
- //ADD_SIGNAL( MethodInfo("item_doubleclicked" ) );
+ //ADD_SIGNAL( MethodInfo("item_double_clicked" ) );
ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("custom_popup_edited", PropertyInfo(Variant::BOOL, "arrow_clicked")));
ADD_SIGNAL(MethodInfo("item_activated"));
@@ -4223,19 +4283,8 @@ void Tree::_bind_methods() {
}
Tree::Tree() {
- selected_col = 0;
columns.resize(1);
- selected_item = nullptr;
- edited_item = nullptr;
- selected_col = -1;
- edited_col = -1;
- hide_root = false;
- select_mode = SELECT_SINGLE;
- root = nullptr;
- popup_menu = nullptr;
- popup_edited_item = nullptr;
- text_editor = nullptr;
set_focus_mode(FOCUS_ALL);
popup_menu = memnew(PopupMenu);
@@ -4279,50 +4328,9 @@ Tree::Tree() {
set_notify_transform(true);
- updating_value_editor = false;
- pressed_button = -1;
- show_column_titles = false;
-
- cache.click_type = Cache::CLICK_NONE;
- cache.hover_type = Cache::CLICK_NONE;
- cache.hover_index = -1;
- cache.click_index = -1;
- cache.click_id = -1;
- cache.click_item = nullptr;
- cache.click_column = 0;
- cache.hover_cell = -1;
- last_keypress = 0;
- focus_in_id = 0;
-
- blocked = 0;
-
- cursor_can_exit_tree = true;
set_mouse_filter(MOUSE_FILTER_STOP);
- drag_speed = 0;
- drag_touching = false;
- drag_touching_deaccel = false;
- pressing_for_editor = false;
- range_drag_enabled = false;
-
- hide_folding = false;
-
- drop_mode_flags = 0;
- drop_mode_over = nullptr;
- drop_mode_section = 0;
- single_select_defer = nullptr;
-
- scrolling = false;
- allow_rmb_select = false;
- force_edit_checkbox_only_on_checkbox = false;
-
set_clip_contents(true);
-
- cache.hover_item = nullptr;
- cache.hover_cell = -1;
-
- allow_reselect = false;
- propagate_mouse_activated = false;
}
Tree::~Tree() {
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index dfc02f760d..6d36f0df7f 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -62,7 +62,7 @@ private:
friend class Tree;
struct Cell {
- TreeCellMode mode;
+ TreeCellMode mode = TreeItem::CELL_MODE_STRING;
Ref<Texture2D> icon;
Rect2i icon_region;
@@ -74,24 +74,27 @@ private:
Control::StructuredTextParser st_parser = Control::STRUCTURED_TEXT_DEFAULT;
Array st_args;
Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED;
- bool dirty;
- double min, max, step, val;
- int icon_max_w;
- bool expr;
- bool checked;
- bool editable;
- bool selected;
- bool selectable;
- bool custom_color;
+ bool dirty = true;
+ double min = 0.0;
+ double max = 100.0;
+ double step = 1.0;
+ double val = 0.0;
+ int icon_max_w = 0;
+ bool expr = false;
+ bool checked = false;
+ bool editable = false;
+ bool selected = false;
+ bool selectable = true;
+ bool custom_color = false;
Color color;
- bool custom_bg_color;
- bool custom_bg_outline;
+ bool custom_bg_color = false;
+ bool custom_bg_outline = false;
Color bg_color;
- bool custom_button;
- bool expand_right;
- Color icon_color;
+ bool custom_button = false;
+ bool expand_right = false;
+ Color icon_color = Color(1, 1, 1);
- TextAlign text_align;
+ TextAlign text_align = ALIGN_LEFT;
Variant meta;
String tooltip;
@@ -100,42 +103,17 @@ private:
StringName custom_draw_callback;
struct Button {
- int id;
- bool disabled;
+ int id = 0;
+ bool disabled = false;
Ref<Texture2D> texture;
- Color color;
+ Color color = Color(1, 1, 1, 1);
String tooltip;
- Button() {
- id = 0;
- disabled = false;
- color = Color(1, 1, 1, 1);
- tooltip = "";
- }
};
Vector<Button> buttons;
Cell() {
text_buf.instance();
- dirty = true;
- custom_draw_obj = ObjectID();
- custom_button = false;
- mode = TreeItem::CELL_MODE_STRING;
- min = 0;
- max = 100;
- step = 1;
- val = 0;
- checked = false;
- editable = false;
- selected = false;
- selectable = true;
- custom_color = false;
- custom_bg_color = false;
- expr = false;
- icon_max_w = 0;
- text_align = ALIGN_LEFT;
- expand_right = false;
- icon_color = Color(1, 1, 1);
}
Size2 get_icon_size() const;
@@ -251,6 +229,8 @@ public:
void set_collapsed(bool p_collapsed);
bool is_collapsed();
+ void uncollapse_tree();
+
void set_custom_minimum_height(int p_height);
int get_custom_minimum_height() const;
@@ -332,46 +312,46 @@ public:
private:
friend class TreeItem;
- TreeItem *root;
- TreeItem *popup_edited_item;
- TreeItem *selected_item;
- TreeItem *edited_item;
+ TreeItem *root = nullptr;
+ TreeItem *popup_edited_item = nullptr;
+ TreeItem *selected_item = nullptr;
+ TreeItem *edited_item = nullptr;
- TreeItem *drop_mode_over;
- int drop_mode_section;
+ TreeItem *drop_mode_over = nullptr;
+ int drop_mode_section = 0;
- TreeItem *single_select_defer;
- int single_select_defer_column;
+ TreeItem *single_select_defer = nullptr;
+ int single_select_defer_column = 0;
- int pressed_button;
- bool pressing_for_editor;
+ int pressed_button = -1;
+ bool pressing_for_editor = false;
String pressing_for_editor_text;
Vector2 pressing_pos;
Rect2 pressing_item_rect;
- float range_drag_base;
- bool range_drag_enabled;
+ float range_drag_base = 0.0;
+ bool range_drag_enabled = false;
Vector2 range_drag_capture_pos;
- bool propagate_mouse_activated;
+ bool propagate_mouse_activated = false;
//TreeItem *cursor_item;
//int cursor_column;
Rect2 custom_popup_rect;
- int edited_col;
- int selected_col;
- int popup_edited_item_col;
- bool hide_root;
- SelectMode select_mode;
+ int edited_col = -1;
+ int selected_col = -1;
+ int popup_edited_item_col = -1;
+ bool hide_root = false;
+ SelectMode select_mode = SELECT_SINGLE;
- int blocked;
+ int blocked = 0;
- int drop_mode_flags;
+ int drop_mode_flags = 0;
struct ColumnInfo {
- int min_width;
- bool expand;
+ int min_width = 1;
+ bool expand = true;
String title;
Ref<TextLine> text_buf;
Dictionary opentype_features;
@@ -379,27 +359,25 @@ private:
Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED;
ColumnInfo() {
text_buf.instance();
- min_width = 1;
- expand = true;
}
};
- bool show_column_titles;
+ bool show_column_titles = false;
VBoxContainer *popup_editor_vb;
Popup *popup_editor;
- LineEdit *text_editor;
+ LineEdit *text_editor = nullptr;
HSlider *value_editor;
- bool updating_value_editor;
- uint64_t focus_in_id;
- PopupMenu *popup_menu;
+ bool updating_value_editor = false;
+ uint64_t focus_in_id = 0;
+ PopupMenu *popup_menu = nullptr;
Vector<ColumnInfo> columns;
Timer *range_click_timer;
- TreeItem *range_item_last;
- bool range_up_last;
+ TreeItem *range_item_last = nullptr;
+ bool range_up_last = false;
void _range_click_timeout();
int compute_item_height(TreeItem *p_item) const;
@@ -409,10 +387,10 @@ private:
void update_item_cell(TreeItem *p_item, int p_col);
void update_item_cache(TreeItem *p_item);
//void draw_item_text(String p_text,const Ref<Texture2D>& p_icon,int p_icon_max_w,bool p_tool,Rect2i p_rect,const Color& p_color);
- void draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color);
+ void draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color);
int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item);
void select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev = nullptr, bool *r_in_range = nullptr, bool p_force_deselect = false);
- int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_doubleclick, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod);
+ int propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool p_double_click, TreeItem *p_item, int p_button, const Ref<InputEventWithModifiers> &p_mod);
void _text_editor_enter(String p_text);
void _text_editor_modal_close();
void value_editor_changed(double p_value);
@@ -434,8 +412,8 @@ private:
struct Cache {
Ref<Font> font;
Ref<Font> tb_font;
- int font_size;
- int tb_font_size;
+ int font_size = 0;
+ int tb_font_size = 0;
Ref<StyleBox> bg;
Ref<StyleBox> selected;
Ref<StyleBox> selected_focus;
@@ -459,21 +437,21 @@ private:
Ref<Texture2D> updown;
Color font_color;
- Color font_color_selected;
+ Color font_selected_color;
Color guide_color;
Color drop_position_color;
Color relationship_line_color;
Color custom_button_font_highlight;
- int hseparation;
- int vseparation;
- int item_margin;
- int button_margin;
+ int hseparation = 0;
+ int vseparation = 0;
+ int item_margin = 0;
+ int button_margin = 0;
Point2 offset;
- int draw_relationship_lines;
- int draw_guides;
- int scroll_border;
- int scroll_speed;
+ int draw_relationship_lines = 0;
+ int draw_guides = 0;
+ int scroll_border = 0;
+ int scroll_speed = 0;
enum ClickType {
CLICK_NONE,
@@ -482,17 +460,17 @@ private:
};
- ClickType click_type;
- ClickType hover_type;
- int click_index;
- int click_id;
- TreeItem *click_item;
- int click_column;
- int hover_index;
+ ClickType click_type = Cache::CLICK_NONE;
+ ClickType hover_type = Cache::CLICK_NONE;
+ int click_index = -1;
+ int click_id = -1;
+ TreeItem *click_item = nullptr;
+ int click_column = 0;
+ int hover_index = -1;
Point2 click_pos;
- TreeItem *hover_item;
- int hover_cell;
+ TreeItem *hover_item = nullptr;
+ int hover_cell = -1;
Point2i text_editor_position;
@@ -510,9 +488,9 @@ private:
Rect2 search_item_rect(TreeItem *p_from, TreeItem *p_item);
//Rect2 get_item_rect(TreeItem *p_item);
- uint64_t last_keypress;
+ uint64_t last_keypress = 0;
String incr_search;
- bool cursor_can_exit_tree;
+ bool cursor_can_exit_tree = true;
void _do_incr_search(const String &p_add);
TreeItem *_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards = false);
@@ -526,21 +504,21 @@ private:
float last_drag_time;
float time_since_motion;*/
- float drag_speed;
- float drag_from;
- float drag_accum;
+ float drag_speed = 0.0;
+ float drag_from = 0.0;
+ float drag_accum = 0.0;
Vector2 last_speed;
- bool drag_touching;
- bool drag_touching_deaccel;
- bool click_handled;
- bool allow_rmb_select;
- bool scrolling;
+ bool drag_touching = false;
+ bool drag_touching_deaccel = false;
+ bool click_handled = false;
+ bool allow_rmb_select = false;
+ bool scrolling = false;
- bool allow_reselect;
+ bool allow_reselect = false;
- bool force_edit_checkbox_only_on_checkbox;
+ bool force_edit_checkbox_only_on_checkbox = false;
- bool hide_folding;
+ bool hide_folding = false;
int _count_selected_items(TreeItem *p_from) const;
void _go_left();
@@ -564,6 +542,10 @@ protected:
return get_item_rect(Object::cast_to<TreeItem>(p_item), p_column);
}
+ void _scroll_to_item(Object *p_item) {
+ scroll_to_item(Object::cast_to<TreeItem>(p_item));
+ }
+
public:
virtual String get_tooltip(const Point2 &p_pos) const override;
@@ -622,6 +604,7 @@ public:
int get_item_offset(TreeItem *p_item) const;
Rect2 get_item_rect(TreeItem *p_item, int p_column = -1) const;
bool edit_selected();
+ bool is_editing();
// First item that starts with the text, from the current focused item down and wraps around.
TreeItem *search_item_text(const String &p_find, int *r_col = nullptr, bool p_selectable = false);
diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp
index 84d689369b..0590ae2415 100644
--- a/scene/gui/video_player.cpp
+++ b/scene/gui/video_player.cpp
@@ -462,25 +462,7 @@ void VideoPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
}
-VideoPlayer::VideoPlayer() {
- volume = 1;
- loops = false;
- paused = false;
- autoplay = false;
- expand = true;
-
- audio_track = 0;
- bus_index = 0;
-
- buffering_ms = 500;
-
- // internal_stream.player=this;
- // stream_rid=AudioServer::get_singleton()->audio_stream_create(&internal_stream);
- last_audio_time = 0;
-
- wait_resampler = 0;
- wait_resampler_limit = 2;
-};
+VideoPlayer::VideoPlayer() {}
VideoPlayer::~VideoPlayer() {
// if (stream_rid.is_valid())
diff --git a/scene/gui/video_player.h b/scene/gui/video_player.h
index 089337eed5..0edad296a1 100644
--- a/scene/gui/video_player.h
+++ b/scene/gui/video_player.h
@@ -41,8 +41,8 @@ class VideoPlayer : public Control {
struct Output {
AudioFrame vol;
- int bus_index;
- Viewport *viewport; //pointer only used for reference to previous mix
+ int bus_index = 0;
+ Viewport *viewport = nullptr; //pointer only used for reference to previous mix
};
Ref<VideoStreamPlayback> playback;
Ref<VideoStream> stream;
@@ -56,17 +56,18 @@ class VideoPlayer : public Control {
AudioRBResampler resampler;
Vector<AudioFrame> mix_buffer;
- int wait_resampler, wait_resampler_limit;
-
- bool paused;
- bool autoplay;
- float volume;
- double last_audio_time;
- bool expand;
- bool loops;
- int buffering_ms;
- int audio_track;
- int bus_index;
+ int wait_resampler = 0;
+ int wait_resampler_limit = 2;
+
+ bool paused = false;
+ bool autoplay = false;
+ float volume = 1.0;
+ double last_audio_time = 0.0;
+ bool expand = true;
+ bool loops = false;
+ int buffering_ms = 500;
+ int audio_track = 0;
+ int bus_index = 0;
StringName bus;