summaryrefslogtreecommitdiff
path: root/scene/gui
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui')
-rw-r--r--scene/gui/base_button.cpp277
-rw-r--r--scene/gui/base_button.h11
-rw-r--r--scene/gui/box_container.cpp1
-rw-r--r--scene/gui/check_button.cpp13
-rw-r--r--scene/gui/color_picker.cpp133
-rw-r--r--scene/gui/color_picker.h10
-rw-r--r--scene/gui/color_rect.h2
-rw-r--r--scene/gui/container.cpp2
-rw-r--r--scene/gui/control.cpp247
-rw-r--r--scene/gui/control.h17
-rw-r--r--scene/gui/dialogs.cpp14
-rw-r--r--scene/gui/dialogs.h3
-rw-r--r--scene/gui/file_dialog.cpp48
-rw-r--r--scene/gui/file_dialog.h2
-rw-r--r--scene/gui/gradient_edit.cpp9
-rw-r--r--scene/gui/gradient_edit.h2
-rw-r--r--scene/gui/graph_edit.cpp12
-rw-r--r--scene/gui/graph_node.cpp4
-rw-r--r--scene/gui/item_list.cpp26
-rw-r--r--scene/gui/label.cpp9
-rw-r--r--scene/gui/line_edit.cpp89
-rw-r--r--scene/gui/line_edit.h3
-rw-r--r--scene/gui/option_button.cpp38
-rw-r--r--scene/gui/option_button.h6
-rw-r--r--scene/gui/popup.cpp30
-rw-r--r--scene/gui/popup.h4
-rw-r--r--scene/gui/popup_menu.cpp138
-rw-r--r--scene/gui/popup_menu.h43
-rw-r--r--scene/gui/range.cpp8
-rw-r--r--scene/gui/reference_rect.cpp21
-rw-r--r--scene/gui/reference_rect.h6
-rw-r--r--scene/gui/rich_text_label.cpp158
-rw-r--r--scene/gui/rich_text_label.h1
-rw-r--r--scene/gui/scroll_bar.cpp57
-rw-r--r--scene/gui/scroll_container.cpp35
-rw-r--r--scene/gui/slider.cpp1
-rw-r--r--scene/gui/spin_box.cpp1
-rw-r--r--scene/gui/split_container.h2
-rw-r--r--scene/gui/tab_container.cpp8
-rw-r--r--scene/gui/tabs.cpp8
-rw-r--r--scene/gui/text_edit.cpp373
-rw-r--r--scene/gui/text_edit.h30
-rw-r--r--scene/gui/tree.cpp78
-rw-r--r--scene/gui/tree.h4
44 files changed, 1236 insertions, 748 deletions
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index f808d6c234..52fcea2a71 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -56,185 +56,23 @@ void BaseButton::_gui_input(Ref<InputEvent> p_event) {
if (status.disabled) // no interaction with disabled button
return;
- Ref<InputEventMouseButton> b = p_event;
+ Ref<InputEventMouseButton> mouse_button = p_event;
+ bool ui_accept = p_event->is_action("ui_accept") && !p_event->is_echo();
- if (b.is_valid()) {
- if (((1 << (b->get_button_index() - 1)) & button_mask) == 0)
- return;
-
- if (status.pressing_button)
- return;
-
- if (action_mode == ACTION_MODE_BUTTON_PRESS) {
-
- if (b->is_pressed()) {
-
- emit_signal("button_down");
-
- if (!toggle_mode) { //mouse press attempt
-
- status.press_attempt = true;
- status.pressing_inside = true;
-
- pressed();
- if (get_script_instance()) {
- Variant::CallError ce;
- get_script_instance()->call(SceneStringNames::get_singleton()->_pressed, NULL, 0, ce);
- }
-
- _unpress_group();
- emit_signal("pressed");
-
- } else {
-
- status.pressed = !status.pressed;
- pressed();
-
- _unpress_group();
- emit_signal("pressed");
-
- toggled(status.pressed);
- if (get_script_instance()) {
- get_script_instance()->call(SceneStringNames::get_singleton()->_toggled, status.pressed);
- }
- emit_signal("toggled", status.pressed);
- }
-
- } else {
-
- emit_signal("button_up");
-
- /* this is pointless if (status.press_attempt && status.pressing_inside) {
- //released();
- emit_signal("released");
- }
-*/
- status.press_attempt = false;
- }
- update();
- return;
- }
-
- if (b->is_pressed()) {
-
- status.press_attempt = true;
- status.pressing_inside = true;
- emit_signal("button_down");
-
- } else {
-
- emit_signal("button_up");
-
- if (status.press_attempt && status.pressing_inside) {
-
- if (!toggle_mode) { //mouse press attempt
-
- pressed();
- if (get_script_instance()) {
- Variant::CallError ce;
- get_script_instance()->call(SceneStringNames::get_singleton()->_pressed, NULL, 0, ce);
- }
-
- _unpress_group();
- emit_signal("pressed");
-
- } else {
-
- status.pressed = !status.pressed;
-
- pressed();
- _unpress_group();
- emit_signal("pressed");
-
- toggled(status.pressed);
- if (get_script_instance()) {
- get_script_instance()->call(SceneStringNames::get_singleton()->_toggled, status.pressed);
- }
- emit_signal("toggled", status.pressed);
- }
- }
-
- status.press_attempt = false;
- }
-
- update();
+ bool button_masked = mouse_button.is_valid() && ((1 << (mouse_button->get_button_index() - 1)) & button_mask) > 0;
+ if (button_masked || ui_accept) {
+ on_action_event(p_event);
+ return;
}
- Ref<InputEventMouseMotion> mm = p_event;
-
- if (mm.is_valid()) {
- if (status.press_attempt && status.pressing_button == 0) {
+ Ref<InputEventMouseMotion> mouse_motion = p_event;
+ if (mouse_motion.is_valid()) {
+ if (status.press_attempt) {
bool last_press_inside = status.pressing_inside;
- status.pressing_inside = has_point(mm->get_position());
- if (last_press_inside != status.pressing_inside)
+ status.pressing_inside = has_point(mouse_motion->get_position());
+ if (last_press_inside != status.pressing_inside) {
update();
- }
- }
-
- if (!mm.is_valid() && !b.is_valid()) {
-
- if (p_event->is_echo()) {
- return;
- }
-
- if (status.disabled) {
- return;
- }
-
- if (status.press_attempt && status.pressing_button == 0) {
- return;
- }
-
- if (p_event->is_action("ui_accept")) {
-
- if (p_event->is_pressed()) {
-
- status.pressing_button++;
- status.press_attempt = true;
- status.pressing_inside = true;
- emit_signal("button_down");
-
- } else if (status.press_attempt) {
-
- if (status.pressing_button)
- status.pressing_button--;
-
- if (status.pressing_button)
- return;
-
- status.press_attempt = false;
- status.pressing_inside = false;
-
- emit_signal("button_up");
-
- if (!toggle_mode) { //mouse press attempt
-
- pressed();
- if (get_script_instance()) {
- Variant::CallError ce;
- get_script_instance()->call(SceneStringNames::get_singleton()->_pressed, NULL, 0, ce);
- }
-
- _unpress_group();
- emit_signal("pressed");
- } else {
-
- status.pressed = !status.pressed;
-
- pressed();
- _unpress_group();
- emit_signal("pressed");
-
- toggled(status.pressed);
- if (get_script_instance()) {
- get_script_instance()->call(SceneStringNames::get_singleton()->_toggled, status.pressed);
- }
- emit_signal("toggled", status.pressed);
- }
}
-
- accept_event();
- update();
}
}
}
@@ -255,7 +93,6 @@ void BaseButton::_notification(int p_what) {
if (status.press_attempt) {
status.press_attempt = false;
- status.pressing_button = 0;
update();
}
}
@@ -268,9 +105,8 @@ void BaseButton::_notification(int p_what) {
if (p_what == NOTIFICATION_FOCUS_EXIT) {
- if (status.pressing_button && status.press_attempt) {
+ if (status.press_attempt) {
status.press_attempt = false;
- status.pressing_button = 0;
status.hovering = false;
update();
} else if (status.hovering) {
@@ -290,21 +126,66 @@ void BaseButton::_notification(int p_what) {
status.hovering = false;
status.press_attempt = false;
status.pressing_inside = false;
- status.pressing_button = 0;
}
}
-void BaseButton::pressed() {
+void BaseButton::_pressed() {
- if (get_script_instance())
- get_script_instance()->call("pressed");
+ if (get_script_instance()) {
+ get_script_instance()->call(SceneStringNames::get_singleton()->_pressed);
+ }
+ pressed();
+ emit_signal("pressed");
}
-void BaseButton::toggled(bool p_pressed) {
+void BaseButton::_toggled(bool p_pressed) {
if (get_script_instance()) {
- get_script_instance()->call("toggled", p_pressed);
+ get_script_instance()->call(SceneStringNames::get_singleton()->_toggled, p_pressed);
}
+ toggled(p_pressed);
+ emit_signal("toggled", p_pressed);
+}
+
+void BaseButton::on_action_event(Ref<InputEvent> p_event) {
+
+ if (p_event->is_pressed()) {
+ status.press_attempt = true;
+ status.pressing_inside = true;
+ emit_signal("button_down");
+ }
+
+ if (status.press_attempt && status.pressing_inside) {
+ if (toggle_mode) {
+ if ((p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_PRESS) || (!p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_RELEASE)) {
+ if (action_mode == ACTION_MODE_BUTTON_PRESS) {
+ status.press_attempt = false;
+ status.pressing_inside = false;
+ }
+ status.pressed = !status.pressed;
+ _unpress_group();
+ _toggled(status.pressed);
+ _pressed();
+ }
+ } else {
+ if (!p_event->is_pressed()) {
+ _pressed();
+ }
+ }
+ }
+
+ if (!p_event->is_pressed()) { // pressed state should be correct with button_up signal
+ emit_signal("button_up");
+ status.press_attempt = false;
+ }
+
+ update();
+}
+
+void BaseButton::pressed() {
+}
+
+void BaseButton::toggled(bool p_pressed) {
}
void BaseButton::set_disabled(bool p_disabled) {
@@ -318,7 +199,6 @@ void BaseButton::set_disabled(bool p_disabled) {
}
status.press_attempt = false;
status.pressing_inside = false;
- status.pressing_button = 0;
}
update();
_change_notify("disabled");
@@ -341,6 +221,8 @@ void BaseButton::set_pressed(bool p_pressed) {
if (p_pressed) {
_unpress_group();
}
+ _toggled(status.pressed);
+
update();
}
@@ -376,7 +258,7 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const {
bool pressing;
if (status.press_attempt) {
- pressing = status.pressing_inside;
+ pressing = (status.pressing_inside || keep_pressed_outside);
if (status.pressed)
pressing = !pressing;
} else {
@@ -446,10 +328,17 @@ Control::FocusMode BaseButton::get_enabled_focus_mode() const {
return enabled_focus_mode;
}
-void BaseButton::set_shortcut(const Ref<ShortCut> &p_shortcut) {
+void BaseButton::set_keep_pressed_outside(bool p_on) {
- if (shortcut.is_null() == p_shortcut.is_null())
- return;
+ keep_pressed_outside = p_on;
+}
+
+bool BaseButton::is_keep_pressed_outside() const {
+
+ return keep_pressed_outside;
+}
+
+void BaseButton::set_shortcut(const Ref<ShortCut> &p_shortcut) {
shortcut = p_shortcut;
set_process_unhandled_input(shortcut.is_valid());
@@ -461,17 +350,12 @@ Ref<ShortCut> BaseButton::get_shortcut() const {
void BaseButton::_unhandled_input(Ref<InputEvent> p_event) {
- if (!is_disabled() && is_visible_in_tree() && p_event->is_pressed() && !p_event->is_echo() && shortcut.is_valid() && shortcut->is_shortcut(p_event)) {
+ if (!is_disabled() && is_visible_in_tree() && !p_event->is_echo() && shortcut.is_valid() && shortcut->is_shortcut(p_event)) {
if (get_viewport()->get_modal_stack_top() && !get_viewport()->get_modal_stack_top()->is_a_parent_of(this))
return; //ignore because of modal window
- if (is_toggle_mode()) {
- set_pressed(!is_pressed());
- emit_signal("toggled", is_pressed());
- }
-
- emit_signal("pressed");
+ on_action_event(p_event);
}
}
@@ -528,6 +412,8 @@ void BaseButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_draw_mode"), &BaseButton::get_draw_mode);
ClassDB::bind_method(D_METHOD("set_enabled_focus_mode", "mode"), &BaseButton::set_enabled_focus_mode);
ClassDB::bind_method(D_METHOD("get_enabled_focus_mode"), &BaseButton::get_enabled_focus_mode);
+ ClassDB::bind_method(D_METHOD("set_keep_pressed_outside", "enabled"), &BaseButton::set_keep_pressed_outside);
+ ClassDB::bind_method(D_METHOD("is_keep_pressed_outside"), &BaseButton::is_keep_pressed_outside);
ClassDB::bind_method(D_METHOD("set_shortcut", "shortcut"), &BaseButton::set_shortcut);
ClassDB::bind_method(D_METHOD("get_shortcut"), &BaseButton::get_shortcut);
@@ -549,6 +435,7 @@ void BaseButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "action_mode", PROPERTY_HINT_ENUM, "Button Press,Button Release"), "set_action_mode", "get_action_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask");
ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_enabled_focus_mode", "get_enabled_focus_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_pressed_outside"), "set_keep_pressed_outside", "is_keep_pressed_outside");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "ShortCut"), "set_shortcut", "get_shortcut");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group");
@@ -566,12 +453,12 @@ 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;
- status.pressing_button = 0;
set_focus_mode(FOCUS_ALL);
enabled_focus_mode = FOCUS_ALL;
action_mode = ACTION_MODE_BUTTON_RELEASE;
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index 9a00cc79f2..ffccdd69d6 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -52,6 +52,7 @@ private:
int button_mask;
bool toggle_mode;
bool shortcut_in_tooltip;
+ bool keep_pressed_outside;
FocusMode enabled_focus_mode;
Ref<ShortCut> shortcut;
@@ -64,13 +65,16 @@ private:
bool pressing_inside;
bool disabled;
- int pressing_button;
} status;
Ref<ButtonGroup> button_group;
void _unpress_group();
+ void _pressed();
+ void _toggled(bool p_pressed);
+
+ void on_action_event(Ref<InputEvent> p_event);
protected:
virtual void pressed();
@@ -110,6 +114,9 @@ public:
void set_action_mode(ActionMode p_mode);
ActionMode get_action_mode() const;
+ void set_keep_pressed_outside(bool p_on);
+ bool is_keep_pressed_outside() const;
+
void set_button_mask(int p_mask);
int get_button_mask() const;
@@ -133,7 +140,7 @@ VARIANT_ENUM_CAST(BaseButton::ActionMode)
class ButtonGroup : public Resource {
- GDCLASS(ButtonGroup, Resource)
+ GDCLASS(ButtonGroup, Resource);
friend class BaseButton;
Set<BaseButton *> buttons;
diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp
index cc37d4cf7d..b7d2131ee9 100644
--- a/scene/gui/box_container.cpp
+++ b/scene/gui/box_container.cpp
@@ -91,7 +91,6 @@ void BoxContainer::_resort() {
int stretch_diff = stretch_max - stretch_min;
if (stretch_diff < 0) {
//avoid negative stretch space
- stretch_max = stretch_min;
stretch_diff = 0;
}
diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp
index 35e3119473..a2d0f388c4 100644
--- a/scene/gui/check_button.cpp
+++ b/scene/gui/check_button.cpp
@@ -34,13 +34,15 @@
#include "servers/visual_server.h"
Size2 CheckButton::get_icon_size() const {
- Ref<Texture> on = Control::get_icon("on");
- Ref<Texture> off = Control::get_icon("off");
+
+ Ref<Texture> on = Control::get_icon(is_disabled() ? "on_disabled" : "on");
+ Ref<Texture> off = Control::get_icon(is_disabled() ? "off_disabled" : "off");
Size2 tex_size = Size2(0, 0);
if (!on.is_null())
tex_size = Size2(on->get_width(), on->get_height());
if (!off.is_null())
tex_size = Size2(MAX(tex_size.width, off->get_width()), MAX(tex_size.height, off->get_height()));
+
return tex_size;
}
@@ -49,9 +51,8 @@ Size2 CheckButton::get_minimum_size() const {
Size2 minsize = Button::get_minimum_size();
Size2 tex_size = get_icon_size();
minsize.width += tex_size.width;
- if (get_text().length() > 0) {
+ if (get_text().length() > 0)
minsize.width += get_constant("hseparation");
- }
Ref<StyleBox> sb = get_stylebox("normal");
minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(MARGIN_TOP) + sb->get_margin(MARGIN_BOTTOM));
@@ -67,8 +68,8 @@ void CheckButton::_notification(int p_what) {
RID ci = get_canvas_item();
- Ref<Texture> on = Control::get_icon("on");
- Ref<Texture> off = Control::get_icon("off");
+ Ref<Texture> on = Control::get_icon(is_disabled() ? "on_disabled" : "on");
+ Ref<Texture> off = Control::get_icon(is_disabled() ? "off_disabled" : "off");
Ref<StyleBox> sb = get_stylebox("normal");
Vector2 ofs;
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index bca3471091..b197971b61 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -93,6 +93,28 @@ void ColorPicker::set_focus_on_line_edit() {
void ColorPicker::_update_controls() {
+ const char *rgb[3] = { "R", "G", "B" };
+ const char *hsv[3] = { "H", "S", "V" };
+
+ if (hsv_mode_enabled) {
+ for (int i = 0; i < 3; i++)
+ labels[i]->set_text(hsv[i]);
+ } else {
+ for (int i = 0; i < 3; i++)
+ labels[i]->set_text(rgb[i]);
+ }
+
+ if (hsv_mode_enabled) {
+ set_raw_mode(false);
+ btn_raw->set_disabled(true);
+ } else if (raw_mode_enabled) {
+ set_hsv_mode(false);
+ btn_hsv->set_disabled(true);
+ } else {
+ btn_raw->set_disabled(false);
+ btn_hsv->set_disabled(false);
+ }
+
if (edit_alpha) {
values[3]->show();
scroll[3]->show();
@@ -104,7 +126,7 @@ void ColorPicker::_update_controls() {
}
}
-void ColorPicker::set_pick_color(const Color &p_color) {
+void ColorPicker::_set_pick_color(const Color &p_color, bool p_update_sliders) {
color = p_color;
if (color != last_hsv) {
@@ -117,7 +139,12 @@ void ColorPicker::set_pick_color(const Color &p_color) {
if (!is_inside_tree())
return;
- _update_color();
+ _update_color(p_update_sliders);
+}
+
+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_edit_alpha(bool p_show) {
@@ -142,11 +169,18 @@ void ColorPicker::_value_changed(double) {
if (updating)
return;
- for (int i = 0; i < 4; i++) {
- color.components[i] = scroll[i]->get_value() / (raw_mode_enabled ? 1.0 : 255.0);
+ 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);
+ } else {
+ for (int i = 0; i < 4; i++) {
+ color.components[i] = scroll[i]->get_value() / (raw_mode_enabled ? 1.0 : 255.0);
+ }
}
- set_pick_color(color);
+ _set_pick_color(color, false);
emit_signal("color_changed", color);
}
@@ -167,22 +201,40 @@ void ColorPicker::_html_entered(const String &p_html) {
emit_signal("color_changed", color);
}
-void ColorPicker::_update_color() {
+void ColorPicker::_update_color(bool p_update_sliders) {
updating = true;
- for (int i = 0; i < 4; i++) {
- if (raw_mode_enabled) {
- scroll[i]->set_step(0.01);
- scroll[i]->set_max(100);
- if (i == 3)
- scroll[i]->set_max(1);
- scroll[i]->set_value(color.components[i]);
+ if (p_update_sliders) {
+
+ if (hsv_mode_enabled) {
+ for (int i = 0; i < 4; i++) {
+ scroll[i]->set_step(1.0);
+ }
+
+ scroll[0]->set_max(359);
+ scroll[0]->set_value(h * 360.0);
+ scroll[1]->set_max(100);
+ scroll[1]->set_value(s * 100.0);
+ scroll[2]->set_max(100);
+ scroll[2]->set_value(v * 100.0);
+ scroll[3]->set_max(255);
+ scroll[3]->set_value(color.components[3] * 255.0);
} else {
- scroll[i]->set_step(1);
- const float byte_value = color.components[i] * 255.0;
- scroll[i]->set_max(next_power_of_2(MAX(255, byte_value)) - 1);
- scroll[i]->set_value(byte_value);
+ for (int i = 0; i < 4; i++) {
+ if (raw_mode_enabled) {
+ scroll[i]->set_step(0.01);
+ scroll[i]->set_max(100);
+ if (i == 3)
+ scroll[i]->set_max(1);
+ scroll[i]->set_value(color.components[i]);
+ } else {
+ scroll[i]->set_step(1);
+ const float byte_value = color.components[i] * 255.0;
+ scroll[i]->set_max(next_power_of_2(MAX(255, byte_value)) - 1);
+ scroll[i]->set_value(byte_value);
+ }
+ }
}
}
@@ -272,13 +324,33 @@ PoolColorArray ColorPicker::get_presets() const {
return arr;
}
+void ColorPicker::set_hsv_mode(bool p_enabled) {
+
+ if (hsv_mode_enabled == p_enabled || raw_mode_enabled)
+ return;
+ hsv_mode_enabled = p_enabled;
+ if (btn_hsv->is_pressed() != p_enabled)
+ btn_hsv->set_pressed(p_enabled);
+
+ if (!is_inside_tree())
+ return;
+
+ _update_controls();
+ _update_color();
+}
+
+bool ColorPicker::is_hsv_mode() const {
+
+ return hsv_mode_enabled;
+}
+
void ColorPicker::set_raw_mode(bool p_enabled) {
- if (raw_mode_enabled == p_enabled)
+ if (raw_mode_enabled == p_enabled || hsv_mode_enabled)
return;
raw_mode_enabled = p_enabled;
- if (btn_mode->is_pressed() != p_enabled)
- btn_mode->set_pressed(p_enabled);
+ if (btn_raw->is_pressed() != p_enabled)
+ btn_raw->set_pressed(p_enabled);
if (!is_inside_tree())
return;
@@ -592,6 +664,8 @@ void ColorPicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPicker::set_pick_color);
ClassDB::bind_method(D_METHOD("get_pick_color"), &ColorPicker::get_pick_color);
+ ClassDB::bind_method(D_METHOD("set_hsv_mode", "mode"), &ColorPicker::set_hsv_mode);
+ ClassDB::bind_method(D_METHOD("is_hsv_mode"), &ColorPicker::is_hsv_mode);
ClassDB::bind_method(D_METHOD("set_raw_mode", "mode"), &ColorPicker::set_raw_mode);
ClassDB::bind_method(D_METHOD("is_raw_mode"), &ColorPicker::is_raw_mode);
ClassDB::bind_method(D_METHOD("set_deferred_mode", "mode"), &ColorPicker::set_deferred_mode);
@@ -623,6 +697,7 @@ void ColorPicker::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "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::BOOL, "presets_enabled"), "set_presets_enabled", "are_presets_enabled");
@@ -639,6 +714,7 @@ ColorPicker::ColorPicker() :
updating = true;
edit_alpha = true;
text_is_constructor = false;
+ hsv_mode_enabled = false;
raw_mode_enabled = false;
deferred_mode_enabled = false;
changing_color = false;
@@ -689,13 +765,12 @@ ColorPicker::ColorPicker() :
VBoxContainer *vbr = memnew(VBoxContainer);
add_child(vbr);
vbr->set_h_size_flags(SIZE_EXPAND_FILL);
- const char *lt[4] = { "R", "G", "B", "A" };
for (int i = 0; i < 4; i++) {
HBoxContainer *hbc = memnew(HBoxContainer);
- labels[i] = memnew(Label(lt[i]));
+ labels[i] = memnew(Label());
labels[i]->set_custom_minimum_size(Size2(get_constant("label_width"), 0));
labels[i]->set_v_size_flags(SIZE_SHRINK_CENTER);
hbc->add_child(labels[i]);
@@ -719,14 +794,20 @@ ColorPicker::ColorPicker() :
vbr->add_child(hbc);
}
+ labels[3]->set_text("A");
HBoxContainer *hhb = memnew(HBoxContainer);
vbr->add_child(hhb);
- btn_mode = memnew(CheckButton);
- hhb->add_child(btn_mode);
- btn_mode->set_text(TTR("Raw Mode"));
- btn_mode->connect("toggled", this, "set_raw_mode");
+ btn_hsv = memnew(CheckButton);
+ hhb->add_child(btn_hsv);
+ btn_hsv->set_text(TTR("HSV"));
+ btn_hsv->connect("toggled", this, "set_hsv_mode");
+
+ btn_raw = memnew(CheckButton);
+ hhb->add_child(btn_raw);
+ btn_raw->set_text(TTR("Raw"));
+ btn_raw->connect("toggled", this, "set_raw_mode");
text_type = memnew(Button);
hhb->add_child(text_type);
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index b5ddf2d0c2..3af27a9856 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -58,7 +58,8 @@ private:
Button *bt_add_preset;
List<Color> presets;
ToolButton *btn_pick;
- CheckButton *btn_mode;
+ CheckButton *btn_hsv;
+ CheckButton *btn_raw;
HSlider *scroll[4];
SpinBox *values[4];
Label *labels[4];
@@ -70,6 +71,7 @@ private:
Color color;
bool raw_mode_enabled;
+ bool hsv_mode_enabled;
bool deferred_mode_enabled;
bool updating;
bool changing_color;
@@ -81,7 +83,7 @@ private:
void _html_entered(const String &p_html);
void _value_changed(double);
void _update_controls();
- void _update_color();
+ void _update_color(bool p_update_sliders = true);
void _update_presets();
void _update_text_value();
void _text_type_toggled();
@@ -106,6 +108,7 @@ public:
void set_edit_alpha(bool p_show);
bool is_editing_alpha() const;
+ 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;
@@ -113,6 +116,9 @@ public:
void erase_preset(const Color &p_color);
PoolColorArray get_presets() const;
+ void set_hsv_mode(bool p_enabled);
+ bool is_hsv_mode() const;
+
void set_raw_mode(bool p_enabled);
bool is_raw_mode() const;
diff --git a/scene/gui/color_rect.h b/scene/gui/color_rect.h
index d7f9ef275d..7a7bbe1029 100644
--- a/scene/gui/color_rect.h
+++ b/scene/gui/color_rect.h
@@ -34,7 +34,7 @@
#include "scene/gui/control.h"
class ColorRect : public Control {
- GDCLASS(ColorRect, Control)
+ GDCLASS(ColorRect, Control);
Color color;
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index 7f1ca58d58..c9cf5f8308 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -177,7 +177,7 @@ String Container::get_configuration_warning() const {
if (warning != String()) {
warning += "\n";
}
- warning += TTR("Container by itself serves no purpose unless a script configures it's children placement behavior.\nIf you dont't intend to add a script, then please use a plain 'Control' node instead.");
+ 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, then please use a plain 'Control' node instead.");
}
return warning;
}
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index a38f97a90c..eccd42cb9f 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -29,22 +29,23 @@
/*************************************************************************/
#include "control.h"
-#include "core/project_settings.h"
-#include "scene/main/canvas_layer.h"
-#include "scene/main/viewport.h"
-#include "servers/visual_server.h"
#include "core/message_queue.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "core/print_string.h"
+#include "core/project_settings.h"
#include "scene/gui/label.h"
#include "scene/gui/panel.h"
+#include "scene/main/canvas_layer.h"
+#include "scene/main/viewport.h"
#include "scene/scene_string_names.h"
+#include "servers/visual_server.h"
+
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
+#include "editor/plugins/canvas_item_editor_plugin.h"
#endif
-#include <stdio.h>
Dictionary Control::_edit_get_state() const {
@@ -66,6 +67,7 @@ Dictionary Control::_edit_get_state() const {
s["margins"] = margins;
return s;
}
+
void Control::_edit_set_state(const Dictionary &p_state) {
Dictionary state = p_state;
@@ -87,7 +89,12 @@ 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());
+#else
+ // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED
set_position(p_position);
+#endif
};
Point2 Control::_edit_get_position() const {
@@ -103,8 +110,14 @@ Size2 Control::_edit_get_scale() const {
}
void Control::_edit_set_rect(const Rect2 &p_edit_rect) {
+#ifdef TOOLS_ENABLED
+ 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
+ // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED
set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)));
set_size(p_edit_rect.size.snapped(Vector2(1, 1)));
+#endif
}
Rect2 Control::_edit_get_rect() const {
@@ -276,7 +289,7 @@ 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();
+ set_size(data.size_cache);
}
data.updating_last_minimum_size = false;
@@ -1029,55 +1042,37 @@ int Control::get_constant(const StringName &p_name, const StringName &p_type) co
bool Control::has_icon_override(const StringName &p_name) const {
const Ref<Texture> *tex = data.icon_override.getptr(p_name);
- if (tex)
- return true;
- else
- return false;
+ return tex != NULL;
}
bool Control::has_shader_override(const StringName &p_name) const {
const Ref<Shader> *sdr = data.shader_override.getptr(p_name);
- if (sdr)
- return true;
- else
- return false;
+ return sdr != NULL;
}
bool Control::has_stylebox_override(const StringName &p_name) const {
const Ref<StyleBox> *style = data.style_override.getptr(p_name);
- if (style)
- return true;
- else
- return false;
+ return style != NULL;
}
bool Control::has_font_override(const StringName &p_name) const {
const Ref<Font> *font = data.font_override.getptr(p_name);
- if (font)
- return true;
- else
- return false;
+ return font != NULL;
}
bool Control::has_color_override(const StringName &p_name) const {
const Color *color = data.color_override.getptr(p_name);
- if (color)
- return true;
- else
- return false;
+ return color != NULL;
}
bool Control::has_constant_override(const StringName &p_name) const {
const int *constant = data.constant_override.getptr(p_name);
- if (constant)
- return true;
- else
- return false;
+ return constant != NULL;
}
bool Control::has_icon(const StringName &p_name, const StringName &p_type) const {
@@ -1366,7 +1361,7 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo
float previous_margin_pos = data.margin[p_margin] + data.anchor[p_margin] * parent_range;
float previous_opposite_margin_pos = data.margin[(p_margin + 2) % 4] + data.anchor[(p_margin + 2) % 4] * parent_range;
- data.anchor[p_margin] = CLAMP(p_anchor, 0.0, 1.0);
+ data.anchor[p_margin] = p_anchor;
if (((p_margin == MARGIN_LEFT || p_margin == MARGIN_TOP) && data.anchor[p_margin] > data.anchor[(p_margin + 2) % 4]) ||
((p_margin == MARGIN_RIGHT || p_margin == MARGIN_BOTTOM) && data.anchor[p_margin] < data.anchor[(p_margin + 2) % 4])) {
@@ -1395,15 +1390,7 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo
}
void Control::_set_anchor(Margin p_margin, float p_anchor) {
-#ifdef TOOLS_ENABLED
- if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) {
- set_anchor(p_margin, p_anchor, EDITOR_DEF("editors/2d/keep_margins_when_changing_anchors", false));
- } else {
- set_anchor(p_margin, p_anchor, false);
- }
-#else
- set_anchor(p_margin, p_anchor, false);
-#endif
+ set_anchor(p_margin, p_anchor);
}
void Control::set_anchor_and_margin(Margin p_margin, float p_anchor, float p_pos, bool p_push_opposite_anchor) {
@@ -1412,7 +1399,7 @@ void Control::set_anchor_and_margin(Margin p_margin, float p_anchor, float p_pos
set_margin(p_margin, p_pos);
}
-void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
+void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins) {
//Left
switch (p_preset) {
case PRESET_TOP_LEFT:
@@ -1423,21 +1410,21 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
case PRESET_LEFT_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
- set_anchor(MARGIN_LEFT, ANCHOR_BEGIN, p_keep_margin);
+ set_anchor(MARGIN_LEFT, ANCHOR_BEGIN, p_keep_margins);
break;
case PRESET_CENTER_TOP:
case PRESET_CENTER_BOTTOM:
case PRESET_CENTER:
case PRESET_VCENTER_WIDE:
- set_anchor(MARGIN_LEFT, 0.5, p_keep_margin);
+ set_anchor(MARGIN_LEFT, 0.5, p_keep_margins);
break;
case PRESET_TOP_RIGHT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_RIGHT:
case PRESET_RIGHT_WIDE:
- set_anchor(MARGIN_LEFT, ANCHOR_END, p_keep_margin);
+ set_anchor(MARGIN_LEFT, ANCHOR_END, p_keep_margins);
break;
}
@@ -1451,21 +1438,21 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
case PRESET_TOP_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_WIDE:
- set_anchor(MARGIN_TOP, ANCHOR_BEGIN, p_keep_margin);
+ set_anchor(MARGIN_TOP, ANCHOR_BEGIN, p_keep_margins);
break;
case PRESET_CENTER_LEFT:
case PRESET_CENTER_RIGHT:
case PRESET_CENTER:
case PRESET_HCENTER_WIDE:
- set_anchor(MARGIN_TOP, 0.5, p_keep_margin);
+ set_anchor(MARGIN_TOP, 0.5, p_keep_margins);
break;
case PRESET_BOTTOM_LEFT:
case PRESET_BOTTOM_RIGHT:
case PRESET_CENTER_BOTTOM:
case PRESET_BOTTOM_WIDE:
- set_anchor(MARGIN_TOP, ANCHOR_END, p_keep_margin);
+ set_anchor(MARGIN_TOP, ANCHOR_END, p_keep_margins);
break;
}
@@ -1475,14 +1462,14 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
case PRESET_BOTTOM_LEFT:
case PRESET_CENTER_LEFT:
case PRESET_LEFT_WIDE:
- set_anchor(MARGIN_RIGHT, ANCHOR_BEGIN, p_keep_margin);
+ set_anchor(MARGIN_RIGHT, ANCHOR_BEGIN, p_keep_margins);
break;
case PRESET_CENTER_TOP:
case PRESET_CENTER_BOTTOM:
case PRESET_CENTER:
case PRESET_VCENTER_WIDE:
- set_anchor(MARGIN_RIGHT, 0.5, p_keep_margin);
+ set_anchor(MARGIN_RIGHT, 0.5, p_keep_margins);
break;
case PRESET_TOP_RIGHT:
@@ -1493,7 +1480,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
case PRESET_BOTTOM_WIDE:
case PRESET_HCENTER_WIDE:
case PRESET_WIDE:
- set_anchor(MARGIN_RIGHT, ANCHOR_END, p_keep_margin);
+ set_anchor(MARGIN_RIGHT, ANCHOR_END, p_keep_margins);
break;
}
@@ -1503,14 +1490,14 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
case PRESET_TOP_RIGHT:
case PRESET_CENTER_TOP:
case PRESET_TOP_WIDE:
- set_anchor(MARGIN_BOTTOM, ANCHOR_BEGIN, p_keep_margin);
+ set_anchor(MARGIN_BOTTOM, ANCHOR_BEGIN, p_keep_margins);
break;
case PRESET_CENTER_LEFT:
case PRESET_CENTER_RIGHT:
case PRESET_CENTER:
case PRESET_HCENTER_WIDE:
- set_anchor(MARGIN_BOTTOM, 0.5, p_keep_margin);
+ set_anchor(MARGIN_BOTTOM, 0.5, p_keep_margins);
break;
case PRESET_BOTTOM_LEFT:
@@ -1521,7 +1508,7 @@ void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin) {
case PRESET_BOTTOM_WIDE:
case PRESET_VCENTER_WIDE:
case PRESET_WIDE:
- set_anchor(MARGIN_BOTTOM, ANCHOR_END, p_keep_margin);
+ set_anchor(MARGIN_BOTTOM, ANCHOR_END, p_keep_margins);
break;
}
}
@@ -1714,7 +1701,11 @@ Point2 Control::get_global_position() const {
return get_global_transform().get_origin();
}
-void Control::set_global_position(const Point2 &p_point) {
+void Control::_set_global_position(const Point2 &p_point) {
+ set_global_position(p_point);
+}
+
+void Control::set_global_position(const Point2 &p_point, bool p_keep_margins) {
Transform2D inv;
@@ -1723,7 +1714,7 @@ void Control::set_global_position(const Point2 &p_point) {
inv = data.parent_canvas_item->get_global_transform().affine_inverse();
}
- set_position(inv.xform(p_point));
+ set_position(inv.xform(p_point), p_keep_margins);
}
Rect2 Control::_compute_child_rect(const float p_anchors[4], const float p_margins[4]) const {
@@ -1737,6 +1728,18 @@ Rect2 Control::_compute_child_rect(const float p_anchors[4], const float p_margi
return result;
}
+void Control::_compute_anchors(Rect2 p_rect, const float p_margins[4], float (&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);
+
+ r_anchors[0] = (p_rect.position.x - p_margins[0]) / parent_rect_size.x;
+ r_anchors[1] = (p_rect.position.y - p_margins[1]) / parent_rect_size.y;
+ r_anchors[2] = (p_rect.position.x + p_rect.size.x - p_margins[2]) / parent_rect_size.x;
+ r_anchors[3] = (p_rect.position.y + p_rect.size.y - p_margins[3]) / parent_rect_size.y;
+}
+
void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]) {
Size2 parent_rect_size = get_parent_anchorable_rect().size;
@@ -1746,13 +1749,28 @@ void Control::_compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r
r_margins[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y);
}
-void Control::set_position(const Size2 &p_point) {
+void Control::_set_position(const Size2 &p_point) {
+ set_position(p_point);
+}
- _compute_margins(Rect2(p_point, data.size_cache), data.anchor, data.margin);
+void Control::set_position(const Size2 &p_point, bool p_keep_margins) {
+ if (p_keep_margins) {
+ _compute_anchors(Rect2(p_point, data.size_cache), data.margin, data.anchor);
+ _change_notify("anchor_left");
+ _change_notify("anchor_right");
+ _change_notify("anchor_top");
+ _change_notify("anchor_bottom");
+ } else {
+ _compute_margins(Rect2(p_point, data.size_cache), data.anchor, data.margin);
+ }
_size_changed();
}
-void Control::set_size(const Size2 &p_size) {
+void Control::_set_size(const Size2 &p_size) {
+ set_size(p_size);
+}
+
+void Control::set_size(const Size2 &p_size, bool p_keep_margins) {
Size2 new_size = p_size;
Size2 min = get_combined_minimum_size();
@@ -1761,7 +1779,15 @@ void Control::set_size(const Size2 &p_size) {
if (new_size.y < min.y)
new_size.y = min.y;
- _compute_margins(Rect2(data.pos_cache, new_size), data.anchor, data.margin);
+ if (p_keep_margins) {
+ _compute_anchors(Rect2(data.pos_cache, new_size), data.margin, data.anchor);
+ _change_notify("anchor_left");
+ _change_notify("anchor_right");
+ _change_notify("anchor_top");
+ _change_notify("anchor_bottom");
+ } else {
+ _compute_margins(Rect2(data.pos_cache, new_size), data.anchor, data.margin);
+ }
_size_changed();
}
@@ -1799,53 +1825,72 @@ Rect2 Control::get_anchorable_rect() const {
void Control::add_icon_override(const StringName &p_name, const Ref<Texture> &p_icon) {
- ERR_FAIL_COND(p_icon.is_null());
if (data.icon_override.has(p_name)) {
data.icon_override[p_name]->disconnect("changed", this, "_override_changed");
}
- data.icon_override[p_name] = p_icon;
- if (data.icon_override[p_name].is_valid()) {
- data.icon_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+
+ // 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", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ }
}
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_shader_override(const StringName &p_name, const Ref<Shader> &p_shader) {
- ERR_FAIL_COND(p_shader.is_null());
+
if (data.shader_override.has(p_name)) {
data.shader_override[p_name]->disconnect("changed", this, "_override_changed");
}
- data.shader_override[p_name] = p_shader;
- if (data.shader_override[p_name].is_valid()) {
- data.shader_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+
+ // clear if "null" is passed instead of a shader
+ if (p_shader.is_null()) {
+ data.shader_override.erase(p_name);
+ } else {
+ data.shader_override[p_name] = p_shader;
+ if (data.shader_override[p_name].is_valid()) {
+ data.shader_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ }
}
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) {
- ERR_FAIL_COND(p_style.is_null());
if (data.style_override.has(p_name)) {
data.style_override[p_name]->disconnect("changed", this, "_override_changed");
}
- data.style_override[p_name] = p_style;
- if (data.style_override[p_name].is_valid()) {
- data.style_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- }
+ // 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", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ }
+ }
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_font_override(const StringName &p_name, const Ref<Font> &p_font) {
- ERR_FAIL_COND(p_font.is_null());
if (data.font_override.has(p_name)) {
data.font_override[p_name]->disconnect("changed", this, "_override_changed");
}
- data.font_override[p_name] = p_font;
- if (data.font_override[p_name].is_valid()) {
- data.font_override[p_name]->connect("changed", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
- }
+ // 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", this, "_override_changed", Vector<Variant>(), CONNECT_REFERENCE_COUNTED);
+ }
+ }
notification(NOTIFICATION_THEME_CHANGED);
}
void Control::add_color_override(const StringName &p_name, const Color &p_color) {
@@ -1934,10 +1979,7 @@ Control *Control::find_next_valid_focus() const {
break;
}
- if (next_child) {
-
- from = next_child;
- } else {
+ if (!next_child) {
next_child = _next_control(from);
if (!next_child) { //nothing else.. go up and find either window or subwindow
@@ -2207,6 +2249,7 @@ Ref<Theme> Control::get_theme() const {
void Control::set_tooltip(const String &p_tooltip) {
data.tooltip = p_tooltip;
+ update_configuration_warning();
}
String Control::get_tooltip(const Point2 &p_pos) const {
@@ -2498,6 +2541,7 @@ void Control::set_mouse_filter(MouseFilter p_filter) {
ERR_FAIL_INDEX(p_filter, 3);
data.mouse_filter = p_filter;
+ update_configuration_warning();
}
Control::MouseFilter Control::get_mouse_filter() const {
@@ -2661,6 +2705,20 @@ 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();
+
+ if (data.mouse_filter == MOUSE_FILTER_IGNORE && data.tooltip != "") {
+ if (warning != String()) {
+ warning += "\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\".");
+ }
+
+ return warning;
+}
+
void Control::set_clip_contents(bool p_clip) {
data.clip_contents = p_clip;
@@ -2702,20 +2760,23 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("accept_event"), &Control::accept_event);
ClassDB::bind_method(D_METHOD("get_minimum_size"), &Control::get_minimum_size);
ClassDB::bind_method(D_METHOD("get_combined_minimum_size"), &Control::get_combined_minimum_size);
- ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_margin"), &Control::set_anchors_preset, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_margins"), &Control::set_anchors_preset, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_margins_preset", "preset", "resize_mode", "margin"), &Control::set_margins_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_anchors_and_margins_preset", "preset", "resize_mode", "margin"), &Control::set_anchors_and_margins_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("set_anchor", "margin", "anchor", "keep_margin", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("_set_anchor", "margin", "anchor"), &Control::_set_anchor);
+ ClassDB::bind_method(D_METHOD("set_anchor", "margin", "anchor", "keep_margin", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_anchor", "margin"), &Control::get_anchor);
ClassDB::bind_method(D_METHOD("set_margin", "margin", "offset"), &Control::set_margin);
ClassDB::bind_method(D_METHOD("set_anchor_and_margin", "margin", "anchor", "offset", "push_opposite_anchor"), &Control::set_anchor_and_margin, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_begin", "position"), &Control::set_begin);
ClassDB::bind_method(D_METHOD("set_end", "position"), &Control::set_end);
- ClassDB::bind_method(D_METHOD("set_position", "position"), &Control::set_position);
- ClassDB::bind_method(D_METHOD("set_size", "size"), &Control::set_size);
+ ClassDB::bind_method(D_METHOD("set_position", "position", "keep_margins"), &Control::set_position, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("_set_position", "margin"), &Control::_set_position);
+ ClassDB::bind_method(D_METHOD("set_size", "size", "keep_margins"), &Control::set_size, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("_set_size", "size"), &Control::_set_size);
ClassDB::bind_method(D_METHOD("set_custom_minimum_size", "size"), &Control::set_custom_minimum_size);
- ClassDB::bind_method(D_METHOD("set_global_position", "position"), &Control::set_global_position);
+ ClassDB::bind_method(D_METHOD("set_global_position", "position", "keep_margins"), &Control::set_global_position, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("_set_global_position", "position"), &Control::_set_global_position);
ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Control::set_rotation);
ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Control::set_rotation_degrees);
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale);
@@ -2835,10 +2896,10 @@ void Control::_bind_methods() {
BIND_VMETHOD(MethodInfo(Variant::BOOL, "_clips_input"));
ADD_GROUP("Anchor", "anchor_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_BOTTOM);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.01,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_BOTTOM);
ADD_GROUP("Margin", "margin_");
ADD_PROPERTYI(PropertyInfo(Variant::INT, "margin_left", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_LEFT);
@@ -2851,9 +2912,9 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction");
ADD_GROUP("Rect", "rect_");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_position", "get_position");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", 0), "_set_global_position", "get_global_position");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "rect_rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.01"), "set_rotation_degrees", "get_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale");
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 5e33f6ba43..1a59a6d2e4 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -139,8 +139,8 @@ private:
bool operator()(const Control *p_a, const Control *p_b) const {
if (p_a->get_canvas_layer() == p_b->get_canvas_layer())
return p_b->is_greater_than(p_a);
- else
- return p_a->get_canvas_layer() < p_b->get_canvas_layer();
+
+ return p_a->get_canvas_layer() < p_b->get_canvas_layer();
}
};
@@ -217,6 +217,9 @@ private:
Control *_get_focus_neighbour(Margin p_margin, int p_count = 0);
void _set_anchor(Margin p_margin, float 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 _propagate_theme_changed(CanvasItem *p_at, Control *p_owner, bool p_assign = true);
void _theme_changed();
@@ -229,6 +232,7 @@ private:
Rect2 _compute_child_rect(const float p_anchors[4], const float p_margins[4]) const;
void _compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]);
+ void _compute_anchors(Rect2 p_rect, const float p_margins[4], float (&r_anchors)[4]);
void _size_changed();
String _get_tooltip() const;
@@ -325,7 +329,7 @@ public:
/* POSITIONING */
- void set_anchors_preset(LayoutPreset p_preset, bool p_keep_margin = true);
+ void set_anchors_preset(LayoutPreset p_preset, bool p_keep_margins = true);
void set_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
void set_anchors_and_margins_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
@@ -343,12 +347,12 @@ public:
Point2 get_begin() const;
Point2 get_end() const;
- void set_position(const Point2 &p_point);
- void set_global_position(const Point2 &p_point);
+ void set_position(const Point2 &p_point, bool p_keep_margins = false);
+ void set_global_position(const Point2 &p_point, bool p_keep_margins = false);
Point2 get_position() const;
Point2 get_global_position() const;
- void set_size(const Size2 &p_size);
+ void set_size(const Size2 &p_size, bool p_keep_margins = false);
Size2 get_size() const;
Rect2 get_rect() const;
@@ -483,6 +487,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;
+ virtual String get_configuration_warning() const;
Control();
~Control();
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index 89f3d509d0..4da11b671e 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -406,12 +406,20 @@ void AcceptDialog::set_hide_on_ok(bool p_hide) {
hide_on_ok = p_hide;
}
-
bool AcceptDialog::get_hide_on_ok() const {
return hide_on_ok;
}
+void AcceptDialog::set_autowrap(bool p_autowrap) {
+
+ label->set_autowrap(p_autowrap);
+}
+bool AcceptDialog::has_autowrap() {
+
+ return label->has_autowrap();
+}
+
void AcceptDialog::register_text_enter(Node *p_line_edit) {
ERR_FAIL_NULL(p_line_edit);
@@ -530,6 +538,8 @@ void AcceptDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_custom_action"), &AcceptDialog::_custom_action);
ClassDB::bind_method(D_METHOD("set_text", "text"), &AcceptDialog::set_text);
ClassDB::bind_method(D_METHOD("get_text"), &AcceptDialog::get_text);
+ ClassDB::bind_method(D_METHOD("set_autowrap", "autowrap"), &AcceptDialog::set_autowrap);
+ ClassDB::bind_method(D_METHOD("has_autowrap"), &AcceptDialog::has_autowrap);
ADD_SIGNAL(MethodInfo("confirmed"));
ADD_SIGNAL(MethodInfo("custom_action", PropertyInfo(Variant::STRING, "action")));
@@ -537,6 +547,7 @@ void AcceptDialog::_bind_methods() {
ADD_GROUP("Dialog", "dialog");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "dialog_text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_hide_on_ok"), "set_hide_on_ok", "get_hide_on_ok");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_autowrap"), "set_autowrap", "has_autowrap");
}
bool AcceptDialog::swap_ok_cancel = false;
@@ -555,7 +566,6 @@ AcceptDialog::AcceptDialog() {
label->set_anchor(MARGIN_BOTTOM, ANCHOR_END);
label->set_begin(Point2(margin, margin));
label->set_end(Point2(-margin, -button_margin - 10));
- //label->set_autowrap(true);
add_child(label);
hbc = memnew(HBoxContainer);
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index 4b89ac54c5..c1a7f26a85 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -145,6 +145,9 @@ public:
void set_text(String p_text);
String get_text() const;
+ void set_autowrap(bool p_autowrap);
+ bool has_autowrap();
+
AcceptDialog();
~AcceptDialog();
};
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 5671b41de8..04fb991f78 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -48,8 +48,9 @@ void FileDialog::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
- refresh->set_icon(get_icon("reload"));
dir_up->set_icon(get_icon("parent_folder"));
+ refresh->set_icon(get_icon("reload"));
+ show_hidden->set_icon(get_icon("toggle_hidden"));
}
if (p_what == NOTIFICATION_POPUP_HIDE) {
@@ -301,11 +302,8 @@ bool FileDialog::_is_open_should_be_disabled() {
Dictionary d = ti->get_metadata(0);
// Opening a file, but selected a folder? Forbidden.
- if (((mode == MODE_OPEN_FILE || mode == MODE_OPEN_FILES) && d["dir"]) || // Flipped case, also forbidden.
- (mode == MODE_OPEN_DIR && !d["dir"]))
- return true;
-
- return false;
+ return ((mode == MODE_OPEN_FILE || mode == MODE_OPEN_FILES) && d["dir"]) || // Flipped case, also forbidden.
+ (mode == MODE_OPEN_DIR && !d["dir"]);
}
void FileDialog::_go_up() {
@@ -383,6 +381,18 @@ void FileDialog::_tree_item_activated() {
}
}
+void FileDialog::update_file_name() {
+ int idx = filter->get_selected() - 1;
+ if ((idx == -1 && filter->get_item_count() == 2) || (filter->get_item_count() > 2 && idx >= 0 && idx < filter->get_item_count() - 2)) {
+ if (idx == -1) idx += 1;
+ String filter_str = filters[idx];
+ String file_str = file->get_text();
+ String base_name = file_str.get_basename();
+ file_str = base_name + "." + filter_str.strip_edges().to_lower();
+ file->set_text(file_str);
+ }
+}
+
void FileDialog::update_file_list() {
tree->clear();
@@ -393,20 +403,19 @@ void FileDialog::update_file_list() {
List<String> files;
List<String> dirs;
- bool isdir;
- bool ishidden;
- bool show_hidden = show_hidden_files;
+ bool is_dir;
+ bool is_hidden;
String item;
- while ((item = dir_access->get_next(&isdir)) != "") {
+ while ((item = dir_access->get_next(&is_dir)) != "") {
if (item == "." || item == "..")
continue;
- ishidden = dir_access->current_is_hidden();
+ is_hidden = dir_access->current_is_hidden();
- if (show_hidden || !ishidden) {
- if (!isdir)
+ if (show_hidden_files || !is_hidden) {
+ if (!is_dir)
files.push_back(item);
else
dirs.push_back(item);
@@ -431,8 +440,6 @@ void FileDialog::update_file_list() {
dirs.pop_front();
}
- dirs.clear();
-
List<String> patterns;
// build filter
if (filter->get_selected() == filter->get_item_count() - 1) {
@@ -507,12 +514,11 @@ void FileDialog::update_file_list() {
if (tree->get_root() && tree->get_root()->get_children() && tree->get_selected() == NULL)
tree->get_root()->get_children()->select(0);
-
- files.clear();
}
void FileDialog::_filter_selected(int) {
+ update_file_name();
update_file_list();
}
@@ -804,6 +810,7 @@ void FileDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_select_drive"), &FileDialog::_select_drive);
ClassDB::bind_method(D_METHOD("_make_dir"), &FileDialog::_make_dir);
ClassDB::bind_method(D_METHOD("_make_dir_confirm"), &FileDialog::_make_dir_confirm);
+ ClassDB::bind_method(D_METHOD("_update_file_name"), &FileDialog::update_file_name);
ClassDB::bind_method(D_METHOD("_update_file_list"), &FileDialog::update_file_list);
ClassDB::bind_method(D_METHOD("_update_dir"), &FileDialog::update_dir);
ClassDB::bind_method(D_METHOD("_go_up"), &FileDialog::_go_up);
@@ -877,6 +884,13 @@ FileDialog::FileDialog() {
refresh->connect("pressed", this, "_update_file_list");
hbc->add_child(refresh);
+ show_hidden = memnew(ToolButton);
+ show_hidden->set_toggle_mode(true);
+ show_hidden->set_pressed(is_showing_hidden_files());
+ show_hidden->set_tooltip(RTR("Toggle Hidden Files"));
+ show_hidden->connect("toggled", this, "set_show_hidden_files");
+ hbc->add_child(show_hidden);
+
drives = memnew(OptionButton);
hbc->add_child(drives);
drives->connect("item_selected", this, "_select_drive");
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 85edac0b12..191af5fef3 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -90,6 +90,7 @@ private:
ToolButton *dir_up;
ToolButton *refresh;
+ ToolButton *show_hidden;
Vector<String> filters;
@@ -101,6 +102,7 @@ private:
bool invalidated;
void update_dir();
+ void update_file_name();
void update_file_list();
void update_filters();
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index cfbc9d6d18..75f5f79873 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -29,10 +29,17 @@
/*************************************************************************/
#include "gradient_edit.h"
+
#include "core/os/keyboard.h"
-#include "editor/editor_scale.h"
+#ifdef TOOLS_ENABLED
+#include "editor/editor_scale.h"
#define SPACING (3 * EDSCALE)
+#define POINT_WIDTH (8 * EDSCALE)
+#else
+#define SPACING 3
+#define POINT_WIDTH 8
+#endif
GradientEdit::GradientEdit() {
grabbed = -1;
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index 662278a17b..6f31107729 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -36,8 +36,6 @@
#include "scene/resources/default_theme/theme_data.h"
#include "scene/resources/gradient.h"
-#define POINT_WIDTH (8 * EDSCALE)
-
class GradientEdit : public Control {
GDCLASS(GradientEdit, Control);
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index dabff08fea..f238aeb392 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -479,7 +479,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_color = gn->get_connection_input_color(j);
connecting_target = false;
connecting_to = pos;
- just_disconnected = true;
+ just_disconnected = false;
return;
}
@@ -550,11 +550,18 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
emit_signal("connection_request", from, from_slot, to, to_slot);
} else if (!just_disconnected) {
+
String from = connecting_from;
int from_slot = connecting_index;
Vector2 ofs = Vector2(mb->get_position().x, mb->get_position().y);
- emit_signal("connection_to_empty", from, from_slot, ofs);
+
+ if (!connecting_out) {
+ emit_signal("connection_from_empty", from, from_slot, ofs);
+ } else {
+ emit_signal("connection_to_empty", from, from_slot, ofs);
+ }
}
+
connecting = false;
top_layer->update();
update();
@@ -1292,6 +1299,7 @@ void GraphEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("duplicate_nodes_request"));
ADD_SIGNAL(MethodInfo("node_selected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::VECTOR2, "release_position")));
+ ADD_SIGNAL(MethodInfo("connection_from_empty", PropertyInfo(Variant::STRING, "to"), PropertyInfo(Variant::INT, "to_slot"), PropertyInfo(Variant::VECTOR2, "release_position")));
ADD_SIGNAL(MethodInfo("delete_nodes_request"));
ADD_SIGNAL(MethodInfo("_begin_node_move"));
ADD_SIGNAL(MethodInfo("_end_node_move"));
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index e8692d56d2..6463ee5ad5 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -159,9 +159,7 @@ void GraphNode::_resort() {
fit_child_in_rect(c, r);
cache_y.push_back(vofs + size.y * 0.5);
- if (vofs > 0)
- vofs += sep;
- vofs += size.y;
+ vofs += size.y + sep;
}
update();
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index f5e979e9e6..91b76839d7 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -369,7 +369,6 @@ void ItemList::clear() {
update();
shape_changed = true;
defer_select_single = -1;
- scroll_bar->set_value(0);
}
void ItemList::set_fixed_column_width(int p_size) {
@@ -471,6 +470,8 @@ Size2 ItemList::Item::get_icon_size() const {
void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
+ double prev_scroll = scroll_bar->get_value();
+
Ref<InputEventMouseMotion> mm = p_event;
if (defer_select_single >= 0 && mm.is_valid()) {
defer_select_single = -1;
@@ -748,9 +749,21 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
search_string = "";
}
- search_string += String::chr(k->get_unicode());
- for (int i = 0; i < items.size(); i++) {
- if (items[i].text.begins_with(search_string)) {
+ if (String::chr(k->get_unicode()) != search_string)
+ search_string += String::chr(k->get_unicode());
+
+ for (int i = current + 1; i <= items.size(); i++) {
+ if (i == items.size()) {
+ if (current == 0)
+ break;
+ else
+ i = 0;
+ }
+
+ if (i == current)
+ break;
+
+ if (items[i].text.findn(search_string) == 0) {
set_current(i);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
@@ -768,6 +781,9 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
scroll_bar->set_value(scroll_bar->get_value() + scroll_bar->get_page() * pan_gesture->get_delta().y / 8);
}
+
+ if (scroll_bar->get_value() != prev_scroll)
+ accept_event(); //accept event if scroll changed
}
void ItemList::ensure_current_is_visible() {
@@ -1244,7 +1260,7 @@ int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const {
Rect2 rc = items[i].rect_cache;
if (i % current_columns == current_columns - 1) {
- rc.size.width = get_size().width; //not right but works
+ rc.size.width = get_size().width - rc.position.x; //make sure you can still select the last item when clicking past the column
}
if (rc.has_point(pos)) {
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index e3e9368a12..510f1b18ad 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -247,11 +247,11 @@ void Label::_notification(int p_what) {
n = String::char_uppercase(n);
}
- float move = font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + shadow_ofs, c, n, font_color_shadow, false);
+ float move = drawer.draw_char(ci, Point2(x_ofs_shadow, y_ofs) + shadow_ofs, c, n, font_color_shadow);
if (use_outline) {
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, shadow_ofs.y), c, n, font_color_shadow, false);
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow, false);
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow, false);
+ drawer.draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, shadow_ofs.y), c, n, font_color_shadow);
+ drawer.draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow);
+ drawer.draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow);
}
x_ofs_shadow += move;
chars_total_shadow++;
@@ -470,7 +470,6 @@ void Label::regenerate_word_cache() {
wc->word_len = i - word_pos;
wc->space_count = space_count;
current_word_size = char_width;
- space_count = 0;
word_pos = i;
}
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index d889c8d8b8..7a015f77db 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -44,7 +44,7 @@
static bool _is_text_char(CharType c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+ return !is_symbol(c);
}
void LineEdit::_gui_input(Ref<InputEvent> p_event) {
@@ -68,6 +68,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
_reset_caret_blink_timer();
if (b->is_pressed()) {
+ accept_event(); //don't pass event further when clicked on text field
if (!text.empty() && is_editable() && _is_over_clear_button(b->get_position())) {
clear_button_status.press_attempt = true;
clear_button_status.pressing_inside = true;
@@ -159,6 +160,38 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (!k->is_pressed())
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_scancode()) {
+ 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_scancode(remap_key);
+ k->set_control(false);
+ }
+ }
+#endif
+
unsigned int code = k->get_scancode();
if (k->get_command()) {
@@ -506,7 +539,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (handled) {
accept_event();
- } else if (!k->get_alt() && !k->get_command()) {
+ } else if (!k->get_command()) {
if (k->get_unicode() >= 32 && k->get_scancode() != KEY_DELETE) {
if (editable) {
@@ -590,10 +623,7 @@ bool LineEdit::_is_over_clear_button(const Point2 &p_pos) const {
}
Ref<Texture> icon = Control::get_icon("clear");
int x_ofs = get_stylebox("normal")->get_offset().x;
- if (p_pos.x > get_size().width - icon->get_width() - x_ofs) {
- return true;
- }
- return false;
+ return p_pos.x > get_size().width - icon->get_width() - x_ofs;
}
void LineEdit::_notification(int p_what) {
@@ -613,12 +643,15 @@ void LineEdit::_notification(int p_what) {
#endif
case NOTIFICATION_RESIZED: {
- if (expand_to_text_length) {
- window_pos = 0; //force scroll back since it's expanding to text length
- }
+ window_pos = 0;
set_cursor_position(get_cursor_position());
} break;
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ placeholder_translated = tr(placeholder);
+ update_placeholder_width();
+ update();
+ } break;
case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
window_has_focus = true;
draw_caret = true;
@@ -644,10 +677,8 @@ void LineEdit::_notification(int p_what) {
RID ci = get_canvas_item();
Ref<StyleBox> style = get_stylebox("normal");
- float disabled_alpha = 1.0; // used to set the disabled input text color
if (!is_editable()) {
style = get_stylebox("read_only");
- disabled_alpha = .5;
draw_caret = false;
}
@@ -693,20 +724,19 @@ void LineEdit::_notification(int p_what) {
int font_ascent = font->get_ascent();
Color selection_color = get_color("selection_color");
- Color font_color = get_color("font_color");
+ Color font_color = is_editable() ? get_color("font_color") : get_color("font_color_uneditable");
Color font_color_selected = get_color("font_color_selected");
Color cursor_color = get_color("cursor_color");
- const String &t = using_placeholder ? placeholder : text;
+ const String &t = using_placeholder ? placeholder_translated : text;
// draw placeholder color
if (using_placeholder)
font_color.a *= placeholder_alpha;
- font_color.a *= disabled_alpha;
bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
if (right_icon.is_valid() || display_clear_icon) {
Ref<Texture> r_icon = display_clear_icon ? Control::get_icon("clear") : right_icon;
- Color color_icon(1, 1, 1, disabled_alpha * .9);
+ Color color_icon(1, 1, 1, !is_editable() ? .5 * .9 : .9);
if (display_clear_icon) {
if (clear_button_status.press_attempt && clear_button_status.pressing_inside) {
color_icon = get_color("clear_button_color_pressed");
@@ -892,7 +922,8 @@ void LineEdit::cut_text() {
void LineEdit::paste_text() {
- String paste_buffer = OS::get_singleton()->get_clipboard();
+ // Strip escape characters like \n and \t as they can't be displayed on LineEdit.
+ String paste_buffer = OS::get_singleton()->get_clipboard().strip_escapes();
if (paste_buffer != "") {
@@ -1116,16 +1147,9 @@ String LineEdit::get_text() const {
void LineEdit::set_placeholder(String p_text) {
- placeholder = tr(p_text);
- if ((max_length <= 0) || (placeholder.length() <= max_length)) {
- Ref<Font> font = get_font("font");
- cached_placeholder_width = 0;
- if (font != NULL) {
- for (int i = 0; i < placeholder.length(); i++) {
- cached_placeholder_width += font->get_char_size(placeholder[i]).width;
- }
- }
- }
+ placeholder = p_text;
+ placeholder_translated = tr(placeholder);
+ update_placeholder_width();
update();
}
@@ -1514,6 +1538,18 @@ void LineEdit::_emit_text_change() {
text_changed_dirty = false;
}
+void LineEdit::update_placeholder_width() {
+ if ((max_length <= 0) || (placeholder_translated.length() <= max_length)) {
+ Ref<Font> font = get_font("font");
+ cached_placeholder_width = 0;
+ if (font != NULL) {
+ for (int i = 0; i < placeholder_translated.length(); i++) {
+ cached_placeholder_width += font->get_char_size(placeholder_translated[i]).width;
+ }
+ }
+ }
+}
+
void LineEdit::_clear_redo() {
_create_undo_state();
if (undo_stack_pos == NULL) {
@@ -1658,6 +1694,7 @@ LineEdit::LineEdit() {
context_menu_enabled = true;
menu = memnew(PopupMenu);
add_child(menu);
+ editable = false; // initialise to opposite first, so we get past the early-out in set_editable
set_editable(true);
menu->connect("id_pressed", this, "menu_option");
expand_to_text_length = false;
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 3b29780dc0..3002f6f637 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -72,6 +72,7 @@ private:
String undo_text;
String text;
String placeholder;
+ String placeholder_translated;
String secret_character;
float placeholder_alpha;
String ime_text;
@@ -126,6 +127,8 @@ private:
void _emit_text_change();
bool expand_to_text_length;
+ void update_placeholder_width();
+
bool caret_blink_enabled;
bool draw_caret;
bool window_has_focus;
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 7238543a14..872402e9e1 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -86,24 +86,7 @@ void OptionButton::_focused(int p_which) {
void OptionButton::_selected(int p_which) {
- int selid = -1;
- for (int i = 0; i < popup->get_item_count(); i++) {
-
- bool is_clicked = popup->get_item_id(i) == p_which;
- if (is_clicked) {
- selid = i;
- break;
- }
- }
-
- if (selid == -1 && p_which >= 0 && p_which < popup->get_item_count()) {
- _select(p_which, true);
- } else {
-
- ERR_FAIL_COND(selid == -1);
-
- _select(selid, true);
- }
+ _select(p_which, true);
}
void OptionButton::pressed() {
@@ -115,15 +98,15 @@ void OptionButton::pressed() {
popup->popup();
}
-void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID) {
+void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id) {
- popup->add_icon_radio_check_item(p_icon, p_label, p_ID);
+ popup->add_icon_radio_check_item(p_icon, p_label, p_id);
if (popup->get_item_count() == 1)
select(0);
}
-void OptionButton::add_item(const String &p_label, int p_ID) {
+void OptionButton::add_item(const String &p_label, int p_id) {
- popup->add_radio_check_item(p_label, p_ID);
+ popup->add_radio_check_item(p_label, p_id);
if (popup->get_item_count() == 1)
select(0);
}
@@ -136,9 +119,9 @@ void OptionButton::set_item_icon(int p_idx, const Ref<Texture> &p_icon) {
popup->set_item_icon(p_idx, p_icon);
}
-void OptionButton::set_item_id(int p_idx, int p_ID) {
+void OptionButton::set_item_id(int p_idx, int p_id) {
- popup->set_item_id(p_idx, p_ID);
+ popup->set_item_id(p_idx, p_id);
}
void OptionButton::set_item_metadata(int p_idx, const Variant &p_metadata) {
@@ -338,8 +321,8 @@ void OptionButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
// "selected" property must come after "items", otherwise GH-10213 occurs
ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected");
- ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "ID")));
- ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "ID")));
+ ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "id")));
}
OptionButton::OptionButton() {
@@ -354,7 +337,8 @@ OptionButton::OptionButton() {
add_child(popup);
popup->set_pass_on_modal_close_click(false);
popup->set_notify_transform(true);
- popup->connect("id_pressed", this, "_selected");
+ popup->set_allow_search(true);
+ popup->connect("index_pressed", this, "_selected");
popup->connect("id_focused", this, "_focused");
popup->connect("popup_hide", this, "set_pressed", varray(false));
}
diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h
index 63b451377a..51d5fd6947 100644
--- a/scene/gui/option_button.h
+++ b/scene/gui/option_button.h
@@ -59,12 +59,12 @@ protected:
static void _bind_methods();
public:
- void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1);
- void add_item(const String &p_label, int p_ID = -1);
+ void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1);
+ void add_item(const String &p_label, int p_id = -1);
void set_item_text(int p_idx, const String &p_text);
void set_item_icon(int p_idx, const Ref<Texture> &p_icon);
- void set_item_id(int p_idx, int p_ID);
+ void set_item_id(int p_idx, int p_id);
void set_item_metadata(int p_idx, const Variant &p_metadata);
void set_item_disabled(int p_idx, bool p_disabled);
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index b7601bdd3e..492e379440 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -115,6 +115,18 @@ void Popup::set_as_minsize() {
set_size(total_minsize);
}
+void Popup::popup_centered_clamped(const Size2 &p_size, float p_fallback_ratio) {
+
+ Size2 popup_size = p_size;
+ Size2 window_size = get_viewport_rect().size;
+
+ // clamp popup size in each dimension if window size is too small (using fallback ratio)
+ popup_size.x = MIN(window_size.x * p_fallback_ratio, popup_size.x);
+ popup_size.y = MIN(window_size.y * p_fallback_ratio, popup_size.y);
+
+ popup_centered(popup_size);
+}
+
void Popup::popup_centered_minsize(const Size2 &p_minsize) {
set_custom_minimum_size(p_minsize);
@@ -129,7 +141,7 @@ void Popup::popup_centered(const Size2 &p_size) {
rect.size = p_size == Size2() ? get_size() : p_size;
rect.position = ((window_size - rect.size) / 2.0).floor();
- popup(rect);
+ _popup(rect, true);
}
void Popup::popup_centered_ratio(float p_screen_ratio) {
@@ -139,18 +151,29 @@ void Popup::popup_centered_ratio(float p_screen_ratio) {
rect.size = (window_size * p_screen_ratio).floor();
rect.position = ((window_size - rect.size) / 2.0).floor();
- popup(rect);
+ _popup(rect, true);
}
void Popup::popup(const Rect2 &p_bounds) {
+ _popup(p_bounds);
+}
+
+void Popup::_popup(const Rect2 &p_bounds, const bool p_centered) {
+
emit_signal("about_to_show");
show_modal(exclusive);
// Fit the popup into the optionally provided bounds.
if (!p_bounds.has_no_area()) {
- set_position(p_bounds.position);
set_size(p_bounds.size);
+
+ // check if p_bounds.size was using an outdated cached values
+ if (p_centered && p_bounds.size != get_size()) {
+ set_position(p_bounds.position - ((get_size() - p_bounds.size) / 2.0).floor());
+ } else {
+ set_position(p_bounds.position);
+ }
}
_fix_size();
@@ -179,6 +202,7 @@ void Popup::_bind_methods() {
ClassDB::bind_method(D_METHOD("popup_centered", "size"), &Popup::popup_centered, DEFVAL(Size2()));
ClassDB::bind_method(D_METHOD("popup_centered_ratio", "ratio"), &Popup::popup_centered_ratio, DEFVAL(0.75));
ClassDB::bind_method(D_METHOD("popup_centered_minsize", "minsize"), &Popup::popup_centered_minsize, DEFVAL(Size2()));
+ ClassDB::bind_method(D_METHOD("popup_centered_clamped", "size", "fallback_ratio"), &Popup::popup_centered_clamped, DEFVAL(Size2()), DEFVAL(0.75));
ClassDB::bind_method(D_METHOD("popup", "bounds"), &Popup::popup, DEFVAL(Rect2()));
ClassDB::bind_method(D_METHOD("set_exclusive", "enable"), &Popup::set_exclusive);
ClassDB::bind_method(D_METHOD("is_exclusive"), &Popup::is_exclusive);
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index 7ccefe1d75..d6d96dfe64 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -43,6 +43,9 @@ class Popup : public Control {
bool exclusive;
bool popped_up;
+private:
+ void _popup(const Rect2 &p_bounds = Rect2(), const bool p_centered = false);
+
protected:
virtual void _post_popup() {}
@@ -64,6 +67,7 @@ public:
void popup_centered(const Size2 &p_size = Size2());
void popup_centered_minsize(const Size2 &p_minsize = Size2());
void set_as_minsize();
+ void popup_centered_clamped(const Size2 &p_size = Size2(), float p_fallback_ratio = 0.75);
virtual void popup(const Rect2 &p_bounds = Rect2());
virtual String get_configuration_warning() const;
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 8825891807..2cac345dae 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -31,6 +31,7 @@
#include "popup_menu.h"
#include "core/os/input.h"
#include "core/os/keyboard.h"
+#include "core/os/os.h"
#include "core/print_string.h"
#include "core/translation.h"
@@ -93,14 +94,14 @@ Size2 PopupMenu::get_minimum_size() const {
if (items[i].submenu != "")
size.width += get_icon("submenu")->get_width();
- if (has_check)
- size.width += check_w;
- max_w = MAX(max_w, size.width + icon_w);
+ max_w = MAX(max_w, size.width);
minsize.height += size.height;
}
- minsize.width += max_w + accel_max_w;
+ minsize.width += max_w + icon_w + accel_max_w;
+ if (has_check)
+ minsize.width += check_w;
return minsize;
}
@@ -355,7 +356,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
}
int over = _get_mouse_over(m->get_position());
- int id = (over < 0 || items[over].separator || items[over].disabled) ? -1 : (items[over].ID >= 0 ? items[over].ID : over);
+ int id = (over < 0 || items[over].separator || items[over].disabled) ? -1 : (items[over].id >= 0 ? items[over].id : over);
if (id < 0) {
mouse_over = -1;
@@ -380,6 +381,43 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
_scroll(-pan_gesture->get_delta().y, pan_gesture->get_position());
}
}
+
+ Ref<InputEventKey> k = p_event;
+
+ if (allow_search && k.is_valid() && k->get_unicode()) {
+
+ uint64_t now = OS::get_singleton()->get_ticks_msec();
+ uint64_t diff = now - search_time_msec;
+ uint64_t max_interval = uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000));
+ search_time_msec = now;
+
+ if (diff > max_interval) {
+ search_string = "";
+ }
+
+ if (String::chr(k->get_unicode()) != search_string)
+ search_string += String::chr(k->get_unicode());
+
+ for (int i = mouse_over + 1; i <= items.size(); i++) {
+ if (i == items.size()) {
+ if (mouse_over <= 0)
+ break;
+ else
+ i = 0;
+ }
+
+ if (i == mouse_over)
+ break;
+
+ if (items[i].text.findn(search_string) == 0) {
+ mouse_over = i;
+ emit_signal("id_focused", i);
+ update();
+ accept_event();
+ break;
+ }
+ }
+ }
}
bool PopupMenu::has_point(const Point2 &p_point) const {
@@ -449,7 +487,7 @@ void PopupMenu::_notification(int p_what) {
for (int i = 0; i < items.size(); i++) {
if (!items[i].icon.is_null())
- icon_ofs = MAX(items[i].icon->get_size().height, icon_ofs);
+ icon_ofs = MAX(items[i].icon->get_size().width, icon_ofs);
if (items[i].checkable_type)
has_check = true;
@@ -591,93 +629,93 @@ void PopupMenu::_notification(int p_what) {
}
}
-void PopupMenu::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) {
Item item;
item.icon = p_icon;
item.text = p_label;
item.xl_text = tr(p_label);
item.accel = p_accel;
- item.ID = p_ID;
+ item.id = p_id;
items.push_back(item);
update();
minimum_size_changed();
}
-void PopupMenu::add_item(const String &p_label, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_item(const String &p_label, int p_id, uint32_t p_accel) {
Item item;
item.text = p_label;
item.xl_text = tr(p_label);
item.accel = p_accel;
- item.ID = p_ID == -1 ? items.size() : p_ID;
+ item.id = p_id == -1 ? items.size() : p_id;
items.push_back(item);
update();
minimum_size_changed();
}
-void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_ID) {
+void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_id) {
Item item;
item.text = p_label;
item.xl_text = tr(p_label);
- item.ID = p_ID;
+ item.id = p_id;
item.submenu = p_submenu;
items.push_back(item);
update();
minimum_size_changed();
}
-void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) {
Item item;
item.icon = p_icon;
item.text = p_label;
item.xl_text = tr(p_label);
item.accel = p_accel;
- item.ID = p_ID;
+ item.id = p_id;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
minimum_size_changed();
}
-void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel) {
Item item;
item.text = p_label;
item.xl_text = tr(p_label);
item.accel = p_accel;
- item.ID = p_ID == -1 ? items.size() : p_ID;
+ item.id = p_id == -1 ? items.size() : p_id;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
minimum_size_changed();
}
-void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_radio_check_item(const String &p_label, int p_id, uint32_t p_accel) {
- add_check_item(p_label, p_ID, p_accel);
+ add_check_item(p_label, p_id, p_accel);
items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
update();
minimum_size_changed();
}
-void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) {
- add_icon_check_item(p_icon, p_label, p_ID, p_accel);
+ add_icon_check_item(p_icon, p_label, p_id, p_accel);
items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
update();
minimum_size_changed();
}
-void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
+void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
_ref_shortcut(p_shortcut);
Item item;
- item.ID = p_ID;
+ item.id = p_id;
item.icon = p_icon;
item.shortcut = p_shortcut;
item.shortcut_is_global = p_global;
@@ -686,14 +724,14 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut
minimum_size_changed();
}
-void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
+void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
_ref_shortcut(p_shortcut);
Item item;
- item.ID = p_ID;
+ item.id = p_id;
item.shortcut = p_shortcut;
item.shortcut_is_global = p_global;
items.push_back(item);
@@ -701,14 +739,14 @@ void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_g
minimum_size_changed();
}
-void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
+void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
_ref_shortcut(p_shortcut);
Item item;
- item.ID = p_ID;
+ item.id = p_id;
item.shortcut = p_shortcut;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
item.icon = p_icon;
@@ -718,14 +756,14 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<Sh
minimum_size_changed();
}
-void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
+void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
_ref_shortcut(p_shortcut);
Item item;
- item.ID = p_ID;
+ item.id = p_id;
item.shortcut = p_shortcut;
item.shortcut_is_global = p_global;
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
@@ -734,21 +772,21 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bo
minimum_size_changed();
}
-void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
+void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
- add_check_shortcut(p_shortcut, p_ID, p_global);
+ add_check_shortcut(p_shortcut, p_id, p_global);
items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
update();
minimum_size_changed();
}
-void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID, uint32_t p_accel) {
+void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id, uint32_t p_accel) {
Item item;
item.text = p_label;
item.xl_text = tr(p_label);
item.accel = p_accel;
- item.ID = p_ID;
+ item.id = p_id;
item.max_states = p_max_states;
item.state = p_default_state;
items.push_back(item);
@@ -782,10 +820,10 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
update();
minimum_size_changed();
}
-void PopupMenu::set_item_id(int p_idx, int p_ID) {
+void PopupMenu::set_item_id(int p_idx, int p_id) {
ERR_FAIL_INDEX(p_idx, items.size());
- items.write[p_idx].ID = p_ID;
+ items.write[p_idx].id = p_id;
update();
minimum_size_changed();
@@ -881,14 +919,14 @@ bool PopupMenu::is_item_checked(int p_idx) const {
int PopupMenu::get_item_id(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, items.size(), 0);
- return items[p_idx].ID;
+ return items[p_idx].id;
}
-int PopupMenu::get_item_index(int p_ID) const {
+int PopupMenu::get_item_index(int p_id) const {
for (int i = 0; i < items.size(); i++) {
- if (items[i].ID == p_ID)
+ if (items[i].id == p_id)
return i;
}
@@ -1077,7 +1115,7 @@ void PopupMenu::activate_item(int p_item) {
ERR_FAIL_INDEX(p_item, items.size());
ERR_FAIL_COND(items[p_item].separator);
- int id = items[p_item].ID >= 0 ? items[p_item].ID : p_item;
+ int id = items[p_item].id >= 0 ? items[p_item].id : p_item;
//hide all parent PopupMenus
Node *next = get_parent();
@@ -1139,7 +1177,7 @@ void PopupMenu::add_separator(const String &p_text) {
Item sep;
sep.separator = true;
- sep.ID = -1;
+ sep.id = -1;
if (p_text != String()) {
sep.text = p_text;
sep.xl_text = tr(p_text);
@@ -1289,6 +1327,16 @@ float PopupMenu::get_submenu_popup_delay() const {
return submenu_timer->get_wait_time();
}
+void PopupMenu::set_allow_search(bool p_allow) {
+
+ allow_search = p_allow;
+}
+
+bool PopupMenu::get_allow_search() const {
+
+ return allow_search;
+}
+
void PopupMenu::set_hide_on_window_lose_focus(bool p_enabled) {
hide_on_window_lose_focus = p_enabled;
@@ -1407,6 +1455,9 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_on_window_lose_focus", "enable"), &PopupMenu::set_hide_on_window_lose_focus);
ClassDB::bind_method(D_METHOD("is_hide_on_window_lose_focus"), &PopupMenu::is_hide_on_window_lose_focus);
+ ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &PopupMenu::set_allow_search);
+ ClassDB::bind_method(D_METHOD("get_allow_search"), &PopupMenu::get_allow_search);
+
ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
@@ -1414,9 +1465,10 @@ void PopupMenu::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search");
- ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "ID")));
- ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "ID")));
+ ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index")));
}
@@ -1435,6 +1487,10 @@ PopupMenu::PopupMenu() {
initial_button_mask = 0;
during_grabbed_click = false;
+ allow_search = false;
+ search_time_msec = 0;
+ search_string = "";
+
set_focus_mode(FOCUS_ALL);
set_as_toplevel(true);
set_hide_on_item_selection(true);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 767ff568fe..babdd21281 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -55,7 +55,7 @@ class PopupMenu : public Popup {
int state;
bool separator;
bool disabled;
- int ID;
+ int id;
Variant metadata;
String submenu;
String tooltip;
@@ -112,6 +112,10 @@ 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;
+
protected:
virtual bool has_point(const Point2 &p_point) const;
@@ -120,26 +124,26 @@ protected:
static void _bind_methods();
public:
- void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
- void add_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
- void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
- void add_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
- void add_radio_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
- void add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
- void add_submenu_item(const String &p_label, const String &p_submenu, int p_ID = -1);
-
- void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
- void add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
- void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
- void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
- void add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
-
- void add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID = -1, uint32_t p_accel = 0);
+ void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0);
+ void add_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0);
+ void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0);
+ void add_check_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0);
+ void add_radio_check_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0);
+ void add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0);
+ void add_submenu_item(const String &p_label, const String &p_submenu, int p_id = -1);
+
+ void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
+
+ void add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id = -1, uint32_t p_accel = 0);
void set_item_text(int p_idx, const String &p_text);
void set_item_icon(int p_idx, const Ref<Texture> &p_icon);
void set_item_checked(int p_idx, bool p_checked);
- void set_item_id(int p_idx, int p_ID);
+ void set_item_id(int p_idx, int p_id);
void set_item_accelerator(int p_idx, uint32_t p_accel);
void set_item_metadata(int p_idx, const Variant &p_meta);
void set_item_disabled(int p_idx, bool p_disabled);
@@ -161,7 +165,7 @@ public:
Ref<Texture> get_item_icon(int p_idx) const;
bool is_item_checked(int p_idx) const;
int get_item_id(int p_idx) const;
- int get_item_index(int p_ID) const;
+ int get_item_index(int p_id) const;
uint32_t get_item_accelerator(int p_idx) const;
Variant get_item_metadata(int p_idx) const;
bool is_item_disabled(int p_idx) const;
@@ -206,6 +210,9 @@ public:
void set_submenu_popup_delay(float p_time);
float get_submenu_popup_delay() const;
+ void set_allow_search(bool p_allow);
+ bool get_allow_search() const;
+
virtual void popup(const Rect2 &p_bounds = Rect2());
void set_hide_on_window_lose_focus(bool p_enabled);
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index c24e62c8cb..d00acaf08a 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -63,7 +63,7 @@ void Range::Shared::emit_value_changed() {
void Range::_changed_notify(const char *p_what) {
- emit_signal("changed", shared->val);
+ emit_signal("changed");
update();
_change_notify(p_what);
}
@@ -79,6 +79,7 @@ void Range::Shared::emit_changed(const char *p_what) {
}
void Range::set_value(double p_val) {
+
if (shared->step > 0)
p_val = Math::round(p_val / shared->step) * shared->step;
@@ -303,22 +304,27 @@ bool Range::is_ratio_exp() const {
}
void Range::set_allow_greater(bool p_allow) {
+
shared->allow_greater = p_allow;
}
bool Range::is_greater_allowed() const {
+
return shared->allow_greater;
}
void Range::set_allow_lesser(bool p_allow) {
+
shared->allow_lesser = p_allow;
}
bool Range::is_lesser_allowed() const {
+
return shared->allow_lesser;
}
Range::Range() {
+
shared = memnew(Shared);
shared->min = 0;
shared->max = 100;
diff --git a/scene/gui/reference_rect.cpp b/scene/gui/reference_rect.cpp
index 553e133946..052c8ccd05 100644
--- a/scene/gui/reference_rect.cpp
+++ b/scene/gui/reference_rect.cpp
@@ -38,26 +38,41 @@ void ReferenceRect::_notification(int p_what) {
if (!is_inside_tree())
return;
- if (Engine::get_singleton()->is_editor_hint())
+ if (Engine::get_singleton()->is_editor_hint() || !editor_only)
draw_rect(Rect2(Point2(), get_size()), border_color, false);
}
}
-void ReferenceRect::set_border_color(const Color &color) {
- border_color = color;
+void ReferenceRect::set_border_color(const Color &p_color) {
+ border_color = p_color;
+ update();
}
Color ReferenceRect::get_border_color() const {
return border_color;
}
+void ReferenceRect::set_editor_only(const bool &p_enabled) {
+ editor_only = p_enabled;
+ update();
+}
+
+bool ReferenceRect::get_editor_only() const {
+ return editor_only;
+}
+
void ReferenceRect::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_border_color"), &ReferenceRect::get_border_color);
ClassDB::bind_method(D_METHOD("set_border_color", "color"), &ReferenceRect::set_border_color);
+ ClassDB::bind_method(D_METHOD("get_editor_only"), &ReferenceRect::get_editor_only);
+ ClassDB::bind_method(D_METHOD("set_editor_only", "enabled"), &ReferenceRect::set_editor_only);
+
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "border_color"), "set_border_color", "get_border_color");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "get_editor_only");
}
ReferenceRect::ReferenceRect() {
border_color = Color(1, 0, 0);
+ editor_only = true;
}
diff --git a/scene/gui/reference_rect.h b/scene/gui/reference_rect.h
index de3ccaeeca..7a88333cf2 100644
--- a/scene/gui/reference_rect.h
+++ b/scene/gui/reference_rect.h
@@ -37,6 +37,7 @@ class ReferenceRect : public Control {
GDCLASS(ReferenceRect, Control);
Color border_color;
+ bool editor_only;
protected:
void _notification(int p_what);
@@ -45,8 +46,11 @@ protected:
public:
ReferenceRect();
- void set_border_color(const Color &color);
+ void set_border_color(const Color &p_color);
Color get_border_color() const;
+
+ void set_editor_only(const bool &p_enabled);
+ bool get_editor_only() const;
};
#endif // REFERENCE_RECT_H
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 101eb2ac88..5b91299de6 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -307,6 +307,25 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
switch (it->type) {
+ case ITEM_ALIGN: {
+
+ ItemAlign *align_it = static_cast<ItemAlign *>(it);
+
+ align = align_it->align;
+
+ } break;
+ case ITEM_INDENT: {
+
+ if (it != l.from) {
+ ItemIndent *indent_it = static_cast<ItemIndent *>(it);
+
+ int indent = indent_it->level * tab_size * cfont->get_char_size(' ').width;
+ margin += indent;
+ begin += indent;
+ wofs += indent;
+ }
+
+ } break;
case ITEM_TEXT: {
ItemText *text = static_cast<ItemText *>(it);
@@ -440,14 +459,13 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
if (p_font_color_shadow.a > 0) {
float x_ofs_shadow = align_ofs + pofs;
float y_ofs_shadow = y + lh - line_descent;
- float move = font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, c[i], c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, c[i], c[i + 1], p_font_color_shadow);
if (p_shadow_as_outline) {
font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
}
- x_ofs_shadow += move;
}
if (selected) {
@@ -592,7 +610,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
//assign actual widths
for (int i = 0; i < table->columns.size(); i++) {
table->columns.write[i].width = table->columns[i].min_width;
- if (table->columns[i].expand)
+ if (table->columns[i].expand && total_ratio > 0)
table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
table->total_width += table->columns[i].width + hseparation;
}
@@ -928,84 +946,80 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
return;
if (b->get_button_index() == BUTTON_LEFT) {
+ if (b->is_pressed() && !b->is_doubleclick()) {
+ scroll_updated = false;
+ int line = 0;
+ Item *item = NULL;
- if (true) {
+ bool outside;
+ _find_click(main, b->get_position(), &item, &line, &outside);
- if (b->is_pressed() && !b->is_doubleclick()) {
- scroll_updated = false;
- int line = 0;
- Item *item = NULL;
+ if (item) {
- bool outside;
- _find_click(main, b->get_position(), &item, &line, &outside);
-
- if (item) {
+ if (selection.enabled) {
- if (selection.enabled) {
+ selection.click = item;
+ selection.click_char = line;
- selection.click = item;
- selection.click_char = line;
+ // Erase previous selection.
+ if (selection.active) {
+ selection.from = NULL;
+ selection.from_char = '\0';
+ selection.to = NULL;
+ selection.to_char = '\0';
+ selection.active = false;
- // Erase previous selection.
- if (selection.active) {
- selection.from = NULL;
- selection.from_char = '\0';
- selection.to = NULL;
- selection.to_char = '\0';
- selection.active = false;
-
- update();
- }
+ update();
}
}
- } else if (b->is_pressed() && b->is_doubleclick() && selection.enabled) {
+ }
+ } else if (b->is_pressed() && b->is_doubleclick() && selection.enabled) {
- //doubleclick: select word
- int line = 0;
- Item *item = NULL;
- bool outside;
+ //doubleclick: select word
+ int line = 0;
+ Item *item = NULL;
+ bool outside;
- _find_click(main, b->get_position(), &item, &line, &outside);
+ _find_click(main, b->get_position(), &item, &line, &outside);
- while (item && item->type != ITEM_TEXT) {
+ while (item && item->type != ITEM_TEXT) {
- item = _get_next_item(item, true);
- }
+ item = _get_next_item(item, true);
+ }
- if (item && item->type == ITEM_TEXT) {
+ if (item && item->type == ITEM_TEXT) {
- String itext = static_cast<ItemText *>(item)->text;
+ String itext = static_cast<ItemText *>(item)->text;
- int beg, end;
- if (select_word(itext, line, beg, end)) {
+ int beg, end;
+ if (select_word(itext, line, beg, end)) {
- selection.from = item;
- selection.to = item;
- selection.from_char = beg;
- selection.to_char = end - 1;
- selection.active = true;
- update();
- }
+ selection.from = item;
+ selection.to = item;
+ selection.from_char = beg;
+ selection.to_char = end - 1;
+ selection.active = true;
+ update();
}
- } else if (!b->is_pressed()) {
+ }
+ } else if (!b->is_pressed()) {
- selection.click = NULL;
+ selection.click = NULL;
- if (!b->is_doubleclick() && !scroll_updated) {
- int line = 0;
- Item *item = NULL;
+ if (!b->is_doubleclick() && !scroll_updated) {
+ int line = 0;
+ Item *item = NULL;
- bool outside;
- _find_click(main, b->get_position(), &item, &line, &outside);
+ bool outside;
+ _find_click(main, b->get_position(), &item, &line, &outside);
- if (item) {
+ if (item) {
- Variant meta;
- if (!outside && _find_meta(item, &meta)) {
- //meta clicked
+ Variant meta;
+ if (!outside && _find_meta(item, &meta)) {
+ //meta clicked
- emit_signal("meta_clicked", meta);
- }
+ emit_signal("meta_clicked", meta);
}
}
}
@@ -1294,6 +1308,23 @@ bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item)
return false;
}
+bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) {
+
+ if (from && from != to) {
+ if (from->type != ITEM_FONT && from->type != ITEM_COLOR && from->type != ITEM_UNDERLINE && from->type != ITEM_STRIKETHROUGH)
+ return true;
+
+ for (List<Item *>::Element *E = from->subitems.front(); E; E = E->next()) {
+ bool layout = _find_layout_subitem(E->get(), to);
+
+ if (layout)
+ return true;
+ }
+ }
+
+ return false;
+}
+
void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
if (p_frame->first_invalid_line == p_frame->lines.size())
@@ -1411,9 +1442,13 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline)
if (p_enter)
current = p_item;
- if (p_ensure_newline && current_frame->lines[current_frame->lines.size() - 1].from) {
- _invalidate_current_line(current_frame);
- current_frame->lines.resize(current_frame->lines.size() + 1);
+ if (p_ensure_newline) {
+ Item *from = current_frame->lines[current_frame->lines.size() - 1].from;
+ // only create a new line for Item types that generate content/layout, ignore those that represent formatting/styling
+ if (_find_layout_subitem(from, p_item)) {
+ _invalidate_current_line(current_frame);
+ current_frame->lines.resize(current_frame->lines.size() + 1);
+ }
}
if (current_frame->lines[current_frame->lines.size() - 1].from == NULL) {
@@ -2336,6 +2371,7 @@ RichTextLabel::RichTextLabel() {
tab_size = 4;
default_align = ALIGN_LEFT;
underline_meta = true;
+ meta_hovering = NULL;
override_selected_font_color = false;
scroll_visible = false;
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 114c6103e2..21d099c37a 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -286,6 +286,7 @@ private:
bool _find_underline(Item *p_item);
bool _find_strikethrough(Item *p_item);
bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = NULL);
+ bool _find_layout_subitem(Item *from, Item *to);
void _update_scroll();
void _scroll_changed(double);
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index 2938654ed9..a7c15151ae 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -53,29 +53,19 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) {
if (b.is_valid()) {
accept_event();
- if (b->get_button_index() == 5 && b->is_pressed()) {
+ if (b->get_button_index() == BUTTON_WHEEL_DOWN && b->is_pressed()) {
- /*
- if (orientation==VERTICAL)
- set_val( get_val() + get_page() / 4.0 );
- else
- */
set_value(get_value() + get_page() / 4.0);
accept_event();
}
- if (b->get_button_index() == 4 && b->is_pressed()) {
+ if (b->get_button_index() == BUTTON_WHEEL_UP && b->is_pressed()) {
- /*
- if (orientation==HORIZONTAL)
- set_val( get_val() - get_page() / 4.0 );
- else
- */
set_value(get_value() - get_page() / 4.0);
accept_event();
}
- if (b->get_button_index() != 1)
+ if (b->get_button_index() != BUTTON_LEFT)
return;
if (b->is_pressed()) {
@@ -449,27 +439,26 @@ double ScrollBar::get_grabber_size() const {
}
double ScrollBar::get_area_size() const {
-
- if (orientation == VERTICAL) {
-
- double area = get_size().height;
- area -= get_stylebox("scroll")->get_minimum_size().height;
- area -= get_icon("increment")->get_height();
- area -= get_icon("decrement")->get_height();
- area -= get_grabber_min_size();
- return area;
-
- } else if (orientation == HORIZONTAL) {
-
- double area = get_size().width;
- area -= get_stylebox("scroll")->get_minimum_size().width;
- area -= get_icon("increment")->get_width();
- area -= get_icon("decrement")->get_width();
- area -= get_grabber_min_size();
- return area;
- } else {
-
- return 0;
+ switch (orientation) {
+ case VERTICAL: {
+ double area = get_size().height;
+ area -= get_stylebox("scroll")->get_minimum_size().height;
+ area -= get_icon("increment")->get_height();
+ area -= get_icon("decrement")->get_height();
+ area -= get_grabber_min_size();
+ return area;
+ } break;
+ case HORIZONTAL: {
+ double area = get_size().width;
+ area -= get_stylebox("scroll")->get_minimum_size().width;
+ area -= get_icon("increment")->get_width();
+ area -= get_icon("decrement")->get_width();
+ area -= get_grabber_min_size();
+ return area;
+ } break;
+ default: {
+ return 0.0;
+ }
}
}
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index e50a71b0ff..d83ae47671 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -88,13 +88,16 @@ void ScrollContainer::_cancel_drag() {
void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
+ 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()) {
// only horizontal is enabled, scroll horizontally
- if (h_scroll->is_visible() && !v_scroll->is_visible()) {
+ 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());
} else if (v_scroll->is_visible_in_tree()) {
v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() / 8 * mb->get_factor());
@@ -103,7 +106,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed()) {
// only horizontal is enabled, scroll horizontally
- if (h_scroll->is_visible() && !v_scroll->is_visible()) {
+ 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());
} else if (v_scroll->is_visible()) {
v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() / 8 * mb->get_factor());
@@ -122,6 +125,9 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
+ if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll)
+ accept_event(); //accept event if scroll changed
+
if (!OS::get_singleton()->has_touchscreen_ui_hint())
return;
@@ -134,19 +140,17 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
_cancel_drag();
}
- if (true) {
- drag_speed = Vector2();
- drag_accum = Vector2();
- last_drag_accum = Vector2();
- drag_from = Vector2(h_scroll->get_value(), v_scroll->get_value());
- drag_touching = OS::get_singleton()->has_touchscreen_ui_hint();
- drag_touching_deaccel = false;
- beyond_deadzone = false;
+ drag_speed = Vector2();
+ drag_accum = Vector2();
+ last_drag_accum = Vector2();
+ drag_from = Vector2(h_scroll->get_value(), v_scroll->get_value());
+ drag_touching = OS::get_singleton()->has_touchscreen_ui_hint();
+ drag_touching_deaccel = false;
+ beyond_deadzone = false;
+ time_since_motion = 0;
+ if (drag_touching) {
+ set_physics_process_internal(true);
time_since_motion = 0;
- if (drag_touching) {
- set_physics_process_internal(true);
- time_since_motion = 0;
- }
}
} else {
@@ -204,6 +208,9 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8);
}
}
+
+ if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll)
+ accept_event(); //accept event if scroll changed
}
void ScrollContainer::_update_scrollbar_position() {
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index 028ca41cbf..b777e77bc3 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -295,6 +295,7 @@ Slider::Slider(Orientation p_orientation) {
mouse_inside = false;
grab.active = false;
ticks = 0;
+ ticks_on_borders = false;
custom_step = -1;
editable = true;
scrollable = true;
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index d21143739c..e778af3ceb 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -277,6 +277,7 @@ SpinBox::SpinBox() {
add_child(line_edit);
line_edit->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+ line_edit->set_mouse_filter(MOUSE_FILTER_PASS);
//connect("value_changed",this,"_value_changed");
line_edit->connect("text_entered", this, "_text_entered", Vector<Variant>(), CONNECT_DEFERRED);
line_edit->connect("focus_exited", this, "_line_edit_focus_exit", Vector<Variant>(), CONNECT_DEFERRED);
diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h
index 3c1ca09a9f..97838e19a3 100644
--- a/scene/gui/split_container.h
+++ b/scene/gui/split_container.h
@@ -35,7 +35,7 @@
class SplitContainer : public Container {
- GDCLASS(SplitContainer, Container)
+ GDCLASS(SplitContainer, Container);
public:
enum DraggerVisibility {
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 6997c2809c..39c76e6646 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -146,6 +146,11 @@ void TabContainer::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+
+ minimum_size_changed();
+ update();
+ } break;
case NOTIFICATION_RESIZED: {
Vector<Control *> tabs = _get_tabs();
@@ -181,7 +186,6 @@ void TabContainer::_notification(int p_what) {
first_tab_cache--;
}
} break;
-
case NOTIFICATION_DRAW: {
RID canvas = get_canvas_item();
@@ -732,6 +736,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);
+ update();
}
String TabContainer::get_tab_title(int p_tab) const {
@@ -749,6 +754,7 @@ void TabContainer::set_tab_icon(int p_tab, const Ref<Texture> &p_icon) {
Control *child = _get_tab(p_tab);
ERR_FAIL_COND(!child);
child->set_meta("_tab_icon", p_icon);
+ update();
}
Ref<Texture> TabContainer::get_tab_icon(int p_tab) const {
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index ac643c1320..7b0836cd28 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -222,6 +222,10 @@ void Tabs::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ minimum_size_changed();
+ update();
+ } break;
case NOTIFICATION_MOUSE_EXIT: {
rb_hover = -1;
cb_hover = -1;
@@ -232,7 +236,6 @@ void Tabs::_notification(int p_what) {
_update_cache();
_ensure_no_over_offset();
ensure_tab_visible(current);
-
} break;
case NOTIFICATION_DRAW: {
_update_cache();
@@ -394,7 +397,6 @@ void Tabs::_notification(int p_what) {
} else {
buttons_visible = false;
}
-
} break;
}
}
@@ -890,6 +892,8 @@ void Tabs::ensure_tab_visible(int p_idx) {
}
Rect2 Tabs::get_tab_rect(int p_tab) const {
+
+ ERR_FAIL_INDEX_V(p_tab, tabs.size(), Rect2());
return Rect2(tabs[p_tab].ofs_cache, 0, tabs[p_tab].size_cache, get_size().height);
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 3eb4e6d75a..f9bdce5fef 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -50,7 +50,7 @@ inline bool _is_symbol(CharType c) {
static bool _is_text_char(CharType c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+ return !is_symbol(c);
}
static bool _is_whitespace(CharType c) {
@@ -104,6 +104,13 @@ static CharType _get_right_pair_symbol(CharType c) {
return 0;
}
+static int _find_first_non_whitespace_column_of_line(const String &line) {
+ int left = 0;
+ while (left < line.length() && _is_whitespace(line[left]))
+ left++;
+ return left;
+}
+
void TextEdit::Text::set_font(const Ref<Font> &p_font) {
font = p_font;
@@ -292,6 +299,7 @@ void TextEdit::Text::insert(int p_at, const String &p_text) {
line.marked = false;
line.safe = false;
line.breakpoint = false;
+ line.bookmark = false;
line.hidden = false;
line.width_cache = -1;
line.wrap_amount_cache = -1;
@@ -346,7 +354,7 @@ void TextEdit::_update_scrollbars() {
if (line_numbers)
total_width += cache.line_number_w;
- if (draw_breakpoint_gutter) {
+ if (draw_breakpoint_gutter || draw_bookmark_gutter) {
total_width += cache.breakpoint_gutter_width;
}
@@ -398,6 +406,7 @@ void TextEdit::_update_scrollbars() {
cursor.line_ofs = 0;
cursor.wrap_ofs = 0;
v_scroll->set_value(0);
+ v_scroll->set_max(0);
v_scroll->hide();
}
@@ -416,6 +425,7 @@ void TextEdit::_update_scrollbars() {
cursor.x_ofs = 0;
h_scroll->set_value(0);
+ h_scroll->set_max(0);
h_scroll->hide();
}
@@ -605,7 +615,7 @@ void TextEdit::_notification(int p_what) {
draw_caret = false;
}
- if (draw_breakpoint_gutter) {
+ if (draw_breakpoint_gutter || draw_bookmark_gutter) {
breakpoint_gutter_width = (get_row_height() * 55) / 100;
cache.breakpoint_gutter_width = breakpoint_gutter_width;
} else {
@@ -652,10 +662,8 @@ void TextEdit::_notification(int p_what) {
int xmargin_end = size.width - cache.style_normal->get_margin(MARGIN_RIGHT);
//let's do it easy for now:
cache.style_normal->draw(ci, Rect2(Point2(), size));
- float readonly_alpha = 1.0; // used to set the input text color when in read-only mode
if (readonly) {
cache.style_readonly->draw(ci, Rect2(Point2(), size));
- readonly_alpha = .5;
draw_caret = false;
}
if (has_focus())
@@ -665,8 +673,7 @@ void TextEdit::_notification(int p_what) {
int visible_rows = get_visible_rows() + 1;
- Color color = cache.font_color;
- color.a *= readonly_alpha;
+ Color color = readonly ? cache.font_color_readonly : cache.font_color;
if (syntax_coloring) {
if (cache.background_color.a > 0.01) {
@@ -674,6 +681,13 @@ void TextEdit::_notification(int p_what) {
}
}
+ if (line_length_guideline) {
+ int x = xmargin_beg + cache.font->get_char_size('0').width * line_length_guideline_col - cursor.x_ofs;
+ if (x > xmargin_beg && x < xmargin_end) {
+ VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(x, 0), Point2(x, size.height), cache.line_length_guideline_color);
+ }
+ }
+
int brace_open_match_line = -1;
int brace_open_match_column = -1;
bool brace_open_matching = false;
@@ -861,10 +875,7 @@ void TextEdit::_notification(int p_what) {
color_map = _get_line_syntax_highlighting(line);
}
// ensure we at least use the font color
- Color current_color = cache.font_color;
- if (readonly) {
- current_color.a *= readonly_alpha;
- }
+ Color current_color = readonly ? cache.font_color_readonly : cache.font_color;
bool underlined = false;
@@ -954,6 +965,16 @@ void TextEdit::_notification(int p_what) {
#endif
}
+ // draw bookmark marker
+ if (text.is_bookmark(line)) {
+ if (draw_bookmark_gutter) {
+ int vertical_gap = (get_row_height() * 40) / 100;
+ int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100;
+ int marker_radius = get_row_height() - (vertical_gap * 2);
+ VisualServer::get_singleton()->canvas_item_add_circle(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2 + marker_radius / 2, ofs_y + vertical_gap + marker_radius / 2), marker_radius, Color(cache.bookmark_color.r, cache.bookmark_color.g, cache.bookmark_color.b));
+ }
+ }
+
// draw breakpoint marker
if (text.is_breakpoint(line)) {
if (draw_breakpoint_gutter) {
@@ -1041,10 +1062,7 @@ void TextEdit::_notification(int p_what) {
if (syntax_coloring) {
if (color_map.has(last_wrap_column + j)) {
- current_color = color_map[last_wrap_column + j].color;
- if (readonly) {
- current_color.a *= readonly_alpha;
- }
+ current_color = readonly ? cache.font_color_readonly : color_map[last_wrap_column + j].color;
}
color = current_color;
}
@@ -1072,10 +1090,7 @@ void TextEdit::_notification(int p_what) {
}
if ((char_ofs + char_margin + char_w) >= xmargin_end) {
- if (syntax_coloring)
- continue;
- else
- break;
+ break;
}
bool in_search_result = false;
@@ -1098,7 +1113,7 @@ void TextEdit::_notification(int p_what) {
if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
// draw the wrap indent offset highlight
if (line_wrap_index != 0 && j == 0) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin - indent_px, ofs_y, (char_ofs + char_margin), get_row_height()), cache.current_line_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin - indent_px, ofs_y, indent_px, get_row_height()), cache.current_line_color);
}
// if its the last char draw to end of the line
if (j == str.length() - 1) {
@@ -1161,7 +1176,7 @@ void TextEdit::_notification(int p_what) {
if (brace_open_mismatch)
color = cache.brace_mismatch_color;
- drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_color_selected : color);
}
if ((brace_close_match_line == line && brace_close_match_column == last_wrap_column + j) ||
@@ -1169,7 +1184,7 @@ void TextEdit::_notification(int p_what) {
if (brace_close_mismatch)
color = cache.brace_mismatch_color;
- drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_color_selected : color);
}
}
@@ -1235,24 +1250,28 @@ void TextEdit::_notification(int p_what) {
if (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index && block_caret && draw_caret && !insert_mode) {
color = cache.caret_background_color;
} else if (!syntax_coloring && block_caret) {
- color = cache.font_color;
- color.a *= readonly_alpha;
+ color = readonly ? cache.font_color_readonly : cache.font_color;
}
if (str[j] >= 32) {
int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
- int w = drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ int w = drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_color_selected : color);
if (underlined) {
float line_width = 1.0;
#ifdef TOOLS_ENABLED
line_width *= EDSCALE;
#endif
- draw_rect(Rect2(char_ofs + char_margin + ofs_x, yofs + ascent + 2, w, line_width), in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ draw_rect(Rect2(char_ofs + char_margin + ofs_x, yofs + ascent + 2, w, line_width), in_selection && override_selected_font_color ? cache.font_color_selected : color);
}
} else if (draw_tabs && str[j] == '\t') {
int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2;
- cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_color_selected : color);
+ }
+
+ if (draw_spaces && str[j] == ' ') {
+ int yofs = (get_row_height() - cache.space_icon->get_height()) / 2;
+ cache.space_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_color_selected : color);
}
char_ofs += char_w;
@@ -1327,13 +1346,6 @@ void TextEdit::_notification(int p_what) {
}
}
- if (line_length_guideline) {
- int x = xmargin_beg + cache.font->get_char_size('0').width * line_length_guideline_col - cursor.x_ofs;
- if (x > xmargin_beg && x < xmargin_end) {
- VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(x, 0), Point2(x, size.height), cache.line_length_guideline_color);
- }
- }
-
bool completion_below = false;
if (completion_active) {
// code completion box
@@ -1560,8 +1572,7 @@ void TextEdit::_consume_pair_symbol(CharType ch) {
}
if ((ch == '\'' || ch == '"') &&
- cursor_get_column() > 0 &&
- _is_text_char(text[cursor.line][cursor_get_column() - 1])) {
+ cursor_get_column() > 0 && _is_text_char(text[cursor.line][cursor_get_column() - 1]) && !_is_pair_right_symbol(text[cursor.line][cursor_get_column()])) {
insert_text_at_cursor(ch_single);
cursor_set_column(cursor_position_to_move);
return;
@@ -1582,7 +1593,6 @@ void TextEdit::_consume_pair_symbol(CharType ch) {
insert_text_at_cursor(ch_pair);
cursor_set_column(cursor_position_to_move);
- return;
}
void TextEdit::_consume_backspace_for_pair_symbol(int prev_line, int prev_column) {
@@ -1633,31 +1643,25 @@ void TextEdit::backspace_at_cursor() {
_consume_backspace_for_pair_symbol(prev_line, prev_column);
} else {
// handle space indentation
- if (cursor.column - indent_size >= 0 && indent_using_spaces) {
-
- // if there is enough spaces to count as a tab
+ if (cursor.column != 0 && indent_using_spaces) {
+ // check if there are no other chars before cursor, just indentation
bool unindent = true;
- for (int i = 1; i <= indent_size; i++) {
- if (text[cursor.line][cursor.column - i] != ' ') {
- unindent = false;
- break;
- }
- }
-
- // and it is before the first character
int i = 0;
while (i < cursor.column && i < text[cursor.line].length()) {
- if (text[cursor.line][i] != ' ' && text[cursor.line][i] != '\t') {
+ if (!_is_whitespace(text[cursor.line][i])) {
unindent = false;
break;
}
i++;
}
- // then we can remove it as a single character.
+ // then we can remove all spaces as a single character.
if (unindent) {
- _remove_text(cursor.line, cursor.column - indent_size, cursor.line, cursor.column);
- prev_column = cursor.column - indent_size;
+ // we want to remove spaces up to closest indent
+ // or whole indent if cursor is pointing at it
+ int spaces_to_delete = _calculate_spaces_till_next_left_indent(cursor.column);
+ prev_column = cursor.column - spaces_to_delete;
+ _remove_text(cursor.line, prev_column, cursor.line, cursor.column);
} else {
_remove_text(prev_line, prev_column, cursor.line, cursor.column);
}
@@ -1674,6 +1678,10 @@ void TextEdit::indent_right() {
int start_line;
int end_line;
+
+ // this value informs us by how much we changed selection position by indenting right
+ // default is 1 for tab indentation
+ int selection_offset = 1;
begin_complex_operation();
if (is_selection_active()) {
@@ -1692,18 +1700,24 @@ void TextEdit::indent_right() {
for (int i = start_line; i <= end_line; i++) {
String line_text = get_line(i);
if (indent_using_spaces) {
- line_text = space_indent + line_text;
+ // 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
+ selection_offset = spaces_to_add;
+ for (int j = 0; j < spaces_to_add; j++)
+ line_text = ' ' + line_text;
} else {
line_text = '\t' + line_text;
}
set_line(i, line_text);
}
- // fix selection and cursor being off by one on the last line
+ // fix selection and cursor being off after shifting selection right
if (is_selection_active()) {
- select(selection.from_line, selection.from_column + 1, selection.to_line, selection.to_column + 1);
+ select(selection.from_line, selection.from_column + selection_offset, selection.to_line, selection.to_column + selection_offset);
}
- cursor_set_column(cursor.column + 1, false);
+ cursor_set_column(cursor.column + selection_offset, false);
end_complex_operation();
update();
}
@@ -1712,6 +1726,15 @@ void TextEdit::indent_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)
+ // 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;
+ int initial_cursor_column = cursor.column;
+
begin_complex_operation();
if (is_selection_active()) {
@@ -1734,21 +1757,43 @@ void TextEdit::indent_left() {
if (line_text.begins_with("\t")) {
line_text = line_text.substr(1, line_text.length());
set_line(i, line_text);
- } else if (line_text.begins_with(space_indent)) {
- line_text = line_text.substr(indent_size, line_text.length());
+ removed_characters = 1;
+ } else if (line_text.begins_with(" ")) {
+ // when unindenting we aim to remove spaces before line that has selection no matter what is selected
+ // so we start of by finding first non whitespace character of line
+ int left = _find_first_non_whitespace_column_of_line(line_text);
+
+ // here we remove only enough spaces to align text to nearest full multiple of indentation_size
+ // in case where selection begins at the start of indentation_size multiple we remove whole indentation level
+ int spaces_to_remove = _calculate_spaces_till_next_left_indent(left);
+
+ line_text = line_text.substr(spaces_to_remove, line_text.length());
set_line(i, line_text);
+ removed_characters = spaces_to_remove;
}
}
// 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 - 1, selection.to_line, selection.to_column - 1);
+ select(selection.from_line, selection.from_column - removed_characters,
+ selection.to_line, initial_selection_end_column - removed_characters);
}
- cursor_set_column(cursor.column - 1, false);
+ cursor_set_column(initial_cursor_column - removed_characters, false);
end_complex_operation();
update();
}
+int TextEdit::_calculate_spaces_till_next_left_indent(int column) {
+ int spaces_till_indent = column % indent_size;
+ if (spaces_till_indent == 0)
+ spaces_till_indent = indent_size;
+ return spaces_till_indent;
+}
+
+int TextEdit::_calculate_spaces_till_next_right_indent(int column) {
+ return indent_size - column % indent_size;
+}
+
void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const {
float rows = p_mouse.y;
@@ -1800,6 +1845,9 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
+ 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()) {
@@ -2054,6 +2102,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_scroll_down(delta);
}
h_scroll->set_value(h_scroll->get_value() + pan_gesture->get_delta().x * 100);
+ if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll)
+ accept_event(); //accept event if scroll changed
+
return;
}
@@ -2097,6 +2148,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
+ if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll)
+ accept_event(); //accept event if scroll changed
+
Ref<InputEventKey> k = p_gui_input;
if (k.is_valid()) {
@@ -2276,6 +2330,36 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
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_scancode()) {
+ 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_scancode(remap_key);
+ k->set_control(false);
+ }
+ }
+#endif
_reset_caret_blink_timer();
@@ -2475,15 +2559,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
} else {
if (k->get_shift()) {
- //simple unindent
+ // simple unindent
int cc = cursor.column;
-
- const int len = text[cursor.line].length();
const String &line = text[cursor.line];
- int left = 0; // number of whitespace chars at beginning of line
- while (left < len && (line[left] == '\t' || line[left] == ' '))
- left++;
+ int left = _find_first_non_whitespace_column_of_line(line);
cc = MIN(cc, left);
while (cc < indent_size && cc < left && line[cc] == ' ')
@@ -2491,24 +2571,18 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
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 {
- int n = 0;
-
- for (int i = 1; i <= MIN(cc, indent_size); i++) {
- if (line[cc - i] != ' ') {
- break;
- }
- n++;
- }
-
- if (n > 0) {
- _remove_text(cursor.line, cc - n, cursor.line, cc);
- if (cursor.column > left - n) // inside text?
- cursor_set_column(MAX(0, cursor.column - n));
+ // 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();
}
}
@@ -2517,9 +2591,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
update();
}
} else {
- //simple indent
+ // simple indent
if (indent_using_spaces) {
- _insert_text_at_cursor(space_indent);
+ // 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");
}
@@ -2610,9 +2689,22 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
#ifdef APPLE_STYLE_KEYS
if (k->get_command()) {
- cursor_set_column(0);
+ // 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()) {
scancode_handled = false;
@@ -3559,7 +3651,7 @@ void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r
op.chain_forward = false;
op.chain_backward = false;
- //see if it shold just be set as current op
+ //see if it should just be set as current op
if (current_op.type != op.type) {
op.prev_version = get_version();
_push_current_op();
@@ -3610,7 +3702,7 @@ void TextEdit::_remove_text(int p_from_line, int p_from_column, int p_to_line, i
op.chain_forward = false;
op.chain_backward = false;
- //see if it shold just be set as current op
+ //see if it should just be set as current op
if (current_op.type != op.type) {
op.prev_version = get_version();
_push_current_op();
@@ -3975,7 +4067,7 @@ void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_
cursor.line = p_row;
int n_col = get_char_pos_for_line(cursor.last_fit_x, p_row, p_wrap_index);
- if (is_wrap_enabled() && p_wrap_index < times_line_wraps(p_row)) {
+ if (n_col != 0 && is_wrap_enabled() && p_wrap_index < times_line_wraps(p_row)) {
Vector<String> rows = get_wrap_rows_text(p_row);
int row_end_col = 0;
for (int i = 0; i < p_wrap_index + 1; i++) {
@@ -4066,7 +4158,7 @@ void TextEdit::_scroll_moved(double p_to_val) {
int v_scroll_i = floor(get_v_scroll());
int sc = 0;
int n_line;
- for (n_line = 0; n_line < text.size(); n_line++) {
+ for (n_line = 0; n_line < text.size() - 1; n_line++) {
if (!is_line_hidden(n_line)) {
sc++;
sc += times_line_wraps(n_line);
@@ -4259,17 +4351,22 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
void TextEdit::set_text(String p_text) {
setting_text = true;
- _clear();
- _insert_text_at_cursor(p_text);
- clear_undo_history();
- cursor.column = 0;
- cursor.line = 0;
- cursor.x_ofs = 0;
- cursor.line_ofs = 0;
- cursor.wrap_ofs = 0;
- cursor.last_fit_x = 0;
- cursor_set_line(0);
- cursor_set_column(0);
+ if (!undo_enabled) {
+ _clear();
+ _insert_text_at_cursor(p_text);
+ }
+
+ if (undo_enabled) {
+ cursor_set_line(0);
+ cursor_set_column(0);
+
+ begin_complex_operation();
+ _remove_text(0, 0, MAX(0, get_line_count() - 1), MAX(get_line(MAX(get_line_count() - 1, 0)).size() - 1, 0));
+ _insert_text_at_cursor(p_text);
+ end_complex_operation();
+ selection.active = false;
+ }
+
update();
setting_text = false;
@@ -4446,7 +4543,8 @@ void TextEdit::_update_caches() {
cache.line_number_color = get_color("line_number_color");
cache.safe_line_number_color = get_color("safe_line_number_color");
cache.font_color = get_color("font_color");
- cache.font_selected_color = get_color("font_selected_color");
+ cache.font_color_selected = get_color("font_color_selected");
+ cache.font_color_readonly = get_color("font_color_readonly");
cache.keyword_color = get_color("keyword_color");
cache.function_color = get_color("function_color");
cache.member_variable_color = get_color("member_variable_color");
@@ -4455,6 +4553,7 @@ void TextEdit::_update_caches() {
cache.mark_color = get_color("mark_color");
cache.current_line_color = get_color("current_line_color");
cache.line_length_guideline_color = get_color("line_length_guideline_color");
+ cache.bookmark_color = get_color("bookmark_color");
cache.breakpoint_color = get_color("breakpoint_color");
cache.executing_line_color = get_color("executing_line_color");
cache.code_folding_color = get_color("code_folding_color");
@@ -4471,6 +4570,7 @@ void TextEdit::_update_caches() {
#endif
cache.row_height = cache.font->get_height() + cache.line_spacing;
cache.tab_icon = get_icon("tab");
+ cache.space_icon = get_icon("space");
cache.folded_icon = get_icon("folded");
cache.can_fold_icon = get_icon("fold");
cache.folded_eol_icon = get_icon("GuiEllipsis", "EditorIcons");
@@ -4572,6 +4672,8 @@ bool TextEdit::has_keyword_color(String p_keyword) const {
}
Color TextEdit::get_keyword_color(String p_keyword) const {
+
+ ERR_FAIL_COND_V(!keywords.has(p_keyword), Color());
return keywords[p_keyword];
}
@@ -5051,6 +5153,37 @@ void TextEdit::clear_executing_line() {
update();
}
+bool TextEdit::is_line_set_as_bookmark(int p_line) const {
+
+ ERR_FAIL_INDEX_V(p_line, text.size(), false);
+ return text.is_bookmark(p_line);
+}
+
+void TextEdit::set_line_as_bookmark(int p_line, bool p_bookmark) {
+
+ ERR_FAIL_INDEX(p_line, text.size());
+ text.set_bookmark(p_line, p_bookmark);
+ update();
+}
+
+void TextEdit::get_bookmarks(List<int> *p_bookmarks) const {
+
+ for (int i = 0; i < text.size(); i++) {
+ if (text.is_bookmark(i))
+ p_bookmarks->push_back(i);
+ }
+}
+
+Array TextEdit::get_bookmarks_array() const {
+
+ Array arr;
+ for (int i = 0; i < text.size(); i++) {
+ if (text.is_bookmark(i))
+ arr.append(i);
+ }
+ return arr;
+}
+
bool TextEdit::is_line_set_as_breakpoint(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), false);
@@ -5261,11 +5394,7 @@ bool TextEdit::is_line_comment(int p_line) const {
for (int i = 0; i < line_length - 1; i++) {
if (_is_symbol(text[p_line][i]) && cri_map.has(i)) {
const Text::ColorRegionInfo &cri = cri_map[i];
- if (color_regions[cri.region].begin_key == "#" || color_regions[cri.region].begin_key == "//") {
- return true;
- } else {
- return false;
- }
+ return color_regions[cri.region].begin_key == "#" || color_regions[cri.region].begin_key == "//";
} else if (_is_whitespace(text[p_line][i])) {
continue;
} else {
@@ -5314,9 +5443,7 @@ bool TextEdit::is_folded(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), false);
if (p_line + 1 >= text.size())
return false;
- if (!is_line_hidden(p_line) && is_line_hidden(p_line + 1))
- return true;
- return false;
+ return !is_line_hidden(p_line) && is_line_hidden(p_line + 1);
}
Vector<int> TextEdit::get_folded_lines() const {
@@ -5470,7 +5597,7 @@ void TextEdit::undo() {
TextOperation op = undo_stack_pos->get();
_do_text_op(op, true);
- if (op.from_line != op.to_line || op.to_column != op.from_column + 1)
+ if (op.type != TextOperation::TYPE_INSERT && (op.from_line != op.to_line || op.to_column != op.from_column + 1))
select(op.from_line, op.from_column, op.to_line, op.to_column);
current_op.version = op.prev_version;
@@ -5607,6 +5734,16 @@ bool TextEdit::is_drawing_tabs() const {
return draw_tabs;
}
+void TextEdit::set_draw_spaces(bool p_draw) {
+
+ draw_spaces = p_draw;
+}
+
+bool TextEdit::is_drawing_spaces() const {
+
+ return draw_spaces;
+}
+
void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) {
override_selected_font_color = p_override_selected_font_color;
}
@@ -6128,6 +6265,15 @@ void TextEdit::set_line_length_guideline_column(int p_column) {
update();
}
+void TextEdit::set_bookmark_gutter_enabled(bool p_draw) {
+ draw_bookmark_gutter = p_draw;
+ update();
+}
+
+bool TextEdit::is_bookmark_gutter_enabled() const {
+ return draw_bookmark_gutter;
+}
+
void TextEdit::set_breakpoint_gutter_enabled(bool p_draw) {
draw_breakpoint_gutter = p_draw;
update();
@@ -6182,14 +6328,14 @@ int TextEdit::get_info_gutter_width() const {
return info_gutter_width;
}
-void TextEdit::set_hiding_enabled(int p_enabled) {
+void TextEdit::set_hiding_enabled(bool p_enabled) {
if (!p_enabled)
unhide_all_lines();
hiding_enabled = p_enabled;
update();
}
-int TextEdit::is_hiding_enabled() const {
+bool TextEdit::is_hiding_enabled() const {
return hiding_enabled;
}
@@ -6290,6 +6436,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text"), &TextEdit::get_text);
ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line);
+ ClassDB::bind_method(D_METHOD("center_viewport_to_cursor"), &TextEdit::center_viewport_to_cursor);
ClassDB::bind_method(D_METHOD("cursor_set_column", "column", "adjust_viewport"), &TextEdit::cursor_set_column, DEFVAL(true));
ClassDB::bind_method(D_METHOD("cursor_set_line", "line", "adjust_viewport", "can_be_hidden", "wrap_index"), &TextEdit::cursor_set_line, DEFVAL(true), DEFVAL(true), DEFVAL(0));
@@ -6340,6 +6487,8 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_show_line_numbers_enabled"), &TextEdit::is_show_line_numbers_enabled);
ClassDB::bind_method(D_METHOD("set_draw_tabs"), &TextEdit::set_draw_tabs);
ClassDB::bind_method(D_METHOD("is_drawing_tabs"), &TextEdit::is_drawing_tabs);
+ ClassDB::bind_method(D_METHOD("set_draw_spaces"), &TextEdit::set_draw_spaces);
+ ClassDB::bind_method(D_METHOD("is_drawing_spaces"), &TextEdit::is_drawing_spaces);
ClassDB::bind_method(D_METHOD("set_breakpoint_gutter_enabled", "enable"), &TextEdit::set_breakpoint_gutter_enabled);
ClassDB::bind_method(D_METHOD("is_breakpoint_gutter_enabled"), &TextEdit::is_breakpoint_gutter_enabled);
ClassDB::bind_method(D_METHOD("set_draw_fold_gutter"), &TextEdit::set_draw_fold_gutter);
@@ -6391,6 +6540,7 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "syntax_highlighting"), "set_syntax_coloring", "is_syntax_coloring_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_line_numbers"), "set_show_line_numbers", "is_show_line_numbers_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "breakpoint_gutter"), "set_breakpoint_gutter_enabled", "is_breakpoint_gutter_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled");
@@ -6432,6 +6582,7 @@ TextEdit::TextEdit() {
setting_row = false;
draw_tabs = false;
+ draw_spaces = false;
override_selected_font_color = false;
draw_caret = true;
max_chars = 0;
@@ -6520,6 +6671,7 @@ TextEdit::TextEdit() {
line_numbers_zero_padded = false;
line_length_guideline = false;
line_length_guideline_col = 80;
+ draw_bookmark_gutter = false;
draw_breakpoint_gutter = false;
draw_fold_gutter = false;
draw_info_gutter = false;
@@ -6544,6 +6696,7 @@ TextEdit::TextEdit() {
context_menu_enabled = true;
menu = memnew(PopupMenu);
add_child(menu);
+ readonly = true; // initialise to opposite first, so we get past the early-out in set_readonly
set_readonly(false);
menu->connect("id_pressed", this, "menu_option");
first_draw = true;
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 4badd85e07..30956ccb23 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -40,7 +40,7 @@ class SyntaxHighlighter;
class TextEdit : public Control {
- GDCLASS(TextEdit, Control)
+ GDCLASS(TextEdit, Control);
public:
struct HighlighterInfo {
@@ -75,6 +75,7 @@ public:
int width_cache : 24;
bool marked : 1;
bool breakpoint : 1;
+ bool bookmark : 1;
bool hidden : 1;
bool safe : 1;
int wrap_amount_cache : 24;
@@ -105,6 +106,8 @@ public:
void set(int p_line, const String &p_text);
void set_marked(int p_line, bool p_marked) { text.write[p_line].marked = p_marked; }
bool is_marked(int p_line) const { return text[p_line].marked; }
+ void set_bookmark(int p_line, bool p_bookmark) { text.write[p_line].bookmark = p_bookmark; }
+ bool is_bookmark(int p_line) const { return text[p_line].bookmark; }
void set_breakpoint(int p_line, bool p_breakpoint) { text.write[p_line].breakpoint = p_breakpoint; }
bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; }
@@ -163,6 +166,7 @@ private:
struct Cache {
Ref<Texture> tab_icon;
+ Ref<Texture> space_icon;
Ref<Texture> can_fold_icon;
Ref<Texture> folded_icon;
Ref<Texture> folded_eol_icon;
@@ -180,13 +184,15 @@ private:
Color line_number_color;
Color safe_line_number_color;
Color font_color;
- Color font_selected_color;
+ Color font_color_selected;
+ Color font_color_readonly;
Color keyword_color;
Color number_color;
Color function_color;
Color member_variable_color;
Color selection_color;
Color mark_color;
+ Color bookmark_color;
Color breakpoint_color;
Color executing_line_color;
Color code_folding_color;
@@ -290,6 +296,7 @@ private:
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;
@@ -298,6 +305,7 @@ private:
bool line_numbers_zero_padded;
bool line_length_guideline;
int line_length_guideline_col;
+ bool draw_bookmark_gutter;
bool draw_breakpoint_gutter;
int breakpoint_gutter_width;
bool draw_fold_gutter;
@@ -430,6 +438,9 @@ private:
void _confirm_completion();
void _update_completion_candidates();
+ int _calculate_spaces_till_next_left_indent(int column);
+ int _calculate_spaces_till_next_right_indent(int column);
+
protected:
virtual String get_tooltip(const Point2 &p_pos) const;
@@ -488,6 +499,10 @@ public:
void insert_at(const String &p_text, int at);
int get_line_count() const;
void set_line_as_marked(int p_line, bool p_marked);
+ void set_line_as_bookmark(int p_line, bool p_bookmark);
+ bool is_line_set_as_bookmark(int p_line) const;
+ void get_bookmarks(List<int> *p_bookmarks) const;
+ Array get_bookmarks_array() const;
void set_line_as_breakpoint(int p_line, bool p_breakpoint);
bool is_line_set_as_breakpoint(int p_line) const;
void set_executing_line(int p_line);
@@ -613,6 +628,8 @@ public:
int get_indent_size();
void set_draw_tabs(bool p_draw);
bool is_drawing_tabs() const;
+ void set_draw_spaces(bool p_draw);
+ bool is_drawing_spaces() const;
void set_override_selected_font_color(bool p_override_selected_font_color);
bool is_overriding_selected_font_color() const;
@@ -660,6 +677,9 @@ public:
void set_show_line_length_guideline(bool p_show);
void set_line_length_guideline_column(int p_column);
+ void set_bookmark_gutter_enabled(bool p_draw);
+ bool is_bookmark_gutter_enabled() const;
+
void set_breakpoint_gutter_enabled(bool p_draw);
bool is_breakpoint_gutter_enabled() const;
@@ -678,8 +698,8 @@ public:
void set_info_gutter_width(int p_gutter_width);
int get_info_gutter_width() const;
- void set_hiding_enabled(int p_enabled);
- int is_hiding_enabled() const;
+ void set_hiding_enabled(bool p_enabled);
+ bool is_hiding_enabled() const;
void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata);
@@ -716,7 +736,7 @@ public:
virtual void _update_cache() = 0;
virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line) = 0;
- virtual String get_name() = 0;
+ virtual String get_name() const = 0;
virtual List<String> get_supported_languages() = 0;
void set_text_editor(TextEdit *p_text_editor);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index f22fe5b6a5..c2493ab321 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -39,7 +39,7 @@
#include "scene/main/viewport.h"
#ifdef TOOLS_ENABLED
-#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
#endif
#include <limits.h>
@@ -318,7 +318,7 @@ void TreeItem::set_custom_draw(int p_column, Object *p_object, const StringName
void TreeItem::set_collapsed(bool p_collapsed) {
- if (collapsed == p_collapsed)
+ if (collapsed == p_collapsed || !tree)
return;
collapsed = p_collapsed;
TreeItem *ci = tree->selected_item;
@@ -344,8 +344,7 @@ void TreeItem::set_collapsed(bool p_collapsed) {
}
_changed_notify();
- if (tree)
- tree->emit_signal("item_collapsed", this);
+ tree->emit_signal("item_collapsed", this);
}
bool TreeItem::is_collapsed() {
@@ -389,7 +388,7 @@ TreeItem *TreeItem::get_children() {
return children;
}
-TreeItem *TreeItem::get_prev_visible() {
+TreeItem *TreeItem::get_prev_visible(bool p_wrap) {
TreeItem *current = this;
@@ -398,8 +397,20 @@ TreeItem *TreeItem::get_prev_visible() {
if (!prev) {
current = current->parent;
- if (!current || (current == tree->root && tree->hide_root))
+ if (current == tree->root && tree->hide_root) {
return NULL;
+ } else if (!current) {
+ if (p_wrap) {
+ current = this;
+ TreeItem *temp = this->get_next_visible();
+ while (temp) {
+ current = temp;
+ temp = temp->get_next_visible();
+ }
+ } else {
+ return NULL;
+ }
+ }
} else {
current = prev;
@@ -415,7 +426,7 @@ TreeItem *TreeItem::get_prev_visible() {
return current;
}
-TreeItem *TreeItem::get_next_visible() {
+TreeItem *TreeItem::get_next_visible(bool p_wrap) {
TreeItem *current = this;
@@ -433,10 +444,14 @@ TreeItem *TreeItem::get_next_visible() {
current = current->parent;
}
- if (current == NULL)
- return NULL;
- else
+ if (!current) {
+ if (p_wrap)
+ return tree->root;
+ else
+ return NULL;
+ } else {
current = current->next;
+ }
}
return current;
@@ -740,8 +755,8 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent);
ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children);
- ClassDB::bind_method(D_METHOD("get_next_visible"), &TreeItem::get_next_visible);
- ClassDB::bind_method(D_METHOD("get_prev_visible"), &TreeItem::get_prev_visible);
+ ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false));
ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child);
@@ -1113,6 +1128,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) {
Ref<Texture> b = p_item->cells[i].buttons[j].texture;
Size2 s = b->get_size() + cache.button_pressed->get_minimum_size();
+ if (s.height < label_h)
+ s.height = label_h;
Point2i o = Point2i(ofs + w - s.width, p_pos.y) - cache.offset + p_draw_ofs;
@@ -1914,8 +1931,6 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
edited_col = col;
bool on_arrow = x > col_width - cache.select_arrow->get_width();
- bring_up_editor = false;
-
custom_popup_rect = Rect2i(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h - cache.offset.y), Size2(get_column_width(col), item_h));
if (on_arrow || !p_item->cells[col].custom_button) {
@@ -1970,6 +1985,9 @@ 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) {
+ emit_signal("empty_rmb", get_local_mouse_position());
+ }
}
return item_h; // nothing found
@@ -2266,7 +2284,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
next = _n;
} else {
- return;
+ break;
}
}
if (next == selected_item)
@@ -2304,7 +2322,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
prev = _n;
} else {
- return;
+ break;
}
}
if (prev == selected_item)
@@ -3145,10 +3163,7 @@ bool Tree::is_anything_selected() {
void Tree::clear() {
- if (blocked > 0) {
-
- ERR_FAIL_COND(blocked > 0);
- }
+ ERR_FAIL_COND(blocked > 0);
if (pressing_for_editor) {
if (range_drag_enabled) {
@@ -3488,6 +3503,7 @@ void Tree::scroll_to_item(TreeItem *p_item) {
TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards) {
+ TreeItem *from = p_at;
while (p_at) {
for (int i = 0; i < columns.size(); i++) {
@@ -3499,9 +3515,12 @@ TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_c
}
if (p_backwards)
- p_at = p_at->get_prev_visible();
+ p_at = p_at->get_prev_visible(true);
else
- p_at = p_at->get_next_visible();
+ p_at = p_at->get_next_visible(true);
+
+ if ((p_at) == from)
+ break;
}
return NULL;
@@ -3509,10 +3528,14 @@ TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_c
TreeItem *Tree::search_item_text(const String &p_find, int *r_col, bool p_selectable) {
- if (!root)
+ TreeItem *from = get_selected();
+
+ if (!from)
+ from = root;
+ if (!from)
return NULL;
- return _search_item_text(root, p_find, r_col, p_selectable);
+ return _search_item_text(from->get_next_visible(true), p_find, r_col, p_selectable);
}
void Tree::_do_incr_search(const String &p_add) {
@@ -3521,7 +3544,7 @@ void Tree::_do_incr_search(const String &p_add) {
uint64_t diff = time - last_keypress;
if (diff > uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000)))
incr_search = p_add;
- else
+ else if (incr_search != p_add)
incr_search += p_add;
last_keypress = time;
@@ -3694,6 +3717,10 @@ String Tree::get_tooltip(const Point2 &p_pos) const {
const TreeItem::Cell &c = it->cells[col];
int col_width = get_column_width(col);
+
+ for (int i = 0; i < col; i++)
+ pos.x -= get_column_width(i);
+
for (int j = c.buttons.size() - 1; j >= 0; j--) {
Ref<Texture> b = c.buttons[j].texture;
Size2 size = b->get_size() + cache.button_pressed->get_minimum_size();
@@ -3853,6 +3880,7 @@ void Tree::_bind_methods() {
ADD_SIGNAL(MethodInfo("cell_selected"));
ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::BOOL, "selected")));
ADD_SIGNAL(MethodInfo("item_rmb_selected", PropertyInfo(Variant::VECTOR2, "position")));
+ ADD_SIGNAL(MethodInfo("empty_rmb", PropertyInfo(Variant::VECTOR2, "position")));
ADD_SIGNAL(MethodInfo("empty_tree_rmb_selected", PropertyInfo(Variant::VECTOR2, "position")));
ADD_SIGNAL(MethodInfo("item_edited"));
ADD_SIGNAL(MethodInfo("item_rmb_edited"));
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 26d64baafb..45d451eb72 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -238,8 +238,8 @@ public:
TreeItem *get_parent();
TreeItem *get_children();
- TreeItem *get_prev_visible();
- TreeItem *get_next_visible();
+ TreeItem *get_prev_visible(bool p_wrap = false);
+ TreeItem *get_next_visible(bool p_wrap = false);
void remove_child(TreeItem *p_item);