summaryrefslogtreecommitdiff
path: root/scene/gui
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui')
-rw-r--r--scene/gui/base_button.cpp3
-rw-r--r--scene/gui/base_button.h3
-rw-r--r--scene/gui/button.cpp289
-rw-r--r--scene/gui/button.h9
-rw-r--r--scene/gui/check_box.cpp2
-rw-r--r--scene/gui/check_button.cpp2
-rw-r--r--scene/gui/color_picker.cpp34
-rw-r--r--scene/gui/color_picker.h2
-rw-r--r--scene/gui/control.cpp131
-rw-r--r--scene/gui/control.h4
-rw-r--r--scene/gui/dialogs.cpp44
-rw-r--r--scene/gui/dialogs.h3
-rw-r--r--scene/gui/file_dialog.cpp11
-rw-r--r--scene/gui/file_dialog.h4
-rw-r--r--scene/gui/gradient_edit.cpp10
-rw-r--r--scene/gui/graph_edit.cpp50
-rw-r--r--scene/gui/graph_node.cpp10
-rw-r--r--scene/gui/label.h4
-rw-r--r--scene/gui/line_edit.cpp254
-rw-r--r--scene/gui/line_edit.h19
-rw-r--r--scene/gui/menu_button.h4
-rw-r--r--scene/gui/nine_patch_rect.h4
-rw-r--r--scene/gui/option_button.cpp68
-rw-r--r--scene/gui/option_button.h4
-rw-r--r--scene/gui/panel.h4
-rw-r--r--scene/gui/popup.cpp3
-rw-r--r--scene/gui/popup.h3
-rw-r--r--scene/gui/popup_menu.cpp6
-rw-r--r--scene/gui/popup_menu.h4
-rw-r--r--scene/gui/progress_bar.cpp2
-rw-r--r--scene/gui/range.cpp4
-rw-r--r--scene/gui/range.h4
-rw-r--r--scene/gui/rich_text_effect.cpp122
-rw-r--r--scene/gui/rich_text_effect.h87
-rw-r--r--scene/gui/rich_text_label.cpp475
-rw-r--r--scene/gui/rich_text_label.h150
-rw-r--r--scene/gui/scroll_bar.h4
-rw-r--r--scene/gui/scroll_container.cpp21
-rw-r--r--scene/gui/separator.h4
-rw-r--r--scene/gui/spin_box.cpp27
-rw-r--r--scene/gui/tab_container.cpp16
-rw-r--r--scene/gui/tab_container.h3
-rw-r--r--scene/gui/text_edit.cpp1166
-rw-r--r--scene/gui/text_edit.h36
-rw-r--r--scene/gui/texture_button.cpp16
-rw-r--r--scene/gui/texture_rect.h4
-rw-r--r--scene/gui/tree.cpp66
-rw-r--r--scene/gui/tree.h10
-rw-r--r--scene/gui/viewport_container.cpp1
49 files changed, 2391 insertions, 815 deletions
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 52fcea2a71..4f71481280 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -115,9 +115,6 @@ void BaseButton::_notification(int p_what) {
}
}
- if (p_what == NOTIFICATION_ENTER_TREE) {
- }
-
if (p_what == NOTIFICATION_EXIT_TREE || (p_what == NOTIFICATION_VISIBILITY_CHANGED && !is_visible_in_tree())) {
if (!toggle_mode) {
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index ffccdd69d6..2773f024df 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -32,9 +32,6 @@
#define BASE_BUTTON_H
#include "scene/gui/control.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
class ButtonGroup;
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 65e9cccd05..6b3e89af6c 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -39,158 +39,191 @@ Size2 Button::get_minimum_size() const {
if (clip_text)
minsize.width = 0;
- Ref<Texture> _icon;
- if (icon.is_null() && has_icon("icon"))
- _icon = Control::get_icon("icon");
- else
- _icon = icon;
-
- if (!_icon.is_null()) {
-
- minsize.height = MAX(minsize.height, _icon->get_height());
- minsize.width += _icon->get_width();
- if (xl_text != "")
- minsize.width += get_constant("hseparation");
+ if (!expand_icon) {
+ Ref<Texture> _icon;
+ if (icon.is_null() && has_icon("icon"))
+ _icon = Control::get_icon("icon");
+ else
+ _icon = icon;
+
+ if (!_icon.is_null()) {
+
+ minsize.height = MAX(minsize.height, _icon->get_height());
+ minsize.width += _icon->get_width();
+ if (xl_text != "")
+ minsize.width += get_constant("hseparation");
+ }
}
return get_stylebox("normal")->get_minimum_size() + minsize;
}
void Button::_set_internal_margin(Margin p_margin, float p_value) {
+
_internal_margin[p_margin] = p_value;
}
void Button::_notification(int p_what) {
- if (p_what == NOTIFICATION_TRANSLATION_CHANGED) {
+ switch (p_what) {
+ case NOTIFICATION_TRANSLATION_CHANGED: {
- xl_text = tr(text);
- minimum_size_changed();
- update();
- }
+ xl_text = tr(text);
+ minimum_size_changed();
+ update();
+ } break;
+ case NOTIFICATION_DRAW: {
- if (p_what == NOTIFICATION_DRAW) {
+ RID ci = get_canvas_item();
+ Size2 size = get_size();
+ Color color;
+ Color color_icon(1, 1, 1, 1);
- RID ci = get_canvas_item();
- Size2 size = get_size();
- Color color;
- Color color_icon(1, 1, 1, 1);
+ Ref<StyleBox> style = get_stylebox("normal");
- Ref<StyleBox> style = get_stylebox("normal");
+ switch (get_draw_mode()) {
+ case DRAW_NORMAL: {
- switch (get_draw_mode()) {
-
- case DRAW_NORMAL: {
+ style = get_stylebox("normal");
+ if (!flat)
+ style->draw(ci, Rect2(Point2(0, 0), size));
+ color = get_color("font_color");
+ if (has_color("icon_color_normal"))
+ color_icon = get_color("icon_color_normal");
+ } break;
+ case DRAW_HOVER_PRESSED: {
+
+ if (has_stylebox("hover_pressed") && has_stylebox_override("hover_pressed")) {
+ style = get_stylebox("hover_pressed");
+ if (!flat)
+ style->draw(ci, Rect2(Point2(0, 0), size));
+ if (has_color("font_color_hover_pressed"))
+ color = get_color("font_color_hover_pressed");
+ else
+ color = get_color("font_color");
+ if (has_color("icon_color_hover_pressed"))
+ color_icon = get_color("icon_color_hover_pressed");
+
+ break;
+ }
+ FALLTHROUGH;
+ }
+ case DRAW_PRESSED: {
- style = get_stylebox("normal");
- if (!flat)
- style->draw(ci, Rect2(Point2(0, 0), size));
- color = get_color("font_color");
- if (has_color("icon_color_normal"))
- color_icon = get_color("icon_color_normal");
- } break;
- case DRAW_HOVER_PRESSED: {
- if (has_stylebox("hover_pressed") && has_stylebox_override("hover_pressed")) {
- style = get_stylebox("hover_pressed");
+ style = get_stylebox("pressed");
if (!flat)
style->draw(ci, Rect2(Point2(0, 0), size));
- if (has_color("font_color_hover_pressed"))
- color = get_color("font_color_hover_pressed");
+ if (has_color("font_color_pressed"))
+ color = get_color("font_color_pressed");
else
color = get_color("font_color");
- if (has_color("icon_color_hover_pressed"))
- color_icon = get_color("icon_color_hover_pressed");
+ if (has_color("icon_color_pressed"))
+ color_icon = get_color("icon_color_pressed");
- break;
- }
- FALLTHROUGH;
+ } break;
+ case DRAW_HOVER: {
+
+ style = get_stylebox("hover");
+ if (!flat)
+ style->draw(ci, Rect2(Point2(0, 0), size));
+ color = get_color("font_color_hover");
+ if (has_color("icon_color_hover"))
+ color_icon = get_color("icon_color_hover");
+
+ } break;
+ case DRAW_DISABLED: {
+
+ style = get_stylebox("disabled");
+ if (!flat)
+ style->draw(ci, Rect2(Point2(0, 0), size));
+ color = get_color("font_color_disabled");
+ if (has_color("icon_color_disabled"))
+ color_icon = get_color("icon_color_disabled");
+
+ } break;
}
- case DRAW_PRESSED: {
-
- style = get_stylebox("pressed");
- if (!flat)
- style->draw(ci, Rect2(Point2(0, 0), size));
- if (has_color("font_color_pressed"))
- color = get_color("font_color_pressed");
- else
- color = get_color("font_color");
- if (has_color("icon_color_pressed"))
- color_icon = get_color("icon_color_pressed");
-
- } break;
- case DRAW_HOVER: {
-
- style = get_stylebox("hover");
- if (!flat)
- style->draw(ci, Rect2(Point2(0, 0), size));
- color = get_color("font_color_hover");
- if (has_color("icon_color_hover"))
- color_icon = get_color("icon_color_hover");
-
- } break;
- case DRAW_DISABLED: {
-
- style = get_stylebox("disabled");
- if (!flat)
- style->draw(ci, Rect2(Point2(0, 0), size));
- color = get_color("font_color_disabled");
- if (has_color("icon_color_disabled"))
- color_icon = get_color("icon_color_disabled");
-
- } break;
- }
- if (has_focus()) {
+ if (has_focus()) {
- Ref<StyleBox> style2 = get_stylebox("focus");
- style2->draw(ci, Rect2(Point2(), size));
- }
+ Ref<StyleBox> style2 = get_stylebox("focus");
+ style2->draw(ci, Rect2(Point2(), size));
+ }
- Ref<Font> font = get_font("font");
- Ref<Texture> _icon;
- if (icon.is_null() && has_icon("icon"))
- _icon = Control::get_icon("icon");
- else
- _icon = icon;
+ Ref<Font> font = get_font("font");
+ Ref<Texture> _icon;
+ if (icon.is_null() && has_icon("icon"))
+ _icon = Control::get_icon("icon");
+ else
+ _icon = icon;
+
+ Rect2 icon_region = Rect2();
+ if (!_icon.is_null()) {
- Point2 icon_ofs = (!_icon.is_null()) ? Point2(_icon->get_width() + get_constant("hseparation"), 0) : Point2();
- int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width;
- Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size(xl_text) - Point2(_internal_margin[MARGIN_RIGHT] - _internal_margin[MARGIN_LEFT], 0)) / 2.0;
-
- switch (align) {
- case ALIGN_LEFT: {
- text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x + _internal_margin[MARGIN_LEFT] + get_constant("hseparation");
- text_ofs.y += style->get_offset().y;
- } break;
- case ALIGN_CENTER: {
- if (text_ofs.x < 0)
- text_ofs.x = 0;
- text_ofs += icon_ofs;
- text_ofs += style->get_offset();
- } break;
- case ALIGN_RIGHT: {
- if (_internal_margin[MARGIN_RIGHT] > 0) {
- text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x - _internal_margin[MARGIN_RIGHT] - get_constant("hseparation");
+ int valign = size.height - style->get_minimum_size().y;
+ if (is_disabled()) {
+ color_icon.a = 0.4;
+ }
+
+ float icon_ofs_region = 0;
+ if (_internal_margin[MARGIN_LEFT] > 0) {
+ icon_ofs_region = _internal_margin[MARGIN_LEFT] + get_constant("hseparation");
+ }
+
+ if (expand_icon) {
+ Size2 _size = get_size() - style->get_offset() * 2;
+ _size.width -= get_constant("hseparation") + icon_ofs_region;
+ if (!clip_text)
+ _size.width -= get_font("font")->get_string_size(xl_text).width;
+ float icon_width = icon->get_width() * _size.height / icon->get_height();
+ float icon_height = _size.height;
+
+ if (icon_width > _size.width) {
+ icon_width = _size.width;
+ icon_height = icon->get_height() * icon_width / icon->get_width();
+ }
+
+ icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, (_size.height - icon_height) / 2), Size2(icon_width, icon_height));
} else {
- text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x;
+ icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, Math::floor((valign - _icon->get_height()) / 2.0)), icon->get_size());
}
- text_ofs.y += style->get_offset().y;
- } break;
- }
+ }
- text_ofs.y += font->get_ascent();
- font->draw(ci, text_ofs.floor(), xl_text, color, clip_text ? text_clip : -1);
- if (!_icon.is_null()) {
+ Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_constant("hseparation"), 0) : Point2();
+ int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width;
+ Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size(xl_text) - Point2(_internal_margin[MARGIN_RIGHT] - _internal_margin[MARGIN_LEFT], 0)) / 2.0;
+
+ switch (align) {
+ case ALIGN_LEFT: {
+ if (_internal_margin[MARGIN_LEFT] > 0) {
+ text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x + _internal_margin[MARGIN_LEFT] + get_constant("hseparation");
+ } else {
+ text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x;
+ }
+ text_ofs.y += style->get_offset().y;
+ } break;
+ case ALIGN_CENTER: {
+ if (text_ofs.x < 0)
+ text_ofs.x = 0;
+ text_ofs += icon_ofs;
+ text_ofs += style->get_offset();
+ } break;
+ case ALIGN_RIGHT: {
+ if (_internal_margin[MARGIN_RIGHT] > 0) {
+ text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x - _internal_margin[MARGIN_RIGHT] - get_constant("hseparation");
+ } else {
+ text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x;
+ }
+ text_ofs.y += style->get_offset().y;
+ } break;
+ }
+
+ text_ofs.y += font->get_ascent();
+ font->draw(ci, text_ofs.floor(), xl_text, color, clip_text ? text_clip : -1);
- int valign = size.height - style->get_minimum_size().y;
- if (is_disabled())
- color_icon.a = 0.4;
- if (_internal_margin[MARGIN_LEFT] > 0) {
- _icon->draw(ci, style->get_offset() + Point2(_internal_margin[MARGIN_LEFT] + get_constant("hseparation"), Math::floor((valign - _icon->get_height()) / 2.0)), color_icon);
- } else {
- _icon->draw(ci, style->get_offset() + Point2(0, Math::floor((valign - _icon->get_height()) / 2.0)), color_icon);
+ if (!_icon.is_null() && icon_region.size.width > 0) {
+ draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), icon->get_size()), color_icon);
}
- }
+ } break;
}
}
@@ -224,6 +257,18 @@ Ref<Texture> Button::get_icon() const {
return icon;
}
+void Button::set_expand_icon(bool p_expand_icon) {
+
+ expand_icon = p_expand_icon;
+ update();
+ minimum_size_changed();
+}
+
+bool Button::is_expand_icon() const {
+
+ return expand_icon;
+}
+
void Button::set_flat(bool p_flat) {
flat = p_flat;
@@ -265,6 +310,8 @@ void Button::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text"), &Button::get_text);
ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_icon);
ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_icon);
+ ClassDB::bind_method(D_METHOD("set_expand_icon"), &Button::set_expand_icon);
+ ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon);
ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &Button::set_flat);
ClassDB::bind_method(D_METHOD("set_clip_text", "enabled"), &Button::set_clip_text);
ClassDB::bind_method(D_METHOD("get_clip_text"), &Button::get_clip_text);
@@ -281,12 +328,14 @@ void Button::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_align", "get_text_align");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon");
}
Button::Button(const String &p_text) {
flat = false;
clip_text = false;
+ expand_icon = false;
set_mouse_filter(MOUSE_FILTER_STOP);
set_text(p_text);
align = ALIGN_CENTER;
diff --git a/scene/gui/button.h b/scene/gui/button.h
index 6ba3475e5a..1fff2cfda7 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -32,9 +32,6 @@
#define BUTTON_H
#include "scene/gui/base_button.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
class Button : public BaseButton {
@@ -52,6 +49,7 @@ private:
String text;
String xl_text;
Ref<Texture> icon;
+ bool expand_icon;
bool clip_text;
TextAlign align;
float _internal_margin[4];
@@ -62,8 +60,6 @@ protected:
static void _bind_methods();
public:
- //
-
virtual Size2 get_minimum_size() const;
void set_text(const String &p_text);
@@ -72,6 +68,9 @@ public:
void set_icon(const Ref<Texture> &p_icon);
Ref<Texture> get_icon() const;
+ void set_expand_icon(bool p_expand_icon);
+ bool is_expand_icon() const;
+
void set_flat(bool p_flat);
bool is_flat() const;
diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp
index 1d8b74d9db..8744407763 100644
--- a/scene/gui/check_box.cpp
+++ b/scene/gui/check_box.cpp
@@ -79,7 +79,7 @@ void CheckBox::_notification(int p_what) {
Vector2 ofs;
ofs.x = sb->get_margin(MARGIN_LEFT);
- ofs.y = int((get_size().height - get_icon_size().height) / 2);
+ ofs.y = int((get_size().height - get_icon_size().height) / 2) + get_constant("check_vadjust");
if (is_pressed())
on->draw(ci, ofs);
diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp
index a2d0f388c4..f47547f2cc 100644
--- a/scene/gui/check_button.cpp
+++ b/scene/gui/check_button.cpp
@@ -76,7 +76,7 @@ void CheckButton::_notification(int p_what) {
Size2 tex_size = get_icon_size();
ofs.x = get_size().width - (tex_size.width + sb->get_margin(MARGIN_RIGHT));
- ofs.y = (get_size().height - tex_size.height) / 2;
+ ofs.y = (get_size().height - tex_size.height) / 2 + get_constant("check_vadjust");
if (is_pressed())
on->draw(ci, ofs);
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index b197971b61..6dd9e401f6 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -247,15 +247,19 @@ void ColorPicker::_update_color(bool p_update_sliders) {
}
void ColorPicker::_update_presets() {
+ presets_per_row = 10;
Size2 size = bt_add_preset->get_size();
- Size2 preset_size = Size2(size.width * presets.size(), size.height);
+ Size2 preset_size = Size2(MIN(size.width * presets.size(), presets_per_row * size.width), size.height * (Math::ceil((float)presets.size() / presets_per_row)));
preset->set_custom_minimum_size(preset_size);
-
- preset->draw_texture_rect(get_icon("preset_bg", "ColorPicker"), Rect2(Point2(), preset_size), true);
+ preset_container->set_custom_minimum_size(preset_size);
+ preset->draw_rect(Rect2(Point2(), preset_size), Color(1, 1, 1, 0));
for (int i = 0; i < presets.size(); i++) {
- preset->draw_rect(Rect2(Point2(size.width * i, 0), size), presets[i]);
+ int x = (i % presets_per_row) * size.width;
+ int y = (Math::floor((float)i / presets_per_row)) * size.height;
+ preset->draw_rect(Rect2(Point2(x, y), size), presets[i]);
}
+ _notification(NOTIFICATION_VISIBILITY_CHANGED);
}
void ColorPicker::_text_type_toggled() {
@@ -288,8 +292,6 @@ void ColorPicker::add_preset(const Color &p_color) {
presets.push_back(p_color);
}
preset->update();
- if (presets.size() == 10)
- bt_add_preset->hide();
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
@@ -533,14 +535,20 @@ void ColorPicker::_preset_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> bev = p_event;
if (bev.is_valid()) {
-
+ int index = 0;
if (bev->is_pressed() && bev->get_button_index() == BUTTON_LEFT) {
- int index = bev->get_position().x / (preset->get_size().x / presets.size());
+ for (int i = 0; i < presets.size(); i++) {
+ int x = (i % presets_per_row) * bt_add_preset->get_size().x;
+ int y = (Math::floor((float)i / presets_per_row)) * bt_add_preset->get_size().y;
+ if (bev->get_position().x > x && bev->get_position().x < x + preset->get_size().x && bev->get_position().y > y && bev->get_position().y < y + preset->get_size().y) {
+ index = i;
+ }
+ }
set_pick_color(presets[index]);
_update_color();
emit_signal("color_changed", color);
} else if (bev->is_pressed() && bev->get_button_index() == BUTTON_RIGHT && presets_enabled) {
- int index = bev->get_position().x / (preset->get_size().x / presets.size());
+ index = bev->get_position().x / (preset->get_size().x / presets.size());
Color clicked_preset = presets[index];
erase_preset(clicked_preset);
emit_signal("preset_removed", clicked_preset);
@@ -841,6 +849,7 @@ ColorPicker::ColorPicker() :
add_child(preset_separator);
preset_container = memnew(HBoxContainer);
+ preset_container->set_h_size_flags(SIZE_EXPAND_FILL);
add_child(preset_container);
preset = memnew(TextureRect);
@@ -848,8 +857,11 @@ ColorPicker::ColorPicker() :
preset->connect("gui_input", this, "_preset_input");
preset->connect("draw", this, "_update_presets");
+ preset_container2 = memnew(HBoxContainer);
+ preset_container2->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_child(preset_container2);
bt_add_preset = memnew(Button);
- preset_container->add_child(bt_add_preset);
+ preset_container2->add_child(bt_add_preset);
bt_add_preset->set_tooltip(TTR("Add current color as a preset."));
bt_add_preset->connect("pressed", this, "_add_preset_pressed");
}
@@ -952,6 +964,7 @@ void ColorPickerButton::_update_picker() {
popup->connect("popup_hide", this, "set_pressed", varray(false));
picker->set_pick_color(color);
picker->set_edit_alpha(edit_alpha);
+ emit_signal("picker_created");
}
}
@@ -968,6 +981,7 @@ void ColorPickerButton::_bind_methods() {
ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));
ADD_SIGNAL(MethodInfo("popup_closed"));
+ ADD_SIGNAL(MethodInfo("picker_created"));
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");
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 3af27a9856..167f7b33b3 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -54,6 +54,7 @@ private:
TextureRect *sample;
TextureRect *preset;
HBoxContainer *preset_container;
+ HBoxContainer *preset_container2;
HSeparator *preset_separator;
Button *bt_add_preset;
List<Color> presets;
@@ -68,6 +69,7 @@ private:
bool edit_alpha;
Size2i ms;
bool text_is_constructor;
+ int presets_per_row;
Color color;
bool raw_mode_enabled;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 26be17f6c1..d39f017cad 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -289,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) {
- set_size(data.size_cache);
+ _size_changed();
}
data.updating_last_minimum_size = false;
@@ -818,7 +818,7 @@ Size2 Control::get_minimum_size() const {
Ref<Texture> Control::get_icon(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == "") {
+ if (p_type == StringName() || p_type == get_class_name()) {
const Ref<Texture> *tex = data.icon_override.getptr(p_name);
if (tex)
@@ -850,11 +850,17 @@ Ref<Texture> Control::get_icon(const StringName &p_name, const StringName &p_typ
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_icon(p_name, type)) {
+ return Theme::get_project_default()->get_icon(p_name, type);
+ }
+ }
+
return Theme::get_default()->get_icon(p_name, type);
}
Ref<Shader> Control::get_shader(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == "") {
+ if (p_type == StringName() || p_type == get_class_name()) {
const Ref<Shader> *sdr = data.shader_override.getptr(p_name);
if (sdr)
@@ -886,12 +892,18 @@ Ref<Shader> Control::get_shader(const StringName &p_name, const StringName &p_ty
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_shader(p_name, type)) {
+ return Theme::get_project_default()->get_shader(p_name, type);
+ }
+ }
+
return Theme::get_default()->get_shader(p_name, type);
}
Ref<StyleBox> Control::get_stylebox(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == "") {
+ if (p_type == StringName() || p_type == get_class_name()) {
const Ref<StyleBox> *style = data.style_override.getptr(p_name);
if (style)
return *style;
@@ -925,6 +937,9 @@ Ref<StyleBox> Control::get_stylebox(const StringName &p_name, const StringName &
}
while (class_name != StringName()) {
+ if (Theme::get_project_default().is_valid() && Theme::get_project_default()->has_stylebox(p_name, type))
+ return Theme::get_project_default()->get_stylebox(p_name, type);
+
if (Theme::get_default()->has_stylebox(p_name, class_name))
return Theme::get_default()->get_stylebox(p_name, class_name);
@@ -934,7 +949,7 @@ Ref<StyleBox> Control::get_stylebox(const StringName &p_name, const StringName &
}
Ref<Font> Control::get_font(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == "") {
+ if (p_type == StringName() || p_type == get_class_name()) {
const Ref<Font> *font = data.font_override.getptr(p_name);
if (font)
return *font;
@@ -971,7 +986,7 @@ Ref<Font> Control::get_font(const StringName &p_name, const StringName &p_type)
}
Color Control::get_color(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == "") {
+ if (p_type == StringName() || p_type == get_class_name()) {
const Color *color = data.color_override.getptr(p_name);
if (color)
return *color;
@@ -1001,12 +1016,17 @@ Color Control::get_color(const StringName &p_name, const StringName &p_type) con
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_color(p_name, type)) {
+ return Theme::get_project_default()->get_color(p_name, type);
+ }
+ }
return Theme::get_default()->get_color(p_name, type);
}
int Control::get_constant(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == "") {
+ if (p_type == StringName() || p_type == get_class_name()) {
const int *constant = data.constant_override.getptr(p_name);
if (constant)
return *constant;
@@ -1036,6 +1056,11 @@ int Control::get_constant(const StringName &p_name, const StringName &p_type) co
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_constant(p_name, type)) {
+ return Theme::get_project_default()->get_constant(p_name, type);
+ }
+ }
return Theme::get_default()->get_constant(p_name, type);
}
@@ -1077,7 +1102,7 @@ bool Control::has_constant_override(const StringName &p_name) const {
bool Control::has_icon(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == "") {
+ if (p_type == StringName() || p_type == get_class_name()) {
if (has_icon_override(p_name))
return true;
}
@@ -1106,12 +1131,17 @@ bool Control::has_icon(const StringName &p_name, const StringName &p_type) const
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_color(p_name, type)) {
+ return true;
+ }
+ }
return Theme::get_default()->has_icon(p_name, type);
}
bool Control::has_shader(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == "") {
+ if (p_type == StringName() || p_type == get_class_name()) {
if (has_shader_override(p_name))
return true;
}
@@ -1140,11 +1170,16 @@ bool Control::has_shader(const StringName &p_name, const StringName &p_type) con
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_shader(p_name, type)) {
+ return true;
+ }
+ }
return Theme::get_default()->has_shader(p_name, type);
}
bool Control::has_stylebox(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == "") {
+ if (p_type == StringName() || p_type == get_class_name()) {
if (has_stylebox_override(p_name))
return true;
}
@@ -1173,11 +1208,16 @@ bool Control::has_stylebox(const StringName &p_name, const StringName &p_type) c
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_stylebox(p_name, type)) {
+ return true;
+ }
+ }
return Theme::get_default()->has_stylebox(p_name, type);
}
bool Control::has_font(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == "") {
+ if (p_type == StringName() || p_type == get_class_name()) {
if (has_font_override(p_name))
return true;
}
@@ -1206,12 +1246,17 @@ bool Control::has_font(const StringName &p_name, const StringName &p_type) const
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_font(p_name, type)) {
+ return true;
+ }
+ }
return Theme::get_default()->has_font(p_name, type);
}
bool Control::has_color(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == "") {
+ if (p_type == StringName() || p_type == get_class_name()) {
if (has_color_override(p_name))
return true;
}
@@ -1240,12 +1285,17 @@ bool Control::has_color(const StringName &p_name, const StringName &p_type) cons
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_color(p_name, type)) {
+ return true;
+ }
+ }
return Theme::get_default()->has_color(p_name, type);
}
bool Control::has_constant(const StringName &p_name, const StringName &p_type) const {
- if (p_type == StringName() || p_type == "") {
+ if (p_type == StringName() || p_type == get_class_name()) {
if (has_constant_override(p_name))
return true;
}
@@ -1274,6 +1324,11 @@ bool Control::has_constant(const StringName &p_name, const StringName &p_type) c
theme_owner = NULL;
}
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_constant(p_name, type)) {
+ return true;
+ }
+ }
return Theme::get_default()->has_constant(p_name, type);
}
@@ -1717,17 +1772,6 @@ void Control::set_global_position(const Point2 &p_point, bool p_keep_margins) {
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 {
-
- Rect2 anchorable = get_parent_anchorable_rect();
- Rect2 result = anchorable;
- for (int i = 0; i < 4; i++) {
- result.grow_margin((Margin)i, p_anchors[i] * anchorable.get_size()[i % 2] + p_margins[i]);
- }
-
- 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;
@@ -1951,12 +1995,7 @@ Control *Control::find_next_valid_focus() const {
Node *n = get_node(data.focus_next);
if (n) {
from = Object::cast_to<Control>(n);
-
- if (!from) {
-
- ERR_EXPLAIN("Next focus node is not a control: " + n->get_name());
- ERR_FAIL_V(NULL);
- }
+ ERR_FAIL_COND_V_MSG(!from, NULL, "Next focus node is not a control: " + n->get_name() + ".");
} else {
return NULL;
}
@@ -2046,12 +2085,7 @@ Control *Control::find_prev_valid_focus() const {
Node *n = get_node(data.focus_prev);
if (n) {
from = Object::cast_to<Control>(n);
-
- if (!from) {
-
- ERR_EXPLAIN("Previous focus node is not a control: " + n->get_name());
- ERR_FAIL_V(NULL);
- }
+ ERR_FAIL_COND_V_MSG(!from, NULL, "Previous focus node is not a control: " + n->get_name() + ".");
} else {
return NULL;
}
@@ -2115,9 +2149,7 @@ bool Control::has_focus() const {
void Control::grab_focus() {
- if (!is_inside_tree()) {
- ERR_FAIL_COND(!is_inside_tree());
- }
+ ERR_FAIL_COND(!is_inside_tree());
if (data.focus_mode == FOCUS_NONE) {
WARN_PRINT("This control can't grab focus. Use set_focus_mode() to allow a control to get focus.");
@@ -2333,12 +2365,7 @@ Control *Control::_get_focus_neighbour(Margin p_margin, int p_count) {
Node *n = get_node(data.focus_neighbour[p_margin]);
if (n) {
c = Object::cast_to<Control>(n);
-
- if (!c) {
-
- ERR_EXPLAIN("Neighbour focus node is not a control: " + n->get_name());
- ERR_FAIL_V(NULL);
- }
+ ERR_FAIL_COND_V_MSG(!c, NULL, "Neighbor focus node is not a control: " + n->get_name() + ".");
} else {
return NULL;
}
@@ -2889,17 +2916,21 @@ void Control::_bind_methods() {
BIND_VMETHOD(MethodInfo("_gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
BIND_VMETHOD(MethodInfo(Variant::VECTOR2, "_get_minimum_size"));
- BIND_VMETHOD(MethodInfo(Variant::OBJECT, "get_drag_data", PropertyInfo(Variant::VECTOR2, "position")));
+
+ MethodInfo get_drag_data = MethodInfo("get_drag_data", PropertyInfo(Variant::VECTOR2, "position"));
+ get_drag_data.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ BIND_VMETHOD(get_drag_data);
+
BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data")));
BIND_VMETHOD(MethodInfo("drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data")));
BIND_VMETHOD(MethodInfo(Variant::OBJECT, "_make_custom_tooltip", PropertyInfo(Variant::STRING, "for_text")));
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,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_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", MARGIN_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.001,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);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 1a59a6d2e4..7305b3ce93 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -38,9 +38,6 @@
#include "scene/main/node.h"
#include "scene/main/timer.h"
#include "scene/resources/theme.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
class Viewport;
class Label;
@@ -230,7 +227,6 @@ private:
void _update_scroll();
void _resize(const Size2 &p_size);
- Rect2 _compute_child_rect(const float p_anchors[4], const float p_margins[4]) const;
void _compute_margins(Rect2 p_rect, const float p_anchors[4], float (&r_margins)[4]);
void _compute_anchors(Rect2 p_rect, const float p_margins[4], float (&r_anchors)[4]);
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index 4da11b671e..9ed1d2bf45 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -35,6 +35,7 @@
#ifdef TOOLS_ENABLED
#include "editor/editor_node.h"
+#include "scene/main/viewport.h" // Only used to check for more modals when dimming the editor.
#endif
// WindowDialog
@@ -59,7 +60,7 @@ void WindowDialog::_fix_size() {
float left = 0;
float bottom = 0;
float right = 0;
- // Check validity, because the theme could contain a different type of StyleBox
+ // Check validity, because the theme could contain a different type of StyleBox.
if (panel->get_class() == "StyleBoxTexture") {
Ref<StyleBoxTexture> panel_texture = Object::cast_to<StyleBoxTexture>(*panel);
top = panel_texture->get_expand_margin_size(MARGIN_TOP);
@@ -219,6 +220,14 @@ void WindowDialog::_notification(int p_what) {
close_button->set_begin(Point2(-get_constant("close_h_ofs", "WindowDialog"), -get_constant("close_v_ofs", "WindowDialog")));
} break;
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ String new_title = tr(title);
+ if (title != new_title) {
+ title = new_title;
+ update();
+ }
+ } break;
+
case NOTIFICATION_MOUSE_EXIT: {
// Reset the mouse cursor when leaving the resizable window border.
if (resizable && !drag_type) {
@@ -226,13 +235,15 @@ void WindowDialog::_notification(int p_what) {
set_default_cursor_shape(CURSOR_ARROW);
}
} break;
+
#ifdef TOOLS_ENABLED
case NOTIFICATION_POST_POPUP: {
if (get_tree() && Engine::get_singleton()->is_editor_hint() && EditorNode::get_singleton())
EditorNode::get_singleton()->dim_editor(true);
} break;
+
case NOTIFICATION_POPUP_HIDE: {
- if (get_tree() && Engine::get_singleton()->is_editor_hint() && EditorNode::get_singleton())
+ if (get_tree() && Engine::get_singleton()->is_editor_hint() && EditorNode::get_singleton() && !get_viewport()->gui_has_modal_stack())
EditorNode::get_singleton()->dim_editor(false);
} break;
#endif
@@ -272,8 +283,11 @@ int WindowDialog::_drag_hit_test(const Point2 &pos) const {
void WindowDialog::set_title(const String &p_title) {
- title = tr(p_title);
- update();
+ String new_title = tr(p_title);
+ if (title != new_title) {
+ title = new_title;
+ update();
+ }
}
String WindowDialog::get_title() const {
@@ -296,8 +310,8 @@ Size2 WindowDialog::get_minimum_size() const {
const int padding = button_width / 2;
const int button_area = button_width + padding;
- // as the title gets centered, title_width + close_button_width is not enough.
- // we want a width w, such that w / 2 - title_width / 2 >= button_area, i.e.
+ // As the title gets centered, title_width + close_button_width is not enough.
+ // We want a width w, such that w / 2 - title_width / 2 >= button_area, i.e.
// w >= 2 * button_area + title_width
return Size2(2 * button_area + title_width, 1);
@@ -324,7 +338,6 @@ void WindowDialog::_bind_methods() {
WindowDialog::WindowDialog() {
- //title="Hello!";
drag_type = DRAG_NONE;
resizable = false;
close_button = memnew(TextureButton);
@@ -340,7 +353,6 @@ WindowDialog::~WindowDialog() {
void PopupDialog::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
-
RID ci = get_canvas_item();
get_stylebox("panel", "PopupMenu")->draw(ci, Rect2(Point2(), get_size()));
}
@@ -362,15 +374,15 @@ void AcceptDialog::_post_popup() {
void AcceptDialog::_notification(int p_what) {
- if (p_what == NOTIFICATION_MODAL_CLOSE) {
-
- cancel_pressed();
- } else if (p_what == NOTIFICATION_READY) {
-
- _update_child_rects();
- } else if (p_what == NOTIFICATION_RESIZED) {
+ switch (p_what) {
+ case NOTIFICATION_MODAL_CLOSE: {
+ cancel_pressed();
+ } break;
- _update_child_rects();
+ case NOTIFICATION_READY:
+ case NOTIFICATION_RESIZED: {
+ _update_child_rects();
+ } break;
}
}
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index c1a7f26a85..1a0350ba18 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -37,9 +37,6 @@
#include "scene/gui/panel.h"
#include "scene/gui/popup.h"
#include "scene/gui/texture_button.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
class WindowDialog : public Popup {
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 04fb991f78..9bc593ea3b 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -400,14 +400,14 @@ void FileDialog::update_file_list() {
TreeItem *root = tree->create_item();
Ref<Texture> folder = get_icon("folder");
+ const Color folder_color = get_color("folder_icon_modulate");
List<String> files;
List<String> dirs;
- bool is_dir;
bool is_hidden;
String item;
- while ((item = dir_access->get_next(&is_dir)) != "") {
+ while ((item = dir_access->get_next()) != "") {
if (item == "." || item == "..")
continue;
@@ -415,7 +415,7 @@ void FileDialog::update_file_list() {
is_hidden = dir_access->current_is_hidden();
if (show_hidden_files || !is_hidden) {
- if (!is_dir)
+ if (!dir_access->current_is_dir())
files.push_back(item);
else
dirs.push_back(item);
@@ -430,6 +430,7 @@ void FileDialog::update_file_list() {
TreeItem *ti = tree->create_item(root);
ti->set_text(0, dir_name);
ti->set_icon(0, folder);
+ ti->set_icon_modulate(0, folder_color);
Dictionary d;
d["name"] = dir_name;
@@ -880,14 +881,14 @@ FileDialog::FileDialog() {
dir->set_h_size_flags(SIZE_EXPAND_FILL);
refresh = memnew(ToolButton);
- refresh->set_tooltip(RTR("Refresh"));
+ refresh->set_tooltip(RTR("Refresh files."));
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->set_tooltip(RTR("Toggle the visibility of hidden files."));
show_hidden->connect("toggled", this, "set_show_hidden_files");
hbc->add_child(show_hidden);
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 191af5fef3..4fd6d0d13c 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -38,9 +38,7 @@
#include "scene/gui/option_button.h"
#include "scene/gui/tool_button.h"
#include "scene/gui/tree.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
+
class FileDialog : public ConfirmationDialog {
GDCLASS(FileDialog, ConfirmationDialog);
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index 75f5f79873..09ef6f26bf 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -241,9 +241,13 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) {
float newofs = CLAMP(x / float(total_w), 0, 1);
- //Snap to nearest point if holding shift
- if (mm->get_shift()) {
- float snap_threshold = 0.03;
+ // Snap to "round" coordinates if holding Ctrl.
+ // Be more precise if holding Shift as well
+ if (mm->get_control()) {
+ newofs = Math::stepify(newofs, mm->get_shift() ? 0.025 : 0.1);
+ } else if (mm->get_shift()) {
+ // Snap to nearest point if holding just Shift
+ const float snap_threshold = 0.03;
float smallest_ofs = snap_threshold;
bool found = false;
int nearest_point = 0;
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index f238aeb392..7827c66841 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -276,6 +276,11 @@ void GraphEdit::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
port_grab_distance_horizontal = get_constant("port_grab_distance_horizontal");
port_grab_distance_vertical = get_constant("port_grab_distance_vertical");
+
+ zoom_minus->set_icon(get_icon("minus"));
+ zoom_reset->set_icon(get_icon("reset"));
+ zoom_plus->set_icon(get_icon("more"));
+ snap_button->set_icon(get_icon("snap"));
}
if (p_what == NOTIFICATION_READY) {
Size2 hmin = h_scroll->get_combined_minimum_size();
@@ -290,11 +295,6 @@ void GraphEdit::_notification(int p_what) {
h_scroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0);
h_scroll->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -hmin.height);
h_scroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0);
-
- zoom_minus->set_icon(get_icon("minus"));
- zoom_reset->set_icon(get_icon("reset"));
- zoom_plus->set_icon(get_icon("more"));
- snap_button->set_icon(get_icon("snap"));
}
if (p_what == NOTIFICATION_DRAW) {
@@ -776,10 +776,16 @@ void GraphEdit::_top_layer_draw() {
_draw_cos_line(top_layer, pos, topos, col, col);
}
- if (box_selecting)
+ if (box_selecting) {
top_layer->draw_rect(
box_selecting_rect,
- get_color("accent_color", "Editor") * Color(1, 1, 1, 0.375));
+ get_color("box_selection_fill_color", "Editor"));
+
+ top_layer->draw_rect(
+ box_selecting_rect,
+ get_color("box_selection_stroke_color", "Editor"),
+ false);
+ }
}
void GraphEdit::set_selected(Node *p_child) {
@@ -1030,14 +1036,28 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
}
Ref<InputEventKey> k = p_ev;
- if (k.is_valid() && k->get_scancode() == KEY_D && k->is_pressed() && k->get_command()) {
- emit_signal("duplicate_nodes_request");
- accept_event();
- }
- if (k.is_valid() && k->get_scancode() == KEY_DELETE && k->is_pressed()) {
- emit_signal("delete_nodes_request");
- accept_event();
+ if (k.is_valid()) {
+
+ if (k->get_scancode() == KEY_D && k->is_pressed() && k->get_command()) {
+ emit_signal("duplicate_nodes_request");
+ accept_event();
+ }
+
+ if (k->get_scancode() == KEY_C && k->is_pressed() && k->get_command()) {
+ emit_signal("copy_nodes_request");
+ accept_event();
+ }
+
+ if (k->get_scancode() == KEY_V && k->is_pressed() && k->get_command()) {
+ emit_signal("paste_nodes_request");
+ accept_event();
+ }
+
+ if (k->get_scancode() == KEY_DELETE && k->is_pressed()) {
+ emit_signal("delete_nodes_request");
+ accept_event();
+ }
}
Ref<InputEventMagnifyGesture> magnify_gesture = p_ev;
@@ -1297,6 +1317,8 @@ void GraphEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING, "to"), PropertyInfo(Variant::INT, "to_slot")));
ADD_SIGNAL(MethodInfo("popup_request", PropertyInfo(Variant::VECTOR2, "position")));
ADD_SIGNAL(MethodInfo("duplicate_nodes_request"));
+ ADD_SIGNAL(MethodInfo("copy_nodes_request"));
+ ADD_SIGNAL(MethodInfo("paste_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")));
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index 6463ee5ad5..5b2f8812d5 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -210,6 +210,7 @@ void GraphNode::_notification(int p_what) {
int close_offset = get_constant("close_offset");
int close_h_offset = get_constant("close_h_offset");
Color close_color = get_color("close_color");
+ Color resizer_color = get_color("resizer_color");
Ref<Font> title_font = get_font("title_font");
int title_offset = get_constant("title_offset");
int title_h_offset = get_constant("title_h_offset");
@@ -274,7 +275,7 @@ void GraphNode::_notification(int p_what) {
}
if (resizable) {
- draw_texture(resizer, get_size() - resizer->get_size());
+ draw_texture(resizer, get_size() - resizer->get_size(), resizer_color);
}
} break;
@@ -592,13 +593,14 @@ void GraphNode::_gui_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventMouseButton> mb = p_ev;
if (mb.is_valid()) {
- ERR_EXPLAIN("GraphNode must be the child of a GraphEdit node.");
- ERR_FAIL_COND(get_parent_control() == NULL);
+ ERR_FAIL_COND_MSG(get_parent_control() == NULL, "GraphNode must be the child of a GraphEdit node.");
if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
Vector2 mpos = Vector2(mb->get_position().x, mb->get_position().y);
if (close_rect.size != Size2() && close_rect.has_point(mpos)) {
+ //send focus to parent
+ get_parent_control()->grab_focus();
emit_signal("close_request");
accept_event();
return;
@@ -615,9 +617,7 @@ void GraphNode::_gui_input(const Ref<InputEvent> &p_ev) {
return;
}
- //send focus to parent
emit_signal("raise_request");
- get_parent_control()->grab_focus();
}
if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
diff --git a/scene/gui/label.h b/scene/gui/label.h
index 561c42ef9e..2cc55a47ef 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -32,9 +32,7 @@
#define LABEL_H
#include "scene/gui/control.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
+
class Label : public Control {
GDCLASS(Label, Control);
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 3dcbf64e7c..04e03f569e 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -59,6 +59,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
menu->set_scale(get_global_transform().get_scale());
menu->popup();
grab_focus();
+ accept_event();
return;
}
@@ -86,7 +87,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
} else {
- if (b->is_doubleclick()) {
+ if (b->is_doubleclick() && selecting_enabled) {
selection.enabled = true;
selection.begin = 0;
@@ -194,13 +195,13 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
unsigned int code = k->get_scancode();
- if (k->get_command()) {
+ if (k->get_command() && is_shortcut_keys_enabled()) {
bool handled = true;
switch (code) {
- case (KEY_X): { // CUT
+ case (KEY_X): { // CUT.
if (editable) {
cut_text();
@@ -208,13 +209,13 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
} break;
- case (KEY_C): { // COPY
+ case (KEY_C): { // COPY.
copy_text();
} break;
- case (KEY_V): { // PASTE
+ case (KEY_V): { // PASTE.
if (editable) {
@@ -223,7 +224,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
} break;
- case (KEY_Z): { // undo / redo
+ case (KEY_Z): { // Undo/redo.
if (editable) {
if (k->get_shift()) {
redo();
@@ -233,7 +234,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
}
} break;
- case (KEY_U): { // Delete from start to cursor
+ case (KEY_U): { // Delete from start to cursor.
if (editable) {
@@ -254,7 +255,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
} break;
- case (KEY_Y): { // PASTE (Yank for unix users)
+ case (KEY_Y): { // PASTE (Yank for unix users).
if (editable) {
@@ -262,7 +263,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
}
} break;
- case (KEY_K): { // Delete from cursor_pos to end
+ case (KEY_K): { // Delete from cursor_pos to end.
if (editable) {
@@ -272,14 +273,15 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
}
} break;
- case (KEY_A): { //Select All
+ case (KEY_A): { // Select all.
select();
+
} break;
#ifdef APPLE_STYLE_KEYS
- case (KEY_LEFT): { // Go to start of text - like HOME key
+ case (KEY_LEFT): { // Go to start of text - like HOME key.
set_cursor_position(0);
} break;
- case (KEY_RIGHT): { // Go to end of text - like END key
+ case (KEY_RIGHT): { // Go to end of text - like END key.
set_cursor_position(text.length());
} break;
#endif
@@ -472,7 +474,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
int text_len = text.length();
if (cursor_pos == text_len)
- break; // nothing to do
+ break; // Nothing to do.
#ifdef APPLE_STYLE_KEYS
if (k->get_alt()) {
@@ -530,6 +532,16 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
set_cursor_position(text.length());
shift_selection_check_post(k->get_shift());
} break;
+ case KEY_MENU: {
+ if (context_menu_enabled) {
+ Point2 pos = Point2(get_cursor_pixel_pos(), (get_size().y + get_font("font")->get_height()) / 2);
+ menu->set_position(get_global_transform().xform(pos));
+ menu->set_size(Vector2(1, 1));
+ menu->set_scale(get_global_transform().get_scale());
+ menu->popup();
+ menu->grab_focus();
+ }
+ } break;
default: {
@@ -729,7 +741,7 @@ void LineEdit::_notification(int p_what) {
Color cursor_color = get_color("cursor_color");
const String &t = using_placeholder ? placeholder_translated : text;
- // draw placeholder color
+ // Draw placeholder color.
if (using_placeholder)
font_color.a *= placeholder_alpha;
@@ -744,24 +756,28 @@ void LineEdit::_notification(int p_what) {
color_icon = get_color("clear_button_color");
}
}
- r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon);
+
+ float icon_width = MIN(r_icon->get_width(), r_icon->get_width() * (height - (style->get_margin(MARGIN_TOP) + style->get_margin(MARGIN_BOTTOM))) / r_icon->get_height());
+ float icon_height = MIN(r_icon->get_height(), height - (style->get_margin(MARGIN_TOP) + style->get_margin(MARGIN_BOTTOM)));
+ Rect2 icon_region = Rect2(Point2(width - icon_width - style->get_margin(MARGIN_RIGHT), height / 2 - icon_height / 2), Size2(icon_width, icon_height));
+ draw_texture_rect_region(r_icon, icon_region, Rect2(Point2(), r_icon->get_size()), color_icon);
if (align == ALIGN_CENTER) {
if (window_pos == 0) {
- x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - cached_text_width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT) * 2) / 2);
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - cached_text_width - icon_width - style->get_margin(MARGIN_RIGHT) * 2) / 2);
}
} else {
- x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - r_icon->get_width() - style->get_margin(MARGIN_RIGHT));
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - icon_width - style->get_margin(MARGIN_RIGHT));
}
- ofs_max -= r_icon->get_width();
+ ofs_max -= icon_width;
}
int caret_height = font->get_height() > y_area ? y_area : font->get_height();
FontDrawer drawer(font, Color(1, 1, 1));
while (true) {
- //end of string, break!
+ // End of string, break.
if (char_ofs >= t.length())
break;
@@ -798,7 +814,7 @@ void LineEdit::_notification(int p_what) {
CharType next = (pass && !text.empty()) ? secret_character[0] : t[char_ofs + 1];
int char_width = font->get_char_size(cchar, next).width;
- // end of widget, break!
+ // End of widget, break.
if ((x_ofs + char_width) > ofs_max)
break;
@@ -853,7 +869,7 @@ void LineEdit::_notification(int p_what) {
}
}
- if (char_ofs == cursor_pos && draw_caret) { //may be at the end
+ if (char_ofs == cursor_pos && draw_caret) { // May be at the end.
if (ime_text.length() == 0) {
#ifdef TOOLS_ENABLED
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(Math::round(EDSCALE), caret_height)), cursor_color);
@@ -871,7 +887,9 @@ void LineEdit::_notification(int p_what) {
} break;
case NOTIFICATION_FOCUS_ENTER: {
- if (!caret_blink_enabled) {
+ if (caret_blink_enabled) {
+ caret_blink_timer->start();
+ } else {
draw_caret = true;
}
@@ -885,6 +903,10 @@ void LineEdit::_notification(int p_what) {
} break;
case NOTIFICATION_FOCUS_EXIT: {
+ if (caret_blink_enabled) {
+ caret_blink_timer->stop();
+ }
+
OS::get_singleton()->set_ime_position(Point2());
OS::get_singleton()->set_ime_active(false);
ime_text = "";
@@ -999,6 +1021,8 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) {
Ref<StyleBox> style = get_stylebox("normal");
int pixel_ofs = 0;
Size2 size = get_size();
+ bool display_clear_icon = !text.empty() && is_editable() && clear_button_enabled;
+ int r_icon_width = Control::get_icon("clear")->get_width();
switch (align) {
@@ -1013,10 +1037,16 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) {
pixel_ofs = int(style->get_offset().x);
else
pixel_ofs = int(size.width - (cached_width)) / 2;
+
+ if (display_clear_icon)
+ pixel_ofs -= int(r_icon_width / 2 + style->get_margin(MARGIN_RIGHT));
} break;
case ALIGN_RIGHT: {
pixel_ofs = int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_width));
+
+ if (display_clear_icon)
+ pixel_ofs -= int(r_icon_width + style->get_margin(MARGIN_RIGHT));
} break;
}
@@ -1028,7 +1058,7 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) {
}
pixel_ofs += char_w;
- if (pixel_ofs > p_x) { //found what we look for
+ if (pixel_ofs > p_x) { // Found what we look for.
break;
}
@@ -1038,17 +1068,67 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) {
set_cursor_position(ofs);
}
+int LineEdit::get_cursor_pixel_pos() {
+
+ Ref<Font> font = get_font("font");
+ int ofs = window_pos;
+ Ref<StyleBox> style = get_stylebox("normal");
+ int pixel_ofs = 0;
+ Size2 size = get_size();
+ bool display_clear_icon = !text.empty() && is_editable() && clear_button_enabled;
+ int r_icon_width = Control::get_icon("clear")->get_width();
+
+ switch (align) {
+
+ case ALIGN_FILL:
+ case ALIGN_LEFT: {
+
+ pixel_ofs = int(style->get_offset().x);
+ } break;
+ case ALIGN_CENTER: {
+
+ if (window_pos != 0)
+ pixel_ofs = int(style->get_offset().x);
+ else
+ pixel_ofs = int(size.width - (cached_width)) / 2;
+
+ if (display_clear_icon)
+ pixel_ofs -= int(r_icon_width / 2 + style->get_margin(MARGIN_RIGHT));
+ } break;
+ case ALIGN_RIGHT: {
+
+ pixel_ofs = int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_width));
+
+ if (display_clear_icon)
+ pixel_ofs -= int(r_icon_width + style->get_margin(MARGIN_RIGHT));
+ } break;
+ }
+
+ while (ofs < cursor_pos) {
+ if (font != NULL) {
+ pixel_ofs += font->get_char_size(text[ofs]).width;
+ }
+ ofs++;
+ }
+
+ return pixel_ofs;
+}
+
bool LineEdit::cursor_get_blink_enabled() const {
return caret_blink_enabled;
}
void LineEdit::cursor_set_blink_enabled(const bool p_enabled) {
caret_blink_enabled = p_enabled;
- if (p_enabled) {
- caret_blink_timer->start();
- } else {
- caret_blink_timer->stop();
+
+ if (has_focus()) {
+ if (p_enabled) {
+ caret_blink_timer->start();
+ } else {
+ caret_blink_timer->stop();
+ }
}
+
draw_caret = true;
}
@@ -1063,10 +1143,12 @@ void LineEdit::cursor_set_blink_speed(const float p_speed) {
void LineEdit::_reset_caret_blink_timer() {
if (caret_blink_enabled) {
- caret_blink_timer->stop();
- caret_blink_timer->start();
draw_caret = true;
- update();
+ if (has_focus()) {
+ caret_blink_timer->stop();
+ caret_blink_timer->start();
+ update();
+ }
}
}
@@ -1189,15 +1271,18 @@ void LineEdit::set_cursor_position(int p_pos) {
Ref<Font> font = get_font("font");
if (cursor_pos <= window_pos) {
- /* Adjust window if cursor goes too much to the left */
+ // Adjust window if cursor goes too much to the left.
set_window_pos(MAX(0, cursor_pos - 1));
} else {
- /* Adjust window if cursor goes too much to the right */
+ // Adjust window if cursor goes too much to the right.
int window_width = get_size().width - style->get_minimum_size().width;
bool display_clear_icon = !text.empty() && 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;
- window_width -= r_icon->get_width();
+
+ float icon_width = MIN(r_icon->get_width(), r_icon->get_width() * (get_size().height - (style->get_margin(MARGIN_TOP) + style->get_margin(MARGIN_BOTTOM))) / r_icon->get_height());
+
+ window_width -= icon_width;
}
if (window_width < 0)
@@ -1211,10 +1296,10 @@ void LineEdit::set_cursor_position(int p_pos) {
for (int i = cursor_pos; i >= window_pos; i--) {
if (i >= text.length()) {
- //do not do this, because if the cursor is at the end, its just fine that it takes no space
- //accum_width = font->get_char_size(' ').width; //anything should do
+ // Do not do this, because if the cursor is at the end, its just fine that it takes no space.
+ // accum_width = font->get_char_size(' ').width;
} else {
- accum_width += font->get_char_size(text[i], i + 1 < text.length() ? text[i + 1] : 0).width; //anything should do
+ accum_width += font->get_char_size(text[i], i + 1 < text.length() ? text[i + 1] : 0).width; // Anything should do.
}
if (accum_width > window_width)
break;
@@ -1279,12 +1364,13 @@ Size2 LineEdit::get_minimum_size() const {
Size2 min = style->get_minimum_size();
min.height += font->get_height();
- //minimum size of text
+ // Minimum size of text.
int space_size = font->get_char_size(' ').x;
int mstext = get_constant("minimum_spaces") * space_size;
if (expand_to_text_length) {
- mstext = MAX(mstext, font->get_string_size(text).x + space_size); //add a spce because some fonts are too exact, and because cursor needs a bit more when at the end
+ // Add a space because some fonts are too exact, and because cursor needs a bit more when at the end.
+ mstext = MAX(mstext, font->get_string_size(text).x + space_size);
}
min.width += mstext;
@@ -1292,8 +1378,6 @@ Size2 LineEdit::get_minimum_size() const {
return min;
}
-/* selection */
-
void LineEdit::deselect() {
selection.begin = 0;
@@ -1326,6 +1410,8 @@ int LineEdit::get_max_length() const {
}
void LineEdit::selection_fill_at_cursor() {
+ if (!selecting_enabled)
+ return;
selection.begin = cursor_pos;
selection.end = selection.cursor_start;
@@ -1340,6 +1426,8 @@ void LineEdit::selection_fill_at_cursor() {
}
void LineEdit::select_all() {
+ if (!selecting_enabled)
+ return;
if (!text.length())
return;
@@ -1356,22 +1444,7 @@ void LineEdit::set_editable(bool p_editable) {
return;
editable = p_editable;
-
- // Reorganize context menu.
- menu->clear();
- if (editable)
- menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X);
- menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C);
- if (editable)
- menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V);
- menu->add_separator();
- menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A);
- if (editable) {
- menu->add_item(RTR("Clear"), MENU_CLEAR);
- menu->add_separator();
- menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z);
- menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z);
- }
+ _generate_context_menu();
update();
}
@@ -1394,10 +1467,9 @@ bool LineEdit::is_secret() const {
void LineEdit::set_secret_character(const String &p_string) {
- // An empty string as the secret character would crash the engine
- // It also wouldn't make sense to use multiple characters as the secret character
- ERR_EXPLAIN("Secret character must be exactly one character long (" + itos(p_string.length()) + " characters given)");
- ERR_FAIL_COND(p_string.length() != 1);
+ // An empty string as the secret character would crash the engine.
+ // It also wouldn't make sense to use multiple characters as the secret character.
+ ERR_FAIL_COND_MSG(p_string.length() != 1, "Secret character must be exactly one character long (" + itos(p_string.length()) + " characters given).");
secret_character = p_string;
update();
@@ -1408,6 +1480,8 @@ String LineEdit::get_secret_character() const {
}
void LineEdit::select(int p_from, int p_to) {
+ if (!selecting_enabled)
+ return;
if (p_from == 0 && p_to == 0) {
deselect();
@@ -1515,6 +1589,29 @@ bool LineEdit::is_clear_button_enabled() const {
return clear_button_enabled;
}
+void LineEdit::set_shortcut_keys_enabled(bool p_enabled) {
+ shortcut_keys_enabled = p_enabled;
+
+ _generate_context_menu();
+}
+
+bool LineEdit::is_shortcut_keys_enabled() const {
+ return shortcut_keys_enabled;
+}
+
+void LineEdit::set_selecting_enabled(bool p_enabled) {
+ selecting_enabled = p_enabled;
+
+ if (!selecting_enabled)
+ deselect();
+
+ _generate_context_menu();
+}
+
+bool LineEdit::is_selecting_enabled() const {
+ return selecting_enabled;
+}
+
void LineEdit::set_right_icon(const Ref<Texture> &p_icon) {
if (right_icon == p_icon) {
return;
@@ -1523,8 +1620,11 @@ void LineEdit::set_right_icon(const Ref<Texture> &p_icon) {
update();
}
-void LineEdit::_text_changed() {
+Ref<Texture> LineEdit::get_right_icon() {
+ return right_icon;
+}
+void LineEdit::_text_changed() {
if (expand_to_text_length)
minimum_size_changed();
@@ -1578,6 +1678,25 @@ void LineEdit::_create_undo_state() {
undo_stack.push_back(op);
}
+void LineEdit::_generate_context_menu() {
+ // Reorganize context menu.
+ menu->clear();
+ if (editable)
+ menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_X : 0);
+ menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_C : 0);
+ if (editable)
+ menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_V : 0);
+ menu->add_separator();
+ if (is_selecting_enabled())
+ menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_A : 0);
+ if (editable) {
+ menu->add_item(RTR("Clear"), MENU_CLEAR);
+ menu->add_separator();
+ menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_Z : 0);
+ menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z : 0);
+ }
+}
+
void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("_text_changed"), &LineEdit::_text_changed);
@@ -1622,6 +1741,12 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &LineEdit::is_context_menu_enabled);
ClassDB::bind_method(D_METHOD("set_clear_button_enabled", "enable"), &LineEdit::set_clear_button_enabled);
ClassDB::bind_method(D_METHOD("is_clear_button_enabled"), &LineEdit::is_clear_button_enabled);
+ ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &LineEdit::set_shortcut_keys_enabled);
+ ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &LineEdit::is_shortcut_keys_enabled);
+ ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &LineEdit::set_selecting_enabled);
+ ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &LineEdit::is_selecting_enabled);
+ ClassDB::bind_method(D_METHOD("set_right_icon", "icon"), &LineEdit::set_right_icon);
+ ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon);
ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text")));
ADD_SIGNAL(MethodInfo("text_entered", PropertyInfo(Variant::STRING, "new_text")));
@@ -1650,6 +1775,9 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon");
ADD_GROUP("Placeholder", "placeholder_");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha");
@@ -1677,6 +1805,8 @@ LineEdit::LineEdit() {
clear_button_enabled = false;
clear_button_status.press_attempt = false;
clear_button_status.pressing_inside = false;
+ shortcut_keys_enabled = true;
+ selecting_enabled = true;
deselect();
set_focus_mode(FOCUS_ALL);
@@ -1694,7 +1824,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
+ 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 3002f6f637..3424131dad 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -34,9 +34,6 @@
#include "scene/gui/control.h"
#include "scene/gui/popup_menu.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
class LineEdit : public Control {
GDCLASS(LineEdit, Control);
@@ -78,18 +75,22 @@ private:
String ime_text;
Point2 ime_selection;
+ bool selecting_enabled;
+
bool context_menu_enabled;
PopupMenu *menu;
int cursor_pos;
int window_pos;
- int max_length; // 0 for no maximum
+ int max_length; // 0 for no maximum.
int cached_width;
int cached_placeholder_width;
bool clear_button_enabled;
+ bool shortcut_keys_enabled;
+
Ref<Texture> right_icon;
struct Selection {
@@ -121,6 +122,8 @@ private:
void _clear_redo();
void _create_undo_state();
+ void _generate_context_menu();
+
Timer *caret_blink_timer;
void _text_changed();
@@ -140,6 +143,7 @@ private:
void set_window_pos(int p_pos);
void set_cursor_at_pixel_pos(int p_x);
+ int get_cursor_pixel_pos();
void _reset_caret_blink_timer();
void _toggle_draw_caret();
@@ -219,7 +223,14 @@ public:
void set_clear_button_enabled(bool p_enabled);
bool is_clear_button_enabled() const;
+ void set_shortcut_keys_enabled(bool p_enabled);
+ bool is_shortcut_keys_enabled() const;
+
+ void set_selecting_enabled(bool p_enabled);
+ bool is_selecting_enabled() const;
+
void set_right_icon(const Ref<Texture> &p_icon);
+ Ref<Texture> get_right_icon();
virtual bool is_text_field() const;
LineEdit();
diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h
index 42e909d991..5448ff13f2 100644
--- a/scene/gui/menu_button.h
+++ b/scene/gui/menu_button.h
@@ -33,9 +33,7 @@
#include "scene/gui/button.h"
#include "scene/gui/popup_menu.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
+
class MenuButton : public Button {
GDCLASS(MenuButton, Button);
diff --git a/scene/gui/nine_patch_rect.h b/scene/gui/nine_patch_rect.h
index ac17e52fc1..f31a09a482 100644
--- a/scene/gui/nine_patch_rect.h
+++ b/scene/gui/nine_patch_rect.h
@@ -32,9 +32,7 @@
#define NINE_PATCH_RECT_H
#include "scene/gui/control.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
+
class NinePatchRect : public Control {
GDCLASS(NinePatchRect, Control);
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 58671655dc..de8df4215d 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -43,40 +43,42 @@ Size2 OptionButton::get_minimum_size() const {
void OptionButton::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
-
- if (!has_icon("arrow"))
- return;
-
- RID ci = get_canvas_item();
- Ref<Texture> arrow = Control::get_icon("arrow");
- Ref<StyleBox> normal = get_stylebox("normal");
- Color clr = Color(1, 1, 1);
- if (get_constant("modulate_arrow")) {
- switch (get_draw_mode()) {
- case DRAW_PRESSED:
- clr = get_color("font_color_pressed");
- break;
- case DRAW_HOVER:
- clr = get_color("font_color_hover");
- break;
- case DRAW_DISABLED:
- clr = get_color("font_color_disabled");
- break;
- default:
- clr = get_color("font_color");
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+
+ if (!has_icon("arrow"))
+ return;
+
+ RID ci = get_canvas_item();
+ Ref<Texture> arrow = Control::get_icon("arrow");
+ Color clr = Color(1, 1, 1);
+ if (get_constant("modulate_arrow")) {
+ switch (get_draw_mode()) {
+ case DRAW_PRESSED:
+ clr = get_color("font_color_pressed");
+ break;
+ case DRAW_HOVER:
+ clr = get_color("font_color_hover");
+ break;
+ case DRAW_DISABLED:
+ clr = get_color("font_color_disabled");
+ break;
+ default:
+ clr = get_color("font_color");
+ }
}
- }
- Size2 size = get_size();
+ Size2 size = get_size();
- Point2 ofs(size.width - arrow->get_width() - get_constant("arrow_margin"), int(Math::abs((size.height - arrow->get_height()) / 2)));
- arrow->draw(ci, ofs, clr);
- } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ Point2 ofs(size.width - arrow->get_width() - get_constant("arrow_margin"), int(Math::abs((size.height - arrow->get_height()) / 2)));
+ arrow->draw(ci, ofs, clr);
+ } break;
+ case NOTIFICATION_VISIBILITY_CHANGED: {
- if (!is_visible_in_tree()) {
- popup->hide();
- }
+ if (!is_visible_in_tree()) {
+ popup->hide();
+ }
+ } break;
}
}
@@ -114,10 +116,16 @@ void OptionButton::add_item(const String &p_label, int p_id) {
void OptionButton::set_item_text(int p_idx, const String &p_text) {
popup->set_item_text(p_idx, p_text);
+
+ if (current == p_idx)
+ set_text(p_text);
}
void OptionButton::set_item_icon(int p_idx, const Ref<Texture> &p_icon) {
popup->set_item_icon(p_idx, p_icon);
+
+ if (current == p_idx)
+ set_icon(p_icon);
}
void OptionButton::set_item_id(int p_idx, int p_id) {
diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h
index 51d5fd6947..7210708042 100644
--- a/scene/gui/option_button.h
+++ b/scene/gui/option_button.h
@@ -33,9 +33,7 @@
#include "scene/gui/button.h"
#include "scene/gui/popup_menu.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
+
class OptionButton : public Button {
GDCLASS(OptionButton, Button);
diff --git a/scene/gui/panel.h b/scene/gui/panel.h
index f8d15e4261..84bf6e75f5 100644
--- a/scene/gui/panel.h
+++ b/scene/gui/panel.h
@@ -32,9 +32,7 @@
#define PANEL_H
#include "scene/gui/control.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
+
class Panel : public Control {
GDCLASS(Panel, Control);
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index 3e003af396..32380b6457 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -74,7 +74,7 @@ void Popup::_fix_size() {
Point2 pos = get_global_position();
Size2 size = get_size() * get_scale();
- Point2 window_size = get_viewport_rect().size;
+ Point2 window_size = get_viewport_rect().size - get_viewport_transform().get_origin();
if (pos.x + size.width > window_size.width)
pos.x = window_size.width - size.width;
@@ -207,6 +207,7 @@ bool Popup::is_exclusive() const {
void Popup::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_as_minsize"), &Popup::set_as_minsize);
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()));
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index d6d96dfe64..925760984e 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -33,9 +33,6 @@
#include "scene/gui/control.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
class Popup : public Control {
GDCLASS(Popup, Control);
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 2cac345dae..a7c6c5ccab 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -148,11 +148,9 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
void PopupMenu::_activate_submenu(int over) {
Node *n = get_node(items[over].submenu);
- ERR_EXPLAIN("Item subnode does not exist: " + items[over].submenu);
- ERR_FAIL_COND(!n);
+ ERR_FAIL_COND_MSG(!n, "Item subnode does not exist: " + items[over].submenu + ".");
Popup *pm = Object::cast_to<Popup>(n);
- ERR_EXPLAIN("Item subnode is not a Popup: " + items[over].submenu);
- ERR_FAIL_COND(!pm);
+ ERR_FAIL_COND_MSG(!pm, "Item subnode is not a Popup: " + items[over].submenu + ".");
if (pm->is_visible_in_tree())
return; //already visible!
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index babdd21281..8bfe8fc607 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -33,10 +33,6 @@
#include "scene/gui/popup.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
-
class PopupMenu : public Popup {
GDCLASS(PopupMenu, Popup);
diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp
index 264eda4035..0154a452ad 100644
--- a/scene/gui/progress_bar.cpp
+++ b/scene/gui/progress_bar.cpp
@@ -60,7 +60,7 @@ void ProgressBar::_notification(int p_what) {
draw_style_box(bg, Rect2(Point2(), get_size()));
float r = get_as_ratio();
int mp = fg->get_minimum_size().width;
- int p = r * get_size().width - mp;
+ int p = r * (get_size().width - mp);
if (p > 0) {
draw_style_box(fg, Rect2(Point2(), Size2(p + fg->get_minimum_size().width, get_size().height)));
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index e709bac377..ed5dd77f53 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -180,12 +180,12 @@ double Range::get_as_ratio() const {
float value = CLAMP(get_value(), shared->min, shared->max);
double v = Math::log(value) / Math::log((double)2);
- return (v - exp_min) / (exp_max - exp_min);
+ return CLAMP((v - exp_min) / (exp_max - exp_min), 0, 1);
} else {
float value = CLAMP(get_value(), shared->min, shared->max);
- return (value - get_min()) / (get_max() - get_min());
+ return CLAMP((value - get_min()) / (get_max() - get_min()), 0, 1);
}
}
diff --git a/scene/gui/range.h b/scene/gui/range.h
index cf0add8c89..8ce450f8fc 100644
--- a/scene/gui/range.h
+++ b/scene/gui/range.h
@@ -32,9 +32,7 @@
#define RANGE_H
#include "scene/gui/control.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
+
class Range : public Control {
GDCLASS(Range, Control);
diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp
new file mode 100644
index 0000000000..67fa85b832
--- /dev/null
+++ b/scene/gui/rich_text_effect.cpp
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* rich_text_effect.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "rich_text_effect.h"
+
+#include "core/script_language.h"
+
+void RichTextEffect::_bind_methods() {
+ BIND_VMETHOD(MethodInfo(Variant::BOOL, "_process_custom_fx", PropertyInfo(Variant::OBJECT, "char_fx", PROPERTY_HINT_RESOURCE_TYPE, "CharFXTransform")));
+}
+
+Variant RichTextEffect::get_bbcode() const {
+ Variant r;
+ if (get_script_instance()) {
+ if (!get_script_instance()->get("bbcode", r)) {
+ String path = get_script_instance()->get_script()->get_path();
+ r = path.get_file().get_basename();
+ }
+ }
+ return r;
+}
+
+bool RichTextEffect::_process_effect_impl(Ref<CharFXTransform> p_cfx) {
+ bool return_value = false;
+ if (get_script_instance()) {
+ Variant v = get_script_instance()->call("_process_custom_fx", p_cfx);
+ if (v.get_type() != Variant::BOOL) {
+ return_value = false;
+ } else {
+ return_value = (bool)v;
+ }
+ }
+ return return_value;
+}
+
+RichTextEffect::RichTextEffect() {
+}
+
+void CharFXTransform::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("get_relative_index"), &CharFXTransform::get_relative_index);
+ ClassDB::bind_method(D_METHOD("set_relative_index", "index"), &CharFXTransform::set_relative_index);
+
+ ClassDB::bind_method(D_METHOD("get_absolute_index"), &CharFXTransform::get_absolute_index);
+ ClassDB::bind_method(D_METHOD("set_absolute_index", "index"), &CharFXTransform::set_absolute_index);
+
+ ClassDB::bind_method(D_METHOD("get_elapsed_time"), &CharFXTransform::get_elapsed_time);
+ ClassDB::bind_method(D_METHOD("set_elapsed_time", "time"), &CharFXTransform::set_elapsed_time);
+
+ ClassDB::bind_method(D_METHOD("is_visible"), &CharFXTransform::is_visible);
+ ClassDB::bind_method(D_METHOD("set_visibility", "visibility"), &CharFXTransform::set_visibility);
+
+ ClassDB::bind_method(D_METHOD("get_offset"), &CharFXTransform::get_offset);
+ ClassDB::bind_method(D_METHOD("set_offset", "offset"), &CharFXTransform::set_offset);
+
+ ClassDB::bind_method(D_METHOD("get_color"), &CharFXTransform::get_color);
+ ClassDB::bind_method(D_METHOD("set_color", "color"), &CharFXTransform::set_color);
+
+ ClassDB::bind_method(D_METHOD("get_environment"), &CharFXTransform::get_environment);
+ ClassDB::bind_method(D_METHOD("set_environment", "environment"), &CharFXTransform::set_environment);
+
+ ClassDB::bind_method(D_METHOD("get_character"), &CharFXTransform::get_character);
+ ClassDB::bind_method(D_METHOD("set_character", "character"), &CharFXTransform::set_character);
+
+ ClassDB::bind_method(D_METHOD("get_value_or", "key", "default_value"), &CharFXTransform::get_value_or);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "relative_index"), "set_relative_index", "get_relative_index");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "absolute_index"), "set_absolute_index", "get_absolute_index");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "elapsed_time"), "set_elapsed_time", "get_elapsed_time");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visibility", "is_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "env"), "set_environment", "get_environment");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "character"), "set_character", "get_character");
+}
+
+Variant CharFXTransform::get_value_or(String p_key, Variant p_default_value) {
+ if (!this->environment.has(p_key))
+ return p_default_value;
+
+ Variant r = environment[p_key];
+ if (r.get_type() != p_default_value.get_type())
+ return p_default_value;
+
+ return r;
+}
+
+CharFXTransform::CharFXTransform() {
+ relative_index = 0;
+ absolute_index = 0;
+ visibility = true;
+ offset = Point2();
+ color = Color();
+ character = 0;
+}
diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h
new file mode 100644
index 0000000000..f9c3e15399
--- /dev/null
+++ b/scene/gui/rich_text_effect.h
@@ -0,0 +1,87 @@
+/*************************************************************************/
+/* rich_text_effect.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RICH_TEXT_EFFECT_H
+#define RICH_TEXT_EFFECT_H
+
+#include "core/resource.h"
+
+class RichTextEffect : public Resource {
+ GDCLASS(RichTextEffect, Resource);
+ OBJ_SAVE_TYPE(RichTextEffect);
+
+protected:
+ static void _bind_methods();
+
+public:
+ Variant get_bbcode() const;
+ bool _process_effect_impl(Ref<class CharFXTransform> p_cfx);
+
+ RichTextEffect();
+};
+
+class CharFXTransform : public Reference {
+ GDCLASS(CharFXTransform, Reference);
+
+protected:
+ static void _bind_methods();
+
+public:
+ uint64_t relative_index;
+ uint64_t absolute_index;
+ bool visibility;
+ Point2 offset;
+ Color color;
+ CharType character;
+ float elapsed_time;
+ Dictionary environment;
+
+ CharFXTransform();
+ uint64_t get_relative_index() { return relative_index; }
+ void set_relative_index(uint64_t p_index) { relative_index = p_index; }
+ uint64_t get_absolute_index() { return absolute_index; }
+ void set_absolute_index(uint64_t p_index) { absolute_index = p_index; }
+ float get_elapsed_time() { return elapsed_time; }
+ void set_elapsed_time(float p_elapsed_time) { elapsed_time = p_elapsed_time; }
+ bool is_visible() { return visibility; }
+ void set_visibility(bool p_vis) { visibility = p_vis; }
+ Point2 get_offset() { return offset; }
+ void set_offset(Point2 p_offset) { offset = p_offset; }
+ Color get_color() { return color; }
+ void set_color(Color p_color) { color = p_color; }
+ int get_character() { return (int)character; }
+ void set_character(int p_char) { character = (CharType)p_char; }
+ Dictionary get_environment() { return environment; }
+ void set_environment(Dictionary p_environment) { environment = p_environment; }
+
+ Variant get_value_or(String p_key, Variant p_default_value);
+};
+
+#endif // RICH_TEXT_EFFECT_H
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index d6c0981ebc..d9ae42d6e6 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -30,10 +30,11 @@
#include "rich_text_label.h"
+#include "core/math/math_defs.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
+#include "modules/regex/regex.h"
#include "scene/scene_string_names.h"
-
#ifdef TOOLS_ENABLED
#include "editor/editor_scale.h"
#endif
@@ -139,6 +140,7 @@ Rect2 RichTextLabel::_get_text_rect() {
Ref<StyleBox> style = get_stylebox("normal");
return Rect2(style->get_offset(), get_size() - style->get_minimum_size());
}
+
int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) {
RID ci;
@@ -292,7 +294,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
Color selection_bg;
if (p_mode == PROCESS_DRAW) {
-
selection_fg = get_color("font_color_selected");
selection_bg = get_color("selection_color");
}
@@ -343,18 +344,24 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
Color font_color_shadow;
bool underline = false;
bool strikethrough = false;
+ ItemFade *fade = NULL;
+ int it_char_start = p_char_count;
+
+ Vector<ItemFX *> fx_stack = Vector<ItemFX *>();
+ bool custom_fx_ok = true;
if (p_mode == PROCESS_DRAW) {
color = _find_color(text, p_base_color);
font_color_shadow = _find_color(text, p_font_color_shadow);
if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) {
-
underline = true;
} else if (_find_strikethrough(text)) {
-
strikethrough = true;
}
+ fade = _fetch_by_type<ItemFade>(text, ITEM_FADE);
+ _fetch_item_stack<ItemFX>(text, fx_stack);
+
} else if (p_mode == PROCESS_CACHE) {
l.char_count += text->text.length();
}
@@ -431,8 +438,11 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
ofs += cw;
} else if (p_mode == PROCESS_DRAW) {
-
bool selected = false;
+ Color fx_color = Color(color);
+ Point2 fx_offset;
+ CharType fx_char = c[i];
+
if (selection.active) {
int cofs = (&c[i]) - cf;
@@ -442,8 +452,78 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
}
int cw = 0;
+ int c_item_offset = p_char_count - it_char_start;
+
+ float faded_visibility = 1.0f;
+ if (fade) {
+ if (c_item_offset >= fade->starting_index) {
+ faded_visibility -= (float)(c_item_offset - fade->starting_index) / (float)fade->length;
+ faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
+ }
+ fx_color.a = faded_visibility;
+ }
+
+ bool visible = visible_characters < 0 || ((p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent)) &&
+ faded_visibility > 0.0f);
+
+ for (int j = 0; j < fx_stack.size(); j++) {
+ ItemCustomFX *item_custom = Object::cast_to<ItemCustomFX>(fx_stack[j]);
+ ItemShake *item_shake = Object::cast_to<ItemShake>(fx_stack[j]);
+ ItemWave *item_wave = Object::cast_to<ItemWave>(fx_stack[j]);
+ ItemTornado *item_tornado = Object::cast_to<ItemTornado>(fx_stack[j]);
+ ItemRainbow *item_rainbow = Object::cast_to<ItemRainbow>(fx_stack[j]);
+
+ if (item_custom && custom_fx_ok) {
+ Ref<CharFXTransform> charfx = Ref<CharFXTransform>(memnew(CharFXTransform));
+ Ref<RichTextEffect> custom_effect = _get_custom_effect_by_code(item_custom->identifier);
+ if (!custom_effect.is_null()) {
+ charfx->elapsed_time = item_custom->elapsed_time;
+ charfx->environment = item_custom->environment;
+ charfx->relative_index = c_item_offset;
+ charfx->absolute_index = p_char_count;
+ charfx->visibility = visible;
+ charfx->offset = fx_offset;
+ charfx->color = fx_color;
+ charfx->character = fx_char;
+
+ bool effect_status = custom_effect->_process_effect_impl(charfx);
+ custom_fx_ok = effect_status;
+
+ fx_offset += charfx->offset;
+ fx_color = charfx->color;
+ visible &= charfx->visibility;
+ fx_char = charfx->character;
+ }
+ } else if (item_shake) {
+ uint64_t char_current_rand = item_shake->offset_random(c_item_offset);
+ uint64_t char_previous_rand = item_shake->offset_previous_random(c_item_offset);
+ uint64_t max_rand = 2147483647;
+ double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
+ double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
+ double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate));
+ n_time = (n_time > 1.0) ? 1.0 : n_time;
+ fx_offset += Point2(Math::lerp(Math::sin(previous_offset),
+ Math::sin(current_offset),
+ n_time),
+ Math::lerp(Math::cos(previous_offset),
+ Math::cos(current_offset),
+ n_time)) *
+ (float)item_shake->strength / 10.0f;
+ } else if (item_wave) {
+ double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_wave->amplitude / 10.0f);
+ fx_offset += Point2(0, 1) * value;
+ } else if (item_tornado) {
+ double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius);
+ double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius);
+ fx_offset += Point2(torn_x, torn_y);
+ } else if (item_rainbow) {
+ fx_color = fx_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + pofs) / 50)),
+ item_rainbow->saturation,
+ item_rainbow->value,
+ fx_color.a);
+ }
+ }
- bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent));
if (visible)
line_is_blank = false;
@@ -451,27 +531,28 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
visible = false;
if (visible) {
+
if (selected) {
- cw = font->get_char_size(c[i], c[i + 1]).x;
+ cw = font->get_char_size(fx_char, c[i + 1]).x;
draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg);
}
if (p_font_color_shadow.a > 0) {
float x_ofs_shadow = align_ofs + pofs;
float y_ofs_shadow = y + lh - line_descent;
- 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, fx_char, 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);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), fx_char, 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), fx_char, 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), fx_char, c[i + 1], p_font_color_shadow);
}
}
if (selected) {
- drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color);
+ drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), fx_char, c[i + 1], override_selected_font_color ? selection_fg : fx_color);
} else {
- cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], color);
+ cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent) + fx_offset, fx_char, c[i + 1], fx_color);
}
}
@@ -800,6 +881,31 @@ void RichTextLabel::_update_scroll() {
}
}
+void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, float p_delta_time) {
+ Item *it = p_frame;
+ while (it) {
+ ItemFX *ifx = Object::cast_to<ItemFX>(it);
+
+ if (!ifx) {
+ it = _get_next_item(it, true);
+ continue;
+ }
+
+ ifx->elapsed_time += p_delta_time;
+
+ ItemShake *shake = Object::cast_to<ItemShake>(it);
+ if (shake) {
+ bool cycle = (shake->elapsed_time > (1.0f / shake->rate));
+ if (cycle) {
+ shake->elapsed_time -= (1.0f / shake->rate);
+ shake->reroll_random();
+ }
+ }
+
+ it = _get_next_item(it, true);
+ }
+}
+
void RichTextLabel::_notification(int p_what) {
switch (p_what) {
@@ -821,11 +927,7 @@ void RichTextLabel::_notification(int p_what) {
} break;
case NOTIFICATION_THEME_CHANGED: {
- if (is_inside_tree() && use_bbcode) {
- parse_bbcode(bbcode);
- //first_invalid_line=0; //invalidate ALL
- //update();
- }
+ update();
} break;
case NOTIFICATION_DRAW: {
@@ -877,6 +979,15 @@ void RichTextLabel::_notification(int p_what) {
from_line++;
}
+ } break;
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ float dt = get_process_delta_time();
+
+ for (int i = 0; i < custom_effects.size(); i++) {
+ }
+
+ _update_fx(main, dt);
+ update();
}
}
}
@@ -920,9 +1031,12 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const {
- if (!underline_meta || selection.click)
+ if (!underline_meta)
return CURSOR_ARROW;
+ if (selection.click)
+ return CURSOR_IBEAM;
+
if (main->first_invalid_line < main->lines.size())
return CURSOR_ARROW; //invalid
@@ -1027,15 +1141,11 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
}
if (b->get_button_index() == BUTTON_WHEEL_UP) {
-
if (scroll_active)
-
vscroll->set_value(vscroll->get_value() - vscroll->get_page() * b->get_factor() * 0.5 / 8);
}
if (b->get_button_index() == BUTTON_WHEEL_DOWN) {
-
if (scroll_active)
-
vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8);
}
}
@@ -1286,8 +1396,19 @@ bool RichTextLabel::_find_strikethrough(Item *p_item) {
return false;
}
-bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) {
+bool RichTextLabel::_find_by_type(Item *p_item, ItemType p_type) {
+ Item *item = p_item;
+ while (item) {
+ if (item->type == p_type) {
+ return true;
+ }
+ item = item->parent;
+ }
+ return false;
+}
+
+bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) {
Item *item = p_item;
while (item) {
@@ -1619,6 +1740,49 @@ void RichTextLabel::push_table(int p_columns) {
_add_item(item, true, true);
}
+void RichTextLabel::push_fade(int p_start_index, int p_length) {
+ ItemFade *item = memnew(ItemFade);
+ item->starting_index = p_start_index;
+ item->length = p_length;
+ _add_item(item, true);
+}
+
+void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) {
+ ItemShake *item = memnew(ItemShake);
+ item->strength = p_strength;
+ item->rate = p_rate;
+ _add_item(item, true);
+}
+
+void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0f) {
+ ItemWave *item = memnew(ItemWave);
+ item->frequency = p_frequency;
+ item->amplitude = p_amplitude;
+ _add_item(item, true);
+}
+
+void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0f) {
+ ItemTornado *item = memnew(ItemTornado);
+ item->frequency = p_frequency;
+ item->radius = p_radius;
+ _add_item(item, true);
+}
+
+void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_frequency) {
+ ItemRainbow *item = memnew(ItemRainbow);
+ item->frequency = p_frequency;
+ item->saturation = p_saturation;
+ item->value = p_value;
+ _add_item(item, true);
+}
+
+void RichTextLabel::push_customfx(String p_identifier, Dictionary p_environment) {
+ ItemCustomFX *item = memnew(ItemCustomFX);
+ item->identifier = p_identifier;
+ item->environment = p_environment;
+ _add_item(item, true);
+}
+
void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) {
ERR_FAIL_COND(current->type != ITEM_TABLE);
@@ -1763,6 +1927,8 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
bool in_bold = false;
bool in_italics = false;
+ set_process_internal(false);
+
while (pos < p_bbcode.length()) {
int brk_pos = p_bbcode.find("[", pos);
@@ -1786,7 +1952,6 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
}
String tag = p_bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1);
-
if (tag.begins_with("/") && tag_stack.size()) {
bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length());
@@ -1799,9 +1964,8 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
indent_level--;
if (!tag_ok) {
-
- add_text("[");
- pos++;
+ add_text("[" + tag);
+ pos = brk_end;
continue;
}
@@ -1993,10 +2157,145 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front("font");
- } else {
+ } else if (tag.begins_with("fade")) {
+ Vector<String> tags = tag.split(" ", false);
+ int startIndex = 0;
+ int length = 10;
+
+ if (tags.size() > 1) {
+ tags.remove(0);
+ for (int i = 0; i < tags.size(); i++) {
+ String expr = tags[i];
+ if (expr.begins_with("start=")) {
+ String start_str = expr.substr(6, expr.length());
+ startIndex = start_str.to_int();
+ } else if (expr.begins_with("length=")) {
+ String end_str = expr.substr(7, expr.length());
+ length = end_str.to_int();
+ }
+ }
+ }
- add_text("["); //ignore
- pos = brk_pos + 1;
+ push_fade(startIndex, length);
+ pos = brk_end + 1;
+ tag_stack.push_front("fade");
+ } else if (tag.begins_with("shake")) {
+ Vector<String> tags = tag.split(" ", false);
+ int strength = 5;
+ float rate = 20.0f;
+
+ if (tags.size() > 1) {
+ tags.remove(0);
+ for (int i = 0; i < tags.size(); i++) {
+ String expr = tags[i];
+ if (expr.begins_with("level=")) {
+ String str_str = expr.substr(6, expr.length());
+ strength = str_str.to_int();
+ } else if (expr.begins_with("rate=")) {
+ String rate_str = expr.substr(5, expr.length());
+ rate = rate_str.to_float();
+ }
+ }
+ }
+
+ push_shake(strength, rate);
+ pos = brk_end + 1;
+ tag_stack.push_front("shake");
+ set_process_internal(true);
+ } else if (tag.begins_with("wave")) {
+ Vector<String> tags = tag.split(" ", false);
+ float amplitude = 20.0f;
+ float period = 5.0f;
+
+ if (tags.size() > 1) {
+ tags.remove(0);
+ for (int i = 0; i < tags.size(); i++) {
+ String expr = tags[i];
+ if (expr.begins_with("amp=")) {
+ String amp_str = expr.substr(4, expr.length());
+ amplitude = amp_str.to_float();
+ } else if (expr.begins_with("freq=")) {
+ String period_str = expr.substr(5, expr.length());
+ period = period_str.to_float();
+ }
+ }
+ }
+
+ push_wave(period, amplitude);
+ pos = brk_end + 1;
+ tag_stack.push_front("wave");
+ set_process_internal(true);
+ } else if (tag.begins_with("tornado")) {
+ Vector<String> tags = tag.split(" ", false);
+ float radius = 10.0f;
+ float frequency = 1.0f;
+
+ if (tags.size() > 1) {
+ tags.remove(0);
+ for (int i = 0; i < tags.size(); i++) {
+ String expr = tags[i];
+ if (expr.begins_with("radius=")) {
+ String amp_str = expr.substr(7, expr.length());
+ radius = amp_str.to_float();
+ } else if (expr.begins_with("freq=")) {
+ String period_str = expr.substr(5, expr.length());
+ frequency = period_str.to_float();
+ }
+ }
+ }
+
+ push_tornado(frequency, radius);
+ pos = brk_end + 1;
+ tag_stack.push_front("tornado");
+ set_process_internal(true);
+ } else if (tag.begins_with("rainbow")) {
+ Vector<String> tags = tag.split(" ", false);
+ float saturation = 0.8f;
+ float value = 0.8f;
+ float frequency = 1.0f;
+
+ if (tags.size() > 1) {
+ tags.remove(0);
+ for (int i = 0; i < tags.size(); i++) {
+ String expr = tags[i];
+ if (expr.begins_with("sat=")) {
+ String sat_str = expr.substr(4, expr.length());
+ saturation = sat_str.to_float();
+ } else if (expr.begins_with("val=")) {
+ String val_str = expr.substr(4, expr.length());
+ value = val_str.to_float();
+ } else if (expr.begins_with("freq=")) {
+ String freq_str = expr.substr(5, expr.length());
+ frequency = freq_str.to_float();
+ }
+ }
+ }
+
+ push_rainbow(saturation, value, frequency);
+ pos = brk_end + 1;
+ tag_stack.push_front("rainbow");
+ set_process_internal(true);
+ } else {
+ Vector<String> expr = tag.split(" ", false);
+ if (expr.size() < 1) {
+ add_text("[");
+ pos = brk_pos + 1;
+ } else {
+ String identifier = expr[0];
+ expr.remove(0);
+ Dictionary properties = parse_expressions_for_values(expr);
+ Ref<RichTextEffect> effect = _get_custom_effect_by_code(identifier);
+
+ if (!effect.is_null()) {
+ push_customfx(identifier, properties);
+ pos = brk_end + 1;
+ tag_stack.push_front(identifier);
+ set_process_internal(true);
+ } else {
+ add_text("["); //ignore
+ pos = brk_pos + 1;
+ }
+ }
}
}
@@ -2205,6 +2504,34 @@ float RichTextLabel::get_percent_visible() const {
return percent_visible;
}
+void RichTextLabel::set_effects(const Vector<Variant> &effects) {
+ custom_effects.clear();
+ for (int i = 0; i < effects.size(); i++) {
+ Ref<RichTextEffect> effect = Ref<RichTextEffect>(effects[i]);
+ custom_effects.push_back(effect);
+ }
+
+ parse_bbcode(bbcode);
+}
+
+Vector<Variant> RichTextLabel::get_effects() {
+ Vector<Variant> r;
+ for (int i = 0; i < custom_effects.size(); i++) {
+ r.push_back(custom_effects[i].get_ref_ptr());
+ }
+ return r;
+}
+
+void RichTextLabel::install_effect(const Variant effect) {
+ Ref<RichTextEffect> rteffect;
+ rteffect = effect;
+
+ if (rteffect.is_valid()) {
+ custom_effects.push_back(effect);
+ parse_bbcode(bbcode);
+ }
+}
+
int RichTextLabel::get_content_height() {
int total_height = 0;
if (main->lines.size())
@@ -2281,6 +2608,12 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height);
+ ClassDB::bind_method(D_METHOD("parse_expressions_for_values", "expressions"), &RichTextLabel::parse_expressions_for_values);
+
+ ClassDB::bind_method(D_METHOD("set_effects", "effects"), &RichTextLabel::set_effects);
+ ClassDB::bind_method(D_METHOD("get_effects"), &RichTextLabel::get_effects);
+ ClassDB::bind_method(D_METHOD("install_effect", "effect"), &RichTextLabel::install_effect);
+
ADD_GROUP("BBCode", "bbcode_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bbcode_text", PROPERTY_HINT_MULTILINE_TEXT), "set_bbcode", "get_bbcode");
@@ -2298,6 +2631,8 @@ void RichTextLabel::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_RESOURCE_TYPE, "17/17:RichTextEffect", (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE), "RichTextEffect"), "set_effects", "get_effects");
+
ADD_SIGNAL(MethodInfo("meta_clicked", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("meta_hover_ended", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
@@ -2323,11 +2658,16 @@ void RichTextLabel::_bind_methods() {
BIND_ENUM_CONSTANT(ITEM_INDENT);
BIND_ENUM_CONSTANT(ITEM_LIST);
BIND_ENUM_CONSTANT(ITEM_TABLE);
+ BIND_ENUM_CONSTANT(ITEM_FADE);
+ BIND_ENUM_CONSTANT(ITEM_SHAKE);
+ BIND_ENUM_CONSTANT(ITEM_WAVE);
+ BIND_ENUM_CONSTANT(ITEM_TORNADO);
+ BIND_ENUM_CONSTANT(ITEM_RAINBOW);
+ BIND_ENUM_CONSTANT(ITEM_CUSTOMFX);
BIND_ENUM_CONSTANT(ITEM_META);
}
void RichTextLabel::set_visible_characters(int p_visible) {
-
visible_characters = p_visible;
update();
}
@@ -2359,6 +2699,77 @@ Size2 RichTextLabel::get_minimum_size() const {
return Size2();
}
+Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) {
+ Ref<RichTextEffect> r;
+ for (int i = 0; i < custom_effects.size(); i++) {
+ if (!custom_effects[i].is_valid())
+ continue;
+
+ if (custom_effects[i]->get_bbcode() == p_bbcode_identifier) {
+ r = custom_effects[i];
+ }
+ }
+
+ return r;
+}
+
+Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressions) {
+ Dictionary d = Dictionary();
+ for (int i = 0; i < p_expressions.size(); i++) {
+ String expression = p_expressions[i];
+
+ Array a = Array();
+ Vector<String> parts = expression.split("=", true);
+ String key = parts[0];
+ if (parts.size() != 2) {
+ return d;
+ }
+
+ Vector<String> values = parts[1].split(",", false);
+
+ RegEx color = RegEx();
+ color.compile("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$");
+ RegEx nodepath = RegEx();
+ nodepath.compile("^\\$");
+ RegEx boolean = RegEx();
+ boolean.compile("^(true|false)$");
+ RegEx decimal = RegEx();
+ decimal.compile("^-?^.?\\d+(\\.\\d+?)?$");
+ RegEx numerical = RegEx();
+ numerical.compile("^\\d+$");
+
+ for (int j = 0; j < values.size(); j++) {
+ if (!color.search(values[j]).is_null()) {
+ a.append(Color::html(values[j]));
+ } else if (!nodepath.search(values[j]).is_null()) {
+ if (values[j].begins_with("$")) {
+ String v = values[j].substr(1, values[j].length());
+ a.append(NodePath(v));
+ }
+ } else if (!boolean.search(values[j]).is_null()) {
+ if (values[j] == "true") {
+ a.append(true);
+ } else if (values[j] == "false") {
+ a.append(false);
+ }
+ } else if (!decimal.search(values[j]).is_null()) {
+ a.append(values[j].to_double());
+ } else if (!numerical.search(values[j]).is_null()) {
+ a.append(values[j].to_int());
+ } else {
+ a.append(values[j]);
+ }
+ }
+
+ if (values.size() > 1) {
+ d[key] = a;
+ } else if (values.size() == 1) {
+ d[key] = a[0];
+ }
+ }
+ return d;
+}
+
RichTextLabel::RichTextLabel() {
main = memnew(ItemFrame);
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 21d099c37a..1c212700ef 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -31,6 +31,7 @@
#ifndef RICH_TEXT_LABEL_H
#define RICH_TEXT_LABEL_H
+#include "rich_text_effect.h"
#include "scene/gui/scroll_bar.h"
class RichTextLabel : public Control {
@@ -67,7 +68,13 @@ public:
ITEM_INDENT,
ITEM_LIST,
ITEM_TABLE,
- ITEM_META
+ ITEM_FADE,
+ ITEM_SHAKE,
+ ITEM_WAVE,
+ ITEM_TORNADO,
+ ITEM_RAINBOW,
+ ITEM_META,
+ ITEM_CUSTOMFX
};
protected:
@@ -96,7 +103,7 @@ private:
}
};
- struct Item {
+ struct Item : public Object {
int index;
Item *parent;
@@ -214,6 +221,101 @@ private:
ItemTable() { type = ITEM_TABLE; }
};
+ struct ItemFade : public Item {
+ int starting_index;
+ int length;
+
+ ItemFade() { type = ITEM_FADE; }
+ };
+
+ struct ItemFX : public Item {
+ float elapsed_time;
+
+ ItemFX() {
+ elapsed_time = 0.0f;
+ }
+ };
+
+ struct ItemShake : public ItemFX {
+ int strength;
+ float rate;
+ uint64_t _current_rng;
+ uint64_t _previous_rng;
+
+ ItemShake() {
+ strength = 0;
+ rate = 0.0f;
+ _current_rng = 0;
+ type = ITEM_SHAKE;
+ }
+
+ void reroll_random() {
+ _previous_rng = _current_rng;
+ _current_rng = Math::rand();
+ }
+
+ uint64_t offset_random(int index) {
+ return (_current_rng >> (index % 64)) |
+ (_current_rng << (64 - (index % 64)));
+ }
+
+ uint64_t offset_previous_random(int index) {
+ return (_previous_rng >> (index % 64)) |
+ (_previous_rng << (64 - (index % 64)));
+ }
+ };
+
+ struct ItemWave : public ItemFX {
+ float frequency;
+ float amplitude;
+
+ ItemWave() {
+ frequency = 1.0f;
+ amplitude = 1.0f;
+ type = ITEM_WAVE;
+ }
+ };
+
+ struct ItemTornado : public ItemFX {
+ float radius;
+ float frequency;
+
+ ItemTornado() {
+ radius = 1.0f;
+ frequency = 1.0f;
+ type = ITEM_TORNADO;
+ }
+ };
+
+ struct ItemRainbow : public ItemFX {
+ float saturation;
+ float value;
+ float frequency;
+
+ ItemRainbow() {
+ saturation = 0.8f;
+ value = 0.8f;
+ frequency = 1.0f;
+ type = ITEM_RAINBOW;
+ }
+ };
+
+ struct ItemCustomFX : public ItemFX {
+ String identifier;
+ Dictionary environment;
+
+ ItemCustomFX() {
+ identifier = "";
+ environment = Dictionary();
+ type = ITEM_CUSTOMFX;
+ }
+
+ virtual ~ItemCustomFX() {
+ _clear_children();
+ environment.clear();
+ }
+ };
+
ItemFrame *main;
Item *current;
ItemFrame *current_frame;
@@ -239,6 +341,8 @@ private:
ItemMeta *meta_hovering;
Variant current_meta;
+ Vector<Ref<RichTextEffect> > custom_effects;
+
void _invalidate_current_line(ItemFrame *p_frame);
void _validate_line_caches(ItemFrame *p_frame);
@@ -246,7 +350,6 @@ private:
void _remove_item(Item *p_item, const int p_line, const int p_subitem_line);
struct ProcessState {
-
int line_width;
};
@@ -287,8 +390,36 @@ private:
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);
+ bool _find_by_type(Item *p_item, ItemType p_type);
+ template <typename T>
+ T *_fetch_by_type(Item *p_item, ItemType p_type) {
+ Item *item = p_item;
+ T *result = NULL;
+ while (item) {
+ if (item->type == p_type) {
+ result = Object::cast_to<T>(item);
+ if (result)
+ return result;
+ }
+ item = item->parent;
+ }
+
+ return result;
+ };
+ template <typename T>
+ void _fetch_item_stack(Item *p_item, Vector<T *> &r_stack) {
+ Item *item = p_item;
+ while (item) {
+ T *found = Object::cast_to<T>(item);
+ if (found) {
+ r_stack.push_back(found);
+ }
+ item = item->parent;
+ }
+ }
void _update_scroll();
+ void _update_fx(ItemFrame *p_frame, float p_delta_time);
void _scroll_changed(double);
void _gui_input(Ref<InputEvent> p_event);
@@ -296,6 +427,8 @@ private:
Item *_get_prev_item(Item *p_item, bool p_free = false);
Rect2 _get_text_rect();
+ Ref<RichTextEffect> _get_custom_effect_by_code(String p_bbcode_identifier);
+ virtual Dictionary parse_expressions_for_values(Vector<String> p_expressions);
bool use_bbcode;
String bbcode;
@@ -322,6 +455,12 @@ public:
void push_list(ListType p_list);
void push_meta(const Variant &p_meta);
void push_table(int p_columns);
+ void push_fade(int p_start_index, int p_length);
+ void push_shake(int p_strength, float p_rate);
+ void push_wave(float p_frequency, float p_amplitude);
+ void push_tornado(float p_frequency, float p_radius);
+ void push_rainbow(float p_saturation, float p_value, float p_frequency);
+ void push_customfx(String p_identifier, Dictionary p_environment);
void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1);
int get_current_table_column() const;
void push_cell();
@@ -380,6 +519,11 @@ public:
void set_percent_visible(float p_percent);
float get_percent_visible() const;
+ void set_effects(const Vector<Variant> &effects);
+ Vector<Variant> get_effects();
+
+ void install_effect(const Variant effect);
+
void set_fixed_size_to_width(int p_width);
virtual Size2 get_minimum_size() const;
diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h
index 5ceabfc06b..cbcee1dae3 100644
--- a/scene/gui/scroll_bar.h
+++ b/scene/gui/scroll_bar.h
@@ -33,10 +33,6 @@
#include "scene/gui/range.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
-
class ScrollBar : public Range {
GDCLASS(ScrollBar, Range);
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 461281a4ed..a840e3fec1 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -382,7 +382,10 @@ void ScrollContainer::update_scrollbars() {
Size2 min = child_max_size;
- if (!scroll_v || min.height <= size.height - hmin.height) {
+ bool hide_scroll_v = !scroll_v || min.height <= size.height - hmin.height;
+ bool hide_scroll_h = !scroll_h || min.width <= size.width - vmin.width;
+
+ if (hide_scroll_v) {
v_scroll->hide();
v_scroll->set_max(0);
@@ -391,11 +394,16 @@ void ScrollContainer::update_scrollbars() {
v_scroll->show();
v_scroll->set_max(min.height);
- v_scroll->set_page(size.height - hmin.height);
+ if (hide_scroll_h) {
+ v_scroll->set_page(size.height);
+ } else {
+ v_scroll->set_page(size.height - hmin.height);
+ }
+
scroll.y = v_scroll->get_value();
}
- if (!scroll_h || min.width <= size.width - vmin.width) {
+ if (hide_scroll_h) {
h_scroll->hide();
h_scroll->set_max(0);
@@ -404,7 +412,12 @@ void ScrollContainer::update_scrollbars() {
h_scroll->show();
h_scroll->set_max(min.width);
- h_scroll->set_page(size.width - vmin.width);
+ if (hide_scroll_v) {
+ h_scroll->set_page(size.width);
+ } else {
+ h_scroll->set_page(size.width - vmin.width);
+ }
+
scroll.x = h_scroll->get_value();
}
}
diff --git a/scene/gui/separator.h b/scene/gui/separator.h
index 54ad9b5bb5..89039f3112 100644
--- a/scene/gui/separator.h
+++ b/scene/gui/separator.h
@@ -31,10 +31,6 @@
#ifndef SEPARATOR_H
#define SEPARATOR_H
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
-
#include "scene/gui/control.h"
class Separator : public Control {
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 279253889c..172c366c41 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "spin_box.h"
+#include "core/math/expression.h"
#include "core/os/input.h"
Size2 SpinBox::get_minimum_size() const {
@@ -40,7 +41,7 @@ Size2 SpinBox::get_minimum_size() const {
void SpinBox::_value_changed(double) {
- String value = String::num(get_value(), Math::step_decimals(get_step()));
+ String value = String::num(get_value(), Math::range_step_decimals(get_step()));
if (prefix != "")
value = prefix + " " + value;
if (suffix != "")
@@ -50,15 +51,19 @@ void SpinBox::_value_changed(double) {
void SpinBox::_text_entered(const String &p_string) {
- /*
- if (!p_string.is_numeric())
+ Ref<Expression> expr;
+ expr.instance();
+ // Ignore the prefix and suffix in the expression
+ Error err = expr->parse(p_string.trim_prefix(prefix + " ").trim_suffix(" " + suffix));
+ if (err != OK) {
return;
- */
- String value = p_string;
- if (prefix != "" && p_string.begins_with(prefix))
- value = p_string.substr(prefix.length(), p_string.length() - prefix.length());
- set_value(value.to_double());
- _value_changed(0);
+ }
+
+ Variant value = expr->execute(Array(), NULL, false);
+ if (value.get_type() != Variant::NIL) {
+ set_value(value);
+ _value_changed(0);
+ }
}
LineEdit *SpinBox::get_line_edit() {
@@ -170,6 +175,10 @@ void SpinBox::_gui_input(const Ref<InputEvent> &p_event) {
void SpinBox::_line_edit_focus_exit() {
+ // discontinue because the focus_exit was caused by right-click context menu
+ if (line_edit->get_menu()->is_visible())
+ return;
+
_text_entered(line_edit->get_text());
}
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 39c76e6646..292d80be9d 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -303,7 +303,7 @@ void TabContainer::_notification(int p_what) {
// Draw the tab contents.
Control *control = Object::cast_to<Control>(tabs[i + first_tab_cache]);
- String text = control->has_meta("_tab_name") ? String(tr(String(control->get_meta("_tab_name")))) : String(control->get_name());
+ String text = control->has_meta("_tab_name") ? String(tr(String(control->get_meta("_tab_name")))) : String(tr(control->get_name()));
int x_content = tab_rect.position.x + tab_style->get_margin(MARGIN_LEFT);
int top_margin = tab_style->get_margin(MARGIN_TOP);
@@ -840,7 +840,7 @@ Size2 TabContainer::get_minimum_size() const {
Control *c = tabs[i];
- if (!c->is_visible_in_tree())
+ if (!c->is_visible_in_tree() && !use_hidden_tabs_for_min_size)
continue;
Size2 cms = c->get_combined_minimum_size();
@@ -887,6 +887,13 @@ int TabContainer::get_tabs_rearrange_group() const {
return tabs_rearrange_group;
}
+void TabContainer::set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs) {
+ use_hidden_tabs_for_min_size = p_use_hidden_tabs;
+}
+
+bool TabContainer::get_use_hidden_tabs_for_min_size() const {
+ return use_hidden_tabs_for_min_size;
+}
void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &TabContainer::_gui_input);
@@ -913,6 +920,9 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabContainer::set_tabs_rearrange_group);
ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabContainer::get_tabs_rearrange_group);
+ ClassDB::bind_method(D_METHOD("set_use_hidden_tabs_for_min_size", "enabled"), &TabContainer::set_use_hidden_tabs_for_min_size);
+ ClassDB::bind_method(D_METHOD("get_use_hidden_tabs_for_min_size"), &TabContainer::get_use_hidden_tabs_for_min_size);
+
ClassDB::bind_method(D_METHOD("_child_renamed_callback"), &TabContainer::_child_renamed_callback);
ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed);
ClassDB::bind_method(D_METHOD("_update_current_tab"), &TabContainer::_update_current_tab);
@@ -925,6 +935,7 @@ void TabContainer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hidden_tabs_for_min_size"), "set_use_hidden_tabs_for_min_size", "get_use_hidden_tabs_for_min_size");
BIND_ENUM_CONSTANT(ALIGN_LEFT);
BIND_ENUM_CONSTANT(ALIGN_CENTER);
@@ -945,4 +956,5 @@ TabContainer::TabContainer() {
popup = NULL;
drag_to_rearrange_enabled = false;
tabs_rearrange_group = -1;
+ use_hidden_tabs_for_min_size = false;
}
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index f7a9fb64fd..0314f86837 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -59,6 +59,7 @@ private:
int _get_top_margin() const;
Popup *popup;
bool drag_to_rearrange_enabled;
+ bool use_hidden_tabs_for_min_size;
int tabs_rearrange_group;
Vector<Control *> _get_tabs() const;
@@ -118,6 +119,8 @@ public:
bool get_drag_to_rearrange_enabled() const;
void set_tabs_rearrange_group(int p_group_id);
int get_tabs_rearrange_group() const;
+ void set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs);
+ bool get_use_hidden_tabs_for_min_size() const;
TabContainer();
};
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index ff0c723141..d5f1d317c7 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -129,7 +129,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
int len = text[p_line].data.length();
const CharType *str = text[p_line].data.c_str();
- //update width
+ // Update width.
for (int i = 0; i < len; i++) {
w += get_char_width(str[i], str[i + 1], w);
@@ -139,7 +139,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
text.write[p_line].wrap_amount_cache = -1;
- //update regions
+ // Update regions.
text.write[p_line].region_info.clear();
@@ -148,7 +148,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
if (!_is_symbol(str[i]))
continue;
if (str[i] == '\\') {
- i++; //skip quoted anything
+ i++; // Skip quoted anything.
continue;
}
@@ -275,7 +275,7 @@ void TextEdit::Text::clear() {
}
int TextEdit::Text::get_max_width(bool p_exclude_hidden) const {
- //quite some work.. but should be fast enough.
+ // Quite some work, but should be fast enough.
int max = 0;
for (int i = 0; i < text.size(); i++) {
@@ -323,7 +323,7 @@ int TextEdit::Text::get_char_width(CharType c, CharType next_c, int px) const {
if (left == 0)
w = tab_w;
else
- w = tab_w - px % tab_w; // is right...
+ w = tab_w - px % tab_w; // Is right.
} else {
w = font->get_char_size(c, next_c).width;
@@ -367,23 +367,25 @@ void TextEdit::_update_scrollbars() {
total_width += cache.fold_gutter_width;
}
+ if (draw_minimap) {
+ total_width += cache.minimap_width;
+ }
+
bool use_hscroll = true;
bool use_vscroll = true;
+ // Thanks yessopie for this clever bit of logic.
if (total_rows <= visible_rows && total_width <= visible_width) {
- //thanks yessopie for this clever bit of logic
+
use_hscroll = false;
use_vscroll = false;
-
} else {
if (total_rows > visible_rows && total_width <= visible_width) {
- //thanks yessopie for this clever bit of logic
use_hscroll = false;
}
if (total_rows <= visible_rows && total_width > visible_width) {
- //thanks yessopie for this clever bit of logic
use_vscroll = false;
}
}
@@ -459,6 +461,7 @@ void TextEdit::_click_selection_held() {
}
void TextEdit::_update_selection_mode_pointer() {
+ dragging_selection = true;
Point2 mp = get_local_mouse_position();
int row, col;
@@ -474,6 +477,7 @@ void TextEdit::_update_selection_mode_pointer() {
}
void TextEdit::_update_selection_mode_word() {
+ dragging_selection = true;
Point2 mp = get_local_mouse_position();
int row, col;
@@ -481,7 +485,7 @@ void TextEdit::_update_selection_mode_word() {
String line = text[row];
int beg = CLAMP(col, 0, line.length());
- // if its the first selection and on whitespace make sure we grab the word instead..
+ // If its the first selection and on whitespace make sure we grab the word instead.
if (!selection.active) {
while (beg > 0 && line[beg] <= 32) {
beg--;
@@ -490,7 +494,7 @@ void TextEdit::_update_selection_mode_word() {
int end = beg;
bool symbol = beg < line.length() && _is_symbol(line[beg]);
- // get the word end and begin points
+ // Get the word end and begin points.
while (beg > 0 && line[beg - 1] > 32 && (symbol == _is_symbol(line[beg - 1]))) {
beg--;
}
@@ -501,7 +505,7 @@ void TextEdit::_update_selection_mode_word() {
end += 1;
}
- // initial selection
+ // Initial selection.
if (!selection.active) {
select(row, beg, row, end);
selection.selecting_column = beg;
@@ -530,6 +534,7 @@ void TextEdit::_update_selection_mode_word() {
}
void TextEdit::_update_selection_mode_line() {
+ dragging_selection = true;
Point2 mp = get_local_mouse_position();
int row, col;
@@ -537,11 +542,11 @@ void TextEdit::_update_selection_mode_line() {
col = 0;
if (row < selection.selecting_line) {
- // cursor is above us
+ // Cursor is above us.
cursor_set_line(row - 1, false);
selection.selecting_column = text[selection.selecting_line].length();
} else {
- // cursor is below us
+ // Cursor is below us.
cursor_set_line(row + 1, false);
selection.selecting_column = 0;
col = text[row].length();
@@ -554,11 +559,58 @@ void TextEdit::_update_selection_mode_line() {
click_select_held->start();
}
+void TextEdit::_update_minimap_click() {
+ Point2 mp = get_local_mouse_position();
+
+ int xmargin_end = get_size().width - cache.style_normal->get_margin(MARGIN_RIGHT);
+ if (!dragging_minimap && (mp.x < xmargin_end - minimap_width || mp.y > xmargin_end)) {
+ minimap_clicked = false;
+ return;
+ }
+ minimap_clicked = true;
+ dragging_minimap = true;
+
+ int row;
+ _get_minimap_mouse_row(Point2i(mp.x, mp.y), row);
+
+ if (row >= get_first_visible_line() && (row < get_last_visible_line() || row >= (text.size() - 1))) {
+ minimap_scroll_ratio = v_scroll->get_as_ratio();
+ minimap_scroll_click_pos = mp.y;
+ can_drag_minimap = true;
+ return;
+ }
+
+ int wi;
+ int first_line = row - num_lines_from_rows(row, 0, -get_visible_rows() / 2, wi) + 1;
+ double delta = get_scroll_pos_for_line(first_line, wi) - get_v_scroll();
+ if (delta < 0) {
+ _scroll_up(-delta);
+ } else {
+ _scroll_down(delta);
+ }
+}
+
+void TextEdit::_update_minimap_drag() {
+
+ if (!can_drag_minimap) {
+ return;
+ }
+
+ int control_height = _get_control_height();
+ int scroll_height = v_scroll->get_max() * (minimap_char_size.y + minimap_line_spacing);
+ if (control_height > scroll_height) {
+ control_height = scroll_height;
+ }
+
+ Point2 mp = get_local_mouse_position();
+ double diff = (mp.y - minimap_scroll_click_pos) / control_height;
+ v_scroll->set_as_ratio(minimap_scroll_ratio + diff);
+}
+
void TextEdit::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
-
_update_caches();
if (cursor_changed_dirty)
MessageQueue::get_singleton()->push_call(this, "_cursor_changed_emit");
@@ -567,14 +619,19 @@ void TextEdit::_notification(int p_what) {
_update_wrap_at();
} break;
case NOTIFICATION_RESIZED: {
-
_update_scrollbars();
- call_deferred("_update_wrap_at");
+ _update_wrap_at();
+ } break;
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (is_visible()) {
+ call_deferred("_update_scrollbars");
+ call_deferred("_update_wrap_at");
+ }
} break;
case NOTIFICATION_THEME_CHANGED: {
-
_update_caches();
_update_wrap_at();
+ syntax_highlighting_cache.clear();
} break;
case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
window_has_focus = true;
@@ -590,24 +647,26 @@ void TextEdit::_notification(int p_what) {
if (scrolling && get_v_scroll() != target_v_scroll) {
double target_y = target_v_scroll - get_v_scroll();
double dist = sqrt(target_y * target_y);
- double vel = ((target_y / dist) * v_scroll_speed) * get_physics_process_delta_time();
+ // To ensure minimap is responsive override the speed setting.
+ double vel = ((target_y / dist) * ((minimap_clicked) ? 3000 : v_scroll_speed)) * get_physics_process_delta_time();
if (Math::abs(vel) >= dist) {
set_v_scroll(target_v_scroll);
scrolling = false;
+ minimap_clicked = false;
set_physics_process_internal(false);
} else {
set_v_scroll(get_v_scroll() + vel);
}
} else {
scrolling = false;
+ minimap_clicked = false;
set_physics_process_internal(false);
}
} break;
case NOTIFICATION_DRAW: {
-
if (first_draw) {
- //size may not be the final one, so attempts to ensure cursor was visible may have failed
+ // Size may not be the final one, so attempts to ensure cursor was visible may have failed.
adjust_viewport_to_cursor();
first_draw = false;
}
@@ -637,6 +696,11 @@ void TextEdit::_notification(int p_what) {
cache.fold_gutter_width = 0;
}
+ cache.minimap_width = 0;
+ if (draw_minimap) {
+ cache.minimap_width = minimap_width;
+ }
+
int line_number_char_count = 0;
{
@@ -660,8 +724,9 @@ void TextEdit::_notification(int p_what) {
RID ci = get_canvas_item();
VisualServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true);
int xmargin_beg = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width;
- int xmargin_end = size.width - cache.style_normal->get_margin(MARGIN_RIGHT);
- //let's do it easy for now:
+
+ int xmargin_end = size.width - cache.style_normal->get_margin(MARGIN_RIGHT) - cache.minimap_width;
+ // Let's do it easy for now.
cache.style_normal->draw(ci, Rect2(Point2(), size));
if (readonly) {
cache.style_readonly->draw(ci, Rect2(Point2(), size));
@@ -701,7 +766,7 @@ void TextEdit::_notification(int p_what) {
if (brace_matching_enabled && cursor.line >= 0 && cursor.line < text.size() && cursor.column >= 0) {
if (cursor.column < text[cursor.line].length()) {
- //check for open
+ // Check for open.
CharType c = text[cursor.line][cursor.column];
CharType closec = 0;
@@ -723,7 +788,7 @@ void TextEdit::_notification(int p_what) {
for (int j = from; j < text[i].length(); j++) {
CharType cc = text[i][j];
- //ignore any brackets inside a string
+ // Ignore any brackets inside a string.
if (cc == '"' || cc == '\'') {
CharType quotation = cc;
do {
@@ -732,7 +797,7 @@ void TextEdit::_notification(int p_what) {
break;
}
cc = text[i][j];
- //skip over escaped quotation marks inside strings
+ // Skip over escaped quotation marks inside strings.
if (cc == '\\') {
bool escaped = true;
while (j + 1 < text[i].length() && text[i][j + 1] == '\\') {
@@ -789,7 +854,7 @@ void TextEdit::_notification(int p_what) {
for (int j = from; j >= 0; j--) {
CharType cc = text[i][j];
- //ignore any brackets inside a string
+ // Ignore any brackets inside a string.
if (cc == '"' || cc == '\'') {
CharType quotation = cc;
do {
@@ -798,7 +863,7 @@ void TextEdit::_notification(int p_what) {
break;
}
cc = text[i][j];
- //skip over escaped quotation marks inside strings
+ // Skip over escaped quotation marks inside strings.
if (cc == quotation) {
bool escaped = false;
while (j - 1 >= 0 && text[i][j - 1] == '\\') {
@@ -837,10 +902,10 @@ void TextEdit::_notification(int p_what) {
Point2 cursor_pos;
int cursor_insert_offset_y = 0;
- // get the highlighted words
+ // Get the highlighted words.
String highlighted_text = get_selection_text();
- // check if highlighted words contains only whitespaces (tabs or spaces)
+ // Check if highlighted words contains only whitespaces (tabs or spaces).
bool only_whitespaces_highlighted = highlighted_text.strip_edges() == String();
String line_num_padding = line_numbers_zero_padded ? "0" : " ";
@@ -849,9 +914,156 @@ void TextEdit::_notification(int p_what) {
FontDrawer drawer(cache.font, Color(1, 1, 1));
- int line = get_first_visible_line() - 1;
+ int first_visible_line = get_first_visible_line() - 1;
int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0);
- draw_amount += times_line_wraps(line + 1);
+ draw_amount += times_line_wraps(first_visible_line + 1);
+
+ // minimap
+ if (draw_minimap) {
+ int minimap_visible_lines = _get_minimap_visible_rows();
+ int minimap_line_height = (minimap_char_size.y + minimap_line_spacing);
+ int minimap_tab_size = minimap_char_size.x * indent_size;
+
+ // calculate viewport size and y offset
+ int viewport_height = (draw_amount - 1) * minimap_line_height;
+ int control_height = _get_control_height() - viewport_height;
+ int viewport_offset_y = round(get_scroll_pos_for_line(first_visible_line) * control_height) / ((v_scroll->get_max() <= minimap_visible_lines) ? (minimap_visible_lines - draw_amount) : (v_scroll->get_max() - draw_amount));
+
+ // calculate the first line.
+ int num_lines_before = round((viewport_offset_y) / minimap_line_height);
+ int wi;
+ int minimap_line = (v_scroll->get_max() <= minimap_visible_lines) ? -1 : first_visible_line;
+ if (minimap_line >= 0) {
+ minimap_line -= num_lines_from_rows(first_visible_line, 0, -num_lines_before, wi);
+ minimap_line -= (smooth_scroll_enabled ? 1 : 0);
+ }
+ int minimap_draw_amount = minimap_visible_lines + times_line_wraps(minimap_line + 1);
+
+ // draw the minimap
+ Color viewport_color = (cache.background_color.get_v() < 0.5) ? Color(1, 1, 1, 0.1) : Color(0, 0, 0, 0.1);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), viewport_offset_y, cache.minimap_width, viewport_height), viewport_color);
+ for (int i = 0; i < minimap_draw_amount; i++) {
+
+ minimap_line++;
+
+ if (minimap_line < 0 || minimap_line >= (int)text.size()) {
+ break;
+ }
+
+ while (is_line_hidden(minimap_line)) {
+ minimap_line++;
+ if (minimap_line < 0 || minimap_line >= (int)text.size()) {
+ break;
+ }
+ }
+
+ Map<int, HighlighterInfo> color_map;
+ if (syntax_coloring) {
+ color_map = _get_line_syntax_highlighting(minimap_line);
+ }
+
+ Color current_color = cache.font_color;
+ if (readonly) {
+ current_color = cache.font_color_readonly;
+ }
+
+ Vector<String> wrap_rows = get_wrap_rows_text(minimap_line);
+ int line_wrap_amount = times_line_wraps(minimap_line);
+ int last_wrap_column = 0;
+
+ for (int line_wrap_index = 0; line_wrap_index < line_wrap_amount + 1; line_wrap_index++) {
+ if (line_wrap_index != 0) {
+ i++;
+ if (i >= minimap_draw_amount)
+ break;
+ }
+
+ const String &str = wrap_rows[line_wrap_index];
+ int indent_px = line_wrap_index != 0 ? get_indent_level(minimap_line) : 0;
+ if (indent_px >= wrap_at) {
+ indent_px = 0;
+ }
+ indent_px = minimap_char_size.x * indent_px;
+
+ if (line_wrap_index > 0) {
+ last_wrap_column += wrap_rows[line_wrap_index - 1].length();
+ }
+
+ if (minimap_line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), i * 3, cache.minimap_width, 2), cache.current_line_color);
+ }
+
+ Color previous_color;
+ int characters = 0;
+ int tabs = 0;
+ for (int j = 0; j < str.length(); j++) {
+ 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 = cache.font_color_readonly.a;
+ }
+ }
+ color = current_color;
+ }
+
+ if (j == 0) {
+ previous_color = color;
+ }
+
+ int xpos = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * j)) + tabs;
+ bool out_of_bounds = (xpos >= xmargin_end + cache.minimap_width);
+
+ bool is_whitespace = _is_whitespace(str[j]);
+ if (!is_whitespace) {
+ characters++;
+
+ if (j < str.length() - 1 && color == previous_color && !out_of_bounds) {
+ continue;
+ }
+
+ // If we've changed colour we are at the start of a new section, therefore we need to go back to the end
+ // of the previous section to draw it, we'll also add the character back on.
+ if (color != previous_color) {
+ characters--;
+ j--;
+
+ if (str[j] == '\t') {
+ tabs -= minimap_tab_size;
+ }
+ }
+ }
+
+ if (characters > 0) {
+ previous_color.a *= 0.6;
+ // take one for zero indexing, and if we hit whitespace / the end of a word.
+ int chars = MAX(0, (j - (characters - 1)) - (is_whitespace ? 1 : 0)) + 1;
+ int char_x_ofs = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * chars)) + tabs;
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_x_ofs, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color);
+ }
+
+ if (out_of_bounds) {
+ break;
+ }
+
+ // re-adjust if we went backwards.
+ if (color != previous_color && !is_whitespace) {
+ characters++;
+ }
+
+ if (str[j] == '\t') {
+ tabs += minimap_tab_size;
+ }
+
+ previous_color = color;
+ characters = 0;
+ }
+ }
+ }
+ }
+
+ // draw main text
+ int line = first_visible_line;
for (int i = 0; i < draw_amount; i++) {
line++;
@@ -875,7 +1087,7 @@ void TextEdit::_notification(int p_what) {
if (syntax_coloring) {
color_map = _get_line_syntax_highlighting(line);
}
- // ensure we at least use the font color
+ // Ensure we at least use the font color.
Color current_color = readonly ? cache.font_color_readonly : cache.font_color;
bool underlined = false;
@@ -916,7 +1128,7 @@ void TextEdit::_notification(int p_what) {
if (smooth_scroll_enabled)
ofs_y += (-get_v_scroll_offset()) * get_row_height();
- // check if line contains highlighted word
+ // Check if line contains highlighted word.
int highlighted_text_col = -1;
int search_text_col = -1;
int highlighted_word_col = -1;
@@ -938,25 +1150,25 @@ void TextEdit::_notification(int p_what) {
}
if (str.length() == 0) {
- // draw line background if empty as we won't loop at at all
+ // Draw line background if empty as we won't loop at at all.
if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, get_row_height()), cache.current_line_color);
}
- // give visual indication of empty selected line
+ // Give visual indication of empty selected line.
if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) {
int char_w = cache.font->get_char_size(' ').width;
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, char_w, get_row_height()), cache.selection_color);
}
} else {
- // if it has text, then draw current line marker in the margin, as line number etc will draw over it, draw the rest of line marker later.
+ // If it has text, then draw current line marker in the margin, as line number etc will draw over it, draw the rest of line marker later.
if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_beg, get_row_height()), cache.current_line_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_beg + ofs_x, get_row_height()), cache.current_line_color);
}
}
if (line_wrap_index == 0) {
- // only do these if we are on the first wrapped part of a line
+ // Only do these if we are on the first wrapped part of a line.
if (text.is_breakpoint(line) && !draw_breakpoint_gutter) {
#ifdef TOOLS_ENABLED
@@ -966,7 +1178,7 @@ void TextEdit::_notification(int p_what) {
#endif
}
- // draw bookmark marker
+ // Draw bookmark marker.
if (text.is_bookmark(line)) {
if (draw_bookmark_gutter) {
int vertical_gap = (get_row_height() * 40) / 100;
@@ -976,26 +1188,26 @@ void TextEdit::_notification(int p_what) {
}
}
- // draw breakpoint marker
+ // Draw breakpoint marker.
if (text.is_breakpoint(line)) {
if (draw_breakpoint_gutter) {
int vertical_gap = (get_row_height() * 40) / 100;
int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100;
int marker_height = get_row_height() - (vertical_gap * 2);
int marker_width = cache.breakpoint_gutter_width - (horizontal_gap * 2);
- // no transparency on marker
+ // No transparency on marker.
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2, ofs_y + vertical_gap, marker_width, marker_height), Color(cache.breakpoint_color.r, cache.breakpoint_color.g, cache.breakpoint_color.b));
}
}
- // draw info icons
+ // Draw info icons.
if (draw_info_gutter && text.has_info_icon(line)) {
int vertical_gap = (get_row_height() * 40) / 100;
int horizontal_gap = (cache.info_gutter_width * 30) / 100;
int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width;
Ref<Texture> info_icon = text.get_info_icon(line);
- // ensure the icon fits the gutter size
+ // Ensure the icon fits the gutter size.
Size2i icon_size = info_icon->get_size();
if (icon_size.width > cache.info_gutter_width - horizontal_gap) {
icon_size.width = cache.info_gutter_width - horizontal_gap;
@@ -1013,7 +1225,7 @@ void TextEdit::_notification(int p_what) {
draw_texture_rect(info_icon, Rect2(icon_pos, icon_size));
}
- // draw execution marker
+ // Draw execution marker.
if (executing_line == line) {
if (draw_breakpoint_gutter) {
int icon_extra_size = 4;
@@ -1031,7 +1243,7 @@ void TextEdit::_notification(int p_what) {
}
}
- // draw fold markers
+ // Draw fold markers.
if (draw_fold_gutter) {
int horizontal_gap = (cache.fold_gutter_width * 30) / 100;
int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w + cache.info_gutter_width;
@@ -1046,7 +1258,7 @@ void TextEdit::_notification(int p_what) {
}
}
- // draw line numbers
+ // Draw line numbers.
if (cache.line_number_w) {
int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
String fc = String::num(line + 1);
@@ -1058,32 +1270,35 @@ void TextEdit::_notification(int p_what) {
}
}
- //loop through characters in one line
+ // Loop through characters in one line.
for (int j = 0; j < str.length(); j++) {
if (syntax_coloring) {
if (color_map.has(last_wrap_column + j)) {
- current_color = readonly ? cache.font_color_readonly : color_map[last_wrap_column + j].color;
+ current_color = color_map[last_wrap_column + j].color;
+ if (readonly && current_color.a > cache.font_color_readonly.a) {
+ current_color.a = cache.font_color_readonly.a;
+ }
}
color = current_color;
}
int char_w;
- //handle tabulator
+ // Handle tabulator.
char_w = text.get_char_width(str[j], str[j + 1], char_ofs);
if ((char_ofs + char_margin) < xmargin_beg) {
char_ofs += char_w;
- // line highlighting handle horizontal clipping
+ // Line highlighting handle horizontal clipping.
if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
if (j == str.length() - 1) {
- // end of line when last char is skipped
+ // End of line when last char is skipped.
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color);
} else if ((char_ofs + char_margin) > xmargin_beg) {
- // char next to margin is skipped
+ // Char next to margin is skipped.
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, (char_ofs + char_margin) - (xmargin_beg + ofs_x), get_row_height()), cache.current_line_color);
}
}
@@ -1097,7 +1312,7 @@ void TextEdit::_notification(int p_what) {
bool in_search_result = false;
if (search_text_col != -1) {
- // if we are at the end check for new search result on same line
+ // If we are at the end check for new search result on same line.
if (j >= search_text_col + search_text.length())
search_text_col = _get_column_pos_of_word(search_text, str, search_flags, j);
@@ -1108,19 +1323,19 @@ void TextEdit::_notification(int p_what) {
}
}
- //current line highlighting
+ // Current line highlighting.
bool in_selection = (selection.active && line >= selection.from_line && line <= selection.to_line && (line > selection.from_line || last_wrap_column + j >= selection.from_column) && (line < selection.to_line || last_wrap_column + j < selection.to_column));
if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
- // draw the wrap indent offset highlight
+ // 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, indent_px, get_row_height()), cache.current_line_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + ofs_x - 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 its the last char draw to end of the line.
if (j == str.length() - 1) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + char_w, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + char_w + ofs_x, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color);
}
- // actual text
+ // Actual text.
if (!in_selection) {
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.current_line_color);
}
@@ -1145,14 +1360,14 @@ void TextEdit::_notification(int p_what) {
if (highlight_all_occurrences && !only_whitespaces_highlighted) {
if (highlighted_text_col != -1) {
- // if we are at the end check for new word on same line
+ // If we are at the end check for new word on same line.
if (j > highlighted_text_col + highlighted_text.length()) {
highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j);
}
bool in_highlighted_word = (j >= highlighted_text_col && j < highlighted_text_col + highlighted_text.length());
- // if this is the original highlighted text we don't want to highlight it again
+ // If this is the original highlighted text we don't want to highlight it again.
if (cursor.line == line && cursor_wrap_index == line_wrap_index && (cursor.column >= highlighted_text_col && cursor.column <= highlighted_text_col + highlighted_text.length())) {
in_highlighted_word = false;
}
@@ -1349,7 +1564,7 @@ void TextEdit::_notification(int p_what) {
bool completion_below = false;
if (completion_active) {
- // code completion box
+ // Code completion box.
Ref<StyleBox> csb = get_stylebox("completion");
int maxlines = get_constant("completion_lines");
int cmax_width = get_constant("completion_max_width") * cache.font->get_char_size('x').x;
@@ -1371,7 +1586,7 @@ void TextEdit::_notification(int p_what) {
w = cmax_width;
}
- // Add space for completion icons
+ // Add space for completion icons.
const int icon_hsep = get_constant("hseparation", "ItemList");
Size2 icon_area_size(get_row_height(), get_row_height());
w += icon_area_size.width + icon_hsep;
@@ -1403,7 +1618,7 @@ void TextEdit::_notification(int p_what) {
}
int line_from = CLAMP(completion_index - lines / 2, 0, completion_options.size() - lines);
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(completion_rect.position.x, completion_rect.position.y + (completion_index - line_from) * get_row_height()), Size2(completion_rect.size.width, get_row_height())), cache.completion_selected_color);
- draw_rect(Rect2(completion_rect.position + Vector2(icon_area_size.x + icon_hsep, 0), Size2(nofs, completion_rect.size.height)), cache.completion_existing_color);
+ draw_rect(Rect2(completion_rect.position + Vector2(icon_area_size.x + icon_hsep, 0), Size2(MIN(nofs, completion_rect.size.width - (icon_area_size.x + icon_hsep)), completion_rect.size.height)), cache.completion_existing_color);
for (int i = 0; i < lines; i++) {
@@ -1418,7 +1633,7 @@ void TextEdit::_notification(int p_what) {
int yofs = (get_row_height() - cache.font->get_height()) / 2;
Point2 title_pos(completion_rect.position.x, completion_rect.position.y + i * get_row_height() + cache.font->get_ascent() + yofs);
- //draw completion icon if it is valid
+ // Draw completion icon if it is valid.
Ref<Texture> icon = completion_options[l].icon;
Rect2 icon_area(completion_rect.position.x, completion_rect.position.y + i * get_row_height(), icon_area_size.width, icon_area_size.height);
if (icon.is_valid()) {
@@ -1430,11 +1645,11 @@ void TextEdit::_notification(int p_what) {
}
title_pos.x = icon_area.position.x + icon_area.size.width + icon_hsep;
- draw_string(cache.font, title_pos, completion_options[l].display, text_color, completion_rect.size.width);
+ draw_string(cache.font, title_pos, completion_options[l].display, text_color, completion_rect.size.width - (icon_area_size.x + icon_hsep));
}
if (scrollw) {
- //draw a small scroll rectangle to show a position in the options
+ // Draw a small scroll rectangle to show a position in the options.
float r = maxlines / (float)completion_options.size();
float o = line_from / (float)completion_options.size();
draw_rect(Rect2(completion_rect.position.x + completion_rect.size.width, completion_rect.position.y + o * completion_rect.size.y, scrollw, completion_rect.size.y * r), scrollc);
@@ -1443,7 +1658,7 @@ void TextEdit::_notification(int p_what) {
completion_line_ofs = line_from;
}
- // check to see if the hint should be drawn
+ // Check to see if the hint should be drawn.
bool show_hint = false;
if (completion_hint != "") {
if (completion_active) {
@@ -1520,11 +1735,12 @@ void TextEdit::_notification(int p_what) {
OS::get_singleton()->set_ime_active(true);
OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos + Point2(0, get_row_height()));
}
-
} break;
case NOTIFICATION_FOCUS_ENTER: {
- if (!caret_blink_enabled) {
+ if (caret_blink_enabled) {
+ caret_blink_timer->start();
+ } else {
draw_caret = true;
}
@@ -1537,6 +1753,10 @@ void TextEdit::_notification(int p_what) {
} break;
case NOTIFICATION_FOCUS_EXIT: {
+ if (caret_blink_enabled) {
+ caret_blink_timer->stop();
+ }
+
OS::get_singleton()->set_ime_position(Point2());
OS::get_singleton()->set_ime_active(false);
ime_text = "";
@@ -1662,9 +1882,9 @@ void TextEdit::backspace_at_cursor() {
_is_pair_left_symbol(text[cursor.line][cursor.column - 1])) {
_consume_backspace_for_pair_symbol(prev_line, prev_column);
} else {
- // handle space indentation
+ // Handle space indentation.
if (cursor.column != 0 && indent_using_spaces) {
- // check if there are no other chars before cursor, just indentation
+ // Check if there are no other chars before cursor, just indentation.
bool unindent = true;
int i = 0;
while (i < cursor.column && i < text[cursor.line].length()) {
@@ -1675,10 +1895,9 @@ void TextEdit::backspace_at_cursor() {
i++;
}
- // then we can remove all spaces as a single character.
+ // Then we can remove all spaces as a single character.
if (unindent) {
- // we want to remove spaces up to closest indent
- // or whole indent if cursor is pointing at it
+ // 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);
@@ -1699,8 +1918,8 @@ 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
+ // 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();
@@ -1712,7 +1931,7 @@ void TextEdit::indent_right() {
end_line = start_line;
}
- // ignore if the cursor is not past the first column
+ // Ignore if the cursor is not past the first column.
if (is_selection_active() && get_selection_to_column() == 0) {
end_line--;
}
@@ -1720,10 +1939,10 @@ void TextEdit::indent_right() {
for (int i = start_line; i <= end_line; i++) {
String line_text = get_line(i);
if (indent_using_spaces) {
- // we don't really care where selection is - we just need to know indentation level at the beginning of the line
+ // We don't really care where selection is - we just need to know indentation level at the beginning of the line.
int left = _find_first_non_whitespace_column_of_line(line_text);
int spaces_to_add = _calculate_spaces_till_next_right_indent(left);
- // since we will add this much spaces we want move whole selection and cursor by this much
+ // Since we will add 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;
@@ -1733,7 +1952,7 @@ void TextEdit::indent_right() {
set_line(i, line_text);
}
- // fix selection and cursor being off after shifting selection right
+ // Fix selection and cursor being off after shifting selection right.
if (is_selection_active()) {
select(selection.from_line, selection.from_column + selection_offset, selection.to_line, selection.to_column + selection_offset);
}
@@ -1747,10 +1966,9 @@ 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
+ // 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;
@@ -1765,7 +1983,7 @@ void TextEdit::indent_left() {
end_line = start_line;
}
- // ignore if the cursor is not past the first column
+ // Ignore if the cursor is not past the first column.
if (is_selection_active() && get_selection_to_column() == 0) {
end_line--;
}
@@ -1779,12 +1997,12 @@ void TextEdit::indent_left() {
set_line(i, line_text);
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
+ // 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
+ // 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());
@@ -1793,7 +2011,7 @@ void TextEdit::indent_left() {
}
}
- // fix selection and cursor being off by one on the last line
+ // Fix selection and cursor being off by one on the last line.
if (is_selection_active() && last_line_text != get_line(end_line)) {
select(selection.from_line, selection.from_column - removed_characters,
selection.to_line, initial_selection_end_column - removed_characters);
@@ -1834,7 +2052,7 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
}
if (row < 0)
- row = 0; //todo
+ row = 0; // TODO.
int col = 0;
@@ -1848,7 +2066,7 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
colx += cursor.x_ofs;
col = get_char_pos_for_line(colx, row, wrap_index);
if (is_wrap_enabled() && wrap_index < times_line_wraps(row)) {
- // move back one if we are at the end of the row
+ // Move back one if we are at the end of the row.
Vector<String> rows2 = get_wrap_rows_text(row);
int row_end_col = 0;
for (int i = 0; i < wrap_index + 1; i++) {
@@ -1863,6 +2081,99 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
r_col = col;
}
+Vector2i TextEdit::_get_cursor_pixel_pos() {
+ adjust_viewport_to_cursor();
+ int row = (cursor.line - get_first_visible_line() - cursor.wrap_ofs);
+ // Correct for hidden and wrapped lines
+ for (int i = get_first_visible_line(); i < cursor.line; i++) {
+ if (is_line_hidden(i)) {
+ row -= 1;
+ continue;
+ }
+ row += times_line_wraps(i);
+ }
+ // Row might be wrapped. Adjust row and r_column
+ Vector<String> rows2 = get_wrap_rows_text(cursor.line);
+ while (rows2.size() > 1) {
+ if (cursor.column >= rows2[0].length()) {
+ cursor.column -= rows2[0].length();
+ rows2.remove(0);
+ row++;
+ } else {
+ break;
+ }
+ }
+
+ // Calculate final pixel position
+ int y = (row - get_v_scroll_offset() + 1 /*Bottom of line*/) * get_row_height();
+ int x = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width - cursor.x_ofs;
+ int ix = 0;
+ while (ix < rows2[0].size() && ix < cursor.column) {
+ if (cache.font != NULL) {
+ x += cache.font->get_char_size(rows2[0].get(ix)).width;
+ }
+ ix++;
+ }
+ x += get_indent_level(cursor.line) * cache.font->get_char_size(' ').width;
+
+ return Vector2i(x, y);
+}
+
+void TextEdit::_get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const {
+
+ float rows = p_mouse.y;
+ rows -= cache.style_normal->get_margin(MARGIN_TOP);
+ rows /= (minimap_char_size.y + minimap_line_spacing);
+ rows += get_v_scroll_offset();
+
+ // calculate visible lines
+ int minimap_visible_lines = _get_minimap_visible_rows();
+ int visible_rows = get_visible_rows() + 1;
+ int first_visible_line = get_first_visible_line() - 1;
+ int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0);
+ draw_amount += times_line_wraps(first_visible_line + 1);
+ int minimap_line_height = (minimap_char_size.y + minimap_line_spacing);
+
+ // calculate viewport size and y offset
+ int viewport_height = (draw_amount - 1) * minimap_line_height;
+ int control_height = _get_control_height() - viewport_height;
+ int viewport_offset_y = round(get_scroll_pos_for_line(first_visible_line) * control_height) / ((v_scroll->get_max() <= minimap_visible_lines) ? (minimap_visible_lines - draw_amount) : (v_scroll->get_max() - draw_amount));
+
+ // calculate the first line.
+ int num_lines_before = round((viewport_offset_y) / minimap_line_height);
+ int wi;
+ int minimap_line = (v_scroll->get_max() <= minimap_visible_lines) ? -1 : first_visible_line;
+ if (first_visible_line > 0 && minimap_line >= 0) {
+ minimap_line -= num_lines_from_rows(first_visible_line, 0, -num_lines_before, wi);
+ minimap_line -= (smooth_scroll_enabled ? 1 : 0);
+ } else {
+ minimap_line = 0;
+ }
+
+ int row = minimap_line + Math::floor(rows);
+ int wrap_index = 0;
+
+ if (is_wrap_enabled() || is_hiding_enabled()) {
+
+ int f_ofs = num_lines_from_rows(minimap_line, cursor.wrap_ofs, rows + (1 * SGN(rows)), wrap_index) - 1;
+ if (rows < 0) {
+ row = minimap_line - f_ofs;
+ } else {
+ row = minimap_line + f_ofs;
+ }
+ }
+
+ if (row < 0) {
+ row = 0;
+ }
+
+ if (row >= text.size()) {
+ row = text.size() - 1;
+ }
+
+ r_row = row;
+}
+
void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
double prev_v_scroll = v_scroll->get_value();
@@ -1936,13 +2247,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
int row, col;
_get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col);
- if (mb->get_command() && highlighted_word != String()) {
-
- emit_signal("symbol_lookup", highlighted_word, row, col);
- return;
- }
-
- // toggle breakpoint on gutter click
+ // Toggle breakpoint on gutter click.
if (draw_breakpoint_gutter) {
int gutter = cache.style_normal->get_margin(MARGIN_LEFT);
if (mb->get_position().x > gutter - 6 && mb->get_position().x <= gutter + cache.breakpoint_gutter_width - 3) {
@@ -1952,7 +2257,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- // emit info clicked
+ // Emit info clicked.
if (draw_info_gutter && text.has_info_icon(row)) {
int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
int gutter_left = left_margin + cache.breakpoint_gutter_width;
@@ -1962,7 +2267,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- // toggle fold on gutter click if can
+ // Toggle fold on gutter click if can.
if (draw_fold_gutter) {
int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
@@ -1977,7 +2282,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- // unfold on folded icon click
+ // Unfold on folded icon click.
if (is_folded(row)) {
int line_width = text.get_line_width(row);
line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.info_gutter_width + cache.fold_gutter_width - cursor.x_ofs;
@@ -1987,6 +2292,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
+ // minimap
+ if (draw_minimap) {
+ _update_minimap_click();
+ if (dragging_minimap) {
+ return;
+ }
+ }
+
int prev_col = cursor.column;
int prev_line = cursor.line;
@@ -2044,9 +2357,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
} else {
- //if sel active and dblick last time < something
-
- //else
selection.active = false;
selection.selecting_mode = Selection::MODE_POINTER;
selection.selecting_line = row;
@@ -2054,14 +2364,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
if (!mb->is_doubleclick() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < 600 && cursor.line == prev_line) {
- //tripleclick select line
+
+ // Triple-click select line.
selection.selecting_mode = Selection::MODE_LINE;
_update_selection_mode_line();
last_dblclk = 0;
-
} else if (mb->is_doubleclick() && text[cursor.line].length()) {
- //doubleclick select word
+ // Double-click select word.
selection.selecting_mode = Selection::MODE_WORD;
_update_selection_mode_word();
last_dblclk = OS::get_singleton()->get_ticks_msec();
@@ -2086,7 +2396,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
int to_column = get_selection_to_column();
if (row < from_line || row > to_line || (row == from_line && col < from_column) || (row == to_line && col > to_column)) {
- // Right click is outside the selected text
+ // Right click is outside the selected text.
deselect();
}
}
@@ -2104,10 +2414,22 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
} else {
- if (mb->get_button_index() == BUTTON_LEFT)
+ if (mb->get_button_index() == BUTTON_LEFT) {
+ if (mb->get_command() && highlighted_word != String()) {
+ int row, col;
+ _get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col);
+
+ emit_signal("symbol_lookup", highlighted_word, row, col);
+ return;
+ }
+
+ dragging_minimap = false;
+ dragging_selection = false;
+ can_drag_minimap = false;
click_select_held->stop();
+ }
- // notify to show soft keyboard
+ // Notify to show soft keyboard.
notification(NOTIFICATION_FOCUS_ENTER);
}
}
@@ -2123,7 +2445,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
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
+ accept_event(); // Accept event if scroll changed.
return;
}
@@ -2133,7 +2455,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (mm.is_valid()) {
if (select_identifiers_enabled) {
- if (mm->get_command() && mm->get_button_mask() == 0) {
+ if (!dragging_minimap && !dragging_selection && mm->get_command() && mm->get_button_mask() == 0) {
String new_word = get_word_at_pos(mm->get_position());
if (new_word != highlighted_word) {
@@ -2148,34 +2470,40 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- if (mm->get_button_mask() & BUTTON_MASK_LEFT && get_viewport()->gui_get_drag_data() == Variant()) { //ignore if dragging
+ if (mm->get_button_mask() & BUTTON_MASK_LEFT && get_viewport()->gui_get_drag_data() == Variant()) { // Ignore if dragging.
_reset_caret_blink_timer();
- switch (selection.selecting_mode) {
- case Selection::MODE_POINTER: {
- _update_selection_mode_pointer();
- } break;
- case Selection::MODE_WORD: {
- _update_selection_mode_word();
- } break;
- case Selection::MODE_LINE: {
- _update_selection_mode_line();
- } break;
- default: {
- break;
+ if (draw_minimap && !dragging_selection) {
+ _update_minimap_drag();
+ }
+
+ if (!dragging_minimap) {
+ switch (selection.selecting_mode) {
+ case Selection::MODE_POINTER: {
+ _update_selection_mode_pointer();
+ } break;
+ case Selection::MODE_WORD: {
+ _update_selection_mode_word();
+ } break;
+ case Selection::MODE_LINE: {
+ _update_selection_mode_line();
+ } break;
+ default: {
+ break;
+ }
}
}
}
}
if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll)
- accept_event(); //accept event if scroll changed
+ accept_event(); // Accept event if scroll changed.
Ref<InputEventKey> k = p_gui_input;
if (k.is_valid()) {
- k = k->duplicate(); //it will be modified later on
+ k = k->duplicate(); // It will be modified later on.
#ifdef OSX_ENABLED
if (k->get_scancode() == KEY_META) {
@@ -2185,7 +2513,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
#endif
if (select_identifiers_enabled) {
- if (k->is_pressed()) {
+ if (k->is_pressed() && !dragging_minimap && !dragging_selection) {
highlighted_word = get_word_at_pos(get_local_mouse_position());
update();
@@ -2311,11 +2639,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_consume_pair_symbol(chr[0]);
} else {
- // remove the old character if in insert mode
+ // Remove the old character if in insert mode.
if (insert_mode) {
begin_complex_operation();
- // make sure we don't try and remove empty space
+ // Make sure we don't try and remove empty space.
if (cursor.column < get_line(cursor.line).length()) {
_remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
}
@@ -2337,9 +2665,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_cancel_completion();
}
- /* TEST CONTROL FIRST!! */
+ /* TEST CONTROL FIRST! */
- // some remaps for duplicate functions..
+ // Some remaps for duplicate functions.
if (k->get_command() && !k->get_shift() && !k->get_alt() && !k->get_metakey() && k->get_scancode() == KEY_INSERT) {
k->set_scancode(KEY_C);
@@ -2383,10 +2711,10 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_reset_caret_blink_timer();
- // save here for insert mode, just in case it is cleared in the following section
+ // Save here for insert mode, just in case it is cleared in the following section.
bool had_selection = selection.active;
- // stuff to do when selection is active..
+ // Stuff to do when selection is active.
if (!readonly && selection.active) {
bool clear = false;
@@ -2406,7 +2734,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
} break;
case KEY_X:
case KEY_C:
- //special keys often used with control, wait...
+ // Special keys often used with control, wait.
clear = (!k->get_command() || k->get_shift() || k->get_alt());
break;
case KEY_DELETE:
@@ -2431,7 +2759,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
case KEY_PAGEDOWN:
case KEY_HOME:
case KEY_END:
- // ignore arrows if any modifiers are held (shift = selecting, others may be used for editor hotkeys)
+ // Ignore arrows if any modifiers are held (shift = selecting, others may be used for editor hotkeys).
if (k->get_command() || k->get_shift() || k->get_alt())
break;
unselect = true;
@@ -2469,7 +2797,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
bool scancode_handled = true;
- // special scancode test...
+ // Special scancode test.
switch (k->get_scancode()) {
@@ -2481,7 +2809,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
String ins = "\n";
- //keep indentation
+ // Keep indentation.
int space_count = 0;
for (int i = 0; i < cursor.column; i++) {
if (text[cursor.line][i] == '\t') {
@@ -2512,10 +2840,10 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
bool brace_indent = false;
- // no need to indent if we are going upwards.
+ // No need to indent if we are going upwards.
if (auto_indent && !(k->get_command() && k->get_shift())) {
- // indent once again if previous line will end with ':' or '{' and the line is not a comment
- // (i.e. colon/brace precedes current cursor position)
+ // Indent once again if previous line will end with ':' or '{' and the line is not a comment
+ // (i.e. colon/brace precedes current cursor position).
if (cursor.column > 0 && (text[cursor.line][cursor.column - 1] == ':' || text[cursor.line][cursor.column - 1] == '{') && !is_line_comment(cursor.line)) {
if (indent_using_spaces) {
ins += space_indent;
@@ -2523,7 +2851,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
ins += "\t";
}
- // no need to move the brace below if we are not taking the text with us.
+ // No need to move the brace below if we are not taking the text with us.
if (text[cursor.line][cursor.column] == '}' && !k->get_command()) {
brace_indent = true;
ins += "\n" + ins.substr(1, ins.length() - 2);
@@ -2565,7 +2893,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
} break;
case KEY_TAB: {
- if (k->get_command()) break; // avoid tab when command
+ if (k->get_command()) break; // Avoid tab when command.
if (readonly)
break;
@@ -2579,7 +2907,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
} else {
if (k->get_shift()) {
- // simple unindent
+ // Simple unindent.
int cc = cursor.column;
const String &line = text[cursor.line];
@@ -2591,17 +2919,17 @@ 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
+ // Tabs unindentation.
_remove_text(cursor.line, cc - 1, cursor.line, cc);
if (cursor.column >= left)
cursor_set_column(MAX(0, cursor.column - 1));
update();
} else {
- // spaces unindentation
+ // 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?
+ if (cursor.column > left - spaces_to_remove) // Inside text?
cursor_set_column(MAX(0, cursor.column - spaces_to_remove));
update();
}
@@ -2611,9 +2939,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
update();
}
} else {
- // simple indent
+ // Simple indent.
if (indent_using_spaces) {
- // insert only as much spaces as needed till next indentation level
+ // 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++)
@@ -2641,20 +2969,20 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
int line = cursor.line;
int column = cursor.column;
- // check if we are removing a single whitespace, if so remove it and the next char type
- // else we just remove the whitespace
+ // Check if we are removing a single whitespace, if so remove it and the next char type,
+ // else we just remove the whitespace.
bool only_whitespace = false;
if (_is_whitespace(text[line][column - 1]) && _is_whitespace(text[line][column - 2])) {
only_whitespace = true;
} else if (_is_whitespace(text[line][column - 1])) {
- // remove the single whitespace
+ // Remove the single whitespace.
column--;
}
- // check if its a text char
+ // Check if its a text char.
bool only_char = (_is_text_char(text[line][column - 1]) && !only_whitespace);
- // if its not whitespace or char then symbol.
+ // If its not whitespace or char then symbol.
bool only_symbols = !(only_whitespace || only_char);
while (column > 0) {
@@ -2932,7 +3260,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (readonly)
break;
- if (k->get_shift() && !k->get_command() && !k->get_alt()) {
+ if (k->get_shift() && !k->get_command() && !k->get_alt() && is_shortcut_keys_enabled()) {
cut();
break;
}
@@ -2940,7 +3268,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
int curline_len = text[cursor.line].length();
if (cursor.line == text.size() - 1 && cursor.column == curline_len)
- break; //nothing to do
+ break; // Nothing to do.
int next_line = cursor.column < curline_len ? cursor.line : cursor.line + 1;
int next_column;
@@ -2957,20 +3285,20 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
int line = cursor.line;
int column = cursor.column;
- // check if we are removing a single whitespace, if so remove it and the next char type
- // else we just remove the whitespace
+ // Check if we are removing a single whitespace, if so remove it and the next char type,
+ // else we just remove the whitespace.
bool only_whitespace = false;
if (_is_whitespace(text[line][column]) && _is_whitespace(text[line][column + 1])) {
only_whitespace = true;
} else if (_is_whitespace(text[line][column])) {
- // remove the single whitespace
+ // Remove the single whitespace.
column++;
}
- // check if its a text char
+ // Check if its a text char.
bool only_char = (_is_text_char(text[line][column]) && !only_whitespace);
- // if its not whitespace or char then symbol.
+ // If its not whitespace or char then symbol.
bool only_symbols = !(only_whitespace || only_char);
while (column < curline_len) {
@@ -3029,7 +3357,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
cursor_set_column(0);
} else {
- // move cursor column to start of wrapped row and then to start of text
+ // Move cursor column to start of wrapped row and then to start of text.
Vector<String> rows = get_wrap_rows_text(cursor.line);
int wi = get_cursor_wrap_index();
int row_start_col = 0;
@@ -3037,7 +3365,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
row_start_col += rows[i].length();
}
if (cursor.column == row_start_col || wi == 0) {
- // compute whitespace symbols seq length
+ // Compute whitespace symbols seq length.
int current_line_whitespace_len = 0;
while (current_line_whitespace_len < text[cursor.line].length()) {
CharType c = text[cursor.line][current_line_whitespace_len];
@@ -3088,7 +3416,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (k->get_command())
cursor_set_line(get_last_unhidden_line(), true, false, 9999);
- // move cursor column to end of wrapped row and then to end of text
+ // Move cursor column to end of wrapped row and then to end of text.
Vector<String> rows = get_wrap_rows_text(cursor.line);
int wi = get_cursor_wrap_index();
int row_end_col = -1;
@@ -3163,13 +3491,15 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
scancode_handled = false;
break;
}
- select_all();
+ if (is_shortcut_keys_enabled()) {
+ select_all();
+ }
#else
if ((!k->get_command() && !k->get_control())) {
scancode_handled = false;
break;
}
- if (!k->get_shift() && k->get_command())
+ if (!k->get_shift() && k->get_command() && is_shortcut_keys_enabled())
select_all();
else if (k->get_control()) {
if (k->get_shift())
@@ -3225,8 +3555,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
scancode_handled = false;
break;
}
-
- cut();
+ if (is_shortcut_keys_enabled()) {
+ cut();
+ }
} break;
case KEY_C: {
@@ -3236,29 +3567,43 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
break;
}
- copy();
+ if (is_shortcut_keys_enabled()) {
+ copy();
+ }
} break;
case KEY_Z: {
+ if (readonly) {
+ break;
+ }
+
if (!k->get_command()) {
scancode_handled = false;
break;
}
- if (k->get_shift())
- redo();
- else
- undo();
+ if (is_shortcut_keys_enabled()) {
+ if (k->get_shift())
+ redo();
+ else
+ undo();
+ }
} break;
case KEY_Y: {
+ if (readonly) {
+ break;
+ }
+
if (!k->get_command()) {
scancode_handled = false;
break;
}
- redo();
+ if (is_shortcut_keys_enabled()) {
+ redo();
+ }
} break;
case KEY_V: {
if (readonly) {
@@ -3269,12 +3614,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
break;
}
- paste();
+ if (is_shortcut_keys_enabled()) {
+ paste();
+ }
} break;
case KEY_SPACE: {
#ifdef OSX_ENABLED
- if (completion_enabled && k->get_metakey()) { //cmd-space is spotlight shortcut in OSX
+ if (completion_enabled && k->get_metakey()) { // cmd-space is spotlight shortcut in OSX
#else
if (completion_enabled && k->get_command()) {
#endif
@@ -3287,6 +3634,16 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
} break;
+ case KEY_MENU: {
+ if (context_menu_enabled) {
+ menu->set_position(get_global_transform().xform(_get_cursor_pixel_pos()));
+ menu->set_size(Vector2(1, 1));
+ menu->set_scale(get_global_transform().get_scale());
+ menu->popup();
+ menu->grab_focus();
+ }
+ } break;
+
default: {
scancode_handled = false;
@@ -3302,18 +3659,18 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
return;
}
- if (!scancode_handled && !k->get_command()) { //for German kbds
+ if (!scancode_handled && !k->get_command()) { // For German keyboards.
if (k->get_unicode() >= 32) {
if (readonly)
return;
- // remove the old character if in insert mode and no selection
+ // Remove the old character if in insert mode and no selection.
if (insert_mode && !had_selection) {
begin_complex_operation();
- // make sure we don't try and remove empty space
+ // Make sure we don't try and remove empty space.
if (cursor.column < get_line(cursor.line).length()) {
_remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
}
@@ -3347,8 +3704,10 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
void TextEdit::_scroll_up(real_t p_delta) {
- if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(-p_delta))
+ if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(-p_delta)) {
scrolling = false;
+ minimap_clicked = false;
+ }
if (scrolling) {
target_v_scroll = (target_v_scroll - p_delta);
@@ -3373,8 +3732,10 @@ void TextEdit::_scroll_up(real_t p_delta) {
void TextEdit::_scroll_down(real_t p_delta) {
- if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(p_delta))
+ if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(p_delta)) {
scrolling = false;
+ minimap_clicked = false;
+ }
if (scrolling) {
target_v_scroll = (target_v_scroll + p_delta);
@@ -3423,11 +3784,12 @@ void TextEdit::_post_shift_selection() {
void TextEdit::_scroll_lines_up() {
scrolling = false;
+ minimap_clicked = false;
- // adjust the vertical scroll
+ // Adjust the vertical scroll.
set_v_scroll(get_v_scroll() - 1);
- // adjust the cursor to viewport
+ // Adjust the cursor to viewport.
if (!selection.active) {
int cur_line = cursor.line;
int cur_wrap = get_cursor_wrap_index();
@@ -3442,11 +3804,12 @@ void TextEdit::_scroll_lines_up() {
void TextEdit::_scroll_lines_down() {
scrolling = false;
+ minimap_clicked = false;
- // adjust the vertical scroll
+ // Adjust the vertical scroll.
set_v_scroll(get_v_scroll() + 1);
- // adjust the cursor to viewport
+ // Adjust the cursor to viewport.
if (!selection.active) {
int cur_line = cursor.line;
int cur_wrap = get_cursor_wrap_index();
@@ -3463,15 +3826,15 @@ void TextEdit::_scroll_lines_down() {
void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, int &r_end_line, int &r_end_column) {
- //save for undo...
+ // Save for undo.
ERR_FAIL_INDEX(p_line, text.size());
ERR_FAIL_COND(p_char < 0);
- /* STEP 1 remove \r from source text and separate in substrings */
+ /* STEP 1: Remove \r from source text and separate in substrings. */
Vector<String> substrings = p_text.replace("\r", "").split("\n");
- /* STEP 2 fire breakpoint_toggled signals */
+ /* STEP 2: Fire breakpoint_toggled signals. */
// Is this just a new empty line?
bool shift_first_line = p_char == 0 && p_text.replace("\r", "") == "\n";
@@ -3487,19 +3850,19 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
}
}
- /* STEP 3 add spaces if the char is greater than the end of the line */
+ /* STEP 3: Add spaces if the char is greater than the end of the line. */
while (p_char > text[p_line].length()) {
text.set(p_line, text[p_line] + String::chr(' '));
}
- /* STEP 4 separate dest string in pre and post text */
+ /* STEP 4: Separate dest string in pre and post text. */
String preinsert_text = text[p_line].substr(0, p_char);
String postinsert_text = text[p_line].substr(p_char, text[p_line].size());
for (int j = 0; j < substrings.size(); j++) {
- //insert the substrings
+ // Insert the substrings.
if (j == 0) {
@@ -3544,8 +3907,8 @@ String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_lin
ERR_FAIL_INDEX_V(p_from_column, text[p_from_line].length() + 1, String());
ERR_FAIL_INDEX_V(p_to_line, text.size(), String());
ERR_FAIL_INDEX_V(p_to_column, text[p_to_line].length() + 1, String());
- ERR_FAIL_COND_V(p_to_line < p_from_line, String()); // from > to
- ERR_FAIL_COND_V(p_to_line == p_from_line && p_to_column < p_from_column, String()); // from > to
+ ERR_FAIL_COND_V(p_to_line < p_from_line, String()); // 'from > to'.
+ ERR_FAIL_COND_V(p_to_line == p_from_line && p_to_column < p_from_column, String()); // 'from > to'.
String ret;
@@ -3568,8 +3931,8 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
ERR_FAIL_INDEX(p_from_column, text[p_from_line].length() + 1);
ERR_FAIL_INDEX(p_to_line, text.size());
ERR_FAIL_INDEX(p_to_column, text[p_to_line].length() + 1);
- ERR_FAIL_COND(p_to_line < p_from_line); // from > to
- ERR_FAIL_COND(p_to_line == p_from_line && p_to_column < p_from_column); // from > to
+ ERR_FAIL_COND(p_to_line < p_from_line); // 'from > to'.
+ ERR_FAIL_COND(p_to_line == p_from_line && p_to_column < p_from_column); // 'from > to'.
String pre_text = text[p_from_line].substr(0, p_from_column);
String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length());
@@ -3631,22 +3994,22 @@ 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 should 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();
current_op = op;
- return; //set as current op, return
+ return; // Set as current op, return.
}
- //see if it can be merged
+ // See if it can be merged.
if (current_op.to_line != p_line || current_op.to_column != p_char) {
op.prev_version = get_version();
_push_current_op();
current_op = op;
- return; //set as current op, return
+ return; // Set as current op, return.
}
- //merge current op
+ // Merge current op.
current_op.text += p_text;
current_op.to_column = retchar;
@@ -3670,7 +4033,7 @@ void TextEdit::_remove_text(int p_from_line, int p_from_column, int p_to_line, i
if (!undo_enabled)
return;
- /* UNDO!! */
+ /* UNDO! */
TextOperation op;
op.type = TextOperation::TYPE_REMOVE;
op.from_line = p_from_line;
@@ -3682,27 +4045,20 @@ 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 should 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();
current_op = op;
- return; //set as current op, return
+ return; // Set as current op, return.
}
- //see if it can be merged
+ // See if it can be merged.
if (current_op.from_line == p_to_line && current_op.from_column == p_to_column) {
- //basckace or similar
+ // Backspace or similar.
current_op.text = text + current_op.text;
current_op.from_line = p_from_line;
current_op.from_column = p_from_column;
- return; //update current op
- }
- if (current_op.from_line == p_from_line && current_op.from_column == p_from_column) {
-
- //current_op.text=text+current_op.text;
- //current_op.from_line=p_from_line;
- //current_op.from_column=p_from_column;
- //return; //update current op
+ return; // Update current op.
}
op.prev_version = get_version();
@@ -3725,6 +4081,15 @@ void TextEdit::_line_edited_from(int p_line) {
for (int i = p_line; i < cache_size; i++) {
color_region_cache.erase(i);
}
+
+ if (syntax_highlighting_cache.size() > 0) {
+ cache_size = syntax_highlighting_cache.back()->key();
+ for (int i = p_line - 1; i <= cache_size; i++) {
+ if (syntax_highlighting_cache.has(i)) {
+ syntax_highlighting_cache.erase(i);
+ }
+ }
+ }
}
int TextEdit::get_char_count() {
@@ -3734,11 +4099,11 @@ int TextEdit::get_char_count() {
for (int i = 0; i < text.size(); i++) {
if (i > 0)
- totalsize++; // incliude \n
+ totalsize++; // Include \n.
totalsize += text[i].length();
}
- return totalsize; // omit last \n
+ return totalsize; // Omit last \n.
}
Size2 TextEdit::get_minimum_size() const {
@@ -3746,19 +4111,45 @@ Size2 TextEdit::get_minimum_size() const {
return cache.style_normal->get_minimum_size();
}
+int TextEdit::_get_control_height() const {
+ int control_height = get_size().height;
+ control_height -= cache.style_normal->get_minimum_size().height;
+ if (h_scroll->is_visible_in_tree()) {
+ control_height -= h_scroll->get_size().height;
+ }
+ return control_height;
+}
+
+void TextEdit::_generate_context_menu() {
+ // Reorganize context menu.
+ menu->clear();
+ if (!readonly)
+ menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_X : 0);
+ menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_C : 0);
+ if (!readonly)
+ menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_V : 0);
+ menu->add_separator();
+ if (is_selecting_enabled())
+ menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_A : 0);
+ if (!readonly) {
+ menu->add_item(RTR("Clear"), MENU_CLEAR);
+ menu->add_separator();
+ menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_Z : 0);
+ menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z : 0);
+ }
+}
+
int TextEdit::get_visible_rows() const {
+ return _get_control_height() / get_row_height();
+}
- int total = get_size().height;
- total -= cache.style_normal->get_minimum_size().height;
- if (h_scroll->is_visible_in_tree())
- total -= h_scroll->get_size().height;
- total /= get_row_height();
- return total;
+int TextEdit::_get_minimap_visible_rows() const {
+ return _get_control_height() / (minimap_char_size.y + minimap_line_spacing);
}
int TextEdit::get_total_visible_rows() const {
- // returns the total amount of rows we need in the editor.
+ // Returns the total amount of rows we need in the editor.
// This skips hidden lines and counts each wrapping of a line.
if (!is_hiding_enabled() && !is_wrap_enabled())
return text.size();
@@ -3775,12 +4166,12 @@ int TextEdit::get_total_visible_rows() const {
void TextEdit::_update_wrap_at() {
- wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width - wrap_right_offset;
+ wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width - cache.minimap_width - wrap_right_offset;
update_cursor_wrap_offset();
text.clear_wrap_cache();
for (int i = 0; i < text.size(); i++) {
- // update all values that wrap
+ // Update all values that wrap.
if (!line_wraps(i))
continue;
Vector<String> rows = get_wrap_rows_text(i);
@@ -3790,8 +4181,9 @@ void TextEdit::_update_wrap_at() {
void TextEdit::adjust_viewport_to_cursor() {
- // make sure cursor is visible on the screen
+ // Make sure cursor is visible on the screen.
scrolling = false;
+ minimap_clicked = false;
int cur_line = cursor.line;
int cur_wrap = get_cursor_wrap_index();
@@ -3802,20 +4194,20 @@ void TextEdit::adjust_viewport_to_cursor() {
int last_vis_wrap = get_last_visible_line_wrap_index();
if (cur_line < first_vis_line || (cur_line == first_vis_line && cur_wrap < first_vis_wrap)) {
- // cursor is above screen
+ // Cursor is above screen.
set_line_as_first_visible(cur_line, cur_wrap);
} else if (cur_line > last_vis_line || (cur_line == last_vis_line && cur_wrap > last_vis_wrap)) {
- // cursor is below screen
+ // Cursor is below screen.
set_line_as_last_visible(cur_line, cur_wrap);
}
- int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width;
+ int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width - cache.minimap_width;
if (v_scroll->is_visible_in_tree())
visible_width -= v_scroll->get_combined_minimum_size().width;
- visible_width -= 20; // give it a little more space
+ visible_width -= 20; // Give it a little more space.
if (!is_wrap_enabled()) {
- // adjust x offset
+ // Adjust x offset.
int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]);
if (cursor_x > (cursor.x_ofs + visible_width))
@@ -3833,20 +4225,21 @@ void TextEdit::adjust_viewport_to_cursor() {
void TextEdit::center_viewport_to_cursor() {
- // move viewport so the cursor is in the center of the screen
+ // Move viewport so the cursor is in the center of the screen.
scrolling = false;
+ minimap_clicked = false;
if (is_line_hidden(cursor.line))
unfold_line(cursor.line);
set_line_as_center_visible(cursor.line, get_cursor_wrap_index());
- int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width;
+ int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - cache.info_gutter_width - cache.minimap_width;
if (v_scroll->is_visible_in_tree())
visible_width -= v_scroll->get_combined_minimum_size().width;
- visible_width -= 20; // give it a little more space
+ visible_width -= 20; // Give it a little more space.
if (is_wrap_enabled()) {
- // center x offset
+ // Center x offset.
int cursor_x = get_column_x_offset_for_line(cursor.column, cursor.line);
if (cursor_x > (cursor.x_ofs + visible_width))
@@ -3888,7 +4281,7 @@ int TextEdit::times_line_wraps(int line) const {
int wrap_amount = text.get_line_wrap_amount(line);
if (wrap_amount == -1) {
- // update the value
+ // Update the value.
Vector<String> rows = get_wrap_rows_text(line);
wrap_amount = rows.size() - 1;
text.set_line_wrap_amount(line, wrap_amount);
@@ -3928,7 +4321,7 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const {
int indent_ofs = (cur_wrap_index != 0 ? tab_offset_px : 0);
if (indent_ofs + word_px + w > wrap_at) {
- // not enough space to add this char; start next line
+ // Not enough space to add this char; start next line.
wrap_substring += word_str;
lines.push_back(wrap_substring);
cur_wrap_index++;
@@ -3942,7 +4335,7 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const {
word_str += c;
word_px += w;
if (c == ' ') {
- // end of a word; add this word to the substring
+ // End of a word; add this word to the substring.
wrap_substring += word_str;
px += word_px;
word_str = "";
@@ -3950,9 +4343,9 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const {
}
if (indent_ofs + px + word_px > wrap_at) {
- // this word will be moved to the next line
+ // This word will be moved to the next line.
lines.push_back(wrap_substring);
- // reset for next wrap
+ // Reset for next wrap.
cur_wrap_index++;
wrap_substring = "";
px = 0;
@@ -3960,11 +4353,11 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const {
}
col++;
}
- // line ends before hit wrap_at; add this word to the substring
+ // Line ends before hit wrap_at; add this word to the substring.
wrap_substring += word_str;
lines.push_back(wrap_substring);
- // update cache
+ // Update cache.
text.set_line_wrap_amount(p_line, lines.size() - 1);
return lines;
@@ -3982,7 +4375,7 @@ int TextEdit::get_line_wrap_index_at_col(int p_line, int p_column) const {
if (!line_wraps(p_line))
return 0;
- // loop through wraps in the line text until we get to the column
+ // Loop through wraps in the line text until we get to the column.
int wrap_index = 0;
int col = 0;
Vector<String> rows = get_wrap_rows_text(p_line);
@@ -4087,11 +4480,14 @@ bool TextEdit::cursor_get_blink_enabled() const {
void TextEdit::cursor_set_blink_enabled(const bool p_enabled) {
caret_blink_enabled = p_enabled;
- if (p_enabled) {
- caret_blink_timer->start();
- } else {
- caret_blink_timer->stop();
+ if (has_focus()) {
+ if (p_enabled) {
+ caret_blink_timer->start();
+ } else {
+ caret_blink_timer->stop();
+ }
}
+
draw_caret = true;
}
@@ -4123,6 +4519,7 @@ bool TextEdit::is_right_click_moving_caret() const {
void TextEdit::_v_scroll_input() {
scrolling = false;
+ minimap_clicked = false;
}
void TextEdit::_scroll_moved(double p_to_val) {
@@ -4134,7 +4531,7 @@ void TextEdit::_scroll_moved(double p_to_val) {
cursor.x_ofs = h_scroll->get_value();
if (v_scroll->is_visible_in_tree()) {
- // set line ofs and wrap ofs
+ // Set line ofs and wrap ofs.
int v_scroll_i = floor(get_v_scroll());
int sc = 0;
int n_line;
@@ -4289,12 +4686,12 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
_get_mouse_pos(p_pos, row, col);
int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
- // breakpoint icon
+ // Breakpoint icon.
if (draw_breakpoint_gutter && p_pos.x > left_margin - 6 && p_pos.x <= left_margin + cache.breakpoint_gutter_width - 3) {
return CURSOR_POINTING_HAND;
}
- // info icons
+ // Info icons.
int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.info_gutter_width;
if (draw_info_gutter && p_pos.x > left_margin + cache.breakpoint_gutter_width - 6 && p_pos.x <= gutter_left - 3) {
if (text.has_info_icon(row)) {
@@ -4303,7 +4700,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
return CURSOR_ARROW;
}
- // fold icon
+ // Fold icon.
if (draw_fold_gutter && p_pos.x > gutter_left + cache.line_number_w - 6 && p_pos.x <= gutter_left + cache.line_number_w + cache.fold_gutter_width - 3) {
if (is_folded(row) || can_fold(row))
return CURSOR_POINTING_HAND;
@@ -4313,9 +4710,14 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
return CURSOR_ARROW;
} else {
+ int xmargin_end = get_size().width - cache.style_normal->get_margin(MARGIN_RIGHT);
+ if (draw_minimap && p_pos.x > xmargin_end - minimap_width && p_pos.x <= xmargin_end) {
+ return CURSOR_ARROW;
+ }
+
int row, col;
_get_mouse_pos(p_pos, row, col);
- // eol fold icon
+ // EOL fold icon.
if (is_folded(row)) {
int line_width = text.get_line_width(row);
line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width - cursor.x_ofs;
@@ -4349,8 +4751,6 @@ void TextEdit::set_text(String p_text) {
update();
setting_text = false;
-
- //get_range()->set(0);
};
String TextEdit::get_text() {
@@ -4377,7 +4777,7 @@ String TextEdit::get_text_for_lookup_completion() {
if (i == row) {
longthing += text[i].substr(0, col);
- longthing += String::chr(0xFFFF); //not unicode, represents the cursor
+ longthing += String::chr(0xFFFF); // Not unicode, represents the cursor.
longthing += text[i].substr(col, text[i].size());
} else {
@@ -4399,7 +4799,7 @@ String TextEdit::get_text_for_completion() {
if (i == cursor.line) {
longthing += text[i].substr(0, cursor.column);
- longthing += String::chr(0xFFFF); //not unicode, represents the cursor
+ longthing += String::chr(0xFFFF); // Not unicode, represents the cursor.
longthing += text[i].substr(cursor.column, text[i].size());
} else {
@@ -4447,21 +4847,32 @@ void TextEdit::set_readonly(bool p_readonly) {
return;
readonly = p_readonly;
+ _generate_context_menu();
// Reorganize context menu.
menu->clear();
- if (!readonly)
+
+ if (!readonly) {
+ menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z);
+ menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z);
+ }
+
+ if (!readonly) {
+ menu->add_separator();
menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X);
+ }
+
menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C);
- if (!readonly)
+
+ if (!readonly) {
menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V);
+ }
+
menu->add_separator();
menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A);
+
if (!readonly) {
menu->add_item(RTR("Clear"), MENU_CLEAR);
- menu->add_separator();
- menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z);
- menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z);
}
update();
@@ -4494,10 +4905,12 @@ int TextEdit::get_max_chars() const {
void TextEdit::_reset_caret_blink_timer() {
if (caret_blink_enabled) {
- caret_blink_timer->stop();
- caret_blink_timer->start();
draw_caret = true;
- update();
+ if (has_focus()) {
+ caret_blink_timer->stop();
+ caret_blink_timer->start();
+ update();
+ }
}
}
@@ -4572,17 +4985,18 @@ void TextEdit::_set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter)
syntax_highlighter->set_text_editor(this);
syntax_highlighter->_update_cache();
}
+ syntax_highlighting_cache.clear();
update();
}
int TextEdit::_is_line_in_region(int p_line) {
- // do we have in cache?
+ // Do we have in cache?
if (color_region_cache.has(p_line)) {
return color_region_cache[p_line];
}
- // if not find the closest line we have
+ // If not find the closest line we have.
int previous_line = p_line - 1;
for (; previous_line > -1; previous_line--) {
if (color_region_cache.has(p_line)) {
@@ -4590,7 +5004,7 @@ int TextEdit::_is_line_in_region(int p_line) {
}
}
- // calculate up to line we need and update the cache along the way.
+ // Calculate up to line we need and update the cache along the way.
int in_region = color_region_cache[previous_line];
if (previous_line == -1) {
in_region = -1;
@@ -4638,6 +5052,7 @@ void TextEdit::clear_colors() {
keywords.clear();
color_regions.clear();
color_region_cache.clear();
+ syntax_highlighting_cache.clear();
text.clear_width_cache();
}
@@ -4718,7 +5133,7 @@ void TextEdit::cut() {
OS::get_singleton()->set_clipboard(clipboard);
_remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column);
- cursor_set_line(selection.from_line); // set afterwards else it causes the view to be offset
+ cursor_set_line(selection.from_line); // Set afterwards else it causes the view to be offset.
cursor_set_column(selection.from_column);
selection.active = false;
@@ -4772,6 +5187,8 @@ void TextEdit::paste() {
}
void TextEdit::select_all() {
+ if (!selecting_enabled)
+ return;
if (text.size() == 1 && text[0].length() == 0)
return;
@@ -4796,6 +5213,8 @@ void TextEdit::deselect() {
}
void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_to_column) {
+ if (!selecting_enabled)
+ return;
if (p_from_line < 0)
p_from_line = 0;
@@ -4947,7 +5366,7 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc
col = p_search.findn(p_key, p_from_column);
}
- // whole words only
+ // Whole words only.
if (col != -1 && p_search_flags & SEARCH_WHOLE_WORDS) {
p_from_column = col;
@@ -4987,14 +5406,12 @@ bool TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_l
ERR_FAIL_INDEX_V(p_from_line, text.size(), false);
ERR_FAIL_INDEX_V(p_from_column, text[p_from_line].length() + 1, false);
- //search through the whole document, but start by current line
+ // Search through the whole document, but start by current line.
int line = p_from_line;
int pos = -1;
for (int i = 0; i < text.size() + 1; i++) {
- //backwards is broken...
- //int idx=(p_search_flags&SEARCH_BACKWARDS)?(text.size()-i):i; //do backwards seearch
if (line < 0) {
line = text.size() - 1;
@@ -5008,7 +5425,7 @@ bool TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_l
if (line == p_from_line) {
if (i == text.size()) {
- //wrapped
+ // Wrapped.
if (p_search_flags & SEARCH_BACKWARDS) {
from_column = text_line.length();
@@ -5042,6 +5459,9 @@ bool TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_l
break;
}
pos_from = last_pos - p_key.length();
+ if (pos_from < 0) {
+ break;
+ }
}
} else {
while ((last_pos = (p_search_flags & SEARCH_MATCH_CASE) ? text_line.find(p_key, pos_from) : text_line.findn(p_key, pos_from)) != -1) {
@@ -5056,7 +5476,7 @@ bool TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_l
bool is_match = true;
if (pos != -1 && (p_search_flags & SEARCH_WHOLE_WORDS)) {
- //validate for whole words
+ // Validate for whole words.
if (pos > 0 && _is_text_char(text_line[pos - 1]))
is_match = false;
else if (pos + p_key.length() < text_line.length() && _is_text_char(text_line[pos + p_key.length()]))
@@ -5254,7 +5674,7 @@ void TextEdit::unhide_all_lines() {
int TextEdit::num_lines_from(int p_line_from, int visible_amount) const {
- // returns the number of lines (hidden and unhidden) from p_line_from to (p_line_from + visible_amount of unhidden lines)
+ // Returns the number of lines (hidden and unhidden) from p_line_from to (p_line_from + visible_amount of unhidden lines).
ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(visible_amount));
if (!is_hiding_enabled())
@@ -5287,8 +5707,8 @@ int TextEdit::num_lines_from(int p_line_from, int visible_amount) const {
int TextEdit::num_lines_from_rows(int p_line_from, int p_wrap_index_from, int visible_amount, int &wrap_index) const {
- // returns the number of lines (hidden and unhidden) from (p_line_from + p_wrap_index_from) row to (p_line_from + visible_amount of unhidden and wrapped rows)
- // wrap index is set to the wrap index of the last line
+ // Returns the number of lines (hidden and unhidden) from (p_line_from + p_wrap_index_from) row to (p_line_from + visible_amount of unhidden and wrapped rows).
+ // Wrap index is set to the wrap index of the last line.
wrap_index = 0;
ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(visible_amount));
@@ -5334,7 +5754,7 @@ int TextEdit::num_lines_from_rows(int p_line_from, int p_wrap_index_from, int vi
int TextEdit::get_last_unhidden_line() const {
- // returns the last line in the text that is not hidden
+ // Returns the last line in the text that is not hidden.
if (!is_hiding_enabled())
return text.size() - 1;
@@ -5351,7 +5771,7 @@ int TextEdit::get_indent_level(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
- // counts number of tabs and spaces before line starts
+ // Counts number of tabs and spaces before line starts.
int tab_count = 0;
int whitespace_count = 0;
int line_length = text[p_line].size();
@@ -5369,7 +5789,7 @@ int TextEdit::get_indent_level(int p_line) const {
bool TextEdit::is_line_comment(int p_line) const {
- // checks to see if this line is the start of a comment
+ // Checks to see if this line is the start of a comment.
ERR_FAIL_INDEX_V(p_line, text.size(), false);
const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(p_line);
@@ -5449,7 +5869,7 @@ void TextEdit::fold_line(int p_line) {
if (!can_fold(p_line))
return;
- // hide lines below this one
+ // Hide lines below this one.
int start_indent = get_indent_level(p_line);
int last_line = start_indent;
for (int i = p_line + 1; i < text.size(); i++) {
@@ -5467,7 +5887,7 @@ void TextEdit::fold_line(int p_line) {
set_line_as_hidden(i, true);
}
- // fix selection
+ // Fix selection.
if (is_selection_active()) {
if (is_line_hidden(selection.from_line) && is_line_hidden(selection.to_line)) {
deselect();
@@ -5478,7 +5898,7 @@ void TextEdit::fold_line(int p_line) {
}
}
- // reset cursor
+ // Reset cursor.
if (is_line_hidden(cursor.line)) {
cursor_set_line(p_line, false, false);
cursor_set_column(get_line(p_line).length(), false);
@@ -5539,8 +5959,8 @@ void TextEdit::_do_text_op(const TextOperation &p_op, bool p_reverse) {
int check_line;
int check_column;
_base_insert_text(p_op.from_line, p_op.from_column, p_op.text, check_line, check_column);
- ERR_FAIL_COND(check_line != p_op.to_line); // BUG
- ERR_FAIL_COND(check_column != p_op.to_column); // BUG
+ ERR_FAIL_COND(check_line != p_op.to_line); // BUG.
+ ERR_FAIL_COND(check_column != p_op.to_column); // BUG.
} else {
_base_remove_text(p_op.from_line, p_op.from_column, p_op.to_line, p_op.to_column);
@@ -5550,7 +5970,7 @@ void TextEdit::_do_text_op(const TextOperation &p_op, bool p_reverse) {
void TextEdit::_clear_redo() {
if (undo_stack_pos == NULL)
- return; //nothing to clear
+ return; // Nothing to clear.
_push_current_op();
@@ -5568,12 +5988,12 @@ void TextEdit::undo() {
if (undo_stack_pos == NULL) {
if (!undo_stack.size())
- return; //nothing to undo
+ return; // Nothing to undo.
undo_stack_pos = undo_stack.back();
} else if (undo_stack_pos == undo_stack.front())
- return; // at the bottom of the undo stack
+ return; // At the bottom of the undo stack.
else
undo_stack_pos = undo_stack_pos->prev();
@@ -5614,7 +6034,7 @@ void TextEdit::redo() {
_push_current_op();
if (undo_stack_pos == NULL)
- return; //nothing to do.
+ return; // Nothing to do.
deselect();
@@ -5668,7 +6088,7 @@ void TextEdit::end_complex_operation() {
void TextEdit::_push_current_op() {
if (current_op.type == TextOperation::TYPE_NONE)
- return; // do nothing
+ return; // Nothing to do.
if (next_operation_is_complex) {
current_op.chain_forward = true;
@@ -5768,7 +6188,7 @@ double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const {
if (!is_wrap_enabled() && !is_hiding_enabled())
return p_line;
- // count the number of visible lines up to this line
+ // Count the number of visible lines up to this line.
double new_line_scroll_pos = 0;
int to = CLAMP(p_line, 0, text.size() - 1);
for (int i = 0; i < to; i++) {
@@ -5828,10 +6248,7 @@ int TextEdit::get_last_visible_line_wrap_index() const {
double TextEdit::get_visible_rows_offset() const {
- double total = get_size().height;
- total -= cache.style_normal->get_minimum_size().height;
- if (h_scroll->is_visible_in_tree())
- total -= h_scroll->get_size().height;
+ double total = _get_control_height();
total /= (double)get_row_height();
total = total - floor(total);
total = -CLAMP(total, 0.001, 1) + 1;
@@ -5913,7 +6330,7 @@ void TextEdit::_confirm_completion() {
CharType last_completion_char = completion_current.insert_text[completion_current.insert_text.length() - 1];
if ((last_completion_char == '"' || last_completion_char == '\'') && last_completion_char == next_char) {
- _base_remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
+ _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
}
if (last_completion_char == '(') {
@@ -5969,7 +6386,7 @@ void TextEdit::_update_completion_candidates() {
String s;
- //look for keywords first
+ // Look for keywords first.
bool inquote = false;
int first_quote = -1;
@@ -5994,7 +6411,7 @@ void TextEdit::_update_completion_candidates() {
bool cancel = false;
if (!inquote && first_quote == cofs - 1) {
- //no completion here
+ // No completion here.
cancel = true;
} else if (inquote && first_quote != -1) {
@@ -6032,11 +6449,12 @@ void TextEdit::_update_completion_candidates() {
bool prev_is_prefix = false;
if (cofs > 0 && completion_prefixes.has(String::chr(l[cofs - 1])))
prev_is_prefix = true;
- if (cofs > 1 && l[cofs - 1] == ' ' && completion_prefixes.has(String::chr(l[cofs - 2]))) //check with one space before prefix, to allow indent
+ // Check with one space before prefix, to allow indent.
+ if (cofs > 1 && l[cofs - 1] == ' ' && completion_prefixes.has(String::chr(l[cofs - 2])))
prev_is_prefix = true;
if (cancel || (!pre_keyword && s == "" && (cofs == 0 || !prev_is_prefix))) {
- //none to complete, cancel
+ // None to complete, cancel.
_cancel_completion();
return;
}
@@ -6086,18 +6504,18 @@ void TextEdit::_update_completion_candidates() {
}
if (completion_options.size() == 0) {
- //no options to complete, cancel
+ // No options to complete, cancel.
_cancel_completion();
return;
}
if (completion_options.size() == 1 && s == completion_options[0].display) {
- // A perfect match, stop completion
+ // A perfect match, stop completion.
_cancel_completion();
return;
}
- // The top of the list is the best match
+ // The top of the list is the best match.
completion_current = completion_options[0];
completion_enabled = true;
}
@@ -6116,10 +6534,30 @@ void TextEdit::query_code_comple() {
c--;
}
- if (ofs > 0 && (inquote || _is_completable(l[ofs - 1]) || completion_prefixes.has(String::chr(l[ofs - 1]))))
- emit_signal("request_completion");
- else if (ofs > 1 && l[ofs - 1] == ' ' && completion_prefixes.has(String::chr(l[ofs - 2]))) //make it work with a space too, it's good enough
- emit_signal("request_completion");
+ bool ignored = completion_active && !completion_options.empty();
+ if (ignored) {
+ ScriptCodeCompletionOption::Kind kind = ScriptCodeCompletionOption::KIND_PLAIN_TEXT;
+ const ScriptCodeCompletionOption *previous_option = NULL;
+ for (int i = 0; i < completion_options.size(); i++) {
+ const ScriptCodeCompletionOption &current_option = completion_options[i];
+ if (!previous_option) {
+ previous_option = &current_option;
+ kind = current_option.kind;
+ }
+ if (previous_option->kind != current_option.kind) {
+ ignored = false;
+ break;
+ }
+ }
+ ignored = ignored && (kind == ScriptCodeCompletionOption::KIND_FILE_PATH || kind == ScriptCodeCompletionOption::KIND_NODE_PATH || kind == ScriptCodeCompletionOption::KIND_SIGNAL);
+ }
+
+ if (!ignored) {
+ if (ofs > 0 && (inquote || _is_completable(l[ofs - 1]) || completion_prefixes.has(String::chr(l[ofs - 1]))))
+ emit_signal("request_completion");
+ else if (ofs > 1 && l[ofs - 1] == ' ' && completion_prefixes.has(String::chr(l[ofs - 2]))) // Make it work with a space too, it's good enough.
+ emit_signal("request_completion");
+ }
}
void TextEdit::set_code_hint(const String &p_hint) {
@@ -6220,9 +6658,21 @@ void TextEdit::set_line(int line, String new_text) {
}
void TextEdit::insert_at(const String &p_text, int at) {
- cursor_set_column(0);
- cursor_set_line(at, false, true);
_insert_text(at, 0, p_text + "\n");
+ if (cursor.line >= at) {
+ // offset cursor when located after inserted line
+ ++cursor.line;
+ }
+ if (is_selection_active()) {
+ if (selection.from_line >= at) {
+ // offset selection when located after inserted line
+ ++selection.from_line;
+ ++selection.to_line;
+ } else if (selection.to_line >= at) {
+ // extend selection that includes inserted line
+ ++selection.to_line;
+ }
+ }
}
void TextEdit::set_show_line_numbers(bool p_show) {
@@ -6314,6 +6764,24 @@ int TextEdit::get_info_gutter_width() const {
return info_gutter_width;
}
+void TextEdit::set_draw_minimap(bool p_draw) {
+ draw_minimap = p_draw;
+ update();
+}
+
+bool TextEdit::is_drawing_minimap() const {
+ return draw_minimap;
+}
+
+void TextEdit::set_minimap_width(int p_minimap_width) {
+ minimap_width = p_minimap_width;
+ update();
+}
+
+int TextEdit::get_minimap_width() const {
+ return minimap_width;
+}
+
void TextEdit::set_hiding_enabled(bool p_enabled) {
if (!p_enabled)
unhide_all_lines();
@@ -6390,6 +6858,29 @@ bool TextEdit::is_context_menu_enabled() {
return context_menu_enabled;
}
+void TextEdit::set_shortcut_keys_enabled(bool p_enabled) {
+ shortcut_keys_enabled = p_enabled;
+
+ _generate_context_menu();
+}
+
+void TextEdit::set_selecting_enabled(bool p_enabled) {
+ selecting_enabled = p_enabled;
+
+ if (!selecting_enabled)
+ deselect();
+
+ _generate_context_menu();
+}
+
+bool TextEdit::is_selecting_enabled() const {
+ return selecting_enabled;
+}
+
+bool TextEdit::is_shortcut_keys_enabled() const {
+ return shortcut_keys_enabled;
+}
+
PopupMenu *TextEdit::get_menu() const {
return menu;
}
@@ -6443,10 +6934,12 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_wrap_enabled", "enable"), &TextEdit::set_wrap_enabled);
ClassDB::bind_method(D_METHOD("is_wrap_enabled"), &TextEdit::is_wrap_enabled);
- // ClassDB::bind_method(D_METHOD("set_max_chars", "amount"), &TextEdit::set_max_chars);
- // ClassDB::bind_method(D_METHOD("get_max_char"), &TextEdit::get_max_chars);
ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &TextEdit::set_context_menu_enabled);
ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &TextEdit::is_context_menu_enabled);
+ ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &TextEdit::set_shortcut_keys_enabled);
+ ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &TextEdit::is_shortcut_keys_enabled);
+ ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &TextEdit::set_selecting_enabled);
+ ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &TextEdit::is_selecting_enabled);
ClassDB::bind_method(D_METHOD("cut"), &TextEdit::cut);
ClassDB::bind_method(D_METHOD("copy"), &TextEdit::copy);
@@ -6520,6 +7013,11 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_breakpoints"), &TextEdit::get_breakpoints_array);
ClassDB::bind_method(D_METHOD("remove_breakpoints"), &TextEdit::remove_breakpoints);
+ ClassDB::bind_method(D_METHOD("draw_minimap", "draw"), &TextEdit::set_draw_minimap);
+ ClassDB::bind_method(D_METHOD("is_drawing_minimap"), &TextEdit::is_drawing_minimap);
+ ClassDB::bind_method(D_METHOD("set_minimap_width", "width"), &TextEdit::set_minimap_width);
+ ClassDB::bind_method(D_METHOD("get_minimap_width"), &TextEdit::get_minimap_width);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "readonly"), "set_readonly", "is_readonly");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled");
@@ -6532,11 +7030,16 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_scrolling"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hiding_enabled"), "set_hiding_enabled", "is_hiding_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_enabled"), "set_wrap_enabled", "is_wrap_enabled");
- // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "max_chars"), "set_max_chars", "get_max_chars");
+
+ ADD_GROUP("Minimap", "minimap_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_draw"), "draw_minimap", "is_drawing_minimap");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "minimap_width"), "set_minimap_width", "get_minimap_width");
ADD_GROUP("Caret", "caret_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_block_mode"), "cursor_set_block_mode", "cursor_is_block_mode");
@@ -6561,7 +7064,7 @@ void TextEdit::_bind_methods() {
BIND_ENUM_CONSTANT(MENU_MAX);
GLOBAL_DEF("gui/timers/text_edit_idle_detect_sec", 3);
- ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/text_edit_idle_detect_sec", PropertyInfo(Variant::REAL, "gui/timers/text_edit_idle_detect_sec", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater")); // No negative numbers
+ ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/text_edit_idle_detect_sec", PropertyInfo(Variant::REAL, "gui/timers/text_edit_idle_detect_sec", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater")); // No negative numbers.
}
TextEdit::TextEdit() {
@@ -6592,8 +7095,6 @@ TextEdit::TextEdit() {
indent_size = 4;
text.set_indent_size(indent_size);
text.clear();
- //text.insert(1,"Mongolia...");
- //text.insert(2,"PAIS GENEROSO!!");
text.set_color_regions(&color_regions);
h_scroll = memnew(HScrollBar);
@@ -6676,13 +7177,25 @@ TextEdit::TextEdit() {
select_identifiers_enabled = false;
smooth_scroll_enabled = false;
scrolling = false;
+ minimap_clicked = false;
+ dragging_minimap = false;
+ can_drag_minimap = false;
+ minimap_scroll_ratio = 0;
+ minimap_scroll_click_pos = 0;
+ dragging_selection = false;
target_v_scroll = 0;
v_scroll_speed = 80;
+ draw_minimap = false;
+ minimap_width = 80;
+ minimap_char_size = Point2(1, 2);
+ minimap_line_spacing = 1;
+ selecting_enabled = true;
context_menu_enabled = true;
+ shortcut_keys_enabled = true;
menu = memnew(PopupMenu);
add_child(menu);
- readonly = true; // initialise to opposite first, so we get past the early-out in set_readonly
+ 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;
@@ -6696,8 +7209,14 @@ TextEdit::~TextEdit() {
///////////////////////////////////////////////////////////////////////////////
Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int p_line) {
+ if (syntax_highlighting_cache.has(p_line)) {
+ return syntax_highlighting_cache[p_line];
+ }
+
if (syntax_highlighter != NULL) {
- return syntax_highlighter->_get_line_syntax_highlighting(p_line);
+ Map<int, HighlighterInfo> color_map = syntax_highlighter->_get_line_syntax_highlighting(p_line);
+ syntax_highlighting_cache[p_line] = color_map;
+ return color_map;
}
Map<int, HighlighterInfo> color_map;
@@ -6743,14 +7262,14 @@ Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int
bool is_symbol = _is_symbol(str[j]);
bool is_number = _is_number(str[j]);
- // allow ABCDEF in hex notation
+ // Allow ABCDEF in hex notation.
if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
is_number = true;
} else {
is_hex_notation = false;
}
- // check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation
+ // Check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation.
if ((str[j] == '.' || str[j] == 'x' || str[j] == '_' || str[j] == 'f' || str[j] == 'e') && !in_word && prev_is_number && !is_number) {
is_number = true;
is_symbol = false;
@@ -6780,7 +7299,7 @@ Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int
if (!cri.end) {
in_region = cri.region;
}
- } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
+ } else if (in_region == cri.region && !color_regions[cri.region].line_only) { // Ignore otherwise.
if (cri.end || color_regions[cri.region].eq) {
deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length();
}
@@ -6808,7 +7327,7 @@ Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int
if (col) {
for (int k = j - 1; k >= 0; k--) {
if (str[k] == '.') {
- col = NULL; //member indexing not allowed
+ col = NULL; // Member indexing not allowed.
break;
} else if (str[k] > 32) {
break;
@@ -6830,7 +7349,7 @@ Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int
k++;
}
- // check for space between name and bracket
+ // Check for space between name and bracket.
while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
k++;
}
@@ -6879,6 +7398,7 @@ Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int
}
}
+ syntax_highlighting_cache[p_line] = color_map;
return color_map;
}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index b47dac0902..e5d9b006fe 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -211,9 +211,11 @@ private:
int breakpoint_gutter_width;
int fold_gutter_width;
int info_gutter_width;
+ int minimap_width;
} cache;
Map<int, int> color_region_cache;
+ Map<int, Map<int, HighlighterInfo> > syntax_highlighting_cache;
struct TextOperation {
@@ -313,6 +315,10 @@ private:
bool hiding_enabled;
bool draw_info_gutter;
int info_gutter_width;
+ bool draw_minimap;
+ int minimap_width;
+ Point2 minimap_char_size;
+ int minimap_line_spacing;
bool highlight_all_occurrences;
bool scroll_past_end_of_file_enabled;
@@ -326,6 +332,12 @@ private:
bool smooth_scroll_enabled;
bool scrolling;
+ bool dragging_selection;
+ bool dragging_minimap;
+ bool can_drag_minimap;
+ bool minimap_clicked;
+ double minimap_scroll_ratio;
+ double minimap_scroll_click_pos;
float target_v_scroll;
float v_scroll_speed;
@@ -353,13 +365,20 @@ private:
int search_result_line;
int search_result_col;
+ bool selecting_enabled;
+
bool context_menu_enabled;
+ bool shortcut_keys_enabled;
int executing_line;
+ void _generate_context_menu();
+
int get_visible_rows() const;
int get_total_visible_rows() const;
+ int _get_minimap_visible_rows() const;
+
void update_cursor_wrap_offset();
void _update_wrap_at();
bool line_wraps(int line) const;
@@ -395,6 +414,8 @@ private:
void _update_selection_mode_word();
void _update_selection_mode_line();
+ void _update_minimap_click();
+ void _update_minimap_drag();
void _scroll_up(real_t p_delta);
void _scroll_down(real_t p_delta);
@@ -406,6 +427,7 @@ private:
//void mouse_motion(const Point& p_pos, const Point& p_rel, int p_button_mask);
Size2 get_minimum_size() const;
+ int _get_control_height() const;
int get_row_height() const;
@@ -484,6 +506,7 @@ public:
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const;
void _get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const;
+ void _get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const;
//void delete_char();
//void delete_line();
@@ -564,6 +587,7 @@ public:
int cursor_get_column() const;
int cursor_get_line() const;
+ Vector2i _get_cursor_pixel_pos();
bool cursor_get_blink_enabled() const;
void cursor_set_blink_enabled(const bool p_enabled);
@@ -697,6 +721,12 @@ public:
void set_info_gutter_width(int p_gutter_width);
int get_info_gutter_width() const;
+ void set_draw_minimap(bool p_draw);
+ bool is_drawing_minimap() const;
+
+ void set_minimap_width(int p_minimap_width);
+ int get_minimap_width() const;
+
void set_hiding_enabled(bool p_enabled);
bool is_hiding_enabled() const;
@@ -713,6 +743,12 @@ public:
void set_context_menu_enabled(bool p_enable);
bool is_context_menu_enabled();
+ void set_selecting_enabled(bool p_enabled);
+ bool is_selecting_enabled() const;
+
+ void set_shortcut_keys_enabled(bool p_enabled);
+ bool is_shortcut_keys_enabled() const;
+
PopupMenu *get_menu() const;
String get_text_for_completion();
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index b5f949aeb7..e9b0bd8f38 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -205,24 +205,26 @@ void TextureButton::_notification(int p_what) {
case STRETCH_KEEP_ASPECT_COVERED: {
size = get_size();
Size2 tex_size = texdraw->get_size();
- Size2 scaleSize(size.width / tex_size.width, size.height / tex_size.height);
- float scale = scaleSize.width > scaleSize.height ? scaleSize.width : scaleSize.height;
- Size2 scaledTexSize = tex_size * scale;
- Point2 ofs2 = ((scaledTexSize - size) / scale).abs() / 2.0f;
+ Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
+ float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
+ Size2 scaled_tex_size = tex_size * scale;
+ Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f;
_texture_region = Rect2(ofs2, size / scale);
} break;
}
}
+
_position_rect = Rect2(ofs, size);
- if (_tile)
+ if (_tile) {
draw_texture_rect(texdraw, _position_rect, _tile);
- else
+ } else {
draw_texture_rect_region(texdraw, _position_rect, _texture_region);
+ }
} else {
_position_rect = Rect2();
}
- if (has_focus() && focused.is_valid()) {
+ if (has_focus() && focused.is_valid()) {
draw_texture_rect(focused, _position_rect, false);
};
} break;
diff --git a/scene/gui/texture_rect.h b/scene/gui/texture_rect.h
index 3ab35324e5..1c5bd9d99c 100644
--- a/scene/gui/texture_rect.h
+++ b/scene/gui/texture_rect.h
@@ -32,9 +32,7 @@
#define TEXTURE_FRAME_H
#include "scene/gui/control.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
+
class TextureRect : public Control {
GDCLASS(TextureRect, Control);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 4005505830..57663bbe82 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -224,14 +224,14 @@ Rect2 TreeItem::get_icon_region(int p_column) const {
return cells[p_column].icon_region;
}
-void TreeItem::set_icon_color(int p_column, const Color &p_icon_color) {
+void TreeItem::set_icon_modulate(int p_column, const Color &p_modulate) {
ERR_FAIL_INDEX(p_column, cells.size());
- cells.write[p_column].icon_color = p_icon_color;
+ cells.write[p_column].icon_color = p_modulate;
_changed_notify(p_column);
}
-Color TreeItem::get_icon_color(int p_column) const {
+Color TreeItem::get_icon_modulate(int p_column) const {
ERR_FAIL_INDEX_V(p_column, cells.size(), Color());
return cells[p_column].icon_color;
@@ -328,7 +328,7 @@ void TreeItem::set_collapsed(bool p_collapsed) {
ci = ci->parent;
}
- if (ci) { // collapsing cursor/selectd, move it!
+ if (ci) { // collapsing cursor/selected, move it!
if (tree->select_mode == Tree::SELECT_MULTI) {
@@ -744,6 +744,9 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_icon_max_width", "column", "width"), &TreeItem::set_icon_max_width);
ClassDB::bind_method(D_METHOD("get_icon_max_width", "column"), &TreeItem::get_icon_max_width);
+ ClassDB::bind_method(D_METHOD("set_icon_modulate", "column", "modulate"), &TreeItem::set_icon_modulate);
+ ClassDB::bind_method(D_METHOD("get_icon_modulate", "column"), &TreeItem::get_icon_modulate);
+
ClassDB::bind_method(D_METHOD("set_range", "column", "value"), &TreeItem::set_range);
ClassDB::bind_method(D_METHOD("get_range", "column"), &TreeItem::get_range);
ClassDB::bind_method(D_METHOD("set_range_config", "column", "min", "max", "step", "expr"), &TreeItem::set_range_config, DEFVAL(false));
@@ -911,7 +914,6 @@ void Tree::update_cache() {
cache.arrow_collapsed = get_icon("arrow_collapsed");
cache.arrow = get_icon("arrow");
cache.select_arrow = get_icon("select_arrow");
- cache.select_option = get_icon("select_option");
cache.updown = get_icon("updown");
cache.custom_button = get_stylebox("custom_button");
@@ -927,7 +929,6 @@ void Tree::update_cache() {
cache.vseparation = get_constant("vseparation");
cache.item_margin = get_constant("item_margin");
cache.button_margin = get_constant("button_margin");
- cache.guide_width = get_constant("guide_width");
cache.draw_guides = get_constant("draw_guides");
cache.draw_relationship_lines = get_constant("draw_relationship_lines");
cache.relationship_line_color = get_color("relationship_line_color");
@@ -1181,23 +1182,22 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
}
}
- if (p_item->cells[i].selected && select_mode != SELECT_ROW) {
-
+ if (select_mode != SELECT_ROW && (p_item->cells[i].selected || selected_item == p_item)) {
Rect2i r(cell_rect.position, cell_rect.size);
+
if (p_item->cells[i].text.size() > 0) {
float icon_width = p_item->cells[i].get_icon_size().width;
r.position.x += icon_width;
r.size.x -= icon_width;
}
p_item->set_meta("__focus_rect", Rect2(r.position, r.size));
- if (has_focus()) {
- cache.selected_focus->draw(ci, r);
- } else {
- cache.selected->draw(ci, r);
- }
- if (text_editor->is_visible_in_tree()) {
- Vector2 ofs2(0, (text_editor->get_size().height - r.size.height) / 2);
- text_editor->set_position(get_global_position() + r.position - ofs2);
+
+ if (p_item->cells[i].selected) {
+ if (has_focus()) {
+ cache.selected_focus->draw(ci, r);
+ } else {
+ cache.selected->draw(ci, r);
+ }
}
}
@@ -1259,10 +1259,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
check_ofs.y += Math::floor((real_t)(item_rect.size.y - checked->get_height()) / 2);
if (p_item->cells[i].checked) {
-
- checked->draw(ci, check_ofs, icon_col);
+ checked->draw(ci, check_ofs);
} else {
- unchecked->draw(ci, check_ofs, icon_col);
+ unchecked->draw(ci, check_ofs);
}
int check_w = checked->get_width() + cache.hseparation;
@@ -1274,8 +1273,6 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
draw_item_rect(p_item->cells[i], item_rect, col, icon_col);
- //font->draw( ci, text_pos, p_item->cells[i].text, col,item_rect.size.x-check_w );
-
} break;
case TreeItem::CELL_MODE_RANGE: {
if (p_item->cells[i].text != "") {
@@ -1305,18 +1302,16 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
font->draw(ci, text_pos, s, col, item_rect.size.x - downarrow->get_width());
- //?
Point2i arrow_pos = item_rect.position;
arrow_pos.x += item_rect.size.x - downarrow->get_width();
arrow_pos.y += Math::floor(((item_rect.size.y - downarrow->get_height())) / 2.0);
- downarrow->draw(ci, arrow_pos, icon_col);
+ downarrow->draw(ci, arrow_pos);
} else {
Ref<Texture> updown = cache.updown;
- String valtext = String::num(p_item->cells[i].val, Math::step_decimals(p_item->cells[i].step));
- //String valtext = rtos( p_item->cells[i].val );
+ String valtext = String::num(p_item->cells[i].val, Math::range_step_decimals(p_item->cells[i].step));
if (p_item->cells[i].suffix != String())
valtext += " " + p_item->cells[i].suffix;
@@ -1330,7 +1325,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
updown_pos.x += item_rect.size.x - updown->get_width();
updown_pos.y += Math::floor(((item_rect.size.y - updown->get_height())) / 2.0);
- updown->draw(ci, updown_pos, icon_col);
+ updown->draw(ci, updown_pos);
}
} break;
@@ -1348,13 +1343,10 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
icon_ofs += item_rect.position;
draw_texture_rect(p_item->cells[i].icon, Rect2(icon_ofs, icon_size), false, icon_col);
- //p_item->cells[i].icon->draw(ci, icon_ofs);
} break;
case TreeItem::CELL_MODE_CUSTOM: {
- //int option = (int)p_item->cells[i].val;
-
if (p_item->cells[i].custom_draw_obj) {
Object *cdo = ObjectDB::get_instance(p_item->cells[i].custom_draw_obj);
@@ -1429,10 +1421,6 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
arrow->draw(ci, p_pos + p_draw_ofs + Point2i(0, (label_h - arrow->get_height()) / 2) - cache.offset);
}
- //separator
- //get_painter()->draw_fill_rect( Point2i(0,pos.y),Size2i(get_size().width,1),color( COLOR_TREE_GRID) );
-
- //pos=p_pos; //reset pos
}
Point2 children_pos = p_pos;
@@ -1926,7 +1914,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
} else {
- editor_text = String::num(p_item->cells[col].val, Math::step_decimals(p_item->cells[col].step));
+ editor_text = String::num(p_item->cells[col].val, Math::range_step_decimals(p_item->cells[col].step));
if (select_mode == SELECT_MULTI && get_tree()->get_event_count() == focus_in_id)
bring_up_editor = false;
}
@@ -2700,12 +2688,10 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
bool Tree::edit_selected() {
TreeItem *s = get_selected();
- ERR_EXPLAIN("No item selected!");
- ERR_FAIL_COND_V(!s, false);
+ ERR_FAIL_COND_V_MSG(!s, false, "No item selected.");
ensure_cursor_is_visible();
int col = get_selected_column();
- ERR_EXPLAIN("No item column selected!");
- ERR_FAIL_INDEX_V(col, columns.size(), false);
+ ERR_FAIL_INDEX_V_MSG(col, columns.size(), false, "No item column selected.");
if (!s->cells[col].editable)
return false;
@@ -2755,7 +2741,7 @@ bool Tree::edit_selected() {
text_editor->set_position(textedpos);
text_editor->set_size(rect.size);
text_editor->clear();
- text_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::step_decimals(c.step)));
+ text_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::range_step_decimals(c.step)));
text_editor->select_all();
if (c.mode == TreeItem::CELL_MODE_RANGE) {
@@ -2921,8 +2907,6 @@ void Tree::_notification(int p_what) {
drag_touching = false;
drag_touching_deaccel = false;
}
-
- } else {
}
}
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index b6cdab766f..f12d8fc4d2 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -37,10 +37,6 @@
#include "scene/gui/scroll_bar.h"
#include "scene/gui/slider.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
-
class Tree;
class TreeItem : public Object {
@@ -197,8 +193,8 @@ public:
void set_icon_region(int p_column, const Rect2 &p_icon_region);
Rect2 get_icon_region(int p_column) const;
- void set_icon_color(int p_column, const Color &p_icon_color);
- Color get_icon_color(int p_column) const;
+ void set_icon_modulate(int p_column, const Color &p_modulate);
+ Color get_icon_modulate(int p_column) const;
void set_icon_max_width(int p_column, int p_max);
int get_icon_max_width(int p_column) const;
@@ -420,7 +416,6 @@ private:
Ref<Texture> arrow_collapsed;
Ref<Texture> arrow;
Ref<Texture> select_arrow;
- Ref<Texture> select_option;
Ref<Texture> updown;
Color font_color;
@@ -433,7 +428,6 @@ private:
int hseparation;
int vseparation;
int item_margin;
- int guide_width;
int button_margin;
Point2 offset;
int draw_relationship_lines;
diff --git a/scene/gui/viewport_container.cpp b/scene/gui/viewport_container.cpp
index 3f7a110c1b..35696a0459 100644
--- a/scene/gui/viewport_container.cpp
+++ b/scene/gui/viewport_container.cpp
@@ -211,4 +211,5 @@ ViewportContainer::ViewportContainer() {
stretch = false;
shrink = 1;
set_process_input(true);
+ set_process_unhandled_input(true);
}